From d0b66fdfd3c5c31764740f6ffe5a345549d6af97 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 3 Feb 2012 05:12:18 -0300 Subject: [media] ivtv: only start streaming in poll() if polling for input Signed-off-by: Hans Verkuil Acked-by: Andy Walls Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ivtv/ivtv-fileops.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index c9663e885b9f..9ff69b5a87e2 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -746,8 +746,9 @@ unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) return res; } -unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; @@ -755,7 +756,8 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) unsigned res = 0; /* Start a capture if there is none */ - if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags) && + (req_events & (POLLIN | POLLRDNORM))) { int rc; rc = ivtv_start_capture(id); -- cgit v1.2.3 From bf5c7cbb996d6af51f8cc18a30ffa426196bf840 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 13 Jul 2011 04:01:30 -0300 Subject: [media] videobuf2: only start streaming in poll() if so requested by the poll mask Signed-off-by: Hans Verkuil Acked-by: Marek Szyprowski Acked-by: Pawel Osciak Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 2e8f1df775b6..0b1c77114e83 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -1647,6 +1647,7 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q); */ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); unsigned long flags; unsigned int ret; struct vb2_buffer *vb = NULL; @@ -1655,12 +1656,14 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) * Start file I/O emulator only if streaming API has not been used yet. */ if (q->num_buffers == 0 && q->fileio == NULL) { - if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ)) { + if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && + (req_events & (POLLIN | POLLRDNORM))) { ret = __vb2_init_fileio(q, 1); if (ret) return POLLERR; } - if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) { + if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && + (req_events & (POLLOUT | POLLWRNORM))) { ret = __vb2_init_fileio(q, 0); if (ret) return POLLERR; -- cgit v1.2.3 From 0e17e9a9f6bfedb6aefcd88632f5d6d17c871176 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 13 Jul 2011 04:03:52 -0300 Subject: [media] videobuf: only start streaming in poll() if so requested by the poll mask Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index de4fa4eb8844..ffdf59cfe405 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -1129,6 +1129,7 @@ unsigned int videobuf_poll_stream(struct file *file, struct videobuf_queue *q, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct videobuf_buffer *buf = NULL; unsigned int rc = 0; @@ -1137,7 +1138,7 @@ unsigned int videobuf_poll_stream(struct file *file, if (!list_empty(&q->stream)) buf = list_entry(q->stream.next, struct videobuf_buffer, stream); - } else { + } else if (req_events & (POLLIN | POLLRDNORM)) { if (!q->reading) __videobuf_read_start(q); if (!q->reading) { -- cgit v1.2.3 From 95213ceb1b527b8102c589bd41fcb7c9163fdd79 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 13 Jul 2011 04:26:52 -0300 Subject: [media] videobuf2-core: also test for pending events Signed-off-by: Hans Verkuil Acked-by: Marek Szyprowski Acked-by: Pawel Osciak Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-core.c | 41 ++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 0b1c77114e83..3786d88183eb 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -19,6 +19,9 @@ #include #include +#include +#include +#include #include static int debug; @@ -1642,15 +1645,28 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q); * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor * will be reported as available for writing. * + * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any + * pending events. + * * The return values from this function are intended to be directly returned * from poll handler in driver. */ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { + struct video_device *vfd = video_devdata(file); unsigned long req_events = poll_requested_events(wait); - unsigned long flags; - unsigned int ret; struct vb2_buffer *vb = NULL; + unsigned int res = 0; + unsigned long flags; + + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { + struct v4l2_fh *fh = file->private_data; + + if (v4l2_event_pending(fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->wait, wait); + } /* * Start file I/O emulator only if streaming API has not been used yet. @@ -1658,19 +1674,17 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) if (q->num_buffers == 0 && q->fileio == NULL) { if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && (req_events & (POLLIN | POLLRDNORM))) { - ret = __vb2_init_fileio(q, 1); - if (ret) - return POLLERR; + if (__vb2_init_fileio(q, 1)) + return res | POLLERR; } if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && (req_events & (POLLOUT | POLLWRNORM))) { - ret = __vb2_init_fileio(q, 0); - if (ret) - return POLLERR; + if (__vb2_init_fileio(q, 0)) + return res | POLLERR; /* * Write to OUTPUT queue can be done immediately. */ - return POLLOUT | POLLWRNORM; + return res | POLLOUT | POLLWRNORM; } } @@ -1678,7 +1692,7 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) * There is nothing to wait for if no buffers have already been queued. */ if (list_empty(&q->queued_list)) - return POLLERR; + return res | POLLERR; poll_wait(file, &q->done_wq, wait); @@ -1693,10 +1707,11 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) if (vb && (vb->state == VB2_BUF_STATE_DONE || vb->state == VB2_BUF_STATE_ERROR)) { - return (V4L2_TYPE_IS_OUTPUT(q->type)) ? POLLOUT | POLLWRNORM : - POLLIN | POLLRDNORM; + return (V4L2_TYPE_IS_OUTPUT(q->type)) ? + res | POLLOUT | POLLWRNORM : + res | POLLIN | POLLRDNORM; } - return 0; + return res; } EXPORT_SYMBOL_GPL(vb2_poll); -- cgit v1.2.3 From 0bf0f713d6e6b9f1c510d598c29ac17812a4eea5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 13 Jul 2011 04:28:27 -0300 Subject: [media] vivi: let vb2_poll handle events The vb2_poll function now tests for events and sets POLLPRI accordingly. So there it is no longer necessary to test for it in the vivi driver. Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/vivi.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 5e8b0710105b..b456d3ed1197 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -1039,17 +1039,10 @@ static unsigned int vivi_poll(struct file *file, struct poll_table_struct *wait) { struct vivi_dev *dev = video_drvdata(file); - struct v4l2_fh *fh = file->private_data; struct vb2_queue *q = &dev->vb_vidq; - unsigned int res; dprintk(dev, 1, "%s\n", __func__); - res = vb2_poll(q, file, wait); - if (v4l2_event_pending(fh)) - res |= POLLPRI; - else - poll_wait(file, &fh->wait, wait); - return res; + return vb2_poll(q, file, wait); } static int vivi_close(struct file *file) -- cgit v1.2.3 From 296da3cd14db9eb5606924962b2956c9c656dbb0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 9 Oct 2011 10:28:27 -0300 Subject: [media] pwc: poll(): Check that the device has not beem claimed for streaming already Some apps which use read() start the streaming through a call to poll(), this means that if another app has already claimed the device for streaming (through for example a s_fmt, or a reqbufs), that the poll should fail instead of getting passed through to vb2_poll. We only check for this when the app is polling for reads, so that ctrl events still work. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-if.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 122fbd0081eb..f3370a87cbc0 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -625,10 +625,19 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, static unsigned int pwc_video_poll(struct file *file, poll_table *wait) { struct pwc_device *pdev = video_drvdata(file); + unsigned long req_events = poll_requested_events(wait); if (!pdev->udev) return POLL_ERR; + if ((req_events & (POLLIN | POLLRDNORM)) && + pdev->vb_queue.num_buffers == 0 && + !pdev->iso_init) { + /* This poll will start a read stream, check capt_file */ + if (pwc_test_n_set_capt_file(pdev, file)) + return POLL_ERR; + } + return vb2_poll(&pdev->vb_queue, file, wait); } -- cgit v1.2.3 From a875431dfd357b9b2e95859fe3e380557ddceab1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 27 Mar 2012 05:39:21 -0300 Subject: [media] radio-rtrack2: add missing slab.h Missed this one, this patch should solve this issue: http://www.spinics.net/lists/linux-media/msg45880.html Slab.h was added to all the other isa drivers, but not radio-rtrack2.c. Signed-off-by: Hans Verkuil Acked-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-rtrack2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index b275c5d0fe9a..b1f844c64fde 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -17,6 +17,7 @@ #include /* kernel radio structs */ #include #include /* outb, outb_p */ +#include #include #include #include "radio-isa.h" -- cgit v1.2.3 From 3f1bfef8e79c53d5d697119a7e11e5db80c2d371 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 27 Mar 2012 18:57:55 +0200 Subject: pwc: Add support for control events Since the pwc driver already uses v4l2-device, v4l2-fh, and the control framework, all that is needed is hooking up event subscription. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-v4l.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 2834e3e65b39..c1ba1a060c93 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -1166,4 +1166,6 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_enum_frameintervals = pwc_enum_frameintervals, .vidioc_g_parm = pwc_g_parm, .vidioc_s_parm = pwc_s_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -- cgit v1.2.3 From f9263747b1669cbe21b7e21fe4316559cf5138f7 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 30 Mar 2012 06:37:26 -0300 Subject: [media] Infineon TUA 9001 silicon tuner driver Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/Kconfig | 6 + drivers/media/common/tuners/Makefile | 1 + drivers/media/common/tuners/tua9001.c | 215 +++++++++++++++++++++++++++++ drivers/media/common/tuners/tua9001.h | 46 ++++++ drivers/media/common/tuners/tua9001_priv.h | 34 +++++ 5 files changed, 302 insertions(+) create mode 100644 drivers/media/common/tuners/tua9001.c create mode 100644 drivers/media/common/tuners/tua9001.h create mode 100644 drivers/media/common/tuners/tua9001_priv.h diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 4a6d5cef3964..ae8ebcfa6fa2 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -211,4 +211,10 @@ config MEDIA_TUNER_TDA18212 help NXP TDA18212 silicon tuner driver. +config MEDIA_TUNER_TUA9001 + tristate "Infineon TUA 9001 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Infineon TUA 9001 silicon tuner driver. endmenu diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index f80407eb8998..6c3040501c45 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o +obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/tua9001.c b/drivers/media/common/tuners/tua9001.c new file mode 100644 index 000000000000..de2607084672 --- /dev/null +++ b/drivers/media/common/tuners/tua9001.c @@ -0,0 +1,215 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "tua9001.h" +#include "tua9001_priv.h" + +/* write register */ +static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val) +{ + int ret; + u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff }; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + printk(KERN_WARNING "%s: I2C wr failed=%d reg=%02x\n", + __func__, ret, reg); + ret = -EREMOTEIO; + } + + return ret; +} + +static int tua9001_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int tua9001_init(struct dvb_frontend *fe) +{ + struct tua9001_priv *priv = fe->tuner_priv; + int ret = 0; + u8 i; + struct reg_val data[] = { + { 0x1e, 0x6512 }, + { 0x25, 0xb888 }, + { 0x39, 0x5460 }, + { 0x3b, 0x00c0 }, + { 0x3a, 0xf000 }, + { 0x08, 0x0000 }, + { 0x32, 0x0030 }, + { 0x41, 0x703a }, + { 0x40, 0x1c78 }, + { 0x2c, 0x1c00 }, + { 0x36, 0xc013 }, + { 0x37, 0x6f18 }, + { 0x27, 0x0008 }, + { 0x2a, 0x0001 }, + { 0x34, 0x0a40 }, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + + if (ret < 0) + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int tua9001_set_params(struct dvb_frontend *fe) +{ + struct tua9001_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u16 val; + u32 frequency; + struct reg_val data[2]; + + pr_debug("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", + __func__, c->delivery_system, c->frequency, + c->bandwidth_hz); + + switch (c->delivery_system) { + case SYS_DVBT: + switch (c->bandwidth_hz) { + case 8000000: + val = 0x0000; + break; + case 7000000: + val = 0x1000; + break; + case 6000000: + val = 0x2000; + break; + case 5000000: + val = 0x3000; + break; + default: + ret = -EINVAL; + goto err; + } + break; + default: + ret = -EINVAL; + goto err; + } + + data[0].reg = 0x04; + data[0].val = val; + + frequency = (c->frequency - 150000000); + frequency /= 100; + frequency *= 48; + frequency /= 10000; + + data[1].reg = 0x1f; + data[1].val = frequency; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); + if (ret < 0) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + +err: + if (ret < 0) + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; /* Zero-IF */ + + return 0; +} + +static const struct dvb_tuner_ops tua9001_tuner_ops = { + .info = { + .name = "Infineon TUA 9001", + + .frequency_min = 170000000, + .frequency_max = 862000000, + .frequency_step = 0, + }, + + .release = tua9001_release, + + .init = tua9001_init, + .set_params = tua9001_set_params, + + .get_if_frequency = tua9001_get_if_frequency, +}; + +struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg) +{ + struct tua9001_priv *priv = NULL; + + priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + + printk(KERN_INFO "Infineon TUA 9001 successfully attached."); + + memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL(tua9001_attach); + +MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/tua9001.h b/drivers/media/common/tuners/tua9001.h new file mode 100644 index 000000000000..38d6ae76b1d6 --- /dev/null +++ b/drivers/media/common/tuners/tua9001.h @@ -0,0 +1,46 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TUA9001_H +#define TUA9001_H + +#include "dvb_frontend.h" + +struct tua9001_config { + /* + * I2C address + */ + u8 i2c_addr; +}; + +#if defined(CONFIG_MEDIA_TUNER_TUA9001) || \ + (defined(CONFIG_MEDIA_TUNER_TUA9001_MODULE) && defined(MODULE)) +extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg); +#else +static inline struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/tua9001_priv.h b/drivers/media/common/tuners/tua9001_priv.h new file mode 100644 index 000000000000..73cc1ce0575c --- /dev/null +++ b/drivers/media/common/tuners/tua9001_priv.h @@ -0,0 +1,34 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TUA9001_PRIV_H +#define TUA9001_PRIV_H + +struct reg_val { + u8 reg; + u16 val; +}; + +struct tua9001_priv { + struct tua9001_config *cfg; + struct i2c_adapter *i2c; +}; + +#endif -- cgit v1.2.3 From 4b64bb268fa14b8aa971b55a090731caae6641e0 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 30 Mar 2012 08:21:25 -0300 Subject: [media] Afatech AF9033 DVB-T demodulator driver Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/Kconfig | 5 + drivers/media/dvb/frontends/Makefile | 1 + drivers/media/dvb/frontends/af9033.c | 706 ++++++++++++++++++++++++++++++ drivers/media/dvb/frontends/af9033.h | 73 +++ drivers/media/dvb/frontends/af9033_priv.h | 242 ++++++++++ 5 files changed, 1027 insertions(+) create mode 100644 drivers/media/dvb/frontends/af9033.c create mode 100644 drivers/media/dvb/frontends/af9033.h create mode 100644 drivers/media/dvb/frontends/af9033_priv.h diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 21246707fbfb..e11adb64e9e6 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -713,6 +713,11 @@ config DVB_M88RS2000 A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_AF9033 + tristate "Afatech AF9033 DVB-T demodulator" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 86fa808bf589..6ca75578ecac 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -98,4 +98,5 @@ obj-$(CONFIG_DVB_A8293) += a8293.o obj-$(CONFIG_DVB_TDA10071) += tda10071.o obj-$(CONFIG_DVB_RTL2830) += rtl2830.o obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o +obj-$(CONFIG_DVB_AF9033) += af9033.o diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c new file mode 100644 index 000000000000..161bbe5f2e3f --- /dev/null +++ b/drivers/media/dvb/frontends/af9033.c @@ -0,0 +1,706 @@ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "af9033_priv.h" + +struct af9033_state { + struct i2c_adapter *i2c; + struct dvb_frontend fe; + struct af9033_config cfg; + + u32 bandwidth_hz; + bool ts_mode_parallel; + bool ts_mode_serial; +}; + +/* write multiple registers */ +static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val, + int len) +{ + int ret; + u8 buf[3 + len]; + struct i2c_msg msg[1] = { + { + .addr = state->cfg.i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + buf[0] = (reg >> 16) & 0xff; + buf[1] = (reg >> 8) & 0xff; + buf[2] = (reg >> 0) & 0xff; + memcpy(&buf[3], val, len); + + ret = i2c_transfer(state->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + printk(KERN_WARNING "%s: i2c wr failed=%d reg=%06x len=%d\n", + __func__, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* read multiple registers */ +static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len) +{ + int ret; + u8 buf[3] = { (reg >> 16) & 0xff, (reg >> 8) & 0xff, + (reg >> 0) & 0xff }; + struct i2c_msg msg[2] = { + { + .addr = state->cfg.i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf + }, { + .addr = state->cfg.i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = val + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret == 2) { + ret = 0; + } else { + printk(KERN_WARNING "%s: i2c rd failed=%d reg=%06x len=%d\n", + __func__, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + + +/* write single register */ +static int af9033_wr_reg(struct af9033_state *state, u32 reg, u8 val) +{ + return af9033_wr_regs(state, reg, &val, 1); +} + +/* read single register */ +static int af9033_rd_reg(struct af9033_state *state, u32 reg, u8 *val) +{ + return af9033_rd_regs(state, reg, val, 1); +} + +/* write single register with mask */ +static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val, + u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = af9033_rd_regs(state, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return af9033_wr_regs(state, reg, &val, 1); +} + +/* read single register with mask */ +static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val, + u8 mask) +{ + int ret, i; + u8 tmp; + + ret = af9033_rd_regs(state, reg, &tmp, 1); + if (ret) + return ret; + + tmp &= mask; + + /* find position of the first bit */ + for (i = 0; i < 8; i++) { + if ((mask >> i) & 0x01) + break; + } + *val = tmp >> i; + + return 0; +} + +static u32 af9033_div(u32 a, u32 b, u32 x) +{ + u32 r = 0, c = 0, i; + + pr_debug("%s: a=%d b=%d x=%d\n", __func__, a, b, x); + + if (a > b) { + c = a / b; + a = a - c * b; + } + + for (i = 0; i < x; i++) { + if (a >= b) { + r += 1; + a -= b; + } + a <<= 1; + r <<= 1; + } + r = (c << (u32)x) + r; + + pr_debug("%s: a=%d b=%d x=%d r=%d r=%x\n", __func__, a, b, x, r, r); + + return r; +} + +static void af9033_release(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + + kfree(state); +} + +static int af9033_init(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret, i, len; + const struct reg_val *init; + u8 buf[4]; + u32 adc_cw, clock_cw; + struct reg_val_mask tab[] = { + { 0x80fb24, 0x00, 0x08 }, + { 0x80004c, 0x00, 0xff }, + { 0x00f641, state->cfg.tuner, 0xff }, + { 0x80f5ca, 0x01, 0x01 }, + { 0x80f715, 0x01, 0x01 }, + { 0x00f41f, 0x04, 0x04 }, + { 0x00f41a, 0x01, 0x01 }, + { 0x80f731, 0x00, 0x01 }, + { 0x00d91e, 0x00, 0x01 }, + { 0x00d919, 0x00, 0x01 }, + { 0x80f732, 0x00, 0x01 }, + { 0x00d91f, 0x00, 0x01 }, + { 0x00d91a, 0x00, 0x01 }, + { 0x80f730, 0x00, 0x01 }, + { 0x80f778, 0x00, 0xff }, + { 0x80f73c, 0x01, 0x01 }, + { 0x80f776, 0x00, 0x01 }, + { 0x00d8fd, 0x01, 0xff }, + { 0x00d830, 0x01, 0xff }, + { 0x00d831, 0x00, 0xff }, + { 0x00d832, 0x00, 0xff }, + { 0x80f985, state->ts_mode_serial, 0x01 }, + { 0x80f986, state->ts_mode_parallel, 0x01 }, + { 0x00d827, 0x00, 0xff }, + { 0x00d829, 0x00, 0xff }, + }; + + /* program clock control */ + clock_cw = af9033_div(state->cfg.clock, 1000000ul, 19ul); + buf[0] = (clock_cw >> 0) & 0xff; + buf[1] = (clock_cw >> 8) & 0xff; + buf[2] = (clock_cw >> 16) & 0xff; + buf[3] = (clock_cw >> 24) & 0xff; + + pr_debug("%s: clock=%d clock_cw=%08x\n", __func__, state->cfg.clock, + clock_cw); + + ret = af9033_wr_regs(state, 0x800025, buf, 4); + if (ret < 0) + goto err; + + /* program ADC control */ + for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { + if (clock_adc_lut[i].clock == state->cfg.clock) + break; + } + + adc_cw = af9033_div(clock_adc_lut[i].adc, 1000000ul, 19ul); + buf[0] = (adc_cw >> 0) & 0xff; + buf[1] = (adc_cw >> 8) & 0xff; + buf[2] = (adc_cw >> 16) & 0xff; + + pr_debug("%s: adc=%d adc_cw=%06x\n", __func__, clock_adc_lut[i].adc, + adc_cw); + + ret = af9033_wr_regs(state, 0x80f1cd, buf, 3); + if (ret < 0) + goto err; + + /* program register table */ + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = af9033_wr_reg_mask(state, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret < 0) + goto err; + } + + /* settings for TS interface */ + if (state->cfg.ts_mode == AF9033_TS_MODE_USB) { + ret = af9033_wr_reg_mask(state, 0x80f9a5, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x01, 0x01); + if (ret < 0) + goto err; + } else { + ret = af9033_wr_reg_mask(state, 0x80f990, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x00, 0x01); + if (ret < 0) + goto err; + } + + /* load OFSM settings */ + pr_debug("%s: load ofsm settings\n", __func__); + len = ARRAY_SIZE(ofsm_init); + init = ofsm_init; + for (i = 0; i < len; i++) { + ret = af9033_wr_reg(state, init[i].reg, init[i].val); + if (ret < 0) + goto err; + } + + /* load tuner specific settings */ + pr_debug("%s: load tuner specific settings\n", + __func__); + switch (state->cfg.tuner) { + case AF9033_TUNER_TUA9001: + len = ARRAY_SIZE(tuner_init_tua9001); + init = tuner_init_tua9001; + break; + default: + pr_debug("%s: unsupported tuner ID=%d\n", __func__, + state->cfg.tuner); + ret = -ENODEV; + goto err; + } + + for (i = 0; i < len; i++) { + ret = af9033_wr_reg(state, init[i].reg, init[i].val); + if (ret < 0) + goto err; + } + + state->bandwidth_hz = 0; /* force to program all parameters */ + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_sleep(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret, i; + u8 tmp; + + ret = af9033_wr_reg(state, 0x80004c, 1); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800000, 0); + if (ret < 0) + goto err; + + for (i = 100, tmp = 1; i && tmp; i--) { + ret = af9033_rd_reg(state, 0x80004c, &tmp); + if (ret < 0) + goto err; + + usleep_range(200, 10000); + } + + pr_debug("%s: loop=%d", __func__, i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto err; + } + + ret = af9033_wr_reg_mask(state, 0x80fb24, 0x08, 0x08); + if (ret < 0) + goto err; + + /* prevent current leak (?) */ + if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + /* enable parallel TS */ + ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x00d916, 0x01, 0x01); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 800; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int af9033_set_frontend(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u8 tmp, buf[3], bandwidth_reg_val; + u32 if_frequency, freq_cw; + + pr_debug("%s: frequency=%d bandwidth_hz=%d\n", __func__, c->frequency, + c->bandwidth_hz); + + /* check bandwidth */ + switch (c->bandwidth_hz) { + case 6000000: + bandwidth_reg_val = 0x00; + break; + case 7000000: + bandwidth_reg_val = 0x01; + break; + case 8000000: + bandwidth_reg_val = 0x02; + break; + default: + pr_debug("%s: invalid bandwidth_hz\n", __func__); + ret = -EINVAL; + goto err; + } + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* program CFOE coefficients */ + if (c->bandwidth_hz != state->bandwidth_hz) { + for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) { + if (coeff_lut[i].clock == state->cfg.clock && + coeff_lut[i].bandwidth_hz == c->bandwidth_hz) { + break; + } + } + ret = af9033_wr_regs(state, 0x800001, + coeff_lut[i].val, sizeof(coeff_lut[i].val)); + } + + /* program frequency control */ + if (c->bandwidth_hz != state->bandwidth_hz) { + /* get used IF frequency */ + if (fe->ops.tuner_ops.get_if_frequency) + fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + else + if_frequency = 0; + + /* FIXME: we support only Zero-IF currently */ + if (if_frequency != 0) { + pr_debug("%s: only Zero-IF supported currently\n", + __func__); + + ret = -ENODEV; + goto err; + } + + freq_cw = 0; + buf[0] = (freq_cw >> 0) & 0xff; + buf[1] = (freq_cw >> 8) & 0xff; + buf[2] = (freq_cw >> 16) & 0x7f; + ret = af9033_wr_regs(state, 0x800029, buf, 3); + if (ret < 0) + goto err; + + state->bandwidth_hz = c->bandwidth_hz; + } + + ret = af9033_wr_reg_mask(state, 0x80f904, bandwidth_reg_val, 0x03); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800040, 0x00); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800047, 0x00); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x80f999, 0x00, 0x01); + if (ret < 0) + goto err; + + if (c->frequency <= 230000000) + tmp = 0x00; /* VHF */ + else + tmp = 0x01; /* UHF */ + + ret = af9033_wr_reg(state, 0x80004b, tmp); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800000, 0x00); + if (ret < 0) + goto err; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + u8 tmp; + + *status = 0; + + /* radio channel status, 0=no result, 1=has signal, 2=no signal */ + ret = af9033_rd_reg(state, 0x800047, &tmp); + if (ret < 0) + goto err; + + /* has signal */ + if (tmp == 0x01) + *status |= FE_HAS_SIGNAL; + + if (tmp != 0x02) { + /* TPS lock */ + ret = af9033_rd_reg_mask(state, 0x80f5a9, &tmp, 0x01); + if (ret < 0) + goto err; + + if (tmp) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + + /* full lock */ + ret = af9033_rd_reg_mask(state, 0x80f999, &tmp, 0x01); + if (ret < 0) + goto err; + + if (tmp) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0; + + return 0; +} + +static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + u8 strength2; + + /* read signal strength of 0-100 scale */ + ret = af9033_rd_reg(state, 0x800048, &strength2); + if (ret < 0) + goto err; + + /* scale value to 0x0000-0xffff */ + *strength = strength2 * 0xffff / 100; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + + return 0; +} + +static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + + return 0; +} + +static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + + pr_debug("%s: enable=%d\n", __func__, enable); + + ret = af9033_wr_reg_mask(state, 0x00fa04, enable, 0x01); + if (ret < 0) + goto err; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static struct dvb_frontend_ops af9033_ops; + +struct dvb_frontend *af9033_attach(const struct af9033_config *config, + struct i2c_adapter *i2c) +{ + int ret; + struct af9033_state *state; + u8 buf[8]; + + pr_debug("%s:\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct af9033_state), GFP_KERNEL); + if (state == NULL) + goto err; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->cfg, config, sizeof(struct af9033_config)); + + /* firmware version */ + ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4); + if (ret < 0) + goto err; + + ret = af9033_rd_regs(state, 0x804191, &buf[4], 4); + if (ret < 0) + goto err; + + printk(KERN_INFO "af9033: firmware version: LINK=%d.%d.%d.%d " \ + "OFDM=%d.%d.%d.%d\n", buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + /* configure internal TS mode */ + switch (state->cfg.ts_mode) { + case AF9033_TS_MODE_PARALLEL: + state->ts_mode_parallel = true; + break; + case AF9033_TS_MODE_SERIAL: + state->ts_mode_serial = true; + break; + case AF9033_TS_MODE_USB: + /* usb mode for AF9035 */ + default: + break; + } + + /* create dvb_frontend */ + memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops)); + state->fe.demodulator_priv = state; + + return &state->fe; + +err: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(af9033_attach); + +static struct dvb_frontend_ops af9033_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Afatech AF9033 (DVB-T)", + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 250000, + .frequency_tolerance = 0, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .release = af9033_release, + + .init = af9033_init, + .sleep = af9033_sleep, + + .get_tune_settings = af9033_get_tune_settings, + .set_frontend = af9033_set_frontend, + + .read_status = af9033_read_status, + .read_snr = af9033_read_snr, + .read_signal_strength = af9033_read_signal_strength, + .read_ber = af9033_read_ber, + .read_ucblocks = af9033_read_ucblocks, + + .i2c_gate_ctrl = af9033_i2c_gate_ctrl, +}; + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/af9033.h b/drivers/media/dvb/frontends/af9033.h new file mode 100644 index 000000000000..af8c1e3e30d0 --- /dev/null +++ b/drivers/media/dvb/frontends/af9033.h @@ -0,0 +1,73 @@ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AF9033_H +#define AF9033_H + +struct af9033_config { + /* + * I2C address + */ + u8 i2c_addr; + + /* + * clock Hz + * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000, + * 30000000, 36000000, 20480000, 16384000 + */ + u32 clock; + + /* + * tuner + */ +#define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ +#define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ + u8 tuner; + + /* + * TS settings + */ +#define AF9033_TS_MODE_USB 0 +#define AF9033_TS_MODE_PARALLEL 1 +#define AF9033_TS_MODE_SERIAL 2 + u8 ts_mode:2; + + /* + * input spectrum inversion + */ + bool spec_inv; +}; + + +#if defined(CONFIG_DVB_AF9033) || \ + (defined(CONFIG_DVB_AF9033_MODULE) && defined(MODULE)) +extern struct dvb_frontend *af9033_attach(const struct af9033_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *af9033_attach( + const struct af9033_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* AF9033_H */ diff --git a/drivers/media/dvb/frontends/af9033_priv.h b/drivers/media/dvb/frontends/af9033_priv.h new file mode 100644 index 000000000000..2bf579d89a83 --- /dev/null +++ b/drivers/media/dvb/frontends/af9033_priv.h @@ -0,0 +1,242 @@ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AF9033_PRIV_H +#define AF9033_PRIV_H + +#include "dvb_frontend.h" +#include "af9033.h" + +struct reg_val { + u32 reg; + u8 val; +}; + +struct reg_val_mask { + u32 reg; + u8 val; + u8 mask; +}; + +struct coeff { + u32 clock; + u32 bandwidth_hz; + u8 val[36]; +}; + +struct clock_adc { + u32 clock; + u32 adc; +}; + +/* Xtal clock vs. ADC clock lookup table */ +static const struct clock_adc clock_adc_lut[] = { + { 16384000, 20480000 }, + { 20480000, 20480000 }, + { 36000000, 20250000 }, + { 30000000, 20156250 }, + { 26000000, 20583333 }, + { 28000000, 20416667 }, + { 32000000, 20500000 }, + { 34000000, 20187500 }, + { 24000000, 20500000 }, + { 22000000, 20625000 }, + { 12000000, 20250000 }, +}; + +/* pre-calculated coeff lookup table */ +static const struct coeff coeff_lut[] = { + /* 12.000 MHz */ + { 12000000, 8000000, { + 0x01, 0xce, 0x55, 0xc9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, + 0x99, 0x0f, 0x00, 0x73, 0x95, 0x72, 0x00, 0x73, 0x91, 0xd5, + 0x00, 0x39, 0xca, 0xb9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, + 0x95, 0x72, 0x37, 0x02, 0xce, 0x01 } + }, + { 12000000, 7000000, { + 0x01, 0x94, 0x8b, 0x10, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, + 0x25, 0xed, 0x00, 0x65, 0x22, 0xc4, 0x00, 0x65, 0x1f, 0x9b, + 0x00, 0x32, 0x91, 0x62, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, + 0x22, 0xc4, 0x88, 0x02, 0x95, 0x01 } + }, + { 12000000, 6000000, { + 0x01, 0x5a, 0xc0, 0x56, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, + 0xb2, 0xcb, 0x00, 0x56, 0xb0, 0x15, 0x00, 0x56, 0xad, 0x60, + 0x00, 0x2b, 0x58, 0x0b, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, + 0xb0, 0x15, 0xf4, 0x02, 0x5b, 0x01 } + }, +}; + +static const struct reg_val ofsm_init[] = { + { 0x800051, 0x01 }, + { 0x800070, 0x0a }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800099, 0x01 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000ab, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000c0, 0x05 }, + { 0x8000c4, 0x19 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* Infineon TUA 9001 tuner init + AF9033_TUNER_TUA9001 = 0x27 */ +static const struct reg_val tuner_init_tua9001[] = { + { 0x800046, 0x27 }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x80006d, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800075, 0x03 }, + { 0x800076, 0x02 }, + { 0x800077, 0x00 }, + { 0x800078, 0x01 }, + { 0x800079, 0x00 }, + { 0x80007a, 0x7e }, + { 0x80007b, 0x3e }, + { 0x800093, 0x00 }, + { 0x800094, 0x01 }, + { 0x800095, 0x02 }, + { 0x800096, 0x01 }, + { 0x800098, 0x0a }, + { 0x80009b, 0x05 }, + { 0x80009c, 0x80 }, + { 0x8000b3, 0x00 }, + { 0x8000c1, 0x01 }, + { 0x8000c2, 0x00 }, + { 0x80f007, 0x00 }, + { 0x80f01f, 0x82 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, +}; + +#endif /* AF9033_PRIV_H */ + -- cgit v1.2.3 From 7f882c2e353ca58695e9b4fcc9e97a9d4cbcf273 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 30 Mar 2012 09:10:08 -0300 Subject: [media] Afatech AF9035 DVB USB driver AF9035 is integrated DVB USB interface and DVB-T demodulator. Integrated demodulator is AF9033 and its driver is attached runtime as a own module. Driver currently supports only one device, TerraTec Cinergy T Stick [0ccd:0093]. TerraTec Cinergy T Stick is based of Afatech AF9035 + Infineon TUA 9001 silicon tuner. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/Kconfig | 9 + drivers/media/dvb/dvb-usb/Makefile | 3 + drivers/media/dvb/dvb-usb/af9035.c | 799 ++++++++++++++++++++++++++++++++ drivers/media/dvb/dvb-usb/af9035.h | 102 ++++ drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + 5 files changed, 914 insertions(+) create mode 100644 drivers/media/dvb/dvb-usb/af9035.c create mode 100644 drivers/media/dvb/dvb-usb/af9035.h diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 63bf45679f98..df229fcc8f07 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -422,3 +422,12 @@ config DVB_USB_RTL28XXU select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Realtek RTL28xxU DVB USB receiver. + +config DVB_USB_AF9035 + tristate "Afatech AF9035 DVB-T USB2.0 support" + depends on DVB_USB + select DVB_AF9033 + select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Afatech AF9035 based DVB USB receiver. + diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index b76acb5387e6..b667ac39a4e3 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -110,6 +110,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o dvb-usb-rtl28xxu-objs = rtl28xxu.o obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o +dvb-usb-af9035-objs = af9035.o +obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o + ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends/ # due to tuner-xc3028 diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c new file mode 100644 index 000000000000..6606cf52ea98 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -0,0 +1,799 @@ +/* + * Afatech AF9035 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "af9035.h" +#include "af9033.h" +#include "tua9001.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +static DEFINE_MUTEX(af9035_usb_mutex); +static struct config af9035_config; +static struct dvb_usb_device_properties af9035_properties[1]; +static int af9035_properties_count = ARRAY_SIZE(af9035_properties); +static struct af9033_config af9035_af9033_config[] = { + { + .ts_mode = AF9033_TS_MODE_USB, + }, { + .ts_mode = AF9033_TS_MODE_SERIAL, + } +}; + +static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) +{ +#define BUF_LEN 63 +#define REQ_HDR_LEN 4 /* send header size */ +#define ACK_HDR_LEN 3 /* rece header size */ +#define CHECKSUM_LEN 2 +#define USB_TIMEOUT 2000 + + int ret, i, act_len; + u8 buf[BUF_LEN]; + u32 msg_len; + static u8 seq; /* packet sequence number */ + u16 checksum = 0; + + /* buffer overflow check */ + if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || + req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) { + pr_debug("%s: too much data wlen=%d rlen=%d\n", __func__, + req->wlen, req->rlen); + return -EINVAL; + } + + if (mutex_lock_interruptible(&af9035_usb_mutex) < 0) + return -EAGAIN; + + buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1; + buf[1] = req->mbox; + buf[2] = req->cmd; + buf[3] = seq++; + if (req->wlen) + memcpy(&buf[4], req->wbuf, req->wlen); + + /* calc and add checksum */ + for (i = 1; i < buf[0]-1; i++) { + if (i % 2) + checksum += buf[i] << 8; + else + checksum += buf[i]; + } + checksum = ~checksum; + + buf[buf[0]-1] = (checksum >> 8); + buf[buf[0]-0] = (checksum & 0xff); + + msg_len = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN ; + + /* send req */ + ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len, + &act_len, USB_TIMEOUT); + if (ret < 0) + err("bulk message failed=%d (%d/%d)", ret, msg_len, act_len); + else + if (act_len != msg_len) + ret = -EIO; /* all data is not send */ + if (ret < 0) + goto err_mutex_unlock; + + /* no ack for those packets */ + if (req->cmd == CMD_FW_DL) + goto exit_mutex_unlock; + + /* receive ack and data if read req */ + msg_len = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN; + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, + &act_len, USB_TIMEOUT); + if (ret < 0) { + err("recv bulk message failed=%d", ret); + ret = -EIO; + goto err_mutex_unlock; + } + + /* check status */ + if (buf[2]) { + pr_debug("%s: command=%02x failed fw error=%d\n", __func__, + req->cmd, buf[2]); + ret = -EIO; + goto err_mutex_unlock; + } + + /* read request, copy returned data to return buf */ + if (req->rlen) + memcpy(req->rbuf, &buf[ACK_HDR_LEN], req->rlen); + +err_mutex_unlock: +exit_mutex_unlock: + mutex_unlock(&af9035_usb_mutex); + + return ret; +} + +/* write multiple registers */ +static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) +{ + u8 wbuf[6 + len]; + u8 mbox = (reg >> 16) & 0xff; + struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL }; + + wbuf[0] = len; + wbuf[1] = 2; + wbuf[2] = 0; + wbuf[3] = 0; + wbuf[4] = (reg >> 8) & 0xff; + wbuf[5] = (reg >> 0) & 0xff; + memcpy(&wbuf[6], val, len); + + return af9035_ctrl_msg(d->udev, &req); +} + +/* read multiple registers */ +static int af9035_rd_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) +{ + u8 wbuf[] = { len, 2, 0, 0, (reg >> 8) & 0xff, reg & 0xff }; + u8 mbox = (reg >> 16) & 0xff; + struct usb_req req = { CMD_MEM_RD, mbox, sizeof(wbuf), wbuf, len, val }; + + return af9035_ctrl_msg(d->udev, &req); +} + +/* write single register */ +static int af9035_wr_reg(struct dvb_usb_device *d, u32 reg, u8 val) +{ + return af9035_wr_regs(d, reg, &val, 1); +} + +/* read single register */ +static int af9035_rd_reg(struct dvb_usb_device *d, u32 reg, u8 *val) +{ + return af9035_rd_regs(d, reg, val, 1); +} + +/* write single register with mask */ +static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val, + u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = af9035_rd_regs(d, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return af9035_wr_regs(d, reg, &val, 1); +} + +static int af9035_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num == 2 && !(msg[0].flags & I2C_M_RD) && + (msg[1].flags & I2C_M_RD)) { + if (msg[0].len > 40 || msg[1].len > 40) { + /* TODO: correct limits > 40 */ + ret = -EOPNOTSUPP; + } else if (msg[0].addr == af9035_af9033_config[0].i2c_addr) { + /* integrated demod */ + u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; + ret = af9035_rd_regs(d, reg, &msg[1].buf[0], + msg[1].len); + } else { + /* I2C */ +#if 0 + /* + * FIXME: Keep that code. It should work but as it is + * not tested I left it disabled and return -EOPNOTSUPP + * for the sure. + */ + u8 buf[4 + msg[0].len]; + struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), + buf, msg[1].len, msg[1].buf }; + buf[0] = msg[0].len; + buf[1] = msg[0].addr << 1; + buf[2] = 0x01; + buf[3] = 0x00; + memcpy(&buf[4], msg[0].buf, msg[0].len); + ret = af9035_ctrl_msg(d->udev, &req); +#endif + pr_debug("%s: I2C operation not supported\n", __func__); + ret = -EOPNOTSUPP; + } + } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + if (msg[0].len > 40) { + /* TODO: correct limits > 40 */ + ret = -EOPNOTSUPP; + } else if (msg[0].addr == af9035_af9033_config[0].i2c_addr) { + /* integrated demod */ + u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; + ret = af9035_wr_regs(d, reg, &msg[0].buf[3], + msg[0].len - 3); + } else { + /* I2C */ + u8 buf[4 + msg[0].len]; + struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf, + 0, NULL }; + buf[0] = msg[0].len; + buf[1] = msg[0].addr << 1; + buf[2] = 0x01; + buf[3] = 0x00; + memcpy(&buf[4], msg[0].buf, msg[0].len); + ret = af9035_ctrl_msg(d->udev, &req); + } + } else { + /* + * We support only two kind of I2C transactions: + * 1) 1 x read + 1 x write + * 2) 1 x write + */ + ret = -EOPNOTSUPP; + } + + mutex_unlock(&d->i2c_mutex); + + if (ret < 0) + return ret; + else + return num; +} + +static u32 af9035_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm af9035_i2c_algo = { + .master_xfer = af9035_i2c_master_xfer, + .functionality = af9035_i2c_functionality, +}; + +static int af9035_init(struct dvb_usb_device *d) +{ + int ret, i; + u16 frame_size = 87 * 188 / 4; + u8 packet_size = 512 / 4; + struct reg_val_mask tab[] = { + { 0x80f99d, 0x01, 0x01 }, + { 0x80f9a4, 0x01, 0x01 }, + { 0x00dd11, 0x00, 0x20 }, + { 0x00dd11, 0x00, 0x40 }, + { 0x00dd13, 0x00, 0x20 }, + { 0x00dd13, 0x00, 0x40 }, + { 0x00dd11, 0x20, 0x20 }, + { 0x00dd88, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd89, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0c, packet_size, 0xff}, + { 0x00dd11, af9035_config.dual_mode << 6, 0x40 }, + { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0d, packet_size, 0xff }, + { 0x80f9a3, 0x00, 0x01 }, + { 0x80f9cd, 0x00, 0x01 }, + { 0x80f99d, 0x00, 0x01 }, + { 0x80f9a4, 0x00, 0x01 }, + }; + + pr_debug("%s: USB speed=%d frame_size=%04x packet_size=%02x\n", + __func__, d->udev->speed, frame_size, packet_size); + + /* init endpoints */ + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = af9035_wr_reg_mask(d, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int ret; + u8 wbuf[1] = { 1 }; + u8 rbuf[4]; + struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf, + sizeof(rbuf), rbuf }; + + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + pr_debug("%s: reply=%02x %02x %02x %02x\n", __func__, + rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3]) + *cold = 0; + else + *cold = 1; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_download_firmware(struct usb_device *udev, + const struct firmware *fw) +{ + u8 *fw_data_ptr = (u8 *) fw->data; + int i, j, len, packets, remainder, ret; + u8 wbuf[1]; + u8 rbuf[4]; + struct fw_header fw_hdr; + struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL }; + struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; + + /* read firmware segment info from beginning of the firmware file */ + fw_hdr.segment_count = *fw_data_ptr++; + pr_debug("%s: fw segment count=%d\n", __func__, fw_hdr.segment_count); + if (fw_hdr.segment_count > SEGMENT_MAX_COUNT) { + pr_debug("%s: too big fw segmen count=%d\n", __func__, + fw_hdr.segment_count); + fw_hdr.segment_count = SEGMENT_MAX_COUNT; + } + for (i = 0; i < fw_hdr.segment_count; i++) { + fw_hdr.segment[i].type = (*fw_data_ptr++); + fw_hdr.segment[i].len = (*fw_data_ptr++) << 24; + fw_hdr.segment[i].len += (*fw_data_ptr++) << 16; + fw_hdr.segment[i].len += (*fw_data_ptr++) << 8; + fw_hdr.segment[i].len += (*fw_data_ptr++) << 0; + pr_debug("%s: fw segment type=%d len=%d\n", __func__, + fw_hdr.segment[i].type, fw_hdr.segment[i].len); + } + + #define FW_PACKET_MAX_DATA 57 /* 63-4-2, packet_size-header-checksum */ + + /* download all segments */ + for (i = 0; i < fw_hdr.segment_count; i++) { + pr_debug("%s: segment type=%d\n", __func__, + fw_hdr.segment[i].type); + if (fw_hdr.segment[i].type == SEGMENT_FW_DL) { + /* download begin packet */ + req.cmd = CMD_FW_DL_BEGIN; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) { + pr_debug("%s: fw dl failed=%d\n", __func__, + ret); + goto err; + } + + packets = fw_hdr.segment[i].len / FW_PACKET_MAX_DATA; + remainder = fw_hdr.segment[i].len % FW_PACKET_MAX_DATA; + len = FW_PACKET_MAX_DATA; + for (j = 0; j <= packets; j++) { + if (j == packets) /* size of the last packet */ + len = remainder; + + req_fw_dl.wlen = len; + req_fw_dl.wbuf = fw_data_ptr; + ret = af9035_ctrl_msg(udev, &req_fw_dl); + if (ret < 0) { + pr_debug("%s: fw dl failed=%d " \ + "segment=%d " \ + "packet=%d\n", + __func__, ret, i, j); + goto err; + } + fw_data_ptr += len; + } + /* download end packet */ + req.cmd = CMD_FW_DL_END; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) { + pr_debug("%s: fw dl failed=%d\n", __func__, + ret); + goto err; + } + } else { + pr_debug("%s: segment type=%d not implemented\n", + __func__, fw_hdr.segment[i].type); + } + } + + /* firmware loaded, request boot */ + req.cmd = CMD_FW_BOOT; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + /* ensure firmware starts */ + wbuf[0] = 1; + ret = af9035_ctrl_msg(udev, &req_fw_ver); + if (ret < 0) + goto err; + + pr_debug("%s: reply=%02x %02x %02x %02x\n", __func__, + rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + + if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { + pr_debug("%s: fw did not run\n", __func__); + ret = -ENODEV; + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* abuse that callback as there is no better one for reading eeprom */ +static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + int ret, i, eeprom_shift = 0; + u8 tmp; + u16 tmp16; + + /* check if there is dual tuners */ + ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + af9035_config.dual_mode = tmp; + pr_debug("%s: dual mode=%d\n", __func__, af9035_config.dual_mode); + + for (i = 0; i < af9035_properties[0].num_adapters; i++) { + /* tuner */ + ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + af9035_af9033_config[i].tuner = tmp; + pr_debug("%s: [%d]tuner=%02x\n", __func__, i, tmp); + + switch (tmp) { + case AF9033_TUNER_TUA9001: + af9035_af9033_config[i].spec_inv = 1; + break; + default: + warn("tuner ID=%20x not supported, please report!", + tmp); + ret = -ENODEV; + goto err; + }; + + /* tuner IF frequency */ + ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + tmp16 = tmp; + + ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_H + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + tmp16 |= tmp << 8; + + pr_debug("%s: [%d]IF=%d\n", __func__, i, tmp16); + + eeprom_shift = 0x10; /* shift for the 2nd tuner params */ + } + + /* get demod clock */ + ret = af9035_rd_reg(d, 0x00d800, &tmp); + if (ret < 0) + goto err; + + tmp = (tmp >> 0) & 0x0f; + + for (i = 0; i < af9035_properties[0].num_adapters; i++) + af9035_af9033_config[i].clock = clock_lut[tmp]; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + + if (adap->id == 0) { + ret = af9035_wr_reg(adap->dev, 0x00417f, + af9035_af9033_config[1].i2c_addr); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(adap->dev, 0x00d81a, + af9035_config.dual_mode); + if (ret < 0) + goto err; + } + + /* attach demodulator */ + adap->fe_adap[0].fe = dvb_attach(af9033_attach, + &af9035_af9033_config[adap->id], &adap->dev->i2c_adap); + if (adap->fe_adap[0].fe == NULL) { + ret = -ENODEV; + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static struct tua9001_config af9035_tua9001_config = { + .i2c_addr = 0x60, +}; + +static int af9035_tuner_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct dvb_frontend *fe; + + switch (af9035_af9033_config[adap->id].tuner) { + case AF9033_TUNER_TUA9001: + /* AF9035 gpiot3 = TUA9001 RESETN + AF9035 gpiot2 = TUA9001 RXEN */ + + /* configure gpiot2 and gpiot2 as output */ + ret = af9035_wr_reg_mask(adap->dev, 0x00d8ec, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8ed, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e8, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e9, 0x01, 0x01); + if (ret < 0) + goto err; + + /* reset tuner */ + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x00, 0x01); + if (ret < 0) + goto err; + + usleep_range(2000, 20000); + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x01, 0x01); + if (ret < 0) + goto err; + + /* activate tuner RX */ + /* TODO: use callback for TUA9001 RXEN */ + ret = af9035_wr_reg_mask(adap->dev, 0x00d8eb, 0x01, 0x01); + if (ret < 0) + goto err; + + /* attach tuner */ + fe = dvb_attach(tua9001_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9035_tua9001_config); + break; + default: + fe = NULL; + } + + if (fe == NULL) { + ret = -ENODEV; + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +enum af9035_id_entry { + AF9035_0CCD_0093, +}; + +static struct usb_device_id af9035_id[] = { + [AF9035_0CCD_0093] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)}, + {}, +}; + +MODULE_DEVICE_TABLE(usb, af9035_id); + +static struct dvb_usb_device_properties af9035_properties[] = { + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9035_download_firmware, + .firmware = "dvb-usb-af9035-01.fw", + .no_reconnect = 1, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9035_frontend_attach, + .tuner_attach = af9035_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + .u = { + .bulk = { + .buffersize = (87 * 188), + } + } + } + } + } + } + }, + + .identify_state = af9035_identify_state, + .read_mac_address = af9035_read_mac_address, + + .i2c_algo = &af9035_i2c_algo, + + .num_device_descs = 1, + .devices = { + { + .name = "TerraTec Cinergy T Stick", + .cold_ids = { + &af9035_id[AF9035_0CCD_0093], + }, + }, + } + }, +}; + +static int af9035_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret, i; + struct dvb_usb_device *d = NULL; + struct usb_device *udev; + bool found; + + pr_debug("%s: interface=%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + /* interface 0 is used by DVB-T receiver and + interface 1 is for remote controller (HID) */ + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return 0; + + /* Dynamic USB ID support. Replaces first device ID with current one. */ + udev = interface_to_usbdev(intf); + + for (i = 0, found = false; i < ARRAY_SIZE(af9035_id) - 1; i++) { + if (af9035_id[i].idVendor == + le16_to_cpu(udev->descriptor.idVendor) && + af9035_id[i].idProduct == + le16_to_cpu(udev->descriptor.idProduct)) { + found = true; + break; + } + } + + if (!found) { + pr_debug("%s: using dynamic ID %04x:%04x\n", __func__, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + af9035_properties[0].devices[0].cold_ids[0]->idVendor = + le16_to_cpu(udev->descriptor.idVendor); + af9035_properties[0].devices[0].cold_ids[0]->idProduct = + le16_to_cpu(udev->descriptor.idProduct); + } + + + for (i = 0; i < af9035_properties_count; i++) { + ret = dvb_usb_device_init(intf, &af9035_properties[i], + THIS_MODULE, &d, adapter_nr); + + if (ret == -ENODEV) + continue; + else + break; + } + + if (ret < 0) + goto err; + + if (d) { + ret = af9035_init(d); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver af9035_usb_driver = { + .name = "dvb_usb_af9035", + .probe = af9035_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = af9035_id, +}; + +/* module stuff */ +static int __init af9035_usb_module_init(void) +{ + int ret; + + ret = usb_register(&af9035_usb_driver); + if (ret < 0) + goto err; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static void __exit af9035_usb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&af9035_usb_driver); +} + +module_init(af9035_usb_module_init); +module_exit(af9035_usb_module_exit); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Afatech AF9035 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h new file mode 100644 index 000000000000..b9af9c81e44c --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9035.h @@ -0,0 +1,102 @@ +/* + * Afatech AF9035 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AF9035_H +#define AF9035_H + +#include "dvb-usb.h" + +struct reg_val { + u32 reg; + u8 val; +}; + +struct reg_val_mask { + u32 reg; + u8 val; + u8 mask; +}; + +struct usb_req { + u8 cmd; + u8 mbox; + u8 wlen; + u8 *wbuf; + u8 rlen; + u8 *rbuf; +}; + +struct config { + bool dual_mode; +}; + +struct fw_segment { +#define SEGMENT_FW_DL 0 +#define SEGMENT_ROM_COPY 1 +#define SEGMENT_DIRECT_CMD 2 + u8 type; + u32 len; +}; + +struct fw_header { +#define SEGMENT_MAX_COUNT 6 + u8 segment_count; + struct fw_segment segment[SEGMENT_MAX_COUNT]; +}; + +u32 clock_lut[] = { + 20480000, /* FPGA */ + 16384000, /* 16.38 MHz */ + 20480000, /* 20.48 MHz */ + 36000000, /* 36.00 MHz */ + 30000000, /* 30.00 MHz */ + 26000000, /* 26.00 MHz */ + 28000000, /* 28.00 MHz */ + 32000000, /* 32.00 MHz */ + 34000000, /* 34.00 MHz */ + 24000000, /* 24.00 MHz */ + 22000000, /* 22.00 MHz */ + 12000000, /* 12.00 MHz */ +}; + +/* EEPROM locations */ +#define EEPROM_IR_MODE 0x430d +#define EEPROM_DUAL_MODE 0x4326 +#define EEPROM_IR_TYPE 0x4329 +#define EEPROM_1_IFFREQ_L 0x432d +#define EEPROM_1_IFFREQ_H 0x432e +#define EEPROM_1_TUNER_ID 0x4331 +#define EEPROM_2_IFFREQ_L 0x433d +#define EEPROM_2_IFFREQ_H 0x433e +#define EEPROM_2_TUNER_ID 0x4341 + +/* USB commands */ +#define CMD_MEM_RD 0x00 +#define CMD_MEM_WR 0x01 +#define CMD_I2C_RD 0x02 +#define CMD_I2C_WR 0x03 +#define CMD_FW_DL 0x21 +#define CMD_FW_QUERYINFO 0x22 +#define CMD_FW_BOOT 0x23 +#define CMD_FW_DL_BEGIN 0x24 +#define CMD_FW_DL_END 0x25 + +#endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 397d8f232731..35f981352b13 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -152,6 +152,7 @@ #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 #define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069 +#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093 #define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097 #define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099 #define USB_PID_TWINHAN_VP7041_COLD 0x3201 -- cgit v1.2.3 From 5a9abae497f8c9e08c919d80fefcaeaf1cf9ab73 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 30 Mar 2012 17:15:16 -0300 Subject: [media] af9035: enhancement for unknown tuner ID handling * do not attach frontend when tuner is not supported * fix unkown tuner ID log write format * add prefix for dvb-usb log writings Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 10 +++++++--- drivers/media/dvb/dvb-usb/af9035.h | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 6606cf52ea98..b8cd27ae161b 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -489,10 +489,9 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) af9035_af9033_config[i].spec_inv = 1; break; default: - warn("tuner ID=%20x not supported, please report!", + af9035_config.hw_not_supported = true; + warn("tuner ID=%02x not supported, please report!", tmp); - ret = -ENODEV; - goto err; }; /* tuner IF frequency */ @@ -535,6 +534,11 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) { int ret; + if (af9035_config.hw_not_supported) { + ret = -ENODEV; + goto err; + } + if (adap->id == 0) { ret = af9035_wr_reg(adap->dev, 0x00417f, af9035_af9033_config[1].i2c_addr); diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h index b9af9c81e44c..0df24cdf2504 100644 --- a/drivers/media/dvb/dvb-usb/af9035.h +++ b/drivers/media/dvb/dvb-usb/af9035.h @@ -22,6 +22,9 @@ #ifndef AF9035_H #define AF9035_H +/* prefix for dvb-usb log writings */ +#define DVB_USB_LOG_PREFIX "af9035" + #include "dvb-usb.h" struct reg_val { @@ -46,6 +49,7 @@ struct usb_req { struct config { bool dual_mode; + bool hw_not_supported; }; struct fw_segment { -- cgit v1.2.3 From 77c5ff2d8992e9c4b8ed722f391e92aece5c14cc Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 1 Apr 2012 01:32:23 -0300 Subject: [media] af9035: reimplement firmware downloader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Big thanks to Daniel Glöckner for revealing firmware structure on Linux Media mailing list. Signed-off-by: Antti Palosaari Cc: Daniel Glöckner Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 136 ++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 71 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index b8cd27ae161b..01dee02ff280 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -355,80 +355,74 @@ err: static int af9035_download_firmware(struct usb_device *udev, const struct firmware *fw) { - u8 *fw_data_ptr = (u8 *) fw->data; - int i, j, len, packets, remainder, ret; + int ret, i, j, len; u8 wbuf[1]; u8 rbuf[4]; - struct fw_header fw_hdr; struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL }; struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; + u8 hdr_core; + u16 hdr_addr, hdr_data_len, hdr_checksum; + #define MAX_DATA 57 + #define HDR_SIZE 7 + + /* + * Thanks to Daniel Glöckner about that info! + * + * byte 0: MCS 51 core + * There are two inside the AF9035 (1=Link and 2=OFDM) with separate + * address spaces + * byte 1-2: Big endian destination address + * byte 3-4: Big endian number of data bytes following the header + * byte 5-6: Big endian header checksum, apparently ignored by the chip + * Calculated as ~(h[0]*256+h[1]+h[2]*256+h[3]+h[4]*256) + */ + + for (i = fw->size; i > HDR_SIZE;) { + hdr_core = fw->data[fw->size - i + 0]; + hdr_addr = fw->data[fw->size - i + 1] << 8; + hdr_addr |= fw->data[fw->size - i + 2] << 0; + hdr_data_len = fw->data[fw->size - i + 3] << 8; + hdr_data_len |= fw->data[fw->size - i + 4] << 0; + hdr_checksum = fw->data[fw->size - i + 5] << 8; + hdr_checksum |= fw->data[fw->size - i + 6] << 0; + + pr_debug("%s: core=%d addr=%04x data_len=%d checksum=%04x\n", + __func__, hdr_core, hdr_addr, hdr_data_len, + hdr_checksum); + + if (((hdr_core != 1) && (hdr_core != 2)) || + (hdr_data_len > i)) { + pr_debug("%s: bad firmware\n", __func__); + break; + } - /* read firmware segment info from beginning of the firmware file */ - fw_hdr.segment_count = *fw_data_ptr++; - pr_debug("%s: fw segment count=%d\n", __func__, fw_hdr.segment_count); - if (fw_hdr.segment_count > SEGMENT_MAX_COUNT) { - pr_debug("%s: too big fw segmen count=%d\n", __func__, - fw_hdr.segment_count); - fw_hdr.segment_count = SEGMENT_MAX_COUNT; - } - for (i = 0; i < fw_hdr.segment_count; i++) { - fw_hdr.segment[i].type = (*fw_data_ptr++); - fw_hdr.segment[i].len = (*fw_data_ptr++) << 24; - fw_hdr.segment[i].len += (*fw_data_ptr++) << 16; - fw_hdr.segment[i].len += (*fw_data_ptr++) << 8; - fw_hdr.segment[i].len += (*fw_data_ptr++) << 0; - pr_debug("%s: fw segment type=%d len=%d\n", __func__, - fw_hdr.segment[i].type, fw_hdr.segment[i].len); - } - - #define FW_PACKET_MAX_DATA 57 /* 63-4-2, packet_size-header-checksum */ - - /* download all segments */ - for (i = 0; i < fw_hdr.segment_count; i++) { - pr_debug("%s: segment type=%d\n", __func__, - fw_hdr.segment[i].type); - if (fw_hdr.segment[i].type == SEGMENT_FW_DL) { - /* download begin packet */ - req.cmd = CMD_FW_DL_BEGIN; - ret = af9035_ctrl_msg(udev, &req); - if (ret < 0) { - pr_debug("%s: fw dl failed=%d\n", __func__, - ret); - goto err; - } - - packets = fw_hdr.segment[i].len / FW_PACKET_MAX_DATA; - remainder = fw_hdr.segment[i].len % FW_PACKET_MAX_DATA; - len = FW_PACKET_MAX_DATA; - for (j = 0; j <= packets; j++) { - if (j == packets) /* size of the last packet */ - len = remainder; - - req_fw_dl.wlen = len; - req_fw_dl.wbuf = fw_data_ptr; - ret = af9035_ctrl_msg(udev, &req_fw_dl); - if (ret < 0) { - pr_debug("%s: fw dl failed=%d " \ - "segment=%d " \ - "packet=%d\n", - __func__, ret, i, j); - goto err; - } - fw_data_ptr += len; - } - /* download end packet */ - req.cmd = CMD_FW_DL_END; - ret = af9035_ctrl_msg(udev, &req); - if (ret < 0) { - pr_debug("%s: fw dl failed=%d\n", __func__, - ret); + /* download begin packet */ + req.cmd = CMD_FW_DL_BEGIN; + ret = af9035_ctrl_msg(udev, &req); + + /* download firmware packet(s) */ + for (j = HDR_SIZE + hdr_data_len; j > 0; j -= MAX_DATA) { + len = j; + if (len > MAX_DATA) + len = MAX_DATA; + req_fw_dl.wlen = len; + req_fw_dl.wbuf = (u8 *) &fw->data[fw->size - i + + HDR_SIZE + hdr_data_len - j]; + ret = af9035_ctrl_msg(udev, &req_fw_dl); + if (ret < 0) goto err; - } - } else { - pr_debug("%s: segment type=%d not implemented\n", - __func__, fw_hdr.segment[i].type); } + + /* download end packet */ + req.cmd = CMD_FW_DL_END; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + i -= hdr_data_len + HDR_SIZE; + + pr_debug("%s: data uploaded=%lu\n", __func__, fw->size - i); } /* firmware loaded, request boot */ @@ -443,15 +437,15 @@ static int af9035_download_firmware(struct usb_device *udev, if (ret < 0) goto err; - pr_debug("%s: reply=%02x %02x %02x %02x\n", __func__, - rbuf[0], rbuf[1], rbuf[2], rbuf[3]); - if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { - pr_debug("%s: fw did not run\n", __func__); + info("firmware did not run"); ret = -ENODEV; goto err; } + info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2], + rbuf[3]); + return 0; err: @@ -654,7 +648,7 @@ static struct dvb_usb_device_properties af9035_properties[] = { .usb_ctrl = DEVICE_SPECIFIC, .download_firmware = af9035_download_firmware, - .firmware = "dvb-usb-af9035-01.fw", + .firmware = "dvb-usb-af9035-02.fw", .no_reconnect = 1, .num_adapters = 1, -- cgit v1.2.3 From 41d44a815a68a2618805c1d670b4ff93091a99d8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 1 Apr 2012 11:06:23 -0300 Subject: [media] af9035: add missing error check Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 01dee02ff280..dc0a49ac664a 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -400,6 +400,8 @@ static int af9035_download_firmware(struct usb_device *udev, /* download begin packet */ req.cmd = CMD_FW_DL_BEGIN; ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; /* download firmware packet(s) */ for (j = HDR_SIZE + hdr_data_len; j > 0; j -= MAX_DATA) { -- cgit v1.2.3 From 3a871ca270545194887ddae726b51f1e2bf45f32 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 1 Apr 2012 11:14:59 -0300 Subject: [media] af9033: correct debug print Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/af9033.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c index 161bbe5f2e3f..9ade510f9dfe 100644 --- a/drivers/media/dvb/frontends/af9033.c +++ b/drivers/media/dvb/frontends/af9033.c @@ -342,7 +342,7 @@ static int af9033_sleep(struct dvb_frontend *fe) usleep_range(200, 10000); } - pr_debug("%s: loop=%d", __func__, i); + pr_debug("%s: loop=%d\n", __func__, i); if (i == 0) { ret = -ETIMEDOUT; -- cgit v1.2.3 From e898ef627214627883ed950ef3da5fa5788beb41 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 1 Apr 2012 12:50:02 -0300 Subject: [media] af9033: implement .read_snr() Returns values as 0.1 dB resolution as preferred nowadays. Actual resolution is 1 dB. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/af9033.c | 49 +++++++++++++++- drivers/media/dvb/frontends/af9033_priv.h | 98 +++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c index 9ade510f9dfe..40ef4b1faebc 100644 --- a/drivers/media/dvb/frontends/af9033.c +++ b/drivers/media/dvb/frontends/af9033.c @@ -540,9 +540,56 @@ err: static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr) { - *snr = 0; + struct af9033_state *state = fe->demodulator_priv; + int ret, i, len; + u8 buf[3], tmp; + u32 snr_val; + const struct val_snr *uninitialized_var(snr_lut); + + /* read value */ + ret = af9033_rd_regs(state, 0x80002c, buf, 3); + if (ret < 0) + goto err; + + snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + /* read current modulation */ + ret = af9033_rd_reg(state, 0x80f903, &tmp); + if (ret < 0) + goto err; + + switch ((tmp >> 0) & 3) { + case 0: + len = ARRAY_SIZE(qpsk_snr_lut); + snr_lut = qpsk_snr_lut; + break; + case 1: + len = ARRAY_SIZE(qam16_snr_lut); + snr_lut = qam16_snr_lut; + break; + case 2: + len = ARRAY_SIZE(qam64_snr_lut); + snr_lut = qam64_snr_lut; + break; + default: + goto err; + } + + for (i = 0; i < len; i++) { + tmp = snr_lut[i].snr; + + if (snr_val < snr_lut[i].val) + break; + } + + *snr = tmp * 10; /* dB/10 */ return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; } static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength) diff --git a/drivers/media/dvb/frontends/af9033_priv.h b/drivers/media/dvb/frontends/af9033_priv.h index 2bf579d89a83..f0096298d2ac 100644 --- a/drivers/media/dvb/frontends/af9033_priv.h +++ b/drivers/media/dvb/frontends/af9033_priv.h @@ -47,6 +47,11 @@ struct clock_adc { u32 adc; }; +struct val_snr { + u32 val; + u8 snr; +}; + /* Xtal clock vs. ADC clock lookup table */ static const struct clock_adc clock_adc_lut[] = { { 16384000, 20480000 }, @@ -85,6 +90,99 @@ static const struct coeff coeff_lut[] = { }, }; +/* QPSK SNR lookup table */ +static const struct val_snr qpsk_snr_lut[] = { + { 0x0b4771, 0 }, + { 0x0c1aed, 1 }, + { 0x0d0d27, 2 }, + { 0x0e4d19, 3 }, + { 0x0e5da8, 4 }, + { 0x107097, 5 }, + { 0x116975, 6 }, + { 0x1252d9, 7 }, + { 0x131fa4, 8 }, + { 0x13d5e1, 9 }, + { 0x148e53, 10 }, + { 0x15358b, 11 }, + { 0x15dd29, 12 }, + { 0x168112, 13 }, + { 0x170b61, 14 }, + { 0x17a532, 15 }, + { 0x180f94, 16 }, + { 0x186ed2, 17 }, + { 0x18b271, 18 }, + { 0x18e118, 19 }, + { 0x18ff4b, 20 }, + { 0x190af1, 21 }, + { 0x191451, 22 }, + { 0xffffff, 23 }, +}; + +/* QAM16 SNR lookup table */ +static const struct val_snr qam16_snr_lut[] = { + { 0x04f0d5, 0 }, + { 0x05387a, 1 }, + { 0x0573a4, 2 }, + { 0x05a99e, 3 }, + { 0x05cc80, 4 }, + { 0x05eb62, 5 }, + { 0x05fecf, 6 }, + { 0x060b80, 7 }, + { 0x062501, 8 }, + { 0x064865, 9 }, + { 0x069604, 10 }, + { 0x06f356, 11 }, + { 0x07706a, 12 }, + { 0x0804d3, 13 }, + { 0x089d1a, 14 }, + { 0x093e3d, 15 }, + { 0x09e35d, 16 }, + { 0x0a7c3c, 17 }, + { 0x0afaf8, 18 }, + { 0x0b719d, 19 }, + { 0x0bda6a, 20 }, + { 0x0c0c75, 21 }, + { 0x0c3f7d, 22 }, + { 0x0c5e62, 23 }, + { 0x0c6c31, 24 }, + { 0x0c7925, 25 }, + { 0xffffff, 26 }, +}; + +/* QAM64 SNR lookup table */ +static const struct val_snr qam64_snr_lut[] = { + { 0x0256d0, 0 }, + { 0x027a65, 1 }, + { 0x029873, 2 }, + { 0x02b7fe, 3 }, + { 0x02cf1e, 4 }, + { 0x02e234, 5 }, + { 0x02f409, 6 }, + { 0x030046, 7 }, + { 0x030844, 8 }, + { 0x030a02, 9 }, + { 0x030cde, 10 }, + { 0x031031, 11 }, + { 0x03144c, 12 }, + { 0x0315dd, 13 }, + { 0x031920, 14 }, + { 0x0322d0, 15 }, + { 0x0339fc, 16 }, + { 0x0364a1, 17 }, + { 0x038bcc, 18 }, + { 0x03c7d3, 19 }, + { 0x0408cc, 20 }, + { 0x043bed, 21 }, + { 0x048061, 22 }, + { 0x04be95, 23 }, + { 0x04fa7d, 24 }, + { 0x052405, 25 }, + { 0x05570d, 26 }, + { 0x059feb, 27 }, + { 0x05bf38, 28 }, + { 0xffffff, 29 }, +}; + static const struct reg_val ofsm_init[] = { { 0x800051, 0x01 }, { 0x800070, 0x0a }, -- cgit v1.2.3 From 8e8a5ac763d057fae50f2f7cc4fc830c5f815d26 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 1 Apr 2012 14:13:36 -0300 Subject: [media] af9035: add log writing if unsupported Xtal freq is given Supports currently only 12 MHz Xtals. It is better to print log and not to attach frontend in that case. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/af9033.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c index 40ef4b1faebc..277255481607 100644 --- a/drivers/media/dvb/frontends/af9033.c +++ b/drivers/media/dvb/frontends/af9033.c @@ -667,6 +667,13 @@ struct dvb_frontend *af9033_attach(const struct af9033_config *config, state->i2c = i2c; memcpy(&state->cfg, config, sizeof(struct af9033_config)); + if (state->cfg.clock != 12000000) { + printk(KERN_INFO "af9033: unsupported clock=%d, only " \ + "12000000 Hz is supported currently\n", + state->cfg.clock); + goto err; + } + /* firmware version */ ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4); if (ret < 0) -- cgit v1.2.3 From 812fe6d9426a23ad78055f1fa955acef9bea9a93 Mon Sep 17 00:00:00 2001 From: Hans-Frieder Vogt Date: Sun, 1 Apr 2012 14:11:29 -0300 Subject: [media] af9035: i2c read fix Enable i2c read requests. I2C read fix (necessary e.g. for mxl5007t tuner, because it sends a 2 bytes for a read request, thus msg[0].len != msg[1].len). Signed-off-by: Hans-Frieder Vogt Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index dc0a49ac664a..6a83120afcd6 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -209,24 +209,15 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, msg[1].len); } else { /* I2C */ -#if 0 - /* - * FIXME: Keep that code. It should work but as it is - * not tested I left it disabled and return -EOPNOTSUPP - * for the sure. - */ u8 buf[4 + msg[0].len]; struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), buf, msg[1].len, msg[1].buf }; - buf[0] = msg[0].len; + buf[0] = msg[1].len; buf[1] = msg[0].addr << 1; buf[2] = 0x01; buf[3] = 0x00; memcpy(&buf[4], msg[0].buf, msg[0].len); ret = af9035_ctrl_msg(d->udev, &req); -#endif - pr_debug("%s: I2C operation not supported\n", __func__); - ret = -EOPNOTSUPP; } } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) { if (msg[0].len > 40) { -- cgit v1.2.3 From b1a9599a0d228d6555ad9e5c1fe0b0dfac7c5ccb Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Sun, 1 Apr 2012 16:33:48 -0300 Subject: [media] af9035: Add USB read checksumming This adds USB message read checksumming to protect against device and bus errors. It also adds a read length check to avoid returning garbage from the buffer, if the device truncated the message. Signed-off-by: Michael Buesch Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 45 +++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 6a83120afcd6..92d27aa80800 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -36,6 +36,22 @@ static struct af9033_config af9035_af9033_config[] = { } }; +static u16 af9035_checksum(const u8 *buf, size_t len) +{ + size_t i; + u16 checksum = 0; + + for (i = 1; i < len; i++) { + if (i % 2) + checksum += buf[i] << 8; + else + checksum += buf[i]; + } + checksum = ~checksum; + + return checksum; +} + static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) { #define BUF_LEN 63 @@ -44,11 +60,11 @@ static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) #define CHECKSUM_LEN 2 #define USB_TIMEOUT 2000 - int ret, i, act_len; + int ret, act_len; u8 buf[BUF_LEN]; u32 msg_len; static u8 seq; /* packet sequence number */ - u16 checksum = 0; + u16 checksum, tmpsum; /* buffer overflow check */ if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || @@ -69,14 +85,7 @@ static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) memcpy(&buf[4], req->wbuf, req->wlen); /* calc and add checksum */ - for (i = 1; i < buf[0]-1; i++) { - if (i % 2) - checksum += buf[i] << 8; - else - checksum += buf[i]; - } - checksum = ~checksum; - + checksum = af9035_checksum(buf, buf[0] - 1); buf[buf[0]-1] = (checksum >> 8); buf[buf[0]-0] = (checksum & 0xff); @@ -106,7 +115,23 @@ static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) ret = -EIO; goto err_mutex_unlock; } + if (act_len != msg_len) { + err("recv bulk message truncated (%d != %u)\n", + act_len, (unsigned int)msg_len); + ret = -EIO; + goto err_mutex_unlock; + } + /* verify checksum */ + checksum = af9035_checksum(buf, act_len - 2); + tmpsum = (buf[act_len - 2] << 8) | buf[act_len - 1]; + if (tmpsum != checksum) { + err("%s: command=%02X checksum mismatch (%04X != %04X)\n", + __func__, req->cmd, + (unsigned int)tmpsum, (unsigned int)checksum); + ret = -EIO; + goto err_mutex_unlock; + } /* check status */ if (buf[2]) { pr_debug("%s: command=%02x failed fw error=%d\n", __func__, -- cgit v1.2.3 From 6ec12988c022bee4af28a5d3941a01f9e78a96e9 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Mon, 2 Apr 2012 20:32:58 -0300 Subject: [media] af9035: fix warning On a 32 bit system: af9035.c: In function 'af9035_download_firmware': af9035.c:446:3: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'unsigned int' [-Wformat] %zu avoids any warning on both 32 and 64 bit systems. Signed-off-by: Gianluca Gennari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 92d27aa80800..d5c1fa7947ac 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -440,7 +440,7 @@ static int af9035_download_firmware(struct usb_device *udev, i -= hdr_data_len + HDR_SIZE; - pr_debug("%s: data uploaded=%lu\n", __func__, fw->size - i); + pr_debug("%s: data uploaded=%zu\n", __func__, fw->size - i); } /* firmware loaded, request boot */ -- cgit v1.2.3 From eea977ed63c16888a87acd12958966638ac4fb3a Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Mon, 2 Apr 2012 12:14:32 -0300 Subject: [media] Add fc0011 tuner driver This adds support for the Fitipower fc0011 DVB-t tuner. Signed-off-by: Michael Buesch Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 + drivers/media/common/tuners/Kconfig | 7 + drivers/media/common/tuners/Makefile | 1 + drivers/media/common/tuners/fc0011.c | 524 +++++++++++++++++++++++++++++++++++ drivers/media/common/tuners/fc0011.h | 41 +++ 5 files changed, 580 insertions(+) create mode 100644 drivers/media/common/tuners/fc0011.c create mode 100644 drivers/media/common/tuners/fc0011.h diff --git a/MAINTAINERS b/MAINTAINERS index 699546996559..9e05ceb2921f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2692,6 +2692,13 @@ S: Maintained F: Documentation/hwmon/f71805f F: drivers/hwmon/f71805f.c +FC0011 TUNER DRIVER +M: Michael Buesch +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/common/tuners/fc0011.h +F: drivers/media/common/tuners/fc0011.c + FANOTIFY M: Eric Paris S: Maintained diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index ae8ebcfa6fa2..0fd15d925e15 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -204,6 +204,13 @@ config MEDIA_TUNER_TDA18218 help NXP TDA18218 silicon tuner driver. +config MEDIA_TUNER_FC0011 + tristate "Fitipower FC0011 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0011 silicon tuner driver. + config MEDIA_TUNER_TDA18212 tristate "NXP TDA18212 silicon tuner" depends on VIDEO_MEDIA && I2C diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 6c3040501c45..64ee06fa83f1 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o +obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/common/tuners/fc0011.c new file mode 100644 index 000000000000..7842a4eee3d7 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.c @@ -0,0 +1,524 @@ +/* + * Fitipower FC0011 tuner driver + * + * Copyright (C) 2012 Michael Buesch + * + * Derived from FC0012 tuner driver: + * Copyright (C) 2012 Hans-Frieder Vogt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "fc0011.h" + + +/* Tuner registers */ +enum { + FC11_REG_0, + FC11_REG_FA, /* FA */ + FC11_REG_FP, /* FP */ + FC11_REG_XINHI, /* XIN high 8 bit */ + FC11_REG_XINLO, /* XIN low 8 bit */ + FC11_REG_VCO, /* VCO */ + FC11_REG_VCOSEL, /* VCO select */ + FC11_REG_7, /* Unknown tuner reg 7 */ + FC11_REG_8, /* Unknown tuner reg 8 */ + FC11_REG_9, + FC11_REG_10, /* Unknown tuner reg 10 */ + FC11_REG_11, /* Unknown tuner reg 11 */ + FC11_REG_12, + FC11_REG_RCCAL, /* RC calibrate */ + FC11_REG_VCOCAL, /* VCO calibrate */ + FC11_REG_15, + FC11_REG_16, /* Unknown tuner reg 16 */ + FC11_REG_17, + + FC11_NR_REGS, /* Number of registers */ +}; + +enum FC11_REG_VCOSEL_bits { + FC11_VCOSEL_2 = 0x08, /* VCO select 2 */ + FC11_VCOSEL_1 = 0x10, /* VCO select 1 */ + FC11_VCOSEL_CLKOUT = 0x20, /* Fix clock out */ + FC11_VCOSEL_BW7M = 0x40, /* 7MHz bw */ + FC11_VCOSEL_BW6M = 0x80, /* 6MHz bw */ +}; + +enum FC11_REG_RCCAL_bits { + FC11_RCCAL_FORCE = 0x10, /* force */ +}; + +enum FC11_REG_VCOCAL_bits { + FC11_VCOCAL_RUN = 0, /* VCO calibration run */ + FC11_VCOCAL_VALUEMASK = 0x3F, /* VCO calibration value mask */ + FC11_VCOCAL_OK = 0x40, /* VCO calibration Ok */ + FC11_VCOCAL_RESET = 0x80, /* VCO calibration reset */ +}; + + +struct fc0011_priv { + struct i2c_adapter *i2c; + u8 addr; + + u32 frequency; + u32 bandwidth; +}; + + +static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { .addr = priv->addr, + .flags = 0, .buf = buf, .len = 2 }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + dev_err(&priv->i2c->dev, + "I2C write reg failed, reg: %02x, val: %02x\n", + reg, val); + return -EIO; + } + + return 0; +} + +static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val) +{ + u8 dummy; + struct i2c_msg msg[2] = { + { .addr = priv->addr, + .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, + .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + dev_err(&priv->i2c->dev, + "I2C read failed, reg: %02x\n", reg); + return -EIO; + } + + return 0; +} + +static int fc0011_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int fc0011_init(struct dvb_frontend *fe) +{ + struct fc0011_priv *priv = fe->tuner_priv; + int err; + + if (WARN_ON(!fe->callback)) + return -EINVAL; + + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_POWER, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Power-on callback failed\n"); + return err; + } + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Reset callback failed\n"); + return err; + } + + return 0; +} + +/* Initiate VCO calibration */ +static int fc0011_vcocal_trigger(struct fc0011_priv *priv) +{ + int err; + + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET); + if (err) + return err; + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); + if (err) + return err; + + return 0; +} + +/* Read VCO calibration value */ +static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) +{ + int err; + + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); + if (err) + return err; + msleep(10); + err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); + if (err) + return err; + + return 0; +} + +static int fc0011_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct fc0011_priv *priv = fe->tuner_priv; + int err; + unsigned int i, vco_retries; + u32 freq = p->frequency / 1000; + u32 bandwidth = p->bandwidth_hz / 1000; + u32 fvco, xin, xdiv, xdivr; + u16 frac; + u8 fa, fp, vco_sel, vco_cal; + u8 regs[FC11_NR_REGS] = { }; + + regs[FC11_REG_7] = 0x0F; + regs[FC11_REG_8] = 0x3E; + regs[FC11_REG_10] = 0xB8; + regs[FC11_REG_11] = 0x80; + regs[FC11_REG_RCCAL] = 0x04; + err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); + err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); + err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); + err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); + err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return -EIO; + + /* Set VCO freq and VCO div */ + if (freq < 54000) { + fvco = freq * 64; + regs[FC11_REG_VCO] = 0x82; + } else if (freq < 108000) { + fvco = freq * 32; + regs[FC11_REG_VCO] = 0x42; + } else if (freq < 216000) { + fvco = freq * 16; + regs[FC11_REG_VCO] = 0x22; + } else if (freq < 432000) { + fvco = freq * 8; + regs[FC11_REG_VCO] = 0x12; + } else { + fvco = freq * 4; + regs[FC11_REG_VCO] = 0x0A; + } + + /* Calc XIN. The PLL reference frequency is 18 MHz. */ + xdiv = fvco / 18000; + frac = fvco - xdiv * 18000; + frac = (frac << 15) / 18000; + if (frac >= 16384) + frac += 32786; + if (!frac) + xin = 0; + else if (frac < 511) + xin = 512; + else if (frac < 65026) + xin = frac; + else + xin = 65024; + regs[FC11_REG_XINHI] = xin >> 8; + regs[FC11_REG_XINLO] = xin; + + /* Calc FP and FA */ + xdivr = xdiv; + if (fvco - xdiv * 18000 >= 9000) + xdivr += 1; /* round */ + fp = xdivr / 8; + fa = xdivr - fp * 8; + if (fa < 2) { + fp -= 1; + fa += 8; + } + if (fp > 0x1F) { + fp &= 0x1F; + fa &= 0xF; + } + if (fa >= fp) { + dev_warn(&priv->i2c->dev, + "fa %02X >= fp %02X, but trying to continue\n", + (unsigned int)(u8)fa, (unsigned int)(u8)fp); + } + regs[FC11_REG_FA] = fa; + regs[FC11_REG_FP] = fp; + + /* Select bandwidth */ + switch (bandwidth) { + case 8000: + break; + case 7000: + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M; + break; + default: + dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. " + "Using 6000 kHz.\n", + bandwidth); + bandwidth = 6000; + /* fallthrough */ + case 6000: + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M; + break; + } + + /* Pre VCO select */ + if (fvco < 2320000) { + vco_sel = 0; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + } else if (fvco < 3080000) { + vco_sel = 1; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + } else { + vco_sel = 2; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + } + + /* Fix for low freqs */ + if (freq < 45000) { + regs[FC11_REG_FA] = 0x6; + regs[FC11_REG_FP] = 0x11; + } + + /* Clock out fix */ + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT; + + /* Write the cached registers */ + for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) { + err = fc0011_writereg(priv, i, regs[i]); + if (err) + return err; + } + + /* VCO calibration */ + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries = 0; + while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 6) { + /* Reset the tuner and try again */ + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Failed to reset tuner\n"); + return err; + } + /* Reinit tuner config */ + err = 0; + for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) + err |= fc0011_writereg(priv, i, regs[i]); + err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); + err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); + err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); + err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); + err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return -EIO; + /* VCO calibration */ + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries++; + } + if (!(vco_cal & FC11_VCOCAL_OK)) { + dev_err(&priv->i2c->dev, + "Failed to read VCO calibration value (got %02X)\n", + (unsigned int)vco_cal); + return -EIO; + } + vco_cal &= FC11_VCOCAL_VALUEMASK; + + switch (vco_sel) { + case 0: + if (vco_cal < 8) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } + break; + case 1: + if (vco_cal < 5) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else if (vco_cal <= 48) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } + break; + case 2: + if (vco_cal > 53) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } + break; + } + err = fc0011_vcocal_read(priv, NULL); + if (err) + return err; + msleep(10); + + err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]); + if (err) + return err; + regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE; + err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return err; + err = fc0011_writereg(priv, FC11_REG_16, 0xB); + if (err) + return err; + + dev_dbg(&priv->i2c->dev, "Tuned to " + "fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X " + "vcocal=%02X(%u) bw=%u\n", + (unsigned int)regs[FC11_REG_FA], + (unsigned int)regs[FC11_REG_FP], + (unsigned int)regs[FC11_REG_XINHI], + (unsigned int)regs[FC11_REG_XINLO], + (unsigned int)regs[FC11_REG_VCO], + (unsigned int)regs[FC11_REG_VCOSEL], + (unsigned int)vco_cal, vco_retries, + (unsigned int)bandwidth); + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + + return 0; +} + +static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0011_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency; + + return 0; +} + +static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; + + return 0; +} + +static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0011_priv *priv = fe->tuner_priv; + + *bandwidth = priv->bandwidth; + + return 0; +} + +static const struct dvb_tuner_ops fc0011_tuner_ops = { + .info = { + .name = "Fitipower FC0011", + + .frequency_min = 45000000, + .frequency_max = 1000000000, + }, + + .release = fc0011_release, + .init = fc0011_init, + + .set_params = fc0011_set_params, + + .get_frequency = fc0011_get_frequency, + .get_if_frequency = fc0011_get_if_frequency, + .get_bandwidth = fc0011_get_bandwidth, +}; + +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config) +{ + struct fc0011_priv *priv; + + priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->i2c = i2c; + priv->addr = config->i2c_address; + + fe->tuner_priv = priv; + fe->ops.tuner_ops = fc0011_tuner_ops; + + dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n"); + + return fe; +} +EXPORT_SYMBOL(fc0011_attach); + +MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver"); +MODULE_AUTHOR("Michael Buesch "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/fc0011.h b/drivers/media/common/tuners/fc0011.h new file mode 100644 index 000000000000..0ee581f122d2 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.h @@ -0,0 +1,41 @@ +#ifndef LINUX_FC0011_H_ +#define LINUX_FC0011_H_ + +#include "dvb_frontend.h" + + +/** struct fc0011_config - fc0011 hardware config + * + * @i2c_address: I2C bus address. + */ +struct fc0011_config { + u8 i2c_address; +}; + +/** enum fc0011_fe_callback_commands - Frontend callbacks + * + * @FC0011_FE_CALLBACK_POWER: Power on tuner hardware. + * @FC0011_FE_CALLBACK_RESET: Request a tuner reset. + */ +enum fc0011_fe_callback_commands { + FC0011_FE_CALLBACK_POWER, + FC0011_FE_CALLBACK_RESET, +}; + +#if defined(CONFIG_MEDIA_TUNER_FC0011) ||\ + defined(CONFIG_MEDIA_TUNER_FC0011_MODULE) +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config); +#else +static inline +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config) +{ + dev_err(&i2c->dev, "fc0011 driver disabled in Kconfig\n"); + return NULL; +} +#endif + +#endif /* LINUX_FC0011_H_ */ -- cgit v1.2.3 From ffc501f654f566bf6a9e567f75c302d93f9e22e8 Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Mon, 2 Apr 2012 12:18:36 -0300 Subject: [media] af9035: Add fc0011 tuner support This adds Fitipower fc0011 tuner support to the af9035 driver. Signed-off-by: Michael Buesch Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/Kconfig | 1 + drivers/media/dvb/dvb-usb/af9035.c | 88 +++++++++++++++++++++++++++++++ drivers/media/dvb/frontends/af9033.c | 4 ++ drivers/media/dvb/frontends/af9033_priv.h | 61 +++++++++++++++++++++ 4 files changed, 154 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index df229fcc8f07..cf57c0659d5b 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -428,6 +428,7 @@ config DVB_USB_AF9035 depends on DVB_USB select DVB_AF9033 select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Afatech AF9035 based DVB USB receiver. diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index d5c1fa7947ac..15dcb9bc0742 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -22,6 +22,7 @@ #include "af9035.h" #include "af9033.h" #include "tua9001.h" +#include "fc0011.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static DEFINE_MUTEX(af9035_usb_mutex); @@ -498,6 +499,7 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) switch (tmp) { case AF9033_TUNER_TUA9001: + case AF9033_TUNER_FC0011: af9035_af9033_config[i].spec_inv = 1; break; default: @@ -542,6 +544,83 @@ err: return ret; } +static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + int err; + + switch (cmd) { + case FC0011_FE_CALLBACK_POWER: + /* Tuner enable */ + err = af9035_wr_reg_mask(d, 0xd8eb, 1, 1); + if (err) + return err; + err = af9035_wr_reg_mask(d, 0xd8ec, 1, 1); + if (err) + return err; + err = af9035_wr_reg_mask(d, 0xd8ed, 1, 1); + if (err) + return err; + /* LED */ + err = af9035_wr_reg_mask(d, 0xd8d0, 1, 1); + if (err) + return err; + err = af9035_wr_reg_mask(d, 0xd8d1, 1, 1); + if (err) + return err; + msleep(10); + break; + case FC0011_FE_CALLBACK_RESET: + err = af9035_wr_reg(d, 0xd8e9, 1); + if (err) + return err; + err = af9035_wr_reg(d, 0xd8e8, 1); + if (err) + return err; + err = af9035_wr_reg(d, 0xd8e7, 1); + if (err) + return err; + msleep(10); + err = af9035_wr_reg(d, 0xd8e7, 0); + if (err) + return err; + msleep(10); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) +{ + switch (af9035_af9033_config[0].tuner) { + case AF9033_TUNER_FC0011: + return af9035_fc0011_tuner_callback(d, cmd, arg); + default: + break; + } + + return -ENODEV; +} + +static int af9035_frontend_callback(void *adapter_priv, int component, + int cmd, int arg) +{ + struct i2c_adapter *adap = adapter_priv; + struct dvb_usb_device *d = i2c_get_adapdata(adap); + + switch (component) { + case DVB_FRONTEND_COMPONENT_TUNER: + return af9035_tuner_callback(d, cmd, arg); + default: + break; + } + + return -EINVAL; +} + static int af9035_frontend_attach(struct dvb_usb_adapter *adap) { int ret; @@ -570,6 +649,7 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) ret = -ENODEV; goto err; } + adap->fe_adap[0].fe->callback = af9035_frontend_callback; return 0; @@ -583,6 +663,10 @@ static struct tua9001_config af9035_tua9001_config = { .i2c_addr = 0x60, }; +static const struct fc0011_config af9035_fc0011_config = { + .i2c_address = 0x60, +}; + static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -631,6 +715,10 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(tua9001_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &af9035_tua9001_config); break; + case AF9033_TUNER_FC0011: + fe = dvb_attach(fc0011_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9035_fc0011_config); + break; default: fe = NULL; } diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c index 277255481607..9eedf93c6384 100644 --- a/drivers/media/dvb/frontends/af9033.c +++ b/drivers/media/dvb/frontends/af9033.c @@ -297,6 +297,10 @@ static int af9033_init(struct dvb_frontend *fe) len = ARRAY_SIZE(tuner_init_tua9001); init = tuner_init_tua9001; break; + case AF9033_TUNER_FC0011: + len = ARRAY_SIZE(tuner_init_fc0011); + init = tuner_init_fc0011; + break; default: pr_debug("%s: unsupported tuner ID=%d\n", __func__, state->cfg.tuner); diff --git a/drivers/media/dvb/frontends/af9033_priv.h b/drivers/media/dvb/frontends/af9033_priv.h index f0096298d2ac..337964257dab 100644 --- a/drivers/media/dvb/frontends/af9033_priv.h +++ b/drivers/media/dvb/frontends/af9033_priv.h @@ -336,5 +336,66 @@ static const struct reg_val tuner_init_tua9001[] = { { 0x80f1e6, 0x00 }, }; +/* Fitipower fc0011 tuner init + AF9033_TUNER_FC0011 = 0x28 */ +static const struct reg_val tuner_init_fc0011[] = { + { 0x800046, AF9033_TUNER_FC0011 }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0xa5 }, + { 0x80006e, 0x01 }, + { 0x800071, 0x0A }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800079, 0x01 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x80009b, 0x2D }, + { 0x80009c, 0x60 }, + { 0x80009d, 0x23 }, + { 0x8000a4, 0x50 }, + { 0x8000ad, 0x50 }, + { 0x8000b3, 0x01 }, + { 0x8000b7, 0x88 }, + { 0x8000b8, 0xa6 }, + { 0x8000c3, 0x01 }, + { 0x8000c4, 0x01 }, + { 0x8000c7, 0x69 }, + { 0x80F007, 0x00 }, + { 0x80F00A, 0x1B }, + { 0x80F00B, 0x1B }, + { 0x80F00C, 0x1B }, + { 0x80F00D, 0x1B }, + { 0x80F00E, 0xFF }, + { 0x80F00F, 0x01 }, + { 0x80F010, 0x00 }, + { 0x80F011, 0x02 }, + { 0x80F012, 0xFF }, + { 0x80F013, 0x01 }, + { 0x80F014, 0x00 }, + { 0x80F015, 0x02 }, + { 0x80F01B, 0xEF }, + { 0x80F01C, 0x01 }, + { 0x80F01D, 0x0f }, + { 0x80F01E, 0x02 }, + { 0x80F01F, 0x6E }, + { 0x80F020, 0x00 }, + { 0x80F025, 0xDE }, + { 0x80F026, 0x00 }, + { 0x80F027, 0x0A }, + { 0x80F028, 0x03 }, + { 0x80F029, 0x6E }, + { 0x80F02A, 0x00 }, + { 0x80F047, 0x00 }, + { 0x80F054, 0x00 }, + { 0x80F055, 0x00 }, + { 0x80F077, 0x01 }, + { 0x80F1E6, 0x00 }, +}; + #endif /* AF9033_PRIV_H */ -- cgit v1.2.3 From 1083a0f9b8f622cefbd53fe75089c37728b6452f Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Mon, 2 Apr 2012 12:34:52 -0300 Subject: [media] af9035: Add Afatech USB PIDs Add some generic Afatech USB PIDs used by "Cabstone" sticks and others. Signed-off-by: Michael Buesch Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 16 ++++++++++++++-- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 2 ++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 15dcb9bc0742..a7e05a18c35f 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -738,11 +738,17 @@ err: enum af9035_id_entry { AF9035_0CCD_0093, + AF9035_15A4_9035, + AF9035_15A4_1001, }; static struct usb_device_id af9035_id[] = { [AF9035_0CCD_0093] = { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)}, + [AF9035_15A4_9035] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035)}, + [AF9035_15A4_1001] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_2)}, {}, }; @@ -785,14 +791,20 @@ static struct dvb_usb_device_properties af9035_properties[] = { .i2c_algo = &af9035_i2c_algo, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { .name = "TerraTec Cinergy T Stick", .cold_ids = { &af9035_id[AF9035_0CCD_0093], }, - }, + }, { + .name = "Afatech Technologies DVB-T stick", + .cold_ids = { + &af9035_id[AF9035_15A4_9035], + &af9035_id[AF9035_15A4_1001], + }, + } } }, }; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 35f981352b13..c817a98017d4 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -76,6 +76,8 @@ #define USB_PID_AFATECH_AF9005 0x9020 #define USB_PID_AFATECH_AF9015_9015 0x9015 #define USB_PID_AFATECH_AF9015_9016 0x9016 +#define USB_PID_AFATECH_AF9035 0x9035 +#define USB_PID_AFATECH_AF9035_2 0x1001 #define USB_PID_TREKSTOR_DVBT 0x901b #define USB_VID_ALINK_DTU 0xf170 #define USB_PID_ANSONIC_DVBT_USB 0x6000 -- cgit v1.2.3 From 540fd4ba053356cca91429cf4f6bf25fabd2984a Mon Sep 17 00:00:00 2001 From: Hans-Frieder Vogt Date: Mon, 2 Apr 2012 14:18:16 -0300 Subject: [media] af9035: add Avermedia Volar HD (A867R) support Support of AVerMedia AVerTV HD Volar, with tuner MxL5007t. Signed-off-by: Hans-Frieder Vogt Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/Kconfig | 1 + drivers/media/dvb/dvb-usb/af9035.c | 65 ++++++++++++++++++++++++++++++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + drivers/media/dvb/frontends/af9033.c | 41 ++++++++++++++----- drivers/media/dvb/frontends/af9033.h | 1 + drivers/media/dvb/frontends/af9033_priv.h | 35 +++++++++++++++++ 6 files changed, 133 insertions(+), 11 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index cf57c0659d5b..f53fb3c8530e 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -429,6 +429,7 @@ config DVB_USB_AF9035 select DVB_AF9033 select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Afatech AF9035 based DVB USB receiver. diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index a7e05a18c35f..3f2891abcba7 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -23,6 +23,7 @@ #include "af9033.h" #include "tua9001.h" #include "fc0011.h" +#include "mxl5007t.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static DEFINE_MUTEX(af9035_usb_mutex); @@ -500,6 +501,7 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) switch (tmp) { case AF9033_TUNER_TUA9001: case AF9033_TUNER_FC0011: + case AF9033_TUNER_MXL5007T: af9035_af9033_config[i].spec_inv = 1; break; default: @@ -667,6 +669,15 @@ static const struct fc0011_config af9035_fc0011_config = { .i2c_address = 0x60, }; +static struct mxl5007t_config af9035_mxl5007t_config = { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 0, + .clk_out_enable = 0, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, +}; + static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -719,6 +730,48 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(fc0011_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &af9035_fc0011_config); break; + case AF9033_TUNER_MXL5007T: + ret = af9035_wr_reg(adap->dev, 0x00d8e0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8e1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8df, 0); + if (ret < 0) + goto err; + + msleep(30); + + ret = af9035_wr_reg(adap->dev, 0x00d8df, 1); + if (ret < 0) + goto err; + + msleep(300); + + ret = af9035_wr_reg(adap->dev, 0x00d8c0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8c1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8bf, 0); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8b4, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8b5, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8b3, 1); + if (ret < 0) + goto err; + + /* attach tuner */ + fe = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, 0x60, &af9035_mxl5007t_config); + break; default: fe = NULL; } @@ -740,6 +793,7 @@ enum af9035_id_entry { AF9035_0CCD_0093, AF9035_15A4_9035, AF9035_15A4_1001, + AF9035_07CA_1867, }; static struct usb_device_id af9035_id[] = { @@ -749,6 +803,8 @@ static struct usb_device_id af9035_id[] = { USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035)}, [AF9035_15A4_1001] = { USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_2)}, + [AF9035_07CA_1867] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867)}, {}, }; @@ -791,7 +847,7 @@ static struct dvb_usb_device_properties af9035_properties[] = { .i2c_algo = &af9035_i2c_algo, - .num_device_descs = 2, + .num_device_descs = 3, .devices = { { .name = "TerraTec Cinergy T Stick", @@ -804,7 +860,12 @@ static struct dvb_usb_device_properties af9035_properties[] = { &af9035_id[AF9035_15A4_9035], &af9035_id[AF9035_15A4_1001], }, - } + }, { + .name = "AVerMedia HD Volar", + .cold_ids = { + &af9035_id[AF9035_07CA_1867], + }, + }, } }, }; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index c817a98017d4..8f77a6c608f4 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -224,6 +224,7 @@ #define USB_PID_AVERMEDIA_A850T 0x850b #define USB_PID_AVERMEDIA_A805 0xa805 #define USB_PID_AVERMEDIA_A815M 0x815a +#define USB_PID_AVERMEDIA_1867 0x1867 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c index 9eedf93c6384..8c0f4a3ef0f0 100644 --- a/drivers/media/dvb/frontends/af9033.c +++ b/drivers/media/dvb/frontends/af9033.c @@ -301,6 +301,10 @@ static int af9033_init(struct dvb_frontend *fe) len = ARRAY_SIZE(tuner_init_fc0011); init = tuner_init_fc0011; break; + case AF9033_TUNER_MXL5007T: + len = ARRAY_SIZE(tuner_init_mxl5007t); + init = tuner_init_mxl5007t; + break; default: pr_debug("%s: unsupported tuner ID=%d\n", __func__, state->cfg.tuner); @@ -391,9 +395,9 @@ static int af9033_set_frontend(struct dvb_frontend *fe) { struct af9033_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, i; + int ret, i, spec_inv; u8 tmp, buf[3], bandwidth_reg_val; - u32 if_frequency, freq_cw; + u32 if_frequency, freq_cw, adc_freq; pr_debug("%s: frequency=%d bandwidth_hz=%d\n", __func__, c->frequency, c->bandwidth_hz); @@ -433,22 +437,41 @@ static int af9033_set_frontend(struct dvb_frontend *fe) /* program frequency control */ if (c->bandwidth_hz != state->bandwidth_hz) { + spec_inv = state->cfg.spec_inv ? -1 : 1; + + for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { + if (clock_adc_lut[i].clock == state->cfg.clock) + break; + } + adc_freq = clock_adc_lut[i].adc; + /* get used IF frequency */ if (fe->ops.tuner_ops.get_if_frequency) fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); else if_frequency = 0; - /* FIXME: we support only Zero-IF currently */ - if (if_frequency != 0) { - pr_debug("%s: only Zero-IF supported currently\n", - __func__); + while (if_frequency > (adc_freq / 2)) + if_frequency -= adc_freq; - ret = -ENODEV; + if (if_frequency >= 0) + spec_inv *= -1; + else + if_frequency *= -1; + + freq_cw = af9033_div(if_frequency, adc_freq, 23ul); + + if (spec_inv == -1) + freq_cw *= -1; + + /* get adc multiplies */ + ret = af9033_rd_reg(state, 0x800045, &tmp); + if (ret < 0) goto err; - } - freq_cw = 0; + if (tmp == 1) + freq_cw /= 2; + buf[0] = (freq_cw >> 0) & 0xff; buf[1] = (freq_cw >> 8) & 0xff; buf[2] = (freq_cw >> 16) & 0x7f; diff --git a/drivers/media/dvb/frontends/af9033.h b/drivers/media/dvb/frontends/af9033.h index af8c1e3e30d0..dcf7e290b6fe 100644 --- a/drivers/media/dvb/frontends/af9033.h +++ b/drivers/media/dvb/frontends/af9033.h @@ -40,6 +40,7 @@ struct af9033_config { */ #define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ #define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ +#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ u8 tuner; /* diff --git a/drivers/media/dvb/frontends/af9033_priv.h b/drivers/media/dvb/frontends/af9033_priv.h index 337964257dab..e6041bc7c2fc 100644 --- a/drivers/media/dvb/frontends/af9033_priv.h +++ b/drivers/media/dvb/frontends/af9033_priv.h @@ -397,5 +397,40 @@ static const struct reg_val tuner_init_fc0011[] = { { 0x80F1E6, 0x00 }, }; +/* MaxLinear MxL5007T tuner init + AF9033_TUNER_MXL5007T = 0xa0 */ +static const struct reg_val tuner_init_mxl5007t[] = { + { 0x800046, 0x1b }, + { 0x800057, 0x01 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x96 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800079, 0x01 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x8000b3, 0x01 }, + { 0x8000c1, 0x01 }, + { 0x8000c2, 0x00 }, + { 0x80f007, 0x00 }, + { 0x80f00c, 0x19 }, + { 0x80f00d, 0x1a }, + { 0x80f012, 0xda }, + { 0x80f013, 0x00 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01f, 0x82 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f077, 0x02 }, + { 0x80f1e6, 0x00 }, +}; + #endif /* AF9033_PRIV_H */ -- cgit v1.2.3 From 728827b8d32319a2989f89b656e2d9fc6d7a3ab3 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Mon, 2 Apr 2012 17:25:13 -0300 Subject: [media] af9035: add USB id for 07ca:a867 New USB id for the Avermedia A867 stick (Sky Digital Key with blue led). Signed-off-by: Gianluca Gennari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 6 +++++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 3f2891abcba7..9ac3b4174d22 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -794,6 +794,7 @@ enum af9035_id_entry { AF9035_15A4_9035, AF9035_15A4_1001, AF9035_07CA_1867, + AF9035_07CA_A867, }; static struct usb_device_id af9035_id[] = { @@ -805,6 +806,8 @@ static struct usb_device_id af9035_id[] = { USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_2)}, [AF9035_07CA_1867] = { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867)}, + [AF9035_07CA_A867] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A867)}, {}, }; @@ -861,9 +864,10 @@ static struct dvb_usb_device_properties af9035_properties[] = { &af9035_id[AF9035_15A4_1001], }, }, { - .name = "AVerMedia HD Volar", + .name = "AVerMedia HD Volar (A867)", .cold_ids = { &af9035_id[AF9035_07CA_1867], + &af9035_id[AF9035_07CA_A867], }, }, } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 8f77a6c608f4..3cf002b423f7 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -225,6 +225,7 @@ #define USB_PID_AVERMEDIA_A805 0xa805 #define USB_PID_AVERMEDIA_A815M 0x815a #define USB_PID_AVERMEDIA_1867 0x1867 +#define USB_PID_AVERMEDIA_A867 0xa867 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a -- cgit v1.2.3 From ce1fe3799c0e92b9219ab123002d0383c5c3cc07 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Mon, 2 Apr 2012 17:25:14 -0300 Subject: [media] af9035: add support for the tda18218 tuner Add basic support for the tda18218 tuner and the AVerMedia A835 devices. Signed-off-by: Gianluca Gennari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/Kconfig | 1 + drivers/media/dvb/dvb-usb/af9035.c | 26 ++++++++++++++++++++++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 2 ++ drivers/media/dvb/frontends/af9033.c | 4 ++++ drivers/media/dvb/frontends/af9033.h | 1 + drivers/media/dvb/frontends/af9033_priv.h | 34 +++++++++++++++++++++++++++++++ 6 files changed, 67 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index f53fb3c8530e..be1db75091ff 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -430,6 +430,7 @@ config DVB_USB_AF9035 select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Afatech AF9035 based DVB USB receiver. diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 9ac3b4174d22..970bdb6bb60a 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -24,6 +24,7 @@ #include "tua9001.h" #include "fc0011.h" #include "mxl5007t.h" +#include "tda18218.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static DEFINE_MUTEX(af9035_usb_mutex); @@ -502,6 +503,7 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) case AF9033_TUNER_TUA9001: case AF9033_TUNER_FC0011: case AF9033_TUNER_MXL5007T: + case AF9033_TUNER_TDA18218: af9035_af9033_config[i].spec_inv = 1; break; default: @@ -678,6 +680,11 @@ static struct mxl5007t_config af9035_mxl5007t_config = { .clk_out_amp = MxL_CLKOUT_AMP_0_94V, }; +static struct tda18218_config af9035_tda18218_config = { + .i2c_address = 0x60, + .i2c_wr_max = 21, +}; + static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -772,6 +779,11 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0x60, &af9035_mxl5007t_config); break; + case AF9033_TUNER_TDA18218: + /* attach tuner */ + fe = dvb_attach(tda18218_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9035_tda18218_config); + break; default: fe = NULL; } @@ -793,6 +805,8 @@ enum af9035_id_entry { AF9035_0CCD_0093, AF9035_15A4_9035, AF9035_15A4_1001, + AF9035_07CA_A835, + AF9035_07CA_B835, AF9035_07CA_1867, AF9035_07CA_A867, }; @@ -804,6 +818,10 @@ static struct usb_device_id af9035_id[] = { USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035)}, [AF9035_15A4_1001] = { USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_2)}, + [AF9035_07CA_A835] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835)}, + [AF9035_07CA_B835] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_B835)}, [AF9035_07CA_1867] = { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867)}, [AF9035_07CA_A867] = { @@ -850,7 +868,7 @@ static struct dvb_usb_device_properties af9035_properties[] = { .i2c_algo = &af9035_i2c_algo, - .num_device_descs = 3, + .num_device_descs = 4, .devices = { { .name = "TerraTec Cinergy T Stick", @@ -863,6 +881,12 @@ static struct dvb_usb_device_properties af9035_properties[] = { &af9035_id[AF9035_15A4_9035], &af9035_id[AF9035_15A4_1001], }, + }, { + .name = "AVerMedia AVerTV Volar HD/PRO (A835)", + .cold_ids = { + &af9035_id[AF9035_07CA_A835], + &af9035_id[AF9035_07CA_B835], + }, }, { .name = "AVerMedia HD Volar (A867)", .cold_ids = { diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 3cf002b423f7..6a761c546a98 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -224,6 +224,8 @@ #define USB_PID_AVERMEDIA_A850T 0x850b #define USB_PID_AVERMEDIA_A805 0xa805 #define USB_PID_AVERMEDIA_A815M 0x815a +#define USB_PID_AVERMEDIA_A835 0xa835 +#define USB_PID_AVERMEDIA_B835 0xb835 #define USB_PID_AVERMEDIA_1867 0x1867 #define USB_PID_AVERMEDIA_A867 0xa867 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c index 8c0f4a3ef0f0..5fadee79b325 100644 --- a/drivers/media/dvb/frontends/af9033.c +++ b/drivers/media/dvb/frontends/af9033.c @@ -305,6 +305,10 @@ static int af9033_init(struct dvb_frontend *fe) len = ARRAY_SIZE(tuner_init_mxl5007t); init = tuner_init_mxl5007t; break; + case AF9033_TUNER_TDA18218: + len = ARRAY_SIZE(tuner_init_tda18218); + init = tuner_init_tda18218; + break; default: pr_debug("%s: unsupported tuner ID=%d\n", __func__, state->cfg.tuner); diff --git a/drivers/media/dvb/frontends/af9033.h b/drivers/media/dvb/frontends/af9033.h index dcf7e290b6fe..9e302c3f0f7d 100644 --- a/drivers/media/dvb/frontends/af9033.h +++ b/drivers/media/dvb/frontends/af9033.h @@ -41,6 +41,7 @@ struct af9033_config { #define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ #define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ #define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ +#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ u8 tuner; /* diff --git a/drivers/media/dvb/frontends/af9033_priv.h b/drivers/media/dvb/frontends/af9033_priv.h index e6041bc7c2fc..0b783b9ed75e 100644 --- a/drivers/media/dvb/frontends/af9033_priv.h +++ b/drivers/media/dvb/frontends/af9033_priv.h @@ -432,5 +432,39 @@ static const struct reg_val tuner_init_mxl5007t[] = { { 0x80f1e6, 0x00 }, }; +/* NXP TDA 18218HN tuner init + AF9033_TUNER_TDA18218 = 0xa1 */ +static const struct reg_val tuner_init_tda18218[] = { + {0x800046, 0xa1}, + {0x800057, 0x01}, + {0x800058, 0x01}, + {0x80005f, 0x00}, + {0x800060, 0x00}, + {0x800071, 0x05}, + {0x800072, 0x02}, + {0x800074, 0x01}, + {0x800079, 0x01}, + {0x800093, 0x00}, + {0x800094, 0x00}, + {0x800095, 0x00}, + {0x800096, 0x00}, + {0x8000b3, 0x01}, + {0x8000c3, 0x01}, + {0x8000c4, 0x00}, + {0x80f007, 0x00}, + {0x80f00c, 0x19}, + {0x80f00d, 0x1a}, + {0x80f012, 0xda}, + {0x80f013, 0x00}, + {0x80f014, 0x00}, + {0x80f015, 0x02}, + {0x80f01f, 0x82}, + {0x80f020, 0x00}, + {0x80f029, 0x82}, + {0x80f02a, 0x00}, + {0x80f077, 0x02}, + {0x80f1e6, 0x00}, +}; + #endif /* AF9033_PRIV_H */ -- cgit v1.2.3 From 48bf7e1a9dce24adf79ea118ef001e781fb0e2e4 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Mon, 2 Apr 2012 17:25:17 -0300 Subject: [media] af9035: use module_usb_driver macro Let's save a few lines of code using the module_usb_driver macro. Signed-off-by: Gianluca Gennari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 970bdb6bb60a..73044632d943 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -973,31 +973,7 @@ static struct usb_driver af9035_usb_driver = { .id_table = af9035_id, }; -/* module stuff */ -static int __init af9035_usb_module_init(void) -{ - int ret; - - ret = usb_register(&af9035_usb_driver); - if (ret < 0) - goto err; - - return 0; - -err: - pr_debug("%s: failed=%d\n", __func__, ret); - - return ret; -} - -static void __exit af9035_usb_module_exit(void) -{ - /* deregister this driver from the USB subsystem */ - usb_deregister(&af9035_usb_driver); -} - -module_init(af9035_usb_module_init); -module_exit(af9035_usb_module_exit); +module_usb_driver(af9035_usb_driver); MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("Afatech AF9035 driver"); -- cgit v1.2.3 From ad30e91befd7153827a97faa8281dcd48aa6702d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 2 Apr 2012 20:18:59 -0300 Subject: [media] af9035: fix and enhance I2C adapter There was a bug I2C adapter writes and reads one byte too much. As the most I2C clients has auto-increment register addressing this leads next register from the target register overwritten by garbage data. As a change remove whole register address byte usage and write data directly to the I2C bus without saying what are register address bytes to firmware. Signed-off-by: Antti Palosaari Cc: Michael Buesch Cc: Hans-Frieder Vogt Cc: Gianluca Gennari Tested-by: Michael Buesch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 73044632d943..58fe69deda1f 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -224,6 +224,22 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; + /* + * I2C sub header is 5 bytes long. Meaning of those bytes are: + * 0: data len + * 1: I2C addr << 1 + * 2: reg addr len + * byte 3 and 4 can be used as reg addr + * 3: reg addr MSB + * used when reg addr len is set to 2 + * 4: reg addr LSB + * used when reg addr len is set to 1 or 2 + * + * For the simplify we do not use register addr at all. + * NOTE: As a firmware knows tuner type there is very small possibility + * there could be some tuner I2C hacks done by firmware and this may + * lead problems if firmware expects those bytes are used. + */ if (num == 2 && !(msg[0].flags & I2C_M_RD) && (msg[1].flags & I2C_M_RD)) { if (msg[0].len > 40 || msg[1].len > 40) { @@ -237,14 +253,15 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, msg[1].len); } else { /* I2C */ - u8 buf[4 + msg[0].len]; + u8 buf[5 + msg[0].len]; struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), buf, msg[1].len, msg[1].buf }; buf[0] = msg[1].len; buf[1] = msg[0].addr << 1; - buf[2] = 0x01; - buf[3] = 0x00; - memcpy(&buf[4], msg[0].buf, msg[0].len); + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); ret = af9035_ctrl_msg(d->udev, &req); } } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) { @@ -259,14 +276,15 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, msg[0].len - 3); } else { /* I2C */ - u8 buf[4 + msg[0].len]; + u8 buf[5 + msg[0].len]; struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf, 0, NULL }; buf[0] = msg[0].len; buf[1] = msg[0].addr << 1; - buf[2] = 0x01; - buf[3] = 0x00; - memcpy(&buf[4], msg[0].buf, msg[0].len); + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); ret = af9035_ctrl_msg(d->udev, &req); } } else { -- cgit v1.2.3 From c421d5c9d439857434916b1bc420623fe9519285 Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Tue, 3 Apr 2012 05:08:45 -0300 Subject: [media] fc0011: use usleep_range() Use usleep_range() instead of msleep() to improve power saving opportunities. Signed-off-by: Michael Buesch Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/fc0011.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/common/tuners/fc0011.c index 7842a4eee3d7..d43a5a29ed29 100644 --- a/drivers/media/common/tuners/fc0011.c +++ b/drivers/media/common/tuners/fc0011.c @@ -167,7 +167,7 @@ static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); if (err) return err; - msleep(10); + usleep_range(10000, 20000); err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); if (err) return err; @@ -423,7 +423,7 @@ static int fc0011_set_params(struct dvb_frontend *fe) err = fc0011_vcocal_read(priv, NULL); if (err) return err; - msleep(10); + usleep_range(10000, 50000); err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]); if (err) -- cgit v1.2.3 From de2bec5e54307223c8973dff75162416abde80a9 Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Tue, 3 Apr 2012 05:11:30 -0300 Subject: [media] af9035: Use usleep_range() in fc0011 support code Use usleep_range() instead of msleep() to improve power saving opportunities. Signed-off-by: Michael Buesch Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 58fe69deda1f..7bb8817864af 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -590,7 +590,7 @@ static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, err = af9035_wr_reg_mask(d, 0xd8d1, 1, 1); if (err) return err; - msleep(10); + usleep_range(10000, 50000); break; case FC0011_FE_CALLBACK_RESET: err = af9035_wr_reg(d, 0xd8e9, 1); @@ -602,11 +602,11 @@ static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, err = af9035_wr_reg(d, 0xd8e7, 1); if (err) return err; - msleep(10); + usleep_range(10000, 20000); err = af9035_wr_reg(d, 0xd8e7, 0); if (err) return err; - msleep(10); + usleep_range(10000, 20000); break; default: return -EINVAL; -- cgit v1.2.3 From a182fd8f7953be661f9c36c0f6436905d08395d3 Mon Sep 17 00:00:00 2001 From: Michael Büsch Date: Tue, 3 Apr 2012 05:05:03 -0300 Subject: [media] fc0011: Reduce number of retries Now that i2c transfers are fixed, 3 retries are enough. Signed-off-by: Michael Buesch Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/fc0011.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/common/tuners/fc0011.c index d43a5a29ed29..e4882546c283 100644 --- a/drivers/media/common/tuners/fc0011.c +++ b/drivers/media/common/tuners/fc0011.c @@ -314,7 +314,7 @@ static int fc0011_set_params(struct dvb_frontend *fe) if (err) return err; vco_retries = 0; - while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 6) { + while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) { /* Reset the tuner and try again */ err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, FC0011_FE_CALLBACK_RESET, priv->addr); -- cgit v1.2.3 From f2b61d0c3966c424b85591b6e538183871b8db35 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 5 Apr 2012 20:28:51 -0300 Subject: [media] af9035: initial support for IT9135 chip AF9035 code needed for IT9135 chip support. Needs still small changes for AF9033 and totally new tuner driver in order to get that chip version working. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 141 ++++++++++++++++++++++++++++++++++++- drivers/media/dvb/dvb-usb/af9035.h | 14 ++++ 2 files changed, 154 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 7bb8817864af..5ae5cc167c9f 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -29,7 +29,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static DEFINE_MUTEX(af9035_usb_mutex); static struct config af9035_config; -static struct dvb_usb_device_properties af9035_properties[1]; +static struct dvb_usb_device_properties af9035_properties[2]; static int af9035_properties_count = ARRAY_SIZE(af9035_properties); static struct af9033_config af9035_af9033_config[] = { { @@ -493,6 +493,76 @@ err: return ret; } +static int af9035_download_firmware_it9135(struct usb_device *udev, + const struct firmware *fw) +{ + int ret, i, i_prev; + u8 wbuf[1]; + u8 rbuf[4]; + struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; + #define HDR_SIZE 7 + + /* + * There seems to be following firmware header. Meaning of bytes 0-3 + * is unknown. + * + * 0: 3 + * 1: 0, 1 + * 2: 0 + * 3: 1, 2, 3 + * 4: addr MSB + * 5: addr LSB + * 6: count of data bytes ? + */ + + for (i = HDR_SIZE, i_prev = 0; i <= fw->size; i++) { + if (i == fw->size || + (fw->data[i + 0] == 0x03 && + (fw->data[i + 1] == 0x00 || + fw->data[i + 1] == 0x01) && + fw->data[i + 2] == 0x00)) { + req_fw_dl.wlen = i - i_prev; + req_fw_dl.wbuf = (u8 *) &fw->data[i_prev]; + i_prev = i; + ret = af9035_ctrl_msg(udev, &req_fw_dl); + if (ret < 0) + goto err; + + pr_debug("%s: data uploaded=%d\n", __func__, i); + } + } + + /* firmware loaded, request boot */ + req.cmd = CMD_FW_BOOT; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + /* ensure firmware starts */ + wbuf[0] = 1; + ret = af9035_ctrl_msg(udev, &req_fw_ver); + if (ret < 0) + goto err; + + if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { + info("firmware did not run"); + ret = -ENODEV; + goto err; + } + + info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2], + rbuf[3]); + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + /* abuse that callback as there is no better one for reading eeprom */ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) { @@ -566,6 +636,32 @@ err: return ret; } +/* abuse that callback as there is no better one for reading eeprom */ +static int af9035_read_mac_address_it9135(struct dvb_usb_device *d, u8 mac[6]) +{ + int ret, i; + u8 tmp; + + af9035_config.dual_mode = 0; + + /* get demod clock */ + ret = af9035_rd_reg(d, 0x00d800, &tmp); + if (ret < 0) + goto err; + + tmp = (tmp >> 0) & 0x0f; + + for (i = 0; i < af9035_properties[0].num_adapters; i++) + af9035_af9033_config[i].clock = clock_lut_it9135[tmp]; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { @@ -914,6 +1010,49 @@ static struct dvb_usb_device_properties af9035_properties[] = { }, } }, + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9035_download_firmware_it9135, + .firmware = "dvb-usb-it9135-01.fw", + .no_reconnect = 1, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9035_frontend_attach, + .tuner_attach = af9035_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + .u = { + .bulk = { + .buffersize = (87 * 188), + } + } + } + } + } + } + }, + + .identify_state = af9035_identify_state, + .read_mac_address = af9035_read_mac_address_it9135, + + .i2c_algo = &af9035_i2c_algo, + + .num_device_descs = 0, /* disabled as no support for IT9135 */ + .devices = { + { + .name = "ITE Tech. IT9135 reference design", + }, + } + }, }; static int af9035_usb_probe(struct usb_interface *intf, diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h index 0df24cdf2504..031bd9cf0cb2 100644 --- a/drivers/media/dvb/dvb-usb/af9035.h +++ b/drivers/media/dvb/dvb-usb/af9035.h @@ -81,6 +81,19 @@ u32 clock_lut[] = { 12000000, /* 12.00 MHz */ }; +u32 clock_lut_it9135[] = { + 12000000, /* 12.00 MHz */ + 20480000, /* 20.48 MHz */ + 36000000, /* 36.00 MHz */ + 30000000, /* 30.00 MHz */ + 26000000, /* 26.00 MHz */ + 28000000, /* 28.00 MHz */ + 32000000, /* 32.00 MHz */ + 34000000, /* 34.00 MHz */ + 24000000, /* 24.00 MHz */ + 22000000, /* 22.00 MHz */ +}; + /* EEPROM locations */ #define EEPROM_IR_MODE 0x430d #define EEPROM_DUAL_MODE 0x4326 @@ -102,5 +115,6 @@ u32 clock_lut[] = { #define CMD_FW_BOOT 0x23 #define CMD_FW_DL_BEGIN 0x24 #define CMD_FW_DL_END 0x25 +#define CMD_FW_SCATTER_WR 0x29 #endif -- cgit v1.2.3 From 0a4df239af50650fdcf6bd4911a2f18156c2c9f0 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Thu, 5 Apr 2012 12:47:19 -0300 Subject: [media] af9033: implement get_frontend Implement the get_frontend function. The code is derived from the old af9033 driver by Antti Palosaari. Signed-off-by: Gianluca Gennari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/af9033.c | 133 +++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c index 5fadee79b325..da91155b8a26 100644 --- a/drivers/media/dvb/frontends/af9033.c +++ b/drivers/media/dvb/frontends/af9033.c @@ -26,6 +26,7 @@ struct af9033_state { struct dvb_frontend fe; struct af9033_config cfg; + u32 frequency; u32 bandwidth_hz; bool ts_mode_parallel; bool ts_mode_serial; @@ -406,6 +407,8 @@ static int af9033_set_frontend(struct dvb_frontend *fe) pr_debug("%s: frequency=%d bandwidth_hz=%d\n", __func__, c->frequency, c->bandwidth_hz); + state->frequency = c->frequency; + /* check bandwidth */ switch (c->bandwidth_hz) { case 6000000: @@ -523,6 +526,135 @@ err: return ret; } +static int af9033_get_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct af9033_state *state = fe->demodulator_priv; + int ret; + u8 buf[8]; + + pr_debug("%s\n", __func__); + + /* read all needed registers */ + ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf)); + if (ret) + goto error; + + switch ((buf[0] >> 0) & 3) { + case 0: + p->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + p->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + switch ((buf[1] >> 0) & 3) { + case 0: + p->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + p->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + p->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + p->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[2] >> 0) & 7) { + case 0: + p->hierarchy = HIERARCHY_NONE; + break; + case 1: + p->hierarchy = HIERARCHY_1; + break; + case 2: + p->hierarchy = HIERARCHY_2; + break; + case 3: + p->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[3] >> 0) & 3) { + case 0: + p->modulation = QPSK; + break; + case 1: + p->modulation = QAM_16; + break; + case 2: + p->modulation = QAM_64; + break; + } + + switch ((buf[4] >> 0) & 3) { + case 0: + p->bandwidth_hz = 6000000; + break; + case 1: + p->bandwidth_hz = 7000000; + break; + case 2: + p->bandwidth_hz = 8000000; + break; + } + + switch ((buf[6] >> 0) & 7) { + case 0: + p->code_rate_HP = FEC_1_2; + break; + case 1: + p->code_rate_HP = FEC_2_3; + break; + case 2: + p->code_rate_HP = FEC_3_4; + break; + case 3: + p->code_rate_HP = FEC_5_6; + break; + case 4: + p->code_rate_HP = FEC_7_8; + break; + case 5: + p->code_rate_HP = FEC_NONE; + break; + } + + switch ((buf[7] >> 0) & 7) { + case 0: + p->code_rate_LP = FEC_1_2; + break; + case 1: + p->code_rate_LP = FEC_2_3; + break; + case 2: + p->code_rate_LP = FEC_3_4; + break; + case 3: + p->code_rate_LP = FEC_5_6; + break; + case 4: + p->code_rate_LP = FEC_7_8; + break; + case 5: + p->code_rate_LP = FEC_NONE; + break; + } + + p->inversion = INVERSION_AUTO; + p->frequency = state->frequency; + +error: + if (ret) + pr_debug("%s: failed:%d\n", __func__, ret); + + return ret; +} + static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct af9033_state *state = fe->demodulator_priv; @@ -776,6 +908,7 @@ static struct dvb_frontend_ops af9033_ops = { .get_tune_settings = af9033_get_tune_settings, .set_frontend = af9033_set_frontend, + .get_frontend = af9033_get_frontend, .read_status = af9033_read_status, .read_snr = af9033_read_snr, -- cgit v1.2.3 From de7f14fcad50ccf66514498fd3bfaac015b071a5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 5 Apr 2012 21:14:32 -0300 Subject: [media] af9033: do some minor changes for .get_frontend() Minor functionality and style changes. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/af9033.c | 73 +++++++++++++++++------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c index da91155b8a26..2cb1f8d6955e 100644 --- a/drivers/media/dvb/frontends/af9033.c +++ b/drivers/media/dvb/frontends/af9033.c @@ -26,7 +26,6 @@ struct af9033_state { struct dvb_frontend fe; struct af9033_config cfg; - u32 frequency; u32 bandwidth_hz; bool ts_mode_parallel; bool ts_mode_serial; @@ -407,8 +406,6 @@ static int af9033_set_frontend(struct dvb_frontend *fe) pr_debug("%s: frequency=%d bandwidth_hz=%d\n", __func__, c->frequency, c->bandwidth_hz); - state->frequency = c->frequency; - /* check bandwidth */ switch (c->bandwidth_hz) { case 6000000: @@ -528,8 +525,8 @@ err: static int af9033_get_frontend(struct dvb_frontend *fe) { - struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct af9033_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; u8 buf[8]; @@ -537,120 +534,118 @@ static int af9033_get_frontend(struct dvb_frontend *fe) /* read all needed registers */ ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf)); - if (ret) - goto error; + if (ret < 0) + goto err; switch ((buf[0] >> 0) & 3) { case 0: - p->transmission_mode = TRANSMISSION_MODE_2K; + c->transmission_mode = TRANSMISSION_MODE_2K; break; case 1: - p->transmission_mode = TRANSMISSION_MODE_8K; + c->transmission_mode = TRANSMISSION_MODE_8K; break; } switch ((buf[1] >> 0) & 3) { case 0: - p->guard_interval = GUARD_INTERVAL_1_32; + c->guard_interval = GUARD_INTERVAL_1_32; break; case 1: - p->guard_interval = GUARD_INTERVAL_1_16; + c->guard_interval = GUARD_INTERVAL_1_16; break; case 2: - p->guard_interval = GUARD_INTERVAL_1_8; + c->guard_interval = GUARD_INTERVAL_1_8; break; case 3: - p->guard_interval = GUARD_INTERVAL_1_4; + c->guard_interval = GUARD_INTERVAL_1_4; break; } switch ((buf[2] >> 0) & 7) { case 0: - p->hierarchy = HIERARCHY_NONE; + c->hierarchy = HIERARCHY_NONE; break; case 1: - p->hierarchy = HIERARCHY_1; + c->hierarchy = HIERARCHY_1; break; case 2: - p->hierarchy = HIERARCHY_2; + c->hierarchy = HIERARCHY_2; break; case 3: - p->hierarchy = HIERARCHY_4; + c->hierarchy = HIERARCHY_4; break; } switch ((buf[3] >> 0) & 3) { case 0: - p->modulation = QPSK; + c->modulation = QPSK; break; case 1: - p->modulation = QAM_16; + c->modulation = QAM_16; break; case 2: - p->modulation = QAM_64; + c->modulation = QAM_64; break; } switch ((buf[4] >> 0) & 3) { case 0: - p->bandwidth_hz = 6000000; + c->bandwidth_hz = 6000000; break; case 1: - p->bandwidth_hz = 7000000; + c->bandwidth_hz = 7000000; break; case 2: - p->bandwidth_hz = 8000000; + c->bandwidth_hz = 8000000; break; } switch ((buf[6] >> 0) & 7) { case 0: - p->code_rate_HP = FEC_1_2; + c->code_rate_HP = FEC_1_2; break; case 1: - p->code_rate_HP = FEC_2_3; + c->code_rate_HP = FEC_2_3; break; case 2: - p->code_rate_HP = FEC_3_4; + c->code_rate_HP = FEC_3_4; break; case 3: - p->code_rate_HP = FEC_5_6; + c->code_rate_HP = FEC_5_6; break; case 4: - p->code_rate_HP = FEC_7_8; + c->code_rate_HP = FEC_7_8; break; case 5: - p->code_rate_HP = FEC_NONE; + c->code_rate_HP = FEC_NONE; break; } switch ((buf[7] >> 0) & 7) { case 0: - p->code_rate_LP = FEC_1_2; + c->code_rate_LP = FEC_1_2; break; case 1: - p->code_rate_LP = FEC_2_3; + c->code_rate_LP = FEC_2_3; break; case 2: - p->code_rate_LP = FEC_3_4; + c->code_rate_LP = FEC_3_4; break; case 3: - p->code_rate_LP = FEC_5_6; + c->code_rate_LP = FEC_5_6; break; case 4: - p->code_rate_LP = FEC_7_8; + c->code_rate_LP = FEC_7_8; break; case 5: - p->code_rate_LP = FEC_NONE; + c->code_rate_LP = FEC_NONE; break; } - p->inversion = INVERSION_AUTO; - p->frequency = state->frequency; + return 0; -error: - if (ret) - pr_debug("%s: failed:%d\n", __func__, ret); +err: + pr_debug("%s: failed=%d\n", __func__, ret); return ret; } -- cgit v1.2.3 From dbac01ffbb8619591ee2980eb093f086a5ba1848 Mon Sep 17 00:00:00 2001 From: Pierangelo Terzulli Date: Thu, 5 Apr 2012 21:26:18 -0300 Subject: [media] af9035: add AVerMedia Twinstar (A825) [07ca:0825] [crope@iki.fi: applied manually since erroneous patch] Signed-off-by: Pierangelo Terzulli Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 10 +++++++++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 5ae5cc167c9f..9503b2d440e6 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -923,6 +923,7 @@ enum af9035_id_entry { AF9035_07CA_B835, AF9035_07CA_1867, AF9035_07CA_A867, + AF9035_07CA_0825, }; static struct usb_device_id af9035_id[] = { @@ -940,6 +941,8 @@ static struct usb_device_id af9035_id[] = { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867)}, [AF9035_07CA_A867] = { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A867)}, + [AF9035_07CA_0825] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TWINSTAR)}, {}, }; @@ -982,7 +985,7 @@ static struct dvb_usb_device_properties af9035_properties[] = { .i2c_algo = &af9035_i2c_algo, - .num_device_descs = 4, + .num_device_descs = 5, .devices = { { .name = "TerraTec Cinergy T Stick", @@ -1007,6 +1010,11 @@ static struct dvb_usb_device_properties af9035_properties[] = { &af9035_id[AF9035_07CA_1867], &af9035_id[AF9035_07CA_A867], }, + }, { + .name = "AVerMedia Twinstar (A825)", + .cold_ids = { + &af9035_id[AF9035_07CA_0825], + }, }, } }, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 6a761c546a98..94d3f8a5cd9e 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -228,6 +228,7 @@ #define USB_PID_AVERMEDIA_B835 0xb835 #define USB_PID_AVERMEDIA_1867 0x1867 #define USB_PID_AVERMEDIA_A867 0xa867 +#define USB_PID_AVERMEDIA_TWINSTAR 0x0825 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a -- cgit v1.2.3 From daacd5b27f8d5b19ea65dc4ad9929c94c0ef3e35 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 5 Apr 2012 21:52:21 -0300 Subject: [media] af9035: minor changes for af9035_fc0011_tuner_callback() Change function to use same logic than existing functions. Debugs log writings are done in error case, earlier it was just returning error code. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 76 +++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 9503b2d440e6..e822706629db 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -663,52 +663,68 @@ err: } static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, - int cmd, int arg) + int cmd, int arg) { - int err; + int ret; switch (cmd) { case FC0011_FE_CALLBACK_POWER: /* Tuner enable */ - err = af9035_wr_reg_mask(d, 0xd8eb, 1, 1); - if (err) - return err; - err = af9035_wr_reg_mask(d, 0xd8ec, 1, 1); - if (err) - return err; - err = af9035_wr_reg_mask(d, 0xd8ed, 1, 1); - if (err) - return err; + ret = af9035_wr_reg_mask(d, 0xd8eb, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ec, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ed, 1, 1); + if (ret < 0) + goto err; + /* LED */ - err = af9035_wr_reg_mask(d, 0xd8d0, 1, 1); - if (err) - return err; - err = af9035_wr_reg_mask(d, 0xd8d1, 1, 1); - if (err) - return err; + ret = af9035_wr_reg_mask(d, 0xd8d0, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8d1, 1, 1); + if (ret < 0) + goto err; + usleep_range(10000, 50000); break; case FC0011_FE_CALLBACK_RESET: - err = af9035_wr_reg(d, 0xd8e9, 1); - if (err) - return err; - err = af9035_wr_reg(d, 0xd8e8, 1); - if (err) - return err; - err = af9035_wr_reg(d, 0xd8e7, 1); - if (err) - return err; + ret = af9035_wr_reg(d, 0xd8e9, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0xd8e8, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0xd8e7, 1); + if (ret < 0) + goto err; + usleep_range(10000, 20000); - err = af9035_wr_reg(d, 0xd8e7, 0); - if (err) - return err; + + ret = af9035_wr_reg(d, 0xd8e7, 0); + if (ret < 0) + goto err; + usleep_range(10000, 20000); break; default: - return -EINVAL; + ret = -EINVAL; + goto err; } return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; } static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) -- cgit v1.2.3 From b55b4b7a109f218fc25b9451934ff4d9100e691c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 5 Apr 2012 21:58:09 -0300 Subject: [media] af9035: reorganise USB ID and device list Add and rename "Afatech AF9035 reference design" as a first device in the list since it sounds logical to keep reference design IDs on top of the list. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index e822706629db..9f962428368c 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -932,9 +932,9 @@ err: } enum af9035_id_entry { - AF9035_0CCD_0093, AF9035_15A4_9035, AF9035_15A4_1001, + AF9035_0CCD_0093, AF9035_07CA_A835, AF9035_07CA_B835, AF9035_07CA_1867, @@ -943,12 +943,12 @@ enum af9035_id_entry { }; static struct usb_device_id af9035_id[] = { - [AF9035_0CCD_0093] = { - USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)}, [AF9035_15A4_9035] = { USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035)}, [AF9035_15A4_1001] = { USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_2)}, + [AF9035_0CCD_0093] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)}, [AF9035_07CA_A835] = { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835)}, [AF9035_07CA_B835] = { @@ -1004,15 +1004,15 @@ static struct dvb_usb_device_properties af9035_properties[] = { .num_device_descs = 5, .devices = { { - .name = "TerraTec Cinergy T Stick", + .name = "Afatech AF9035 reference design", .cold_ids = { - &af9035_id[AF9035_0CCD_0093], + &af9035_id[AF9035_15A4_9035], + &af9035_id[AF9035_15A4_1001], }, }, { - .name = "Afatech Technologies DVB-T stick", + .name = "TerraTec Cinergy T Stick", .cold_ids = { - &af9035_id[AF9035_15A4_9035], - &af9035_id[AF9035_15A4_1001], + &af9035_id[AF9035_0CCD_0093], }, }, { .name = "AVerMedia AVerTV Volar HD/PRO (A835)", -- cgit v1.2.3 From 852f0d9d6ffdeac28045aa9724c8e2a9ac4ffe6a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 6 Apr 2012 07:09:23 -0300 Subject: [media] af9035: disable frontend0 I2C-gate control Seems like tuners used for frontend0 are not behind demodulator I2C-gate thus gate control is not needed. Disable it for sure as it can cause problems some situations. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index 9f962428368c..e1d6e6efa77b 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -783,6 +783,9 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) ret = -ENODEV; goto err; } + + /* disable I2C-gate */ + adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL; adap->fe_adap[0].fe->callback = af9035_frontend_callback; return 0; -- cgit v1.2.3 From 3fd7e4341e04f80e2605f56bbd8cb1e8b027901a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Mar 2012 04:35:33 -0300 Subject: [media] mxl111sf: remove an unused variable We don't use this any more after 3be5bb71fb "[media] mxl111sf: fix error on stream stop in mxl111sf_ep6_streaming_ctrl()" and it makes GCC complain. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/mxl111sf.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c index 81305de2fea5..91834c583580 100644 --- a/drivers/media/dvb/dvb-usb/mxl111sf.c +++ b/drivers/media/dvb/dvb-usb/mxl111sf.c @@ -340,7 +340,6 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) struct mxl111sf_state *state = d->priv; struct mxl111sf_adap_state *adap_state = adap->fe_adap[adap->active_fe].priv; int ret = 0; - u8 tmp; deb_info("%s(%d)\n", __func__, onoff); -- cgit v1.2.3 From ce580fe5190dec4d872e7925946b0aec1f694370 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 4 Aug 2011 13:51:11 -0300 Subject: [media] v4l: Introduce integer menu controls Create a new control type called V4L2_CTRL_TYPE_INTEGER_MENU. Integer menu controls are just like menu controls but the menu items are 64-bit integers rather than strings. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Tested-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 74 +++++++++++++++++++++++++++++++--------- include/linux/videodev2.h | 6 +++- include/media/v4l2-ctrls.h | 6 +++- 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 18015c0a8d31..3e0a72dec994 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -852,7 +852,8 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change ev->u.ctrl.value64 = ctrl->cur.val64; ev->u.ctrl.minimum = ctrl->minimum; ev->u.ctrl.maximum = ctrl->maximum; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) ev->u.ctrl.step = 1; else ev->u.ctrl.step = ctrl->step; @@ -1083,10 +1084,13 @@ static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval) return 0; case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: if (val < ctrl->minimum || val > ctrl->maximum) return -ERANGE; - if (ctrl->qmenu[val][0] == '\0' || - (ctrl->menu_skip_mask & (1 << val))) + if (ctrl->menu_skip_mask & (1 << val)) + return -EINVAL; + if (ctrl->type == V4L2_CTRL_TYPE_MENU && + ctrl->qmenu[val][0] == '\0') return -EINVAL; return 0; @@ -1114,6 +1118,7 @@ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: @@ -1343,7 +1348,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, s32 min, s32 max, u32 step, s32 def, - u32 flags, const char * const *qmenu, void *priv) + u32 flags, const char * const *qmenu, + const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; unsigned sz_extra = 0; @@ -1356,6 +1362,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, (type == V4L2_CTRL_TYPE_INTEGER && step == 0) || (type == V4L2_CTRL_TYPE_BITMASK && max == 0) || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || + (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) || (type == V4L2_CTRL_TYPE_STRING && max == 0)) { handler_set_err(hdl, -ERANGE); return NULL; @@ -1366,6 +1373,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } if ((type == V4L2_CTRL_TYPE_INTEGER || type == V4L2_CTRL_TYPE_MENU || + type == V4L2_CTRL_TYPE_INTEGER_MENU || type == V4L2_CTRL_TYPE_BOOLEAN) && (def < min || def > max)) { handler_set_err(hdl, -ERANGE); @@ -1400,7 +1408,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; - ctrl->qmenu = qmenu; + if (type == V4L2_CTRL_TYPE_MENU) + ctrl->qmenu = qmenu; + else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) + ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; ctrl->cur.val = ctrl->val = ctrl->default_value = def; @@ -1427,6 +1438,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl; const char *name = cfg->name; const char * const *qmenu = cfg->qmenu; + const s64 *qmenu_int = cfg->qmenu_int; enum v4l2_ctrl_type type = cfg->type; u32 flags = cfg->flags; s32 min = cfg->min; @@ -1438,18 +1450,24 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, &def, &flags); - is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU); + is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU || + cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU); if (is_menu) WARN_ON(step); else WARN_ON(cfg->menu_skip_mask); - if (is_menu && qmenu == NULL) + if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) qmenu = v4l2_ctrl_get_menu(cfg->id); + else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU && + qmenu_int == NULL) { + handler_set_err(hdl, -EINVAL); + return NULL; + } ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, - def, flags, qmenu, priv); + def, flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; @@ -1466,12 +1484,13 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type == V4L2_CTRL_TYPE_MENU) { + if (type == V4L2_CTRL_TYPE_MENU + || type == V4L2_CTRL_TYPE_INTEGER_MENU) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - min, max, step, def, flags, NULL, NULL); + min, max, step, def, flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1493,7 +1512,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, NULL); + 0, max, mask, def, flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -1659,6 +1678,9 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]); + break; case V4L2_CTRL_TYPE_BITMASK: printk(KERN_CONT "0x%08x", ctrl->cur.val); break; @@ -1795,7 +1817,8 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) qc->step = 1; else qc->step = ctrl->step; @@ -1825,16 +1848,33 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm) qm->reserved = 0; /* Sanity checks */ - if (ctrl->qmenu == NULL || - i < ctrl->minimum || i > ctrl->maximum) + switch (ctrl->type) { + case V4L2_CTRL_TYPE_MENU: + if (ctrl->qmenu == NULL) + return -EINVAL; + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (ctrl->qmenu_int == NULL) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (i < ctrl->minimum || i > ctrl->maximum) return -EINVAL; + /* Use mask to see if this menu item should be skipped */ if (ctrl->menu_skip_mask & (1 << i)) return -EINVAL; /* Empty menu items should also be skipped */ - if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') - return -EINVAL; - strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + if (ctrl->type == V4L2_CTRL_TYPE_MENU) { + if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') + return -EINVAL; + strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + } else { + qm->value = ctrl->qmenu_int[i]; + } return 0; } EXPORT_SYMBOL(v4l2_querymenu); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index c9c9a4680cc5..e69cacc9e9ea 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1151,6 +1151,7 @@ enum v4l2_ctrl_type { V4L2_CTRL_TYPE_CTRL_CLASS = 6, V4L2_CTRL_TYPE_STRING = 7, V4L2_CTRL_TYPE_BITMASK = 8, + V4L2_CTRL_TYPE_INTEGER_MENU = 9, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ @@ -1170,7 +1171,10 @@ struct v4l2_queryctrl { struct v4l2_querymenu { __u32 id; __u32 index; - __u8 name[32]; /* Whatever */ + union { + __u8 name[32]; /* Whatever */ + __s64 value; + }; __u32 reserved; }; diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 3dbd06638506..533315bd74e0 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -130,7 +130,10 @@ struct v4l2_ctrl { u32 step; u32 menu_skip_mask; }; - const char * const *qmenu; + union { + const char * const *qmenu; + const s64 *qmenu_int; + }; unsigned long flags; union { s32 val; @@ -220,6 +223,7 @@ struct v4l2_ctrl_config { u32 flags; u32 menu_skip_mask; const char * const *qmenu; + const s64 *qmenu_int; unsigned int is_private:1; }; -- cgit v1.2.3 From 6ec299f3dc86b59c1787d3541dcb52b934c901f0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 6 Aug 2011 16:13:47 -0300 Subject: [media] v4l: Document integer menu controls Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 10 ++++++ Documentation/DocBook/media/v4l/v4l2.xml | 8 +++++ .../DocBook/media/v4l/vidioc-queryctrl.xml | 39 ++++++++++++++++++++-- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index bce97c50391b..d881520e1a54 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2410,6 +2410,16 @@ details. +
+ V4L2 in Linux 3.5 + + + Added integer menus, the new type will be + V4L2_CTRL_TYPE_INTEGER_MENU. + + +
+
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 8ae38876172e..864ba5d6873d 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -127,6 +127,14 @@ structs, ioctls) must be noted in more detail in the history chapter (compat.xml), along with the possible impact on existing drivers and applications. --> + + 3.5 + 2012-04-02 + sa + Added V4L2_CTRL_TYPE_INTEGER_MENU. + + + 3.4 2012-01-25 diff --git a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml index 36660d311b51..505f0206e5bd 100644 --- a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml @@ -215,11 +215,12 @@ the array to zero. struct <structname>v4l2_querymenu</structname> - + &cs-str; __u32 + id Identifies the control, set by the application from the respective &v4l2-queryctrl; @@ -227,18 +228,38 @@ from the respective &v4l2-queryctrl; __u32 + index Index of the menu item, starting at zero, set by the application. + union + + + + + + __u8 name[32] Name of the menu item, a NUL-terminated ASCII -string. This information is intended for the user. +string. This information is intended for the user. This field is valid +for V4L2_CTRL_FLAG_MENU type controls. + + + + __s64 + value + + Value of the integer menu item. This field is valid for + V4L2_CTRL_FLAG_INTEGER_MENU type + controls. + __u32 + reserved Reserved for future extensions. Drivers must set the array to zero. @@ -291,6 +312,20 @@ values which are actually different on the hardware. the menu items can be enumerated with the VIDIOC_QUERYMENU ioctl. + + V4L2_CTRL_TYPE_INTEGER_MENU + ≥ 0 + 1 + N-1 + + The control has a menu of N choices. The values of the + menu items can be enumerated with the + VIDIOC_QUERYMENU ioctl. This is + similar to V4L2_CTRL_TYPE_MENU + except that instead of strings, the menu items are + signed 64-bit integers. + + V4L2_CTRL_TYPE_BITMASK 0 -- cgit v1.2.3 From c520331a52094182d6682fa7d6f630bdfbfc2974 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 5 Aug 2011 06:38:05 -0300 Subject: [media] vivi: Add an integer menu test control Add an integer menu test control for the vivi driver. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/vivi.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index b456d3ed1197..ff39eb2f2d7b 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -177,6 +177,7 @@ struct vivi_dev { struct v4l2_ctrl *menu; struct v4l2_ctrl *string; struct v4l2_ctrl *bitmask; + struct v4l2_ctrl *int_menu; spinlock_t slock; struct mutex mutex; @@ -503,6 +504,10 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->boolean->cur.val, dev->menu->qmenu[dev->menu->cur.val], dev->string->cur.string); + snprintf(str, sizeof(str), " integer_menu %lld, value %d ", + dev->int_menu->qmenu_int[dev->int_menu->cur.val], + dev->int_menu->cur.val); + gen_text(dev, vbuf, line++ * 16, 16, str); mutex_unlock(&dev->ctrl_handler.lock); gen_text(dev, vbuf, line++ * 16, 16, str); if (dev->button_pressed) { @@ -1158,6 +1163,22 @@ static const struct v4l2_ctrl_config vivi_ctrl_bitmask = { .step = 0, }; +static const s64 vivi_ctrl_int_menu_values[] = { + 1, 1, 2, 3, 5, 8, 13, 21, 42, +}; + +static const struct v4l2_ctrl_config vivi_ctrl_int_menu = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 7, + .name = "Integer menu", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .min = 1, + .max = 8, + .def = 4, + .menu_skip_mask = 0x02, + .qmenu_int = vivi_ctrl_int_menu_values, +}; + static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, @@ -1268,6 +1289,7 @@ static int __init vivi_create_instance(int inst) dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL); dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL); dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL); + dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL); if (hdl->error) { ret = hdl->error; goto unreg_dev; -- cgit v1.2.3 From ae184cda8d0eebfea6cf217abc3f94a7cfffe24d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 14 Oct 2011 14:14:26 -0300 Subject: [media] v4l: VIDIOC_SUBDEV_S_SELECTION and VIDIOC_SUBDEV_G_SELECTION IOCTLs Add support for VIDIOC_SUBDEV_S_SELECTION and VIDIOC_SUBDEV_G_SELECTION IOCTLs. They replace functionality provided by VIDIOC_SUBDEV_S_CROP and VIDIOC_SUBDEV_G_CROP IOCTLs and also add new functionality (composing). VIDIOC_SUBDEV_G_CROP and VIDIOC_SUBDEV_S_CROP continue to be supported. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-subdev.c | 42 +++++++++++++++++++++++++++++---------- include/linux/v4l2-subdev.h | 41 ++++++++++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 21 ++++++++++++++++---- 3 files changed, 90 insertions(+), 14 deletions(-) diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 6fe88e965a8c..7d225389bfb1 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -35,14 +35,9 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - /* Allocate try format and crop in the same memory block */ - fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) - * sd->entity.num_pads, GFP_KERNEL); - if (fh->try_fmt == NULL) + fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL); + if (fh->pad == NULL) return -ENOMEM; - - fh->try_crop = (struct v4l2_rect *) - (fh->try_fmt + sd->entity.num_pads); #endif return 0; } @@ -50,9 +45,8 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) static void subdev_fh_free(struct v4l2_subdev_fh *fh) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - kfree(fh->try_fmt); - fh->try_fmt = NULL; - fh->try_crop = NULL; + kfree(fh->pad); + fh->pad = NULL; #endif } @@ -293,6 +287,34 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, fie); } + + case VIDIOC_SUBDEV_G_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, get_selection, subdev_fh, sel); + } + + case VIDIOC_SUBDEV_S_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, set_selection, subdev_fh, sel); + } #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index ed29cbbebfef..812019ee1e06 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -123,6 +123,43 @@ struct v4l2_subdev_frame_interval_enum { __u32 reserved[9]; }; +#define V4L2_SUBDEV_SEL_FLAG_SIZE_GE (1 << 0) +#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE (1 << 1) +#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG (1 << 2) + +/* active cropping area */ +#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL 0x0000 +/* cropping bounds */ +#define V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS 0x0002 +/* current composing area */ +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL 0x0100 +/* composing bounds */ +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS 0x0102 + + +/** + * struct v4l2_subdev_selection - selection info + * + * @which: either V4L2_SUBDEV_FORMAT_ACTIVE or V4L2_SUBDEV_FORMAT_TRY + * @pad: pad number, as reported by the media API + * @target: selection target, used to choose one of possible rectangles + * @flags: constraint flags + * @r: coordinates of the selection window + * @reserved: for future use, set to zero for now + * + * Hardware may use multiple helper windows to process a video stream. + * The structure is used to exchange this selection areas between + * an application and a driver. + */ +struct v4l2_subdev_selection { + __u32 which; + __u32 pad; + __u32 target; + __u32 flags; + struct v4l2_rect r; + __u32 reserved[8]; +}; + #define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format) #define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format) #define VIDIOC_SUBDEV_G_FRAME_INTERVAL \ @@ -137,5 +174,9 @@ struct v4l2_subdev_frame_interval_enum { _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum) #define VIDIOC_SUBDEV_G_CROP _IOWR('V', 59, struct v4l2_subdev_crop) #define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) +#define VIDIOC_SUBDEV_G_SELECTION \ + _IOWR('V', 61, struct v4l2_subdev_selection) +#define VIDIOC_SUBDEV_S_SELECTION \ + _IOWR('V', 62, struct v4l2_subdev_selection) #endif diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f0f3358d1b1b..feab950bc8ab 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -466,6 +466,10 @@ struct v4l2_subdev_pad_ops { struct v4l2_subdev_crop *crop); int (*get_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_crop *crop); + int (*get_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel); + int (*set_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel); }; struct v4l2_subdev_ops { @@ -549,8 +553,11 @@ struct v4l2_subdev { struct v4l2_subdev_fh { struct v4l2_fh vfh; #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - struct v4l2_mbus_framefmt *try_fmt; - struct v4l2_rect *try_crop; + struct { + struct v4l2_mbus_framefmt try_fmt; + struct v4l2_rect try_crop; + struct v4l2_rect try_compose; + } *pad; #endif }; @@ -561,13 +568,19 @@ struct v4l2_subdev_fh { static inline struct v4l2_mbus_framefmt * v4l2_subdev_get_try_format(struct v4l2_subdev_fh *fh, unsigned int pad) { - return &fh->try_fmt[pad]; + return &fh->pad[pad].try_fmt; } static inline struct v4l2_rect * v4l2_subdev_get_try_crop(struct v4l2_subdev_fh *fh, unsigned int pad) { - return &fh->try_crop[pad]; + return &fh->pad[pad].try_crop; +} + +static inline struct v4l2_rect * +v4l2_subdev_get_try_compose(struct v4l2_subdev_fh *fh, unsigned int pad) +{ + return &fh->pad[pad].try_compose; } #endif -- cgit v1.2.3 From c5a766ceb497078459115fcbd1412917083aa4a5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 15 Feb 2012 22:58:12 -0300 Subject: [media] v4l: vdev_to_v4l2_subdev() should have return type "struct v4l2_subdev *" vdev_to_v4l2_subdev() should return struct v4l2_subdev *, not void *. Fix this. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index feab950bc8ab..bcaf6b80bb20 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -545,7 +545,7 @@ struct v4l2_subdev { #define media_entity_to_v4l2_subdev(ent) \ container_of(ent, struct v4l2_subdev, entity) #define vdev_to_v4l2_subdev(vdev) \ - video_get_drvdata(vdev) + ((struct v4l2_subdev *)video_get_drvdata(vdev)) /* * Used for storing subdev information per file handle -- cgit v1.2.3 From 5e6ff7c17bf468b8bc012e49174771e5f718e72c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 15 Feb 2012 22:57:22 -0300 Subject: [media] v4l: Check pad number in get try pointer functions Unify functions to get try pointers and validate the pad number accessed by the user. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index bcaf6b80bb20..7e850355a6f0 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -565,23 +565,19 @@ struct v4l2_subdev_fh { container_of(fh, struct v4l2_subdev_fh, vfh) #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) -static inline struct v4l2_mbus_framefmt * -v4l2_subdev_get_try_format(struct v4l2_subdev_fh *fh, unsigned int pad) -{ - return &fh->pad[pad].try_fmt; -} - -static inline struct v4l2_rect * -v4l2_subdev_get_try_crop(struct v4l2_subdev_fh *fh, unsigned int pad) -{ - return &fh->pad[pad].try_crop; -} - -static inline struct v4l2_rect * -v4l2_subdev_get_try_compose(struct v4l2_subdev_fh *fh, unsigned int pad) -{ - return &fh->pad[pad].try_compose; -} +#define __V4L2_SUBDEV_MK_GET_TRY(rtype, fun_name, field_name) \ + static inline struct rtype * \ + v4l2_subdev_get_try_##fun_name(struct v4l2_subdev_fh *fh, \ + unsigned int pad) \ + { \ + BUG_ON(unlikely(pad >= vdev_to_v4l2_subdev( \ + fh->vfh.vdev)->entity.num_pads)); \ + return &fh->pad[pad].field_name; \ + } + +__V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, format, try_fmt) +__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, crop, try_compose) +__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, compose, try_compose) #endif extern const struct v4l2_file_operations v4l2_subdev_fops; -- cgit v1.2.3 From 5b9d770fa3f5cf210b31137404ae702a33e00473 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 14 Nov 2011 15:30:24 -0300 Subject: [media] v4l: Support s_crop and g_crop through s/g_selection Fall back to s_selection if s_crop isn't implemented by a driver. Same for g_selection / g_crop. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-subdev.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 7d225389bfb1..268d80584101 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -228,6 +228,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_CROP: { struct v4l2_subdev_crop *crop = arg; + struct v4l2_subdev_selection sel; + int rval; if (crop->which != V4L2_SUBDEV_FORMAT_TRY && crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) @@ -236,11 +238,27 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (crop->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); + rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); + if (rval != -ENOIOCTLCMD) + return rval; + + memset(&sel, 0, sizeof(sel)); + sel.which = crop->which; + sel.pad = crop->pad; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + + rval = v4l2_subdev_call( + sd, pad, get_selection, subdev_fh, &sel); + + crop->rect = sel.r; + + return rval; } case VIDIOC_SUBDEV_S_CROP: { struct v4l2_subdev_crop *crop = arg; + struct v4l2_subdev_selection sel; + int rval; if (crop->which != V4L2_SUBDEV_FORMAT_TRY && crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) @@ -249,7 +267,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (crop->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); + rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); + if (rval != -ENOIOCTLCMD) + return rval; + + memset(&sel, 0, sizeof(sel)); + sel.which = crop->which; + sel.pad = crop->pad; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.r = crop->rect; + + rval = v4l2_subdev_call( + sd, pad, set_selection, subdev_fh, &sel); + + crop->rect = sel.r; + + return rval; } case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { -- cgit v1.2.3 From 20676a4c9503dbcbb283c4c6565426dc63b81775 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 21 Mar 2012 06:21:30 -0300 Subject: [media] s5p-fimc: Don't use platform data for CSI data alignment configuration The MIPI-CSI2 data alignment parameter can be derived from media bus pixel code, so it can be now dropped from the platform data structure. This is a prerequisite for adding the device tree support. Once this patch is merged the corresponding fields will be removed from the drivers' public headers and corresponding board files. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-reg.c | 3 ++- drivers/media/video/s5p-fimc/mipi-csis.c | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 15466d0529c1..ff11f10fea0b 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -674,6 +674,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, { u32 cfg, tmp; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + u32 csis_data_alignment = 32; cfg = readl(fimc->regs + S5P_CIGCTRL); @@ -703,7 +704,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, vid_cap->mf.code); return -EINVAL; } - tmp |= (cam->csi_data_align == 32) << 8; + tmp |= (csis_data_alignment == 32) << 8; writel(tmp, fimc->regs + S5P_CSIIMGFMT); diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c index f44f690397f7..1cd6b6bc6279 100644 --- a/drivers/media/video/s5p-fimc/mipi-csis.c +++ b/drivers/media/video/s5p-fimc/mipi-csis.c @@ -127,20 +127,24 @@ struct csis_state { * multiple of 2^pix_width_alignment * @code: corresponding media bus code * @fmt_reg: S5PCSIS_CONFIG register value + * @data_alignment: MIPI-CSI data alignment in bits */ struct csis_pix_format { unsigned int pix_width_alignment; enum v4l2_mbus_pixelcode code; u32 fmt_reg; + u8 data_alignment; }; static const struct csis_pix_format s5pcsis_formats[] = { { .code = V4L2_MBUS_FMT_VYUY8_2X8, .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT, + .data_alignment = 32, }, { .code = V4L2_MBUS_FMT_JPEG_1X8, .fmt_reg = S5PCSIS_CFG_FMT_USER(1), + .data_alignment = 32, }, }; @@ -239,7 +243,7 @@ static void s5pcsis_set_params(struct csis_state *state) s5pcsis_set_hsync_settle(state, pdata->hs_settle); val = s5pcsis_read(state, S5PCSIS_CTRL); - if (pdata->alignment == 32) + if (state->csis_fmt->data_alignment == 32) val |= S5PCSIS_CTRL_ALIGN_32BIT; else /* 24-bits */ val &= ~S5PCSIS_CTRL_ALIGN_32BIT; -- cgit v1.2.3 From aa333122c9c7d11d7d8486db09869517995af0a8 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 16 Mar 2012 11:47:51 -0300 Subject: [media] s5p-fimc: Reinitialize the pipeline properly after VIDIOC_STREAMOFF This patch prevents blocking on DQBUF at a video capture node in some conditions, after STREAOMOFF/STREAMON sequence. The ST_CAPT_SUSPEND flag should not be set during normal stream off, otherwise the capture engine is not properly enabled at stream on. Reported-by: Bernard Debbasch Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index b06efd208328..a080f0c91e35 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -83,7 +83,9 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM); - if (!suspend) + if (suspend) + fimc->state |= (1 << ST_CAPT_SUSPENDED); + else fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED); /* Release unused buffers */ @@ -99,7 +101,6 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) else vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } - set_bit(ST_CAPT_SUSPENDED, &fimc->state); fimc_hw_reset(fimc); cap->buf_index = 0; -- cgit v1.2.3 From efb13c3d4d969199eaaae3b3540b919f7f149448 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 19 Mar 2012 13:11:40 -0300 Subject: [media] s5p-fimc: Simplify locking by removing the context data structure spinlock Access to the memory-to-memory video node is serialized through a mutex so now there is no point in having per device context structure spinlock. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 28 ++++++++++++++-------------- drivers/media/video/s5p-fimc/fimc-core.c | 23 ++++++++--------------- drivers/media/video/s5p-fimc/fimc-core.h | 12 +++++------- 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index a080f0c91e35..dc18ba510986 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -147,21 +147,22 @@ int fimc_capture_config_update(struct fimc_ctx *ctx) if (!test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) return 0; - spin_lock(&ctx->slock); fimc_hw_set_camera_offset(fimc, &ctx->s_frame); + ret = fimc_set_scaler_info(ctx); - if (ret == 0) { - fimc_hw_set_prescaler(ctx); - fimc_hw_set_mainscaler(ctx); - fimc_hw_set_target_format(ctx); - fimc_hw_set_rotation(ctx); - fimc_prepare_dma_offset(ctx, &ctx->d_frame); - fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) - fimc_hw_set_rgb_alpha(ctx); - clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); - } - spin_unlock(&ctx->slock); + if (ret) + return ret; + + fimc_hw_set_prescaler(ctx); + fimc_hw_set_mainscaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_prepare_dma_offset(ctx, &ctx->d_frame); + fimc_hw_set_out_dma(ctx); + if (fimc->variant->has_alpha) + fimc_hw_set_rgb_alpha(ctx); + + clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); return ret; } @@ -1525,7 +1526,6 @@ int fimc_register_capture_device(struct fimc_dev *fimc, INIT_LIST_HEAD(&vid_cap->pending_buf_q); INIT_LIST_HEAD(&vid_cap->active_buf_q); - spin_lock_init(&ctx->slock); vid_cap->ctx = ctx; q = &fimc->vid_cap.vbq; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index e184e650022a..8a5951f54d7d 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -320,7 +320,7 @@ static int fimc_m2m_shutdown(struct fimc_ctx *ctx) if (!fimc_m2m_pending(fimc)) return 0; - fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx); + fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); ret = wait_event_timeout(fimc->irq_queue, !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), @@ -430,14 +430,12 @@ static irqreturn_t fimc_irq_handler(int irq, void *priv) spin_unlock(&fimc->slock); fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); - spin_lock(&ctx->slock); if (ctx->state & FIMC_CTX_SHUT) { ctx->state &= ~FIMC_CTX_SHUT; wake_up(&fimc->irq_queue); } - spin_unlock(&ctx->slock); + return IRQ_HANDLED; } - return IRQ_HANDLED; } else if (test_bit(ST_CAPT_PEND, &fimc->state)) { fimc_capture_irq_handler(fimc, !test_bit(ST_CAPT_JPEG, &fimc->state)); @@ -644,7 +642,6 @@ static void fimc_dma_run(void *priv) spin_lock_irqsave(&fimc->slock, flags); set_bit(ST_M2M_PEND, &fimc->state); - spin_lock(&ctx->slock); ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR); ret = fimc_prepare_config(ctx, ctx->state); if (ret) @@ -661,10 +658,8 @@ static void fimc_dma_run(void *priv) fimc_hw_set_input_path(ctx); fimc_hw_set_in_dma(ctx); ret = fimc_set_scaler_info(ctx); - if (ret) { - spin_unlock(&fimc->slock); + if (ret) goto dma_unlock; - } fimc_hw_set_prescaler(ctx); fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); @@ -688,7 +683,6 @@ static void fimc_dma_run(void *priv) FIMC_SRC_FMT | FIMC_DST_FMT); fimc_hw_activate_input_dma(fimc, true); dma_unlock: - spin_unlock(&ctx->slock); spin_unlock_irqrestore(&fimc->slock, flags); } @@ -827,9 +821,9 @@ static int fimc_s_ctrl(struct v4l2_ctrl *ctrl) unsigned long flags; int ret; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ret = __fimc_s_ctrl(ctx, ctrl); - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); return ret; } @@ -1174,9 +1168,9 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, ctx->scaler.enabled = 1; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); + fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); else - fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); + fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); @@ -1363,7 +1357,7 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) f->width = cr->c.width; f->height = cr->c.height; - fimc_ctx_state_lock_set(FIMC_PARAMS, ctx); + fimc_ctx_state_set(FIMC_PARAMS, ctx); return 0; } @@ -1467,7 +1461,6 @@ static int fimc_m2m_open(struct file *file) ctx->flags = 0; ctx->in_path = FIMC_DMA; ctx->out_path = FIMC_DMA; - spin_lock_init(&ctx->slock); ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index a18291e648e2..54198c781fe1 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -465,7 +465,6 @@ struct fimc_dev { /** * fimc_ctx - the device context data - * @slock: spinlock protecting this data structure * @s_frame: source frame properties * @d_frame: destination frame properties * @out_order_1p: output 1-plane YCBCR order @@ -492,7 +491,6 @@ struct fimc_dev { * @ctrls_rdy: true if the control handler is initialized */ struct fimc_ctx { - spinlock_t slock; struct fimc_frame s_frame; struct fimc_frame d_frame; u32 out_order_1p; @@ -560,13 +558,13 @@ static inline bool fimc_capture_active(struct fimc_dev *fimc) return ret; } -static inline void fimc_ctx_state_lock_set(u32 state, struct fimc_ctx *ctx) +static inline void fimc_ctx_state_set(u32 state, struct fimc_ctx *ctx) { unsigned long flags; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ctx->state |= state; - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); } static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) @@ -574,9 +572,9 @@ static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) unsigned long flags; bool ret; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ret = (ctx->state & mask) == mask; - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); return ret; } -- cgit v1.2.3 From 935b1892d81fbf1a4426ca5140ea3fd32dce1614 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 19 Mar 2012 10:02:41 -0300 Subject: [media] s5p-fimc: Refactor hardware setup for m2m transaction Remove redundant H/W setup logic by merging fimc_prepare_config() and the device_run() callback. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-core.c | 77 +++++++++----------------------- drivers/media/video/s5p-fimc/fimc-core.h | 2 - 2 files changed, 21 insertions(+), 58 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 8a5951f54d7d..21691e4ee553 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -582,55 +582,11 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); } -/** - * fimc_prepare_config - check dimensions, operation and color mode - * and pre-calculate offset and the scaling coefficients. - * - * @ctx: hardware context information - * @flags: flags indicating which parameters to check/update - * - * Return: 0 if dimensions are valid or non zero otherwise. - */ -int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) -{ - struct fimc_frame *s_frame, *d_frame; - struct vb2_buffer *vb = NULL; - int ret = 0; - - s_frame = &ctx->s_frame; - d_frame = &ctx->d_frame; - - if (flags & FIMC_PARAMS) { - /* Prepare the DMA offset ratios for scaler. */ - fimc_prepare_dma_offset(ctx, &ctx->s_frame); - fimc_prepare_dma_offset(ctx, &ctx->d_frame); - - if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) || - s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) { - err("out of scaler range"); - return -EINVAL; - } - fimc_set_yuv_order(ctx); - } - - if (flags & FIMC_SRC_ADDR) { - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr); - if (ret) - return ret; - } - - if (flags & FIMC_DST_ADDR) { - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, d_frame, &d_frame->paddr); - } - - return ret; -} - static void fimc_dma_run(void *priv) { + struct vb2_buffer *vb = NULL; struct fimc_ctx *ctx = priv; + struct fimc_frame *sf, *df; struct fimc_dev *fimc; unsigned long flags; u32 ret; @@ -641,9 +597,22 @@ static void fimc_dma_run(void *priv) fimc = ctx->fimc_dev; spin_lock_irqsave(&fimc->slock, flags); set_bit(ST_M2M_PEND, &fimc->state); + sf = &ctx->s_frame; + df = &ctx->d_frame; + + if (ctx->state & FIMC_PARAMS) { + /* Prepare the DMA offsets for scaler */ + fimc_prepare_dma_offset(ctx, sf); + fimc_prepare_dma_offset(ctx, df); + } - ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR); - ret = fimc_prepare_config(ctx, ctx->state); + vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); + if (ret) + goto dma_unlock; + + vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); if (ret) goto dma_unlock; @@ -652,9 +621,9 @@ static void fimc_dma_run(void *priv) ctx->state |= FIMC_PARAMS; fimc->m2m.ctx = ctx; } - fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr); if (ctx->state & FIMC_PARAMS) { + fimc_set_yuv_order(ctx); fimc_hw_set_input_path(ctx); fimc_hw_set_in_dma(ctx); ret = fimc_set_scaler_info(ctx); @@ -665,17 +634,13 @@ static void fimc_dma_run(void *priv) fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx, false); - } - - fimc_hw_set_output_path(ctx); - if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS)) - fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr, -1); - - if (ctx->state & FIMC_PARAMS) { fimc_hw_set_out_dma(ctx); if (fimc->variant->has_alpha) fimc_hw_set_rgb_alpha(ctx); + fimc_hw_set_output_path(ctx); } + fimc_hw_set_input_addr(fimc, &sf->paddr); + fimc_hw_set_output_addr(fimc, &df->paddr, -1); fimc_activate_capture(ctx); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 54198c781fe1..101c930f08b8 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -119,8 +119,6 @@ enum fimc_color_fmt { /* The hardware context state. */ #define FIMC_PARAMS (1 << 0) -#define FIMC_SRC_ADDR (1 << 1) -#define FIMC_DST_ADDR (1 << 2) #define FIMC_SRC_FMT (1 << 3) #define FIMC_DST_FMT (1 << 4) #define FIMC_DST_CROP (1 << 5) -- cgit v1.2.3 From 6ec0163b7952ad087844338806402468fb06a32c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 17 Mar 2012 18:31:33 -0300 Subject: [media] s5p-fimc: Remove unneeded fields from struct fimc_dev irq is used only locally and num_clocks is constant, so remove them. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-core.c | 8 +++----- drivers/media/video/s5p-fimc/fimc-core.h | 4 ---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 21691e4ee553..4d6b86791187 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1559,7 +1559,7 @@ void fimc_unregister_m2m_device(struct fimc_dev *fimc) static void fimc_clk_put(struct fimc_dev *fimc) { int i; - for (i = 0; i < fimc->num_clocks; i++) { + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { if (IS_ERR_OR_NULL(fimc->clock[i])) continue; clk_unprepare(fimc->clock[i]); @@ -1572,7 +1572,7 @@ static int fimc_clk_get(struct fimc_dev *fimc) { int i, ret; - for (i = 0; i < fimc->num_clocks; i++) { + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); if (IS_ERR(fimc->clock[i])) goto err; @@ -1672,9 +1672,7 @@ static int fimc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to get IRQ resource\n"); return -ENXIO; } - fimc->irq = res->start; - fimc->num_clocks = MAX_FIMC_CLOCKS; ret = fimc_clk_get(fimc); if (ret) return ret; @@ -1683,7 +1681,7 @@ static int fimc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fimc); - ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler, + ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, 0, pdev->name, fimc); if (ret) { dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 101c930f08b8..193e8f603949 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -429,10 +429,8 @@ struct fimc_ctx; * @pdata: pointer to the device platform data * @variant: the IP variant information * @id: FIMC device index (0..FIMC_MAX_DEVS) - * @num_clocks: the number of clocks managed by this device instance * @clock: clocks required for FIMC operation * @regs: the mapped hardware registers - * @irq: FIMC interrupt number * @irq_queue: interrupt handler waitqueue * @v4l2_dev: root v4l2_device * @m2m: memory-to-memory V4L2 device information @@ -448,10 +446,8 @@ struct fimc_dev { struct s5p_platform_fimc *pdata; struct samsung_fimc_variant *variant; u16 id; - u16 num_clocks; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; - int irq; wait_queue_head_t irq_queue; struct v4l2_device *v4l2_dev; struct fimc_m2m_device m2m; -- cgit v1.2.3 From ecd9acbf545a0d7191478eea8a14331baf5ed121 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 21 Mar 2012 09:58:09 -0300 Subject: [media] s5p-fimc: Handle sub-device interdependencies using deferred probing In this driver there are several entities associated with separate platform or I2C client devices, which may get probed in random order. When the platform device bound to the media device driver is probed all other entity drivers need to be already in place and initialized. If any of them is not, fail the media device probe and return an error indicating we need to be retried once any new driver gets registered. The media device driver probe will not succeed until there are available all needed sub-drivers, as specified in the platform data. While at it, make sure the s5p-csis module (MIPI-CSI receiver driver) does not get unloaded when in use, by guarding its usage with try_module_get/module_put. This patch is a prerequisite for adding the device tree support. It now also allows again to unbind/bind the driver at runtime from user space via sysfs. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-core.c | 2 +- drivers/media/video/s5p-fimc/fimc-mdevice.c | 69 +++++++++++++++++++++++------ drivers/media/video/s5p-fimc/mipi-csis.c | 15 +------ 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 4d6b86791187..7b90a897beeb 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1992,7 +1992,7 @@ static struct platform_driver fimc_driver = { int __init fimc_register_driver(void) { - return platform_driver_probe(&fimc_driver, fimc_probe); + return platform_driver_register(&fimc_driver); } void __exit fimc_unregister_driver(void) diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 62ed37e40149..75296a625a9d 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -214,14 +214,20 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, return NULL; adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num); - if (!adapter) - return NULL; + if (!adapter) { + v4l2_warn(&fmd->v4l2_dev, + "Failed to get I2C adapter %d, deferring probe\n", + s_info->pdata->i2c_bus_num); + return ERR_PTR(-EPROBE_DEFER); + } sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, s_info->pdata->board_info, NULL); if (IS_ERR_OR_NULL(sd)) { i2c_put_adapter(adapter); - v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n"); - return NULL; + v4l2_warn(&fmd->v4l2_dev, + "Failed to acquire subdev %s, deferring probe\n", + s_info->pdata->board_info->type); + return ERR_PTR(-EPROBE_DEFER); } v4l2_set_subdev_hostdata(sd, s_info); sd->grp_id = SENSOR_GROUP_ID; @@ -269,13 +275,22 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) fmd->num_sensors = num_clients; for (i = 0; i < num_clients; i++) { + struct v4l2_subdev *sd; + fmd->sensor[i].pdata = &pdata->isp_info[i]; ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); if (ret) break; - fmd->sensor[i].subdev = - fimc_md_register_sensor(fmd, &fmd->sensor[i]); + sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); + + if (!IS_ERR(sd)) { + fmd->sensor[i].subdev = sd; + } else { + fmd->sensor[i].subdev = NULL; + ret = PTR_ERR(sd); + break; + } if (ret) break; } @@ -336,22 +351,45 @@ static int csis_register_callback(struct device *dev, void *p) */ static int fimc_md_register_platform_entities(struct fimc_md *fmd) { + struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; struct device_driver *driver; - int ret; + int ret, i; driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); - if (!driver) - return -ENODEV; + if (!driver) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + FIMC_MODULE_NAME); + return -EPROBE_DEFER; + } + ret = driver_for_each_device(driver, NULL, fmd, fimc_register_callback); if (ret) return ret; + /* + * Check if there is any sensor on the MIPI-CSI2 bus and + * if not skip the s5p-csis module loading. + */ + for (i = 0; i < pdata->num_clients; i++) { + if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) { + ret = 1; + break; + } + } + if (!ret) + return 0; driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); - if (driver) - ret = driver_for_each_device(driver, NULL, fmd, - csis_register_callback); - return ret; + if (!driver || !try_module_get(driver->owner)) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + CSIS_DRIVER_NAME); + return -EPROBE_DEFER; + } + + return driver_for_each_device(driver, NULL, fmd, + csis_register_callback); } static void fimc_md_unregister_entities(struct fimc_md *fmd) @@ -369,6 +407,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) if (fmd->csis[i].sd == NULL) continue; v4l2_device_unregister_subdev(fmd->csis[i].sd); + module_put(fmd->csis[i].sd->owner); fmd->csis[i].sd = NULL; } for (i = 0; i < fmd->num_sensors; i++) { @@ -744,7 +783,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, fimc_md_sysfs_show, fimc_md_sysfs_store); -static int __devinit fimc_md_probe(struct platform_device *pdev) +static int fimc_md_probe(struct platform_device *pdev) { struct v4l2_device *v4l2_dev; struct fimc_md *fmd; @@ -841,10 +880,12 @@ static struct platform_driver fimc_md_driver = { int __init fimc_md_init(void) { int ret; + request_module("s5p-csis"); ret = fimc_register_driver(); if (ret) return ret; + return platform_driver_register(&fimc_md_driver); } void __exit fimc_md_exit(void) diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c index 1cd6b6bc6279..2f73d9e3d0b7 100644 --- a/drivers/media/video/s5p-fimc/mipi-csis.c +++ b/drivers/media/video/s5p-fimc/mipi-csis.c @@ -715,19 +715,8 @@ static struct platform_driver s5pcsis_driver = { }, }; -static int __init s5pcsis_init(void) -{ - return platform_driver_probe(&s5pcsis_driver, s5pcsis_probe); -} - -static void __exit s5pcsis_exit(void) -{ - platform_driver_unregister(&s5pcsis_driver); -} - -module_init(s5pcsis_init); -module_exit(s5pcsis_exit); +module_platform_driver(s5pcsis_driver); MODULE_AUTHOR("Sylwester Nawrocki "); -MODULE_DESCRIPTION("S5P/EXYNOS4 MIPI CSI receiver driver"); +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f443d5878a45933e9924f755bcad28e3732e1750 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Tue, 20 Mar 2012 10:10:39 -0300 Subject: [media] lirc: delete unused init/exit function prototypes The lirc sasem and imon drivers now use the module_usb_driver macro, so the old init/exit function prototypes are useless. This patch eliminates this warnings: media_build/v4l/lirc_imon.c:74:19: warning: 'imon_init' declared 'static' but never defined [-Wunused-function] media_build/v4l/lirc_imon.c:75:20: warning: 'imon_exit' declared 'static' but never defined [-Wunused-function] media_build/v4l/lirc_sasem.c:84:19: warning: 'sasem_init' declared 'static' but never defined [-Wunused-function] media_build/v4l/lirc_sasem.c:85:20: warning: 'sasem_exit' declared 'static' but never defined [-Wunused-function] Signed-off-by: Gianluca Gennari Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_imon.c | 4 ---- drivers/staging/media/lirc/lirc_sasem.c | 4 ---- 2 files changed, 8 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 5f7f8cd3a661..083219da3a39 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -70,10 +70,6 @@ static ssize_t vfd_write(struct file *file, const char __user *buf, static int ir_open(void *data); static void ir_close(void *data); -/* Driver init/exit prototypes */ -static int __init imon_init(void); -static void __exit imon_exit(void); - /*** G L O B A L S ***/ #define IMON_DATA_BUF_SZ 35 diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index 74421043b954..d5bc35aea1c1 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -80,10 +80,6 @@ static ssize_t vfd_write(struct file *file, const char *buf, static int ir_open(void *data); static void ir_close(void *data); -/* Driver init/exit prototypes */ -static int __init sasem_init(void); -static void __exit sasem_exit(void); - /*** G L O B A L S ***/ #define SASEM_DATA_BUF_SZ 32 -- cgit v1.2.3 From 28638601cf2440a7335eb2478b773dcc63e29e7e Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 20 Mar 2012 11:33:59 -0300 Subject: [media] i.MX2: eMMa-PrP: Allow userptr IO mode Userptr can be very useful if this device is requested to use video buffers allocated by another processing device. So that buffers don't need to be copied. Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mx2_emmaprp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c index ba89a7401c8c..55ac1735e85b 100644 --- a/drivers/media/video/mx2_emmaprp.c +++ b/drivers/media/video/mx2_emmaprp.c @@ -755,7 +755,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_MMAP; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &emmaprp_qops; @@ -767,7 +767,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_MMAP; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &emmaprp_qops; -- cgit v1.2.3 From 004ac38859c5bbcc4ffccc37d33be5e467d61c28 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 20 Mar 2012 14:05:40 -0300 Subject: [media] [3.3.0] ir-raw: remove BUG_ON in ir_raw_event_thread This patch removes BUG_ON in ir_raw_event_thread which IMO is a over-kill, and this kills the ir_raw_event_thread too. With a bit of additional logic in this patch, we nomore need to kill this thread. Other disadvantage of having a BUG-ON is, wake_up_process(dev->raw->thread) called on dead thread via ir_raw_event_handle will result in total lockup in SMP system. Advantage of this patch is ir-raw event thread is left in a usable state even if the fifo does not have enough bytes. This patch sets the thread into TASK_INTERRUPTIBLE if raw-fifo has less then sizeof(struct ir_raw_event) bytes. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-raw.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 95e630998aaf..a82025121345 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -46,9 +46,9 @@ static int ir_raw_event_thread(void *data) while (!kthread_should_stop()) { spin_lock_irq(&raw->lock); - retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); + retval = kfifo_len(&raw->kfifo); - if (!retval) { + if (retval < sizeof(ev)) { set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) @@ -59,11 +59,9 @@ static int ir_raw_event_thread(void *data) continue; } + retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); spin_unlock_irq(&raw->lock); - - BUG_ON(retval != sizeof(ev)); - mutex_lock(&ir_raw_handler_lock); list_for_each_entry(handler, &ir_raw_handler_list, list) handler->decode(raw->dev, ev); -- cgit v1.2.3 From 72da1f20afae1bd3f7ee4a27b2b8cda8838d61ef Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 21 Mar 2012 02:35:23 -0300 Subject: [media] uvcvideo: remove unneeded access_ok() check copy_in_user() already checks for write permission, so we don't need to do it here. This was added in 1a5e4c867c "[media] uvcvideo: Implement compat_ioctl32 for custom ioctls". Signed-off-by: Dan Carpenter Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_v4l2.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index ff2cdddf9bc6..111bfff1640d 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -1105,8 +1105,6 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, if (get_user(p, &up->menu_info)) return -EFAULT; umenus = compat_ptr(p); - if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus))) - return -EFAULT; if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) return -EFAULT; -- cgit v1.2.3 From 7cfce77d779f43299c1cfeddd72462fed596c168 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Wed, 21 Mar 2012 02:56:33 -0300 Subject: [media] mm/drivers: use vm_flags_t for vma flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Konstantin Khlebnikov Cc: linux-media@vger.kernel.org Cc: devel@driverdev.osuosl.org Cc: Laurent Pinchart Cc: Mauro Carvalho Chehab Cc: Greg Kroah-Hartman Cc: John Stultz Cc: "Arve HjønnevÃ¥g" Acked-by: Laurent Pinchart Acked-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispqueue.h | 2 +- drivers/staging/android/ashmem.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/omap3isp/ispqueue.h b/drivers/media/video/omap3isp/ispqueue.h index 92c5a12157d5..908dfd712e8e 100644 --- a/drivers/media/video/omap3isp/ispqueue.h +++ b/drivers/media/video/omap3isp/ispqueue.h @@ -90,7 +90,7 @@ struct isp_video_buffer { void *vaddr; /* For userspace buffers. */ - unsigned long vm_flags; + vm_flags_t vm_flags; unsigned long offset; unsigned int npages; struct page **pages; diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 9f1f27e7c86e..4511420849bc 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -269,7 +269,7 @@ out: return ret; } -static inline unsigned long calc_vm_may_flags(unsigned long prot) +static inline vm_flags_t calc_vm_may_flags(unsigned long prot) { return _calc_vm_trans(prot, PROT_READ, VM_MAYREAD) | _calc_vm_trans(prot, PROT_WRITE, VM_MAYWRITE) | -- cgit v1.2.3 From 95faf82bd3ea607f8e1ca78930c49b71078266e6 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Thu, 22 Mar 2012 00:50:37 -0300 Subject: [media] usb: gadget/uvc: Remove non-required locking from 'uvc_queue_next_buffer' routine This patch removes the non-required spinlock acquire/release calls on 'queue_irqlock' from 'uvc_queue_next_buffer' routine. This routine is called from 'video->encode' function (which translates to either 'uvc_video_encode_bulk' or 'uvc_video_encode_isoc') in 'uvc_video.c'. As, the 'video->encode' routines are called with 'queue_irqlock' already held, so acquiring a 'queue_irqlock' again in 'uvc_queue_next_buffer' routine causes a spin lock recursion. Signed-off-by: Bhupesh Sharma Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/usb/gadget/uvc_queue.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index d776adb2da67..104ae9c81251 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -543,11 +543,11 @@ done: return ret; } +/* called with &queue_irqlock held.. */ static struct uvc_buffer * uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) { struct uvc_buffer *nextbuf; - unsigned long flags; if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && buf->buf.length != buf->buf.bytesused) { @@ -556,14 +556,12 @@ uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) return buf; } - spin_lock_irqsave(&queue->irqlock, flags); list_del(&buf->queue); if (!list_empty(&queue->irqqueue)) nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, queue); else nextbuf = NULL; - spin_unlock_irqrestore(&queue->irqlock, flags); buf->buf.sequence = queue->sequence++; do_gettimeofday(&buf->buf.timestamp); -- cgit v1.2.3 From 5f5f147f638734c0739b663aa68a3ae03cdc682b Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Thu, 22 Mar 2012 08:48:17 -0300 Subject: [media] em28xx-dvb: stop URBs when stopping the streaming Stop the URBs in em28xx_stop_streaming(), so that em28xx_irq_callback() cannot be called after the streaming has stopped. This should eliminate the crashes reported by Antti Palosaari and the warnings reported by Andy Furniss. Signed-off-by: Gianluca Gennari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-core.c | 26 +++++++++++++++++++++++++- drivers/media/video/em28xx/em28xx-dvb.c | 2 +- drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 53a9fb91e97e..cbbe399bc77f 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -666,7 +666,6 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } -EXPORT_SYMBOL_GPL(em28xx_capture_start); int em28xx_vbi_supported(struct em28xx *dev) { @@ -1007,6 +1006,31 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) } EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); +/* + * Stop URBs + */ +void em28xx_stop_urbs(struct em28xx *dev) +{ + int i; + struct urb *urb; + struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + + em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); + + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = isoc_bufs->urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + } + } + + em28xx_capture_start(dev, 0); +} +EXPORT_SYMBOL_GPL(em28xx_stop_urbs); + /* * Allocate URBs */ diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 503a8d5b5382..2d73ee2abf7e 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -183,7 +183,7 @@ static int em28xx_stop_streaming(struct em28xx_dvb *dvb) { struct em28xx *dev = dvb->adapter.priv; - em28xx_capture_start(dev, 0); + em28xx_stop_urbs(dev); em28xx_set_mode(dev, EM28XX_SUSPEND); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 2868b19f8b54..286b9f8b0022 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -695,6 +695,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, int max_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); +void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); -- cgit v1.2.3 From 5978ec646e9063b8d083a2fbdc7fbda599336334 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Thu, 22 Mar 2012 11:13:55 -0300 Subject: [media] em28xx: clean-up several unused parametrs in struct em28xx_usb_isoc_ctl Get rid of several unused parameters in struct em28xx_usb_isoc_ctl. Signed-off-by: Gianluca Gennari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-core.c | 1 - drivers/media/video/em28xx/em28xx.h | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index cbbe399bc77f..fd54c807cf29 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -974,7 +974,6 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) else isoc_bufs = &dev->isoc_ctl.analog_bufs; - dev->isoc_ctl.nfields = -1; for (i = 0; i < isoc_bufs->num_bufs; i++) { urb = isoc_bufs->urb[i]; if (urb) { diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 286b9f8b0022..c2a5a99398b0 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -226,24 +226,10 @@ struct em28xx_usb_isoc_ctl { /* isoc transfer buffers for digital mode */ struct em28xx_usb_isoc_bufs digital_bufs; - /* Last buffer command and region */ - u8 cmd; - int pos, size, pktsize; - - /* Last field: ODD or EVEN? */ - int field; - - /* Stores incomplete commands */ - u32 tmp_buf; - int tmp_buf_len; - /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; struct em28xx_buffer *vbi_buf; - /* Stores the number of received fields */ - int nfields; - /* isoc urb callback */ int (*isoc_copy) (struct em28xx *dev, struct urb *urb); -- cgit v1.2.3 From 59ef29cc86afb3a22b2ba96a1c276d332639fc30 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 2 Feb 2012 09:30:00 -0300 Subject: [media] v4l: Add subdev selections documentation: svg and dia files Add svga and dia files for V4L2 subdev selections documentation. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- .../media/v4l/subdev-image-processing-crop.dia | 614 ++++++++ .../media/v4l/subdev-image-processing-crop.svg | 63 + .../media/v4l/subdev-image-processing-full.dia | 1588 ++++++++++++++++++++ .../media/v4l/subdev-image-processing-full.svg | 163 ++ ...ubdev-image-processing-scaling-multi-source.dia | 1152 ++++++++++++++ ...ubdev-image-processing-scaling-multi-source.svg | 116 ++ 6 files changed, 3696 insertions(+) create mode 100644 Documentation/DocBook/media/v4l/subdev-image-processing-crop.dia create mode 100644 Documentation/DocBook/media/v4l/subdev-image-processing-crop.svg create mode 100644 Documentation/DocBook/media/v4l/subdev-image-processing-full.dia create mode 100644 Documentation/DocBook/media/v4l/subdev-image-processing-full.svg create mode 100644 Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.dia create mode 100644 Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.svg diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-crop.dia b/Documentation/DocBook/media/v4l/subdev-image-processing-crop.dia new file mode 100644 index 000000000000..e32ba5362e1d --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-crop.dia @@ -0,0 +1,614 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 1 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 0 (sink)# + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-crop.svg b/Documentation/DocBook/media/v4l/subdev-image-processing-crop.svg new file mode 100644 index 000000000000..18b0f5de9ed2 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-crop.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + sink + crop + selection + + + + + + sink media + bus format + + + source media + bus format + + + + + + + + + + + + + + + + + + + + + pad 1 (source) + + + + + + + + + + + + + pad 0 (sink) + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-full.dia b/Documentation/DocBook/media/v4l/subdev-image-processing-full.dia new file mode 100644 index 000000000000..a0d782927840 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-full.dia @@ -0,0 +1,1588 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 0 (sink)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 2 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink compose +selection (scaling)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink compose +bounds selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 1 (sink)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 3 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-full.svg b/Documentation/DocBook/media/v4l/subdev-image-processing-full.svg new file mode 100644 index 000000000000..3322cf4c0093 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-full.svg @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pad 0 (sink) + + + pad 2 (source) + + + + + + + + + + + + + + sink media + bus format + + + + + + + + + + + sink compose + selection (scaling) + + + + + + + source media + bus format + + + + + + + + + + + sink compose + bounds selection + + + + + + + + + + + + + pad 1 (sink) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pad 3 (source) + + + sink + crop + selection + + + source + crop + selection + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.dia b/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.dia new file mode 100644 index 000000000000..0cd50a7bda80 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.dia @@ -0,0 +1,1152 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink compose +selection (scaling)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 1 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 0 (sink)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 2 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.svg b/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.svg new file mode 100644 index 000000000000..2340c0f8bc92 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + sink + crop + selection + + + + + + sink media + bus format + + + + + + + + + + + sink compose + selection (scaling) + + + + + + + source + crop + selection + + + source media + bus format + + + + + + + + + + + + + + + + + + + + + pad 1 (source) + + + + + + + + + + + + + pad 0 (sink) + + + + + + + + + + + + + + + + + + + + + + pad 2 (source) + + + + + + + + + + + + -- cgit v1.2.3 From 955f645aea04f3130d39c7889e2c5bd7f6d638a3 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 12 Dec 2011 18:17:25 -0300 Subject: [media] v4l: Add subdev selections documentation Add documentation for V4L2 subdev selection API. This changes also experimental V4L2 subdev API so that scaling now works through selection API only. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/Makefile | 4 +- Documentation/DocBook/media/v4l/compat.xml | 9 + Documentation/DocBook/media/v4l/dev-subdev.xml | 202 +++++++++++++++--- Documentation/DocBook/media/v4l/v4l2.xml | 15 +- .../media/v4l/vidioc-subdev-g-selection.xml | 228 +++++++++++++++++++++ 5 files changed, 432 insertions(+), 26 deletions(-) create mode 100644 Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile index 6628b4b9cac4..362520992ced 100644 --- a/Documentation/DocBook/media/Makefile +++ b/Documentation/DocBook/media/Makefile @@ -70,6 +70,8 @@ IOCTLS = \ VIDIOC_SUBDEV_ENUM_MBUS_CODE \ VIDIOC_SUBDEV_ENUM_FRAME_SIZE \ VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL \ + VIDIOC_SUBDEV_G_SELECTION \ + VIDIOC_SUBDEV_S_SELECTION \ TYPES = \ $(shell perl -ne 'print "$$1 " if /^typedef\s+[^\s]+\s+([^\s]+)\;/' $(srctree)/include/linux/videodev2.h) \ @@ -193,7 +195,7 @@ DVB_DOCUMENTED = \ # install_media_images = \ - $(Q)cp $(OBJIMGFILES) $(MEDIA_OBJ_DIR)/media_api + $(Q)cp $(OBJIMGFILES) $(MEDIA_SRC_DIR)/v4l/*.svg $(MEDIA_OBJ_DIR)/media_api $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%.b64 $(Q)base64 -d $< >$@ diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index d881520e1a54..0fc74ca81f0f 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2417,6 +2417,11 @@ details. Added integer menus, the new type will be V4L2_CTRL_TYPE_INTEGER_MENU. + + Added selection API for V4L2 subdev interface: + &VIDIOC-SUBDEV-G-SELECTION; and + &VIDIOC-SUBDEV-S-SELECTION;. + @@ -2533,6 +2538,10 @@ ioctls. Selection API. + + Sub-device selection API: &VIDIOC-SUBDEV-G-SELECTION; + and &VIDIOC-SUBDEV-S-SELECTION; ioctls. + diff --git a/Documentation/DocBook/media/v4l/dev-subdev.xml b/Documentation/DocBook/media/v4l/dev-subdev.xml index 0916a7343a16..4afcbbec5eda 100644 --- a/Documentation/DocBook/media/v4l/dev-subdev.xml +++ b/Documentation/DocBook/media/v4l/dev-subdev.xml @@ -76,11 +76,12 @@ format means the combination of media bus data format, frame width and frame height. - Image formats are typically negotiated on video capture and output - devices using the cropping and scaling ioctls. - The driver is responsible for configuring every block in the video pipeline - according to the requested format at the pipeline input and/or - output. + Image formats are typically negotiated on video capture and + output devices using the format and selection ioctls. The + driver is responsible for configuring every block in the video + pipeline according to the requested format at the pipeline input + and/or output. For complex devices, such as often found in embedded systems, identical image sizes at the output of a pipeline can be achieved using @@ -276,11 +277,11 @@
- Cropping and scaling + Selections: cropping, scaling and composition Many sub-devices support cropping frames on their input or output pads (or possible even on both). Cropping is used to select the area of - interest in an image, typically on a video sensor or video decoder. It can + interest in an image, typically on an image sensor or a video decoder. It can also be used as part of digital zoom implementations to select the area of the image that will be scaled up. @@ -288,26 +289,179 @@ &v4l2-rect; by the coordinates of the top left corner and the rectangle size. Both the coordinates and sizes are expressed in pixels. - The crop rectangle is retrieved and set using the - &VIDIOC-SUBDEV-G-CROP; and &VIDIOC-SUBDEV-S-CROP; ioctls. Like for pad - formats, drivers store try and active crop rectangles. The format - negotiation mechanism applies to crop settings as well. - - On input pads, cropping is applied relatively to the current pad - format. The pad format represents the image size as received by the - sub-device from the previous block in the pipeline, and the crop rectangle - represents the sub-image that will be transmitted further inside the - sub-device for processing. The crop rectangle be entirely containted - inside the input image size. - - Input crop rectangle are reset to their default value when the input - image format is modified. Drivers should use the input image size as the - crop rectangle default value, but hardware requirements may prevent this. - + As for pad formats, drivers store try and active + rectangles for the selection targets of ACTUAL type . + + On sink pads, cropping is applied relative to the + current pad format. The pad format represents the image size as + received by the sub-device from the previous block in the + pipeline, and the crop rectangle represents the sub-image that + will be transmitted further inside the sub-device for + processing. + + The scaling operation changes the size of the image by + scaling it to new dimensions. The scaling ratio isn't specified + explicitly, but is implied from the original and scaled image + sizes. Both sizes are represented by &v4l2-rect;. + + Scaling support is optional. When supported by a subdev, + the crop rectangle on the subdev's sink pad is scaled to the + size configured using the &VIDIOC-SUBDEV-S-SELECTION; IOCTL + using V4L2_SUBDEV_SEL_COMPOSE_ACTUAL + selection target on the same pad. If the subdev supports scaling + but not composing, the top and left values are not used and must + always be set to zero. + + On source pads, cropping is similar to sink pads, with the + exception that the source size from which the cropping is + performed, is the COMPOSE rectangle on the sink pad. In both + sink and source pads, the crop rectangle must be entirely + contained inside the source image size for the crop + operation. + + The drivers should always use the closest possible + rectangle the user requests on all selection targets, unless + specifically told otherwise. + V4L2_SUBDEV_SEL_FLAG_SIZE_GE and + V4L2_SUBDEV_SEL_FLAG_SIZE_LE flags may be + used to round the image size either up or down. +
+ +
+ Types of selection targets + +
+ ACTUAL targets + + ACTUAL targets reflect the actual hardware configuration + at any point of time. There is a BOUNDS target + corresponding to every ACTUAL. +
+ +
+ BOUNDS targets + + BOUNDS targets is the smallest rectangle that contains + all valid ACTUAL rectangles. It may not be possible to set the + ACTUAL rectangle as large as the BOUNDS rectangle, however. + This may be because e.g. a sensor's pixel array is not + rectangular but cross-shaped or round. The maximum size may + also be smaller than the BOUNDS rectangle. +
- Cropping behaviour on output pads is not defined. +
+ +
+ Order of configuration and format propagation + + Inside subdevs, the order of image processing steps will + always be from the sink pad towards the source pad. This is also + reflected in the order in which the configuration must be + performed by the user: the changes made will be propagated to + any subsequent stages. If this behaviour is not desired, the + user must set + V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG flag. This + flag causes no propagation of the changes are allowed in any + circumstances. This may also cause the accessed rectangle to be + adjusted by the driver, depending on the properties of the + underlying hardware. + + The coordinates to a step always refer to the actual size + of the previous step. The exception to this rule is the source + compose rectangle, which refers to the sink compose bounds + rectangle --- if it is supported by the hardware. + + + Sink pad format. The user configures the sink pad + format. This format defines the parameters of the image the + entity receives through the pad for further processing. + + Sink pad actual crop selection. The sink pad crop + defines the crop performed to the sink pad format. + + Sink pad actual compose selection. The size of the + sink pad compose rectangle defines the scaling ratio compared + to the size of the sink pad crop rectangle. The location of + the compose rectangle specifies the location of the actual + sink compose rectangle in the sink compose bounds + rectangle. + + Source pad actual crop selection. Crop on the source + pad defines crop performed to the image in the sink compose + bounds rectangle. + + Source pad format. The source pad format defines the + output pixel format of the subdev, as well as the other + parameters with the exception of the image width and height. + Width and height are defined by the size of the source pad + actual crop selection. + + + Accessing any of the above rectangles not supported by the + subdev will return EINVAL. Any rectangle + referring to a previous unsupported rectangle coordinates will + instead refer to the previous supported rectangle. For example, + if sink crop is not supported, the compose selection will refer + to the sink pad format dimensions instead. + +
+ Image processing in subdevs: simple crop example + + + + + +
+ + In the above example, the subdev supports cropping on its + sink pad. To configure it, the user sets the media bus format on + the subdev's sink pad. Now the actual crop rectangle can be set + on the sink pad --- the location and size of this rectangle + reflect the location and size of a rectangle to be cropped from + the sink format. The size of the sink crop rectangle will also + be the size of the format of the subdev's source pad. + +
+ Image processing in subdevs: scaling with multiple sources + + + + + +
+ + In this example, the subdev is capable of first cropping, + then scaling and finally cropping for two source pads + individually from the resulting scaled image. The location of + the scaled image in the cropped image is ignored in sink compose + target. Both of the locations of the source crop rectangles + refer to the sink scaling rectangle, independently cropping an + area at location specified by the source crop rectangle from + it. + +
+ Image processing in subdevs: scaling and composition + with multiple sinks and sources + + + + + +
+ + The subdev driver supports two sink pads and two source + pads. The images from both of the sink pads are individually + cropped, then scaled and further composed on the composition + bounds rectangle. From that, two independent streams are cropped + and sent out of the subdev from the source pads.
+ &sub-subdev-formats; diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 864ba5d6873d..fbf808d242f7 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -96,6 +96,17 @@ Remote Controller chapter. + + + Sakari + Ailus + Subdev selections API. + +
+ sakari.ailus@iki.fi +
+
+
@@ -131,7 +142,8 @@ applications. --> 3.5 2012-04-02 sa - Added V4L2_CTRL_TYPE_INTEGER_MENU. + Added V4L2_CTRL_TYPE_INTEGER_MENU and V4L2 subdev + selections API. @@ -548,6 +560,7 @@ and discussions on the V4L mailing list. &sub-subdev-g-crop; &sub-subdev-g-fmt; &sub-subdev-g-frame-interval; + &sub-subdev-g-selection; &sub-subscribe-event; &sub-mmap; diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml new file mode 100644 index 000000000000..208e9f0da3f3 --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml @@ -0,0 +1,228 @@ + + + ioctl VIDIOC_SUBDEV_G_SELECTION, VIDIOC_SUBDEV_S_SELECTION + &manvol; + + + + VIDIOC_SUBDEV_G_SELECTION + VIDIOC_SUBDEV_S_SELECTION + Get or set selection rectangles on a subdev pad + + + + + + int ioctl + int fd + int request + struct v4l2_subdev_selection *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_SUBDEV_G_SELECTION, VIDIOC_SUBDEV_S_SELECTION + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + The selections are used to configure various image + processing functionality performed by the subdevs which affect the + image size. This currently includes cropping, scaling and + composition. + + The selection API replaces the old subdev crop API. All + the function of the crop API, and more, are supported by the + selections API. + + See for + more information on how each selection target affects the image + processing pipeline inside the subdevice. + +
+ Types of selection targets + + There are two types of selection targets: actual and bounds. + The ACTUAL targets are the targets which configure the hardware. + The BOUNDS target will return a rectangle that contain all + possible ACTUAL rectangles. +
+ +
+ Discovering supported features + + To discover which targets are supported, the user can + perform VIDIOC_SUBDEV_G_SELECTION on them. + Any unsupported target will return + EINVAL. +
+ +
+ V4L2 subdev selection targets + + &cs-def; + + + V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL + 0x0000 + Actual crop. Defines the cropping + performed by the processing step. + + + V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS + 0x0002 + Bounds of the crop rectangle. + + + V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL + 0x0100 + Actual compose rectangle. Used to configure scaling + on sink pads and composition on source pads. + + + V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS + 0x0102 + Bounds of the compose rectangle. + + + +
+ + + V4L2 subdev selection flags + + &cs-def; + + + V4L2_SUBDEV_SEL_FLAG_SIZE_GE + (1 << 0) Suggest the driver it + should choose greater or equal rectangle (in size) than + was requested. Albeit the driver may choose a lesser size, + it will only do so due to hardware limitations. Without + this flag (and + V4L2_SUBDEV_SEL_FLAG_SIZE_LE) the + behaviour is to choose the closest possible + rectangle. + + + V4L2_SUBDEV_SEL_FLAG_SIZE_LE + (1 << 1) Suggest the driver it + should choose lesser or equal rectangle (in size) than was + requested. Albeit the driver may choose a greater size, it + will only do so due to hardware limitations. + + + V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG + (1 << 2) + The configuration should not be propagated to any + further processing steps. If this flag is not given, the + configuration is propagated inside the subdevice to all + further processing steps. + + + +
+ + + struct <structname>v4l2_subdev_selection</structname> + + &cs-str; + + + __u32 + which + Active or try selection, from + &v4l2-subdev-format-whence;. + + + __u32 + pad + Pad number as reported by the media framework. + + + __u32 + target + Target selection rectangle. See + .. + + + __u32 + flags + Flags. See + . + + + &v4l2-rect; + rect + Selection rectangle, in pixels. + + + __u32 + reserved[8] + Reserved for future extensions. Applications and drivers must + set the array to zero. + + + +
+ + + + + &return-value; + + + + EBUSY + + The selection rectangle can't be changed because the + pad is currently busy. This can be caused, for instance, by + an active video stream on the pad. The ioctl must not be + retried without performing another action to fix the problem + first. Only returned by + VIDIOC_SUBDEV_S_SELECTION + + + + EINVAL + + The &v4l2-subdev-selection; + pad references a non-existing + pad, the which field references a + non-existing format, or the selection target is not + supported on the given subdev pad. + + + + + -- cgit v1.2.3 From 8766e86535c9c9d46621f285b85266b2975348c2 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 7 Jan 2012 06:23:36 -0300 Subject: [media] v4l: Mark VIDIOC_SUBDEV_G_CROP and VIDIOC_SUBDEV_S_CROP obsolete These two IOCTLS are obsoleted by VIDIOC_SUBDEV_G_SELECTION and VIDIOC_SUBDEV_S_SELECTION. Mark them obsolete. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 7 +++++++ Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml | 9 ++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 0fc74ca81f0f..87339b2aad78 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2557,6 +2557,13 @@ interfaces and should not be implemented in new drivers. VIDIOC_S_MPEGCOMP ioctls. Use Extended Controls, . + + VIDIOC_SUBDEV_G_CROP and + VIDIOC_SUBDEV_S_CROP ioctls. Use + VIDIOC_SUBDEV_G_SELECTION and + VIDIOC_SUBDEV_S_SELECTION, . +
diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml index 06197323a8cc..4cddd788c589 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml @@ -58,9 +58,12 @@ Description - Experimental - This is an experimental - interface and may change in the future. + Obsolete + + This is an obsolete + interface and may be removed in the future. It is superseded by + the selection + API. To retrieve the current crop rectangle applications set the -- cgit v1.2.3 From 865d7ec93433a3c9d8d2c2372e582853f52a7327 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 22 Mar 2012 14:53:01 -0300 Subject: [media] radio-isa: PnP support for the new ISA radio framework Add PnP support to the new ISA radio framework. Signed-off-by: Ondrej Zary Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-isa.c | 155 ++++++++++++++++++++++++++++------------ drivers/media/radio/radio-isa.h | 9 +++ 2 files changed, 117 insertions(+), 47 deletions(-) diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c index 06f906351fad..ed9039f8571c 100644 --- a/drivers/media/radio/radio-isa.c +++ b/drivers/media/radio/radio-isa.c @@ -199,56 +199,31 @@ static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io) return false; } -int radio_isa_probe(struct device *pdev, unsigned int dev) +struct radio_isa_card *radio_isa_alloc(struct radio_isa_driver *drv, + struct device *pdev) { - struct radio_isa_driver *drv = pdev->platform_data; - const struct radio_isa_ops *ops = drv->ops; struct v4l2_device *v4l2_dev; - struct radio_isa_card *isa; - int res; + struct radio_isa_card *isa = drv->ops->alloc(); + if (!isa) + return NULL; - isa = drv->ops->alloc(); - if (isa == NULL) - return -ENOMEM; dev_set_drvdata(pdev, isa); isa->drv = drv; - isa->io = drv->io_params[dev]; v4l2_dev = &isa->v4l2_dev; strlcpy(v4l2_dev->name, dev_name(pdev), sizeof(v4l2_dev->name)); - if (drv->probe && ops->probe) { - int i; - - for (i = 0; i < drv->num_of_io_ports; ++i) { - int io = drv->io_ports[i]; - - if (request_region(io, drv->region_size, v4l2_dev->name)) { - bool found = ops->probe(isa, io); - - release_region(io, drv->region_size); - if (found) { - isa->io = io; - break; - } - } - } - } - - if (!radio_isa_valid_io(drv, isa->io)) { - int i; + return isa; +} - if (isa->io < 0) - return -ENODEV; - v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x", - drv->io_ports[0]); - for (i = 1; i < drv->num_of_io_ports; i++) - printk(KERN_CONT "/0x%03x", drv->io_ports[i]); - printk(KERN_CONT ".\n"); - kfree(isa); - return -EINVAL; - } +int radio_isa_common_probe(struct radio_isa_card *isa, struct device *pdev, + int radio_nr, unsigned region_size) +{ + const struct radio_isa_driver *drv = isa->drv; + const struct radio_isa_ops *ops = drv->ops; + struct v4l2_device *v4l2_dev = &isa->v4l2_dev; + int res; - if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) { + if (!request_region(isa->io, region_size, v4l2_dev->name)) { v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io); kfree(isa); return -EBUSY; @@ -301,8 +276,8 @@ int radio_isa_probe(struct device *pdev, unsigned int dev) v4l2_err(v4l2_dev, "Could not setup card\n"); goto err_node_reg; } - res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, - drv->radio_nr_params[dev]); + res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, radio_nr); + if (res < 0) { v4l2_err(v4l2_dev, "Could not register device node\n"); goto err_node_reg; @@ -317,24 +292,110 @@ err_node_reg: err_hdl: v4l2_device_unregister(&isa->v4l2_dev); err_dev_reg: - release_region(isa->io, drv->region_size); + release_region(isa->io, region_size); kfree(isa); return res; } -EXPORT_SYMBOL_GPL(radio_isa_probe); -int radio_isa_remove(struct device *pdev, unsigned int dev) +int radio_isa_common_remove(struct radio_isa_card *isa, unsigned region_size) { - struct radio_isa_card *isa = dev_get_drvdata(pdev); const struct radio_isa_ops *ops = isa->drv->ops; ops->s_mute_volume(isa, true, isa->volume ? isa->volume->cur.val : 0); video_unregister_device(&isa->vdev); v4l2_ctrl_handler_free(&isa->hdl); v4l2_device_unregister(&isa->v4l2_dev); - release_region(isa->io, isa->drv->region_size); + release_region(isa->io, region_size); v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card); kfree(isa); return 0; } + +int radio_isa_probe(struct device *pdev, unsigned int dev) +{ + struct radio_isa_driver *drv = pdev->platform_data; + const struct radio_isa_ops *ops = drv->ops; + struct v4l2_device *v4l2_dev; + struct radio_isa_card *isa; + + isa = radio_isa_alloc(drv, pdev); + if (!isa) + return -ENOMEM; + isa->io = drv->io_params[dev]; + v4l2_dev = &isa->v4l2_dev; + + if (drv->probe && ops->probe) { + int i; + + for (i = 0; i < drv->num_of_io_ports; ++i) { + int io = drv->io_ports[i]; + + if (request_region(io, drv->region_size, v4l2_dev->name)) { + bool found = ops->probe(isa, io); + + release_region(io, drv->region_size); + if (found) { + isa->io = io; + break; + } + } + } + } + + if (!radio_isa_valid_io(drv, isa->io)) { + int i; + + if (isa->io < 0) + return -ENODEV; + v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x", + drv->io_ports[0]); + for (i = 1; i < drv->num_of_io_ports; i++) + printk(KERN_CONT "/0x%03x", drv->io_ports[i]); + printk(KERN_CONT ".\n"); + kfree(isa); + return -EINVAL; + } + + return radio_isa_common_probe(isa, pdev, drv->radio_nr_params[dev], + drv->region_size); +} +EXPORT_SYMBOL_GPL(radio_isa_probe); + +int radio_isa_remove(struct device *pdev, unsigned int dev) +{ + struct radio_isa_card *isa = dev_get_drvdata(pdev); + + return radio_isa_common_remove(isa, isa->drv->region_size); +} EXPORT_SYMBOL_GPL(radio_isa_remove); + +#ifdef CONFIG_PNP +int radio_isa_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + struct pnp_driver *pnp_drv = to_pnp_driver(dev->dev.driver); + struct radio_isa_driver *drv = container_of(pnp_drv, + struct radio_isa_driver, pnp_driver); + struct radio_isa_card *isa; + + if (!pnp_port_valid(dev, 0)) + return -ENODEV; + + isa = radio_isa_alloc(drv, &dev->dev); + if (!isa) + return -ENOMEM; + + isa->io = pnp_port_start(dev, 0); + + return radio_isa_common_probe(isa, &dev->dev, drv->radio_nr_params[0], + pnp_port_len(dev, 0)); +} +EXPORT_SYMBOL_GPL(radio_isa_pnp_probe); + +void radio_isa_pnp_remove(struct pnp_dev *dev) +{ + struct radio_isa_card *isa = dev_get_drvdata(&dev->dev); + + radio_isa_common_remove(isa, pnp_port_len(dev, 0)); +} +EXPORT_SYMBOL_GPL(radio_isa_pnp_remove); +#endif diff --git a/drivers/media/radio/radio-isa.h b/drivers/media/radio/radio-isa.h index 8a0ea84d86de..ba4c01f1bd0c 100644 --- a/drivers/media/radio/radio-isa.h +++ b/drivers/media/radio/radio-isa.h @@ -24,6 +24,7 @@ #define _RADIO_ISA_H_ #include +#include #include #include #include @@ -76,6 +77,9 @@ struct radio_isa_ops { /* Top level structure needed to instantiate the cards */ struct radio_isa_driver { struct isa_driver driver; +#ifdef CONFIG_PNP + struct pnp_driver pnp_driver; +#endif const struct radio_isa_ops *ops; /* The module_param_array with the specified I/O ports */ int *io_params; @@ -101,5 +105,10 @@ struct radio_isa_driver { int radio_isa_match(struct device *pdev, unsigned int dev); int radio_isa_probe(struct device *pdev, unsigned int dev); int radio_isa_remove(struct device *pdev, unsigned int dev); +#ifdef CONFIG_PNP +int radio_isa_pnp_probe(struct pnp_dev *dev, + const struct pnp_device_id *dev_id); +void radio_isa_pnp_remove(struct pnp_dev *dev); +#endif #endif -- cgit v1.2.3 From 38ed1aef92ab4ba1a3881940ce3e8b680dddf50a Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 22 Mar 2012 14:53:29 -0300 Subject: [media] radio-gemtek: add PnP support for AOpen FX-3D/Pro Radio Add PnP support to radio-gemtek for AOpen FX-3D/Pro Radio card (AD1816 + Gemtek radio). Signed-off-by: Ondrej Zary Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-gemtek.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 2e639ce6f256..235c0e349820 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -29,6 +29,7 @@ #include /* kernel radio structs */ #include #include /* outb, outb_p */ +#include #include #include #include @@ -283,6 +284,16 @@ static const struct radio_isa_ops gemtek_ops = { static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; +#ifdef CONFIG_PNP +static struct pnp_device_id gemtek_pnp_devices[] = { + /* AOpen FX-3D/Pro Radio */ + {.id = "ADS7183", .driver_data = 0}, + {.id = ""} +}; + +MODULE_DEVICE_TABLE(pnp, gemtek_pnp_devices); +#endif + static struct radio_isa_driver gemtek_driver = { .driver = { .match = radio_isa_match, @@ -292,6 +303,14 @@ static struct radio_isa_driver gemtek_driver = { .name = "radio-gemtek", }, }, +#ifdef CONFIG_PNP + .pnp_driver = { + .name = "radio-gemtek", + .id_table = gemtek_pnp_devices, + .probe = radio_isa_pnp_probe, + .remove = radio_isa_pnp_remove, + }, +#endif .io_params = io, .radio_nr_params = radio_nr, .io_ports = gemtek_ioports, @@ -305,12 +324,18 @@ static struct radio_isa_driver gemtek_driver = { static int __init gemtek_init(void) { gemtek_driver.probe = probe; +#ifdef CONFIG_PNP + pnp_register_driver(&gemtek_driver.pnp_driver); +#endif return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX); } static void __exit gemtek_exit(void) { hardmute = 1; /* Turn off PLL */ +#ifdef CONFIG_PNP + pnp_unregister_driver(&gemtek_driver.pnp_driver); +#endif isa_unregister_driver(&gemtek_driver.driver); } -- cgit v1.2.3 From 96371fc89b7f813a38739946eb7ea7c0a841fd86 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 23 Mar 2012 18:09:34 -0300 Subject: [media] em28xx: Remove redundant dev->ctl_input set dev->ctl_input() is always set before a call to video_mux(), but then video_mux() sets it again with the same value. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-video.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 324b695c0724..bcc41603c193 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1305,9 +1305,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) if (0 == INPUT(i)->type) return -EINVAL; - dev->ctl_input = i; - - video_mux(dev, dev->ctl_input); + video_mux(dev, i); return 0; } @@ -2518,7 +2516,6 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->norm = em28xx_video_template.current_norm; v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); dev->interlaced = EM28XX_INTERLACED_DEFAULT; - dev->ctl_input = 0; /* Analog specific initialization */ dev->format = &format[0]; @@ -2532,7 +2529,7 @@ int em28xx_register_analog_devices(struct em28xx *dev) em28xx_set_video_format(dev, format[0].fourcc, maxw, norm_maxh(dev)); - video_mux(dev, dev->ctl_input); + video_mux(dev, 0); /* Audio defaults */ dev->mute = 1; -- cgit v1.2.3 From 4ae2e594b70b04fb90fd5fef96a996f42ecea7d5 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sat, 24 Mar 2012 18:39:18 -0300 Subject: [media] staging/media/as102: Don't call release_firmware() on uninitialized variable If, in drivers/staging/media/as102/as102_fw.c::as102_fw_upload(), the call cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL); should fail and return NULL so that we jump to the 'error:' label, then we'll end up calling 'release_firmware(firmware);' with 'firmware' still uninitialized - not good. The easy fix is to just initialize 'firmware' to NULL when we declare it, since release_firmware() deals gracefully with being passed NULL pointers. Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/as102/as102_fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/as102/as102_fw.c b/drivers/staging/media/as102/as102_fw.c index 43ebc43e6b9a..1075fb1df0d9 100644 --- a/drivers/staging/media/as102/as102_fw.c +++ b/drivers/staging/media/as102/as102_fw.c @@ -165,7 +165,7 @@ error: int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) { int errno = -EFAULT; - const struct firmware *firmware; + const struct firmware *firmware = NULL; unsigned char *cmd_buf = NULL; char *fw1, *fw2; struct usb_device *dev = bus_adap->usb_dev; -- cgit v1.2.3 From 37e65dceccf1b556afc17328fbdd782693807af9 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Mon, 26 Mar 2012 09:13:31 -0300 Subject: [media] em28xx: Export em28xx_[read,write]_reg functions as SYMBOL_GPL Those functions will be needed by em28xx-input module, to be added on the next patches. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index fd54c807cf29..5717bdee8f1b 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -139,6 +139,7 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg) { return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg); } +EXPORT_SYMBOL_GPL(em28xx_read_reg); /* * em28xx_write_regs_req() @@ -205,6 +206,7 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) return rc; } +EXPORT_SYMBOL_GPL(em28xx_write_regs); /* Write a single register */ int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val) @@ -239,6 +241,7 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, return em28xx_write_regs(dev, reg, &newval, 1); } +EXPORT_SYMBOL_GPL(em28xx_write_reg_bits); /* * em28xx_is_ac97_ready() -- cgit v1.2.3 From 2fd6f8d15371686e3fee87d6119cab9bc4d76349 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Mon, 26 Mar 2012 09:13:32 -0300 Subject: [media] em28xx: Move ir/rc related initialization to em28xx_ir_init() Moving this helps isolating em28xx_input and will help converting it into a separate module. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 10 ---------- drivers/media/video/em28xx/em28xx-i2c.c | 3 --- drivers/media/video/em28xx/em28xx-input.c | 11 +++++++++++ 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 9fd8cc7dbb23..e3df8594ba26 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -2849,13 +2849,6 @@ void em28xx_card_setup(struct em28xx *dev) break; } -#if defined(CONFIG_MODULES) && defined(MODULE) - if (dev->board.has_ir_i2c && !disable_ir) - request_module("ir-kbd-i2c"); -#endif - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); - if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) { em28xx_errdev("\n\n"); em28xx_errdev("The support for this board weren't " @@ -2972,9 +2965,6 @@ static void flush_request_modules(struct em28xx *dev) */ void em28xx_release_resources(struct em28xx *dev) { - if (dev->sbutton_input_dev) - em28xx_deregister_snapshot_button(dev); - if (dev->ir) em28xx_ir_fini(dev); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index a88e169dba23..185db65b766e 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -553,9 +553,6 @@ int em28xx_i2c_register(struct em28xx *dev) if (i2c_scan) em28xx_do_i2c_scan(dev); - /* Instantiate the IR receiver device, if present */ - em28xx_register_i2c_ir(dev); - return 0; } diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 2630b265b0e8..dd6e3f2dfba9 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -448,6 +448,15 @@ int em28xx_ir_init(struct em28xx *dev) if (err) goto err_out_stop; + em28xx_register_i2c_ir(dev); + +#if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->board.has_ir_i2c) + request_module("ir-kbd-i2c"); +#endif + if (dev->board.has_snapshot_button) + em28xx_register_snapshot_button(dev); + return 0; err_out_stop: @@ -462,6 +471,8 @@ int em28xx_ir_fini(struct em28xx *dev) { struct em28xx_IR *ir = dev->ir; + em28xx_deregister_snapshot_button(dev); + /* skip detach on non attached boards */ if (!ir) return 0; -- cgit v1.2.3 From 9d9f479b39d58d5b7d9eae1d12b0f9de3c4fc606 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Mon, 26 Mar 2012 09:13:33 -0300 Subject: [media] em28xx: Move em28xx_register_i2c_ir() to em28xx-input.c This function is only used in em28xx-input.c so it makes no sense to have it anywhere but in em28xx-input.c. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 48 ------------------------------- drivers/media/video/em28xx/em28xx-input.c | 44 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 48 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index e3df8594ba26..160f21966b18 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -2661,54 +2661,6 @@ static int em28xx_hint_board(struct em28xx *dev) return -1; } -/* ----------------------------------------------------------------------- */ -void em28xx_register_i2c_ir(struct em28xx *dev) -{ - /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ - /* at address 0x18, so if that address is needed for another board in */ - /* the future, please put it after 0x1f. */ - struct i2c_board_info info; - const unsigned short addr_list[] = { - 0x1f, 0x30, 0x47, I2C_CLIENT_END - }; - - if (disable_ir) - return; - - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - - /* detect & configure */ - switch (dev->model) { - case EM2800_BOARD_TERRATEC_CINERGY_200: - case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; - dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "i2c IR (EM28XX Terratec)"; - break; - case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; - dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; - break; - case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; - dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; - break; - case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; - dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; - break; - } - - if (dev->init_data.name) - info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); -} - void em28xx_card_setup(struct em28xx *dev) { /* diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index dd6e3f2dfba9..0a58ba89590a 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -387,6 +387,50 @@ int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) return rc; } +void em28xx_register_i2c_ir(struct em28xx *dev) +{ + /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ + /* at address 0x18, so if that address is needed for another board in */ + /* the future, please put it after 0x1f. */ + struct i2c_board_info info; + const unsigned short addr_list[] = { + 0x1f, 0x30, 0x47, I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + + /* detect & configure */ + switch (dev->model) { + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; + dev->init_data.get_key = em28xx_get_key_terratec; + dev->init_data.name = "i2c IR (EM28XX Terratec)"; + break; + case EM2820_BOARD_PINNACLE_USB_2: + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; + dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; + dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + break; + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; + dev->init_data.get_key = em28xx_get_key_em_haup; + dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + break; + case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: + dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; + dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; + dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; + break; + } + + if (dev->init_data.name) + info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); +} + int em28xx_ir_init(struct em28xx *dev) { struct em28xx_IR *ir; -- cgit v1.2.3 From 769af2146a93c27c8834dbca54c02cd67468036d Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Mon, 26 Mar 2012 09:13:34 -0300 Subject: [media] em28xx: Change scope of em28xx-input local functions to static This functions are no longer used from another file, so they should be declared as static. Also is it necessary to move some of them before they are used, since they are no longer header-declared. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-input.c | 184 +++++++++++++++--------------- drivers/media/video/em28xx/em28xx.h | 17 --- 2 files changed, 93 insertions(+), 108 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 0a58ba89590a..249662597c0c 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -80,7 +80,7 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char b; @@ -108,7 +108,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[2]; u16 code; @@ -157,7 +157,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, +static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[3]; @@ -179,7 +179,8 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } -int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) { unsigned char subaddr, keydetect, key; @@ -387,7 +388,7 @@ int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) return rc; } -void em28xx_register_i2c_ir(struct em28xx *dev) +static void em28xx_register_i2c_ir(struct em28xx *dev) { /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ /* at address 0x18, so if that address is needed for another board in */ @@ -431,6 +432,93 @@ void em28xx_register_i2c_ir(struct em28xx *dev) i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); } +/********************************************************** + Handle Webcam snapshot button + **********************************************************/ + +static void em28xx_query_sbutton(struct work_struct *work) +{ + /* Poll the register and see if the button is depressed */ + struct em28xx *dev = + container_of(work, struct em28xx, sbutton_query_work.work); + int ret; + + ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); + + if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { + u8 cleared; + /* Button is depressed, clear the register */ + cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; + em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); + + /* Not emulate the keypress */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 1); + /* Now unpress the key */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 0); + } + + /* Schedule next poll */ + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); +} + +static void em28xx_register_snapshot_button(struct em28xx *dev) +{ + struct input_dev *input_dev; + int err; + + em28xx_info("Registering snapshot button...\n"); + input_dev = input_allocate_device(); + if (!input_dev) { + em28xx_errdev("input_allocate_device failed\n"); + return; + } + + usb_make_path(dev->udev, dev->snapshot_button_path, + sizeof(dev->snapshot_button_path)); + strlcat(dev->snapshot_button_path, "/sbutton", + sizeof(dev->snapshot_button_path)); + INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); + + input_dev->name = "em28xx snapshot button"; + input_dev->phys = dev->snapshot_button_path; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); + input_dev->keycodesize = 0; + input_dev->keycodemax = 0; + input_dev->id.bustype = BUS_USB; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + input_dev->id.version = 1; + input_dev->dev.parent = &dev->udev->dev; + + err = input_register_device(input_dev); + if (err) { + em28xx_errdev("input_register_device failed\n"); + input_free_device(input_dev); + return; + } + + dev->sbutton_input_dev = input_dev; + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + return; + +} + +static void em28xx_deregister_snapshot_button(struct em28xx *dev) +{ + if (dev->sbutton_input_dev != NULL) { + em28xx_info("Deregistering snapshot button\n"); + cancel_delayed_work_sync(&dev->sbutton_query_work); + input_unregister_device(dev->sbutton_input_dev); + dev->sbutton_input_dev = NULL; + } + return; +} + int em28xx_ir_init(struct em28xx *dev) { struct em28xx_IR *ir; @@ -530,89 +618,3 @@ int em28xx_ir_fini(struct em28xx *dev) return 0; } -/********************************************************** - Handle Webcam snapshot button - **********************************************************/ - -static void em28xx_query_sbutton(struct work_struct *work) -{ - /* Poll the register and see if the button is depressed */ - struct em28xx *dev = - container_of(work, struct em28xx, sbutton_query_work.work); - int ret; - - ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); - - if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { - u8 cleared; - /* Button is depressed, clear the register */ - cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; - em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); - - /* Not emulate the keypress */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 1); - /* Now unpress the key */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 0); - } - - /* Schedule next poll */ - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); -} - -void em28xx_register_snapshot_button(struct em28xx *dev) -{ - struct input_dev *input_dev; - int err; - - em28xx_info("Registering snapshot button...\n"); - input_dev = input_allocate_device(); - if (!input_dev) { - em28xx_errdev("input_allocate_device failed\n"); - return; - } - - usb_make_path(dev->udev, dev->snapshot_button_path, - sizeof(dev->snapshot_button_path)); - strlcat(dev->snapshot_button_path, "/sbutton", - sizeof(dev->snapshot_button_path)); - INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); - - input_dev->name = "em28xx snapshot button"; - input_dev->phys = dev->snapshot_button_path; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); - input_dev->keycodesize = 0; - input_dev->keycodemax = 0; - input_dev->id.bustype = BUS_USB; - input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); - input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); - input_dev->id.version = 1; - input_dev->dev.parent = &dev->udev->dev; - - err = input_register_device(input_dev); - if (err) { - em28xx_errdev("input_register_device failed\n"); - input_free_device(input_dev); - return; - } - - dev->sbutton_input_dev = input_dev; - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); - return; - -} - -void em28xx_deregister_snapshot_button(struct em28xx *dev) -{ - if (dev->sbutton_input_dev != NULL) { - em28xx_info("Deregistering snapshot button\n"); - cancel_delayed_work_sync(&dev->sbutton_query_work); - input_unregister_device(dev->sbutton_input_dev); - dev->sbutton_input_dev = NULL; - } - return; -} diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index c2a5a99398b0..005316fd071d 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -702,7 +702,6 @@ extern void em28xx_card_setup(struct em28xx *dev); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; -void em28xx_register_i2c_ir(struct em28xx *dev); int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); @@ -710,27 +709,11 @@ void em28xx_release_resources(struct em28xx *dev); #ifdef CONFIG_VIDEO_EM28XX_RC -int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw); -int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw); -void em28xx_register_snapshot_button(struct em28xx *dev); -void em28xx_deregister_snapshot_button(struct em28xx *dev); - int em28xx_ir_init(struct em28xx *dev); int em28xx_ir_fini(struct em28xx *dev); #else -#define em28xx_get_key_terratec NULL -#define em28xx_get_key_em_haup NULL -#define em28xx_get_key_pinnacle_usb_grey NULL -#define em28xx_get_key_winfast_usbii_deluxe NULL - -static inline void em28xx_register_snapshot_button(struct em28xx *dev) {} -static inline void em28xx_deregister_snapshot_button(struct em28xx *dev) {} static inline int em28xx_ir_init(struct em28xx *dev) { return 0; } static inline int em28xx_ir_fini(struct em28xx *dev) { return 0; } -- cgit v1.2.3 From f4d4e7656b26a6013bc5072c946920d2e2c44e8e Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Mon, 26 Mar 2012 09:13:35 -0300 Subject: [media] em28xx: Make em28xx-input.c a separate module Signed-off-by: Ezequiel Garcia [mchehab@redhat.com: Changed the default to follow the em28xx selection] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/Kconfig | 4 ++-- drivers/media/video/em28xx/Makefile | 5 ++--- drivers/media/video/em28xx/em28xx-cards.c | 8 ++------ drivers/media/video/em28xx/em28xx-input.c | 27 +++++++++++++++++++++++++-- drivers/media/video/em28xx/em28xx.h | 15 +-------------- 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index f6f622e123bd..928ef0d0429f 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -49,10 +49,10 @@ config VIDEO_EM28XX_DVB Empiatech em28xx chips. config VIDEO_EM28XX_RC - bool "EM28XX Remote Controller support" + tristate "EM28XX Remote Controller support" depends on RC_CORE depends on VIDEO_EM28XX depends on !(RC_CORE=m && VIDEO_EM28XX=y) - default y + default VIDEO_EM28XX ---help--- Enables Remote Controller support on em28xx driver. diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index 2abdf76c5203..c8b338d4be05 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -1,16 +1,15 @@ em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-y += em28xx-core.o em28xx-vbi.o -em28xx-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-input.o - em28xx-alsa-objs := em28xx-audio.o +em28xx-rc-objs := em28xx-input.o obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o +obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o ccflags-y += -Idrivers/media/video ccflags-y += -Idrivers/media/common/tuners ccflags-y += -Idrivers/media/dvb/dvb-core ccflags-y += -Idrivers/media/dvb/frontends - diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 160f21966b18..0ac117c23c47 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -2874,9 +2874,6 @@ void em28xx_card_setup(struct em28xx *dev) } em28xx_tuner_setup(dev); - - if(!disable_ir) - em28xx_ir_init(dev); } @@ -2893,6 +2890,8 @@ static void request_module_async(struct work_struct *work) if (dev->board.has_dvb) request_module("em28xx-dvb"); + if (dev->board.has_ir_i2c && !disable_ir) + request_module("em28xx-rc"); } static void request_modules(struct em28xx *dev) @@ -2917,9 +2916,6 @@ static void flush_request_modules(struct em28xx *dev) */ void em28xx_release_resources(struct em28xx *dev) { - if (dev->ir) - em28xx_ir_fini(dev); - /*FIXME: I2C IR should be disconnected */ em28xx_release_analog_resources(dev); diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 249662597c0c..fce5f7680c99 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -519,7 +519,7 @@ static void em28xx_deregister_snapshot_button(struct em28xx *dev) return; } -int em28xx_ir_init(struct em28xx *dev) +static int em28xx_ir_init(struct em28xx *dev) { struct em28xx_IR *ir; struct rc_dev *rc; @@ -599,7 +599,7 @@ int em28xx_ir_init(struct em28xx *dev) return err; } -int em28xx_ir_fini(struct em28xx *dev) +static int em28xx_ir_fini(struct em28xx *dev) { struct em28xx_IR *ir = dev->ir; @@ -618,3 +618,26 @@ int em28xx_ir_fini(struct em28xx *dev) return 0; } +static struct em28xx_ops rc_ops = { + .id = EM28XX_RC, + .name = "Em28xx Input Extension", + .init = em28xx_ir_init, + .fini = em28xx_ir_fini, +}; + +static int __init em28xx_rc_register(void) +{ + return em28xx_register_extension(&rc_ops); +} + +static void __exit em28xx_rc_unregister(void) +{ + em28xx_unregister_extension(&rc_ops); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_DESCRIPTION("Em28xx Input driver"); + +module_init(em28xx_rc_register); +module_exit(em28xx_rc_unregister); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 005316fd071d..9a2bd5c40163 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -453,6 +453,7 @@ enum em28xx_dev_state { /* em28xx extensions */ #define EM28XX_AUDIO 0x10 #define EM28XX_DVB 0x20 +#define EM28XX_RC 0x30 /* em28xx resource types (used for res_get/res_lock etc */ #define EM28XX_RESOURCE_VIDEO 0x01 @@ -705,20 +706,6 @@ extern const unsigned int em28xx_bcount; int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); -/* Provided by em28xx-input.c */ - -#ifdef CONFIG_VIDEO_EM28XX_RC - -int em28xx_ir_init(struct em28xx *dev); -int em28xx_ir_fini(struct em28xx *dev); - -#else - -static inline int em28xx_ir_init(struct em28xx *dev) { return 0; } -static inline int em28xx_ir_fini(struct em28xx *dev) { return 0; } - -#endif - /* Provided by em28xx-vbi.c */ extern struct videobuf_queue_ops em28xx_vbi_qops; -- cgit v1.2.3 From e879b7f34e18cd886754069e988587a5618c73f0 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Fri, 30 Mar 2012 05:41:27 -0300 Subject: [media] V4L: OV5642:remove redundant code to set cropping w/h This patch contains code change only to remove redundant code to set priv->crop_rect.width/height in probe function. Signed-off-by: Liu Ying Acked-by: Chris Lalancette Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ov5642.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c index 80e07794ac8e..0bc93313d37a 100644 --- a/drivers/media/video/ov5642.c +++ b/drivers/media/video/ov5642.c @@ -1025,8 +1025,6 @@ static int ov5642_probe(struct i2c_client *client, priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; - priv->crop_rect.width = OV5642_DEFAULT_WIDTH; - priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; priv->total_height = BLANKING_MIN_HEIGHT; -- cgit v1.2.3 From f9d1ae81dc9abfa7e6a2128989cca35a9bdea57b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 30 Mar 2012 16:05:03 -0300 Subject: [media] s5p-tv: mark const init data with __initconst instead of __initdata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As long as there is no other non-const variable marked __initdata in the same compilation unit it doesn't hurt. If there were one however compilation would fail with error: $variablename causes a section type conflict because a section containing const variables is marked read only and so cannot contain non-const variables. Signed-off-by: Uwe Kleine-König Cc: Kyungmin Park Cc: Tomasz Stanislawski Cc: Mauro Carvalho Chehab Cc: linux-arm-kernel@lists.infradead.org Cc: linux-media@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-tv/mixer_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c index a2c0c25ad130..edca06592883 100644 --- a/drivers/media/video/s5p-tv/mixer_drv.c +++ b/drivers/media/video/s5p-tv/mixer_drv.c @@ -461,7 +461,7 @@ static struct platform_driver mxr_driver __refdata = { static int __init mxr_init(void) { int i, ret; - static const char banner[] __initdata = KERN_INFO + static const char banner[] __initconst = KERN_INFO "Samsung TV Mixer driver, " "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; printk(banner); -- cgit v1.2.3 From feed0258e11e04b7e0d2df8ae3793ab5d302a035 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 31 Mar 2012 05:43:55 -0300 Subject: [media] V4L: JPEG class documentation corrections This patch fixes following compilation warning: Error: no ID for constraint linkend: v4l2-jpeg-chroma-subsampling. and adds missing JPEG control class at the Table A.58. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 2 +- Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index b84f25e9cc87..5038a3a9a24c 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3486,7 +3486,7 @@ interface and may change in the future. from RGB to Y'CbCr color space. - + diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index b17a7aac6997..27e20bcbdf42 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -265,6 +265,13 @@ These controls are described in . + + V4L2_CTRL_CLASS_JPEG + 0x9d0000 + The class containing JPEG compression controls. +These controls are described in . + -- cgit v1.2.3 From 0d74679c37241f539b80334085e73ba063b2116d Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 1 Apr 2012 16:41:45 -0300 Subject: [media] ati_remote: allow specifying a default keymap selector function Currently the ati_remote default keymap is selected directly based on the USB device id. Add support for instead specifying a function returning the default keymap, allowing more complex selection logic to be added when needed. This will be used for Medion X10 remotes in a following commit. Signed-off-by: Anssi Hannula Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ati_remote.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index baf907b3ce76..7a35f7afad50 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -151,13 +151,23 @@ MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes"); #undef err #define err(format, arg...) printk(KERN_ERR format , ## arg) +struct ati_receiver_type { + /* either default_keymap or get_default_keymap should be set */ + const char *default_keymap; + const char *(*get_default_keymap)(struct usb_interface *interface); +}; + +static const struct ati_receiver_type type_ati = { .default_keymap = RC_MAP_ATI_X10 }; +static const struct ati_receiver_type type_medion = { .default_keymap = RC_MAP_MEDION_X10 }; +static const struct ati_receiver_type type_firefly = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY }; + static struct usb_device_id ati_remote_table[] = { - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_MEDION_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_SNAPSTREAM_FIREFLY }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_medion }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_firefly }, {} /* Terminating entry */ }; @@ -766,6 +776,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de struct usb_device *udev = interface_to_usbdev(interface); struct usb_host_interface *iface_host = interface->cur_altsetting; struct usb_endpoint_descriptor *endpoint_in, *endpoint_out; + struct ati_receiver_type *type = (struct ati_receiver_type *)id->driver_info; struct ati_remote *ati_remote; struct input_dev *input_dev; struct rc_dev *rc_dev; @@ -827,10 +838,15 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de snprintf(ati_remote->mouse_name, sizeof(ati_remote->mouse_name), "%s mouse", ati_remote->rc_name); - if (id->driver_info) - rc_dev->map_name = (const char *)id->driver_info; - else - rc_dev->map_name = RC_MAP_ATI_X10; + rc_dev->map_name = RC_MAP_ATI_X10; /* default map */ + + /* set default keymap according to receiver model */ + if (type) { + if (type->default_keymap) + rc_dev->map_name = type->default_keymap; + else if (type->get_default_keymap) + rc_dev->map_name = type->get_default_keymap(interface); + } ati_remote_rc_init(ati_remote); mutex_init(&ati_remote->open_mutex); -- cgit v1.2.3 From 9d454d48ebcd9938ac60a245fa545d9db1035f1a Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 1 Apr 2012 16:41:46 -0300 Subject: [media] ati_remote: add support for Medion X10 Digitainer remote Add support for another Medion X10 remote. This was apparently originally used with the Medion Digitainer box, but is now sold separately without any Digitainer labeling. A peculiarity of this remote is a scrollwheel in place of up/down buttons. Each direction is mapped to 8 different scancodes, each corresponding to 1..8 notches, allowing multiple notches to the same direction to be transmitted in a single scancode. The driver transforms the multi-notch scancodes to multiple events of the single-notch scancode. (0x70..0x77 = 1..8 notches down, 0x78..0x7f = 1..8 notches up) Since the scrollwheel scancodes are the same that are used for mouse on some other X10 (ati_remote) remotes, the driver will now check whether the active keymap has a keycode defined for the single-notch scancode when a mouse/scrollwheel scancode (0x70..0x7f) is received. If set, scrollwheel is assumed, otherwise mouse is assumed. This remote ships with a different receiver than the already supported Medion X10 remote, but they share the same USB ID. The only difference in the USB descriptors is that the Digitainer receiver has the Remote Wakeup bit set in bmAttributes of the Configuration Descriptor. Therefore that is used to select the default keymap. Thanks to Stephan Raue from OpenELEC (www.openelec.tv) for providing me both a Medion X10 Digitainer remote+receiver and an already supported Medion X10 remote+receiver. Thanks to Martin Beyss for providing some useful information about the remote (including the "Digitainer" name). This patch has been tested by both of them and myself. Signed-off-by: Anssi Hannula Tested-by: Stephan Raue Tested-by: Martin Beyss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ati_remote.c | 90 +++++++++++----- drivers/media/rc/keymaps/Makefile | 1 + .../media/rc/keymaps/rc-medion-x10-digitainer.c | 115 +++++++++++++++++++++ include/media/rc-map.h | 1 + 4 files changed, 179 insertions(+), 28 deletions(-) create mode 100644 drivers/media/rc/keymaps/rc-medion-x10-digitainer.c diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 7a35f7afad50..26fa043d3de7 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -1,7 +1,7 @@ /* * USB ATI Remote support * - * Copyright (c) 2011 Anssi Hannula + * Copyright (c) 2011, 2012 Anssi Hannula * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev * @@ -157,8 +157,20 @@ struct ati_receiver_type { const char *(*get_default_keymap)(struct usb_interface *interface); }; +static const char *get_medion_keymap(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + /* The receiver shipped with the "Digitainer" variant helpfully has + * a single additional bit set in its descriptor. */ + if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) + return RC_MAP_MEDION_X10_DIGITAINER; + + return RC_MAP_MEDION_X10; +} + static const struct ati_receiver_type type_ati = { .default_keymap = RC_MAP_ATI_X10 }; -static const struct ati_receiver_type type_medion = { .default_keymap = RC_MAP_MEDION_X10 }; +static const struct ati_receiver_type type_medion = { .get_default_keymap = get_medion_keymap }; static const struct ati_receiver_type type_firefly = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY }; static struct usb_device_id ati_remote_table[] = { @@ -455,6 +467,7 @@ static void ati_remote_input_report(struct urb *urb) int acc; int remote_num; unsigned char scancode; + u32 wheel_keycode = KEY_RESERVED; int i; /* @@ -494,26 +507,33 @@ static void ati_remote_input_report(struct urb *urb) */ scancode = data[2] & 0x7f; - /* Look up event code index in the mouse translation table. */ - for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { - if (scancode == ati_remote_tbl[i].data) { - index = i; - break; + dbginfo(&ati_remote->interface->dev, + "channel 0x%02x; key data %02x, scancode %02x\n", + remote_num, data[2], scancode); + + if (scancode >= 0x70) { + /* + * This is either a mouse or scrollwheel event, depending on + * the remote/keymap. + * Get the keycode assigned to scancode 0x78/0x70. If it is + * set, assume this is a scrollwheel up/down event. + */ + wheel_keycode = rc_g_keycode_from_table(ati_remote->rdev, + scancode & 0x78); + + if (wheel_keycode == KEY_RESERVED) { + /* scrollwheel was not mapped, assume mouse */ + + /* Look up event code index in the mouse translation table. */ + for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { + if (scancode == ati_remote_tbl[i].data) { + index = i; + break; + } + } } } - if (index >= 0) { - dbginfo(&ati_remote->interface->dev, - "channel 0x%02x; mouse data %02x; index %d; keycode %d\n", - remote_num, data[2], index, ati_remote_tbl[index].code); - if (!dev) - return; /* no mouse device */ - } else - dbginfo(&ati_remote->interface->dev, - "channel 0x%02x; key data %02x, scancode %02x\n", - remote_num, data[2], scancode); - - if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) { input_event(dev, ati_remote_tbl[index].type, ati_remote_tbl[index].code, @@ -552,15 +572,29 @@ static void ati_remote_input_report(struct urb *urb) if (index < 0) { /* Not a mouse event, hand it to rc-core. */ - - /* - * We don't use the rc-core repeat handling yet as - * it would cause ghost repeats which would be a - * regression for this driver. - */ - rc_keydown_notimeout(ati_remote->rdev, scancode, - data[2]); - rc_keyup(ati_remote->rdev); + int count = 1; + + if (wheel_keycode != KEY_RESERVED) { + /* + * This is a scrollwheel event, send the + * scroll up (0x78) / down (0x70) scancode + * repeatedly as many times as indicated by + * rest of the scancode. + */ + count = (scancode & 0x07) + 1; + scancode &= 0x78; + } + + while (count--) { + /* + * We don't use the rc-core repeat handling yet as + * it would cause ghost repeats which would be a + * regression for this driver. + */ + rc_keydown_notimeout(ati_remote->rdev, scancode, + data[2]); + rc_keyup(ati_remote->rdev); + } return; } diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 49ce2662f56b..38ff6e0e099a 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-lme2510.o \ rc-manli.o \ rc-medion-x10.o \ + rc-medion-x10-digitainer.o \ rc-msi-digivox-ii.o \ rc-msi-digivox-iii.o \ rc-msi-tvanywhere.o \ diff --git a/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c new file mode 100644 index 000000000000..0a5ce84d9fd8 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c @@ -0,0 +1,115 @@ +/* + * Medion X10 RF remote keytable (Digitainer variant) + * + * Copyright (C) 2012 Anssi Hannula + * + * This keymap is for a variant that has a distinctive scrollwheel instead of + * up/down buttons (tested with P/N 40009936 / 20018268), reportedly + * originally shipped with Medion Digitainer but now sold separately simply as + * an "X10" remote. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +static struct rc_map_table medion_x10_digitainer[] = { + { 0x02, KEY_POWER }, + + { 0x2c, KEY_TV }, + { 0x2d, KEY_VIDEO }, + { 0x04, KEY_DVD }, /* CD/DVD */ + { 0x16, KEY_TEXT }, /* "teletext" icon, i.e. a screen with lines */ + { 0x06, KEY_AUDIO }, + { 0x2e, KEY_RADIO }, + { 0x31, KEY_EPG }, /* a screen with an open book */ + { 0x05, KEY_IMAGES }, /* Photo */ + { 0x2f, KEY_INFO }, + + { 0x78, KEY_UP }, /* scrollwheel up 1 notch */ + /* 0x79..0x7f: 2-8 notches, driver repeats 0x78 entry */ + + { 0x70, KEY_DOWN }, /* scrollwheel down 1 notch */ + /* 0x71..0x77: 2-8 notches, driver repeats 0x70 entry */ + + { 0x19, KEY_MENU }, + { 0x1d, KEY_LEFT }, + { 0x1e, KEY_OK }, /* scrollwheel press */ + { 0x1f, KEY_RIGHT }, + { 0x20, KEY_BACK }, + + { 0x09, KEY_VOLUMEUP }, + { 0x08, KEY_VOLUMEDOWN }, + { 0x00, KEY_MUTE }, + + { 0x1b, KEY_SELECT }, /* also has "U" rotated 90 degrees CCW */ + + { 0x0b, KEY_CHANNELUP }, + { 0x0c, KEY_CHANNELDOWN }, + { 0x1c, KEY_LAST }, + + { 0x32, KEY_RED }, /* also Audio */ + { 0x33, KEY_GREEN }, /* also Subtitle */ + { 0x34, KEY_YELLOW }, /* also Angle */ + { 0x35, KEY_BLUE }, /* also Title */ + + { 0x28, KEY_STOP }, + { 0x29, KEY_PAUSE }, + { 0x25, KEY_PLAY }, + { 0x21, KEY_PREVIOUS }, + { 0x18, KEY_CAMERA }, + { 0x23, KEY_NEXT }, + { 0x24, KEY_REWIND }, + { 0x27, KEY_RECORD }, + { 0x26, KEY_FORWARD }, + + { 0x0d, KEY_1 }, + { 0x0e, KEY_2 }, + { 0x0f, KEY_3 }, + { 0x10, KEY_4 }, + { 0x11, KEY_5 }, + { 0x12, KEY_6 }, + { 0x13, KEY_7 }, + { 0x14, KEY_8 }, + { 0x15, KEY_9 }, + { 0x17, KEY_0 }, +}; + +static struct rc_map_list medion_x10_digitainer_map = { + .map = { + .scan = medion_x10_digitainer, + .size = ARRAY_SIZE(medion_x10_digitainer), + .rc_type = RC_TYPE_OTHER, + .name = RC_MAP_MEDION_X10_DIGITAINER, + } +}; + +static int __init init_rc_map_medion_x10_digitainer(void) +{ + return rc_map_register(&medion_x10_digitainer_map); +} + +static void __exit exit_rc_map_medion_x10_digitainer(void) +{ + rc_map_unregister(&medion_x10_digitainer_map); +} + +module_init(init_rc_map_medion_x10_digitainer) +module_exit(exit_rc_map_medion_x10_digitainer) + +MODULE_DESCRIPTION("Medion X10 RF remote keytable (Digitainer variant)"); +MODULE_AUTHOR("Anssi Hannula "); +MODULE_LICENSE("GPL"); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 8db6741c1256..88583a6ff7f2 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -113,6 +113,7 @@ void rc_map_init(void); #define RC_MAP_LME2510 "rc-lme2510" #define RC_MAP_MANLI "rc-manli" #define RC_MAP_MEDION_X10 "rc-medion-x10" +#define RC_MAP_MEDION_X10_DIGITAINER "rc-medion-x10-digitainer" #define RC_MAP_MSI_DIGIVOX_II "rc-msi-digivox-ii" #define RC_MAP_MSI_DIGIVOX_III "rc-msi-digivox-iii" #define RC_MAP_MSI_TVANYWHERE_PLUS "rc-msi-tvanywhere-plus" -- cgit v1.2.3 From 961e668b3447a73c040181def293cfa875c74738 Mon Sep 17 00:00:00 2001 From: Alan McIvor Date: Sun, 1 Apr 2012 21:11:08 -0300 Subject: [media] Default bt878 contrast value The default_value for the Bt878 V4L2_CID_CONTRAST control is currently set to 32768. Internally this gets translated to an analog input circuit gain of 1.19. However, the default gain should be 1.0. This patch alters the default value to 27648 which corresponds to a gain of 1.0. It also alters the probe routine so that the correct value is written on board initialisation. [mchehab@redhat.com: behavior confirmed via Fusion 878a datasheet] Signed-off-by: Alan McIvor Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/bttv-driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index e581b37be789..a9cfb0f4be48 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -663,7 +663,7 @@ static const struct v4l2_queryctrl bttv_ctls[] = { .minimum = 0, .maximum = 65535, .step = 128, - .default_value = 32768, + .default_value = 27648, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_SATURATION, @@ -4394,7 +4394,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, if (!bttv_tvcards[btv->c.type].no_video) { bttv_register_video(btv); bt848_bright(btv,32768); - bt848_contrast(btv,32768); + bt848_contrast(btv, 27648); bt848_hue(btv,32768); bt848_sat(btv,32768); audio_mute(btv, 1); -- cgit v1.2.3 From 30059d93b07a034555defbf14d689a279fd7368d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 11 Apr 2012 05:01:56 -0300 Subject: [media] kernel:kfifo: export __kfifo_max_r symbol kfifo_avail expands to __kfifo_max_r which is not an exported symbol. Any kernel module using kfifo_avail will result in build failures because of this. This patch just exports __kfifo_max_r symbol to fix such problems in future. Reported-by: Stephen Rothwell Signed-off-by: Srinivas Kandagatla Acked-by: Stefani Seibold Signed-off-by: Mauro Carvalho Chehab --- kernel/kfifo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/kfifo.c b/kernel/kfifo.c index c744b88c44e2..59dcf5b81d24 100644 --- a/kernel/kfifo.c +++ b/kernel/kfifo.c @@ -402,6 +402,7 @@ unsigned int __kfifo_max_r(unsigned int len, size_t recsize) return max; return len; } +EXPORT_SYMBOL(__kfifo_max_r); #define __KFIFO_PEEK(data, out, mask) \ ((data)[(out) & (mask)]) -- cgit v1.2.3 From 5f0049bd69b96537dc7c02755c169fb4ccca3ddf Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Fri, 6 Apr 2012 09:32:36 -0300 Subject: [media] v4l2-ctrls: fix integer overflow in v4l2_g_ext_ctrls() A large cs->count from userspace may overflow the allocation size, leading to memory corruption. v4l2_g_ext_ctrls() can be reached from subdev_do_ioctl() or __video_do_ioctl(). Use kmalloc_array() to avoid the overflow. Signed-off-by: Xi Wang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 3e0a72dec994..bf62b105b49d 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -2036,7 +2036,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs return class_check(hdl, cs->ctrl_class); if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + helpers = kmalloc_array(cs->count, sizeof(helper[0]), + GFP_KERNEL); if (helpers == NULL) return -ENOMEM; } -- cgit v1.2.3 From 0a3475eb618321013dab9ac744201ed09e8061f9 Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Fri, 6 Apr 2012 09:32:37 -0300 Subject: [media] v4l2-ctrls: fix integer overflow in try_set_ext_ctrls() A large cs->count from userspace may overflow the allocation size, leading to memory corruption. try_set_ext_ctrls() can be reached from subdev_do_ioctl() or __video_do_ioctl(). Use kmalloc_array() to avoid the overflow. Signed-off-by: Xi Wang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index bf62b105b49d..1a71aa5fd458 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -2259,7 +2259,8 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, return class_check(hdl, cs->ctrl_class); if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + helpers = kmalloc_array(cs->count, sizeof(helper[0]), + GFP_KERNEL); if (!helpers) return -ENOMEM; } -- cgit v1.2.3 From b803cc58c33c0c5b6220150336a6f1c7958f1404 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 9 Apr 2012 16:51:48 -0300 Subject: [media] staging: as102: Remove redundant NULL check before release_firmware() and pointless comments release_firmware() deals gracefullt with NULL pointers - it's redundant to check for them before calling the function. Also remove a few pointless comments - it's rather obvious from the code that kfree() free's a buffer and that release_firmware() releases firmware - comments just stating that add no value. Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/as102/as102_fw.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/media/as102/as102_fw.c b/drivers/staging/media/as102/as102_fw.c index 1075fb1df0d9..b9670ee41b4e 100644 --- a/drivers/staging/media/as102/as102_fw.c +++ b/drivers/staging/media/as102/as102_fw.c @@ -230,11 +230,8 @@ int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) pr_info("%s: firmware: %s loaded with success\n", DRIVER_NAME, fw2); error: - /* free data buffer */ kfree(cmd_buf); - /* release firmware if needed */ - if (firmware != NULL) - release_firmware(firmware); + release_firmware(firmware); LEAVE(); return errno; -- cgit v1.2.3 From 3fc82fa001cac8f22e7493a02c795f2bb33cafac Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 9 Apr 2012 16:50:04 -0300 Subject: [media] s2255drv: Remove redundant NULL test before release_firmware() release_firmware() tests for NULL pointers on its own - there's no reason to do an explicit check before calling the function. Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s2255drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 4894cbb1c547..37845def41c5 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -1826,8 +1826,7 @@ static void s2255_destroy(struct s2255_dev *dev) usb_free_urb(dev->fw_data->fw_urb); dev->fw_data->fw_urb = NULL; } - if (dev->fw_data->fw) - release_firmware(dev->fw_data->fw); + release_firmware(dev->fw_data->fw); kfree(dev->fw_data->pfw_data); kfree(dev->fw_data); /* reset the DSP so firmware can be reloaded next time */ -- cgit v1.2.3 From 32898a145404acbebe3256709e012c2830a2043b Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Mon, 9 Apr 2012 17:15:45 -0300 Subject: [media] zoran: fix integer overflow in setup_window() `clipcount' is from userspace and thus needs validation. Otherwise, a large `clipcount' could overflow the vmalloc() size, leading to out-of-bounds access. | setup_window() | zoran_s_fmt_vid_overlay() | __video_do_ioctl() | video_ioctl2() Use 2048 as the maximum `clipcount'. Also change the corresponding parameter type to `unsigned int'. Signed-off-by: Xi Wang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/zoran/zoran_driver.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 4c09ab781ec3..c57310931810 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -1131,8 +1131,14 @@ static int setup_fbuffer(struct zoran_fh *fh, } -static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height, - struct v4l2_clip __user *clips, int clipcount, void __user *bitmap) +static int setup_window(struct zoran_fh *fh, + int x, + int y, + int width, + int height, + struct v4l2_clip __user *clips, + unsigned int clipcount, + void __user *bitmap) { struct zoran *zr = fh->zr; struct v4l2_clip *vcp = NULL; @@ -1155,6 +1161,14 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height return -EINVAL; } + if (clipcount > 2048) { + dprintk(1, + KERN_ERR + "%s: %s - invalid clipcount\n", + ZR_DEVNAME(zr), __func__); + return -EINVAL; + } + /* * The video front end needs 4-byte alinged line sizes, we correct that * silently here if necessary @@ -1218,7 +1232,7 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height (width * height + 7) / 8)) { return -EFAULT; } - } else if (clipcount > 0) { + } else if (clipcount) { /* write our own bitmap from the clips */ vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4)); if (vcp == NULL) { -- cgit v1.2.3 From 7d3d0d8d6fd50fd180a6bc3953bb68acf7089b2b Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 16 Apr 2012 14:59:32 -0300 Subject: [media] xc5000: support 32MHz & 31.875MHz xtal using the 41.024.5 firmware Rather than loading firmware specific for the xtal frequency, just use the standard firmware and set the xtal frequency after firmware upload. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/xc5000.c | 39 ++++++++++++++++++++++++++++++++---- drivers/media/common/tuners/xc5000.h | 1 + 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index 7f98984e4fad..eab2ea424200 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -54,6 +54,7 @@ struct xc5000_priv { struct list_head hybrid_tuner_instance_list; u32 if_khz; + u32 xtal_khz; u32 freq_hz; u32 bandwidth; u8 video_standard; @@ -214,9 +215,9 @@ static const struct xc5000_fw_cfg xc5000a_1_6_114 = { .size = 12401, }; -static const struct xc5000_fw_cfg xc5000c_41_024_5_31875 = { - .name = "dvb-fe-xc5000c-41.024.5-31875.fw", - .size = 16503, +static const struct xc5000_fw_cfg xc5000c_41_024_5 = { + .name = "dvb-fe-xc5000c-41.024.5.fw", + .size = 16497, }; static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id) @@ -226,7 +227,7 @@ static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id) case XC5000A: return &xc5000a_1_6_114; case XC5000C: - return &xc5000c_41_024_5_31875; + return &xc5000c_41_024_5; } } @@ -572,6 +573,31 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode) return found; } +static int xc_set_xtal(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = XC_RESULT_SUCCESS; + + switch (priv->chip_id) { + default: + case XC5000A: + /* 32.000 MHz xtal is default */ + break; + case XC5000C: + switch (priv->xtal_khz) { + default: + case 32000: + /* 32.000 MHz xtal is default */ + break; + case 31875: + /* 31.875 MHz xtal configuration */ + ret = xc_write_reg(priv, 0x000f, 0x8081); + break; + } + break; + } + return ret; +} static int xc5000_fwupload(struct dvb_frontend *fe) { @@ -603,6 +629,8 @@ static int xc5000_fwupload(struct dvb_frontend *fe) } else { printk(KERN_INFO "xc5000: firmware uploading...\n"); ret = xc_load_i2c_sequence(fe, fw->data); + if (XC_RESULT_SUCCESS == ret) + ret = xc_set_xtal(fe); printk(KERN_INFO "xc5000: firmware upload complete...\n"); } @@ -1164,6 +1192,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, priv->if_khz = cfg->if_khz; } + if (priv->xtal_khz == 0) + priv->xtal_khz = cfg->xtal_khz; + if (priv->radio_input == 0) priv->radio_input = cfg->radio_input; diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index 3396f8e02b40..39a73bf01406 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -34,6 +34,7 @@ struct xc5000_config { u8 i2c_address; u32 if_khz; u8 radio_input; + u32 xtal_khz; int chip_id; }; -- cgit v1.2.3 From 35320676569a80fb9c612130211d774e072583b4 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 16 Apr 2012 15:06:45 -0300 Subject: [media] xc5000: log firmware upload failures in xc5000_fwupload Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/xc5000.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index eab2ea424200..377fb8db379b 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -631,7 +631,10 @@ static int xc5000_fwupload(struct dvb_frontend *fe) ret = xc_load_i2c_sequence(fe, fw->data); if (XC_RESULT_SUCCESS == ret) ret = xc_set_xtal(fe); - printk(KERN_INFO "xc5000: firmware upload complete...\n"); + if (XC_RESULT_SUCCESS == ret) + printk(KERN_INFO "xc5000: firmware upload complete...\n"); + else + printk(KERN_ERR "xc5000: firmware upload failed...\n"); } out: -- cgit v1.2.3 From 409328a4dcd40a14cbe42b2e6f0c492ece3b88cc Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 16 Apr 2012 15:21:51 -0300 Subject: [media] xc5000: xtal_khz should be a u16 rather than a u32 Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/xc5000.c | 2 +- drivers/media/common/tuners/xc5000.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index 377fb8db379b..dcca42ca57be 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -54,7 +54,7 @@ struct xc5000_priv { struct list_head hybrid_tuner_instance_list; u32 if_khz; - u32 xtal_khz; + u16 xtal_khz; u32 freq_hz; u32 bandwidth; u8 video_standard; diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index 39a73bf01406..b1a547494625 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -34,7 +34,7 @@ struct xc5000_config { u8 i2c_address; u32 if_khz; u8 radio_input; - u32 xtal_khz; + u16 xtal_khz; int chip_id; }; -- cgit v1.2.3 From be183dc3f73d7e8e0091c54fc3fa04d9ccb91903 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 8 Apr 2012 21:39:56 -0300 Subject: [media] au0828-dvb: attach tuner based on dev->board.tuner_type on hvr950q We can tell from the eeprom whether we have a xc5000a or xc5000c. Attach the correct tuner based on this information. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/au0828/au0828-dvb.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index 518216743c9c..39ece8e24985 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "au0828.h" #include "au8522.h" @@ -79,9 +80,16 @@ static struct au8522_config hauppauge_woodbury_config = { .vsb_if = AU8522_IF_3_25MHZ, }; -static struct xc5000_config hauppauge_hvr950q_tunerconfig = { +static struct xc5000_config hauppauge_xc5000a_config = { .i2c_address = 0x61, .if_khz = 6000, + .chip_id = XC5000A, +}; + +static struct xc5000_config hauppauge_xc5000c_config = { + .i2c_address = 0x61, + .if_khz = 6000, + .chip_id = XC5000C, }; static struct mxl5007t_config mxl5007t_hvr950q_config = { @@ -383,8 +391,19 @@ int au0828_dvb_register(struct au0828_dev *dev) &hauppauge_hvr950q_config, &dev->i2c_adap); if (dvb->frontend != NULL) - dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig); + switch (dev->board.tuner_type) { + default: + case TUNER_XC5000: + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_xc5000a_config); + break; + case TUNER_XC5000C: + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_xc5000c_config); + break; + } break; case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: dvb->frontend = dvb_attach(au8522_attach, @@ -411,7 +430,7 @@ int au0828_dvb_register(struct au0828_dev *dev) if (dvb->frontend != NULL) { dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig); + &hauppauge_xc5000a_config); } break; default: -- cgit v1.2.3 From b31506c47c5ae67a82fa72e6d763a8f34413aac8 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 27 Jan 2012 13:18:29 -0300 Subject: [media] au8522: build ATV/DTV demodulators as separate modules au8522_dig.o and au8522_decoder.o function independentantly of each other, each for a different hardware function using a different software subsystem api, each with its own set of subsystem module dependencies. Since these drivers do not depend on each other, and it is in fact possible to build hardware designs using one function and not the other, lets split this module into two, allowing system integrators to enable the hardware without dragging in undesired dependencies. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/Kconfig | 22 ++- drivers/media/dvb/frontends/Makefile | 5 +- drivers/media/dvb/frontends/au8522_common.c | 258 ++++++++++++++++++++++++++++ drivers/media/dvb/frontends/au8522_dig.c | 215 ----------------------- drivers/media/dvb/frontends/au8522_priv.h | 2 + drivers/media/video/au0828/Kconfig | 3 +- 6 files changed, 283 insertions(+), 222 deletions(-) create mode 100644 drivers/media/dvb/frontends/au8522_common.c diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index e11adb64e9e6..f47983472aed 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -540,12 +540,26 @@ config DVB_S5H1409 to support this frontend. config DVB_AU8522 - tristate "Auvitek AU8522 based" - depends on DVB_CORE && I2C && VIDEO_V4L2 + depends on I2C + tristate + +config DVB_AU8522_DTV + tristate "Auvitek AU8522 based DTV demod" + depends on DVB_CORE && I2C + select DVB_AU8522 default m if DVB_FE_CUSTOMISE help - An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want - to support this frontend. + An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when + you want to enable DTV demodulation support for this frontend. + +config DVB_AU8522_V4L + tristate "Auvitek AU8522 based ATV demod" + depends on VIDEO_V4L2 && I2C + select DVB_AU8522 + default m if DVB_FE_CUSTOMISE + help + An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when + you want to enable ATV demodulation support for this frontend. config DVB_S5H1411 tristate "Samsung S5H1411 based" diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 6ca75578ecac..b0381dc8e176 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -7,7 +7,6 @@ ccflags-y += -I$(srctree)/drivers/media/common/tuners/ stb0899-objs = stb0899_drv.o stb0899_algo.o stv0900-objs = stv0900_core.o stv0900_sw.o -au8522-objs = au8522_dig.o au8522_decoder.o drxd-objs = drxd_firm.o drxd_hard.o cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o drxk-objs := drxk_hard.o @@ -63,7 +62,9 @@ obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o obj-$(CONFIG_DVB_S5H1409) += s5h1409.o obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o -obj-$(CONFIG_DVB_AU8522) += au8522.o +obj-$(CONFIG_DVB_AU8522) += au8522_common.o +obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o +obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o obj-$(CONFIG_DVB_TDA10048) += tda10048.o obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o diff --git a/drivers/media/dvb/frontends/au8522_common.c b/drivers/media/dvb/frontends/au8522_common.c new file mode 100644 index 000000000000..befdff919a89 --- /dev/null +++ b/drivers/media/dvb/frontends/au8522_common.c @@ -0,0 +1,258 @@ +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth + Copyright (C) 2008 Devin Heitmueller + Copyright (C) 2005-2008 Auvitek International, Ltd. + Copyright (C) 2012 Michael Krufky + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include "dvb_frontend.h" +#include "au8522_priv.h" + +static int debug; + +#define dprintk(arg...)\ + do { if (debug)\ + printk(arg);\ + } while (0) + +/* Despite the name "hybrid_tuner", the framework works just as well for + hybrid demodulators as well... */ +static LIST_HEAD(hybrid_tuner_instance_list); +static DEFINE_MUTEX(au8522_list_mutex); + +/* 16 bit registers, 8 bit values */ +int au8522_writereg(struct au8522_state *state, u16 reg, u8 data) +{ + int ret; + u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data }; + + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 3 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, " + "ret == %i)\n", __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} +EXPORT_SYMBOL(au8522_writereg); + +u8 au8522_readreg(struct au8522_state *state, u16 reg) +{ + int ret; + u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff }; + u8 b1[] = { 0 }; + + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, + .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + return b1[0]; +} +EXPORT_SYMBOL(au8522_readreg); + +int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct au8522_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (state->operational_mode == AU8522_ANALOG_MODE) { + /* We're being asked to manage the gate even though we're + not in digital mode. This can occur if we get switched + over to analog mode before the dvb_frontend kernel thread + has completely shutdown */ + return 0; + } + + if (enable) + return au8522_writereg(state, 0x106, 1); + else + return au8522_writereg(state, 0x106, 0); +} +EXPORT_SYMBOL(au8522_i2c_gate_ctrl); + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, + u8 client_address) +{ + int ret; + + mutex_lock(&au8522_list_mutex); + ret = hybrid_tuner_request_state(struct au8522_state, (*state), + hybrid_tuner_instance_list, + i2c, client_address, "au8522"); + mutex_unlock(&au8522_list_mutex); + + return ret; +} +EXPORT_SYMBOL(au8522_get_state); + +void au8522_release_state(struct au8522_state *state) +{ + mutex_lock(&au8522_list_mutex); + if (state != NULL) + hybrid_tuner_release_state(state); + mutex_unlock(&au8522_list_mutex); +} +EXPORT_SYMBOL(au8522_release_state); + +int au8522_led_gpio_enable(struct au8522_state *state, int onoff) +{ + struct au8522_led_config *led_config = state->config->led_cfg; + u8 val; + + /* bail out if we can't control an LED */ + if (!led_config || !led_config->gpio_output || + !led_config->gpio_output_enable || !led_config->gpio_output_disable) + return 0; + + val = au8522_readreg(state, 0x4000 | + (led_config->gpio_output & ~0xc000)); + if (onoff) { + /* enable GPIO output */ + val &= ~((led_config->gpio_output_enable >> 8) & 0xff); + val |= (led_config->gpio_output_enable & 0xff); + } else { + /* disable GPIO output */ + val &= ~((led_config->gpio_output_disable >> 8) & 0xff); + val |= (led_config->gpio_output_disable & 0xff); + } + return au8522_writereg(state, 0x8000 | + (led_config->gpio_output & ~0xc000), val); +} +EXPORT_SYMBOL(au8522_led_gpio_enable); + +/* led = 0 | off + * led = 1 | signal ok + * led = 2 | signal strong + * led < 0 | only light led if leds are currently off + */ +int au8522_led_ctrl(struct au8522_state *state, int led) +{ + struct au8522_led_config *led_config = state->config->led_cfg; + int i, ret = 0; + + /* bail out if we can't control an LED */ + if (!led_config || !led_config->gpio_leds || + !led_config->num_led_states || !led_config->led_states) + return 0; + + if (led < 0) { + /* if LED is already lit, then leave it as-is */ + if (state->led_state) + return 0; + else + led *= -1; + } + + /* toggle LED if changing state */ + if (state->led_state != led) { + u8 val; + + dprintk("%s: %d\n", __func__, led); + + au8522_led_gpio_enable(state, 1); + + val = au8522_readreg(state, 0x4000 | + (led_config->gpio_leds & ~0xc000)); + + /* start with all leds off */ + for (i = 0; i < led_config->num_led_states; i++) + val &= ~led_config->led_states[i]; + + /* set selected LED state */ + if (led < led_config->num_led_states) + val |= led_config->led_states[led]; + else if (led_config->num_led_states) + val |= + led_config->led_states[led_config->num_led_states - 1]; + + ret = au8522_writereg(state, 0x8000 | + (led_config->gpio_leds & ~0xc000), val); + if (ret < 0) + return ret; + + state->led_state = led; + + if (led == 0) + au8522_led_gpio_enable(state, 0); + } + + return 0; +} +EXPORT_SYMBOL(au8522_led_ctrl); + +int au8522_init(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + state->operational_mode = AU8522_DIGITAL_MODE; + + /* Clear out any state associated with the digital side of the + chip, so that when it gets powered back up it won't think + that it is already tuned */ + state->current_frequency = 0; + + au8522_writereg(state, 0xa4, 1 << 5); + + au8522_i2c_gate_ctrl(fe, 1); + + return 0; +} +EXPORT_SYMBOL(au8522_init); + +int au8522_sleep(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + /* Only power down if the digital side is currently using the chip */ + if (state->operational_mode == AU8522_ANALOG_MODE) { + /* We're not in one of the expected power modes, which means + that the DVB thread is probably telling us to go to sleep + even though the analog frontend has already started using + the chip. So ignore the request */ + return 0; + } + + /* turn off led */ + au8522_led_ctrl(state, 0); + + /* Power down the chip */ + au8522_writereg(state, 0xa4, 1 << 5); + + state->current_frequency = 0; + + return 0; +} +EXPORT_SYMBOL(au8522_sleep); diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c index 25f650934c73..5fc70d6cd04f 100644 --- a/drivers/media/dvb/frontends/au8522_dig.c +++ b/drivers/media/dvb/frontends/au8522_dig.c @@ -30,74 +30,11 @@ static int debug; -/* Despite the name "hybrid_tuner", the framework works just as well for - hybrid demodulators as well... */ -static LIST_HEAD(hybrid_tuner_instance_list); -static DEFINE_MUTEX(au8522_list_mutex); - #define dprintk(arg...)\ do { if (debug)\ printk(arg);\ } while (0) -/* 16 bit registers, 8 bit values */ -int au8522_writereg(struct au8522_state *state, u16 reg, u8 data) -{ - int ret; - u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data }; - - struct i2c_msg msg = { .addr = state->config->demod_address, - .flags = 0, .buf = buf, .len = 3 }; - - ret = i2c_transfer(state->i2c, &msg, 1); - - if (ret != 1) - printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, " - "ret == %i)\n", __func__, reg, data, ret); - - return (ret != 1) ? -1 : 0; -} - -u8 au8522_readreg(struct au8522_state *state, u16 reg) -{ - int ret; - u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff }; - u8 b1[] = { 0 }; - - struct i2c_msg msg[] = { - { .addr = state->config->demod_address, .flags = 0, - .buf = b0, .len = 2 }, - { .addr = state->config->demod_address, .flags = I2C_M_RD, - .buf = b1, .len = 1 } }; - - ret = i2c_transfer(state->i2c, msg, 2); - - if (ret != 2) - printk(KERN_ERR "%s: readreg error (ret == %i)\n", - __func__, ret); - return b1[0]; -} - -static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - struct au8522_state *state = fe->demodulator_priv; - - dprintk("%s(%d)\n", __func__, enable); - - if (state->operational_mode == AU8522_ANALOG_MODE) { - /* We're being asked to manage the gate even though we're - not in digital mode. This can occur if we get switched - over to analog mode before the dvb_frontend kernel thread - has completely shutdown */ - return 0; - } - - if (enable) - return au8522_writereg(state, 0x106, 1); - else - return au8522_writereg(state, 0x106, 0); -} - struct mse2snr_tab { u16 val; u16 data; @@ -609,136 +546,6 @@ static int au8522_set_frontend(struct dvb_frontend *fe) return 0; } -/* Reset the demod hardware and reset all of the configuration registers - to a default state. */ -int au8522_init(struct dvb_frontend *fe) -{ - struct au8522_state *state = fe->demodulator_priv; - dprintk("%s()\n", __func__); - - state->operational_mode = AU8522_DIGITAL_MODE; - - /* Clear out any state associated with the digital side of the - chip, so that when it gets powered back up it won't think - that it is already tuned */ - state->current_frequency = 0; - - au8522_writereg(state, 0xa4, 1 << 5); - - au8522_i2c_gate_ctrl(fe, 1); - - return 0; -} - -static int au8522_led_gpio_enable(struct au8522_state *state, int onoff) -{ - struct au8522_led_config *led_config = state->config->led_cfg; - u8 val; - - /* bail out if we can't control an LED */ - if (!led_config || !led_config->gpio_output || - !led_config->gpio_output_enable || !led_config->gpio_output_disable) - return 0; - - val = au8522_readreg(state, 0x4000 | - (led_config->gpio_output & ~0xc000)); - if (onoff) { - /* enable GPIO output */ - val &= ~((led_config->gpio_output_enable >> 8) & 0xff); - val |= (led_config->gpio_output_enable & 0xff); - } else { - /* disable GPIO output */ - val &= ~((led_config->gpio_output_disable >> 8) & 0xff); - val |= (led_config->gpio_output_disable & 0xff); - } - return au8522_writereg(state, 0x8000 | - (led_config->gpio_output & ~0xc000), val); -} - -/* led = 0 | off - * led = 1 | signal ok - * led = 2 | signal strong - * led < 0 | only light led if leds are currently off - */ -static int au8522_led_ctrl(struct au8522_state *state, int led) -{ - struct au8522_led_config *led_config = state->config->led_cfg; - int i, ret = 0; - - /* bail out if we can't control an LED */ - if (!led_config || !led_config->gpio_leds || - !led_config->num_led_states || !led_config->led_states) - return 0; - - if (led < 0) { - /* if LED is already lit, then leave it as-is */ - if (state->led_state) - return 0; - else - led *= -1; - } - - /* toggle LED if changing state */ - if (state->led_state != led) { - u8 val; - - dprintk("%s: %d\n", __func__, led); - - au8522_led_gpio_enable(state, 1); - - val = au8522_readreg(state, 0x4000 | - (led_config->gpio_leds & ~0xc000)); - - /* start with all leds off */ - for (i = 0; i < led_config->num_led_states; i++) - val &= ~led_config->led_states[i]; - - /* set selected LED state */ - if (led < led_config->num_led_states) - val |= led_config->led_states[led]; - else if (led_config->num_led_states) - val |= - led_config->led_states[led_config->num_led_states - 1]; - - ret = au8522_writereg(state, 0x8000 | - (led_config->gpio_leds & ~0xc000), val); - if (ret < 0) - return ret; - - state->led_state = led; - - if (led == 0) - au8522_led_gpio_enable(state, 0); - } - - return 0; -} - -int au8522_sleep(struct dvb_frontend *fe) -{ - struct au8522_state *state = fe->demodulator_priv; - dprintk("%s()\n", __func__); - - /* Only power down if the digital side is currently using the chip */ - if (state->operational_mode == AU8522_ANALOG_MODE) { - /* We're not in one of the expected power modes, which means - that the DVB thread is probably telling us to go to sleep - even though the analog frontend has already started using - the chip. So ignore the request */ - return 0; - } - - /* turn off led */ - au8522_led_ctrl(state, 0); - - /* Power down the chip */ - au8522_writereg(state, 0xa4, 1 << 5); - - state->current_frequency = 0; - - return 0; -} - static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct au8522_state *state = fe->demodulator_priv; @@ -931,28 +738,6 @@ static int au8522_get_tune_settings(struct dvb_frontend *fe, static struct dvb_frontend_ops au8522_ops; -int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, - u8 client_address) -{ - int ret; - - mutex_lock(&au8522_list_mutex); - ret = hybrid_tuner_request_state(struct au8522_state, (*state), - hybrid_tuner_instance_list, - i2c, client_address, "au8522"); - mutex_unlock(&au8522_list_mutex); - - return ret; -} - -void au8522_release_state(struct au8522_state *state) -{ - mutex_lock(&au8522_list_mutex); - if (state != NULL) - hybrid_tuner_release_state(state); - mutex_unlock(&au8522_list_mutex); -} - static void au8522_release(struct dvb_frontend *fe) { diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h index 751e17d692a9..6e4a438732b5 100644 --- a/drivers/media/dvb/frontends/au8522_priv.h +++ b/drivers/media/dvb/frontends/au8522_priv.h @@ -81,6 +81,8 @@ int au8522_sleep(struct dvb_frontend *fe); int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, u8 client_address); void au8522_release_state(struct au8522_state *state); +int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); +int au8522_led_ctrl(struct au8522_state *state, int led); /* REGISTERS */ #define AU8522_INPUT_CONTROL_REG081H 0x081 diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig index 81ba9d9d1b52..23f7fd22f0eb 100644 --- a/drivers/media/video/au0828/Kconfig +++ b/drivers/media/video/au0828/Kconfig @@ -6,7 +6,8 @@ config VIDEO_AU0828 select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC - select DVB_AU8522 if !DVB_FE_CUSTOMISE + select DVB_AU8522_DTV if !DVB_FE_CUSTOMISE + select DVB_AU8522_V4L if !DVB_FE_CUSTOMISE select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE -- cgit v1.2.3 From 0a7b5e2747c55935944cac356cb48f03dc399b5a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 9 Apr 2012 15:50:23 -0300 Subject: [media] au8522_common: add missing MODULE_LICENSE Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/au8522_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/dvb/frontends/au8522_common.c b/drivers/media/dvb/frontends/au8522_common.c index befdff919a89..625c5046c52b 100644 --- a/drivers/media/dvb/frontends/au8522_common.c +++ b/drivers/media/dvb/frontends/au8522_common.c @@ -26,6 +26,8 @@ #include "dvb_frontend.h" #include "au8522_priv.h" +MODULE_LICENSE("GPL"); + static int debug; #define dprintk(arg...)\ -- cgit v1.2.3 From 52dbb57c2322d494116570cabee8d4c9658604d6 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 9 Apr 2012 18:51:08 -0300 Subject: [media] au8522_common: dont EXPORT_SYMBOL(au8522_led_gpio_enable) This function is only called from within au8522_common.c - mark it static. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/au8522_common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/au8522_common.c b/drivers/media/dvb/frontends/au8522_common.c index 625c5046c52b..5cfe151ee394 100644 --- a/drivers/media/dvb/frontends/au8522_common.c +++ b/drivers/media/dvb/frontends/au8522_common.c @@ -127,7 +127,7 @@ void au8522_release_state(struct au8522_state *state) } EXPORT_SYMBOL(au8522_release_state); -int au8522_led_gpio_enable(struct au8522_state *state, int onoff) +static int au8522_led_gpio_enable(struct au8522_state *state, int onoff) { struct au8522_led_config *led_config = state->config->led_cfg; u8 val; @@ -151,7 +151,6 @@ int au8522_led_gpio_enable(struct au8522_state *state, int onoff) return au8522_writereg(state, 0x8000 | (led_config->gpio_output & ~0xc000), val); } -EXPORT_SYMBOL(au8522_led_gpio_enable); /* led = 0 | off * led = 1 | signal ok -- cgit v1.2.3 From e245afe984b120704f15bc8d391fdb6cf96cfe0c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 17 Apr 2012 08:41:58 -0300 Subject: [media] videodev2.h: Fix VIDIOC_QUERYMENU ioctl regression Fixes a regression in VIDIOC_QUERYMENU introduced when the __s64 value field was added to the union. On a 64-bit system this will change the size of this v4l2_querymenu structure from 44 to 48 bytes, thus breaking the ABI. By adding the packed attribute it is working again. Tested on both 64 and 32 bit systems. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e69cacc9e9ea..5a09ac3f7683 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1176,7 +1176,7 @@ struct v4l2_querymenu { __s64 value; }; __u32 reserved; -}; +} __attribute__ ((packed)); /* Control flags */ #define V4L2_CTRL_FLAG_DISABLED 0x0001 -- cgit v1.2.3 From a00f559c9fd3e2c2262a9fae9a64c8f34d9bc720 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Thu, 12 Apr 2012 08:46:51 -0300 Subject: [media] dib7000p: remove duplicate code and comment Remove duplicate code and comment, probably due to a patch applied twice. Signed-off-by: Gianluca Gennari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/dib7000p.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 5ceadc285b3a..3e1eefada0e8 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -2396,11 +2396,6 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, more common) */ st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; - /* FIXME: make sure the dev.parent field is initialized, or else - request_firmware() will hit an OOPS (this should be moved somewhere - more common) */ - st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; - dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr); /* init 7090 tuner adapter */ -- cgit v1.2.3 From a020182ad68fd74f433693cc19059e5f7918d31c Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Thu, 12 Apr 2012 17:00:07 -0300 Subject: [media] staging: go7007: Add MODULE_FIRMWARE Signed-off-by: Tim Gardner Cc: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/s2250-loader.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c index 4e132519e253..829c248350f8 100644 --- a/drivers/staging/media/go7007/s2250-loader.c +++ b/drivers/staging/media/go7007/s2250-loader.c @@ -189,3 +189,5 @@ module_exit(s2250loader_cleanup); MODULE_AUTHOR(""); MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251"); MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(S2250_LOADER_FIRMWARE); +MODULE_FIRMWARE(S2250_FIRMWARE); -- cgit v1.2.3 From d74185b40d803bfa1ec571c6455a973ca778b08a Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Thu, 12 Apr 2012 17:23:21 -0300 Subject: [media] video: vicam: Add MODULE_FIRMWARE Signed-off-by: Tim Gardner Cc: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/vicam.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c index 911152e169d6..e48ec4db6da4 100644 --- a/drivers/media/video/gspca/vicam.c +++ b/drivers/media/video/gspca/vicam.c @@ -37,9 +37,12 @@ #include #include "gspca.h" +#define VICAM_FIRMWARE "vicam/firmware.fw" + MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(VICAM_FIRMWARE); enum e_ctrl { GAIN, @@ -268,7 +271,7 @@ static int sd_init(struct gspca_dev *gspca_dev) const struct firmware *uninitialized_var(fw); u8 *firmware_buf; - ret = request_ihex_firmware(&fw, "vicam/firmware.fw", + ret = request_ihex_firmware(&fw, VICAM_FIRMWARE, &gspca_dev->dev->dev); if (ret) { pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret); -- cgit v1.2.3 From 0bc9d39b8fa695738c3d5061808692361d2a66ab Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Sat, 14 Apr 2012 09:14:07 -0300 Subject: [media] dib0700: add new USB PID for the Elgato EyeTV DTT stick Reported working here: http://ubuntuforums.org/archive/index.php/t-1510188.html http://ubuntuforums.org/archive/index.php/t-1756828.html https://sites.google.com/site/slackwarestuff/home/elgato-eyetv Signed-off-by: Gianluca Gennari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_devices.c | 7 ++++++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index f9e966aa26e7..510001da6e83 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -3569,6 +3569,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090E) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790E) }, /* 80 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -3832,7 +3833,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 11, + .num_device_descs = 12, .devices = { { "DiBcom STK7070P reference design", { &dib0700_usb_id_table[15], NULL }, @@ -3878,6 +3879,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[50], NULL }, { NULL }, }, + { "Elgato EyeTV DTT rev. 2", + { &dib0700_usb_id_table[81], NULL }, + { NULL }, + }, }, .rc.core = { diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 94d3f8a5cd9e..2418e41ed0dc 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -335,6 +335,7 @@ #define USB_PID_MYGICA_D689 0xd811 #define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011 #define USB_PID_ELGATO_EYETV_DTT 0x0021 +#define USB_PID_ELGATO_EYETV_DTT_2 0x003f #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 #define USB_PID_ELGATO_EYETV_SAT 0x002a #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 -- cgit v1.2.3 From d8a9c01484b258573587a52c3226bbdf6d11cda6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 19 Apr 2012 16:35:27 -0300 Subject: [media] tlg2300: Remove usage of KERNEL_VERSION() As reported by Marcos: On 04-18-2012 01:30, Marcos Paulo de Souza wrote: > The output of "make versioncheck" told us that: > > drivers/media/video/tlg2300/pd-video.c: 1669: need linux/version.h > > If we take a look at the code, we can see that this file uses the macro > KERNEL_VERSION. The V4L2 core now fills it automatically, so drivers shouldn't touch on cap->version anymore. Reported by: Marcos Paulo de Souza Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tlg2300/pd-video.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index a794ae62aebf..bfbf9e56b0a4 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -150,7 +150,6 @@ static int vidioc_querycap(struct file *file, void *fh, strcpy(cap->driver, "tele-video"); strcpy(cap->card, "Telegent Poseidon"); usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->version = KERNEL_VERSION(0, 0, 1); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; -- cgit v1.2.3 From f26cede16514eb78f8f01a30b5e8e016d246b5cb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 19 Apr 2012 16:42:36 -0300 Subject: [media] tm6000: don't use KERNEL_VERSION As reported by Marcos: > The output of "make versioncheck" told us that: > > drivers/staging/media/easycap/easycap_ioctl.c: 2442: need linux/version.h Now that drivers/media/video/v4l2-ioctl.c fills cap->version: case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = (struct v4l2_capability *)arg; if (!ops->vidioc_querycap) break; cap->version = LINUX_VERSION_CODE; V4L2 drivers that use video_ioctl2() shouldn't initialize it anymore. Reported-by: Marcos Paulo de Souza Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tm6000/tm6000-video.c | 1 - drivers/media/video/tm6000/tm6000.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c index bc13db736e24..1ba26d5b2ba6 100644 --- a/drivers/media/video/tm6000/tm6000-video.c +++ b/drivers/media/video/tm6000/tm6000-video.c @@ -889,7 +889,6 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); - cap->version = TM6000_VERSION; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | diff --git a/drivers/media/video/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h index 27ba659cfa85..6df418658c9c 100644 --- a/drivers/media/video/tm6000/tm6000.h +++ b/drivers/media/video/tm6000/tm6000.h @@ -33,8 +33,6 @@ #include "dvb_frontend.h" #include "dmxdev.h" -#define TM6000_VERSION KERNEL_VERSION(0, 0, 2) - /* Inputs */ enum tm6000_itype { TM6000_INPUT_TV = 1, -- cgit v1.2.3 From 713ca5dfd050efa61eb92be51a9ccbdaee2239cd Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Wed, 18 Apr 2012 00:30:05 -0300 Subject: [media] drivers: media: video: adp1653.c: Remove unneeded include of version.h The output of "make versioncheck" told us that: drivers/media/video/adp1653.c: 37 linux/version.h not needed. After we take a look at the code, we can afree to remove it. Cc: Mauro Carvalho Chehab Cc: Signed-off-by: Marcos Paulo de Souza Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/adp1653.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c index 5b045b4a66fe..24afc99d26e4 100644 --- a/drivers/media/video/adp1653.c +++ b/drivers/media/video/adp1653.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From af22b83ab895e71400d59f07b6ee89297c3560b1 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Wed, 18 Apr 2012 00:30:04 -0300 Subject: [media] drivers: media: radio: radio-keene.c: Remove unneeded include of version.h The output of "make versioncheck" told us that: drivers/media/radio/radio-keene.c: 31 linux/version.h not needed. After take a look in the code, we can agree to remove it. Cc: Mauro Carvalho Chehab Cc: Signed-off-by: Marcos Paulo de Souza Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-keene.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index 55bd1d2937c8..26a2b7a0304e 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -28,7 +28,6 @@ #include #include #include -#include #include /* driver and module definitions */ -- cgit v1.2.3 From c1341a16f6c1d25d3d8bd1ad64556b8029cbb4b8 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Wed, 18 Apr 2012 00:30:03 -0300 Subject: [media] drivers: media: dvb: ddbridge: ddbridge-code: Remove unneeded include of version.h The output of "make versioncheck" told us that the file drivers/media/dvb/ddbridge/ddbridge-code.c has a incorrect include of version.h: linux/drivers/media/dvb/ddbridge/ddbridge-core.c: 34 linux/version.h not needed. After take a look in the code, we can agree to remove it. Cc: Mauro Carvalho Chehab Cc: Signed-off-by: Marcos Paulo de Souza Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ddbridge/ddbridge-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c index d88c4aa7d24d..115777ec7536 100644 --- a/drivers/media/dvb/ddbridge/ddbridge-core.c +++ b/drivers/media/dvb/ddbridge/ddbridge-core.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From aa6d5f29534a6d1459f9768c591a7a72aadc5941 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 18 Apr 2012 02:55:51 -0300 Subject: [media] pluto2: remove some dead code buf[] is a 4 character array. Perhaps this was some debugging code from back in the day? Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/pluto2/pluto2.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c index e1f20c236989..f148b19a206a 100644 --- a/drivers/media/dvb/pluto2/pluto2.c +++ b/drivers/media/dvb/pluto2/pluto2.c @@ -481,14 +481,6 @@ static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe) if (p->bandwidth_hz == 8000000) buf[3] |= 0x08; - if (sizeof(buf) == 6) { - buf[4] = buf[2]; - buf[4] &= ~0x1c; - buf[4] |= 0x18; - - buf[5] = (0 << 7) | (2 << 4); - } - msg.addr = I2C_ADDR_TUA6034 >> 1; msg.flags = 0; msg.buf = buf; -- cgit v1.2.3 From ee71e7b3ae1780e4475aa5dd980dd99c0309079b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 19 Apr 2012 12:27:56 -0300 Subject: [media] V4L: fix incorrect refcounting Both radio-keene and dsbr100 did one v4l2_device_get too many. Thus the refcount never became 0 and that causes a memory leak. Also updated the V4L2 framework documentation accordingly. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 14 +++++++++----- drivers/media/radio/dsbr100.c | 1 - drivers/media/radio/radio-keene.c | 1 - 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 659b2ba12a4f..e3dfc268d9c1 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -182,11 +182,11 @@ static int __devinit drv_probe(struct pci_dev *pdev, } If you have multiple device nodes then it can be difficult to know when it is -safe to unregister v4l2_device. For this purpose v4l2_device has refcounting -support. The refcount is increased whenever video_register_device is called and -it is decreased whenever that device node is released. When the refcount reaches -zero, then the v4l2_device release() callback is called. You can do your final -cleanup there. +safe to unregister v4l2_device for hotpluggable devices. For this purpose +v4l2_device has refcounting support. The refcount is increased whenever +video_register_device is called and it is decreased whenever that device node +is released. When the refcount reaches zero, then the v4l2_device release() +callback is called. You can do your final cleanup there. If other device nodes (e.g. ALSA) are created, then you can increase and decrease the refcount manually as well by calling: @@ -197,6 +197,10 @@ or: int v4l2_device_put(struct v4l2_device *v4l2_dev); +Since the initial refcount is 1 you also need to call v4l2_device_put in the +disconnect() callback (for USB devices) or in the remove() callback (for e.g. +PCI devices), otherwise the refcount will never reach 0. + struct v4l2_subdev ------------------ diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index f36905b63645..bf813a63ab2a 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -481,7 +481,6 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) { struct dsbr100_device *radio = usb_get_intfdata(intf); - v4l2_device_get(&radio->v4l2_dev); mutex_lock(&radio->v4l2_lock); usb_set_intfdata(intf, NULL); video_unregister_device(&radio->videodev); diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index 26a2b7a0304e..5f33047d0d43 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -148,7 +148,6 @@ static void usb_keene_disconnect(struct usb_interface *intf) { struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf)); - v4l2_device_get(&radio->v4l2_dev); mutex_lock(&radio->lock); usb_set_intfdata(intf, NULL); video_unregister_device(&radio->vdev); -- cgit v1.2.3 From d1c754a9326d95dff93bfe8004cba8574a7a20a8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 19 Apr 2012 12:36:03 -0300 Subject: [media] V4L2: drivers implementing vidioc_default should also return -ENOTTY If the vidioc_default implementation doesn't support the ioctl, then drivers must return -ENOTTY instead of -EINVAL. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-ioctl.c | 2 +- drivers/media/video/davinci/vpfe_capture.c | 2 +- drivers/media/video/ivtv/ivtv-ioctl.c | 2 +- drivers/media/video/meye.c | 2 +- drivers/media/video/mxb.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index be49f68ddf37..35fde4e931f5 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -1137,7 +1137,7 @@ static long cx18_default(struct file *file, void *fh, bool valid_prio, } default: - return -EINVAL; + return -ENOTTY; } return 0; } diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 20cf271a774b..49a845fb804a 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -1761,7 +1761,7 @@ static long vpfe_param_handler(struct file *file, void *priv, } break; default: - ret = -EINVAL; + ret = -ENOTTY; } unlock_out: mutex_unlock(&vpfe_dev->lock); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 989e556913ed..a151271f60e1 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1827,7 +1827,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, return ivtv_decoder_ioctls(file, cmd, (void *)arg); default: - return -EINVAL; + return -ENOTTY; } return 0; } diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index b09a3c80a15e..7bc775219f97 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1570,7 +1570,7 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio, return meyeioc_stilljcapt((int *) arg); default: - return -EINVAL; + return -ENOTTY; } } diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 2e4131748438..ca3f70f0bad5 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -688,7 +688,7 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio, /* DEB2(pr_err("does not handle this ioctl\n")); */ - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; } -- cgit v1.2.3 From ca57681195265d11b03ea4f98f145e701507ff7a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 19 Apr 2012 12:37:18 -0300 Subject: [media] v4l2-ctrls.c: zero min/max/step/def values for 64 bit integers Those fields are meaningless for such control types, and the control framework should zero them. Otherwise v4l2-compliance will complain about non-zero min/max/step/def fields. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 1a71aa5fd458..c93a9796f1fb 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -770,6 +770,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_DEC_PTS: *type = V4L2_CTRL_TYPE_INTEGER64; *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE; + *min = *max = *step = *def = 0; break; default: *type = V4L2_CTRL_TYPE_INTEGER; -- cgit v1.2.3 From f70cfc7f182bf4605449cad4d665febf012c6f6b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 19 Apr 2012 11:44:18 -0300 Subject: [media] vivi: fix duplicate line This was inadvertently introduced when the integer menu control was added. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/vivi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index ff39eb2f2d7b..d64d482f4f6b 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -504,12 +504,12 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->boolean->cur.val, dev->menu->qmenu[dev->menu->cur.val], dev->string->cur.string); + gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " integer_menu %lld, value %d ", dev->int_menu->qmenu_int[dev->int_menu->cur.val], dev->int_menu->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); mutex_unlock(&dev->ctrl_handler.lock); - gen_text(dev, vbuf, line++ * 16, 16, str); if (dev->button_pressed) { dev->button_pressed--; snprintf(str, sizeof(str), " button pressed!"); -- cgit v1.2.3 From b72d66770953c2177d70a7a5d24521a447d2b443 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 18 Apr 2012 03:59:58 -0300 Subject: [media] V4L: fix a compiler warning Fix the warning: In file included from /home/lyakh/software/project/24/src/linux-2.6/drivers/media/video/v4l2-subdev.c:29: linux-2.6/include/media/v4l2-ctrls.h:497: warning: 'struct file' declared inside parameter list linux-2.6/include/media/v4l2-ctrls.h:497: warning: its scope is only this definition or declaration, which is probably not what you want linux-2.6/include/media/v4l2-ctrls.h:505: warning: 'struct file' declared inside parameter list Signed-off-by: Guennadi Liakhovetski Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-ctrls.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 33907a969752..8920f8210eab 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -496,6 +496,7 @@ void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl, void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl, struct v4l2_subscribed_event *sev); +struct file; /* Can be used as a vidioc_log_status function that just dumps all controls associated with the filehandle. */ int v4l2_ctrl_log_status(struct file *file, void *fh); -- cgit v1.2.3 From 2e71064f2f9009c6ef672abc4f9160d16a3d50bd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 20 Apr 2012 02:50:45 -0300 Subject: [media] saa7164: saa7164_vbi_stop_port() returns linux error codes The saa7164_vbi_stop_port() changes the SAA_ERR_ALREADY_STOPPED result code to -EIO before returning. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7164/saa7164-vbi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c index 273cf807401c..d8e6c8f14079 100644 --- a/drivers/media/video/saa7164/saa7164-vbi.c +++ b/drivers/media/video/saa7164/saa7164-vbi.c @@ -952,7 +952,7 @@ static int saa7164_vbi_start_streaming(struct saa7164_port *port) /* Stop the hardware, regardless */ result = saa7164_vbi_stop_port(port); - if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + if (result != SAA_OK) { printk(KERN_ERR "%s() pause/forced stop transition " "failed, res = 0x%x\n", __func__, result); } @@ -971,7 +971,7 @@ static int saa7164_vbi_start_streaming(struct saa7164_port *port) /* Stop the hardware, regardless */ result = saa7164_vbi_acquire_port(port); result = saa7164_vbi_stop_port(port); - if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + if (result != SAA_OK) { printk(KERN_ERR "%s() run/forced stop transition " "failed, res = 0x%x\n", __func__, result); } -- cgit v1.2.3 From bcb2cf6e0bf033d79821c89e5ccb328bfbd44907 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 20 Apr 2012 09:15:02 -0300 Subject: [media] ngene: remove an unneeded condition "stat" is always zero here. The condition used to be needed, but we shifted stuff around in 0f0b270f90 "[media] ngene: CXD2099AR Common Interface driver". This doesn't change how the code works, it's just a bit tidier. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index f129a9303f80..39857384af10 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1409,10 +1409,8 @@ static int ngene_start(struct ngene *dev) if (stat < 0) goto fail; - if (!stat) - return stat; + return 0; - /* otherwise error: fall through */ fail: ngwritel(0, NGENE_INT_ENABLE); free_irq(dev->pci_dev->irq, dev); -- cgit v1.2.3 From d63b21bfa8afbce0872fdb7718d822c5fd0878ea Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 22 Apr 2012 07:54:42 -0300 Subject: [media] drivers/media/video/au0828/au0828-video.c: add missing video_device_release At the point of the call to video_register_device, both dev->vbi_dev and dev->vdev have been allocated, and so should be freed on failure. The error-handling code is moved to the end of the function, to avoid code duplication. Signed-off-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/au0828/au0828-video.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 0b3e481ffe8c..141f9c23a5ca 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -1881,7 +1881,7 @@ int au0828_analog_register(struct au0828_dev *dev, int retval = -ENOMEM; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - int i; + int i, ret; dprintk(1, "au0828_analog_register called!\n"); @@ -1951,8 +1951,8 @@ int au0828_analog_register(struct au0828_dev *dev, dev->vbi_dev = video_device_alloc(); if (NULL == dev->vbi_dev) { dprintk(1, "Can't allocate vbi_device.\n"); - kfree(dev->vdev); - return -ENOMEM; + ret = -ENOMEM; + goto err_vdev; } /* Fill the video capture device struct */ @@ -1971,8 +1971,8 @@ int au0828_analog_register(struct au0828_dev *dev, if (retval != 0) { dprintk(1, "unable to register video device (error = %d).\n", retval); - video_device_release(dev->vdev); - return -ENODEV; + ret = -ENODEV; + goto err_vbi_dev; } /* Register the vbi device */ @@ -1981,13 +1981,18 @@ int au0828_analog_register(struct au0828_dev *dev, if (retval != 0) { dprintk(1, "unable to register vbi device (error = %d).\n", retval); - video_device_release(dev->vbi_dev); - video_device_release(dev->vdev); - return -ENODEV; + ret = -ENODEV; + goto err_vbi_dev; } dprintk(1, "%s completed!\n", __func__); return 0; + +err_vbi_dev: + video_device_release(dev->vbi_dev); +err_vdev: + video_device_release(dev->vdev); + return ret; } -- cgit v1.2.3 From 7a1d082c83d24bfc0d3072cfecc98172bd0a5a3b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 26 Apr 2012 07:39:14 -0300 Subject: [media] V4L2 Spec: fix typo V4L2_CID_JPEG_COMPRESION_QUALITY -> V4L2_CID_JPEG_COMPRESSION_QUALITY Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 5038a3a9a24c..5e12257dfcef 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3538,12 +3538,12 @@ interface and may change in the future. - V4L2_CID_JPEG_COMPRESION_QUALITY + V4L2_CID_JPEG_COMPRESSION_QUALITY integer - V4L2_CID_JPEG_COMPRESION_QUALITY control + V4L2_CID_JPEG_COMPRESSION_QUALITY control determines trade-off between image quality and size. It provides simpler method for applications to control image quality, without a need for direct reconfiguration of luminance and chrominance @@ -3551,7 +3551,7 @@ interface and may change in the future. In cases where a driver uses quantization tables configured directly by an application, using interfaces defined elsewhere, - V4L2_CID_JPEG_COMPRESION_QUALITY control should be set + V4L2_CID_JPEG_COMPRESSION_QUALITY control should be set by driver to 0. The value range of this control is driver-specific. Only -- cgit v1.2.3 From 4d1b58b84472d1d300a66e1c5fd765b21e74ba15 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 22 Mar 2012 13:55:05 -0300 Subject: [media] smsusb: add autodetection support for USB ID 2040:c0a0 Signed-off-by: Michael Krufky Cc: stable@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smsusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c index b1fe5137df09..63c004a25e0b 100644 --- a/drivers/media/dvb/siano/smsusb.c +++ b/drivers/media/dvb/siano/smsusb.c @@ -542,6 +542,8 @@ static const struct usb_device_id smsusb_id_table[] __devinitconst = { .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { USB_DEVICE(0x2040, 0xc090), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xc0a0), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { } /* Terminating entry */ }; -- cgit v1.2.3 From 193d0a510df7f1611af47b0c4a968400298fa1bf Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Sat, 24 Mar 2012 09:14:01 -0300 Subject: [media] gspca - ov519: Add more information about probe problems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error messages in stable kernel releases must be output by 'pr_err'. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 739e8a2a2d30..183457c5cfdb 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -2804,7 +2804,7 @@ static void ov7xx0_configure(struct sd *sd) /* add OV7670 here * it appears to be wrongly detected as a 7610 by default */ if (rc < 0) { - PDEBUG(D_ERR, "Error detecting sensor type"); + pr_err("Error detecting sensor type\n"); return; } if ((rc & 3) == 3) { @@ -2832,12 +2832,12 @@ static void ov7xx0_configure(struct sd *sd) /* try to read product id registers */ high = i2c_r(sd, 0x0a); if (high < 0) { - PDEBUG(D_ERR, "Error detecting camera chip PID"); + pr_err("Error detecting camera chip PID\n"); return; } low = i2c_r(sd, 0x0b); if (low < 0) { - PDEBUG(D_ERR, "Error detecting camera chip VER"); + pr_err("Error detecting camera chip VER\n"); return; } if (high == 0x76) { @@ -2863,7 +2863,7 @@ static void ov7xx0_configure(struct sd *sd) sd->sensor = SEN_OV7660; break; default: - PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low); + pr_err("Unknown sensor: 0x76%02x\n", low); return; } } else { @@ -2884,7 +2884,7 @@ static void ov6xx0_configure(struct sd *sd) /* Detect sensor (sub)type */ rc = i2c_r(sd, OV7610_REG_COM_I); if (rc < 0) { - PDEBUG(D_ERR, "Error detecting sensor type"); + pr_err("Error detecting sensor type\n"); return; } -- cgit v1.2.3 From 9637c6522510d29abdecb80093a377a7bdcc57bb Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Sat, 24 Mar 2012 09:16:14 -0300 Subject: [media] gspca - sn9c20x: Change the number of the sensor mt9vprb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoid skips in switch statements. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 7e71aa2d2522..5e48521d2cb2 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -59,7 +59,7 @@ MODULE_LICENSE("GPL"); #define SENSOR_MT9M111 9 #define SENSOR_MT9M112 10 #define SENSOR_HV7131R 11 -#define SENSOR_MT9VPRB 20 +#define SENSOR_MT9VPRB 12 /* camera flags */ #define HAS_NO_BUTTON 0x1 -- cgit v1.2.3 From bed37388cf9f7d6248b6bd1033afa3dc69ca456b Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Sat, 24 Mar 2012 09:17:37 -0300 Subject: [media] gspca - sn9c20x: Add the sensor mt9vprb to the sensor ident table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The table did not contain the sensor mt9vprb. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 5e48521d2cb2..5107fd6001c8 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -760,6 +760,7 @@ static u16 i2c_ident[] = { V4L2_IDENT_MT9M111, V4L2_IDENT_MT9M112, V4L2_IDENT_HV7131R, +[SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN, }; static u16 bridge_init[][2] = { -- cgit v1.2.3 From dae1de645e41a5d7f36686ebcb62987545f95e94 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Sat, 24 Mar 2012 09:20:25 -0300 Subject: [media] gspca - sn9c20x: Define more tables as constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 5107fd6001c8..0958a93e0b82 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -747,7 +747,7 @@ static const s16 hsv_blue_y[] = { 4, 2, 0, -1, -3, -5, -7, -9, -11 }; -static u16 i2c_ident[] = { +static const u16 i2c_ident[] = { V4L2_IDENT_OV9650, V4L2_IDENT_OV9655, V4L2_IDENT_SOI968, @@ -763,7 +763,7 @@ static u16 i2c_ident[] = { [SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN, }; -static u16 bridge_init[][2] = { +static const u16 bridge_init[][2] = { {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c}, {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40}, {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10}, @@ -787,7 +787,7 @@ static u16 bridge_init[][2] = { }; /* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ -static u8 ov_gain[] = { +static const u8 ov_gain[] = { 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */, 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */, 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */, @@ -799,7 +799,7 @@ static u8 ov_gain[] = { }; /* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */ -static u16 micron1_gain[] = { +static const u16 micron1_gain[] = { /* 1x 1.25x 1.5x 1.75x */ 0x0020, 0x0028, 0x0030, 0x0038, /* 2x 2.25x 2.5x 2.75x */ @@ -820,7 +820,7 @@ static u16 micron1_gain[] = { /* mt9m001 sensor uses a different gain formula then other micron sensors */ /* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */ -static u16 micron2_gain[] = { +static const u16 micron2_gain[] = { /* 1x 1.25x 1.5x 1.75x */ 0x0008, 0x000a, 0x000c, 0x000e, /* 2x 2.25x 2.5x 2.75x */ @@ -840,7 +840,7 @@ static u16 micron2_gain[] = { }; /* Gain = .5 + bit[7:0] / 16 */ -static u8 hv7131r_gain[] = { +static const u8 hv7131r_gain[] = { 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */, 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */, 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */, @@ -851,7 +851,7 @@ static u8 hv7131r_gain[] = { 0x78 /* 8x */ }; -static struct i2c_reg_u8 soi968_init[] = { +static const struct i2c_reg_u8 soi968_init[] = { {0x0c, 0x00}, {0x0f, 0x1f}, {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, @@ -865,7 +865,7 @@ static struct i2c_reg_u8 soi968_init[] = { {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, }; -static struct i2c_reg_u8 ov7660_init[] = { +static const struct i2c_reg_u8 ov7660_init[] = { {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, @@ -877,7 +877,7 @@ static struct i2c_reg_u8 ov7660_init[] = { {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, }; -static struct i2c_reg_u8 ov7670_init[] = { +static const struct i2c_reg_u8 ov7670_init[] = { {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, @@ -934,7 +934,7 @@ static struct i2c_reg_u8 ov7670_init[] = { {0x93, 0x00}, }; -static struct i2c_reg_u8 ov9650_init[] = { +static const struct i2c_reg_u8 ov9650_init[] = { {0x00, 0x00}, {0x01, 0x78}, {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, @@ -964,7 +964,7 @@ static struct i2c_reg_u8 ov9650_init[] = { {0xaa, 0x92}, {0xab, 0x0a}, }; -static struct i2c_reg_u8 ov9655_init[] = { +static const struct i2c_reg_u8 ov9655_init[] = { {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08}, {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d}, @@ -991,7 +991,7 @@ static struct i2c_reg_u8 ov9655_init[] = { {0x04, 0x03}, {0x00, 0x13}, }; -static struct i2c_reg_u16 mt9v112_init[] = { +static const struct i2c_reg_u16 mt9v112_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, @@ -1010,7 +1010,7 @@ static struct i2c_reg_u16 mt9v112_init[] = { {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, }; -static struct i2c_reg_u16 mt9v111_init[] = { +static const struct i2c_reg_u16 mt9v111_init[] = { {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0}, {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e}, @@ -1020,7 +1020,7 @@ static struct i2c_reg_u16 mt9v111_init[] = { {0x0e, 0x0008}, {0x20, 0x0000} }; -static struct i2c_reg_u16 mt9v011_init[] = { +static const struct i2c_reg_u16 mt9v011_init[] = { {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, @@ -1047,7 +1047,7 @@ static struct i2c_reg_u16 mt9v011_init[] = { {0x06, 0x0029}, {0x05, 0x0009}, }; -static struct i2c_reg_u16 mt9m001_init[] = { +static const struct i2c_reg_u16 mt9m001_init[] = { {0x0d, 0x0001}, {0x0d, 0x0000}, {0x04, 0x0500}, /* hres = 1280 */ @@ -1063,21 +1063,21 @@ static struct i2c_reg_u16 mt9m001_init[] = { {0x35, 0x0057}, }; -static struct i2c_reg_u16 mt9m111_init[] = { +static const struct i2c_reg_u16 mt9m111_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, {0xf0, 0x0000}, }; -static struct i2c_reg_u16 mt9m112_init[] = { +static const struct i2c_reg_u16 mt9m112_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, {0xf0, 0x0000}, }; -static struct i2c_reg_u8 hv7131r_init[] = { +static const struct i2c_reg_u8 hv7131r_init[] = { {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, @@ -1181,7 +1181,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) } static void i2c_w1_buf(struct gspca_dev *gspca_dev, - struct i2c_reg_u8 *buf, int sz) + const struct i2c_reg_u8 *buf, int sz) { while (--sz >= 0) { i2c_w1(gspca_dev, buf->reg, buf->val); @@ -1211,7 +1211,7 @@ static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) } static void i2c_w2_buf(struct gspca_dev *gspca_dev, - struct i2c_reg_u16 *buf, int sz) + const struct i2c_reg_u16 *buf, int sz) { while (--sz >= 0) { i2c_w2(gspca_dev, buf->reg, buf->val); @@ -2310,7 +2310,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; int avg_lum, is_jpeg; - static u8 frame_header[] = + static const u8 frame_header[] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; is_jpeg = (sd->fmt & 0x03) == 0; -- cgit v1.2.3 From c4407fe86d3856f60ec711e025bbe9a0159354a3 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Sat, 24 Mar 2012 09:23:56 -0300 Subject: [media] gspca - sn9c20x: Set the i2c interface speed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i2c interface speed was set to 400 Kb/s while it is 100 Kb/s for most sensors. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 0958a93e0b82..c5b42e4ac9b4 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -105,6 +105,7 @@ struct sd { u8 exposure_step; u8 i2c_addr; + u8 i2c_intf; u8 sensor; u8 hstart; u8 vstart; @@ -1168,7 +1169,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) * from the point of view of the bridge, the length * includes the address */ - row[0] = 0x81 | (2 << 4); + row[0] = sd->i2c_intf | (2 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = val; @@ -1198,7 +1199,7 @@ static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) * from the point of view of the bridge, the length * includes the address */ - row[0] = 0x81 | (3 << 4); + row[0] = sd->i2c_intf | (3 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = val >> 8; @@ -1224,7 +1225,7 @@ static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | (1 << 4); + row[0] = sd->i2c_intf | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1233,7 +1234,7 @@ static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) row[6] = 0; row[7] = 0x10; i2c_w(gspca_dev, row); - row[0] = 0x81 | (1 << 4) | 0x02; + row[0] = sd->i2c_intf | (1 << 4) | 0x02; row[2] = 0; i2c_w(gspca_dev, row); reg_r(gspca_dev, 0x10c2, 5); @@ -1245,7 +1246,7 @@ static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | (1 << 4); + row[0] = sd->i2c_intf | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1254,7 +1255,7 @@ static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) row[6] = 0; row[7] = 0x10; i2c_w(gspca_dev, row); - row[0] = 0x81 | (2 << 4) | 0x02; + row[0] = sd->i2c_intf | (2 << 4) | 0x02; row[2] = 0; i2c_w(gspca_dev, row); reg_r(gspca_dev, 0x10c2, 5); @@ -1642,7 +1643,8 @@ static void set_hvflip(struct gspca_dev *gspca_dev) static void set_exposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; + u8 exp[8] = {sd->i2c_intf, sd->i2c_addr, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; int expo; expo = sd->ctrls[EXPOSURE].val; @@ -1680,7 +1682,8 @@ static void set_exposure(struct gspca_dev *gspca_dev) static void set_gain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; + u8 gain[8] = {sd->i2c_intf, sd->i2c_addr, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; int g; g = sd->ctrls[GAIN].val; @@ -1828,6 +1831,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = id->driver_info >> 8; sd->i2c_addr = id->driver_info; sd->flags = id->driver_info >> 16; + sd->i2c_intf = 0x80; /* i2c 100 Kb/s */ switch (sd->sensor) { case SENSOR_MT9M112: @@ -1841,6 +1845,9 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = mono_mode; cam->nmodes = ARRAY_SIZE(mono_mode); break; + case SENSOR_HV7131R: + sd->i2c_intf = 0x81; /* i2c 400 Kb/s */ + /* fall thru */ default: cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); -- cgit v1.2.3 From 4fb8137c43ebc0f5bc0dde6b64faa9dd1b1d7970 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Sat, 24 Mar 2012 09:28:39 -0300 Subject: [media] gspca - sn9c20x: Don't do sensor update before the capture is started MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Telling the bridge to update the sensor when setting the exposure or the gain is not needed when the image transfer is not started. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index c5b42e4ac9b4..da2904a891aa 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -1644,9 +1644,12 @@ static void set_exposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 exp[8] = {sd->i2c_intf, sd->i2c_addr, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; int expo; + if (gspca_dev->streaming) + exp[7] = 0x1e; + expo = sd->ctrls[EXPOSURE].val; switch (sd->sensor) { case SENSOR_OV7660: @@ -1683,9 +1686,12 @@ static void set_gain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 gain[8] = {sd->i2c_intf, sd->i2c_addr, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; int g; + if (gspca_dev->streaming) + gain[7] = 0x15; /* or 1d ? */ + g = sd->ctrls[GAIN].val; switch (sd->sensor) { case SENSOR_OV7660: -- cgit v1.2.3 From a1ac5dc28d2b4ca78e183229f7c595ffd725241c Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Sat, 24 Mar 2012 09:33:42 -0300 Subject: [media] gspca - sn9c20x: Change the exposure setting of Omnivision sensors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The exposure of Omnivision sensors is defined by the registers 07, 10 and 04. This patch updates the registers 10 and 04 before using the registers 2d and 2e (dummy lines). Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index da2904a891aa..5285a519c1f3 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -874,8 +874,8 @@ static const struct i2c_reg_u8 ov7660_init[] = { 0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */ {0x17, 0x10}, {0x18, 0x61}, {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43}, - {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6}, - {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, + {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0x00}, + {0x2e, 0x00}, {0x01, 0x78}, {0x02, 0x50}, }; static const struct i2c_reg_u8 ov7670_init[] = { @@ -1645,7 +1645,7 @@ static void set_exposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 exp[8] = {sd->i2c_intf, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; - int expo; + int expo, expo2; if (gspca_dev->streaming) exp[7] = 0x1e; @@ -1656,8 +1656,23 @@ static void set_exposure(struct gspca_dev *gspca_dev) case SENSOR_OV7670: case SENSOR_OV9655: case SENSOR_OV9650: + if (expo > 547) + expo2 = 547; + else + expo2 = expo; + exp[0] |= (2 << 4); + exp[2] = 0x10; /* AECH */ + exp[3] = expo2 >> 2; + exp[7] = 0x10; + i2c_w(gspca_dev, exp); + exp[2] = 0x04; /* COM1 */ + exp[3] = expo2 & 0x0003; + exp[7] = 0x10; + i2c_w(gspca_dev, exp); + expo -= expo2; + exp[7] = 0x1e; exp[0] |= (3 << 4); - exp[2] = 0x2d; + exp[2] = 0x2d; /* ADVFL & ADVFH */ exp[3] = expo; exp[4] = expo >> 8; break; -- cgit v1.2.3 From 5d778648ed571f8dfe9e105c540661dd7451b315 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2012 08:13:26 -0300 Subject: [media] dsbr100: clean up and update to the latest v4l2 framework This driver now complies with the v4l2-compliance tests and uses the v4l2 frameworks correctly. It has been tested with the radio-keene FM transmitter as well. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/dsbr100.c | 527 +++++++++++++----------------------------- 1 file changed, 165 insertions(+), 362 deletions(-) diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index bf813a63ab2a..63b112b555b2 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -1,92 +1,37 @@ /* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21. - The device plugs into both the USB and an analog audio input, so this thing - only deals with initialisation and frequency setting, the - audio data has to be handled by a sound driver. - - Major issue: I can't find out where the device reports the signal - strength, and indeed the windows software appearantly just looks - at the stereo indicator as well. So, scanning will only find - stereo stations. Sad, but I can't help it. - - Also, the windows program sends oodles of messages over to the - device, and I couldn't figure out their meaning. My suspicion - is that they don't have any:-) - - You might find some interesting stuff about this module at - http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr - - Copyright (c) 2000 Markus Demleitner - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - History: - - Version 0.46: - Removed usb_dsbr100_open/close calls and radio->users counter. Also, - radio->muted changed to radio->status and suspend/resume calls updated. - - Version 0.45: - Converted to v4l2_device. - - Version 0.44: - Add suspend/resume functions, fix unplug of device, - a lot of cleanups and fixes by Alexey Klimov - - Version 0.43: - Oliver Neukum: avoided DMA coherency issue - - Version 0.42: - Converted dsbr100 to use video_ioctl2 - by Douglas Landgraf - - Version 0.41-ac1: - Alan Cox: Some cleanups and fixes - - Version 0.41: - Converted to V4L2 API by Mauro Carvalho Chehab - - Version 0.40: - Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing - - Version 0.30: - Markus: Updates for 2.5.x kernel and more ISO compliant source - - Version 0.25: - PSL and Markus: Cleanup, radio now doesn't stop on device close - - Version 0.24: - Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally - right. Some minor cleanup, improved standalone compilation - - Version 0.23: - Markus: Sign extension bug fixed by declaring transfer_buffer unsigned - - Version 0.22: - Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, - thanks to Mike Cox for pointing the problem out. - - Version 0.21: - Markus: Minor cleanup, warnings if something goes wrong, lame attempt - to adhere to Documentation/CodingStyle - - Version 0.2: - Brad Hards : Fixes to make it work as non-module - Markus: Copyright clarification - - Version 0.01: Markus: initial release - + * The device plugs into both the USB and an analog audio input, so this thing + * only deals with initialisation and frequency setting, the + * audio data has to be handled by a sound driver. + * + * Major issue: I can't find out where the device reports the signal + * strength, and indeed the windows software appearantly just looks + * at the stereo indicator as well. So, scanning will only find + * stereo stations. Sad, but I can't help it. + * + * Also, the windows program sends oodles of messages over to the + * device, and I couldn't figure out their meaning. My suspicion + * is that they don't have any:-) + * + * You might find some interesting stuff about this module at + * http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr + * + * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. + * + * Copyright (c) 2000 Markus Demleitner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include @@ -95,17 +40,19 @@ #include #include #include +#include #include #include -#include +#include +#include /* * Version Information */ -#define DRIVER_VERSION "0.4.7" - -#define DRIVER_AUTHOR "Markus Demleitner " -#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" +MODULE_AUTHOR("Markus Demleitner "); +MODULE_DESCRIPTION("D-Link DSB-R100 USB FM radio driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.1.0"); #define DSB100_VENDOR 0x04b4 #define DSB100_PRODUCT 0x1002 @@ -122,19 +69,8 @@ devices, that would be 76 and 91. */ #define FREQ_MAX 108.0 #define FREQ_MUL 16000 -/* defines for radio->status */ -#define STARTED 0 -#define STOPPED 1 - #define v4l2_dev_to_radio(d) container_of(d, struct dsbr100_device, v4l2_dev) -static int usb_dsbr100_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void usb_dsbr100_disconnect(struct usb_interface *intf); -static int usb_dsbr100_suspend(struct usb_interface *intf, - pm_message_t message); -static int usb_dsbr100_resume(struct usb_interface *intf); - static int radio_nr = -1; module_param(radio_nr, int, 0); @@ -143,179 +79,92 @@ struct dsbr100_device { struct usb_device *usbdev; struct video_device videodev; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; u8 *transfer_buffer; struct mutex v4l2_lock; int curfreq; - int stereo; - int status; -}; - -static struct usb_device_id usb_dsbr100_device_table [] = { - { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); - -/* USB subsystem interface */ -static struct usb_driver usb_dsbr100_driver = { - .name = "dsbr100", - .probe = usb_dsbr100_probe, - .disconnect = usb_dsbr100_disconnect, - .id_table = usb_dsbr100_device_table, - .suspend = usb_dsbr100_suspend, - .resume = usb_dsbr100_resume, - .reset_resume = usb_dsbr100_resume, - .supports_autosuspend = 0, + bool stereo; + bool muted; }; /* Low-level device interface begins here */ -/* switch on radio */ -static int dsbr100_start(struct dsbr100_device *radio) +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int dsbr100_setfreq(struct dsbr100_device *radio, unsigned freq) { - int retval; - int request; - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00, 0xC7, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = USB_REQ_GET_STATUS; - goto usb_control_msg_failed; + unsigned f = (freq / 16 * 80) / 1000 + 856; + int retval = 0; + + if (!radio->muted) { + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_TUNE, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + (f >> 8) & 0x00ff, f & 0xff, + radio->transfer_buffer, 8, 300); + if (retval >= 0) + mdelay(1); } - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - DSB100_ONOFF, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x01, 0x00, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = DSB100_ONOFF; - goto usb_control_msg_failed; + if (retval >= 0) { + radio->curfreq = freq; + return 0; } - - radio->status = STARTED; - return (radio->transfer_buffer)[0]; - -usb_control_msg_failed: dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", - __func__, retval, request); + __func__, retval, DSB100_TUNE); return retval; - } -/* switch off radio */ -static int dsbr100_stop(struct dsbr100_device *radio) +/* switch on radio */ +static int dsbr100_start(struct dsbr100_device *radio) { - int retval; - int request; - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x16, 0x1C, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = USB_REQ_GET_STATUS; - goto usb_control_msg_failed; - } - - retval = usb_control_msg(radio->usbdev, + int retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), DSB100_ONOFF, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00, 0x00, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = DSB100_ONOFF; - goto usb_control_msg_failed; - } - - radio->status = STOPPED; - return (radio->transfer_buffer)[0]; + 0x01, 0x00, radio->transfer_buffer, 8, 300); -usb_control_msg_failed: + if (retval >= 0) + return dsbr100_setfreq(radio, radio->curfreq); dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", - __func__, retval, request); + __func__, retval, DSB100_ONOFF); return retval; } -/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ -static int dsbr100_setfreq(struct dsbr100_device *radio) +/* switch off radio */ +static int dsbr100_stop(struct dsbr100_device *radio) { - int retval; - int request; - int freq = (radio->curfreq / 16 * 80) / 1000 + 856; - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - DSB100_TUNE, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - (freq >> 8) & 0x00ff, freq & 0xff, - radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = DSB100_TUNE; - goto usb_control_msg_failed; - } - - retval = usb_control_msg(radio->usbdev, + int retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, + DSB100_ONOFF, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x96, 0xB7, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = USB_REQ_GET_STATUS; - goto usb_control_msg_failed; - } - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00, 0x24, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = USB_REQ_GET_STATUS; - goto usb_control_msg_failed; - } - - radio->stereo = !((radio->transfer_buffer)[0] & 0x01); - return (radio->transfer_buffer)[0]; + 0x00, 0x00, radio->transfer_buffer, 8, 300); -usb_control_msg_failed: - radio->stereo = -1; + if (retval >= 0) + return 0; dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", - __func__, retval, request); + __func__, retval, DSB100_ONOFF); return retval; + } /* return the device status. This is, in effect, just whether it sees a stereo signal or not. Pity. */ static void dsbr100_getstat(struct dsbr100_device *radio) { - int retval; - - retval = usb_control_msg(radio->usbdev, + int retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), USB_REQ_GET_STATUS, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00 , 0x24, radio->transfer_buffer, 8, 300); + 0x00, 0x24, radio->transfer_buffer, 8, 300); if (retval < 0) { - radio->stereo = -1; + radio->stereo = false; dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", __func__, retval, USB_REQ_GET_STATUS); @@ -332,7 +181,8 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(v->driver, "dsbr100", sizeof(v->driver)); strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER; + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -349,13 +199,11 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->type = V4L2_TUNER_RADIO; v->rangelow = FREQ_MIN * FREQ_MUL; v->rangehigh = FREQ_MAX * FREQ_MUL; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if(radio->stereo) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xffff; /* We can't get the signal strength */ + v->rxsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : + V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + v->audmode = V4L2_TUNER_MODE_STEREO; + v->signal = radio->stereo ? 0xffff : 0; /* We can't get the signal strength */ return 0; } @@ -369,14 +217,12 @@ static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { struct dsbr100_device *radio = video_drvdata(file); - int retval; - radio->curfreq = f->frequency; + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + return -EINVAL; - retval = dsbr100_setfreq(radio); - if (retval < 0) - dev_warn(&radio->usbdev->dev, "Set frequency failed\n"); - return 0; + return dsbr100_setfreq(radio, clamp_t(unsigned, f->frequency, + FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); } static int vidioc_g_frequency(struct file *file, void *priv, @@ -384,90 +230,26 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct dsbr100_device *radio = video_drvdata(file); + if (f->tuner) + return -EINVAL; f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int usb_dsbr100_s_ctrl(struct v4l2_ctrl *ctrl) { - struct dsbr100_device *radio = video_drvdata(file); + struct dsbr100_device *radio = + container_of(ctrl->handler, struct dsbr100_device, hdl); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - ctrl->value = radio->status; - return 0; + radio->muted = ctrl->val; + return radio->muted ? dsbr100_stop(radio) : dsbr100_start(radio); } return -EINVAL; } -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct dsbr100_device *radio = video_drvdata(file); - int retval; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - retval = dsbr100_stop(radio); - if (retval < 0) { - dev_warn(&radio->usbdev->dev, - "Radio did not respond properly\n"); - return -EBUSY; - } - } else { - retval = dsbr100_start(radio); - if (retval < 0) { - dev_warn(&radio->usbdev->dev, - "Radio did not respond properly\n"); - return -EBUSY; - } - } - return 0; - } - return -EINVAL; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - if (a->index > 1) - return -EINVAL; - - strcpy(a->name, "Radio"); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} /* USB subsystem interface begins here */ @@ -482,6 +264,16 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) struct dsbr100_device *radio = usb_get_intfdata(intf); mutex_lock(&radio->v4l2_lock); + /* + * Disconnect is also called on unload, and in that case we need to + * mute the device. This call will silently fail if it is called + * after a physical disconnect. + */ + usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x00, radio->transfer_buffer, 8, 300); usb_set_intfdata(intf, NULL); video_unregister_device(&radio->videodev); v4l2_device_disconnect(&radio->v4l2_dev); @@ -494,25 +286,13 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) { struct dsbr100_device *radio = usb_get_intfdata(intf); - int retval; mutex_lock(&radio->v4l2_lock); - if (radio->status == STARTED) { - retval = dsbr100_stop(radio); - if (retval < 0) - dev_warn(&intf->dev, "dsbr100_stop failed\n"); - - /* After dsbr100_stop() status set to STOPPED. - * If we want driver to start radio on resume - * we set status equal to STARTED. - * On resume we will check status and run radio if needed. - */ - radio->status = STARTED; - } + if (!radio->muted && dsbr100_stop(radio) < 0) + dev_warn(&intf->dev, "dsbr100_stop failed\n"); mutex_unlock(&radio->v4l2_lock); dev_info(&intf->dev, "going into suspend..\n"); - return 0; } @@ -520,18 +300,13 @@ static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) static int usb_dsbr100_resume(struct usb_interface *intf) { struct dsbr100_device *radio = usb_get_intfdata(intf); - int retval; mutex_lock(&radio->v4l2_lock); - if (radio->status == STARTED) { - retval = dsbr100_start(radio); - if (retval < 0) - dev_warn(&intf->dev, "dsbr100_start failed\n"); - } + if (!radio->muted && dsbr100_start(radio) < 0) + dev_warn(&intf->dev, "dsbr100_start failed\n"); mutex_unlock(&radio->v4l2_lock); dev_info(&intf->dev, "coming out of suspend..\n"); - return 0; } @@ -540,15 +315,23 @@ static void usb_dsbr100_release(struct v4l2_device *v4l2_dev) { struct dsbr100_device *radio = v4l2_dev_to_radio(v4l2_dev); + v4l2_ctrl_handler_free(&radio->hdl); v4l2_device_unregister(&radio->v4l2_dev); kfree(radio->transfer_buffer); kfree(radio); } +static const struct v4l2_ctrl_ops usb_dsbr100_ctrl_ops = { + .s_ctrl = usb_dsbr100_s_ctrl, +}; + /* File system interface */ static const struct v4l2_file_operations usb_dsbr100_fops = { .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, }; static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { @@ -557,13 +340,9 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* check if the device is present and register with v4l and usb if it is */ @@ -592,11 +371,17 @@ static int usb_dsbr100_probe(struct usb_interface *intf, retval = v4l2_device_register(&intf->dev, v4l2_dev); if (retval < 0) { v4l2_err(v4l2_dev, "couldn't register v4l2_device\n"); - kfree(radio->transfer_buffer); - kfree(radio); - return retval; + goto err_reg_dev; } + v4l2_ctrl_handler_init(&radio->hdl, 1); + v4l2_ctrl_new_std(&radio->hdl, &usb_dsbr100_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + if (radio->hdl.error) { + retval = radio->hdl.error; + v4l2_err(v4l2_dev, "couldn't register control\n"); + goto err_reg_ctrl; + } mutex_init(&radio->v4l2_lock); strlcpy(radio->videodev.name, v4l2_dev->name, sizeof(radio->videodev.name)); radio->videodev.v4l2_dev = v4l2_dev; @@ -604,28 +389,46 @@ static int usb_dsbr100_probe(struct usb_interface *intf, radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops; radio->videodev.release = video_device_release_empty; radio->videodev.lock = &radio->v4l2_lock; + radio->videodev.ctrl_handler = &radio->hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); radio->usbdev = interface_to_usbdev(intf); radio->curfreq = FREQ_MIN * FREQ_MUL; - radio->status = STOPPED; + radio->muted = true; video_set_drvdata(&radio->videodev, radio); + usb_set_intfdata(intf, radio); retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); - if (retval < 0) { - v4l2_err(v4l2_dev, "couldn't register video device\n"); - v4l2_device_unregister(v4l2_dev); - kfree(radio->transfer_buffer); - kfree(radio); - return -EIO; - } - usb_set_intfdata(intf, radio); - return 0; + if (retval == 0) + return 0; + v4l2_err(v4l2_dev, "couldn't register video device\n"); + +err_reg_ctrl: + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(v4l2_dev); +err_reg_dev: + kfree(radio->transfer_buffer); + kfree(radio); + return retval; } -module_usb_driver(usb_dsbr100_driver); +static struct usb_device_id usb_dsbr100_device_table[] = { + { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, + { } /* Terminating entry */ +}; -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); +MODULE_DEVICE_TABLE(usb, usb_dsbr100_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_dsbr100_driver = { + .name = "dsbr100", + .probe = usb_dsbr100_probe, + .disconnect = usb_dsbr100_disconnect, + .id_table = usb_dsbr100_device_table, + .suspend = usb_dsbr100_suspend, + .resume = usb_dsbr100_resume, + .reset_resume = usb_dsbr100_resume, +}; + +module_usb_driver(usb_dsbr100_driver); -- cgit v1.2.3 From 37481fcacbae3de4972dae265dab7a58f2c9cf8b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Apr 2012 09:51:31 -0300 Subject: [media] ivtv: set max/step to 0 for PTS and FRAME controls When creating 64 bit integer controls make sure the max and step values are 0 (as 64 bit controls do not have ranges). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ivtv/ivtv-driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 679262ed13bc..057929e165ab 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -1201,9 +1201,9 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler; itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, - V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 1, 0); + V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 0, 0); itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, - V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0); + V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0, 0, 0); /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported, mask that menu item. */ itv->ctrl_audio_playback = -- cgit v1.2.3 From ff27cda340450b4d5267a79109204e853f95cb4c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 18 Apr 2012 06:47:02 -0300 Subject: [media] radio-keene: support suspend/resume Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-keene.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index 5f33047d0d43..62d32c4946f7 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -156,6 +156,23 @@ static void usb_keene_disconnect(struct usb_interface *intf) v4l2_device_put(&radio->v4l2_dev); } +static int usb_keene_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf)); + + return keene_cmd_main(radio, 0, false); +} + +static int usb_keene_resume(struct usb_interface *intf) +{ + struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf)); + + mdelay(50); + keene_cmd_set(radio); + keene_cmd_main(radio, radio->curfreq, true); + return 0; +} + static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { @@ -402,6 +419,9 @@ static struct usb_driver usb_keene_driver = { .probe = usb_keene_probe, .disconnect = usb_keene_disconnect, .id_table = usb_keene_device_table, + .suspend = usb_keene_suspend, + .resume = usb_keene_resume, + .reset_resume = usb_keene_resume, }; static int __init keene_init(void) -- cgit v1.2.3 From f61861fa567928614c6dd01560b320bfeb24e3e2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 28 Apr 2012 08:09:48 -0300 Subject: [media] radio-isa: fix memory leak If there is an error when creating controls the v4l2_ctrl_handler_free function must be called. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-isa.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c index ed9039f8571c..c7450fb90d2b 100644 --- a/drivers/media/radio/radio-isa.c +++ b/drivers/media/radio/radio-isa.c @@ -274,23 +274,21 @@ int radio_isa_common_probe(struct radio_isa_card *isa, struct device *pdev, res = ops->s_stereo(isa, isa->stereo); if (res < 0) { v4l2_err(v4l2_dev, "Could not setup card\n"); - goto err_node_reg; + goto err_hdl; } res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, radio_nr); if (res < 0) { v4l2_err(v4l2_dev, "Could not register device node\n"); - goto err_node_reg; + goto err_hdl; } v4l2_info(v4l2_dev, "Initialized radio card %s on port 0x%03x\n", drv->card, isa->io); return 0; -err_node_reg: - v4l2_ctrl_handler_free(&isa->hdl); err_hdl: - v4l2_device_unregister(&isa->v4l2_dev); + v4l2_ctrl_handler_free(&isa->hdl); err_dev_reg: release_region(isa->io, region_size); kfree(isa); -- cgit v1.2.3 From 85578b0fdc107e0d7022d38271fdcf0567afd212 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2012 12:30:40 -0300 Subject: [media] radio-mr800: cleanup and have it comply to the V4L2 API Implement the control framework and update to the latest V4L2 framework. The v4l2-compliance tool now runs without errors. Fixed bad g/s_tuner handling with respect to mono/stereo. Support control events. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-mr800.c | 340 +++++++++++++------------------------- 1 file changed, 112 insertions(+), 228 deletions(-) diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index a860a72a58ec..0a96edae6786 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -62,6 +62,8 @@ #include #include #include +#include +#include #include #include @@ -117,29 +119,20 @@ static int radio_nr = -1; module_param(radio_nr, int, 0); MODULE_PARM_DESC(radio_nr, "Radio Nr"); -static int usb_amradio_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void usb_amradio_disconnect(struct usb_interface *intf); -static int usb_amradio_open(struct file *file); -static int usb_amradio_close(struct file *file); -static int usb_amradio_suspend(struct usb_interface *intf, - pm_message_t message); -static int usb_amradio_resume(struct usb_interface *intf); - /* Data for one (physical) device */ struct amradio_device { /* reference to USB and video device */ struct usb_device *usbdev; struct usb_interface *intf; - struct video_device videodev; + struct video_device vdev; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; unsigned char *buffer; struct mutex lock; /* buffer locking */ int curfreq; int stereo; int muted; - int initialized; }; static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev) @@ -147,27 +140,6 @@ static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev return container_of(v4l2_dev, struct amradio_device, v4l2_dev); } -/* USB Device ID List */ -static struct usb_device_id usb_amradio_device_table[] = { - {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT, - USB_CLASS_HID, 0, 0) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, usb_amradio_device_table); - -/* USB subsystem interface */ -static struct usb_driver usb_amradio_driver = { - .name = MR800_DRIVER_NAME, - .probe = usb_amradio_probe, - .disconnect = usb_amradio_disconnect, - .suspend = usb_amradio_suspend, - .resume = usb_amradio_resume, - .reset_resume = usb_amradio_resume, - .id_table = usb_amradio_device_table, - .supports_autosuspend = 1, -}; - /* switch on/off the radio. Send 8 bytes to device */ static int amradio_set_mute(struct amradio_device *radio, char argument) { @@ -187,21 +159,19 @@ static int amradio_set_mute(struct amradio_device *radio, char argument) (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval < 0 || size != BUFFER_LENGTH) { - amradio_dev_warn(&radio->videodev.dev, "set mute failed\n"); - return retval; + amradio_dev_warn(&radio->vdev.dev, "set mute failed\n"); + return retval < 0 ? retval : -EIO; } - radio->muted = argument; - - return retval; + return 0; } /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ static int amradio_setfreq(struct amradio_device *radio, int freq) { + unsigned short freq_send = 0x10 + (freq >> 3) / 25; int retval; int size; - unsigned short freq_send = 0x10 + (freq >> 3) / 25; radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; @@ -216,7 +186,7 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval < 0 || size != BUFFER_LENGTH) - goto out_err; + goto out; /* frequency is calculated from freq_send and placed in first 2 bytes */ radio->buffer[0] = (freq_send >> 8) & 0xff; @@ -230,16 +200,14 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); - if (retval < 0 || size != BUFFER_LENGTH) - goto out_err; - - radio->curfreq = freq; - goto out; + if (retval >= 0 && size == BUFFER_LENGTH) { + radio->curfreq = freq; + return 0; + } -out_err: - amradio_dev_warn(&radio->videodev.dev, "set frequency failed\n"); out: - return retval; + amradio_dev_warn(&radio->vdev.dev, "set frequency failed\n"); + return retval < 0 ? retval : -EIO; } static int amradio_set_stereo(struct amradio_device *radio, char argument) @@ -260,16 +228,12 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument) (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval < 0 || size != BUFFER_LENGTH) { - amradio_dev_warn(&radio->videodev.dev, "set stereo failed\n"); - return retval; + amradio_dev_warn(&radio->vdev.dev, "set stereo failed\n"); + return retval < 0 ? retval : -EIO; } - if (argument == WANT_STEREO) - radio->stereo = 1; - else - radio->stereo = 0; - - return retval; + radio->stereo = (argument == WANT_STEREO); + return 0; } /* Handle unplugging the device. @@ -282,25 +246,24 @@ static void usb_amradio_disconnect(struct usb_interface *intf) struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); - /* increase the device node's refcount */ - get_device(&radio->videodev.dev); + usb_set_intfdata(intf, NULL); + video_unregister_device(&radio->vdev); v4l2_device_disconnect(&radio->v4l2_dev); - video_unregister_device(&radio->videodev); mutex_unlock(&radio->lock); - /* decrease the device node's refcount, allowing it to be released */ - put_device(&radio->videodev.dev); + v4l2_device_put(&radio->v4l2_dev); } /* vidioc_querycap - query device capabilities */ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { - struct amradio_device *radio = file->private_data; + struct amradio_device *radio = video_drvdata(file); strlcpy(v->driver, "radio-mr800", sizeof(v->driver)); strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER; + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -308,44 +271,31 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct amradio_device *radio = file->private_data; - int retval; + struct amradio_device *radio = video_drvdata(file); if (v->index > 0) return -EINVAL; -/* TODO: Add function which look is signal stereo or not - * amradio_getstat(radio); - */ - -/* we call amradio_set_stereo to set radio->stereo - * Honestly, amradio_getstat should cover this in future and - * amradio_set_stereo shouldn't be here - */ - retval = amradio_set_stereo(radio, WANT_STEREO); - strcpy(v->name, "FM"); v->type = V4L2_TUNER_RADIO; v->rangelow = FREQ_MIN * FREQ_MUL; v->rangehigh = FREQ_MAX * FREQ_MUL; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if (radio->stereo) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xffff; /* Can't get the signal strength, sad.. */ - v->afc = 0; /* Don't know what is this */ - - return retval; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + /* We do not know how to get hold of the stereo indicator, so + all we can do is give back both mono and stereo, which + effectively means that we don't know. */ + v->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + v->signal = 0xffff; + v->audmode = radio->stereo ? + V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + return 0; } /* vidioc_s_tuner - set tuner attributes */ static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct amradio_device *radio = file->private_data; - int retval = -EINVAL; + struct amradio_device *radio = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -353,34 +303,31 @@ static int vidioc_s_tuner(struct file *file, void *priv, /* mono/stereo selector */ switch (v->audmode) { case V4L2_TUNER_MODE_MONO: - retval = amradio_set_stereo(radio, WANT_MONO); - break; - case V4L2_TUNER_MODE_STEREO: - retval = amradio_set_stereo(radio, WANT_STEREO); - break; + return amradio_set_stereo(radio, WANT_MONO); + default: + return amradio_set_stereo(radio, WANT_STEREO); } - - return retval; } /* vidioc_s_frequency - set tuner radio frequency */ static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct amradio_device *radio = file->private_data; + struct amradio_device *radio = video_drvdata(file); - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + if (f->tuner != 0) return -EINVAL; - return amradio_setfreq(radio, f->frequency); + return amradio_setfreq(radio, clamp_t(unsigned, f->frequency, + FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); } /* vidioc_g_frequency - get tuner radio frequency */ static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct amradio_device *radio = file->private_data; + struct amradio_device *radio = video_drvdata(file); - if (f->tuner != 0) + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; @@ -388,89 +335,20 @@ static int vidioc_g_frequency(struct file *file, void *priv, return 0; } -/* vidioc_queryctrl - enumerate control items */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int usb_amradio_s_ctrl(struct v4l2_ctrl *ctrl) { - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - - return -EINVAL; -} - -/* vidioc_g_ctrl - get the value of a control */ -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct amradio_device *radio = file->private_data; + struct amradio_device *radio = + container_of(ctrl->handler, struct amradio_device, hdl); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - ctrl->value = radio->muted; - return 0; + return amradio_set_mute(radio, + ctrl->val ? AMRADIO_STOP : AMRADIO_START); } return -EINVAL; } -/* vidioc_s_ctrl - set the value of a control */ -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct amradio_device *radio = file->private_data; - int retval = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - retval = amradio_set_mute(radio, AMRADIO_STOP); - else - retval = amradio_set_mute(radio, AMRADIO_START); - - break; - } - - return retval; -} - -/* vidioc_g_audio - get audio attributes */ -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - if (a->index > 1) - return -EINVAL; - - strcpy(a->name, "Radio"); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -/* vidioc_s_audio - set audio attributes */ -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - if (a->index != 0) - return -EINVAL; - return 0; -} - -/* vidioc_g_input - get input */ -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -/* vidioc_s_input - set input */ -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - if (i != 0) - return -EINVAL; - return 0; -} - static int usb_amradio_init(struct amradio_device *radio) { int retval; @@ -483,51 +361,21 @@ static int usb_amradio_init(struct amradio_device *radio) if (retval) goto out_err; - radio->initialized = 1; goto out; out_err: - amradio_dev_err(&radio->videodev.dev, "initialization failed\n"); + amradio_dev_err(&radio->vdev.dev, "initialization failed\n"); out: return retval; } -/* open device - amradio_start() and amradio_setfreq() */ -static int usb_amradio_open(struct file *file) -{ - struct amradio_device *radio = video_drvdata(file); - int retval; - - file->private_data = radio; - retval = usb_autopm_get_interface(radio->intf); - if (retval) - return retval; - - if (unlikely(!radio->initialized)) { - retval = usb_amradio_init(radio); - if (retval) - usb_autopm_put_interface(radio->intf); - } - return retval; -} - -/*close device */ -static int usb_amradio_close(struct file *file) -{ - struct amradio_device *radio = file->private_data; - - if (video_is_registered(&radio->videodev)) - usb_autopm_put_interface(radio->intf); - return 0; -} - /* Suspend device - stop device. Need to be checked and fixed */ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) { struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); - if (!radio->muted && radio->initialized) { + if (!radio->muted) { amradio_set_mute(radio, AMRADIO_STOP); radio->muted = 0; } @@ -543,9 +391,6 @@ static int usb_amradio_resume(struct usb_interface *intf) struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); - if (unlikely(!radio->initialized)) - goto unlock; - if (radio->stereo) amradio_set_stereo(radio, WANT_STEREO); else @@ -556,18 +401,22 @@ static int usb_amradio_resume(struct usb_interface *intf) if (!radio->muted) amradio_set_mute(radio, AMRADIO_START); -unlock: mutex_unlock(&radio->lock); dev_info(&intf->dev, "coming out of suspend..\n"); return 0; } +static const struct v4l2_ctrl_ops usb_amradio_ctrl_ops = { + .s_ctrl = usb_amradio_s_ctrl, +}; + /* File system interface */ static const struct v4l2_file_operations usb_amradio_fops = { .owner = THIS_MODULE, - .open = usb_amradio_open, - .release = usb_amradio_close, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, }; @@ -577,20 +426,18 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -static void usb_amradio_video_device_release(struct video_device *videodev) +static void usb_amradio_release(struct v4l2_device *v4l2_dev) { - struct amradio_device *radio = video_get_drvdata(videodev); + struct amradio_device *radio = to_amradio_dev(v4l2_dev); /* free rest memory */ + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(&radio->v4l2_dev); kfree(radio->buffer); kfree(radio); } @@ -624,23 +471,38 @@ static int usb_amradio_probe(struct usb_interface *intf, goto err_v4l2; } + v4l2_ctrl_handler_init(&radio->hdl, 1); + v4l2_ctrl_new_std(&radio->hdl, &usb_amradio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + if (radio->hdl.error) { + retval = radio->hdl.error; + dev_err(&intf->dev, "couldn't register control\n"); + goto err_ctrl; + } mutex_init(&radio->lock); - strlcpy(radio->videodev.name, radio->v4l2_dev.name, - sizeof(radio->videodev.name)); - radio->videodev.v4l2_dev = &radio->v4l2_dev; - radio->videodev.fops = &usb_amradio_fops; - radio->videodev.ioctl_ops = &usb_amradio_ioctl_ops; - radio->videodev.release = usb_amradio_video_device_release; - radio->videodev.lock = &radio->lock; + radio->v4l2_dev.ctrl_handler = &radio->hdl; + radio->v4l2_dev.release = usb_amradio_release; + strlcpy(radio->vdev.name, radio->v4l2_dev.name, + sizeof(radio->vdev.name)); + radio->vdev.v4l2_dev = &radio->v4l2_dev; + radio->vdev.fops = &usb_amradio_fops; + radio->vdev.ioctl_ops = &usb_amradio_ioctl_ops; + radio->vdev.release = video_device_release_empty; + radio->vdev.lock = &radio->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; + usb_set_intfdata(intf, &radio->v4l2_dev); radio->curfreq = 95.16 * FREQ_MUL; - video_set_drvdata(&radio->videodev, radio); + video_set_drvdata(&radio->vdev, radio); + retval = usb_amradio_init(radio); + if (retval) + goto err_vdev; - retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, + retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, radio_nr); if (retval < 0) { dev_err(&intf->dev, "could not register video device\n"); @@ -650,6 +512,8 @@ static int usb_amradio_probe(struct usb_interface *intf, return 0; err_vdev: + v4l2_ctrl_handler_free(&radio->hdl); +err_ctrl: v4l2_device_unregister(&radio->v4l2_dev); err_v4l2: kfree(radio->buffer); @@ -659,4 +523,24 @@ err: return retval; } +/* USB Device ID List */ +static struct usb_device_id usb_amradio_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT, + USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_amradio_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_amradio_driver = { + .name = MR800_DRIVER_NAME, + .probe = usb_amradio_probe, + .disconnect = usb_amradio_disconnect, + .suspend = usb_amradio_suspend, + .resume = usb_amradio_resume, + .reset_resume = usb_amradio_resume, + .id_table = usb_amradio_device_table, +}; + module_usb_driver(usb_amradio_driver); -- cgit v1.2.3 From 77cf393434898d1299c77c32bdd0629b2b4df170 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2012 13:28:34 -0300 Subject: [media] radio-mr800: add support for stereo and signal detection Thanks to an older driver by Faidon Liambotis (as noted in the radio-mr800 comment block at the start) for figuring out how to get the signal/stereo state. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-mr800.c | 43 ++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 0a96edae6786..0b39a8cfee88 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -103,6 +103,7 @@ devices, that would be 76 and 91. */ * List isn't full and will be updated with implementation of new functions */ #define AMRADIO_SET_FREQ 0xa4 +#define AMRADIO_GET_SIGNAL 0xa7 #define AMRADIO_SET_MUTE 0xab #define AMRADIO_SET_MONO 0xae @@ -236,6 +237,35 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument) return 0; } +static int amradio_get_stat(struct amradio_device *radio, bool *is_stereo, u32 *signal) +{ + int retval; + int size; + + radio->buffer[0] = 0x00; + radio->buffer[1] = 0x55; + radio->buffer[2] = 0xaa; + radio->buffer[3] = 0x00; + radio->buffer[4] = AMRADIO_GET_SIGNAL; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x08; + + retval = usb_bulk_msg(radio->usbdev, usb_sndbulkpipe(radio->usbdev, 0x02), + radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); + if (!retval) + retval = usb_bulk_msg(radio->usbdev, usb_rcvbulkpipe(radio->usbdev, 0x81), + radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); + + if (retval || size != BUFFER_LENGTH) { + amradio_dev_warn(&radio->vdev.dev, "get stat failed\n"); + return retval; + } + *is_stereo = radio->buffer[2] >> 7; + *signal = (radio->buffer[3] & 0xf0) << 8; + return 0; +} + /* Handle unplugging the device. * We call video_unregister_device in any case. * The last function called in this procedure is @@ -272,20 +302,23 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { struct amradio_device *radio = video_drvdata(file); + bool is_stereo = false; + int retval; if (v->index > 0) return -EINVAL; + v->signal = 0; + retval = amradio_get_stat(radio, &is_stereo, &v->signal); + if (retval) + return retval; + strcpy(v->name, "FM"); v->type = V4L2_TUNER_RADIO; v->rangelow = FREQ_MIN * FREQ_MUL; v->rangehigh = FREQ_MAX * FREQ_MUL; v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; - /* We do not know how to get hold of the stereo indicator, so - all we can do is give back both mono and stereo, which - effectively means that we don't know. */ - v->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - v->signal = 0xffff; + v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; v->audmode = radio->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; return 0; -- cgit v1.2.3 From 4bd9ff19708ce8d88926121dda98423e36191c58 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2012 18:39:37 -0300 Subject: [media] radio-mr800: add hardware seek support Added hardware seek support based on information gleaned from the GPLv2 driver available here: http://sourceforge.net/projects/av-usbradio/ Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-mr800.c | 255 +++++++++++++++++++++----------------- 1 file changed, 141 insertions(+), 114 deletions(-) diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 0b39a8cfee88..94cb6bc690f5 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -103,13 +103,17 @@ devices, that would be 76 and 91. */ * List isn't full and will be updated with implementation of new functions */ #define AMRADIO_SET_FREQ 0xa4 +#define AMRADIO_GET_READY_FLAG 0xa5 #define AMRADIO_GET_SIGNAL 0xa7 +#define AMRADIO_GET_FREQ 0xa8 +#define AMRADIO_SET_SEARCH_UP 0xa9 +#define AMRADIO_SET_SEARCH_DOWN 0xaa #define AMRADIO_SET_MUTE 0xab +#define AMRADIO_SET_RIGHT_MUTE 0xac +#define AMRADIO_SET_LEFT_MUTE 0xad #define AMRADIO_SET_MONO 0xae - -/* Comfortable defines for amradio_set_mute */ -#define AMRADIO_START 0x00 -#define AMRADIO_STOP 0x01 +#define AMRADIO_SET_SEARCH_LVL 0xb0 +#define AMRADIO_STOP_SEARCH 0xb1 /* Comfortable defines for amradio_set_stereo */ #define WANT_STEREO 0x00 @@ -129,7 +133,7 @@ struct amradio_device { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler hdl; - unsigned char *buffer; + u8 *buffer; struct mutex lock; /* buffer locking */ int curfreq; int stereo; @@ -141,8 +145,8 @@ static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev return container_of(v4l2_dev, struct amradio_device, v4l2_dev); } -/* switch on/off the radio. Send 8 bytes to device */ -static int amradio_set_mute(struct amradio_device *radio, char argument) +static int amradio_send_cmd(struct amradio_device *radio, u8 cmd, u8 arg, + u8 *extra, u8 extralen, bool reply) { int retval; int size; @@ -150,117 +154,89 @@ static int amradio_set_mute(struct amradio_device *radio, char argument) radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; radio->buffer[2] = 0xaa; - radio->buffer[3] = 0x00; - radio->buffer[4] = AMRADIO_SET_MUTE; - radio->buffer[5] = argument; + radio->buffer[3] = extralen; + radio->buffer[4] = cmd; + radio->buffer[5] = arg; radio->buffer[6] = 0x00; - radio->buffer[7] = 0x00; + radio->buffer[7] = extra || reply ? 8 : 0; retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), - (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); + radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval < 0 || size != BUFFER_LENGTH) { - amradio_dev_warn(&radio->vdev.dev, "set mute failed\n"); - return retval < 0 ? retval : -EIO; + if (video_is_registered(&radio->vdev)) + amradio_dev_warn(&radio->vdev.dev, + "cmd %02x failed\n", cmd); + return retval ? retval : -EIO; } - radio->muted = argument; - return 0; + if (!extra && !reply) + return 0; + + if (extra) { + memcpy(radio->buffer, extra, extralen); + memset(radio->buffer + extralen, 0, 8 - extralen); + retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), + radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); + } else { + memset(radio->buffer, 0, 8); + retval = usb_bulk_msg(radio->usbdev, usb_rcvbulkpipe(radio->usbdev, 0x81), + radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); + } + if (retval == 0 && size == BUFFER_LENGTH) + return 0; + if (video_is_registered(&radio->vdev) && cmd != AMRADIO_GET_READY_FLAG) + amradio_dev_warn(&radio->vdev.dev, "follow-up to cmd %02x failed\n", cmd); + return retval ? retval : -EIO; +} + +/* switch on/off the radio. Send 8 bytes to device */ +static int amradio_set_mute(struct amradio_device *radio, bool mute) +{ + int ret = amradio_send_cmd(radio, + AMRADIO_SET_MUTE, mute, NULL, 0, false); + + if (!ret) + radio->muted = mute; + return ret; } /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ -static int amradio_setfreq(struct amradio_device *radio, int freq) +static int amradio_set_freq(struct amradio_device *radio, int freq) { unsigned short freq_send = 0x10 + (freq >> 3) / 25; + u8 buf[3]; int retval; - int size; - - radio->buffer[0] = 0x00; - radio->buffer[1] = 0x55; - radio->buffer[2] = 0xaa; - radio->buffer[3] = 0x03; - radio->buffer[4] = AMRADIO_SET_FREQ; - radio->buffer[5] = 0x00; - radio->buffer[6] = 0x00; - radio->buffer[7] = 0x08; - - retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), - (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); - - if (retval < 0 || size != BUFFER_LENGTH) - goto out; /* frequency is calculated from freq_send and placed in first 2 bytes */ - radio->buffer[0] = (freq_send >> 8) & 0xff; - radio->buffer[1] = freq_send & 0xff; - radio->buffer[2] = 0x01; - radio->buffer[3] = 0x00; - radio->buffer[4] = 0x00; - /* 5 and 6 bytes of buffer already = 0x00 */ - radio->buffer[7] = 0x00; - - retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), - (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); + buf[0] = (freq_send >> 8) & 0xff; + buf[1] = freq_send & 0xff; + buf[2] = 0x01; - if (retval >= 0 && size == BUFFER_LENGTH) { - radio->curfreq = freq; - return 0; - } - -out: - amradio_dev_warn(&radio->vdev.dev, "set frequency failed\n"); - return retval < 0 ? retval : -EIO; + retval = amradio_send_cmd(radio, AMRADIO_SET_FREQ, 0, buf, 3, false); + if (retval) + return retval; + radio->curfreq = freq; + msleep(40); + return 0; } -static int amradio_set_stereo(struct amradio_device *radio, char argument) +static int amradio_set_stereo(struct amradio_device *radio, bool stereo) { - int retval; - int size; - - radio->buffer[0] = 0x00; - radio->buffer[1] = 0x55; - radio->buffer[2] = 0xaa; - radio->buffer[3] = 0x00; - radio->buffer[4] = AMRADIO_SET_MONO; - radio->buffer[5] = argument; - radio->buffer[6] = 0x00; - radio->buffer[7] = 0x00; + int ret = amradio_send_cmd(radio, + AMRADIO_SET_MONO, !stereo, NULL, 0, false); - retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), - (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); - - if (retval < 0 || size != BUFFER_LENGTH) { - amradio_dev_warn(&radio->vdev.dev, "set stereo failed\n"); - return retval < 0 ? retval : -EIO; - } - - radio->stereo = (argument == WANT_STEREO); - return 0; + if (!ret) + radio->stereo = stereo; + return ret; } static int amradio_get_stat(struct amradio_device *radio, bool *is_stereo, u32 *signal) { - int retval; - int size; - - radio->buffer[0] = 0x00; - radio->buffer[1] = 0x55; - radio->buffer[2] = 0xaa; - radio->buffer[3] = 0x00; - radio->buffer[4] = AMRADIO_GET_SIGNAL; - radio->buffer[5] = 0x00; - radio->buffer[6] = 0x00; - radio->buffer[7] = 0x08; - - retval = usb_bulk_msg(radio->usbdev, usb_sndbulkpipe(radio->usbdev, 0x02), - radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); - if (!retval) - retval = usb_bulk_msg(radio->usbdev, usb_rcvbulkpipe(radio->usbdev, 0x81), - radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); + int ret = amradio_send_cmd(radio, + AMRADIO_GET_SIGNAL, 0, NULL, 0, true); - if (retval || size != BUFFER_LENGTH) { - amradio_dev_warn(&radio->vdev.dev, "get stat failed\n"); - return retval; - } + if (ret) + return ret; *is_stereo = radio->buffer[2] >> 7; *signal = (radio->buffer[3] & 0xf0) << 8; return 0; @@ -276,8 +252,9 @@ static void usb_amradio_disconnect(struct usb_interface *intf) struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); - usb_set_intfdata(intf, NULL); video_unregister_device(&radio->vdev); + amradio_set_mute(radio, true); + usb_set_intfdata(intf, NULL); v4l2_device_disconnect(&radio->v4l2_dev); mutex_unlock(&radio->lock); v4l2_device_put(&radio->v4l2_dev); @@ -292,7 +269,8 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(v->driver, "radio-mr800", sizeof(v->driver)); strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER | + V4L2_CAP_HW_FREQ_SEEK; v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -350,7 +328,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (f->tuner != 0) return -EINVAL; - return amradio_setfreq(radio, clamp_t(unsigned, f->frequency, + return amradio_set_freq(radio, clamp_t(unsigned, f->frequency, FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); } @@ -368,6 +346,59 @@ static int vidioc_g_frequency(struct file *file, void *priv, return 0; } +static int vidioc_s_hw_freq_seek(struct file *file, void *priv, + struct v4l2_hw_freq_seek *seek) +{ + static u8 buf[8] = { + 0x3d, 0x32, 0x0f, 0x08, 0x3d, 0x32, 0x0f, 0x08 + }; + struct amradio_device *radio = video_drvdata(file); + unsigned long timeout; + int retval; + + if (seek->tuner != 0 || !seek->wrap_around) + return -EINVAL; + + retval = amradio_send_cmd(radio, + AMRADIO_SET_SEARCH_LVL, 0, buf, 8, false); + if (retval) + return retval; + amradio_set_freq(radio, radio->curfreq); + retval = amradio_send_cmd(radio, + seek->seek_upward ? AMRADIO_SET_SEARCH_UP : AMRADIO_SET_SEARCH_DOWN, + 0, NULL, 0, false); + if (retval) + return retval; + timeout = jiffies + msecs_to_jiffies(30000); + for (;;) { + if (time_after(jiffies, timeout)) { + retval = -EAGAIN; + break; + } + if (schedule_timeout_interruptible(msecs_to_jiffies(10))) { + retval = -ERESTARTSYS; + break; + } + retval = amradio_send_cmd(radio, AMRADIO_GET_READY_FLAG, + 0, NULL, 0, true); + if (retval) + continue; + amradio_send_cmd(radio, AMRADIO_GET_FREQ, 0, NULL, 0, true); + if (radio->buffer[1] || radio->buffer[2]) { + radio->curfreq = (radio->buffer[1] << 8) | radio->buffer[2]; + radio->curfreq = (radio->curfreq - 0x10) * 200; + amradio_send_cmd(radio, AMRADIO_STOP_SEARCH, + 0, NULL, 0, false); + amradio_set_freq(radio, radio->curfreq); + retval = 0; + break; + } + } + amradio_send_cmd(radio, AMRADIO_STOP_SEARCH, 0, NULL, 0, false); + amradio_set_freq(radio, radio->curfreq); + return retval; +} + static int usb_amradio_s_ctrl(struct v4l2_ctrl *ctrl) { struct amradio_device *radio = @@ -375,8 +406,7 @@ static int usb_amradio_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - return amradio_set_mute(radio, - ctrl->val ? AMRADIO_STOP : AMRADIO_START); + return amradio_set_mute(radio, ctrl->val); } return -EINVAL; @@ -386,19 +416,19 @@ static int usb_amradio_init(struct amradio_device *radio) { int retval; - retval = amradio_set_mute(radio, AMRADIO_STOP); + retval = amradio_set_mute(radio, true); if (retval) goto out_err; - - retval = amradio_set_stereo(radio, WANT_STEREO); + retval = amradio_set_stereo(radio, true); if (retval) goto out_err; - - goto out; + retval = amradio_set_freq(radio, radio->curfreq); + if (retval) + goto out_err; + return 0; out_err: amradio_dev_err(&radio->vdev.dev, "initialization failed\n"); -out: return retval; } @@ -409,8 +439,8 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) mutex_lock(&radio->lock); if (!radio->muted) { - amradio_set_mute(radio, AMRADIO_STOP); - radio->muted = 0; + amradio_set_mute(radio, true); + radio->muted = false; } mutex_unlock(&radio->lock); @@ -424,15 +454,11 @@ static int usb_amradio_resume(struct usb_interface *intf) struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); - if (radio->stereo) - amradio_set_stereo(radio, WANT_STEREO); - else - amradio_set_stereo(radio, WANT_MONO); - - amradio_setfreq(radio, radio->curfreq); + amradio_set_stereo(radio, radio->stereo); + amradio_set_freq(radio, radio->curfreq); if (!radio->muted) - amradio_set_mute(radio, AMRADIO_START); + amradio_set_mute(radio, false); mutex_unlock(&radio->lock); @@ -459,6 +485,7 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -- cgit v1.2.3 From 4fd466a13008823648cf2ec6f06a03d90e6412ab Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 11:04:30 -0300 Subject: [media] stk-webcam: Don't flip the image by default Prior to this patch the stk-webcam driver was enabling the vflip and mirror bits in the sensor by default. Which only is the right thing to do if the sensor is actually mounted upside down, which it usually is not. Actually we've received upside down reports for both usb-ids which this driver supports, one for an "ASUSTeK Computer Inc." "A3H" laptop with a build in 174f:a311 webcam, and one for an "To Be Filled By O.E.M." "Z96FM" laptop with a build in 05e1:0501 webcam. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/stk-webcam.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index d427f8436c70..86a0fc56c330 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -38,13 +38,13 @@ #include "stk-webcam.h" -static bool hflip = 1; +static bool hflip; module_param(hflip, bool, 0444); -MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 1"); +MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); -static bool vflip = 1; +static bool vflip; module_param(vflip, bool, 0444); -MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 1"); +MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); static int debug; module_param(debug, int, 0444); -- cgit v1.2.3 From a648e310df9ff088d949befc8d1306264d970826 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 27 Apr 2012 11:32:24 -0300 Subject: [media] gspca/autogain_functions.h: Allow users to declare what they want Allow users of gspca/autogain_functions.h to declare which of the autogain algoritms they are going to use. This allows us to remove the hacks from drivers which don't use coarse_grained_expo_autogain. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/autogain_functions.h | 6 +++++- drivers/media/video/gspca/nw80x.c | 2 ++ drivers/media/video/gspca/pac7302.c | 4 +--- drivers/media/video/gspca/sonixb.c | 2 ++ drivers/media/video/gspca/sonixj.c | 5 +---- drivers/media/video/gspca/topro.c | 6 ++---- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/gspca/autogain_functions.h b/drivers/media/video/gspca/autogain_functions.h index 46777eee678b..d625eafe63eb 100644 --- a/drivers/media/video/gspca/autogain_functions.h +++ b/drivers/media/video/gspca/autogain_functions.h @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef WANT_REGULAR_AUTOGAIN /* auto gain and exposure algorithm based on the knee algorithm described here: http://ytse.tricolour.net/docs/LowLightOptimization.html @@ -91,7 +92,9 @@ static inline int auto_gain_n_exposure( gain, exposure); return retval; } +#endif +#ifdef WANT_COARSE_EXPO_AUTOGAIN /* Autogain + exposure algorithm for cameras with a coarse exposure control (usually this means we can only control the clockdiv to change exposure) As changing the clockdiv so that the fps drops from 30 to 15 fps for @@ -103,7 +106,7 @@ static inline int auto_gain_n_exposure( which leads to oscilating as one exposure step is huge. Note this assumes that the sd struct for the cam in question has - exp_too_high_cnt and exp_too_high_cnt int members for use by this function. + exp_too_low_cnt and exp_too_high_cnt int members for use by this function. Returns 0 if no changes were made, 1 if the gain and or exposure settings where changed. */ @@ -177,3 +180,4 @@ static inline int coarse_grained_expo_autogain( gain, exposure); return retval; } +#endif diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index 7167cac7359c..42e021931e60 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -2001,6 +2001,8 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) return gspca_dev->usb_err; } +#define WANT_REGULAR_AUTOGAIN +#define WANT_COARSE_EXPO_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 30662fccb0cf..856cdd987f42 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -729,9 +729,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x40); } -/* !! coarse_grained_expo_autogain is not used !! */ -#define exp_too_low_cnt flags -#define exp_too_high_cnt sof_read +#define WANT_REGULAR_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 6a1148d7fe92..e2bdf8f632f4 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1000,6 +1000,8 @@ static void setfreq(struct gspca_dev *gspca_dev) } } +#define WANT_REGULAR_AUTOGAIN +#define WANT_COARSE_EXPO_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index db8e5084df06..05c6d0c004d4 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -2800,10 +2800,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } } -/* !! coarse_grained_expo_autogain is not used !! */ -#define exp_too_low_cnt bridge -#define exp_too_high_cnt sensor - +#define WANT_REGULAR_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c index 444d3c5b9079..c6326d177a3d 100644 --- a/drivers/media/video/gspca/topro.c +++ b/drivers/media/video/gspca/topro.c @@ -4675,11 +4675,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* -- do autogain -- */ /* gain setting is done in setexposure() for tp6810 */ static void setgain(struct gspca_dev *gspca_dev) {} -/* !! coarse_grained_expo_autogain is not used !! */ -#define exp_too_low_cnt bridge -#define exp_too_high_cnt sensor - +#define WANT_REGULAR_AUTOGAIN #include "autogain_functions.h" + static void sd_dq_callback(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; -- cgit v1.2.3 From b053c1d0a4430cda924d3aae4ed9e9e7f9655319 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 25 Apr 2012 12:00:49 -0300 Subject: [media] gspca_pac73xx: Remove comments from before the 7302 / 7311 separation The pac7302 and pac7311 driver still contains some comments from before they were separated, such as marking certain functions 7302 or 7311 only, with the new split drivers these make no sense, remove them. Also removed the empty/unused sd_stop0 function from pac7311.c Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 6 ------ drivers/media/video/gspca/pac7311.c | 40 ++++++++++--------------------------- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 856cdd987f42..4f7be66fce45 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -89,7 +89,6 @@ enum e_ctrl { NCTRLS /* number of controls */ }; -/* specific webcam descriptor for pac7302 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -270,7 +269,6 @@ static const struct v4l2_pix_format vga_mode[] = { #define LOAD_PAGE3 255 #define END_OF_SEQUENCE 0 -/* pac 7302 */ static const u8 init_7302[] = { /* index,value */ 0xff, 0x01, /* page 1 */ @@ -509,7 +507,6 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* This function is used by pac7302 only */ static void setbrightcont(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -536,7 +533,6 @@ static void setbrightcont(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0xdc, 0x01); } -/* This function is used by pac7302 only */ static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -700,8 +696,6 @@ static int sd_start(struct gspca_dev *gspca_dev) setautogain(gspca_dev); sethvflip(gspca_dev); - /* only resolution 640x480 is supported for pac7302 */ - sd->sof_read = 0; atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val); diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 1ac111176ffa..6a1ed73a24c6 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -20,31 +20,26 @@ */ /* Some documentation about various registers as determined by trial and error. - When the register addresses differ between the 7202 and the 7311 the 2 - different addresses are written as 7302addr/7311addr, when one of the 2 - addresses is a - sign that register description is not valid for the - matching IC. Register page 1: Address Description - -/0x08 Unknown compressor related, must always be 8 except when not + 0x08 Unknown compressor related, must always be 8 except when not in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! - -/0x1b Auto white balance related, bit 0 is AWB enable (inverted) + 0x1b Auto white balance related, bit 0 is AWB enable (inverted) bits 345 seem to toggle per color gains on/off (inverted) 0x78 Global control, bit 6 controls the LED (inverted) - -/0x80 JPEG compression ratio ? Best not touched + 0x80 JPEG compression ratio ? Best not touched - Register page 3/4: + Register page 4: Address Description 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? - -/0x0f Master gain 1-245, low value = high gain - 0x10/- Master gain 0-31 - -/0x10 Another gain 0-15, limited influence (1-2x gain I guess) + 0x0f Master gain 1-245, low value = high gain + 0x10 Another gain 0-15, limited influence (1-2x gain I guess) 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused - -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to + 0x27 Seems to toggle various gains on / off, Setting bit 7 seems to completely disable the analog amplification block. Set to 0x68 for max gain, 0x14 for minimal gain. */ @@ -60,7 +55,6 @@ MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); MODULE_LICENSE("GPL"); -/* specific webcam descriptor for pac7311 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -92,7 +86,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static const struct ctrl sd_ctrls[] = { -/* This control is for both the 7302 and the 7311 */ { { .id = V4L2_CID_CONTRAST, @@ -108,7 +101,6 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, -/* All controls below are for both the 7302 and the 7311 */ { { .id = V4L2_CID_GAIN, @@ -206,7 +198,6 @@ static const struct v4l2_pix_format vga_mode[] = { #define LOAD_PAGE4 254 #define END_OF_SEQUENCE 0 -/* pac 7311 */ static const __u8 init_7311[] = { 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ @@ -392,7 +383,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; - PDEBUG(D_CONF, "Find Sensor PAC7311"); cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); @@ -405,7 +395,6 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* This function is used by pac7311 only */ static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -438,7 +427,7 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; __u8 reg; - /* register 2 of frame 3/4 contains the clock divider configuring the + /* register 2 of page 4 contains the clock divider configuring the no fps according to the formula: 60 / reg. sd->exposure is the desired exposure time in ms. */ reg = 120 * sd->exposure / 1000; @@ -451,7 +440,7 @@ static void setexposure(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x02, reg); /* Page 1 register 8 must always be 0x08 except when not in - 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ + 640x480 mode and page 4 reg 2 <= 3 then it must be 9 */ reg_w(gspca_dev, 0xff, 0x01); if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && reg <= 3) { @@ -499,12 +488,12 @@ static int sd_start(struct gspca_dev *gspca_dev) /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 2: /* 160x120 pac7311 */ + case 2: /* 160x120 */ reg_w(gspca_dev, 0xff, 0x01); reg_w(gspca_dev, 0x17, 0x20); reg_w(gspca_dev, 0x87, 0x10); break; - case 1: /* 320x240 pac7311 */ + case 1: /* 320x240 */ reg_w(gspca_dev, 0xff, 0x01); reg_w(gspca_dev, 0x17, 0x30); reg_w(gspca_dev, 0x87, 0x11); @@ -541,11 +530,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ } -/* called on streamoff with alt 0 and on disconnect for 7311 */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - /* Include pac common sof detection functions */ #include "pac_common.h" @@ -820,7 +804,6 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, } #endif -/* sub-driver description for pac7311 */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, @@ -829,7 +812,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -- cgit v1.2.3 From 51ae23df428b94654dfb778bf70ca327a6aa83a0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 18 Apr 2012 06:12:57 -0300 Subject: [media] gspca_pac7311: Make sure exposure changes get applied immediately It turns out that the flush to sensor command needs to be done per register bank. We were missing one such flush in set_exposure, causing exposure changes to only show up when another setting in the same bank also got changed. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 6a1ed73a24c6..d12576365038 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -439,6 +439,9 @@ static void setexposure(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ reg_w(gspca_dev, 0x02, reg); + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); + /* Page 1 register 8 must always be 0x08 except when not in 640x480 mode and page 4 reg 2 <= 3 then it must be 9 */ reg_w(gspca_dev, 0xff, 0x01); -- cgit v1.2.3 From c894d26c5a89a241b10e2e8058ac12eb8f251252 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 25 Apr 2012 12:17:19 -0300 Subject: [media] gspca_pac7311: Adjust control scales to match registers Now that the pac7302 and pac7311 drivers are split, they no longer share there control settings, so there is no need to scale the controls to register values, instead make them reflect the registers directly. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 41 ++++++++++++------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index d12576365038..4efa110e6932 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -92,10 +92,10 @@ static const struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, -#define CONTRAST_MAX 255 +#define CONTRAST_MAX 15 .maximum = CONTRAST_MAX, .step = 1, -#define CONTRAST_DEF 127 +#define CONTRAST_DEF 7 .default_value = CONTRAST_DEF, }, .set = sd_setcontrast, @@ -107,11 +107,11 @@ static const struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", .minimum = 0, -#define GAIN_MAX 255 +#define GAIN_MAX 244 .maximum = GAIN_MAX, .step = 1, -#define GAIN_DEF 127 -#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ +#define GAIN_DEF 122 +#define GAIN_KNEE GAIN_MAX /* Gain seems to cause little noise on the 7311 */ .default_value = GAIN_DEF, }, .set = sd_setgain, @@ -122,12 +122,12 @@ static const struct ctrl sd_ctrls[] = { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Exposure", - .minimum = 0, -#define EXPOSURE_MAX 255 + .minimum = 2, +#define EXPOSURE_MAX 63 .maximum = EXPOSURE_MAX, .step = 1, -#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ -#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ +#define EXPOSURE_DEF 2 /* 30 fps */ +#define EXPOSURE_KNEE 6 /* 10 fps */ .default_value = EXPOSURE_DEF, }, .set = sd_setexposure, @@ -400,7 +400,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x10, sd->contrast >> 4); + reg_w(gspca_dev, 0x10, sd->contrast); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } @@ -408,15 +408,10 @@ static void setcontrast(struct gspca_dev *gspca_dev) static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int gain = GAIN_MAX - sd->gain; - if (gain < 1) - gain = 1; - else if (gain > 245) - gain = 245; reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, gain); + reg_w(gspca_dev, 0x0f, GAIN_MAX - sd->gain + 1); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); @@ -425,19 +420,9 @@ static void setgain(struct gspca_dev *gspca_dev) static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 reg; - - /* register 2 of page 4 contains the clock divider configuring the - no fps according to the formula: 60 / reg. sd->exposure is the - desired exposure time in ms. */ - reg = 120 * sd->exposure / 1000; - if (reg < 2) - reg = 2; - else if (reg > 63) - reg = 63; reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x02, reg); + reg_w(gspca_dev, 0x02, sd->exposure); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); @@ -446,7 +431,7 @@ static void setexposure(struct gspca_dev *gspca_dev) 640x480 mode and page 4 reg 2 <= 3 then it must be 9 */ reg_w(gspca_dev, 0xff, 0x01); if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && - reg <= 3) { + sd->exposure <= 3) { reg_w(gspca_dev, 0x08, 0x09); } else { reg_w(gspca_dev, 0x08, 0x08); -- cgit v1.2.3 From a5340ce50092e5458e1da489e06bc55398a76315 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 27 Apr 2012 11:40:28 -0300 Subject: [media] gspca_pac7311: Switch to new gspca control mechanism Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 240 +++++++++--------------------------- 1 file changed, 57 insertions(+), 183 deletions(-) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 4efa110e6932..d4c6ad2f1156 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -50,20 +50,26 @@ #include #include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); MODULE_LICENSE("GPL"); +enum e_ctrl { + CONTRAST, + GAIN, + EXPOSURE, + AUTOGAIN, + HFLIP, + VFLIP, + NCTRLS /* number of controls */ +}; + struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - - unsigned char contrast; - unsigned char gain; - unsigned char exposure; - unsigned char autogain; - __u8 hflip; - __u8 vflip; + struct gspca_ctrl ctrls[NCTRLS]; u8 sof_read; u8 autogain_ignore_frames; @@ -72,68 +78,52 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); -static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static void setcontrast(struct gspca_dev *gspca_dev); +static void setgain(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); +static void setautogain(struct gspca_dev *gspca_dev); +static void sethvflip(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[] = { - { +[CONTRAST] = { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, -#define CONTRAST_MAX 15 - .maximum = CONTRAST_MAX, + .maximum = 15, .step = 1, -#define CONTRAST_DEF 7 - .default_value = CONTRAST_DEF, + .default_value = 7, }, - .set = sd_setcontrast, - .get = sd_getcontrast, + .set_control = setcontrast }, - { +[GAIN] = { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", .minimum = 0, -#define GAIN_MAX 244 - .maximum = GAIN_MAX, + .maximum = 244, .step = 1, -#define GAIN_DEF 122 -#define GAIN_KNEE GAIN_MAX /* Gain seems to cause little noise on the 7311 */ - .default_value = GAIN_DEF, + .default_value = 122, +#define GAIN_KNEE 244 /* Gain seems to cause little noise on the 7311 */ }, - .set = sd_setgain, - .get = sd_getgain, + .set_control = setgain, }, - { +[EXPOSURE] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Exposure", .minimum = 2, -#define EXPOSURE_MAX 63 - .maximum = EXPOSURE_MAX, + .maximum = 63, .step = 1, -#define EXPOSURE_DEF 2 /* 30 fps */ + .default_value = 2, /* 30 fps */ #define EXPOSURE_KNEE 6 /* 10 fps */ - .default_value = EXPOSURE_DEF, }, - .set = sd_setexposure, - .get = sd_getexposure, + .set_control = setexposure, }, - { +[AUTOGAIN] = { { .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -141,13 +131,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define AUTOGAIN_DEF 1 - .default_value = AUTOGAIN_DEF, + .default_value = 1, }, - .set = sd_setautogain, - .get = sd_getautogain, + .set_control = setautogain, }, - { +[HFLIP] = { { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -155,13 +143,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define HFLIP_DEF 0 - .default_value = HFLIP_DEF, + .default_value = 0, }, - .set = sd_sethflip, - .get = sd_gethflip, + .set_control = sethvflip, }, - { +[VFLIP] = { { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -169,11 +155,9 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEF 0 - .default_value = VFLIP_DEF, + .default_value = 0, }, - .set = sd_setvflip, - .get = sd_getvflip, + .set_control = sethvflip, }, }; @@ -379,19 +363,13 @@ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; + struct cam *cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - sd->contrast = CONTRAST_DEF; - sd->gain = GAIN_DEF; - sd->exposure = EXPOSURE_DEF; - sd->autogain = AUTOGAIN_DEF; - sd->hflip = HFLIP_DEF; - sd->vflip = VFLIP_DEF; + gspca_dev->cam.ctrls = sd->ctrls; + return 0; } @@ -400,7 +378,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x10, sd->contrast); + reg_w(gspca_dev, 0x10, sd->ctrls[CONTRAST].val); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } @@ -411,7 +389,7 @@ static void setgain(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, GAIN_MAX - sd->gain + 1); + reg_w(gspca_dev, 0x0f, sd->ctrls[GAIN].max - sd->ctrls[GAIN].val + 1); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); @@ -422,7 +400,7 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x02, sd->exposure); + reg_w(gspca_dev, 0x02, sd->ctrls[EXPOSURE].val); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); @@ -431,7 +409,7 @@ static void setexposure(struct gspca_dev *gspca_dev) 640x480 mode and page 4 reg 2 <= 3 then it must be 9 */ reg_w(gspca_dev, 0xff, 0x01); if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && - sd->exposure <= 3) { + sd->ctrls[EXPOSURE].val <= 3) { reg_w(gspca_dev, 0x08, 0x09); } else { reg_w(gspca_dev, 0x08, 0x08); @@ -447,7 +425,8 @@ static void sethvflip(struct gspca_dev *gspca_dev) __u8 data; reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00); + data = (sd->ctrls[HFLIP].val ? 0x04 : 0x00) | + (sd->ctrls[VFLIP].val ? 0x08 : 0x00); reg_w(gspca_dev, 0x21, data); /* load registers to sensor (Bit 0, auto clear) */ @@ -518,8 +497,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ } -/* Include pac common sof detection functions */ -#include "pac_common.h" +#define WANT_REGULAR_AUTOGAIN +#include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) { @@ -527,7 +506,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) int avg_lum = atomic_read(&sd->avg_lum); int desired_lum, deadzone; - if (avg_lum == -1) + if (sd->ctrls[AUTOGAIN].val == 0 || avg_lum == -1) return; desired_lum = 200; @@ -535,7 +514,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; - else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, + else if (auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, deadzone, GAIN_KNEE, EXPOSURE_KNEE)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } @@ -640,72 +619,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->contrast; - return 0; -} - -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - setgain(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->gain; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - setexposure(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->exposure; - return 0; -} - -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - if (sd->autogain) { - sd->exposure = EXPOSURE_DEF; - sd->gain = GAIN_DEF; + if (sd->ctrls[AUTOGAIN].val) { + sd->ctrls[EXPOSURE].val = 2; + sd->ctrls[GAIN].val = 122; if (gspca_dev->streaming) { sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; @@ -713,52 +633,6 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) setgain(gspca_dev); } } - - return gspca_dev->usb_err; -} - -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->autogain; - return 0; -} - -static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hflip = val; - if (gspca_dev->streaming) - sethvflip(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->hflip; - return 0; -} - -static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vflip = val; - if (gspca_dev->streaming) - sethvflip(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->vflip; - return 0; } #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -- cgit v1.2.3 From ccab75e2831301a03ca1b126aa7375025dc50c88 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 27 Apr 2012 13:06:26 -0300 Subject: [media] gspca_pac7311: Switch to coarse expo autogain algorithm We can only control the clockdivider to control exposure on the pac7311, making our expo control coarse, switch to an autogain algorithm optimized for this. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index d4c6ad2f1156..81f018406bbd 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -70,6 +70,8 @@ enum e_ctrl { struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ struct gspca_ctrl ctrls[NCTRLS]; + int exp_too_low_cnt; + int exp_too_high_cnt; u8 sof_read; u8 autogain_ignore_frames; @@ -81,7 +83,6 @@ struct sd { static void setcontrast(struct gspca_dev *gspca_dev); static void setgain(struct gspca_dev *gspca_dev); static void setexposure(struct gspca_dev *gspca_dev); -static void setautogain(struct gspca_dev *gspca_dev); static void sethvflip(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[] = { @@ -106,7 +107,6 @@ static const struct ctrl sd_ctrls[] = { .maximum = 244, .step = 1, .default_value = 122, -#define GAIN_KNEE 244 /* Gain seems to cause little noise on the 7311 */ }, .set_control = setgain, }, @@ -118,8 +118,7 @@ static const struct ctrl sd_ctrls[] = { .minimum = 2, .maximum = 63, .step = 1, - .default_value = 2, /* 30 fps */ -#define EXPOSURE_KNEE 6 /* 10 fps */ + .default_value = 3, /* 20 fps, avoid using high compr. */ }, .set_control = setexposure, }, @@ -133,7 +132,6 @@ static const struct ctrl sd_ctrls[] = { .step = 1, .default_value = 1, }, - .set_control = setautogain, }, [HFLIP] = { { @@ -497,7 +495,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ } -#define WANT_REGULAR_AUTOGAIN +#define WANT_COARSE_EXPO_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) @@ -514,8 +512,8 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; - else if (auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, - deadzone, GAIN_KNEE, EXPOSURE_KNEE)) + else if (coarse_grained_expo_autogain(gspca_dev, avg_lum, desired_lum, + deadzone)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } @@ -619,22 +617,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static void setautogain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->ctrls[AUTOGAIN].val) { - sd->ctrls[EXPOSURE].val = 2; - sd->ctrls[GAIN].val = 122; - if (gspca_dev->streaming) { - sd->autogain_ignore_frames = - PAC_AUTOGAIN_IGNORE_FRAMES; - setexposure(gspca_dev); - setgain(gspca_dev); - } - } -} - #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ -- cgit v1.2.3 From 4b8ceb6c1539d776de8aec587902d8e0e2705390 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 28 Apr 2012 10:20:50 -0300 Subject: [media] gspca_pac7311: Convert multi-line comments to standard kernel style Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 62 ++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 81f018406bbd..54b4ab68f0b5 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -20,29 +20,29 @@ */ /* Some documentation about various registers as determined by trial and error. - - Register page 1: - - Address Description - 0x08 Unknown compressor related, must always be 8 except when not - in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! - 0x1b Auto white balance related, bit 0 is AWB enable (inverted) - bits 345 seem to toggle per color gains on/off (inverted) - 0x78 Global control, bit 6 controls the LED (inverted) - 0x80 JPEG compression ratio ? Best not touched - - Register page 4: - - Address Description - 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on - the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? - 0x0f Master gain 1-245, low value = high gain - 0x10 Another gain 0-15, limited influence (1-2x gain I guess) - 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused - 0x27 Seems to toggle various gains on / off, Setting bit 7 seems to - completely disable the analog amplification block. Set to 0x68 - for max gain, 0x14 for minimal gain. -*/ + * + * Register page 1: + * + * Address Description + * 0x08 Unknown compressor related, must always be 8 except when not + * in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! + * 0x1b Auto white balance related, bit 0 is AWB enable (inverted) + * bits 345 seem to toggle per color gains on/off (inverted) + * 0x78 Global control, bit 6 controls the LED (inverted) + * 0x80 JPEG compression ratio ? Best not touched + * + * Register page 4: + * + * Address Description + * 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on + * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + * 0x0f Master gain 1-245, low value = high gain + * 0x10 Another gain 0-15, limited influence (1-2x gain I guess) + * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + * 0x27 Seems to toggle various gains on / off, Setting bit 7 seems to + * completely disable the analog amplification block. Set to 0x68 + * for max gain, 0x14 for minimal gain. + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -403,8 +403,10 @@ static void setexposure(struct gspca_dev *gspca_dev) /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); - /* Page 1 register 8 must always be 0x08 except when not in - 640x480 mode and page 4 reg 2 <= 3 then it must be 9 */ + /* + * Page 1 register 8 must always be 0x08 except when not in + * 640x480 mode and page 4 reg 2 <= 3 then it must be 9 + */ reg_w(gspca_dev, 0xff, 0x01); if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && sd->ctrls[EXPOSURE].val <= 3) { @@ -577,10 +579,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (sof) { int n, lum_offset, footer_length; - /* 6 bytes after the FF D9 EOF marker a number of lumination - bytes are send corresponding to different parts of the - image, the 14th and 15th byte after the EOF seem to - correspond to the center of the image */ + /* + * 6 bytes after the FF D9 EOF marker a number of lumination + * bytes are send corresponding to different parts of the + * image, the 14th and 15th byte after the EOF seem to + * correspond to the center of the image. + */ lum_offset = 24 + sizeof pac_sof_marker; footer_length = 26; -- cgit v1.2.3 From 282ddfbcab456eb09429b3ca3c1cc5dde5a65358 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 27 Apr 2012 12:56:59 -0300 Subject: [media] gspca_pac7311: Properly set the compression balance Before this patch sometimes the camera would run out of bandwidth when running at 640x480@30. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 54b4ab68f0b5..5ccf0b41ffc5 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -29,7 +29,18 @@ * 0x1b Auto white balance related, bit 0 is AWB enable (inverted) * bits 345 seem to toggle per color gains on/off (inverted) * 0x78 Global control, bit 6 controls the LED (inverted) - * 0x80 JPEG compression ratio ? Best not touched + * 0x80 Compression balance, interesting settings: + * 0x01 Use this to allow the camera to switch to higher compr. + * on the fly. Needed to stay within bandwidth @ 640x480@30 + * 0x1c From usb captures under Windows for 640x480 + * 0x2a Values >= this switch the camera to a lower compression, + * using the same table for both luminance and chrominance. + * This gives a sharper picture. Usable only at 640x480@ < + * 15 fps or 320x240 / 160x120. Note currently the driver + * does not use this as the quality gain is small and the + * generated JPG-s are only understood by v4l-utils >= 0.8.9 + * 0x3f From usb captures under Windows for 320x240 + * 0x69 From usb captures under Windows for 160x120 * * Register page 4: * @@ -408,12 +419,21 @@ static void setexposure(struct gspca_dev *gspca_dev) * 640x480 mode and page 4 reg 2 <= 3 then it must be 9 */ reg_w(gspca_dev, 0xff, 0x01); - if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && - sd->ctrls[EXPOSURE].val <= 3) { + if (gspca_dev->width != 640 && sd->ctrls[EXPOSURE].val <= 3) reg_w(gspca_dev, 0x08, 0x09); - } else { + else reg_w(gspca_dev, 0x08, 0x08); - } + + /* + * Page1 register 80 sets the compression balance, normally we + * want / use 0x1c, but for 640x480@30fps we must allow the + * camera to use higher compression or we may run out of + * bandwidth. + */ + if (gspca_dev->width == 640 && sd->ctrls[EXPOSURE].val == 2) + reg_w(gspca_dev, 0x80, 0x01); + else + reg_w(gspca_dev, 0x80, 0x1c); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); -- cgit v1.2.3 From 895d464db68a98fe877f724cafb4c1f84b67492f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 28 Apr 2012 10:31:17 -0300 Subject: [media] gspca_pac7302: Convert multi-line comments to standard kernel style Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 142 ++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 63 deletions(-) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 4f7be66fce45..1e88d3a574d9 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -23,43 +23,44 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Some documentation about various registers as determined by trial and error. - - Register page 1: - - Address Description - 0x78 Global control, bit 6 controls the LED (inverted) - - Register page 3: - - Address Description - 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on - the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? - 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps - 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, - 63 -> ~27 fps, the 2 msb's must always be 1 !! - 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: - 1 -> ~30 fps, 2 -> ~20 fps - 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time - 0x0f Exposure bit 8, 0-448, 448 = no exposure at all - 0x10 Master gain 0-31 - 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused - - The registers are accessed in the following functions: - - Page | Register | Function - -----+------------+--------------------------------------------------- - 0 | 0x0f..0x20 | setcolors() - 0 | 0xa2..0xab | setbrightcont() - 0 | 0xc5 | setredbalance() - 0 | 0xc6 | setwhitebalance() - 0 | 0xc7 | setbluebalance() - 0 | 0xdc | setbrightcont(), setcolors() - 3 | 0x02 | setexposure() - 3 | 0x10 | setgain() - 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() - 3 | 0x21 | sethvflip() -*/ +/* + * Some documentation about various registers as determined by trial and error. + * + * Register page 1: + * + * Address Description + * 0x78 Global control, bit 6 controls the LED (inverted) + * + * Register page 3: + * + * Address Description + * 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on + * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + * 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps + * 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, + * 63 -> ~27 fps, the 2 msb's must always be 1 !! + * 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: + * 1 -> ~30 fps, 2 -> ~20 fps + * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time + * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all + * 0x10 Master gain 0-31 + * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + * + * The registers are accessed in the following functions: + * + * Page | Register | Function + * -----+------------+--------------------------------------------------- + * 0 | 0x0f..0x20 | setcolors() + * 0 | 0xa2..0xab | setbrightcont() + * 0 | 0xc5 | setredbalance() + * 0 | 0xc6 | setwhitebalance() + * 0 | 0xc7 | setbluebalance() + * 0 | 0xdc | setbrightcont(), setcolors() + * 3 | 0x02 | setexposure() + * 3 | 0x10 | setgain() + * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() + * 3 | 0x21 | sethvflip() + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -600,28 +601,36 @@ static void setexposure(struct gspca_dev *gspca_dev) u8 clockdiv; u16 exposure; - /* register 2 of frame 3 contains the clock divider configuring the - no fps according to the formula: 90 / reg. sd->exposure is the - desired exposure time in 0.5 ms. */ + /* + * Register 2 of frame 3 contains the clock divider configuring the + * no fps according to the formula: 90 / reg. sd->exposure is the + * desired exposure time in 0.5 ms. + */ clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000; - /* Note clockdiv = 3 also works, but when running at 30 fps, depending - on the scene being recorded, the camera switches to another - quantization table for certain JPEG blocks, and we don't know how - to decompress these blocks. So we cap the framerate at 15 fps */ + /* + * Note clockdiv = 3 also works, but when running at 30 fps, depending + * on the scene being recorded, the camera switches to another + * quantization table for certain JPEG blocks, and we don't know how + * to decompress these blocks. So we cap the framerate at 15 fps. + */ if (clockdiv < 6) clockdiv = 6; else if (clockdiv > 63) clockdiv = 63; - /* reg2 MUST be a multiple of 3, except when between 6 and 12? - Always round up, otherwise we cannot get the desired frametime - using the partial frame time exposure control */ + /* + * Register 2 MUST be a multiple of 3, except when between 6 and 12? + * Always round up, otherwise we cannot get the desired frametime + * using the partial frame time exposure control. + */ if (clockdiv < 6 || clockdiv > 12) clockdiv = ((clockdiv + 2) / 3) * 3; - /* frame exposure time in ms = 1000 * clockdiv / 90 -> - exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */ + /* + * frame exposure time in ms = 1000 * clockdiv / 90 -> + * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) + */ exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv); /* 0 = use full frametime, 448 = no exposure, reverse it */ exposure = 448 - exposure; @@ -639,10 +648,12 @@ static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ + /* + * When switching to autogain set defaults to make sure + * we are on a valid point of the autogain gain / + * exposure knee graph, and give this change time to + * take effect before doing autogain. + */ if (sd->ctrls[AUTOGAIN].val) { sd->ctrls[EXPOSURE].val = EXPOSURE_DEF; sd->ctrls[GAIN].val = GAIN_DEF; @@ -784,10 +795,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (sof) { int n, lum_offset, footer_length; - /* 6 bytes after the FF D9 EOF marker a number of lumination - bytes are send corresponding to different parts of the - image, the 14th and 15th byte after the EOF seem to - correspond to the center of the image */ + /* + * 6 bytes after the FF D9 EOF marker a number of lumination + * bytes are send corresponding to different parts of the + * image, the 14th and 15th byte after the EOF seem to + * correspond to the center of the image. + */ lum_offset = 61 + sizeof pac_sof_marker; footer_length = 74; @@ -831,9 +844,10 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, u8 index; u8 value; - /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit - long on the USB bus) - */ + /* + * reg->reg: bit0..15: reserved for register index (wIndex is 16bit + * long on the USB bus) + */ if (reg->match.type == V4L2_CHIP_MATCH_HOST && reg->match.addr == 0 && (reg->reg < 0x000000ff) && @@ -844,9 +858,11 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, index = reg->reg; value = reg->val; - /* Note that there shall be no access to other page - by any other function between the page swith and - the actual register write */ + /* + * Note that there shall be no access to other page + * by any other function between the page switch and + * the actual register write. + */ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ reg_w(gspca_dev, index, value); -- cgit v1.2.3 From 48bb7315f8205272c69fa49c732adb20b2f08614 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 27 Apr 2012 13:00:57 -0300 Subject: [media] gspca_pac7302: Document some more registers Note like all info on the pac73xx chips, this info was found by trial and error, so it is not necessarily 100% correct. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 1e88d3a574d9..a2a42f61d3c0 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -30,6 +30,14 @@ * * Address Description * 0x78 Global control, bit 6 controls the LED (inverted) + * 0x80 Compression balance, 2 interesting settings: + * 0x0f Default + * 0x50 Values >= this switch the camera to a lower compression, + * using the same table for both luminance and chrominance. + * This gives a sharper picture. Only usable when running + * at < 15 fps! Note currently the driver does not use this + * as the quality gain is small and the generated JPG-s are + * only understood by v4l-utils >= 0.8.9 * * Register page 3: * @@ -43,8 +51,14 @@ * 1 -> ~30 fps, 2 -> ~20 fps * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all - * 0x10 Master gain 0-31 + * 0x10 Gain 0-31 + * 0x12 Another gain 0-31, unlike 0x10 this one seems to start with an + * amplification value of 1 rather then 0 at its lowest setting * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + * 0x80 Another framerate control, best left at 1, moving it from 1 to + * 2 causes the framerate to become 3/4th of what it was, and + * also seems to cause pixel averaging, resulting in an effective + * resolution of 320x240 and thus a much blockier image * * The registers are accessed in the following functions: * -- cgit v1.2.3 From df8b98538c7011c4c74bd2d678369825c66be86c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 28 Apr 2012 10:12:28 -0300 Subject: [media] gspca_pac7302: Improve the gain control Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index a2a42f61d3c0..f196a0ff4fab 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -71,7 +71,7 @@ * 0 | 0xc7 | setbluebalance() * 0 | 0xdc | setbrightcont(), setcolors() * 3 | 0x02 | setexposure() - * 3 | 0x10 | setgain() + * 3 | 0x10, 0x12 | setgain() * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() * 3 | 0x21 | sethvflip() */ @@ -212,10 +212,10 @@ static const struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", .minimum = 0, - .maximum = 255, + .maximum = 62, .step = 1, -#define GAIN_DEF 127 -#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ +#define GAIN_DEF 15 +#define GAIN_KNEE 46 .default_value = GAIN_DEF, }, .set_control = setgain @@ -601,9 +601,19 @@ static void setbluebalance(struct gspca_dev *gspca_dev) static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 reg10, reg12; + + if (sd->ctrls[GAIN].val < 32) { + reg10 = sd->ctrls[GAIN].val; + reg12 = 0; + } else { + reg10 = 31; + reg12 = sd->ctrls[GAIN].val - 31; + } reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x10, sd->ctrls[GAIN].val >> 3); + reg_w(gspca_dev, 0x10, reg10); + reg_w(gspca_dev, 0x12, reg12); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); -- cgit v1.2.3 From 04ef052419ac61f28c6b7eafbe5d8e82c02bbee2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 May 2012 06:38:43 -0300 Subject: [media] media/video/et61x251: Remove this deprecated driver The et61x251 has been deprecated for a couple of releases now, as all devices it supports are also supported by gspca_etoms, and it has not seen any maintenance in years. So now it is time to remove it. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 2 - drivers/media/video/Makefile | 1 - drivers/media/video/et61x251/Kconfig | 18 - drivers/media/video/et61x251/Makefile | 4 - drivers/media/video/et61x251/et61x251.h | 213 -- drivers/media/video/et61x251/et61x251_core.c | 2683 -------------------- drivers/media/video/et61x251/et61x251_sensor.h | 108 - drivers/media/video/et61x251/et61x251_tas5130d1b.c | 143 -- 8 files changed, 3172 deletions(-) delete mode 100644 drivers/media/video/et61x251/Kconfig delete mode 100644 drivers/media/video/et61x251/Makefile delete mode 100644 drivers/media/video/et61x251/et61x251.h delete mode 100644 drivers/media/video/et61x251/et61x251_core.c delete mode 100644 drivers/media/video/et61x251/et61x251_sensor.h delete mode 100644 drivers/media/video/et61x251/et61x251_tas5130d1b.c diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index f2479c5c0eb2..b09982ee21d7 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -662,8 +662,6 @@ source "drivers/media/video/tm6000/Kconfig" source "drivers/media/video/usbvision/Kconfig" -source "drivers/media/video/et61x251/Kconfig" - source "drivers/media/video/sn9c102/Kconfig" source "drivers/media/video/pwc/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a6282a3a6a82..1f5e36ceb944 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -152,7 +152,6 @@ obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o obj-$(CONFIG_USB_SN9C102) += sn9c102/ -obj-$(CONFIG_USB_ET61X251) += et61x251/ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_USB_GSPCA) += gspca/ diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig deleted file mode 100644 index 87981b078fe6..000000000000 --- a/drivers/media/video/et61x251/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -config USB_ET61X251 - tristate "USB ET61X[12]51 PC Camera Controller support (DEPRECATED)" - depends on VIDEO_V4L2 - default n - ---help--- - This driver is DEPRECATED please use the gspca zc3xx module - instead. - - Say Y here if you want support for cameras based on Etoms ET61X151 - or ET61X251 PC Camera Controllers. - - See for more info. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. - - To compile this driver as a module, choose M here: the - module will be called et61x251. diff --git a/drivers/media/video/et61x251/Makefile b/drivers/media/video/et61x251/Makefile deleted file mode 100644 index 2ff4db9ec882..000000000000 --- a/drivers/media/video/et61x251/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o - -obj-$(CONFIG_USB_ET61X251) += et61x251.o - diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h deleted file mode 100644 index 337ded4a6388..000000000000 --- a/drivers/media/video/et61x251/et61x251.h +++ /dev/null @@ -1,213 +0,0 @@ -/*************************************************************************** - * V4L2 driver for ET61X[12]51 PC Camera Controllers * - * * - * Copyright (C) 2006 by Luca Risolia * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _ET61X251_H_ -#define _ET61X251_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "et61x251_sensor.h" - -/*****************************************************************************/ - -#define ET61X251_DEBUG -#define ET61X251_DEBUG_LEVEL 2 -#define ET61X251_MAX_DEVICES 64 -#define ET61X251_PRESERVE_IMGSCALE 0 -#define ET61X251_FORCE_MUNMAP 0 -#define ET61X251_MAX_FRAMES 32 -#define ET61X251_COMPRESSION_QUALITY 0 -#define ET61X251_URBS 2 -#define ET61X251_ISO_PACKETS 7 -#define ET61X251_ALTERNATE_SETTING 13 -#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS) -#define ET61X251_CTRL_TIMEOUT 100 -#define ET61X251_FRAME_TIMEOUT 2 - -/*****************************************************************************/ - -static const struct usb_device_id et61x251_id_table[] = { - { USB_DEVICE(0x102c, 0x6251), }, - { } -}; - -ET61X251_SENSOR_TABLE - -/*****************************************************************************/ - -enum et61x251_frame_state { - F_UNUSED, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, -}; - -struct et61x251_frame_t { - void* bufmem; - struct v4l2_buffer buf; - enum et61x251_frame_state state; - struct list_head frame; - unsigned long vma_use_count; -}; - -enum et61x251_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -enum et61x251_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum et61x251_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -struct et61x251_sysfs_attr { - u8 reg, i2c_reg; -}; - -struct et61x251_module_param { - u8 force_munmap; - u16 frame_timeout; -}; - -static DEFINE_MUTEX(et61x251_sysfs_lock); -static DECLARE_RWSEM(et61x251_dev_lock); - -struct et61x251_device { - struct video_device* v4ldev; - - struct et61x251_sensor sensor; - - struct usb_device* usbdev; - struct urb* urb[ET61X251_URBS]; - void* transfer_buffer[ET61X251_URBS]; - u8* control_buffer; - - struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES]; - struct list_head inqueue, outqueue; - u32 frame_count, nbuffers, nreadbuffers; - - enum et61x251_io_method io; - enum et61x251_stream_state stream; - - struct v4l2_jpegcompression compression; - - struct et61x251_sysfs_attr sysfs; - struct et61x251_module_param module_param; - - struct kref kref; - enum et61x251_dev_state state; - u8 users; - - struct completion probe; - struct mutex open_mutex, fileop_mutex; - spinlock_t queue_lock; - wait_queue_head_t wait_open, wait_frame, wait_stream; -}; - -/*****************************************************************************/ - -struct et61x251_device* -et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) -{ - return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; -} - - -void -et61x251_attach_sensor(struct et61x251_device* cam, - const struct et61x251_sensor* sensor) -{ - memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor)); -} - -/*****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef ET61X251_DEBUG -#define DBG(level, fmt, ...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1) \ - dev_err(&cam->usbdev->dev, fmt "\n", \ - ##__VA_ARGS__); \ - else if ((level) == 2) \ - dev_info(&cam->usbdev->dev, fmt "\n", \ - ##__VA_ARGS__); \ - else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__, \ - ##__VA_ARGS__); \ - } \ -} while (0) -#define KDBG(level, fmt, ...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info(fmt "\n", ##__VA_ARGS__); \ - else if ((level) == 3) \ - pr_debug("[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__, \ - ##__VA_ARGS__); \ - } \ -} while (0) -#define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_print_ioctl(name, cmd); \ -} while (0) -#else -#define DBG(level, fmt, ...) do {;} while(0) -#define KDBG(level, fmt, ...) do {;} while(0) -#define V4LDBG(level, name, cmd) do {;} while(0) -#endif - -#undef PDBG -#define PDBG(fmt, ...) \ - dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__, ##__VA_ARGS__) - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while (0) /* placeholder */ - -#endif /* _ET61X251_H_ */ diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c deleted file mode 100644 index 5539f09440ac..000000000000 --- a/drivers/media/video/et61x251/et61x251_core.c +++ /dev/null @@ -1,2683 +0,0 @@ -/*************************************************************************** - * V4L2 driver for ET61X[12]51 PC Camera Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "et61x251.h" - -/*****************************************************************************/ - -#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \ - "PC Camera Controllers" -#define ET61X251_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia" -#define ET61X251_AUTHOR_EMAIL "" -#define ET61X251_MODULE_LICENSE "GPL" -#define ET61X251_MODULE_VERSION "1.1.10" - -/*****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, et61x251_id_table); - -MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL); -MODULE_DESCRIPTION(ET61X251_MODULE_NAME); -MODULE_VERSION(ET61X251_MODULE_VERSION); -MODULE_LICENSE(ET61X251_MODULE_LICENSE); - -static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1}; -module_param_array(video_nr, short, NULL, 0444); -MODULE_PARM_DESC(video_nr, - "\n<-1|n[,...]> Specify V4L2 minor mode number." - "\n -1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to " - __MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second registered camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static bool force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] = - ET61X251_FORCE_MUNMAP}; -module_param_array(force_munmap, bool, NULL, 0444); -MODULE_PARM_DESC(force_munmap, - "\n<0|1[,...]> Force the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n 0 = do not force memory unmapping" - "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(ET61X251_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] = - ET61X251_FRAME_TIMEOUT}; -module_param_array(frame_timeout, uint, NULL, 0644); -MODULE_PARM_DESC(frame_timeout, - "\n Timeout for a video frame in seconds." - "\nThis parameter is specific for each detected camera." - "\nDefault value is " - __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"." - "\n"); - -#ifdef ET61X251_DEBUG -static unsigned short debug = ET61X251_DEBUG_LEVEL; -module_param(debug, ushort, 0644); -MODULE_PARM_DESC(debug, - "\n Debugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." - "\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"." - "\n"); -#endif - -/*****************************************************************************/ - -static u32 -et61x251_request_buffers(struct et61x251_device* cam, u32 count, - enum et61x251_io_method io) -{ - struct v4l2_pix_format* p = &(cam->sensor.pix_format); - struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - const size_t imagesize = cam->module_param.force_munmap || - io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; - void* buff = NULL; - u32 i; - - if (count > ET61X251_MAX_FRAMES) - count = ET61X251_MAX_FRAMES; - - cam->nbuffers = count; - while (cam->nbuffers > 0) { - if ((buff = vmalloc_32_user(cam->nbuffers * - PAGE_ALIGN(imagesize)))) - break; - cam->nbuffers--; - } - - for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.length = imagesize; - cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->frame[i].buf.sequence = 0; - cam->frame[i].buf.field = V4L2_FIELD_NONE; - cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; - } - - return cam->nbuffers; -} - - -static void et61x251_release_buffers(struct et61x251_device* cam) -{ - if (cam->nbuffers) { - vfree(cam->frame[0].bufmem); - cam->nbuffers = 0; - } - cam->frame_current = NULL; -} - - -static void et61x251_empty_framequeues(struct et61x251_device* cam) -{ - u32 i; - - INIT_LIST_HEAD(&cam->inqueue); - INIT_LIST_HEAD(&cam->outqueue); - - for (i = 0; i < ET61X251_MAX_FRAMES; i++) { - cam->frame[i].state = F_UNUSED; - cam->frame[i].buf.bytesused = 0; - } -} - - -static void et61x251_requeue_outqueue(struct et61x251_device* cam) -{ - struct et61x251_frame_t *i; - - list_for_each_entry(i, &cam->outqueue, frame) { - i->state = F_QUEUED; - list_add(&i->frame, &cam->inqueue); - } - - INIT_LIST_HEAD(&cam->outqueue); -} - - -static void et61x251_queue_unusedframes(struct et61x251_device* cam) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].state == F_UNUSED) { - cam->frame[i].state = F_QUEUED; - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[i].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - } -} - -/*****************************************************************************/ - -int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - *buff = value; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, index " - "0x%02X, error %d)", value, index, res); - return -1; - } - - return 0; -} - - -static int et61x251_read_reg(struct et61x251_device* cam, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - DBG(3, "Failed to read a register (index 0x%02X, error %d)", - index, res); - - return (res >= 0) ? (int)(*buff) : -1; -} - - -static int -et61x251_i2c_wait(struct et61x251_device* cam, - const struct et61x251_sensor* sensor) -{ - int i, r; - - for (i = 1; i <= 8; i++) { - if (sensor->interface == ET61X251_I2C_3WIRES) { - r = et61x251_read_reg(cam, 0x8e); - if (!(r & 0x02) && (r >= 0)) - return 0; - } else { - r = et61x251_read_reg(cam, 0x8b); - if (!(r & 0x01) && (r >= 0)) - return 0; - } - if (r < 0) - return -EIO; - udelay(8*8); /* minimum for sensors at 400kHz */ - } - - return -EBUSY; -} - - -int -et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, - u8 data3, u8 data4, u8 data5, u8 data6, u8 data7, - u8 data8, u8 address) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - data[0] = data2; - data[1] = data3; - data[2] = data4; - data[3] = data5; - data[4] = data6; - data[5] = data7; - data[6] = data8; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - data[0] = address; - data[1] = cam->sensor.i2c_slave_id; - data[2] = cam->sensor.rsta | 0x02 | (n << 4); - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - /* Start writing through the serial interface */ - data[0] = data1; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += et61x251_i2c_wait(cam, &cam->sensor); - - if (err) - DBG(3, "I2C raw write failed for %s image sensor", - cam->sensor.name); - - PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, " - "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X," - " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address, - data1, data2, data3, data4, data5, data6, data7, data8); - - return err ? -1 : 0; - -} - - -/*****************************************************************************/ - -static void et61x251_urb_complete(struct urb *urb) -{ - struct et61x251_device* cam = urb->context; - struct et61x251_frame_t** f; - size_t imagesize; - u8 i; - int err = 0; - - if (urb->status == -ENOENT) - return; - - f = &cam->frame_current; - - if (cam->stream == STREAM_INTERRUPT) { - cam->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - DBG(3, "Stream interrupted"); - wake_up(&cam->wait_stream); - } - - if (cam->state & DEV_DISCONNECTED) - return; - - if (cam->state & DEV_MISCONFIGURED) { - wake_up_interruptible(&cam->wait_frame); - return; - } - - if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) - goto resubmit_urb; - - if (!(*f)) - (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t, - frame); - - imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned int len, status; - void *pos; - u8* b1, * b2, sof; - const u8 VOID_BYTES = 6; - size_t imglen; - - len = urb->iso_frame_desc[i].actual_length; - status = urb->iso_frame_desc[i].status; - pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; - - if (status) { - DBG(3, "Error in isochronous frame"); - (*f)->state = F_ERROR; - continue; - } - - b1 = pos++; - b2 = pos++; - sof = ((*b1 & 0x3f) == 63); - imglen = ((*b1 & 0xc0) << 2) | *b2; - - PDBGG("Isochrnous frame: length %u, #%u i, image length %zu", - len, i, imglen); - - if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) -start_of_frame: - if (sof) { - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - do_gettimeofday(&(*f)->buf.timestamp); - pos += 22; - DBG(3, "SOF detected: new video frame"); - } - - if ((*f)->state == F_GRABBING) { - if (sof && (*f)->buf.bytesused) { - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_ET61X251) - goto end_of_frame; - else { - DBG(3, "Not expected SOF detected " - "after %lu bytes", - (unsigned long)(*f)->buf.bytesused); - (*f)->state = F_ERROR; - continue; - } - } - - if ((*f)->buf.bytesused + imglen > imagesize) { - DBG(3, "Video frame size exceeded"); - (*f)->state = F_ERROR; - continue; - } - - pos += VOID_BYTES; - - memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen); - (*f)->buf.bytesused += imglen; - - if ((*f)->buf.bytesused == imagesize) { - u32 b; -end_of_frame: - b = (*f)->buf.bytesused; - (*f)->state = F_DONE; - (*f)->buf.sequence= ++cam->frame_count; - spin_lock(&cam->queue_lock); - list_move_tail(&(*f)->frame, &cam->outqueue); - if (!list_empty(&cam->inqueue)) - (*f) = list_entry(cam->inqueue.next, - struct et61x251_frame_t, - frame); - else - (*f) = NULL; - spin_unlock(&cam->queue_lock); - DBG(3, "Video frame captured: : %lu bytes", - (unsigned long)(b)); - - if (!(*f)) - goto resubmit_urb; - - if (sof && - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_ET61X251) - goto start_of_frame; - } - } - } - -resubmit_urb: - urb->dev = cam->usbdev; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -EPERM) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "usb_submit_urb() failed"); - } - - wake_up_interruptible(&cam->wait_frame); -} - - -static int et61x251_start_transfer(struct et61x251_device* cam) -{ - struct usb_device *udev = cam->usbdev; - struct urb* urb; - struct usb_host_interface* altsetting = usb_altnum_to_altsetting( - usb_ifnum_to_if(udev, 0), - ET61X251_ALTERNATE_SETTING); - const unsigned int psz = le16_to_cpu(altsetting-> - endpoint[0].desc.wMaxPacketSize); - s8 i, j; - int err = 0; - - for (i = 0; i < ET61X251_URBS; i++) { - cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz, - GFP_KERNEL); - if (!cam->transfer_buffer[i]) { - err = -ENOMEM; - DBG(1, "Not enough memory"); - goto free_buffers; - } - } - - for (i = 0; i < ET61X251_URBS; i++) { - urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; - if (!urb) { - err = -ENOMEM; - DBG(1, "usb_alloc_urb() failed"); - goto free_urbs; - } - urb->dev = udev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(udev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = ET61X251_ISO_PACKETS; - urb->complete = et61x251_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < ET61X251_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = psz * j; - urb->iso_frame_desc[j].length = psz; - } - } - - err = et61x251_write_reg(cam, 0x01, 0x03); - err = et61x251_write_reg(cam, 0x00, 0x03); - err = et61x251_write_reg(cam, 0x08, 0x03); - if (err) { - err = -EIO; - DBG(1, "I/O hardware error"); - goto free_urbs; - } - - err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING); - if (err) { - DBG(1, "usb_set_interface() failed"); - goto free_urbs; - } - - cam->frame_current = NULL; - - for (i = 0; i < ET61X251_URBS; i++) { - err = usb_submit_urb(cam->urb[i], GFP_KERNEL); - if (err) { - for (j = i-1; j >= 0; j--) - usb_kill_urb(cam->urb[j]); - DBG(1, "usb_submit_urb() failed, error %d", err); - goto free_urbs; - } - } - - return 0; - -free_urbs: - for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++) - usb_free_urb(cam->urb[i]); - -free_buffers: - for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++) - kfree(cam->transfer_buffer[i]); - - return err; -} - - -static int et61x251_stop_transfer(struct et61x251_device* cam) -{ - struct usb_device *udev = cam->usbdev; - s8 i; - int err = 0; - - if (cam->state & DEV_DISCONNECTED) - return 0; - - for (i = ET61X251_URBS-1; i >= 0; i--) { - usb_kill_urb(cam->urb[i]); - usb_free_urb(cam->urb[i]); - kfree(cam->transfer_buffer[i]); - } - - err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ - if (err) - DBG(3, "usb_set_interface() failed"); - - return err; -} - - -static int et61x251_stream_interrupt(struct et61x251_device* cam) -{ - long timeout; - - cam->stream = STREAM_INTERRUPT; - timeout = wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - ET61X251_URB_TIMEOUT); - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - else if (cam->stream != STREAM_OFF) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "URB timeout reached. The camera is misconfigured. To " - "use it, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - return 0; -} - -/*****************************************************************************/ - -#ifdef CONFIG_VIDEO_ADV_DEBUG - -static int et61x251_i2c_try_read(struct et61x251_device* cam, - const struct et61x251_sensor* sensor, - u8 address) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - data[0] = address; - data[1] = cam->sensor.i2c_slave_id; - data[2] = cam->sensor.rsta | 0x10; - data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02); - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += et61x251_i2c_wait(cam, sensor); - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - if (err) - DBG(3, "I2C read failed for %s image sensor", sensor->name); - - PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]); - - return err ? -1 : (int)data[0]; -} - - -static int et61x251_i2c_try_write(struct et61x251_device* cam, - const struct et61x251_sensor* sensor, - u8 address, u8 value) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - data[0] = address; - data[1] = cam->sensor.i2c_slave_id; - data[2] = cam->sensor.rsta | 0x12; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - data[0] = value; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += et61x251_i2c_wait(cam, sensor); - - if (err) - DBG(3, "I2C write failed for %s image sensor", sensor->name); - - PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value); - - return err ? -1 : 0; -} - -static int et61x251_i2c_read(struct et61x251_device* cam, u8 address) -{ - return et61x251_i2c_try_read(cam, &cam->sensor, address); -} - -static int et61x251_i2c_write(struct et61x251_device* cam, - u8 address, u8 value) -{ - return et61x251_i2c_try_write(cam, &cam->sensor, address, value); -} - -static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count) -{ - char str[5]; - char* endp; - unsigned long val; - - if (len < 4) { - strncpy(str, buff, len); - str[len] = '\0'; - } else { - strncpy(str, buff, 4); - str[4] = '\0'; - } - - val = simple_strtoul(str, &endp, 0); - - *count = 0; - if (val <= 0xff) - *count = (ssize_t)(endp - str); - if ((*count) && (len == *count+1) && (buff[*count] == '\n')) - *count += 1; - - return (u8)val; -} - -/* - NOTE 1: being inside one of the following methods implies that the v4l - device exists for sure (see kobjects and reference counters) - NOTE 2: buffers are PAGE_SIZE long -*/ - -static ssize_t et61x251_show_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.reg); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_reg(struct device* cd, - struct device_attribute *attr, const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 index; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - index = et61x251_strtou8(buf, len, &count); - if (index > 0x8e || !count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.reg = index; - - DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t et61x251_show_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - value = et61x251_strtou8(buf, len, &count); - if (!count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - err = et61x251_write_reg(cam, value, cam->sysfs.reg); - if (err) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X", - cam->sysfs.reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t et61x251_show_i2c_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_i2c_reg(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 index; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - index = et61x251_strtou8(buf, len, &count); - if (!count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.i2c_reg = index; - - DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t et61x251_show_i2c_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENOSYS; - } - - if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_i2c_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENOSYS; - } - - value = et61x251_strtou8(buf, len, &count); - if (!count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value); - if (err) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", - cam->sysfs.i2c_reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, - et61x251_show_reg, et61x251_store_reg); -static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, - et61x251_show_val, et61x251_store_val); -static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, - et61x251_show_i2c_reg, et61x251_store_i2c_reg); -static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, - et61x251_show_i2c_val, et61x251_store_i2c_val); - - -static int et61x251_create_sysfs(struct et61x251_device* cam) -{ - struct device *classdev = &(cam->v4ldev->dev); - int err = 0; - - if ((err = device_create_file(classdev, &dev_attr_reg))) - goto err_out; - if ((err = device_create_file(classdev, &dev_attr_val))) - goto err_reg; - - if (cam->sensor.sysfs_ops) { - if ((err = device_create_file(classdev, &dev_attr_i2c_reg))) - goto err_val; - if ((err = device_create_file(classdev, &dev_attr_i2c_val))) - goto err_i2c_reg; - } - -err_i2c_reg: - if (cam->sensor.sysfs_ops) - device_remove_file(classdev, &dev_attr_i2c_reg); -err_val: - device_remove_file(classdev, &dev_attr_val); -err_reg: - device_remove_file(classdev, &dev_attr_reg); -err_out: - return err; -} -#endif /* CONFIG_VIDEO_ADV_DEBUG */ - -/*****************************************************************************/ - -static int -et61x251_set_pix_format(struct et61x251_device* cam, - struct v4l2_pix_format* pix) -{ - int r, err = 0; - - if ((r = et61x251_read_reg(cam, 0x12)) < 0) - err += r; - if (pix->pixelformat == V4L2_PIX_FMT_ET61X251) - err += et61x251_write_reg(cam, r & 0xfd, 0x12); - else - err += et61x251_write_reg(cam, r | 0x02, 0x12); - - return err ? -EIO : 0; -} - - -static int -et61x251_set_compression(struct et61x251_device* cam, - struct v4l2_jpegcompression* compression) -{ - int r, err = 0; - - if ((r = et61x251_read_reg(cam, 0x12)) < 0) - err += r; - if (compression->quality == 0) - err += et61x251_write_reg(cam, r & 0xfb, 0x12); - else - err += et61x251_write_reg(cam, r | 0x04, 0x12); - - return err ? -EIO : 0; -} - - -static int et61x251_set_scale(struct et61x251_device* cam, u8 scale) -{ - int r = 0, err = 0; - - r = et61x251_read_reg(cam, 0x12); - if (r < 0) - err += r; - - if (scale == 1) - err += et61x251_write_reg(cam, r & ~0x01, 0x12); - else if (scale == 2) - err += et61x251_write_reg(cam, r | 0x01, 0x12); - - if (err) - return -EIO; - - PDBGG("Scaling factor: %u", scale); - - return 0; -} - - -static int -et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect) -{ - struct et61x251_sensor* s = &cam->sensor; - u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left + - s->active_pixel.left), - fmw_sy = (u16)(rect->top - s->cropcap.bounds.top + - s->active_pixel.top), - fmw_length = (u16)(rect->width), - fmw_height = (u16)(rect->height); - int err = 0; - - err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69); - err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a); - err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b); - err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c); - err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6) - | ((fmw_length & 0x300) >> 4) - | ((fmw_height & 0x300) >> 2), 0x6d); - if (err) - return -EIO; - - PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u", - fmw_sx, fmw_sy, fmw_length, fmw_height); - - return 0; -} - - -static int et61x251_init(struct et61x251_device* cam) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - struct v4l2_queryctrl *qctrl; - struct v4l2_rect* rect; - u8 i = 0; - int err = 0; - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->open_mutex); - init_waitqueue_head(&cam->wait_open); - qctrl = s->qctrl; - rect = &(s->cropcap.defrect); - cam->compression.quality = ET61X251_COMPRESSION_QUALITY; - } else { /* use current values */ - qctrl = s->_qctrl; - rect = &(s->_rect); - } - - err += et61x251_set_scale(cam, rect->width / s->pix_format.width); - err += et61x251_set_crop(cam, rect); - if (err) - return err; - - if (s->init) { - err = s->init(cam); - if (err) { - DBG(3, "Sensor initialization failed"); - return err; - } - } - - err += et61x251_set_compression(cam, &cam->compression); - err += et61x251_set_pix_format(cam, &s->pix_format); - if (s->set_pix_format) - err += s->set_pix_format(cam, &s->pix_format); - if (err) - return err; - - if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251) - DBG(3, "Compressed video format is active, quality %d", - cam->compression.quality); - else - DBG(3, "Uncompressed video format is active"); - - if (s->set_crop) - if ((err = s->set_crop(cam, rect))) { - DBG(3, "set_crop() failed"); - return err; - } - - if (s->set_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && - !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl.id = s->qctrl[i].id; - ctrl.value = qctrl[i].default_value; - err = s->set_ctrl(cam, &ctrl); - if (err) { - DBG(3, "Set %s control failed", - s->qctrl[i].name); - return err; - } - DBG(3, "Image sensor supports '%s' control", - s->qctrl[i].name); - } - } - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->fileop_mutex); - spin_lock_init(&cam->queue_lock); - init_waitqueue_head(&cam->wait_frame); - init_waitqueue_head(&cam->wait_stream); - cam->nreadbuffers = 2; - memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), - sizeof(struct v4l2_rect)); - cam->state |= DEV_INITIALIZED; - } - - DBG(2, "Initialization succeeded"); - return 0; -} - -/*****************************************************************************/ - -static void et61x251_release_resources(struct kref *kref) -{ - struct et61x251_device *cam; - - mutex_lock(&et61x251_sysfs_lock); - - cam = container_of(kref, struct et61x251_device, kref); - - DBG(2, "V4L2 device %s deregistered", - video_device_node_name(cam->v4ldev)); - video_set_drvdata(cam->v4ldev, NULL); - video_unregister_device(cam->v4ldev); - usb_put_dev(cam->usbdev); - kfree(cam->control_buffer); - kfree(cam); - - mutex_unlock(&et61x251_sysfs_lock); -} - - -static int et61x251_open(struct file *filp) -{ - struct et61x251_device* cam; - int err = 0; - - if (!down_read_trylock(&et61x251_dev_lock)) - return -ERESTARTSYS; - - cam = video_drvdata(filp); - - if (wait_for_completion_interruptible(&cam->probe)) { - up_read(&et61x251_dev_lock); - return -ERESTARTSYS; - } - - kref_get(&cam->kref); - - if (mutex_lock_interruptible(&cam->open_mutex)) { - kref_put(&cam->kref, et61x251_release_resources); - up_read(&et61x251_dev_lock); - return -ERESTARTSYS; - } - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - err = -ENODEV; - goto out; - } - - if (cam->users) { - DBG(2, "Device %s is already in use", - video_device_node_name(cam->v4ldev)); - DBG(3, "Simultaneous opens are not supported"); - if ((filp->f_flags & O_NONBLOCK) || - (filp->f_flags & O_NDELAY)) { - err = -EWOULDBLOCK; - goto out; - } - DBG(2, "A blocking open() has been requested. Wait for the " - "device to be released..."); - up_read(&et61x251_dev_lock); - err = wait_event_interruptible_exclusive(cam->wait_open, - (cam->state & DEV_DISCONNECTED) - || !cam->users); - down_read(&et61x251_dev_lock); - if (err) - goto out; - if (cam->state & DEV_DISCONNECTED) { - err = -ENODEV; - goto out; - } - } - - if (cam->state & DEV_MISCONFIGURED) { - err = et61x251_init(cam); - if (err) { - DBG(1, "Initialization failed again. " - "I will retry on next open()."); - goto out; - } - cam->state &= ~DEV_MISCONFIGURED; - } - - if ((err = et61x251_start_transfer(cam))) - goto out; - - filp->private_data = cam; - cam->users++; - cam->io = IO_NONE; - cam->stream = STREAM_OFF; - cam->nbuffers = 0; - cam->frame_count = 0; - et61x251_empty_framequeues(cam); - - DBG(3, "Video device %s is open", - video_device_node_name(cam->v4ldev)); - -out: - mutex_unlock(&cam->open_mutex); - if (err) - kref_put(&cam->kref, et61x251_release_resources); - up_read(&et61x251_dev_lock); - return err; -} - - -static int et61x251_release(struct file *filp) -{ - struct et61x251_device* cam; - - down_write(&et61x251_dev_lock); - - cam = video_drvdata(filp); - - et61x251_stop_transfer(cam); - et61x251_release_buffers(cam); - cam->users--; - wake_up_interruptible_nr(&cam->wait_open, 1); - - DBG(3, "Video device %s closed", - video_device_node_name(cam->v4ldev)); - - kref_put(&cam->kref, et61x251_release_resources); - - up_write(&et61x251_dev_lock); - - return 0; -} - - -static ssize_t -et61x251_read(struct file* filp, char __user * buf, - size_t count, loff_t* f_pos) -{ - struct et61x251_device *cam = video_drvdata(filp); - struct et61x251_frame_t* f, * i; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (cam->io == IO_MMAP) { - DBG(3, "Close and open the device again to choose the read " - "method"); - mutex_unlock(&cam->fileop_mutex); - return -EBUSY; - } - - if (cam->io == IO_NONE) { - if (!et61x251_request_buffers(cam, cam->nreadbuffers, - IO_READ)) { - DBG(1, "read() failed, not enough memory"); - mutex_unlock(&cam->fileop_mutex); - return -ENOMEM; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (list_empty(&cam->inqueue)) { - if (!list_empty(&cam->outqueue)) - et61x251_empty_framequeues(cam); - et61x251_queue_unusedframes(cam); - } - - if (!count) { - mutex_unlock(&cam->fileop_mutex); - return 0; - } - - if (list_empty(&cam->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - msecs_to_jiffies( - cam->module_param.frame_timeout * 1000 - ) - ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; - } - if (cam->state & DEV_DISCONNECTED) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - if (!timeout || (cam->state & DEV_MISCONFIGURED)) { - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - - f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame); - - if (count > f->buf.bytesused) - count = f->buf.bytesused; - - if (copy_to_user(buf, f->bufmem, count)) { - err = -EFAULT; - goto exit; - } - *f_pos += count; - -exit: - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(i, &cam->outqueue, frame) - i->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - et61x251_queue_unusedframes(cam); - - PDBGG("Frame #%lu, bytes read: %zu", - (unsigned long)f->buf.index, count); - - mutex_unlock(&cam->fileop_mutex); - - return err ? err : count; -} - - -static unsigned int et61x251_poll(struct file *filp, poll_table *wait) -{ - struct et61x251_device *cam = video_drvdata(filp); - struct et61x251_frame_t* f; - unsigned long lock_flags; - unsigned int mask = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return POLLERR; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - goto error; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - goto error; - } - - if (cam->io == IO_NONE) { - if (!et61x251_request_buffers(cam, cam->nreadbuffers, - IO_READ)) { - DBG(1, "poll() failed, not enough memory"); - goto error; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (cam->io == IO_READ) { - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(f, &cam->outqueue, frame) - f->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - et61x251_queue_unusedframes(cam); - } - - poll_wait(filp, &cam->wait_frame, wait); - - if (!list_empty(&cam->outqueue)) - mask |= POLLIN | POLLRDNORM; - - mutex_unlock(&cam->fileop_mutex); - - return mask; - -error: - mutex_unlock(&cam->fileop_mutex); - return POLLERR; -} - - -static void et61x251_vm_open(struct vm_area_struct* vma) -{ - struct et61x251_frame_t* f = vma->vm_private_data; - f->vma_use_count++; -} - - -static void et61x251_vm_close(struct vm_area_struct* vma) -{ - /* NOTE: buffers are not freed here */ - struct et61x251_frame_t* f = vma->vm_private_data; - f->vma_use_count--; -} - - -static const struct vm_operations_struct et61x251_vm_ops = { - .open = et61x251_vm_open, - .close = et61x251_vm_close, -}; - - -static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) -{ - struct et61x251_device *cam = video_drvdata(filp); - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { - mutex_unlock(&cam->fileop_mutex); - return -EACCES; - } - - if (cam->io != IO_MMAP || - size != PAGE_ALIGN(cam->frame[0].buf.length)) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - for (i = 0; i < cam->nbuffers; i++) { - if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == cam->nbuffers) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_RESERVED; - - pos = cam->frame[i].bufmem; - while (size > 0) { /* size is page-aligned */ - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &et61x251_vm_ops; - vma->vm_private_data = &cam->frame[i]; - et61x251_vm_open(vma); - - mutex_unlock(&cam->fileop_mutex); - - return 0; -} - -/*****************************************************************************/ - -static int -et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_capability cap = { - .driver = "et61x251", - .version = LINUX_VERSION_CODE, - .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, - }; - - strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); - if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), - sizeof(cap.bus_info)); - - if (copy_to_user(arg, &cap, sizeof(cap))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_input i; - - if (copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - - if (i.index) - return -EINVAL; - - memset(&i, 0, sizeof(i)); - strcpy(i.name, "Camera"); - i.type = V4L2_INPUT_TYPE_CAMERA; - i.capabilities = V4L2_IN_CAP_STD; - - if (copy_to_user(arg, &i, sizeof(i))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg) -{ - int index = 0; - - if (copy_to_user(arg, &index, sizeof(index))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg) -{ - int index; - - if (copy_from_user(&index, arg, sizeof(index))) - return -EFAULT; - - if (index != 0) - return -EINVAL; - - return 0; -} - - -static int -et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_queryctrl qc; - u8 i; - - if (copy_from_user(&qc, arg, sizeof(qc))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (qc.id && qc.id == s->qctrl[i].id) { - memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); - if (copy_to_user(arg, &qc, sizeof(qc))) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - - -static int -et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - int err = 0; - u8 i; - - if (!s->get_ctrl && !s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - if (!s->get_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (ctrl.id == s->qctrl[i].id) { - ctrl.value = s->_qctrl[i].default_value; - goto exit; - } - return -EINVAL; - } else - err = s->get_ctrl(cam, &ctrl); - -exit: - if (copy_to_user(arg, &ctrl, sizeof(ctrl))) - return -EFAULT; - - return err; -} - - -static int -et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - u8 i; - int err = 0; - - if (!s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { - if (ctrl.id == s->qctrl[i].id) { - if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - if (ctrl.value < s->qctrl[i].minimum || - ctrl.value > s->qctrl[i].maximum) - return -ERANGE; - ctrl.value -= ctrl.value % s->qctrl[i].step; - break; - } - } - if (i == ARRAY_SIZE(s->qctrl)) - return -EINVAL; - if ((err = s->set_ctrl(cam, &ctrl))) - return err; - - s->_qctrl[i].default_value = ctrl.value; - - return 0; -} - - -static int -et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_cropcap* cc = &(cam->sensor.cropcap); - - cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cc->pixelaspect.numerator = 1; - cc->pixelaspect.denominator = 1; - - if (copy_to_user(arg, cc, sizeof(*cc))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_crop crop = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - }; - - memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); - - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_crop crop; - struct v4l2_rect* rect; - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_pix_format* pix_format = &(s->pix_format); - u8 scale; - const enum et61x251_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&crop, arg, sizeof(crop))) - return -EFAULT; - - rect = &(crop.c); - - if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_CROP failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - /* Preserve R,G or B origin */ - rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; - rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; - - if (rect->width < 16) - rect->width = 16; - if (rect->height < 16) - rect->height = 16; - if (rect->width > bounds->width) - rect->width = bounds->width; - if (rect->height > bounds->height) - rect->height = bounds->height; - if (rect->left < bounds->left) - rect->left = bounds->left; - if (rect->top < bounds->top) - rect->top = bounds->top; - if (rect->left + rect->width > bounds->left + bounds->width) - rect->left = bounds->left+bounds->width - rect->width; - if (rect->top + rect->height > bounds->top + bounds->height) - rect->top = bounds->top+bounds->height - rect->height; - - rect->width &= ~15L; - rect->height &= ~15L; - - if (ET61X251_PRESERVE_IMGSCALE) { - /* Calculate the actual scaling factor */ - u32 a, b; - a = rect->width * rect->height; - b = pix_format->width * pix_format->height; - scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; - } else - scale = 1; - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &crop, sizeof(crop))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - et61x251_release_buffers(cam); - - err = et61x251_set_crop(cam, rect); - if (s->set_crop) - err += s->set_crop(cam, rect); - err += et61x251_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - s->pix_format.width = rect->width/scale; - s->pix_format.height = rect->height/scale; - memcpy(&(s->_rect), rect, sizeof(*rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - et61x251_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - et61x251_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -et61x251_vidioc_enum_framesizes(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_frmsizeenum frmsize; - - if (copy_from_user(&frmsize, arg, sizeof(frmsize))) - return -EFAULT; - - if (frmsize.index != 0) - return -EINVAL; - - if (frmsize.pixel_format != V4L2_PIX_FMT_ET61X251 && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - - frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; - frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; - frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; - frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; - frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; - memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); - - if (copy_to_user(arg, &frmsize, sizeof(frmsize))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_fmtdesc fmtd; - - if (copy_from_user(&fmtd, arg, sizeof(fmtd))) - return -EFAULT; - - if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (fmtd.index == 0) { - strcpy(fmtd.description, "bayer rgb"); - fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; - } else if (fmtd.index == 1) { - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_ET61X251; - fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; - } else - return -EINVAL; - - fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); - - if (copy_to_user(arg, &fmtd, sizeof(fmtd))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_ET61X251) ? - 0 : V4L2_COLORSPACE_SRGB; - pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251) - ? 0 : (pfmt->width * pfmt->priv) / 8; - pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); - pfmt->field = V4L2_FIELD_NONE; - memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); - - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, - void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_format format; - struct v4l2_pix_format* pix; - struct v4l2_pix_format* pfmt = &(s->pix_format); - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_rect rect; - u8 scale; - const enum et61x251_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - pix = &(format.fmt.pix); - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memcpy(&rect, &(s->_rect), sizeof(rect)); - - { /* calculate the actual scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; - } - - rect.width = scale * pix->width; - rect.height = scale * pix->height; - - if (rect.width < 16) - rect.width = 16; - if (rect.height < 16) - rect.height = 16; - if (rect.width > bounds->left + bounds->width - rect.left) - rect.width = bounds->left + bounds->width - rect.left; - if (rect.height > bounds->top + bounds->height - rect.top) - rect.height = bounds->top + bounds->height - rect.top; - - rect.width &= ~15L; - rect.height &= ~15L; - - { /* adjust the scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; - } - - pix->width = rect.width / scale; - pix->height = rect.height / scale; - - if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ? - 0 : V4L2_COLORSPACE_SRGB; - pix->colorspace = pfmt->colorspace; - pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) - ? 0 : (pix->width * pix->priv) / 8; - pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); - pix->field = V4L2_FIELD_NONE; - - if (cmd == VIDIOC_TRY_FMT) { - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - return 0; - } - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_FMT failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &format, sizeof(format))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - et61x251_release_buffers(cam); - - err += et61x251_set_pix_format(cam, pix); - err += et61x251_set_crop(cam, &rect); - if (s->set_pix_format) - err += s->set_pix_format(cam, pix); - if (s->set_crop) - err += s->set_crop(cam, &rect); - err += et61x251_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - memcpy(pfmt, pix, sizeof(*pix)); - memcpy(&(s->_rect), &rect, sizeof(rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - et61x251_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - et61x251_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg) -{ - if (copy_to_user(arg, &cam->compression, - sizeof(cam->compression))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_jpegcompression jc; - const enum et61x251_stream_state stream = cam->stream; - int err = 0; - - if (copy_from_user(&jc, arg, sizeof(jc))) - return -EFAULT; - - if (jc.quality != 0 && jc.quality != 1) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - err += et61x251_set_compression(cam, &jc); - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " - "problems. To use the camera, close and open " - "%s again.", video_device_node_name(cam->v4ldev)); - return -EIO; - } - - cam->compression.quality = jc.quality; - - cam->stream = stream; - - return 0; -} - - -static int -et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_requestbuffers rb; - u32 i; - int err; - - if (copy_from_user(&rb, arg, sizeof(rb))) - return -EFAULT; - - if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb.memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (cam->io == IO_READ) { - DBG(3, "Close and open the device again to choose the mmap " - "I/O method"); - return -EBUSY; - } - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_REQBUFS failed. " - "Previous buffers are still mapped."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - et61x251_empty_framequeues(cam); - - et61x251_release_buffers(cam); - if (rb.count) - rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP); - - if (copy_to_user(arg, &rb, sizeof(rb))) { - et61x251_release_buffers(cam); - cam->io = IO_NONE; - return -EFAULT; - } - - cam->io = rb.count ? IO_MMAP : IO_NONE; - - return 0; -} - - -static int -et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); - - if (cam->frame[b.index].vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (cam->frame[b.index].state == F_DONE) - b.flags |= V4L2_BUF_FLAG_DONE; - else if (cam->frame[b.index].state != F_UNUSED) - b.flags |= V4L2_BUF_FLAG_QUEUED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - unsigned long lock_flags; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->frame[b.index].state != F_UNUSED) - return -EINVAL; - - cam->frame[b.index].state = F_QUEUED; - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - PDBGG("Frame #%lu queued", (unsigned long)b.index); - - return 0; -} - - -static int -et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp, - void __user * arg) -{ - struct v4l2_buffer b; - struct et61x251_frame_t *f; - unsigned long lock_flags; - long timeout; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) - return -EINVAL; - - if (list_empty(&cam->outqueue)) { - if (cam->stream == STREAM_OFF) - return -EINVAL; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - if (!timeout || (cam->state & DEV_MISCONFIGURED)) - return -EIO; - } - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame); - list_del(cam->outqueue.next); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - f->state = F_UNUSED; - - memcpy(&b, &f->buf, sizeof(b)); - if (f->vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); - - return 0; -} - - -static int -et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg) -{ - int type; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - cam->stream = STREAM_ON; - - DBG(3, "Stream on"); - - return 0; -} - - -static int -et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg) -{ - int type, err; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - et61x251_empty_framequeues(cam); - - DBG(3, "Stream off"); - - return 0; -} - - -static int -et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - - if (sp.parm.capture.readbuffers == 0) - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES) - sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - cam->nreadbuffers = sp.parm.capture.readbuffers; - - return 0; -} - - -static long et61x251_ioctl_v4l2(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct et61x251_device *cam = video_drvdata(filp); - - switch (cmd) { - - case VIDIOC_QUERYCAP: - return et61x251_vidioc_querycap(cam, arg); - - case VIDIOC_ENUMINPUT: - return et61x251_vidioc_enuminput(cam, arg); - - case VIDIOC_G_INPUT: - return et61x251_vidioc_g_input(cam, arg); - - case VIDIOC_S_INPUT: - return et61x251_vidioc_s_input(cam, arg); - - case VIDIOC_QUERYCTRL: - return et61x251_vidioc_query_ctrl(cam, arg); - - case VIDIOC_G_CTRL: - return et61x251_vidioc_g_ctrl(cam, arg); - - case VIDIOC_S_CTRL: - return et61x251_vidioc_s_ctrl(cam, arg); - - case VIDIOC_CROPCAP: - return et61x251_vidioc_cropcap(cam, arg); - - case VIDIOC_G_CROP: - return et61x251_vidioc_g_crop(cam, arg); - - case VIDIOC_S_CROP: - return et61x251_vidioc_s_crop(cam, arg); - - case VIDIOC_ENUM_FMT: - return et61x251_vidioc_enum_fmt(cam, arg); - - case VIDIOC_G_FMT: - return et61x251_vidioc_g_fmt(cam, arg); - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - return et61x251_vidioc_try_s_fmt(cam, cmd, arg); - - case VIDIOC_ENUM_FRAMESIZES: - return et61x251_vidioc_enum_framesizes(cam, arg); - - case VIDIOC_G_JPEGCOMP: - return et61x251_vidioc_g_jpegcomp(cam, arg); - - case VIDIOC_S_JPEGCOMP: - return et61x251_vidioc_s_jpegcomp(cam, arg); - - case VIDIOC_REQBUFS: - return et61x251_vidioc_reqbufs(cam, arg); - - case VIDIOC_QUERYBUF: - return et61x251_vidioc_querybuf(cam, arg); - - case VIDIOC_QBUF: - return et61x251_vidioc_qbuf(cam, arg); - - case VIDIOC_DQBUF: - return et61x251_vidioc_dqbuf(cam, filp, arg); - - case VIDIOC_STREAMON: - return et61x251_vidioc_streamon(cam, arg); - - case VIDIOC_STREAMOFF: - return et61x251_vidioc_streamoff(cam, arg); - - case VIDIOC_G_PARM: - return et61x251_vidioc_g_parm(cam, arg); - - case VIDIOC_S_PARM: - return et61x251_vidioc_s_parm(cam, arg); - - default: - return -ENOTTY; - - } -} - - -static long et61x251_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct et61x251_device *cam = video_drvdata(filp); - long err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - V4LDBG(3, "et61x251", cmd); - - err = et61x251_ioctl_v4l2(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - - return err; -} - - -static const struct v4l2_file_operations et61x251_fops = { - .owner = THIS_MODULE, - .open = et61x251_open, - .release = et61x251_release, - .unlocked_ioctl = et61x251_ioctl, - .read = et61x251_read, - .poll = et61x251_poll, - .mmap = et61x251_mmap, -}; - -/*****************************************************************************/ - -/* It exists a single interface only. We do not need to validate anything. */ -static int -et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct et61x251_device* cam; - static unsigned int dev_nr; - unsigned int i; - int err = 0; - - if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL))) - return -ENOMEM; - - cam->usbdev = udev; - - if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { - DBG(1, "kmalloc() failed"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->v4ldev = video_device_alloc())) { - DBG(1, "video_device_alloc() failed"); - err = -ENOMEM; - goto fail; - } - - DBG(2, "ET61X[12]51 PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); - - for (i = 0; et61x251_sensor_table[i]; i++) { - err = et61x251_sensor_table[i](cam); - if (!err) - break; - } - - if (!err) - DBG(2, "%s image sensor detected", cam->sensor.name); - else { - DBG(1, "No supported image sensor detected"); - err = -ENODEV; - goto fail; - } - - if (et61x251_init(cam)) { - DBG(1, "Initialization failed. I will retry on open()."); - cam->state |= DEV_MISCONFIGURED; - } - - strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera"); - cam->v4ldev->fops = &et61x251_fops; - cam->v4ldev->release = video_device_release; - cam->v4ldev->parent = &udev->dev; - video_set_drvdata(cam->v4ldev, cam); - - init_completion(&cam->probe); - - err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); - if (err) { - DBG(1, "V4L2 device registration failed"); - if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(1, "Free /dev/videoX node not found"); - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - complete_all(&cam->probe); - goto fail; - } - - DBG(2, "V4L2 device registered as %s", - video_device_node_name(cam->v4ldev)); - - cam->module_param.force_munmap = force_munmap[dev_nr]; - cam->module_param.frame_timeout = frame_timeout[dev_nr]; - - dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - -#ifdef CONFIG_VIDEO_ADV_DEBUG - err = et61x251_create_sysfs(cam); - if (!err) - DBG(2, "Optional device control through 'sysfs' " - "interface ready"); - else - DBG(2, "Failed to create 'sysfs' interface for optional " - "device controlling. Error #%d", err); -#else - DBG(2, "Optional device control through 'sysfs' interface disabled"); - DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " - "configuration option to enable it."); -#endif - - usb_set_intfdata(intf, cam); - kref_init(&cam->kref); - usb_get_dev(cam->usbdev); - - complete_all(&cam->probe); - - return 0; - -fail: - if (cam) { - kfree(cam->control_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - kfree(cam); - } - return err; -} - - -static void et61x251_usb_disconnect(struct usb_interface* intf) -{ - struct et61x251_device* cam; - - down_write(&et61x251_dev_lock); - - cam = usb_get_intfdata(intf); - - DBG(2, "Disconnecting %s...", cam->v4ldev->name); - - if (cam->users) { - DBG(2, "Device %s is open! Deregistration and memory " - "deallocation are deferred.", - video_device_node_name(cam->v4ldev)); - cam->state |= DEV_MISCONFIGURED; - et61x251_stop_transfer(cam); - cam->state |= DEV_DISCONNECTED; - wake_up_interruptible(&cam->wait_frame); - wake_up(&cam->wait_stream); - } else - cam->state |= DEV_DISCONNECTED; - - wake_up_interruptible_all(&cam->wait_open); - - kref_put(&cam->kref, et61x251_release_resources); - - up_write(&et61x251_dev_lock); -} - - -static struct usb_driver et61x251_usb_driver = { - .name = "et61x251", - .id_table = et61x251_id_table, - .probe = et61x251_usb_probe, - .disconnect = et61x251_usb_disconnect, -}; - -module_usb_driver(et61x251_usb_driver); diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h deleted file mode 100644 index 71a03148cb09..000000000000 --- a/drivers/media/video/et61x251/et61x251_sensor.h +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************** - * API for image sensors connected to ET61X[12]51 PC Camera Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _ET61X251_SENSOR_H_ -#define _ET61X251_SENSOR_H_ - -#include -#include -#include -#include -#include -#include - -struct et61x251_device; -struct et61x251_sensor; - -/*****************************************************************************/ - -extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam); - -#define ET61X251_SENSOR_TABLE \ -/* Weak detections must go at the end of the list */ \ -static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \ - &et61x251_probe_tas5130d1b, \ - NULL, \ -}; - -extern struct et61x251_device* -et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id); - -extern void -et61x251_attach_sensor(struct et61x251_device* cam, - const struct et61x251_sensor* sensor); - -/*****************************************************************************/ - -extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index); -extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1, - u8 data2, u8 data3, u8 data4, u8 data5, - u8 data6, u8 data7, u8 data8, u8 address); - -/*****************************************************************************/ - -enum et61x251_i2c_sysfs_ops { - ET61X251_I2C_READ = 0x01, - ET61X251_I2C_WRITE = 0x02, -}; - -enum et61x251_i2c_interface { - ET61X251_I2C_2WIRES, - ET61X251_I2C_3WIRES, -}; - -/* Repeat start condition when RSTA is high */ -enum et61x251_i2c_rsta { - ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */ - ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */ -}; - -#define ET61X251_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) - -struct et61x251_sensor { - char name[32]; - - enum et61x251_i2c_sysfs_ops sysfs_ops; - - enum et61x251_i2c_interface interface; - u8 i2c_slave_id; - enum et61x251_i2c_rsta rsta; - struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */ - - struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS]; - struct v4l2_cropcap cropcap; - struct v4l2_pix_format pix_format; - - int (*init)(struct et61x251_device* cam); - int (*get_ctrl)(struct et61x251_device* cam, - struct v4l2_control* ctrl); - int (*set_ctrl)(struct et61x251_device* cam, - const struct v4l2_control* ctrl); - int (*set_crop)(struct et61x251_device* cam, - const struct v4l2_rect* rect); - int (*set_pix_format)(struct et61x251_device* cam, - const struct v4l2_pix_format* pix); - - /* Private */ - struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS]; - struct v4l2_rect _rect; -}; - -#endif /* _ET61X251_SENSOR_H_ */ diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c deleted file mode 100644 index ced2e167935d..000000000000 --- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 * - * PC Camera Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "et61x251_sensor.h" - - -static int tas5130d1b_init(struct et61x251_device* cam) -{ - int err = 0; - - err += et61x251_write_reg(cam, 0x14, 0x01); - err += et61x251_write_reg(cam, 0x1b, 0x02); - err += et61x251_write_reg(cam, 0x02, 0x12); - err += et61x251_write_reg(cam, 0x0e, 0x60); - err += et61x251_write_reg(cam, 0x80, 0x61); - err += et61x251_write_reg(cam, 0xf0, 0x62); - err += et61x251_write_reg(cam, 0x03, 0x63); - err += et61x251_write_reg(cam, 0x14, 0x64); - err += et61x251_write_reg(cam, 0xf4, 0x65); - err += et61x251_write_reg(cam, 0x01, 0x66); - err += et61x251_write_reg(cam, 0x05, 0x67); - err += et61x251_write_reg(cam, 0x8f, 0x68); - err += et61x251_write_reg(cam, 0x0f, 0x8d); - err += et61x251_write_reg(cam, 0x08, 0x8e); - - return err; -} - - -static int tas5130d1b_set_ctrl(struct et61x251_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += et61x251_i2c_raw_write(cam, 2, 0x20, - 0xf6-ctrl->value, 0, 0, 0, - 0, 0, 0, 0); - break; - case V4L2_CID_EXPOSURE: - err += et61x251_i2c_raw_write(cam, 2, 0x40, - 0x47-ctrl->value, 0, 0, 0, - 0, 0, 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static const struct et61x251_sensor tas5130d1b = { - .name = "TAS5130D1B", - .interface = ET61X251_I2C_3WIRES, - .rsta = ET61X251_I2C_RSTA_STOP, - .active_pixel = { - .left = 106, - .top = 13, - }, - .init = &tas5130d1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x02, - .default_value = 0x0d, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x47, - .step = 0x01, - .default_value = 0x23, - .flags = 0, - }, - }, - .set_ctrl = &tas5130d1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, -}; - - -int et61x251_probe_tas5130d1b(struct et61x251_device* cam) -{ - const struct usb_device_id tas5130d1b_id_table[] = { - { USB_DEVICE(0x102c, 0x6251), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!et61x251_match_id(cam, tas5130d1b_id_table)) - return -ENODEV; - - et61x251_attach_sensor(cam, &tas5130d1b); - - return 0; -} -- cgit v1.2.3 From 6c493f8b28c6744995e92801a20dca192635dd22 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 29 Apr 2012 08:44:44 -0300 Subject: [media] cpia2: major overhaul to get it in a working state again This driver was severely broken. This patch makes it work again, and updates it to the latest V4L2 frameworks (except for videobuf2). It passes the v4l2-compliance tests and it now handles suspend/resume correctly. Several custom controls are replaced by new standard controls, only the USB_ALTERNATE control remains. Tested with the Hanse HVS-CM500PC USB microscope. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cpia2/cpia2.h | 34 +- drivers/media/video/cpia2/cpia2_core.c | 142 ++---- drivers/media/video/cpia2/cpia2_usb.c | 78 ++- drivers/media/video/cpia2/cpia2_v4l.c | 846 ++++++++++----------------------- drivers/media/video/cpia2/cpia2dev.h | 50 -- 5 files changed, 363 insertions(+), 787 deletions(-) delete mode 100644 drivers/media/video/cpia2/cpia2dev.h diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h index ab252188981b..cdef677d57ec 100644 --- a/drivers/media/video/cpia2/cpia2.h +++ b/drivers/media/video/cpia2/cpia2.h @@ -32,11 +32,12 @@ #define __CPIA2_H__ #include -#include #include #include +#include +#include +#include -#include "cpia2dev.h" #include "cpia2_registers.h" /* define for verbose debug output */ @@ -65,7 +66,6 @@ /* Flicker Modes */ #define NEVER_FLICKER 0 -#define ANTI_FLICKER_ON 1 #define FLICKER_60 60 #define FLICKER_50 50 @@ -148,7 +148,6 @@ enum { #define DEFAULT_BRIGHTNESS 0x46 #define DEFAULT_CONTRAST 0x93 #define DEFAULT_SATURATION 0x7f -#define DEFAULT_TARGET_KB 0x30 /* Power state */ #define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER @@ -287,7 +286,6 @@ struct camera_params { struct { u8 cam_register; u8 flicker_mode_req; /* 1 if flicker on, else never flicker */ - int mains_frequency; } flicker_control; struct { @@ -337,7 +335,7 @@ struct camera_params { u8 vc_control; u8 vc_mp_direction; u8 vc_mp_data; - u8 target_kb; + u8 quality; } vc_params; struct { @@ -366,23 +364,23 @@ struct framebuf { struct framebuf *next; }; -struct cpia2_fh { - enum v4l2_priority prio; - u8 mmapped; -}; - struct camera_data { /* locks */ + struct v4l2_device v4l2_dev; struct mutex v4l2_lock; /* serialize file operations */ - struct v4l2_prio_state prio; + struct v4l2_ctrl_handler hdl; + struct { + /* Lights control cluster */ + struct v4l2_ctrl *top_light; + struct v4l2_ctrl *bottom_light; + }; + struct v4l2_ctrl *usb_alt; /* camera status */ - volatile int present; /* Is the camera still present? */ - int open_count; /* # of process that have camera open */ int first_image_seen; - u8 mains_freq; /* for flicker control */ enum sensors sensor_type; u8 flush; + struct v4l2_fh *stream_fh; u8 mmapped; int streaming; /* 0 = no, 1 = yes */ int xfer_mode; /* XFER_BULK or XFER_ISOC */ @@ -390,7 +388,7 @@ struct camera_data { /* v4l */ int video_size; /* VIDEO_SIZE_ */ - struct video_device *vdev; /* v4l videodev */ + struct video_device vdev; /* v4l videodev */ u32 width; u32 height; /* Its size */ __u32 pixelformat; /* Format fourcc */ @@ -425,6 +423,7 @@ struct camera_data { /* v4l */ int cpia2_register_camera(struct camera_data *cam); void cpia2_unregister_camera(struct camera_data *cam); +void cpia2_camera_release(struct v4l2_device *v4l2_dev); /* core */ int cpia2_reset_camera(struct camera_data *cam); @@ -443,7 +442,7 @@ int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd); int cpia2_do_command(struct camera_data *cam, unsigned int command, unsigned char direction, unsigned char param); -struct camera_data *cpia2_init_camera_struct(void); +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf); int cpia2_init_camera(struct camera_data *cam); int cpia2_allocate_buffers(struct camera_data *cam); void cpia2_free_buffers(struct camera_data *cam); @@ -454,7 +453,6 @@ unsigned int cpia2_poll(struct camera_data *cam, int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); void cpia2_set_property_flip(struct camera_data *cam, int prop_val); void cpia2_set_property_mirror(struct camera_data *cam, int prop_val); -int cpia2_set_target_kb(struct camera_data *cam, unsigned char value); int cpia2_set_gpio(struct camera_data *cam, unsigned char setting); int cpia2_set_fps(struct camera_data *cam, int framerate); diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c index ee91e295c90a..17188e234770 100644 --- a/drivers/media/video/cpia2/cpia2_core.c +++ b/drivers/media/video/cpia2/cpia2_core.c @@ -66,7 +66,6 @@ static int config_sensor_410(struct camera_data *cam, static int config_sensor_500(struct camera_data *cam, int reqwidth, int reqheight); static int set_all_properties(struct camera_data *cam); -static void get_color_params(struct camera_data *cam); static void wake_system(struct camera_data *cam); static void set_lowlight_boost(struct camera_data *cam); static void reset_camera_struct(struct camera_data *cam); @@ -453,15 +452,6 @@ int cpia2_do_command(struct camera_data *cam, cam->params.version.vp_device_hi = cmd.buffer.block_data[0]; cam->params.version.vp_device_lo = cmd.buffer.block_data[1]; break; - case CPIA2_CMD_GET_VP_BRIGHTNESS: - cam->params.color_params.brightness = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_CONTRAST: - cam->params.color_params.contrast = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_SATURATION: - cam->params.color_params.saturation = cmd.buffer.block_data[0]; - break; case CPIA2_CMD_GET_VP_GPIO_DATA: cam->params.vp_params.gpio_data = cmd.buffer.block_data[0]; break; @@ -617,6 +607,7 @@ int cpia2_reset_camera(struct camera_data *cam) { u8 tmp_reg; int retval = 0; + int target_kb; int i; struct cpia2_command cmd; @@ -800,9 +791,16 @@ int cpia2_reset_camera(struct camera_data *cam) } cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg); - /* Set target size (kb) on vc */ + /* Set target size (kb) on vc + This is a heuristic based on the quality parameter and the raw + framesize in kB divided by 16 (the compression factor when the + quality is 100%) */ + target_kb = (cam->width * cam->height * 2 / 16384) * + cam->params.vc_params.quality / 100; + if (target_kb < 1) + target_kb = 1; cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB, - TRANSFER_WRITE, cam->params.vc_params.target_kb); + TRANSFER_WRITE, target_kb); /* Wiggle VC Reset */ /*** @@ -1538,23 +1536,17 @@ static int set_all_properties(struct camera_data *cam) * framerate and user_mode were already set (set_default_user_mode). **/ - cpia2_set_color_params(cam); - cpia2_usb_change_streaming_alternate(cam, cam->params.camera_state.stream_mode); - cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, - cam->params.vp_params.user_effects); - - cpia2_set_flicker_mode(cam, - cam->params.flicker_control.flicker_mode_req); - cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, TRANSFER_WRITE, cam->params.vp_params.gpio_direction); cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE, cam->params.vp_params.gpio_data); + v4l2_ctrl_handler_setup(&cam->hdl); + wake_system(cam); set_lowlight_boost(cam); @@ -1569,7 +1561,6 @@ static int set_all_properties(struct camera_data *cam) *****************************************************************************/ void cpia2_save_camera_state(struct camera_data *cam) { - get_color_params(cam); cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ, 0); @@ -1577,30 +1568,6 @@ void cpia2_save_camera_state(struct camera_data *cam) /* Don't get framerate or target_kb. Trust the values we already have */ } -/****************************************************************************** - * - * get_color_params - * - *****************************************************************************/ -static void get_color_params(struct camera_data *cam) -{ - cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST, TRANSFER_READ, 0); -} - -/****************************************************************************** - * - * cpia2_set_color_params - * - *****************************************************************************/ -void cpia2_set_color_params(struct camera_data *cam) -{ - DBG("Setting color params\n"); - cpia2_set_brightness(cam, cam->params.color_params.brightness); - cpia2_set_contrast(cam, cam->params.color_params.contrast); - cpia2_set_saturation(cam, cam->params.color_params.saturation); -} /****************************************************************************** * @@ -1664,15 +1631,9 @@ int cpia2_set_flicker_mode(struct camera_data *cam, int mode) switch(mode) { case NEVER_FLICKER: - cam->params.flicker_control.flicker_mode_req = mode; - break; case FLICKER_60: - cam->params.flicker_control.flicker_mode_req = mode; - cam->params.flicker_control.mains_frequency = 60; - break; case FLICKER_50: cam->params.flicker_control.flicker_mode_req = mode; - cam->params.flicker_control.mains_frequency = 50; break; default: err = -EINVAL; @@ -1701,6 +1662,7 @@ void cpia2_set_property_flip(struct camera_data *cam, int prop_val) { cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP; } + cam->params.vp_params.user_effects = cam_reg; cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, cam_reg); } @@ -1725,35 +1687,11 @@ void cpia2_set_property_mirror(struct camera_data *cam, int prop_val) { cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR; } + cam->params.vp_params.user_effects = cam_reg; cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, cam_reg); } -/****************************************************************************** - * - * set_target_kb - * - * The new Target KB is set in cam->params.vc_params.target_kb and - * activates on reset. - *****************************************************************************/ - -int cpia2_set_target_kb(struct camera_data *cam, unsigned char value) -{ - DBG("Requested target_kb = %d\n", value); - if (value != cam->params.vc_params.target_kb) { - - cpia2_usb_stream_pause(cam); - - /* reset camera for new target_kb */ - cam->params.vc_params.target_kb = value; - cpia2_reset_camera(cam); - - cpia2_usb_stream_resume(cam); - } - - return 0; -} - /****************************************************************************** * * cpia2_set_gpio @@ -1843,7 +1781,7 @@ void cpia2_set_brightness(struct camera_data *cam, unsigned char value) if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0) value++; DBG("Setting brightness to %d (0x%0x)\n", value, value); - cpia2_do_command(cam,CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE,value); + cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value); } /****************************************************************************** @@ -1854,7 +1792,6 @@ void cpia2_set_brightness(struct camera_data *cam, unsigned char value) void cpia2_set_contrast(struct camera_data *cam, unsigned char value) { DBG("Setting contrast to %d (0x%0x)\n", value, value); - cam->params.color_params.contrast = value; cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value); } @@ -1866,7 +1803,6 @@ void cpia2_set_contrast(struct camera_data *cam, unsigned char value) void cpia2_set_saturation(struct camera_data *cam, unsigned char value) { DBG("Setting saturation to %d (0x%0x)\n", value, value); - cam->params.color_params.saturation = value; cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value); } @@ -2168,14 +2104,10 @@ static void reset_camera_struct(struct camera_data *cam) /*** * The following parameter values are the defaults from the register map. ***/ - cam->params.color_params.brightness = DEFAULT_BRIGHTNESS; - cam->params.color_params.contrast = DEFAULT_CONTRAST; - cam->params.color_params.saturation = DEFAULT_SATURATION; cam->params.vp_params.lowlight_boost = 0; /* FlickerModes */ cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER; - cam->params.flicker_control.mains_frequency = 60; /* jpeg params */ cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT; @@ -2188,7 +2120,7 @@ static void reset_camera_struct(struct camera_data *cam) cam->params.vp_params.gpio_data = 0; /* Target kb params */ - cam->params.vc_params.target_kb = DEFAULT_TARGET_KB; + cam->params.vc_params.quality = 100; /*** * Set Sensor FPS as fast as possible. @@ -2228,7 +2160,7 @@ static void reset_camera_struct(struct camera_data *cam) * * Initializes camera struct, does not call reset to fill in defaults. *****************************************************************************/ -struct camera_data *cpia2_init_camera_struct(void) +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf) { struct camera_data *cam; @@ -2239,8 +2171,13 @@ struct camera_data *cpia2_init_camera_struct(void) return NULL; } + cam->v4l2_dev.release = cpia2_camera_release; + if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) { + v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n"); + kfree(cam); + return NULL; + } - cam->present = 1; mutex_init(&cam->v4l2_lock); init_waitqueue_head(&cam->wq_stream); @@ -2373,11 +2310,6 @@ long cpia2_read(struct camera_data *cam, return -EINVAL; } - if (!cam->present) { - LOG("%s: camera removed\n",__func__); - return 0; /* EOF */ - } - if (!cam->streaming) { /* Start streaming */ cpia2_usb_stream_start(cam, @@ -2393,12 +2325,12 @@ long cpia2_read(struct camera_data *cam, if (frame->status != FRAME_READY) { mutex_unlock(&cam->v4l2_lock); wait_event_interruptible(cam->wq_stream, - !cam->present || + !video_is_registered(&cam->vdev) || (frame = cam->curbuff)->status == FRAME_READY); mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - if (!cam->present) + if (!video_is_registered(&cam->vdev)) return 0; } @@ -2423,17 +2355,10 @@ long cpia2_read(struct camera_data *cam, unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_table *wait) { - unsigned int status=0; + unsigned int status = v4l2_ctrl_poll(filp, wait); - if (!cam) { - ERR("%s: Internal error, camera_data not found!\n",__func__); - return POLLERR; - } - - if (!cam->present) - return POLLHUP; - - if(!cam->streaming) { + if ((poll_requested_events(wait) & (POLLIN | POLLRDNORM)) && + !cam->streaming) { /* Start streaming */ cpia2_usb_stream_start(cam, cam->params.camera_state.stream_mode); @@ -2441,10 +2366,8 @@ unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_wait(filp, &cam->wq_stream, wait); - if(!cam->present) - status = POLLHUP; - else if(cam->curbuff->status == FRAME_READY) - status = POLLIN | POLLRDNORM; + if (cam->curbuff->status == FRAME_READY) + status |= POLLIN | POLLRDNORM; return status; } @@ -2462,12 +2385,9 @@ int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) unsigned long start = (unsigned long) adr; unsigned long page, pos; - if (!cam) - return -ENODEV; - DBG("mmap offset:%ld size:%ld\n", start_offset, size); - if (!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENODEV; if (size > cam->frame_size*cam->num_frames || diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c index 59c797c15277..95b5d6e7cdc4 100644 --- a/drivers/media/video/cpia2/cpia2_usb.c +++ b/drivers/media/video/cpia2/cpia2_usb.c @@ -54,6 +54,8 @@ static void cpia2_usb_complete(struct urb *urb); static int cpia2_usb_probe(struct usb_interface *intf, const struct usb_device_id *id); static void cpia2_usb_disconnect(struct usb_interface *intf); +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message); +static int cpia2_usb_resume(struct usb_interface *intf); static void free_sbufs(struct camera_data *cam); static void add_APPn(struct camera_data *cam); @@ -74,6 +76,9 @@ static struct usb_driver cpia2_driver = { .name = "cpia2", .probe = cpia2_usb_probe, .disconnect = cpia2_usb_disconnect, + .suspend = cpia2_usb_suspend, + .resume = cpia2_usb_resume, + .reset_resume = cpia2_usb_resume, .id_table = cpia2_id_table }; @@ -218,10 +223,9 @@ static void cpia2_usb_complete(struct urb *urb) return; } - if (!cam->streaming || !cam->present || cam->open_count == 0) { - LOG("Will now stop the streaming: streaming = %d, " - "present=%d, open_count=%d\n", - cam->streaming, cam->present, cam->open_count); + if (!cam->streaming || !video_is_registered(&cam->vdev)) { + LOG("Will now stop the streaming: streaming = %d, present=%d\n", + cam->streaming, video_is_registered(&cam->vdev)); return; } @@ -392,7 +396,7 @@ static int configure_transfer_mode(struct camera_data *cam, unsigned int alt) struct cpia2_command cmd; unsigned char reg; - if(!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENODEV; /*** @@ -752,8 +756,8 @@ int cpia2_usb_stream_pause(struct camera_data *cam) { int ret = 0; if(cam->streaming) { - ret = set_alternate(cam, USBIF_CMDONLY); free_sbufs(cam); + ret = set_alternate(cam, USBIF_CMDONLY); } return ret; } @@ -770,6 +774,10 @@ int cpia2_usb_stream_resume(struct camera_data *cam) cam->first_image_seen = 0; ret = set_alternate(cam, cam->params.camera_state.stream_mode); if(ret == 0) { + /* for some reason the user effects need to be set + again when starting streaming. */ + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam->params.vp_params.user_effects); ret = submit_urbs(cam); } } @@ -784,6 +792,7 @@ int cpia2_usb_stream_resume(struct camera_data *cam) int cpia2_usb_stream_stop(struct camera_data *cam) { int ret; + ret = cpia2_usb_stream_pause(cam); cam->streaming = 0; configure_transfer_mode(cam, 0); @@ -812,7 +821,8 @@ static int cpia2_usb_probe(struct usb_interface *intf, /* If we get to this point, we found a CPiA2 camera */ LOG("CPiA2 USB camera found\n"); - if((cam = cpia2_init_camera_struct()) == NULL) + cam = cpia2_init_camera_struct(intf); + if (cam == NULL) return -ENOMEM; cam->dev = udev; @@ -825,16 +835,9 @@ static int cpia2_usb_probe(struct usb_interface *intf, return ret; } - if ((ret = cpia2_register_camera(cam)) < 0) { - ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); - kfree(cam); - return ret; - } - if((ret = cpia2_init_camera(cam)) < 0) { ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret); - cpia2_unregister_camera(cam); kfree(cam); return ret; } @@ -853,6 +856,13 @@ static int cpia2_usb_probe(struct usb_interface *intf, usb_set_intfdata(intf, cam); + ret = cpia2_register_camera(cam); + if (ret < 0) { + ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); + kfree(cam); + return ret; + } + return 0; } @@ -865,13 +875,16 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) { struct camera_data *cam = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); - cam->present = 0; DBG("Stopping stream\n"); cpia2_usb_stream_stop(cam); + mutex_lock(&cam->v4l2_lock); DBG("Unregistering camera\n"); cpia2_unregister_camera(cam); + v4l2_device_disconnect(&cam->v4l2_dev); + mutex_unlock(&cam->v4l2_lock); + v4l2_device_put(&cam->v4l2_dev); if(cam->buffers) { DBG("Wakeup waiting processes\n"); @@ -884,14 +897,41 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) DBG("Releasing interface\n"); usb_driver_release_interface(&cpia2_driver, intf); - if (cam->open_count == 0) { - DBG("Freeing camera structure\n"); - kfree(cam); + LOG("CPiA2 camera disconnected.\n"); +} + +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + if (cam->streaming) { + cpia2_usb_stream_stop(cam); + cam->streaming = 1; } + mutex_unlock(&cam->v4l2_lock); - LOG("CPiA2 camera disconnected.\n"); + dev_info(&intf->dev, "going into suspend..\n"); + return 0; } +/* Resume device - start device. */ +static int cpia2_usb_resume(struct usb_interface *intf) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + v4l2_ctrl_handler_setup(&cam->hdl); + if (cam->streaming) { + cam->streaming = 0; + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "coming out of suspend..\n"); + return 0; +} /****************************************************************************** * diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 077eb1db80a1..bb4f1d0de829 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -39,15 +39,15 @@ #include #include #include +#include #include "cpia2.h" -#include "cpia2dev.h" static int video_nr = -1; module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr,"video device to register (0=/dev/video0, etc)"); +MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); -static int buffer_size = 68*1024; +static int buffer_size = 68 * 1024; module_param(buffer_size, int, 0); MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)"); @@ -62,18 +62,10 @@ MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-" __stringify(USBIF_ISO_6) ", default " __stringify(DEFAULT_ALT) ")"); -static int flicker_freq = 60; -module_param(flicker_freq, int, 0); -MODULE_PARM_DESC(flicker_freq, "Flicker frequency (" __stringify(50) "or" - __stringify(60) ", default " - __stringify(60) ")"); - -static int flicker_mode = NEVER_FLICKER; +static int flicker_mode; module_param(flicker_mode, int, 0); -MODULE_PARM_DESC(flicker_mode, - "Flicker supression (" __stringify(NEVER_FLICKER) "or" - __stringify(ANTI_FLICKER_ON) ", default " - __stringify(NEVER_FLICKER) ")"); +MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or " + __stringify(60) ", default 0)"); MODULE_AUTHOR("Steve Miller (STMicroelectronics) "); MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras"); @@ -82,153 +74,7 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(CPIA_VERSION); #define ABOUT "V4L-Driver for Vision CPiA2 based cameras" - -struct control_menu_info { - int value; - char name[32]; -}; - -static struct control_menu_info framerate_controls[] = -{ - { CPIA2_VP_FRAMERATE_6_25, "6.25 fps" }, - { CPIA2_VP_FRAMERATE_7_5, "7.5 fps" }, - { CPIA2_VP_FRAMERATE_12_5, "12.5 fps" }, - { CPIA2_VP_FRAMERATE_15, "15 fps" }, - { CPIA2_VP_FRAMERATE_25, "25 fps" }, - { CPIA2_VP_FRAMERATE_30, "30 fps" }, -}; -#define NUM_FRAMERATE_CONTROLS (ARRAY_SIZE(framerate_controls)) - -static struct control_menu_info flicker_controls[] = -{ - { NEVER_FLICKER, "Off" }, - { FLICKER_50, "50 Hz" }, - { FLICKER_60, "60 Hz" }, -}; -#define NUM_FLICKER_CONTROLS (ARRAY_SIZE(flicker_controls)) - -static struct control_menu_info lights_controls[] = -{ - { 0, "Off" }, - { 64, "Top" }, - { 128, "Bottom" }, - { 192, "Both" }, -}; -#define NUM_LIGHTS_CONTROLS (ARRAY_SIZE(lights_controls)) -#define GPIO_LIGHTS_MASK 192 - -static struct v4l2_queryctrl controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_BRIGHTNESS, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_CONTRAST, - }, - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_SATURATION, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_TARGET_KB, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Target KB", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_TARGET_KB, - }, - { - .id = CPIA2_CID_GPIO, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "GPIO", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_FLICKER_MODE, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Flicker Reduction", - .minimum = 0, - .maximum = NUM_FLICKER_CONTROLS-1, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_FRAMERATE, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Framerate", - .minimum = 0, - .maximum = NUM_FRAMERATE_CONTROLS-1, - .step = 1, - .default_value = NUM_FRAMERATE_CONTROLS-1, - }, - { - .id = CPIA2_CID_USB_ALT, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "USB Alternate", - .minimum = USBIF_ISO_1, - .maximum = USBIF_ISO_6, - .step = 1, - .default_value = DEFAULT_ALT, - }, - { - .id = CPIA2_CID_LIGHTS, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Lights", - .minimum = 0, - .maximum = NUM_LIGHTS_CONTROLS-1, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_RESET_CAMERA, - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Reset Camera", - .minimum = 0, - .maximum = 0, - .step = 0, - .default_value = 0, - }, -}; -#define NUM_CONTROLS (ARRAY_SIZE(controls)) - +#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000) /****************************************************************************** * @@ -238,38 +84,27 @@ static struct v4l2_queryctrl controls[] = { static int cpia2_open(struct file *file) { struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh; - - if (!cam) { - ERR("Internal error, camera_data not found!\n"); - return -ENODEV; - } + int retval = v4l2_fh_open(file); - if (!cam->present) - return -ENODEV; + if (retval) + return retval; - if (cam->open_count == 0) { - if (cpia2_allocate_buffers(cam)) + if (v4l2_fh_is_singular_file(file)) { + if (cpia2_allocate_buffers(cam)) { + v4l2_fh_release(file); return -ENOMEM; + } /* reset the camera */ - if (cpia2_reset_camera(cam) < 0) + if (cpia2_reset_camera(cam) < 0) { + v4l2_fh_release(file); return -EIO; + } cam->APP_len = 0; cam->COM_len = 0; } - fh = kmalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) - return -ENOMEM; - file->private_data = fh; - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&cam->prio, &fh->prio); - fh->mmapped = 0; - - ++cam->open_count; - cpia2_dbg_dump_registers(cam); return 0; } @@ -283,37 +118,22 @@ static int cpia2_close(struct file *file) { struct video_device *dev = video_devdata(file); struct camera_data *cam = video_get_drvdata(dev); - struct cpia2_fh *fh = file->private_data; - if (cam->present && - (cam->open_count == 1 || fh->prio == V4L2_PRIORITY_RECORD)) { + if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) { cpia2_usb_stream_stop(cam); - if (cam->open_count == 1) { - /* save camera state for later open */ - cpia2_save_camera_state(cam); + /* save camera state for later open */ + cpia2_save_camera_state(cam); - cpia2_set_low_power(cam); - cpia2_free_buffers(cam); - } + cpia2_set_low_power(cam); + cpia2_free_buffers(cam); } - if (fh->mmapped) + if (cam->stream_fh == file->private_data) { + cam->stream_fh = NULL; cam->mmapped = 0; - v4l2_prio_close(&cam->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - if (--cam->open_count == 0) { - cpia2_free_buffers(cam); - if (!cam->present) { - video_unregister_device(dev); - kfree(cam); - return 0; - } } - - return 0; + return v4l2_fh_release(file); } /****************************************************************************** @@ -327,16 +147,9 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, struct camera_data *cam = video_drvdata(file); int noblock = file->f_flags&O_NONBLOCK; - struct cpia2_fh *fh = file->private_data; - if(!cam) return -EINVAL; - /* Priority check */ - if(fh->prio != V4L2_PRIORITY_RECORD) { - return -EBUSY; - } - return cpia2_read(cam, buf, count, noblock); } @@ -349,15 +162,6 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) { struct camera_data *cam = video_drvdata(filp); - struct cpia2_fh *fh = filp->private_data; - - if(!cam) - return POLLERR; - - /* Priority check */ - if(fh->prio != V4L2_PRIORITY_RECORD) { - return POLLERR; - } return cpia2_poll(cam, filp, wait); } @@ -384,34 +188,11 @@ static int sync(struct camera_data *cam, int frame_nr) mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - if(!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENOTTY; } } -/****************************************************************************** - * - * ioctl_set_gpio - * - *****************************************************************************/ - -static long cpia2_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) -{ - struct camera_data *cam = video_drvdata(file); - __u32 gpio_val; - - if (cmd != CPIA2_CID_GPIO) - return -EINVAL; - - gpio_val = *(__u32*) arg; - - if (gpio_val &~ 0xFFU) - return -EINVAL; - - return cpia2_set_gpio(cam, (unsigned char)gpio_val); -} - /****************************************************************************** * * ioctl_querycap @@ -465,9 +246,11 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0) memset(vc->bus_info,0, sizeof(vc->bus_info)); - vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | + vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + vc->capabilities = vc->device_caps | + V4L2_CAP_DEVICE_CAPS; return 0; } @@ -610,22 +393,12 @@ static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh, struct v4l2_format *f) { struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh = _fh; int err, frame; - err = v4l2_prio_check(&cam->prio, fh->prio); - if (err) - return err; err = cpia2_try_fmt_vid_cap(file, _fh, f); if(err != 0) return err; - /* Ensure that only this process can change the format. */ - err = v4l2_prio_change(&cam->prio, &fh->prio, V4L2_PRIORITY_RECORD); - if(err != 0) { - return err; - } - cam->pixelformat = f->fmt.pix.pixelformat; /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle @@ -713,240 +486,126 @@ static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c) return 0; } -/****************************************************************************** - * - * ioctl_queryctrl - * - * V4L2 query possible control variables - * - *****************************************************************************/ +struct framerate_info { + int value; + struct v4l2_fract period; +}; -static int cpia2_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) +static const struct framerate_info framerate_controls[] = { + { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } }, + { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } }, + { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } }, + { CPIA2_VP_FRAMERATE_15, { 1, 15 } }, + { CPIA2_VP_FRAMERATE_25, { 1, 25 } }, + { CPIA2_VP_FRAMERATE_30, { 1, 30 } }, +}; + +static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p) { struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; int i; - for(i=0; iid == controls[i].id) { - memcpy(c, controls+i, sizeof(*c)); - break; - } - } - - if(i == NUM_CONTROLS) + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - /* Some devices have additional limitations */ - switch(c->id) { - case V4L2_CID_BRIGHTNESS: - /*** - * Don't let the register be set to zero - bug in VP4 - * flash of full brightness - ***/ - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - c->minimum = 1; - break; - case V4L2_CID_VFLIP: - // VP5 Only - if(cam->params.pnp_id.device_type == DEVICE_STV_672) - c->flags |= V4L2_CTRL_FLAG_DISABLED; - break; - case CPIA2_CID_FRAMERATE: - if(cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){ - // Maximum 15fps - for(i=0; imaximum; ++i) { - if(framerate_controls[i].value == - CPIA2_VP_FRAMERATE_15) { - c->maximum = i; - c->default_value = i; - } - } + cap->capability = V4L2_CAP_TIMEPERFRAME; + cap->readbuffers = cam->num_frames; + for (i = 0; i < ARRAY_SIZE(framerate_controls); i++) + if (cam->params.vp_params.frame_rate == framerate_controls[i].value) { + cap->timeperframe = framerate_controls[i].period; + break; } - break; - case CPIA2_CID_FLICKER_MODE: - // Flicker control only valid for 672. - if(cam->params.pnp_id.device_type != DEVICE_STV_672) - c->flags |= V4L2_CTRL_FLAG_DISABLED; - break; - case CPIA2_CID_LIGHTS: - // Light control only valid for the QX5 Microscope. - if(cam->params.pnp_id.product != 0x151) - c->flags |= V4L2_CTRL_FLAG_DISABLED; - break; - default: - break; - } - return 0; } -/****************************************************************************** - * - * ioctl_querymenu - * - * V4L2 query possible control variables - * - *****************************************************************************/ - -static int cpia2_querymenu(struct file *file, void *fh, struct v4l2_querymenu *m) +static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p) { struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; + struct v4l2_fract tpf = cap->timeperframe; + int max = ARRAY_SIZE(framerate_controls) - 1; + int ret; + int i; - switch(m->id) { - case CPIA2_CID_FLICKER_MODE: - if (m->index >= NUM_FLICKER_CONTROLS) - return -EINVAL; + ret = cpia2_g_parm(file, fh, p); + if (ret || !tpf.denominator || !tpf.numerator) + return ret; + + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + for (i = 0; i <= max; i++) { + struct v4l2_fract f1 = tpf; + struct v4l2_fract f2 = framerate_controls[i].period; + + f1.numerator *= f2.denominator; + f2.numerator *= f1.denominator; + if (f1.numerator >= f2.numerator) + break; + } + if (i > max) + i = max; + cap->timeperframe = framerate_controls[i].period; + return cpia2_set_fps(cam, framerate_controls[i].value); +} - strcpy(m->name, flicker_controls[m->index].name); - break; - case CPIA2_CID_FRAMERATE: - { - int maximum = NUM_FRAMERATE_CONTROLS - 1; - if(cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){ - // Maximum 15fps - int i; - for(i=0; iindex > maximum) - return -EINVAL; +static const struct { + u32 width; + u32 height; +} cpia2_framesizes[] = { + { 640, 480 }, + { 352, 288 }, + { 320, 240 }, + { 288, 216 }, + { 256, 192 }, + { 224, 168 }, + { 192, 144 }, + { 176, 144 }, +}; - strcpy(m->name, framerate_controls[m->index].name); - break; - } - case CPIA2_CID_LIGHTS: - if (m->index >= NUM_LIGHTS_CONTROLS) - return -EINVAL; +static int cpia2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ - strcpy(m->name, lights_controls[m->index].name); - break; - default: + if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG && + fsize->pixel_format != V4L2_PIX_FMT_JPEG) return -EINVAL; - } + if (fsize->index >= ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = cpia2_framesizes[fsize->index].width; + fsize->discrete.height = cpia2_framesizes[fsize->index].height; return 0; } -/****************************************************************************** - * - * ioctl_g_ctrl - * - * V4L2 get the value of a control variable - * - *****************************************************************************/ - -static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) +static int cpia2_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) { struct camera_data *cam = video_drvdata(file); + int max = ARRAY_SIZE(framerate_controls) - 1; + int i; - switch(c->id) { - case V4L2_CID_BRIGHTNESS: - cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, - TRANSFER_READ, 0); - c->value = cam->params.color_params.brightness; - break; - case V4L2_CID_CONTRAST: - cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST, - TRANSFER_READ, 0); - c->value = cam->params.color_params.contrast; - break; - case V4L2_CID_SATURATION: - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, - TRANSFER_READ, 0); - c->value = cam->params.color_params.saturation; - break; - case V4L2_CID_HFLIP: - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, - TRANSFER_READ, 0); - c->value = (cam->params.vp_params.user_effects & - CPIA2_VP_USER_EFFECTS_MIRROR) != 0; - break; - case V4L2_CID_VFLIP: - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, - TRANSFER_READ, 0); - c->value = (cam->params.vp_params.user_effects & - CPIA2_VP_USER_EFFECTS_FLIP) != 0; - break; - case CPIA2_CID_TARGET_KB: - c->value = cam->params.vc_params.target_kb; - break; - case CPIA2_CID_GPIO: - cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA, - TRANSFER_READ, 0); - c->value = cam->params.vp_params.gpio_data; - break; - case CPIA2_CID_FLICKER_MODE: - { - int i, mode; - cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES, - TRANSFER_READ, 0); - if(cam->params.flicker_control.cam_register & - CPIA2_VP_FLICKER_MODES_NEVER_FLICKER) { - mode = NEVER_FLICKER; - } else { - if(cam->params.flicker_control.cam_register & - CPIA2_VP_FLICKER_MODES_50HZ) { - mode = FLICKER_50; - } else { - mode = FLICKER_60; - } - } - for(i=0; ivalue = i; - break; - } - } - if(i == NUM_FLICKER_CONTROLS) - return -EINVAL; - break; - } - case CPIA2_CID_FRAMERATE: - { - int maximum = NUM_FRAMERATE_CONTROLS - 1; - int i; - for(i=0; i<= maximum; i++) { - if(cam->params.vp_params.frame_rate == - framerate_controls[i].value) - break; - } - if(i > maximum) - return -EINVAL; - c->value = i; - break; - } - case CPIA2_CID_USB_ALT: - c->value = cam->params.camera_state.stream_mode; - break; - case CPIA2_CID_LIGHTS: - { - int i; - cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA, - TRANSFER_READ, 0); - for(i=0; iparams.vp_params.gpio_data&GPIO_LIGHTS_MASK) == - lights_controls[i].value) { - break; - } - } - if(i == NUM_LIGHTS_CONTROLS) - return -EINVAL; - c->value = i; - break; - } - case CPIA2_CID_RESET_CAMERA: + if (fival->pixel_format != V4L2_PIX_FMT_MJPEG && + fival->pixel_format != V4L2_PIX_FMT_JPEG) return -EINVAL; - default: - return -EINVAL; - } - - DBG("Get control id:%d, value:%d\n", c->id, c->value); + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + if (fival->index > max) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++) + if (fival->width == cpia2_framesizes[i].width && + fival->height == cpia2_framesizes[i].height) + break; + if (i == ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = framerate_controls[fival->index].period; return 0; } @@ -958,72 +617,54 @@ static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) * *****************************************************************************/ -static int cpia2_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl) { - struct camera_data *cam = video_drvdata(file); - int i; - int retval = 0; + struct camera_data *cam = + container_of(ctrl->handler, struct camera_data, hdl); + static const int flicker_table[] = { + NEVER_FLICKER, + FLICKER_50, + FLICKER_60, + }; - DBG("Set control id:%d, value:%d\n", c->id, c->value); - - /* Check that the value is in range */ - for(i=0; iid == controls[i].id) { - if(c->value < controls[i].minimum || - c->value > controls[i].maximum) { - return -EINVAL; - } - break; - } - } - if(i == NUM_CONTROLS) - return -EINVAL; + DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val); - switch(c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - cpia2_set_brightness(cam, c->value); + cpia2_set_brightness(cam, ctrl->val); break; case V4L2_CID_CONTRAST: - cpia2_set_contrast(cam, c->value); + cpia2_set_contrast(cam, ctrl->val); break; case V4L2_CID_SATURATION: - cpia2_set_saturation(cam, c->value); + cpia2_set_saturation(cam, ctrl->val); break; case V4L2_CID_HFLIP: - cpia2_set_property_mirror(cam, c->value); + cpia2_set_property_mirror(cam, ctrl->val); break; case V4L2_CID_VFLIP: - cpia2_set_property_flip(cam, c->value); + cpia2_set_property_flip(cam, ctrl->val); break; - case CPIA2_CID_TARGET_KB: - retval = cpia2_set_target_kb(cam, c->value); + case V4L2_CID_POWER_LINE_FREQUENCY: + return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]); + case V4L2_CID_ILLUMINATORS_1: + return cpia2_set_gpio(cam, (cam->top_light->val << 6) | + (cam->bottom_light->val << 7)); + case V4L2_CID_JPEG_ACTIVE_MARKER: + cam->params.compression.inhibit_htables = + !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT); break; - case CPIA2_CID_GPIO: - retval = cpia2_set_gpio(cam, c->value); - break; - case CPIA2_CID_FLICKER_MODE: - retval = cpia2_set_flicker_mode(cam, - flicker_controls[c->value].value); - break; - case CPIA2_CID_FRAMERATE: - retval = cpia2_set_fps(cam, framerate_controls[c->value].value); + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + cam->params.vc_params.quality = ctrl->val; break; case CPIA2_CID_USB_ALT: - retval = cpia2_usb_change_streaming_alternate(cam, c->value); - break; - case CPIA2_CID_LIGHTS: - retval = cpia2_set_gpio(cam, lights_controls[c->value].value); - break; - case CPIA2_CID_RESET_CAMERA: - cpia2_usb_stream_pause(cam); - cpia2_reset_camera(cam); - cpia2_usb_stream_resume(cam); + cam->params.camera_state.stream_mode = ctrl->val; break; default: - retval = -EINVAL; + return -EINVAL; } - return retval; + return 0; } /****************************************************************************** @@ -1084,6 +725,8 @@ static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompres cam->params.compression.inhibit_htables = !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT); + parms->jpeg_markers &= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | + V4L2_JPEG_MARKER_DHT; if(parms->APP_len != 0) { if(parms->APP_len > 0 && @@ -1270,12 +913,12 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) struct framebuf *cb=cam->curbuff; mutex_unlock(&cam->v4l2_lock); wait_event_interruptible(cam->wq_stream, - !cam->present || + !video_is_registered(&cam->vdev) || (cb=cam->curbuff)->status == FRAME_READY); mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - if(!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENOTTY; frame = cb->num; } @@ -1299,56 +942,39 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) return 0; } -static int cpia2_g_priority(struct file *file, void *_fh, enum v4l2_priority *p) -{ - struct cpia2_fh *fh = _fh; - - *p = fh->prio; - return 0; -} - -static int cpia2_s_priority(struct file *file, void *_fh, enum v4l2_priority prio) -{ - struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh = _fh; - - if (cam->streaming && prio != fh->prio && - fh->prio == V4L2_PRIORITY_RECORD) - /* Can't drop record priority while streaming */ - return -EBUSY; - - if (prio == V4L2_PRIORITY_RECORD && prio != fh->prio && - v4l2_prio_max(&cam->prio) == V4L2_PRIORITY_RECORD) - /* Only one program can record at a time */ - return -EBUSY; - return v4l2_prio_change(&cam->prio, &fh->prio, prio); -} - static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (!cam->streaming) - return cpia2_usb_stream_start(cam, + if (!cam->streaming) { + ret = cpia2_usb_stream_start(cam, cam->params.camera_state.stream_mode); - return -EINVAL; + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, true); + } + return ret; } static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (cam->streaming) - return cpia2_usb_stream_stop(cam); - return -EINVAL; + if (cam->streaming) { + ret = cpia2_usb_stream_stop(cam); + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, false); + } + return ret; } /****************************************************************************** @@ -1361,16 +987,10 @@ static int cpia2_mmap(struct file *file, struct vm_area_struct *area) struct camera_data *cam = video_drvdata(file); int retval; - /* Priority check */ - struct cpia2_fh *fh = file->private_data; - if(fh->prio != V4L2_PRIORITY_RECORD) { - return -EBUSY; - } - retval = cpia2_remap_buffer(cam, area); if(!retval) - fh->mmapped = 1; + cam->stream_fh = file->private_data; return retval; } @@ -1388,15 +1008,13 @@ static void reset_camera_struct_v4l(struct camera_data *cam) cam->frame_size = buffer_size; cam->num_frames = num_buffers; - /* FlickerModes */ + /* Flicker modes */ cam->params.flicker_control.flicker_mode_req = flicker_mode; - cam->params.flicker_control.mains_frequency = flicker_freq; - /* streamMode */ + /* stream modes */ cam->params.camera_state.stream_mode = alternate; cam->pixelformat = V4L2_PIX_FMT_JPEG; - v4l2_prio_init(&cam->prio); } static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { @@ -1408,10 +1026,6 @@ static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap, - .vidioc_queryctrl = cpia2_queryctrl, - .vidioc_querymenu = cpia2_querymenu, - .vidioc_g_ctrl = cpia2_g_ctrl, - .vidioc_s_ctrl = cpia2_s_ctrl, .vidioc_g_jpegcomp = cpia2_g_jpegcomp, .vidioc_s_jpegcomp = cpia2_s_jpegcomp, .vidioc_cropcap = cpia2_cropcap, @@ -1421,9 +1035,12 @@ static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { .vidioc_dqbuf = cpia2_dqbuf, .vidioc_streamon = cpia2_streamon, .vidioc_streamoff = cpia2_streamoff, - .vidioc_g_priority = cpia2_g_priority, - .vidioc_s_priority = cpia2_s_priority, - .vidioc_default = cpia2_default, + .vidioc_s_parm = cpia2_s_parm, + .vidioc_g_parm = cpia2_g_parm, + .vidioc_enum_framesizes = cpia2_enum_framesizes, + .vidioc_enum_frameintervals = cpia2_enum_frameintervals, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /*** @@ -1444,7 +1061,21 @@ static struct video_device cpia2_template = { .name = "CPiA2 Camera", .fops = &cpia2_fops, .ioctl_ops = &cpia2_ioctl_ops, - .release = video_device_release, + .release = video_device_release_empty, +}; + +void cpia2_camera_release(struct v4l2_device *v4l2_dev) +{ + struct camera_data *cam = + container_of(v4l2_dev, struct camera_data, v4l2_dev); + + v4l2_ctrl_handler_free(&cam->hdl); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +static const struct v4l2_ctrl_ops cpia2_ctrl_ops = { + .s_ctrl = cpia2_s_ctrl, }; /****************************************************************************** @@ -1454,20 +1085,74 @@ static struct video_device cpia2_template = { *****************************************************************************/ int cpia2_register_camera(struct camera_data *cam) { - cam->vdev = video_device_alloc(); - if(!cam->vdev) - return -ENOMEM; + struct v4l2_ctrl_handler *hdl = &cam->hdl; + struct v4l2_ctrl_config cpia2_usb_alt = { + .ops = &cpia2_ctrl_ops, + .id = CPIA2_CID_USB_ALT, + .name = "USB Alternate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = USBIF_ISO_1, + .max = USBIF_ISO_6, + .step = 1, + }; + int ret; + + v4l2_ctrl_handler_init(hdl, 12); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_BRIGHTNESS, + cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0, + 255, 1, DEFAULT_BRIGHTNESS); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_ACTIVE_MARKER, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, + 100, 1, 100); + cpia2_usb_alt.def = alternate; + cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL); + /* VP5 Only */ + if (cam->params.pnp_id.device_type != DEVICE_STV_672) + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* Flicker control only valid for 672 */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); + /* Light control only valid for the QX5 Microscope */ + if (cam->params.pnp_id.product == 0x151) { + cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); + cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &cam->top_light); + } - memcpy(cam->vdev, &cpia2_template, sizeof(cpia2_template)); - video_set_drvdata(cam->vdev, cam); - cam->vdev->lock = &cam->v4l2_lock; + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + cam->vdev = cpia2_template; + video_set_drvdata(&cam->vdev, cam); + cam->vdev.lock = &cam->v4l2_lock; + cam->vdev.ctrl_handler = hdl; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); reset_camera_struct_v4l(cam); /* register v4l device */ - if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { ERR("video_register_device failed\n"); - video_device_release(cam->vdev); return -ENODEV; } @@ -1481,13 +1166,7 @@ int cpia2_register_camera(struct camera_data *cam) *****************************************************************************/ void cpia2_unregister_camera(struct camera_data *cam) { - if (!cam->open_count) { - video_unregister_device(cam->vdev); - } else { - LOG("%s removed while open, deferring " - "video_unregister_device\n", - video_device_node_name(cam->vdev)); - } + video_unregister_device(&cam->vdev); } /****************************************************************************** @@ -1524,23 +1203,12 @@ static void __init check_parameters(void) LOG("alternate specified is invalid, using %d\n", alternate); } - if (flicker_mode != NEVER_FLICKER && flicker_mode != ANTI_FLICKER_ON) { - flicker_mode = NEVER_FLICKER; + if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) { + flicker_mode = 0; LOG("Flicker mode specified is invalid, using %d\n", flicker_mode); } - if (flicker_freq != FLICKER_50 && flicker_freq != FLICKER_60) { - flicker_freq = FLICKER_60; - LOG("Flicker mode specified is invalid, using %d\n", - flicker_freq); - } - - if(video_nr < -1 || video_nr > 64) { - video_nr = -1; - LOG("invalid video_nr specified, must be -1 to 64\n"); - } - DBG("Using %d buffers, each %d bytes, alternate=%d\n", num_buffers, buffer_size, alternate); } diff --git a/drivers/media/video/cpia2/cpia2dev.h b/drivers/media/video/cpia2/cpia2dev.h deleted file mode 100644 index f66691fe5a35..000000000000 --- a/drivers/media/video/cpia2/cpia2dev.h +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** - * - * Filename: cpia2dev.h - * - * Copyright 2001, STMicrolectronics, Inc. - * - * Contact: steve.miller@st.com - * - * Description: - * This file provides definitions for applications wanting to use the - * cpia2 driver beyond the generic v4l capabilities. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - ****************************************************************************/ - -#ifndef CPIA2_DEV_HEADER -#define CPIA2_DEV_HEADER - -#include - -/*** - * The following defines are ioctl numbers based on video4linux private ioctls, - * which can range from 192 (BASE_VIDIOCPRIVATE) to 255. All of these take int - * args - */ -#define CPIA2_IOC_SET_GPIO _IOW('v', BASE_VIDIOC_PRIVATE + 17, __u32) - -/* V4L2 driver specific controls */ -#define CPIA2_CID_TARGET_KB (V4L2_CID_PRIVATE_BASE+0) -#define CPIA2_CID_GPIO (V4L2_CID_PRIVATE_BASE+1) -#define CPIA2_CID_FLICKER_MODE (V4L2_CID_PRIVATE_BASE+2) -#define CPIA2_CID_FRAMERATE (V4L2_CID_PRIVATE_BASE+3) -#define CPIA2_CID_USB_ALT (V4L2_CID_PRIVATE_BASE+4) -#define CPIA2_CID_LIGHTS (V4L2_CID_PRIVATE_BASE+5) -#define CPIA2_CID_RESET_CAMERA (V4L2_CID_PRIVATE_BASE+6) - -#endif -- cgit v1.2.3 From a22d85fea89744fad2cb215da1fe0c1ce226a613 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:45 -0300 Subject: [media] media/radio: use v4l2_ctrl_subscribe_event where possible Signed-off-by: Hans de Goede Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-isa.c | 10 +--------- drivers/media/radio/radio-keene.c | 14 +------------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c index c7450fb90d2b..3c0067de4324 100644 --- a/drivers/media/radio/radio-isa.c +++ b/drivers/media/radio/radio-isa.c @@ -150,14 +150,6 @@ static int radio_isa_log_status(struct file *file, void *priv) return 0; } -static int radio_isa_subscribe_event(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) -{ - if (sub->type == V4L2_EVENT_CTRL) - return v4l2_event_subscribe(fh, sub, 0); - return -EINVAL; -} - static const struct v4l2_ctrl_ops radio_isa_ctrl_ops = { .s_ctrl = radio_isa_s_ctrl, }; @@ -177,7 +169,7 @@ static const struct v4l2_ioctl_ops radio_isa_ioctl_ops = { .vidioc_g_frequency = radio_isa_g_frequency, .vidioc_s_frequency = radio_isa_s_frequency, .vidioc_log_status = radio_isa_log_status, - .vidioc_subscribe_event = radio_isa_subscribe_event, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index 62d32c4946f7..79adf3e654e5 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -271,18 +271,6 @@ static int keene_s_ctrl(struct v4l2_ctrl *ctrl) return -EINVAL; } -static int vidioc_subscribe_event(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_CTRL: - return v4l2_event_subscribe(fh, sub, 0); - default: - return -EINVAL; - } -} - - /* File system interface */ static const struct v4l2_file_operations usb_keene_fops = { .owner = THIS_MODULE, @@ -303,7 +291,7 @@ static const struct v4l2_ioctl_ops usb_keene_ioctl_ops = { .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -- cgit v1.2.3 From c53c2549333b340e2662dc64ec81323476b69a97 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:46 -0300 Subject: [media] v4l2-event: Add v4l2_subscribed_event_ops Just like with ctrl events, drivers may want to get called back on listener add / remove for other event types too. Rather then special casing all of this in subscribe / unsubscribe event it is better to use ops for this. Signed-off-by: Hans de Goede Acked-by: Hans Verkuil Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 28 +++++++++++---- drivers/media/video/ivtv/ivtv-ioctl.c | 2 +- drivers/media/video/omap3isp/ispccdc.c | 2 +- drivers/media/video/omap3isp/ispstat.c | 2 +- drivers/media/video/v4l2-ctrls.c | 2 +- drivers/media/video/v4l2-event.c | 54 ++++++++++++++++++++++------ drivers/usb/gadget/uvc_v4l2.c | 2 +- include/media/v4l2-event.h | 24 +++++++++---- 8 files changed, 86 insertions(+), 30 deletions(-) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index e3dfc268d9c1..369d4bc87828 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -945,21 +945,35 @@ fast. Useful functions: -- v4l2_event_queue() +void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) Queue events to video device. The driver's only responsibility is to fill in the type and the data fields. The other fields will be filled in by V4L2. -- v4l2_event_subscribe() +int v4l2_event_subscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub, unsigned elems, + const struct v4l2_subscribed_event_ops *ops) The video_device->ioctl_ops->vidioc_subscribe_event must check the driver is able to produce events with specified event id. Then it calls - v4l2_event_subscribe() to subscribe the event. The last argument is the - size of the event queue for this event. If it is 0, then the framework - will fill in a default value (this depends on the event type). + v4l2_event_subscribe() to subscribe the event. -- v4l2_event_unsubscribe() + The elems argument is the size of the event queue for this event. If it is 0, + then the framework will fill in a default value (this depends on the event + type). + + The ops argument allows the driver to specify a number of callbacks: + * add: called when a new listener gets added (subscribing to the same + event twice will only cause this callback to get called once) + * del: called when a listener stops listening + * replace: replace event 'old' with event 'new'. + * merge: merge event 'old' into event 'new'. + All 4 callbacks are optional, if you don't want to specify any callbacks + the ops argument itself maybe NULL. + +int v4l2_event_unsubscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) vidioc_unsubscribe_event in struct v4l2_ioctl_ops. A driver may use v4l2_event_unsubscribe() directly unless it wants to be involved in @@ -968,7 +982,7 @@ Useful functions: The special type V4L2_EVENT_ALL may be used to unsubscribe all events. The drivers may want to handle this in a special way. -- v4l2_event_pending() +int v4l2_event_pending(struct v4l2_fh *fh) Returns the number of pending events. Useful when implementing poll. diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index a151271f60e1..a7730fd4827f 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1469,7 +1469,7 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti case V4L2_EVENT_VSYNC: case V4L2_EVENT_EOS: case V4L2_EVENT_CTRL: - return v4l2_event_subscribe(fh, sub, 0); + return v4l2_event_subscribe(fh, sub, 0, NULL); default: return -EINVAL; } diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index eaabc27f0fa2..1f3c16d8f0b4 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -1703,7 +1703,7 @@ static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, if (sub->id != 0) return -EINVAL; - return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS); + return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS, NULL); } static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c index 11871ecc6d25..b8640be692f1 100644 --- a/drivers/media/video/omap3isp/ispstat.c +++ b/drivers/media/video/omap3isp/ispstat.c @@ -1032,7 +1032,7 @@ int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, if (sub->type != stat->event_type) return -EINVAL; - return v4l2_event_subscribe(fh, sub, STAT_NEVENTS); + return v4l2_event_subscribe(fh, sub, STAT_NEVENTS, NULL); } int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index c93a9796f1fb..91b197819fc2 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -2468,7 +2468,7 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscription *sub) { if (sub->type == V4L2_EVENT_CTRL) - return v4l2_event_subscribe(fh, sub, 0); + return v4l2_event_subscribe(fh, sub, 0, NULL); return -EINVAL; } EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index c26ad9637143..0ba2dfa86d07 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c @@ -120,6 +120,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e if (sev == NULL) return; + /* + * If the event has been added to the fh->subscribed list, but its + * add op has not completed yet elems will be 0, treat this as + * not being subscribed. + */ + if (!sev->elems) + return; + /* Increase event sequence number on fh. */ fh->sequence++; @@ -132,14 +140,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e sev->first = sev_pos(sev, 1); fh->navailable--; if (sev->elems == 1) { - if (sev->replace) { - sev->replace(&kev->event, ev); + if (sev->ops && sev->ops->replace) { + sev->ops->replace(&kev->event, ev); copy_payload = false; } - } else if (sev->merge) { + } else if (sev->ops && sev->ops->merge) { struct v4l2_kevent *second_oldest = sev->events + sev_pos(sev, 0); - sev->merge(&kev->event, &second_oldest->event); + sev->ops->merge(&kev->event, &second_oldest->event); } } @@ -208,8 +216,14 @@ static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new) new->u.ctrl.changes |= old->u.ctrl.changes; } +static const struct v4l2_subscribed_event_ops ctrl_ops = { + .replace = ctrls_replace, + .merge = ctrls_merge, +}; + int v4l2_event_subscribe(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub, unsigned elems) + struct v4l2_event_subscription *sub, unsigned elems, + const struct v4l2_subscribed_event_ops *ops) { struct v4l2_subscribed_event *sev, *found_ev; struct v4l2_ctrl *ctrl = NULL; @@ -236,10 +250,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, sev->id = sub->id; sev->flags = sub->flags; sev->fh = fh; - sev->elems = elems; + sev->ops = ops; if (ctrl) { - sev->replace = ctrls_replace; - sev->merge = ctrls_merge; + sev->ops = &ctrl_ops; } spin_lock_irqsave(&fh->vdev->fh_lock, flags); @@ -248,12 +261,27 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, list_add(&sev->list, &fh->subscribed); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */ - if (found_ev) + if (found_ev) { kfree(sev); - else if (ctrl) + return 0; /* Already listening */ + } + + if (sev->ops && sev->ops->add) { + int ret = sev->ops->add(sev); + if (ret) { + sev->ops = NULL; + v4l2_event_unsubscribe(fh, sub); + return ret; + } + } + + /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */ + if (ctrl) v4l2_ctrl_add_event(ctrl, sev); + /* Mark as ready for use */ + sev->elems = elems; + return 0; } EXPORT_SYMBOL_GPL(v4l2_event_subscribe); @@ -306,6 +334,10 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, } spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + + if (sev && sev->ops && sev->ops->del) + sev->ops->del(sev); + if (sev && sev->type == V4L2_EVENT_CTRL) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id); diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index f6e083b50191..90db5fe9c56e 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -296,7 +296,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) return -EINVAL; - return v4l2_event_subscribe(&handle->vfh, arg, 2); + return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL); } case VIDIOC_UNSUBSCRIBE_EVENT: diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index 5f14e8895ce2..88fa9a1e0df3 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -78,6 +78,19 @@ struct v4l2_kevent { struct v4l2_event event; }; +/** struct v4l2_subscribed_event_ops - Subscribed event operations. + * @add: Optional callback, called when a new listener is added + * @del: Optional callback, called when a listener stops listening + * @replace: Optional callback that can replace event 'old' with event 'new'. + * @merge: Optional callback that can merge event 'old' into event 'new'. + */ +struct v4l2_subscribed_event_ops { + int (*add)(struct v4l2_subscribed_event *sev); + void (*del)(struct v4l2_subscribed_event *sev); + void (*replace)(struct v4l2_event *old, const struct v4l2_event *new); + void (*merge)(const struct v4l2_event *old, struct v4l2_event *new); +}; + /** struct v4l2_subscribed_event - Internal struct representing a subscribed event. * @list: List node for the v4l2_fh->subscribed list. * @type: Event type. @@ -85,8 +98,7 @@ struct v4l2_kevent { * @flags: Copy of v4l2_event_subscription->flags. * @fh: Filehandle that subscribed to this event. * @node: List node that hooks into the object's event list (if there is one). - * @replace: Optional callback that can replace event 'old' with event 'new'. - * @merge: Optional callback that can merge event 'old' into event 'new'. + * @ops: v4l2_subscribed_event_ops * @elems: The number of elements in the events array. * @first: The index of the events containing the oldest available event. * @in_use: The number of queued events. @@ -99,10 +111,7 @@ struct v4l2_subscribed_event { u32 flags; struct v4l2_fh *fh; struct list_head node; - void (*replace)(struct v4l2_event *old, - const struct v4l2_event *new); - void (*merge)(const struct v4l2_event *old, - struct v4l2_event *new); + const struct v4l2_subscribed_event_ops *ops; unsigned elems; unsigned first; unsigned in_use; @@ -115,7 +124,8 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev); void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev); int v4l2_event_pending(struct v4l2_fh *fh); int v4l2_event_subscribe(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub, unsigned elems); + struct v4l2_event_subscription *sub, unsigned elems, + const struct v4l2_subscribed_event_ops *ops); int v4l2_event_unsubscribe(struct v4l2_fh *fh, struct v4l2_event_subscription *sub); void v4l2_event_unsubscribe_all(struct v4l2_fh *fh); -- cgit v1.2.3 From 3e366149b8957f809081e5f0f70d209175127e29 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:47 -0300 Subject: [media] v4l2-ctrls: Use v4l2_subscribed_event_ops Signed-off-by: Hans de Goede [hans.verkuil@cisco.com: Fix a locking bug] Acked-by: Hans Verkuil Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ivtv/ivtv-ioctl.c | 3 ++- drivers/media/video/v4l2-ctrls.c | 41 +++++++++++++++++++++++++++++------ drivers/media/video/v4l2-event.c | 39 --------------------------------- include/media/v4l2-ctrls.h | 7 +++--- 4 files changed, 39 insertions(+), 51 deletions(-) diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index a7730fd4827f..70cd802c9ca8 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1468,8 +1468,9 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti switch (sub->type) { case V4L2_EVENT_VSYNC: case V4L2_EVENT_EOS: - case V4L2_EVENT_CTRL: return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_CTRL: + return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops); default: return -EINVAL; } diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 91b197819fc2..ae544d870d7d 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -2424,9 +2424,13 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); -void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev) +static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev) { + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); + + if (ctrl == NULL) + return -EINVAL; + v4l2_ctrl_lock(ctrl); list_add_tail(&sev->node, &ctrl->ev_subs); if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS && @@ -2440,17 +2444,40 @@ void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl, v4l2_event_queue_fh(sev->fh, &ev); } v4l2_ctrl_unlock(ctrl); + return 0; } -EXPORT_SYMBOL(v4l2_ctrl_add_event); -void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev) +static void v4l2_ctrl_del_event(struct v4l2_subscribed_event *sev) { + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); + v4l2_ctrl_lock(ctrl); list_del(&sev->node); v4l2_ctrl_unlock(ctrl); } -EXPORT_SYMBOL(v4l2_ctrl_del_event); + +void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new) +{ + u32 old_changes = old->u.ctrl.changes; + + old->u.ctrl = new->u.ctrl; + old->u.ctrl.changes |= old_changes; +} +EXPORT_SYMBOL(v4l2_ctrl_replace); + +void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new) +{ + new->u.ctrl.changes |= old->u.ctrl.changes; +} +EXPORT_SYMBOL(v4l2_ctrl_merge); + +const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops = { + .add = v4l2_ctrl_add_event, + .del = v4l2_ctrl_del_event, + .replace = v4l2_ctrl_replace, + .merge = v4l2_ctrl_merge, +}; +EXPORT_SYMBOL(v4l2_ctrl_sub_ev_ops); int v4l2_ctrl_log_status(struct file *file, void *fh) { @@ -2468,7 +2495,7 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscription *sub) { if (sub->type == V4L2_EVENT_CTRL) - return v4l2_event_subscribe(fh, sub, 0, NULL); + return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops); return -EINVAL; } EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index 0ba2dfa86d07..60b4e2e9c874 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -203,30 +202,11 @@ int v4l2_event_pending(struct v4l2_fh *fh) } EXPORT_SYMBOL_GPL(v4l2_event_pending); -static void ctrls_replace(struct v4l2_event *old, const struct v4l2_event *new) -{ - u32 old_changes = old->u.ctrl.changes; - - old->u.ctrl = new->u.ctrl; - old->u.ctrl.changes |= old_changes; -} - -static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new) -{ - new->u.ctrl.changes |= old->u.ctrl.changes; -} - -static const struct v4l2_subscribed_event_ops ctrl_ops = { - .replace = ctrls_replace, - .merge = ctrls_merge, -}; - int v4l2_event_subscribe(struct v4l2_fh *fh, struct v4l2_event_subscription *sub, unsigned elems, const struct v4l2_subscribed_event_ops *ops) { struct v4l2_subscribed_event *sev, *found_ev; - struct v4l2_ctrl *ctrl = NULL; unsigned long flags; unsigned i; @@ -235,11 +215,6 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, if (elems < 1) elems = 1; - if (sub->type == V4L2_EVENT_CTRL) { - ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id); - if (ctrl == NULL) - return -EINVAL; - } sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL); if (!sev) @@ -251,9 +226,6 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, sev->flags = sub->flags; sev->fh = fh; sev->ops = ops; - if (ctrl) { - sev->ops = &ctrl_ops; - } spin_lock_irqsave(&fh->vdev->fh_lock, flags); found_ev = v4l2_event_subscribed(fh, sub->type, sub->id); @@ -275,10 +247,6 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, } } - /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */ - if (ctrl) - v4l2_ctrl_add_event(ctrl, sev); - /* Mark as ready for use */ sev->elems = elems; @@ -338,13 +306,6 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, if (sev && sev->ops && sev->ops->del) sev->ops->del(sev); - if (sev && sev->type == V4L2_EVENT_CTRL) { - struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id); - - if (ctrl) - v4l2_ctrl_del_event(ctrl, sev); - } - kfree(sev); return 0; diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 8920f8210eab..c6f6b4c2c5f2 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -491,10 +491,9 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl); int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val); /* Internal helper functions that deal with control events. */ -void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev); -void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev); +extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops; +void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new); +void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new); struct file; /* Can be used as a vidioc_log_status function that just dumps all controls -- cgit v1.2.3 From 57fb4a4831793de9e8dbdfc8dc5eb8796026d47e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:48 -0300 Subject: [media] uvcvideo: Fix a "ignoring return value of ‘__clear_user’" warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans de Goede Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_v4l2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 111bfff1640d..4ef21e9cb28c 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -1097,7 +1097,8 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, __put_user(kp->menu_count, &up->menu_count)) return -EFAULT; - __clear_user(up->reserved, sizeof(up->reserved)); + if (__clear_user(up->reserved, sizeof(up->reserved))) + return -EFAULT; if (kp->menu_count == 0) return 0; -- cgit v1.2.3 From 35f16741263fb50c82d5ade53ef9880a6b05f08a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:49 -0300 Subject: [media] uvcvideo: Refactor uvc_ctrl_get and query This is a preparation patch for adding ctrl event support. Signed-off-by: Hans de Goede Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 77 ++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 0efd3b10b353..d20d0de707d1 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -899,24 +899,13 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, return 0; } -int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, +static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, struct v4l2_queryctrl *v4l2_ctrl) { - struct uvc_control *ctrl; - struct uvc_control_mapping *mapping; struct uvc_menu_info *menu; unsigned int i; - int ret; - - ret = mutex_lock_interruptible(&chain->ctrl_mutex); - if (ret < 0) - return -ERESTARTSYS; - - ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); - if (ctrl == NULL) { - ret = -EINVAL; - goto done; - } memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); v4l2_ctrl->id = mapping->id; @@ -930,9 +919,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; if (!ctrl->cached) { - ret = uvc_ctrl_populate_cache(chain, ctrl); + int ret = uvc_ctrl_populate_cache(chain, ctrl); if (ret < 0) - goto done; + return ret; } if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) { @@ -954,19 +943,19 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, } } - goto done; + return 0; case V4L2_CTRL_TYPE_BOOLEAN: v4l2_ctrl->minimum = 0; v4l2_ctrl->maximum = 1; v4l2_ctrl->step = 1; - goto done; + return 0; case V4L2_CTRL_TYPE_BUTTON: v4l2_ctrl->minimum = 0; v4l2_ctrl->maximum = 0; v4l2_ctrl->step = 0; - goto done; + return 0; default: break; @@ -984,6 +973,27 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + return 0; +} + +int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + int ret; + + ret = mutex_lock_interruptible(&chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); + if (ctrl == NULL) { + ret = -EINVAL; + goto done; + } + + ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl); done: mutex_unlock(&chain->ctrl_mutex); return ret; @@ -1148,17 +1158,15 @@ done: return ret; } -int uvc_ctrl_get(struct uvc_video_chain *chain, - struct v4l2_ext_control *xctrl) +static int __uvc_ctrl_get(struct uvc_video_chain *chain, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 *value) { - struct uvc_control *ctrl; - struct uvc_control_mapping *mapping; struct uvc_menu_info *menu; unsigned int i; int ret; - ctrl = uvc_find_control(chain, xctrl->id, &mapping); - if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) return -EINVAL; if (!ctrl->loaded) { @@ -1172,14 +1180,14 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, ctrl->loaded = 1; } - xctrl->value = mapping->get(mapping, UVC_GET_CUR, + *value = mapping->get(mapping, UVC_GET_CUR, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { menu = mapping->menu_info; for (i = 0; i < mapping->menu_count; ++i, ++menu) { - if (menu->value == xctrl->value) { - xctrl->value = i; + if (menu->value == *value) { + *value = i; break; } } @@ -1188,6 +1196,19 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, return 0; } +int uvc_ctrl_get(struct uvc_video_chain *chain, + struct v4l2_ext_control *xctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + + ctrl = uvc_find_control(chain, xctrl->id, &mapping); + if (ctrl == NULL) + return -EINVAL; + + return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value); +} + int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl) { -- cgit v1.2.3 From cb74d482f81bf6a3ef3e29cb228c917e371773f2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:50 -0300 Subject: [media] uvcvideo: Move __uvc_ctrl_get() up This avoids the need for doing a forward declaration of __uvc_ctrl_get (which is a static function) in later patches in this series. Note to reviewers this patch does not change a single line of code, it just moves the function up in uvc_ctrl.c a bit. Signed-off-by: Hans de Goede Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 76 +++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index d20d0de707d1..0c27cc154eaf 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -899,6 +899,44 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, return 0; } +static int __uvc_ctrl_get(struct uvc_video_chain *chain, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 *value) +{ + struct uvc_menu_info *menu; + unsigned int i; + int ret; + + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) + return -EINVAL; + + if (!ctrl->loaded) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info.size); + if (ret < 0) + return ret; + + ctrl->loaded = 1; + } + + *value = mapping->get(mapping, UVC_GET_CUR, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == *value) { + *value = i; + break; + } + } + } + + return 0; +} + static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct uvc_control *ctrl, struct uvc_control_mapping *mapping, @@ -1158,44 +1196,6 @@ done: return ret; } -static int __uvc_ctrl_get(struct uvc_video_chain *chain, - struct uvc_control *ctrl, struct uvc_control_mapping *mapping, - s32 *value) -{ - struct uvc_menu_info *menu; - unsigned int i; - int ret; - - if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) - return -EINVAL; - - if (!ctrl->loaded) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, - chain->dev->intfnum, ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - if (ret < 0) - return ret; - - ctrl->loaded = 1; - } - - *value = mapping->get(mapping, UVC_GET_CUR, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); - - if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { - menu = mapping->menu_info; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { - if (menu->value == *value) { - *value = i; - break; - } - } - } - - return 0; -} - int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl) { -- cgit v1.2.3 From b4012002f3a3983ba2797fde1613691d7f287048 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:51 -0300 Subject: [media] uvcvideo: Add support for control events Signed-off-by: Hans de Goede Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 121 ++++++++++++++++++++++++++++++++++++- drivers/media/video/uvc/uvc_v4l2.c | 43 ++++++++++--- drivers/media/video/uvc/uvcvideo.h | 20 ++++-- 3 files changed, 170 insertions(+), 14 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 0c27cc154eaf..f15a437e5861 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "uvcvideo.h" @@ -1102,6 +1103,117 @@ done: return ret; } +/* -------------------------------------------------------------------------- + * Ctrl event handling + */ + +static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, + struct v4l2_event *ev, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_queryctrl v4l2_ctrl; + + __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl); + + memset(ev->reserved, 0, sizeof(ev->reserved)); + ev->type = V4L2_EVENT_CTRL; + ev->id = v4l2_ctrl.id; + ev->u.ctrl.value = value; + ev->u.ctrl.changes = changes; + ev->u.ctrl.type = v4l2_ctrl.type; + ev->u.ctrl.flags = v4l2_ctrl.flags; + ev->u.ctrl.minimum = v4l2_ctrl.minimum; + ev->u.ctrl.maximum = v4l2_ctrl.maximum; + ev->u.ctrl.step = v4l2_ctrl.step; + ev->u.ctrl.default_value = v4l2_ctrl.default_value; +} + +static void uvc_ctrl_send_event(struct uvc_fh *handle, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_subscribed_event *sev; + struct v4l2_event ev; + + if (list_empty(&mapping->ev_subs)) + return; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); + + list_for_each_entry(sev, &mapping->ev_subs, node) { + if (sev->fh && (sev->fh != &handle->vfh || + (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))) + v4l2_event_queue_fh(sev->fh, &ev); + } +} + +static void uvc_ctrl_send_events(struct uvc_fh *handle, + const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +{ + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + unsigned int i; + + for (i = 0; i < xctrls_count; ++i) { + ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, + V4L2_EVENT_CTRL_CH_VALUE); + } +} + +static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + int ret; + + ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(handle->chain, sev->id, &mapping); + if (ctrl == NULL) { + ret = -EINVAL; + goto done; + } + + list_add_tail(&sev->node, &mapping->ev_subs); + if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) { + struct v4l2_event ev; + u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; + s32 val = 0; + + if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + changes |= V4L2_EVENT_CTRL_CH_VALUE; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val, + changes); + v4l2_event_queue_fh(sev->fh, &ev); + } + +done: + mutex_unlock(&handle->chain->ctrl_mutex); + return ret; +} + +static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + + mutex_lock(&handle->chain->ctrl_mutex); + list_del(&sev->node); + mutex_unlock(&handle->chain->ctrl_mutex); +} + +const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = { + .add = uvc_ctrl_add_event, + .del = uvc_ctrl_del_event, + .replace = v4l2_ctrl_replace, + .merge = v4l2_ctrl_merge, +}; /* -------------------------------------------------------------------------- * Control transactions @@ -1179,8 +1291,11 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, return 0; } -int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) +int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) { + struct uvc_video_chain *chain = handle->chain; struct uvc_entity *entity; int ret = 0; @@ -1191,6 +1306,8 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) goto done; } + if (!rollback) + uvc_ctrl_send_events(handle, xctrls, xctrls_count); done: mutex_unlock(&chain->ctrl_mutex); return ret; @@ -1662,6 +1779,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, if (map == NULL) return -ENOMEM; + INIT_LIST_HEAD(&map->ev_subs); + size = sizeof(*mapping->menu_info) * mapping->menu_count; map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); if (map->menu_info == NULL) { diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 4ef21e9cb28c..a3322ed81e39 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include "uvcvideo.h" @@ -505,6 +507,8 @@ static int uvc_v4l2_open(struct file *file) } } + v4l2_fh_init(&handle->vfh, stream->vdev); + v4l2_fh_add(&handle->vfh); handle->chain = stream->chain; handle->stream = stream; handle->state = UVC_HANDLE_PASSIVE; @@ -528,6 +532,8 @@ static int uvc_v4l2_release(struct file *file) /* Release the file handle. */ uvc_dismiss_privileges(handle); + v4l2_fh_del(&handle->vfh); + v4l2_fh_exit(&handle->vfh); kfree(handle); file->private_data = NULL; @@ -584,7 +590,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) return ret; ret = uvc_ctrl_get(chain, &xctrl); - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); if (ret >= 0) ctrl->value = xctrl.value; break; @@ -605,10 +611,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ret = uvc_ctrl_set(chain, &xctrl); if (ret < 0) { - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); return ret; } - ret = uvc_ctrl_commit(chain); + ret = uvc_ctrl_commit(handle, &xctrl, 1); if (ret == 0) ctrl->value = xctrl.value; break; @@ -630,13 +636,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_ctrl_get(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); ctrls->error_idx = i; return ret; } } ctrls->error_idx = 0; - ret = uvc_ctrl_rollback(chain); + ret = uvc_ctrl_rollback(handle); break; } @@ -654,7 +660,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_ctrl_set(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); ctrls->error_idx = i; return ret; } @@ -663,9 +669,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ctrls->error_idx = 0; if (cmd == VIDIOC_S_EXT_CTRLS) - ret = uvc_ctrl_commit(chain); + ret = uvc_ctrl_commit(handle, + ctrls->controls, ctrls->count); else - ret = uvc_ctrl_rollback(chain); + ret = uvc_ctrl_rollback(handle); break; } @@ -990,6 +997,26 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) return uvc_video_enable(stream, 0); } + case VIDIOC_SUBSCRIBE_EVENT: + { + struct v4l2_event_subscription *sub = arg; + + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_event_subscribe(&handle->vfh, sub, 0, + &uvc_ctrl_sub_ev_ops); + default: + return -EINVAL; + } + } + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_event_unsubscribe(&handle->vfh, arg); + + case VIDIOC_DQEVENT: + return v4l2_event_dequeue(&handle->vfh, arg, + file->f_flags & O_NONBLOCK); + /* Analog video standards make no sense for digital cameras. */ case VIDIOC_ENUMSTD: case VIDIOC_QUERYSTD: diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 67f88d85bb16..e43deb715d98 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include /* -------------------------------------------------------------------------- @@ -153,6 +155,7 @@ struct uvc_control_info { struct uvc_control_mapping { struct list_head list; + struct list_head ev_subs; struct uvc_control_info *ctrl; @@ -524,6 +527,7 @@ enum uvc_handle_state { }; struct uvc_fh { + struct v4l2_fh vfh; struct uvc_video_chain *chain; struct uvc_streaming *stream; enum uvc_handle_state state; @@ -643,6 +647,8 @@ extern int uvc_status_suspend(struct uvc_device *dev); extern int uvc_status_resume(struct uvc_device *dev); /* Controls */ +extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; + extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl); extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain, @@ -655,14 +661,18 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern int uvc_ctrl_begin(struct uvc_video_chain *chain); -extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); -static inline int uvc_ctrl_commit(struct uvc_video_chain *chain) +extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count); +static inline int uvc_ctrl_commit(struct uvc_fh *handle, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) { - return __uvc_ctrl_commit(chain, 0); + return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count); } -static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain) +static inline int uvc_ctrl_rollback(struct uvc_fh *handle) { - return __uvc_ctrl_commit(chain, 1); + return __uvc_ctrl_commit(handle, 1, NULL, 0); } extern int uvc_ctrl_get(struct uvc_video_chain *chain, -- cgit v1.2.3 From 55afeb8a4d3ffdf8de546d5de37aca5b229ca9a4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:52 -0300 Subject: [media] uvcvideo: Properly report the inactive flag for inactive controls Note the unused in this patch slave_ids addition to the mappings will get used in a follow up patch to generate control change events for the slave ctrls when their flags change due to the master control changing value. Signed-off-by: Hans de Goede Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 33 +++++++++++++++++++++++++++++++++ drivers/media/video/uvc/uvcvideo.h | 4 ++++ 2 files changed, 37 insertions(+) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index f15a437e5861..ae7371f3a39e 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -421,6 +421,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_HUE_AUTO, + .master_manual = 0, }, { .id = V4L2_CID_SATURATION, @@ -493,6 +495,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_HUE, }, }, { .id = V4L2_CID_EXPOSURE_AUTO, @@ -505,6 +508,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .data_type = UVC_CTRL_DATA_TYPE_BITMASK, .menu_info = exposure_auto_controls, .menu_count = ARRAY_SIZE(exposure_auto_controls), + .slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, }, }, { .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, @@ -525,6 +529,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_EXPOSURE_AUTO, + .master_manual = V4L2_EXPOSURE_MANUAL, }, { .id = V4L2_CID_AUTO_WHITE_BALANCE, @@ -535,6 +541,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_WHITE_BALANCE_TEMPERATURE, }, }, { .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, @@ -545,6 +552,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, }, { .id = V4L2_CID_AUTO_WHITE_BALANCE, @@ -555,6 +564,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_BLUE_BALANCE, + V4L2_CID_RED_BALANCE }, }, { .id = V4L2_CID_BLUE_BALANCE, @@ -565,6 +576,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, }, { .id = V4L2_CID_RED_BALANCE, @@ -575,6 +588,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 16, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, }, { .id = V4L2_CID_FOCUS_ABSOLUTE, @@ -585,6 +600,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_FOCUS_AUTO, + .master_manual = 0, }, { .id = V4L2_CID_FOCUS_AUTO, @@ -595,6 +612,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_FOCUS_ABSOLUTE, }, }, { .id = V4L2_CID_IRIS_ABSOLUTE, @@ -943,6 +961,8 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct uvc_control_mapping *mapping, struct v4l2_queryctrl *v4l2_ctrl) { + struct uvc_control_mapping *master_map = NULL; + struct uvc_control *master_ctrl = NULL; struct uvc_menu_info *menu; unsigned int i; @@ -957,6 +977,19 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (mapping->master_id) + __uvc_find_control(ctrl->entity, mapping->master_id, + &master_map, &master_ctrl, 0); + if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) { + s32 val; + int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val); + if (ret < 0) + return ret; + + if (val != mapping->master_manual) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + } + if (!ctrl->cached) { int ret = uvc_ctrl_populate_cache(chain, ctrl); if (ret < 0) diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index e43deb715d98..777bb7505f8d 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -172,6 +172,10 @@ struct uvc_control_mapping { struct uvc_menu_info *menu_info; __u32 menu_count; + __u32 master_id; + __s32 master_manual; + __u32 slave_ids[2]; + __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query, const __u8 *data); void (*set) (struct uvc_control_mapping *mapping, __s32 value, -- cgit v1.2.3 From 805e9b4a06bf874c56e0811e9cca5e25cf465e42 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:53 -0300 Subject: [media] uvcvideo: Send control change events for slave ctrls when the master changes This allows v4l2 control UI-s to update the inactive state (ie grey-ing out of controls) for slave controls when the master control changes. [Use __uvc_find_control() to find slave controls, as they're always located in the same entity as the corresponding master control] Signed-off-by: Hans de Goede Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 58 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index ae7371f3a39e..03212c703330 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1177,22 +1177,76 @@ static void uvc_ctrl_send_event(struct uvc_fh *handle, list_for_each_entry(sev, &mapping->ev_subs, node) { if (sev->fh && (sev->fh != &handle->vfh || - (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))) + (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || + (changes & V4L2_EVENT_CTRL_CH_FLAGS))) v4l2_event_queue_fh(sev->fh, &ev); } } +static void uvc_ctrl_send_slave_event(struct uvc_fh *handle, + struct uvc_control *master, u32 slave_id, + const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +{ + struct uvc_control_mapping *mapping = NULL; + struct uvc_control *ctrl = NULL; + u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; + unsigned int i; + s32 val = 0; + + /* + * We can skip sending an event for the slave if the slave + * is being modified in the same transaction. + */ + for (i = 0; i < xctrls_count; i++) { + if (xctrls[i].id == slave_id) + return; + } + + __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0); + if (ctrl == NULL) + return; + + if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + changes |= V4L2_EVENT_CTRL_CH_VALUE; + + uvc_ctrl_send_event(handle, ctrl, mapping, val, changes); +} + static void uvc_ctrl_send_events(struct uvc_fh *handle, const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) { struct uvc_control_mapping *mapping; struct uvc_control *ctrl; + u32 changes = V4L2_EVENT_CTRL_CH_VALUE; unsigned int i; + unsigned int j; for (i = 0; i < xctrls_count; ++i) { ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + + for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) { + if (!mapping->slave_ids[j]) + break; + uvc_ctrl_send_slave_event(handle, ctrl, + mapping->slave_ids[j], + xctrls, xctrls_count); + } + + /* + * If the master is being modified in the same transaction + * flags may change too. + */ + if (mapping->master_id) { + for (j = 0; j < xctrls_count; j++) { + if (xctrls[j].id == mapping->master_id) { + changes |= V4L2_EVENT_CTRL_CH_FLAGS; + break; + } + } + } + uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, - V4L2_EVENT_CTRL_CH_VALUE); + changes); } } -- cgit v1.2.3 From d1c07ef1135310940f4f49765f7e385aa847cff5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Apr 2012 12:59:54 -0300 Subject: [media] uvcvideo: Drop unused ctrl member from struct uvc_control_mapping Signed-off-by: Hans de Goede Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 1 - drivers/media/video/uvc/uvcvideo.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 03212c703330..28363b72ff8a 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1880,7 +1880,6 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, if (map->set == NULL) map->set = uvc_set_le_value; - map->ctrl = &ctrl->info; list_add_tail(&map->list, &ctrl->info.mappings); uvc_trace(UVC_TRACE_CONTROL, "Adding mapping '%s' to control %pUl/%u.\n", diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 777bb7505f8d..7c3d082505b7 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -157,8 +157,6 @@ struct uvc_control_mapping { struct list_head list; struct list_head ev_subs; - struct uvc_control_info *ctrl; - __u32 id; __u8 name[32]; __u8 entity[16]; -- cgit v1.2.3 From 5f1a12f5d03ecd920e09c08e1b9fc1312a1d6da4 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 20 Feb 2012 02:03:39 -0300 Subject: [media] pvrusb2: Stop statically initializing reserved struct fields to zero In any statically initialized data structure, the compiler is going to zero any part which is not already explicitly initialized. While we can take that knowledge overboard and simply avoid initializing anything that needs to be zero, it's at least a good idea not to bother zeroing parts that we don't otherwise care about - like "reserved" fields which may change in the future. Avoiding initialization of those fields now also avoids possible conflict down the road. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index e1111d968a3d..5e462faa2726 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -107,7 +107,6 @@ static struct v4l2_fmtdesc pvr_fmtdesc [] = { // This should really be V4L2_PIX_FMT_MPEG, but xawtv // breaks when I do that. .pixelformat = 0, // V4L2_PIX_FMT_MPEG, - .reserved = { 0, 0, 0, 0 } } }; @@ -145,7 +144,6 @@ static struct v4l2_format pvr_format [] = { .start = { 0, 0 }, .count = { 0, 0 }, .flags = 0, - .reserved = { 0, 0 } } } } -- cgit v1.2.3 From c6d26cfe26e475c9ddd09667dd6694b34469f4dc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Feb 2012 02:21:00 -0300 Subject: [media] pvrusb2: convert to video_ioctl2 Note: there is one FIXME remaining: the tvnorms field of struct video_device should be set up correctly. I have used V4L2_STD_ALL for now, but I'm sure this can be improved. This field is used by video_ioctl2 to implement ENUMSTD. Signed-off-by: Hans Verkuil Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 1318 ++++++++++++++-------------- 1 file changed, 679 insertions(+), 639 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 5e462faa2726..83bc4675ec2a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -150,733 +150,719 @@ static struct v4l2_format pvr_format [] = { }; + /* - * pvr_ioctl() - * - * This is part of Video 4 Linux API. The procedure handles ioctl() calls. - * + * This is part of Video 4 Linux API. These procedures handle ioctl() calls. */ -static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_v4l2 *vp = fh->vhead; - struct pvr2_v4l2_dev *pdi = fh->pdi; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - long ret = -EINVAL; - - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),cmd); - } - if (!pvr2_hdw_dev_ok(hdw)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "ioctl failed - bad or no context"); - return -EFAULT; - } - - /* check priority */ - switch (cmd) { - case VIDIOC_S_CTRL: - case VIDIOC_S_STD: - case VIDIOC_S_INPUT: - case VIDIOC_S_TUNER: - case VIDIOC_S_FREQUENCY: - ret = v4l2_prio_check(&vp->prio, fh->prio); - if (ret) - return ret; - } + memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); + strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw), + sizeof(cap->bus_info)); + strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card)); + return 0; +} - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; +static int pvr2_g_priority(struct file *file, void *priv, enum v4l2_priority *p) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; - memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); - strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw), - sizeof(cap->bus_info)); - strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card)); + *p = v4l2_prio_max(&vp->prio); + return 0; +} - ret = 0; - break; - } +static int pvr2_s_priority(struct file *file, void *priv, enum v4l2_priority prio) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; - case VIDIOC_G_PRIORITY: - { - enum v4l2_priority *p = arg; + return v4l2_prio_change(&vp->prio, &fh->prio, prio); +} - *p = v4l2_prio_max(&vp->prio); - ret = 0; - break; - } +static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *prio = arg; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val); + *std = val; + return ret; +} - ret = v4l2_prio_change(&vp->prio, &fh->prio, *prio); - break; - } +int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *vs = (struct v4l2_standard *)arg; - int idx = vs->index; - ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1); - break; - } + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std); +} - case VIDIOC_QUERYSTD: - { - v4l2_std_id *std = arg; - *std = V4L2_STD_ALL; - ret = pvr2_hdw_get_detected_std(hdw, std); - break; - } +static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *cptr; + struct v4l2_input tmp; + unsigned int cnt; + int val; + int ret; - case VIDIOC_G_STD: - { - int val = 0; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val); - *(v4l2_std_id *)arg = val; + cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + + memset(&tmp, 0, sizeof(tmp)); + tmp.index = vi->index; + ret = 0; + if (vi->index >= fh->input_cnt) + return -EINVAL; + val = fh->input_map[vi->index]; + switch (val) { + case PVR2_CVAL_INPUT_TV: + case PVR2_CVAL_INPUT_DTV: + case PVR2_CVAL_INPUT_RADIO: + tmp.type = V4L2_INPUT_TYPE_TUNER; break; - } - - case VIDIOC_S_STD: - { - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR), - *(v4l2_std_id *)arg); + case PVR2_CVAL_INPUT_SVIDEO: + case PVR2_CVAL_INPUT_COMPOSITE: + tmp.type = V4L2_INPUT_TYPE_CAMERA; break; + default: + return -EINVAL; } - case VIDIOC_ENUMINPUT: - { - struct pvr2_ctrl *cptr; - struct v4l2_input *vi = (struct v4l2_input *)arg; - struct v4l2_input tmp; - unsigned int cnt; - int val; - - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - - memset(&tmp,0,sizeof(tmp)); - tmp.index = vi->index; - ret = 0; - if (vi->index >= fh->input_cnt) { - ret = -EINVAL; - break; - } - val = fh->input_map[vi->index]; - switch (val) { - case PVR2_CVAL_INPUT_TV: - case PVR2_CVAL_INPUT_DTV: - case PVR2_CVAL_INPUT_RADIO: - tmp.type = V4L2_INPUT_TYPE_TUNER; - break; - case PVR2_CVAL_INPUT_SVIDEO: - case PVR2_CVAL_INPUT_COMPOSITE: - tmp.type = V4L2_INPUT_TYPE_CAMERA; - break; - default: - ret = -EINVAL; - break; - } - if (ret < 0) break; - - cnt = 0; - pvr2_ctrl_get_valname(cptr,val, - tmp.name,sizeof(tmp.name)-1,&cnt); - tmp.name[cnt] = 0; - - /* Don't bother with audioset, since this driver currently - always switches the audio whenever the video is - switched. */ - - /* Handling std is a tougher problem. It doesn't make - sense in cases where a device might be multi-standard. - We could just copy out the current value for the - standard, but it can change over time. For now just - leave it zero. */ + cnt = 0; + pvr2_ctrl_get_valname(cptr, val, + tmp.name, sizeof(tmp.name) - 1, &cnt); + tmp.name[cnt] = 0; - memcpy(vi, &tmp, sizeof(tmp)); + /* Don't bother with audioset, since this driver currently + always switches the audio whenever the video is + switched. */ - ret = 0; - break; - } + /* Handling std is a tougher problem. It doesn't make + sense in cases where a device might be multi-standard. + We could just copy out the current value for the + standard, but it can change over time. For now just + leave it zero. */ + *vi = tmp; + return 0; +} - case VIDIOC_G_INPUT: - { - unsigned int idx; - struct pvr2_ctrl *cptr; - struct v4l2_input *vi = (struct v4l2_input *)arg; - int val; - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - val = 0; - ret = pvr2_ctrl_get_value(cptr,&val); - vi->index = 0; - for (idx = 0; idx < fh->input_cnt; idx++) { - if (fh->input_map[idx] == val) { - vi->index = idx; - break; - } - } - break; - } +static int pvr2_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned int idx; + struct pvr2_ctrl *cptr; + int val; + int ret; - case VIDIOC_S_INPUT: - { - struct v4l2_input *vi = (struct v4l2_input *)arg; - if (vi->index >= fh->input_cnt) { - ret = -ERANGE; + cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + val = 0; + ret = pvr2_ctrl_get_value(cptr, &val); + *i = 0; + for (idx = 0; idx < fh->input_cnt; idx++) { + if (fh->input_map[idx] == val) { + *i = idx; break; } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), - fh->input_map[vi->index]); - break; } + return ret; +} - case VIDIOC_ENUMAUDIO: - { - /* pkt: FIXME: We are returning one "fake" input here - which could very well be called "whatever_we_like". - This is for apps that want to see an audio input - just to feel comfortable, as well as to test if - it can do stereo or sth. There is actually no guarantee - that the actual audio input cannot change behind the app's - back, but most applications should not mind that either. - - Hopefully, mplayer people will work with us on this (this - whole mess is to support mplayer pvr://), or Hans will come - up with a more standard way to say "we have inputs but we - don 't want you to change them independent of video" which - will sort this mess. - */ - struct v4l2_audio *vin = arg; - ret = -EINVAL; - if (vin->index > 0) break; - strncpy(vin->name, "PVRUSB2 Audio",14); - vin->capability = V4L2_AUDCAP_STEREO; - ret = 0; - break; - break; - } +static int pvr2_s_input(struct file *file, void *priv, unsigned int inp) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - case VIDIOC_G_AUDIO: - { - /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ - struct v4l2_audio *vin = arg; - memset(vin,0,sizeof(*vin)); - vin->index = 0; - strncpy(vin->name, "PVRUSB2 Audio",14); - vin->capability = V4L2_AUDCAP_STEREO; - ret = 0; - break; - } + if (inp >= fh->input_cnt) + return -EINVAL; + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), + fh->input_map[inp]); +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; +static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin) +{ + /* pkt: FIXME: We are returning one "fake" input here + which could very well be called "whatever_we_like". + This is for apps that want to see an audio input + just to feel comfortable, as well as to test if + it can do stereo or sth. There is actually no guarantee + that the actual audio input cannot change behind the app's + back, but most applications should not mind that either. + + Hopefully, mplayer people will work with us on this (this + whole mess is to support mplayer pvr://), or Hans will come + up with a more standard way to say "we have inputs but we + don 't want you to change them independent of video" which + will sort this mess. + */ + + if (vin->index > 0) + return -EINVAL; + strncpy(vin->name, "PVRUSB2 Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; + return 0; +} - if (vt->index != 0) break; /* Only answer for the 1st tuner */ +static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin) +{ + /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ + vin->index = 0; + strncpy(vin->name, "PVRUSB2 Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; + return 0; +} - pvr2_hdw_execute_tuner_poll(hdw); - ret = pvr2_hdw_get_tuner_status(hdw,vt); - break; - } +static int pvr2_s_audio(struct file *file, void *priv, struct v4l2_audio *vout) +{ + if (vout->index) + return -EINVAL; + return 0; +} - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; +static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - if (vt->index != 0) - break; + if (vt->index != 0) + return -EINVAL; /* Only answer for the 1st tuner */ - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), + pvr2_hdw_execute_tuner_poll(hdw); + return pvr2_hdw_get_tuner_status(hdw, vt); +} + +static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (vt->index != 0) + return -EINVAL; + + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE), vt->audmode); - break; - } +} - case VIDIOC_S_FREQUENCY: - { - const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - unsigned long fv; - struct v4l2_tuner vt; - int cur_input; - struct pvr2_ctrl *ctrlp; - ret = pvr2_hdw_get_tuner_status(hdw,&vt); - if (ret != 0) break; - ctrlp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - ret = pvr2_ctrl_get_value(ctrlp,&cur_input); - if (ret != 0) break; - if (vf->type == V4L2_TUNER_RADIO) { - if (cur_input != PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(ctrlp, - PVR2_CVAL_INPUT_RADIO); - } - } else { - if (cur_input == PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(ctrlp, - PVR2_CVAL_INPUT_TV); - } - } - fv = vf->frequency; - if (vt.capability & V4L2_TUNER_CAP_LOW) { - fv = (fv * 125) / 2; - } else { - fv = fv * 62500; - } - ret = pvr2_ctrl_set_value( +int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned long fv; + struct v4l2_tuner vt; + int cur_input; + struct pvr2_ctrl *ctrlp; + int ret; + + ret = pvr2_hdw_get_tuner_status(hdw, &vt); + if (ret != 0) + return ret; + ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + ret = pvr2_ctrl_get_value(ctrlp, &cur_input); + if (ret != 0) + return ret; + if (vf->type == V4L2_TUNER_RADIO) { + if (cur_input != PVR2_CVAL_INPUT_RADIO) + pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO); + } else { + if (cur_input == PVR2_CVAL_INPUT_RADIO) + pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV); + } + fv = vf->frequency; + if (vt.capability & V4L2_TUNER_CAP_LOW) + fv = (fv * 125) / 2; + else + fv = fv * 62500; + return pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); - break; - } +} - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - int val = 0; - int cur_input; - struct v4l2_tuner vt; - ret = pvr2_hdw_get_tuner_status(hdw,&vt); - if (ret != 0) break; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), +static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int cur_input; + struct v4l2_tuner vt; + int ret; + + ret = pvr2_hdw_get_tuner_status(hdw, &vt); + if (ret != 0) + return ret; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY), &val); - if (ret != 0) break; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), + if (ret != 0) + return ret; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), &cur_input); - if (cur_input == PVR2_CVAL_INPUT_RADIO) { - vf->type = V4L2_TUNER_RADIO; - } else { - vf->type = V4L2_TUNER_ANALOG_TV; - } - if (vt.capability & V4L2_TUNER_CAP_LOW) { - val = (val * 2) / 125; - } else { - val /= 62500; - } - vf->frequency = val; - break; - } + if (cur_input == PVR2_CVAL_INPUT_RADIO) + vf->type = V4L2_TUNER_RADIO; + else + vf->type = V4L2_TUNER_ANALOG_TV; + if (vt.capability & V4L2_TUNER_CAP_LOW) + val = (val * 2) / 125; + else + val /= 62500; + vf->frequency = val; + return 0; +} - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *fd = (struct v4l2_fmtdesc *)arg; +static int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd) +{ + /* Only one format is supported : mpeg.*/ + if (fd->index != 0) + return -EINVAL; - /* Only one format is supported : mpeg.*/ - if (fd->index != 0) - break; + memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc)); + return 0; +} - memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc)); - ret = 0; - break; - } +static int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val; - case VIDIOC_G_FMT: - { - struct v4l2_format *vf = (struct v4l2_format *)arg; - int val; - switch(vf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - memcpy(vf, &pvr_format[PVR_FORMAT_PIX], - sizeof(struct v4l2_format)); - val = 0; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES), - &val); - vf->fmt.pix.width = val; - val = 0; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES), - &val); - vf->fmt.pix.height = val; - ret = 0; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - // ????? Still need to figure out to do VBI correctly - ret = -EINVAL; - break; - default: - ret = -EINVAL; - break; - } - break; - } + memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES), + &val); + vf->fmt.pix.width = val; + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES), + &val); + vf->fmt.pix.height = val; + return 0; +} - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - { - struct v4l2_format *vf = (struct v4l2_format *)arg; - - ret = 0; - switch(vf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - int lmin,lmax,ldef; - struct pvr2_ctrl *hcp,*vcp; - int h = vf->fmt.pix.height; - int w = vf->fmt.pix.width; - hcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES); - vcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES); - - lmin = pvr2_ctrl_get_min(hcp); - lmax = pvr2_ctrl_get_max(hcp); - pvr2_ctrl_get_def(hcp, &ldef); - if (w == -1) { - w = ldef; - } else if (w < lmin) { - w = lmin; - } else if (w > lmax) { - w = lmax; - } - lmin = pvr2_ctrl_get_min(vcp); - lmax = pvr2_ctrl_get_max(vcp); - pvr2_ctrl_get_def(vcp, &ldef); - if (h == -1) { - h = ldef; - } else if (h < lmin) { - h = lmin; - } else if (h > lmax) { - h = lmax; - } +static int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int lmin, lmax, ldef; + struct pvr2_ctrl *hcp, *vcp; + int h = vf->fmt.pix.height; + int w = vf->fmt.pix.width; + + hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); + vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); + + lmin = pvr2_ctrl_get_min(hcp); + lmax = pvr2_ctrl_get_max(hcp); + pvr2_ctrl_get_def(hcp, &ldef); + if (w == -1) + w = ldef; + else if (w < lmin) + w = lmin; + else if (w > lmax) + w = lmax; + lmin = pvr2_ctrl_get_min(vcp); + lmax = pvr2_ctrl_get_max(vcp); + pvr2_ctrl_get_def(vcp, &ldef); + if (h == -1) + h = ldef; + else if (h < lmin) + h = lmin; + else if (h > lmax) + h = lmax; + + memcpy(vf, &pvr_format[PVR_FORMAT_PIX], + sizeof(struct v4l2_format)); + vf->fmt.pix.width = w; + vf->fmt.pix.height = h; + return 0; +} - memcpy(vf, &pvr_format[PVR_FORMAT_PIX], - sizeof(struct v4l2_format)); - vf->fmt.pix.width = w; - vf->fmt.pix.height = h; +static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *hcp, *vcp; + int ret = pvr2_try_fmt_vid_cap(file, fh, vf); - if (cmd == VIDIOC_S_FMT) { - pvr2_ctrl_set_value(hcp,vf->fmt.pix.width); - pvr2_ctrl_set_value(vcp,vf->fmt.pix.height); - } - } break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - // ????? Still need to figure out to do VBI correctly - ret = -EINVAL; - break; - default: - ret = -EINVAL; - break; - } - break; - } + if (ret) + return ret; + hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); + vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); + pvr2_ctrl_set_value(hcp, vf->fmt.pix.width); + pvr2_ctrl_set_value(vcp, vf->fmt.pix.height); + return 0; +} - case VIDIOC_STREAMON: - { - if (!fh->pdi->stream) { - /* No stream defined for this node. This means - that we're not currently allowed to stream from - this node. */ - ret = -EPERM; - break; - } - ret = pvr2_hdw_set_stream_type(hdw,pdi->config); - if (ret < 0) return ret; - ret = pvr2_hdw_set_streaming(hdw,!0); - break; +static int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_v4l2_dev *pdi = fh->pdi; + int ret; + + if (!fh->pdi->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + return -EPERM; } + ret = pvr2_hdw_set_stream_type(hdw, pdi->config); + if (ret < 0) + return ret; + return pvr2_hdw_set_streaming(hdw, !0); +} - case VIDIOC_STREAMOFF: - { - if (!fh->pdi->stream) { - /* No stream defined for this node. This means - that we're not currently allowed to stream from - this node. */ - ret = -EPERM; - break; - } - ret = pvr2_hdw_set_streaming(hdw,0); - break; +static int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (!fh->pdi->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + return -EPERM; } + return pvr2_hdw_set_streaming(hdw, 0); +} - case VIDIOC_QUERYCTRL: - { - struct pvr2_ctrl *cptr; - int val; - struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; - ret = 0; - if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { - cptr = pvr2_hdw_get_ctrl_nextv4l( - hdw,(vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); - if (cptr) vc->id = pvr2_ctrl_get_v4lid(cptr); - } else { - cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id); - } - if (!cptr) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x not implemented here", - vc->id); - ret = -EINVAL; - break; - } +static int pvr2_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *cptr; + int val; + int ret; + ret = 0; + if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { + cptr = pvr2_hdw_get_ctrl_nextv4l( + hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); + if (cptr) + vc->id = pvr2_ctrl_get_v4lid(cptr); + } else { + cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id); + } + if (!cptr) { pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x mapping name=%s (%s)", - vc->id,pvr2_ctrl_get_name(cptr), - pvr2_ctrl_get_desc(cptr)); - strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name)); - vc->flags = pvr2_ctrl_get_v4lflags(cptr); - pvr2_ctrl_get_def(cptr, &val); - vc->default_value = val; - switch (pvr2_ctrl_get_type(cptr)) { - case pvr2_ctl_enum: - vc->type = V4L2_CTRL_TYPE_MENU; - vc->minimum = 0; - vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; - vc->step = 1; - break; - case pvr2_ctl_bool: - vc->type = V4L2_CTRL_TYPE_BOOLEAN; - vc->minimum = 0; - vc->maximum = 1; - vc->step = 1; - break; - case pvr2_ctl_int: - vc->type = V4L2_CTRL_TYPE_INTEGER; - vc->minimum = pvr2_ctrl_get_min(cptr); - vc->maximum = pvr2_ctrl_get_max(cptr); - vc->step = 1; - break; - default: - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x name=%s not mappable", - vc->id,pvr2_ctrl_get_name(cptr)); - ret = -EINVAL; - break; - } + "QUERYCTRL id=0x%x not implemented here", + vc->id); + return -EINVAL; + } + + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x mapping name=%s (%s)", + vc->id, pvr2_ctrl_get_name(cptr), + pvr2_ctrl_get_desc(cptr)); + strlcpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name)); + vc->flags = pvr2_ctrl_get_v4lflags(cptr); + pvr2_ctrl_get_def(cptr, &val); + vc->default_value = val; + switch (pvr2_ctrl_get_type(cptr)) { + case pvr2_ctl_enum: + vc->type = V4L2_CTRL_TYPE_MENU; + vc->minimum = 0; + vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; + vc->step = 1; break; - } - - case VIDIOC_QUERYMENU: - { - struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg; - unsigned int cnt = 0; - ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id), - vm->index, - vm->name,sizeof(vm->name)-1, - &cnt); - vm->name[cnt] = 0; + case pvr2_ctl_bool: + vc->type = V4L2_CTRL_TYPE_BOOLEAN; + vc->minimum = 0; + vc->maximum = 1; + vc->step = 1; break; - } - - case VIDIOC_G_CTRL: - { - struct v4l2_control *vc = (struct v4l2_control *)arg; - int val = 0; - ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), - &val); - vc->value = val; + case pvr2_ctl_int: + vc->type = V4L2_CTRL_TYPE_INTEGER; + vc->minimum = pvr2_ctrl_get_min(cptr); + vc->maximum = pvr2_ctrl_get_max(cptr); + vc->step = 1; break; + default: + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x name=%s not mappable", + vc->id, pvr2_ctrl_get_name(cptr)); + return -EINVAL; } + return 0; +} - case VIDIOC_S_CTRL: - { - struct v4l2_control *vc = (struct v4l2_control *)arg; - ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), - vc->value); - break; - } +static int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned int cnt = 0; + int ret; - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *ctls = - (struct v4l2_ext_controls *)arg; - struct v4l2_ext_control *ctrl; - unsigned int idx; - int val; - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),&val); - if (ret) { - ctls->error_idx = idx; - break; - } - /* Ensure that if read as a 64 bit value, the user - will still get a hopefully sane value */ - ctrl->value64 = 0; - ctrl->value = val; + ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id), + vm->index, + vm->name, sizeof(vm->name) - 1, + &cnt); + vm->name[cnt] = 0; + return ret; +} + +static int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), + &val); + vc->value = val; + return ret; +} + +static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), + vc->value); +} + +static int pvr2_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + unsigned int idx; + int val; + int ret; + + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), &val); + if (ret) { + ctls->error_idx = idx; + return ret; } - break; + /* Ensure that if read as a 64 bit value, the user + will still get a hopefully sane value */ + ctrl->value64 = 0; + ctrl->value = val; } + return 0; +} - case VIDIOC_S_EXT_CTRLS: - { - struct v4l2_ext_controls *ctls = - (struct v4l2_ext_controls *)arg; - struct v4l2_ext_control *ctrl; - unsigned int idx; - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id), +static int pvr2_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + unsigned int idx; + int ret; + + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), ctrl->value); - if (ret) { - ctls->error_idx = idx; - break; - } + if (ret) { + ctls->error_idx = idx; + return ret; } - break; } + return 0; +} - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *ctls = - (struct v4l2_ext_controls *)arg; - struct v4l2_ext_control *ctrl; - struct pvr2_ctrl *pctl; - unsigned int idx; - /* For the moment just validate that the requested control - actually exists. */ - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id); - if (!pctl) { - ret = -EINVAL; - ctls->error_idx = idx; - break; - } - } - break; - } +static int pvr2_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + struct pvr2_ctrl *pctl; + unsigned int idx; + int ret; - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg; - if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - ret = -EINVAL; - break; + /* For the moment just validate that the requested control + actually exists. */ + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id); + if (!pctl) { + ctls->error_idx = idx; + return -EINVAL; } - ret = pvr2_hdw_get_cropcap(hdw, cap); - cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ - break; } - case VIDIOC_G_CROP: - { - struct v4l2_crop *crop = (struct v4l2_crop *)arg; - int val = 0; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_get_value( + return 0; +} + +static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int ret; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = pvr2_hdw_get_cropcap(hdw, cap); + cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ + return ret; +} + +static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.left = val; - ret = pvr2_ctrl_get_value( + if (ret != 0) + return -EINVAL; + crop->c.left = val; + ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.top = val; - ret = pvr2_ctrl_get_value( + if (ret != 0) + return -EINVAL; + crop->c.top = val; + ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.width = val; - ret = pvr2_ctrl_get_value( + if (ret != 0) + return -EINVAL; + crop->c.width = val; + ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.height = val; - } - case VIDIOC_S_CROP: - { - struct v4l2_crop *crop = (struct v4l2_crop *)arg; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( + if (ret != 0) + return -EINVAL; + crop->c.height = val; + return 0; +} + +static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_cropcap cap; + int ret; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), crop->c.left); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), crop->c.top); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), crop->c.width); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), crop->c.height); - if (ret != 0) { - ret = -EINVAL; - break; - } - } - case VIDIOC_LOG_STATUS: - { - pvr2_hdw_trigger_module_log(hdw); - ret = 0; - break; - } + if (ret != 0) + return -EINVAL; + return 0; +} + +static int pvr2_log_status(struct file *file, void *priv) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + pvr2_hdw_trigger_module_log(hdw); + return 0; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_S_REGISTER: - case VIDIOC_DBG_G_REGISTER: - { - u64 val; - struct v4l2_dbg_register *req = (struct v4l2_dbg_register *)arg; - if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val; - ret = pvr2_hdw_register_access( - hdw, &req->match, req->reg, - cmd == VIDIOC_DBG_S_REGISTER, &val); - if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val; - break; - } -#endif +static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_register *req) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + u64 val; + int ret; - default : - ret = -ENOTTY; - break; - } + ret = pvr2_hdw_register_access( + hdw, &req->match, req->reg, + 0, &val); + req->val = val; + return ret; +} - pvr2_hdw_commit_ctl(hdw); +static int pvr2_s_register(struct file *file, void *priv, struct v4l2_dbg_register *req) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + u64 val; + int ret; - if (ret < 0) { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld", ret); - } else { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld" - " command was:", ret); - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), - cmd); - } - } - } else { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)", - ret, ret); - } + val = req->val; + ret = pvr2_hdw_register_access( + hdw, &req->match, req->reg, + 1, &val); return ret; } +#endif + +static const struct v4l2_ioctl_ops pvr2_ioctl_ops = { + .vidioc_querycap = pvr2_querycap, + .vidioc_g_priority = pvr2_g_priority, + .vidioc_s_priority = pvr2_s_priority, + .vidioc_s_audio = pvr2_s_audio, + .vidioc_g_audio = pvr2_g_audio, + .vidioc_enumaudio = pvr2_enumaudio, + .vidioc_enum_input = pvr2_enum_input, + .vidioc_cropcap = pvr2_cropcap, + .vidioc_s_crop = pvr2_s_crop, + .vidioc_g_crop = pvr2_g_crop, + .vidioc_g_input = pvr2_g_input, + .vidioc_s_input = pvr2_s_input, + .vidioc_g_frequency = pvr2_g_frequency, + .vidioc_s_frequency = pvr2_s_frequency, + .vidioc_s_tuner = pvr2_s_tuner, + .vidioc_g_tuner = pvr2_g_tuner, + .vidioc_g_std = pvr2_g_std, + .vidioc_s_std = pvr2_s_std, + .vidioc_log_status = pvr2_log_status, + .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap, + .vidioc_streamon = pvr2_streamon, + .vidioc_streamoff = pvr2_streamoff, + .vidioc_queryctrl = pvr2_queryctrl, + .vidioc_querymenu = pvr2_querymenu, + .vidioc_g_ctrl = pvr2_g_ctrl, + .vidioc_s_ctrl = pvr2_s_ctrl, + .vidioc_g_ext_ctrls = pvr2_g_ext_ctrls, + .vidioc_s_ext_ctrls = pvr2_s_ext_ctrls, + .vidioc_try_ext_ctrls = pvr2_try_ext_ctrls, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = pvr2_g_register, + .vidioc_s_register = pvr2_s_register, +#endif +}; static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) { @@ -959,7 +945,56 @@ static long pvr2_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl); + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + long ret = -EINVAL; + + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) + v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); + + if (!pvr2_hdw_dev_ok(hdw)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "ioctl failed - bad or no context"); + return -EFAULT; + } + + /* check priority */ + switch (cmd) { + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + ret = v4l2_prio_check(&vp->prio, fh->prio); + if (ret) + return ret; + } + + ret = video_ioctl2(file, cmd, arg); + + pvr2_hdw_commit_ctl(hdw); + + if (ret < 0) { + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl failure, ret=%ld", ret); + } else { + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl failure, ret=%ld" + " command was:", ret); + v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), + cmd); + } + } + } else { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)", + ret, ret); + } + return ret; + } @@ -1298,6 +1333,11 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); dip->devbase.release = pvr2_video_device_release; + dip->devbase.ioctl_ops = &pvr2_ioctl_ops; + /* FIXME: tvnorms should be set to the set of supported standards + by this device. Then video_ioctl2 will implement VIDIOC_ENUMSTD + based on this field. */ + dip->devbase.tvnorms = V4L2_STD_ALL; mindevnum = -1; unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw); -- cgit v1.2.3 From 7378c184218227c5b936e3d50c6f03c9e127af00 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 20 Feb 2012 02:24:39 -0300 Subject: [media] pvrusb2: Clean up pvr2_hdw_get_detected_std() Move full implementation of pvr2_hdw_get_detected_std() actually include pvr2_hdw_get_detected_std(). Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index ebc2c7e39233..534dbf3d0144 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2997,6 +2997,7 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std) { + *std = V4L2_STD_ALL; v4l2_device_call_all(&hdw->v4l2_dev, 0, video, querystd, std); return 0; -- cgit v1.2.3 From 0927ee67dd59f715f2a6c796a86a0eda9ea2e7b2 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 20 Feb 2012 02:26:06 -0300 Subject: [media] pvrusb2: Implement querystd for videodev_ioctl2 Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 83bc4675ec2a..3669090f330e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -205,6 +205,14 @@ int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std) pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std); } +static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + return pvr2_hdw_get_detected_std(hdw, std); +} + static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi) { struct pvr2_v4l2_fh *fh = file->private_data; @@ -844,6 +852,7 @@ static const struct v4l2_ioctl_ops pvr2_ioctl_ops = { .vidioc_g_tuner = pvr2_g_tuner, .vidioc_g_std = pvr2_g_std, .vidioc_s_std = pvr2_s_std, + .vidioc_querystd = pvr2_querystd, .vidioc_log_status = pvr2_log_status, .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap, -- cgit v1.2.3 From ac04d00ed1ee173c74bd5d55e80aed1f6e77a700 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 20 Feb 2012 02:28:56 -0300 Subject: [media] pvrusb2: Transform video standard detection result into read-only control ID Other aspects of the pvrusb2 driver - including in particular those dealing with video standards - all use internal control IDs for uniform access by the interfaces. By exporting the querystd result as another control ID, uniform access to it becomes available through the sysfs interface. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 1 + drivers/media/video/pvrusb2/pvrusb2-hdw.c | 38 ++++++++++++++++++---- drivers/media/video/pvrusb2/pvrusb2-hdw.h | 4 +-- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 7 +++- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 305e6aaa844a..17fc57989f6c 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -323,6 +323,7 @@ struct pvr2_hdw { struct pvr2_ctl_info std_info_enum; struct pvr2_ctl_info std_info_avail; struct pvr2_ctl_info std_info_cur; + struct pvr2_ctl_info std_info_detect; struct v4l2_standard *std_defs; const char **std_enum_names; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 534dbf3d0144..c4bb0d1b5f9f 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -346,7 +346,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, void *write_data,unsigned int write_len, void *read_data,unsigned int read_len); static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw); - +static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw); static void trace_stbit(const char *name,int val) { @@ -840,6 +840,12 @@ static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp) return 0; } +static int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp) +{ + *vp = pvr2_hdw_get_detected_std(cptr->hdw); + return 0; +} + static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp) { *vp = cptr->hdw->std_mask_avail; @@ -1302,6 +1308,15 @@ static const struct pvr2_ctl_info control_defs[] = { .is_dirty = ctrl_stdenumcur_is_dirty, .clear_dirty = ctrl_stdenumcur_clear_dirty, .type = pvr2_ctl_enum, + },{ + .desc = "Video Standards Detected Mask", + .name = "video_standard_mask_detected", + .internal_id = PVR2_CID_STDDETECT, + .skip_init = !0, + .get_value = ctrl_stddetect_get, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, } }; @@ -2629,7 +2644,17 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr->info = &hdw->std_info_cur; hdw->std_info_cur.def.type_bitmask.bit_names = hdw->std_mask_ptrs; - hdw->std_info_avail.def.type_bitmask.valid_bits = + hdw->std_info_cur.def.type_bitmask.valid_bits = + valid_std_mask; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT); + if (cptr) { + memcpy(&hdw->std_info_detect,cptr->info, + sizeof(hdw->std_info_detect)); + cptr->info = &hdw->std_info_detect; + hdw->std_info_detect.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_detect.def.type_bitmask.valid_bits = valid_std_mask; } @@ -2995,12 +3020,13 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \ } -int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std) +v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw) { - *std = V4L2_STD_ALL; + v4l2_std_id std; + std = V4L2_STD_ALL; v4l2_device_call_all(&hdw->v4l2_dev, 0, - video, querystd, std); - return 0; + video, querystd, &std); + return std; } /* Execute whatever commands are required to update the state of all the diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 66546580b17d..45ddb81475b7 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -46,6 +46,7 @@ #define PVR2_CID_CROPCAPBT 16 #define PVR2_CID_CROPCAPBW 17 #define PVR2_CID_CROPCAPBH 18 +#define PVR2_CID_STDDETECT 19 /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 @@ -214,9 +215,6 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, unsigned int idx); -/* Get the detected video standard */ -int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std); - /* Enable / disable retrieval of CPU firmware or prom contents. This must be enabled before pvr2_hdw_cpufw_get() will function. Note that doing this may prevent the device from running (and leaving this mode may diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 3669090f330e..ed497bbf539b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -209,8 +209,13 @@ static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; - return pvr2_hdw_get_detected_std(hdw, std); + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val); + *std = val; + return ret; } static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi) -- cgit v1.2.3 From 99ba1514ae3aded098138d08c6165caf71bb3017 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 20 Feb 2012 02:31:25 -0300 Subject: [media][trival] pvrusb2: Fix truncated video standard names Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 17fc57989f6c..5710d7f152c2 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -329,7 +329,7 @@ struct pvr2_hdw { // Generated string names, one per actual V4L2 standard const char *std_mask_ptrs[32]; - char std_mask_names[32][10]; + char std_mask_names[32][16]; int unit_number; /* ID for driver instance */ unsigned long serial_number; /* ID for hardware itself */ -- cgit v1.2.3 From d8329f8e17af85c568768fb268e0695dd1fc8148 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 20 Feb 2012 02:33:06 -0300 Subject: [media] pvrusb2: Base available video standards on what hardware supports With the transition to ioctl2, the pvrusb2 driver's own standards enumeration is no longer used. Instead a generic algorithm internal to v4l is used (which is a great idea - since the pvrusb2 implementation itself was generic anyway). This change ensures that the v4l algorithm works with the correct set of hardware supported standards. This resolves a FIXME left behind from the videodev_ioctl2 transition. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index ed497bbf539b..4c1a47470d39 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -1348,10 +1348,13 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); dip->devbase.release = pvr2_video_device_release; dip->devbase.ioctl_ops = &pvr2_ioctl_ops; - /* FIXME: tvnorms should be set to the set of supported standards - by this device. Then video_ioctl2 will implement VIDIOC_ENUMSTD - based on this field. */ - dip->devbase.tvnorms = V4L2_STD_ALL; + { + int val; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(vp->channel.mc_head->hdw, + PVR2_CID_STDAVAIL), &val); + dip->devbase.tvnorms = (v4l2_std_id)val; + } mindevnum = -1; unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw); -- cgit v1.2.3 From 598e978aadd39e24749e0da5a6cff568570b1d00 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 20 Feb 2012 02:35:20 -0300 Subject: [media] pvrusb2: Trivial tweak to get rid of some redundant dereferences Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 4c1a47470d39..7bddfaeeafc3 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -1309,10 +1309,12 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct usb_device *usbdev; int mindevnum; int unit_number; + struct pvr2_hdw *hdw; int *nr_ptr = NULL; dip->v4lp = vp; - usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw); + hdw = vp->channel.mc_head->hdw; + usbdev = pvr2_hdw_get_dev(hdw); dip->v4l_type = v4l_type; switch (v4l_type) { case VFL_TYPE_GRABBER: @@ -1351,13 +1353,13 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, { int val; pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(vp->channel.mc_head->hdw, + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDAVAIL), &val); dip->devbase.tvnorms = (v4l2_std_id)val; } mindevnum = -1; - unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw); + unit_number = pvr2_hdw_get_unit_number(hdw); if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { mindevnum = nr_ptr[unit_number]; } @@ -1374,7 +1376,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, video_device_node_name(&dip->devbase), pvr2_config_get_name(dip->config)); - pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw, + pvr2_hdw_v4l_store_minor_number(hdw, dip->minor_type,dip->devbase.minor); } -- cgit v1.2.3 From c0bb609fdc0b842799d5108aec7fae13b0fd32cd Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 20 Feb 2012 02:39:22 -0300 Subject: [media] pvrusb2: Get rid of obsolete code for video standard enumeration Get rid of pvrusb2-local implementation for enumeration of video standards - with video_ioctl2 this happens automatically now in the v4l core. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 3 - drivers/media/video/pvrusb2/pvrusb2-hdw.c | 160 +-------------------- drivers/media/video/pvrusb2/pvrusb2-hdw.h | 5 - 3 files changed, 2 insertions(+), 166 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 5710d7f152c2..036952f2a3cb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -317,15 +317,12 @@ struct pvr2_hdw { v4l2_std_id std_mask_eeprom; // Hardware supported selections v4l2_std_id std_mask_avail; // Which standards we may select from v4l2_std_id std_mask_cur; // Currently selected standard(s) - unsigned int std_enum_cnt; // # of enumerated standards int std_enum_cur; // selected standard enumeration value int std_dirty; // True if std_mask_cur has changed struct pvr2_ctl_info std_info_enum; struct pvr2_ctl_info std_info_avail; struct pvr2_ctl_info std_info_cur; struct pvr2_ctl_info std_info_detect; - struct v4l2_standard *std_defs; - const char **std_enum_names; // Generated string names, one per actual V4L2 standard const char *std_mask_ptrs[32]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index c4bb0d1b5f9f..d882c95c5788 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -334,8 +334,6 @@ static void pvr2_hdw_state_log_state(struct pvr2_hdw *); static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw); static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); -static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); -static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); static void pvr2_hdw_quiescent_timeout(unsigned long); static void pvr2_hdw_decoder_stabilization_timeout(unsigned long); static void pvr2_hdw_encoder_wait_timeout(unsigned long); @@ -860,8 +858,7 @@ static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v) ns = (ns & ~m) | (v & m); if (ns == hdw->std_mask_avail) return 0; hdw->std_mask_avail = ns; - pvr2_hdw_internal_set_std_avail(hdw); - pvr2_hdw_internal_find_stdenum(hdw); + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; return 0; } @@ -901,7 +898,6 @@ static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v) if (ns == hdw->std_mask_cur) return 0; hdw->std_mask_cur = ns; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return 0; } @@ -947,40 +943,6 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) } -static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v) -{ - struct pvr2_hdw *hdw = cptr->hdw; - if (v < 0) return -EINVAL; - if (v > hdw->std_enum_cnt) return -EINVAL; - hdw->std_enum_cur = v; - if (!v) return 0; - v--; - if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0; - hdw->std_mask_cur = hdw->std_defs[v].id; - hdw->std_dirty = !0; - return 0; -} - - -static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->std_enum_cur; - return 0; -} - - -static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->std_dirty != 0; -} - - -static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr) -{ - cptr->hdw->std_dirty = 0; -} - - #define DEFINT(vmin,vmax) \ .type = pvr2_ctl_int, \ .def.type_int.min_value = vmin, \ @@ -1298,16 +1260,6 @@ static const struct pvr2_ctl_info control_defs[] = { .val_to_sym = ctrl_std_val_to_sym, .sym_to_val = ctrl_std_sym_to_val, .type = pvr2_ctl_bitmask, - },{ - .desc = "Video Standard Name", - .name = "video_standard", - .internal_id = PVR2_CID_STDENUM, - .skip_init = !0, - .get_value = ctrl_stdenumcur_get, - .set_value = ctrl_stdenumcur_set, - .is_dirty = ctrl_stdenumcur_is_dirty, - .clear_dirty = ctrl_stdenumcur_clear_dirty, - .type = pvr2_ctl_enum, },{ .desc = "Video Standards Detected Mask", .name = "video_standard_mask_detected", @@ -1951,7 +1903,7 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) hdw->std_mask_avail |= std2; } - pvr2_hdw_internal_set_std_avail(hdw); + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; if (std1) { bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1); @@ -1960,7 +1912,6 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) bcnt,buf); hdw->std_mask_cur = std1; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return; } if (std3) { @@ -1970,7 +1921,6 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) " (determined by device type): %.*s",bcnt,buf); hdw->std_mask_cur = std3; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return; } @@ -1990,24 +1940,10 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) bcnt,buf); hdw->std_mask_cur = std_eeprom_maps[idx].std; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return; } } - if (hdw->std_enum_cnt > 1) { - // Autoselect the first listed standard - hdw->std_enum_cur = 1; - hdw->std_mask_cur = hdw->std_defs[hdw->std_enum_cur-1].id; - hdw->std_dirty = !0; - pvr2_trace(PVR2_TRACE_STD, - "Initial video standard auto-selected to %s", - hdw->std_defs[hdw->std_enum_cur-1].name); - return; - } - - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Unable to select a viable initial video standard"); } @@ -2609,14 +2545,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr->info = ciptr; } - // Initialize video standard enum dynamic control - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM); - if (cptr) { - memcpy(&hdw->std_info_enum,cptr->info, - sizeof(hdw->std_info_enum)); - cptr->info = &hdw->std_info_enum; - - } // Initialize control data regarding video standard masks valid_std_mask = pvr2_std_get_usable(); for (idx = 0; idx < 32; idx++) { @@ -2736,8 +2664,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, kfree(hdw->ctl_write_buffer); kfree(hdw->controls); kfree(hdw->mpeg_ctrl_info); - kfree(hdw->std_defs); - kfree(hdw->std_enum_names); kfree(hdw); } return NULL; @@ -2813,8 +2739,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) } while (0); mutex_unlock(&pvr2_unit_mtx); kfree(hdw->controls); kfree(hdw->mpeg_ctrl_info); - kfree(hdw->std_defs); - kfree(hdw->std_enum_names); kfree(hdw); } @@ -2837,86 +2761,6 @@ void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) } -// Attempt to autoselect an appropriate value for std_enum_cur given -// whatever is currently in std_mask_cur -static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw) -{ - unsigned int idx; - for (idx = 1; idx < hdw->std_enum_cnt; idx++) { - if (hdw->std_defs[idx-1].id == hdw->std_mask_cur) { - hdw->std_enum_cur = idx; - return; - } - } - hdw->std_enum_cur = 0; -} - - -// Calculate correct set of enumerated standards based on currently known -// set of available standards bits. -static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw) -{ - struct v4l2_standard *newstd; - unsigned int std_cnt; - unsigned int idx; - - newstd = pvr2_std_create_enum(&std_cnt,hdw->std_mask_avail); - - if (hdw->std_defs) { - kfree(hdw->std_defs); - hdw->std_defs = NULL; - } - hdw->std_enum_cnt = 0; - if (hdw->std_enum_names) { - kfree(hdw->std_enum_names); - hdw->std_enum_names = NULL; - } - - if (!std_cnt) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "WARNING: Failed to identify any viable standards"); - } - - /* Set up the dynamic control for this standard */ - hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL); - if (hdw->std_enum_names) { - hdw->std_enum_names[0] = "none"; - for (idx = 0; idx < std_cnt; idx++) - hdw->std_enum_names[idx+1] = newstd[idx].name; - hdw->std_info_enum.def.type_enum.value_names = - hdw->std_enum_names; - hdw->std_info_enum.def.type_enum.count = std_cnt+1; - } else { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "WARNING: Failed to alloc memory for names"); - hdw->std_info_enum.def.type_enum.value_names = NULL; - hdw->std_info_enum.def.type_enum.count = 0; - } - hdw->std_defs = newstd; - hdw->std_enum_cnt = std_cnt+1; - hdw->std_enum_cur = 0; - hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; -} - - -int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, - struct v4l2_standard *std, - unsigned int idx) -{ - int ret = -EINVAL; - if (!idx) return ret; - LOCK_TAKE(hdw->big_lock); do { - if (idx >= hdw->std_enum_cnt) break; - idx--; - memcpy(std,hdw->std_defs+idx,sizeof(*std)); - ret = 0; - } while (0); LOCK_GIVE(hdw->big_lock); - return ret; -} - - /* Get the number of defined controls */ unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 45ddb81475b7..8060fc666eeb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -28,7 +28,6 @@ /* Private internal control ids, look these up with pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */ -#define PVR2_CID_STDENUM 1 #define PVR2_CID_STDCUR 2 #define PVR2_CID_STDAVAIL 3 #define PVR2_CID_INPUT 4 @@ -211,10 +210,6 @@ int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); /* Get handle to video output stream */ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); -/* Emit a video standard struct */ -int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, - unsigned int idx); - /* Enable / disable retrieval of CPU firmware or prom contents. This must be enabled before pvr2_hdw_cpufw_get() will function. Note that doing this may prevent the device from running (and leaving this mode may -- cgit v1.2.3 From aeebb1b3146a70bf02d0115a2be690d856d12e8c Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 20 Feb 2012 02:40:56 -0300 Subject: [media] pvrusb2: For querystd, start with list of hardware-supported standards The V4L querystd implementation appears to want to narrow down the list of available standards by starting with a hardware-supported list and then attempting to detect which among those are actually available. Prior to this change in the pvrusb2 driver we started with all possible standards. With this change in place we instead narrow to just the standards that we know the hardware can actually support. For example, this removes the ATSC standards from the list if we aren't dealing with a hybrid device... Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index d882c95c5788..fb828ba1dbbe 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2867,7 +2867,7 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw) { v4l2_std_id std; - std = V4L2_STD_ALL; + std = (v4l2_std_id)hdw->std_mask_avail; v4l2_device_call_all(&hdw->v4l2_dev, 0, video, querystd, &std); return std; -- cgit v1.2.3 From 31c5f0c5e25ed71eeced170f113bb590f2f1f6f3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 09:50:36 -0300 Subject: [media] uvcvideo: Fix ENUMINPUT handling Properly validate the user-supplied index against the number of inputs. The code used the pin local variable instead of the index by mistake. Reported-by: Jozef Vesely Signed-off-by: Laurent Pinchart Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_v4l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index a3322ed81e39..759bef8897e9 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -694,7 +694,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } pin = iterm->id; - } else if (pin < selector->bNrInPins) { + } else if (index < selector->bNrInPins) { pin = selector->baSourceID[index]; list_for_each_entry(iterm, &chain->entities, chain) { if (!UVC_ENTITY_IS_ITERM(iterm)) -- cgit v1.2.3 From 616bd4e2573cb114aadd90182ebe7f34066b15da Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 2 Apr 2012 06:11:09 -0300 Subject: [media] MAINTAINERS: Update UVC driver's mailing list address Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 860bc6117851..f3d7e16b90ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7148,7 +7148,7 @@ F: include/linux/usb/usbnet.h USB VIDEO CLASS M: Laurent Pinchart -L: linux-uvc-devel@lists.berlios.de (subscribers-only) +L: linux-uvc-devel@lists.sourceforge.net (subscribers-only) L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.ideasonboard.org/uvc/ -- cgit v1.2.3 From 4742c82ed90d0f8c82639750dcfe481e61d883a4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 30 Apr 2012 08:19:10 -0300 Subject: [media] uvcvideo: Use videobuf2 .get_unmapped_area() implementation The get_unmapped_area() operation was forgotten during conversion to videobuf2. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_queue.c | 43 +++++++++++-------------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 8f54e24e3f35..9288fbd5001b 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -207,6 +207,19 @@ int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) return ret; } +#ifndef CONFIG_MMU +unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) +{ + unsigned long ret; + + mutex_lock(&queue->mutex); + ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0); + mutex_unlock(&queue->mutex); + return ret; +} +#endif + unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, poll_table *wait) { @@ -237,36 +250,6 @@ int uvc_queue_allocated(struct uvc_video_queue *queue) return allocated; } -#ifndef CONFIG_MMU -/* - * Get unmapped area. - * - * NO-MMU arch need this function to make mmap() work correctly. - */ -unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, - unsigned long pgoff) -{ - struct uvc_buffer *buffer; - unsigned int i; - unsigned long ret; - - mutex_lock(&queue->mutex); - for (i = 0; i < queue->count; ++i) { - buffer = &queue->buffer[i]; - if ((buffer->buf.m.offset >> PAGE_SHIFT) == pgoff) - break; - } - if (i == queue->count) { - ret = -EINVAL; - goto done; - } - ret = (unsigned long)buf->mem; -done: - mutex_unlock(&queue->mutex); - return ret; -} -#endif - /* * Enable or disable the video buffers queue. * -- cgit v1.2.3 From 1567bb7dcc6a232693143fdbe3b89791f20890ac Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 18 Nov 2011 11:28:24 -0300 Subject: [media] omap3isp: Prevent pipelines that contain a crashed entity from starting The OMAP3 ISP preview engine will violate the L4 bus protocol if we try to write some of its internal registers after it failed to stop properly. This generates an external abort on non-linefetch fault, triggering a fatal kernel oops. We can't always prevent preview engine stop failures (they can for instance be caused by a sensor crash), but we can improve the system reliability by refusing to start streaming on a pipeline that contains the preview engine if it failed to stop. The driver will then eventually reset the ISP (when all applications will have closed their file handles related to OMAP3 ISP device nodes), making the ISP usable again. Fixes: NB#291334 - camera: Recover gracefully from ISP crash instead of oopsing Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Reviewed-by: Phil Carmody Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isp.c | 27 +++++++++++++++++++++------ drivers/media/video/omap3isp/isp.h | 3 ++- drivers/media/video/omap3isp/ispvideo.c | 4 ++++ drivers/media/video/omap3isp/ispvideo.h | 2 ++ 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c index 12d5f923e1d0..3db8583497ee 100644 --- a/drivers/media/video/omap3isp/isp.c +++ b/drivers/media/video/omap3isp/isp.c @@ -739,6 +739,17 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, unsigned long flags; int ret; + /* If the preview engine crashed it might not respond to read/write + * operations on the L4 bus. This would result in a bus fault and a + * kernel oops. Refuse to start streaming in that case. This check must + * be performed before the loop below to avoid starting entities if the + * pipeline won't start anyway (those entities would then likely fail to + * stop, making the problem worse). + */ + if ((pipe->entities & isp->crashed) & + (1U << isp->isp_prev.subdev.entity.id)) + return -EIO; + spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); spin_unlock_irqrestore(&pipe->lock, flags); @@ -879,13 +890,15 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) if (ret) { dev_info(isp->dev, "Unable to stop %s\n", subdev->name); + /* If the entity failed to stopped, assume it has + * crashed. Mark it as such, the ISP will be reset when + * applications will release it. + */ + isp->crashed |= 1U << subdev->entity.id; failure = -ETIMEDOUT; } } - if (failure < 0) - isp->needs_reset = true; - return failure; } @@ -1069,6 +1082,7 @@ static int isp_reset(struct isp_device *isp) udelay(1); } + isp->crashed = 0; return 0; } @@ -1496,10 +1510,11 @@ void omap3isp_put(struct isp_device *isp) if (--isp->ref_count == 0) { isp_disable_interrupts(isp); isp_save_ctx(isp); - if (isp->needs_reset) { + /* Reset the ISP if an entity has failed to stop. This is the + * only way to recover from such conditions. + */ + if (isp->crashed) isp_reset(isp); - isp->needs_reset = false; - } isp_disable_clocks(isp); } mutex_unlock(&isp->isp_mutex); diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h index d96603eb0d17..f8d1f100fc19 100644 --- a/drivers/media/video/omap3isp/isp.h +++ b/drivers/media/video/omap3isp/isp.h @@ -145,6 +145,7 @@ struct isp_platform_callback { * @raw_dmamask: Raw DMA mask * @stat_lock: Spinlock for handling statistics * @isp_mutex: Mutex for serializing requests to ISP. + * @crashed: Bitmask of crashed entities (indexed by entity ID) * @has_context: Context has been saved at least once and can be restored. * @ref_count: Reference count for handling multiple ISP requests. * @cam_ick: Pointer to camera interface clock structure. @@ -184,7 +185,7 @@ struct isp_device { /* ISP Obj */ spinlock_t stat_lock; /* common lock for statistic drivers */ struct mutex isp_mutex; /* For handling ref_count field */ - bool needs_reset; + u32 crashed; int has_context; int ref_count; unsigned int autoidle; diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index b02070057724..2107d99d523a 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -292,6 +292,7 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) int ret; pipe->max_rate = pipe->l3_ick; + pipe->entities = 0; subdev = isp_video_remote_subdev(pipe->output, NULL); if (subdev == NULL) @@ -299,6 +300,9 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) while (1) { unsigned int shifter_link; + + pipe->entities |= 1U << subdev->entity.id; + /* Retrieve the sink format */ pad = &subdev->entity.pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h index d91bdb919be0..c9187cbc3557 100644 --- a/drivers/media/video/omap3isp/ispvideo.h +++ b/drivers/media/video/omap3isp/ispvideo.h @@ -88,6 +88,7 @@ enum isp_pipeline_state { /* * struct isp_pipeline - An ISP hardware pipeline * @error: A hardware error occurred during capture + * @entities: Bitmask of entities in the pipeline (indexed by entity ID) */ struct isp_pipeline { struct media_pipeline pipe; @@ -96,6 +97,7 @@ struct isp_pipeline { enum isp_pipeline_stream_state stream_state; struct isp_video *input; struct isp_video *output; + u32 entities; unsigned long l3_ick; unsigned int max_rate; atomic_t frame_number; -- cgit v1.2.3 From a32f2f90543853449f0e49eaf885e8d24d5809a7 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 27 Jan 2012 07:18:51 -0300 Subject: [media] omap3isp: Prevent crash at module unload iommu_domain_free() was called in isp_remove() before omap3isp_put(). omap3isp_put() must not save the context if the IOMMU no longer is there. Fix this. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c index 3db8583497ee..06afbc14f6ae 100644 --- a/drivers/media/video/omap3isp/isp.c +++ b/drivers/media/video/omap3isp/isp.c @@ -1509,7 +1509,8 @@ void omap3isp_put(struct isp_device *isp) BUG_ON(isp->ref_count == 0); if (--isp->ref_count == 0) { isp_disable_interrupts(isp); - isp_save_ctx(isp); + if (isp->domain) + isp_save_ctx(isp); /* Reset the ISP if an entity has failed to stop. This is the * only way to recover from such conditions. */ @@ -1996,6 +1997,7 @@ static int isp_remove(struct platform_device *pdev) omap3isp_get(isp); iommu_detach_device(isp->domain, &pdev->dev); iommu_domain_free(isp->domain); + isp->domain = NULL; omap3isp_put(isp); free_irq(isp->irq_num, isp); -- cgit v1.2.3 From b43883d659aa3f3aced5e400538b200341e910e0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 9 Feb 2012 13:00:45 -0300 Subject: [media] omap3isp: Fix frame number propagation When propagating the frame number through the pipeline, the frame number must be incremented at frame start by the appropriate IRQ handler. This was properly handled for the CSI2 and CCP2 receivers, but not when the CCDC parallel interface is used. ADD frame number incrementation to the HS/VS interrupt handler. As the HS/VS interrupt is also generated for frames received by the CSI2 and CCP2 receivers, remove explicit propagation handling from the serial receivers. Reported-by: Kruno Mrak Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Tested-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isp.c | 8 -------- drivers/media/video/omap3isp/ispccdc.c | 3 +++ drivers/media/video/omap3isp/ispccp2.c | 23 ----------------------- drivers/media/video/omap3isp/ispcsi2.c | 20 +++----------------- drivers/media/video/omap3isp/ispcsi2.h | 1 - 5 files changed, 6 insertions(+), 49 deletions(-) diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c index 06afbc14f6ae..df6416c706fd 100644 --- a/drivers/media/video/omap3isp/isp.c +++ b/drivers/media/video/omap3isp/isp.c @@ -785,14 +785,6 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, } } - /* Frame number propagation. In continuous streaming mode the number - * is incremented in the frame start ISR. In mem-to-mem mode - * singleshot is used and frame start IRQs are not available. - * Thus we have to increment the number here. - */ - if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT) - atomic_inc(&pipe->frame_number); - return 0; } diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 1f3c16d8f0b4..17207c7037f7 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -1410,6 +1410,9 @@ static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc) struct video_device *vdev = ccdc->subdev.devnode; struct v4l2_event event; + /* Frame number propagation */ + atomic_inc(&pipe->frame_number); + memset(&event, 0, sizeof(event)); event.type = V4L2_EVENT_FRAME_SYNC; event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number); diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c index 70ddbf35b223..ee7dcda36b6d 100644 --- a/drivers/media/video/omap3isp/ispccp2.c +++ b/drivers/media/video/omap3isp/ispccp2.c @@ -161,7 +161,6 @@ static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2) static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) { struct isp_device *isp = to_isp_device(ccp2); - struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); int i; if (enable && ccp2->vdds_csib) @@ -178,19 +177,6 @@ static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN, enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0); - /* For frame count propagation */ - if (pipe->do_propagation) { - /* We may want the Frame Start IRQ from LC0 */ - if (enable) - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, - ISPCCP2_LC01_IRQENABLE, - ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); - else - isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCP2, - ISPCCP2_LC01_IRQENABLE, - ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); - } - if (!enable && ccp2->vdds_csib) regulator_disable(ccp2->vdds_csib); } @@ -350,7 +336,6 @@ static void ccp2_lcx_config(struct isp_ccp2_device *ccp2, ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | - ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; @@ -613,14 +598,6 @@ void omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2) if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping)) return; - /* Frame number propagation */ - if (lcx_irqstatus & ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ) { - struct isp_pipeline *pipe = - to_isp_pipeline(&ccp2->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - } - /* Handle queued buffers on frame end interrupts */ if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ) ccp2_isr_buffer(ccp2); diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c index fcb5168996a7..75ac6d466962 100644 --- a/drivers/media/video/omap3isp/ispcsi2.c +++ b/drivers/media/video/omap3isp/ispcsi2.c @@ -378,21 +378,17 @@ static void csi2_timing_config(struct isp_device *isp, static void csi2_irq_ctx_set(struct isp_device *isp, struct isp_csi2_device *csi2, int enable) { - u32 reg = ISPCSI2_CTX_IRQSTATUS_FE_IRQ; int i; - if (csi2->use_fs_irq) - reg |= ISPCSI2_CTX_IRQSTATUS_FS_IRQ; - for (i = 0; i < 8; i++) { - isp_reg_writel(isp, reg, csi2->regs1, + isp_reg_writel(isp, ISPCSI2_CTX_IRQSTATUS_FE_IRQ, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(i)); if (enable) isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), - reg); + ISPCSI2_CTX_IRQSTATUS_FE_IRQ); else isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), - reg); + ISPCSI2_CTX_IRQSTATUS_FE_IRQ); } } @@ -690,14 +686,6 @@ static void csi2_isr_ctx(struct isp_csi2_device *csi2, status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); - /* Propagate frame number */ - if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) { - struct isp_pipeline *pipe = - to_isp_pipeline(&csi2->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - } - if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ)) return; @@ -1047,14 +1035,12 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct isp_device *isp = csi2->isp; - struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); struct isp_video *video_out = &csi2->video_out; switch (enable) { case ISP_PIPELINE_STREAM_CONTINUOUS: if (omap3isp_csiphy_acquire(csi2->phy) < 0) return -ENODEV; - csi2->use_fs_irq = pipe->do_propagation; if (csi2->output & CSI2_OUTPUT_MEMORY) omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); csi2_configure(csi2); diff --git a/drivers/media/video/omap3isp/ispcsi2.h b/drivers/media/video/omap3isp/ispcsi2.h index 885ad79a7678..c57729b7e86e 100644 --- a/drivers/media/video/omap3isp/ispcsi2.h +++ b/drivers/media/video/omap3isp/ispcsi2.h @@ -145,7 +145,6 @@ struct isp_csi2_device { u32 output; /* output to CCDC, memory or both? */ bool dpcm_decompress; unsigned int frame_skip; - bool use_fs_irq; struct isp_csiphy *phy; struct isp_csi2_ctx_cfg contexts[ISP_CSI2_MAX_CTX_NUM + 1]; -- cgit v1.2.3 From ca7f4a3821aae53ab4ba5f7b14db9755b856d615 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 2 Mar 2012 13:03:01 -0300 Subject: [media] omap3isp: Handle omap3isp_csi2_reset() errors Handle errors from omap3isp_csi2_reset() in omap3isp_csiphy_acquire(). Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispcsiphy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/omap3isp/ispcsiphy.c b/drivers/media/video/omap3isp/ispcsiphy.c index 5be37ce7d0c2..348f67ebbbc9 100644 --- a/drivers/media/video/omap3isp/ispcsiphy.c +++ b/drivers/media/video/omap3isp/ispcsiphy.c @@ -186,7 +186,9 @@ int omap3isp_csiphy_acquire(struct isp_csiphy *phy) if (rval < 0) goto done; - omap3isp_csi2_reset(phy->csi2); + rval = omap3isp_csi2_reset(phy->csi2); + if (rval < 0) + goto done; csiphy_dphy_config(phy); csiphy_lanes_config(phy); -- cgit v1.2.3 From 213cf90b9ad07d40c1e7aa5781907695107fe188 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 26 Mar 2012 08:54:26 -0300 Subject: [media] omap3isp: preview: Skip brightness and contrast in configuration ioctl Brightness and contrast are handled through V4L2 controls. Their configuration bit in the preview engine update attributes table is set to -1 to reflect that. However, the VIDIOC_OMAP3ISP_PRV_CFG ioctl handler doesn't handle -1 correctly as a configuration bit value, and erroneously considers that the parameter has been selected for update by the ioctl caller. Fix this. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 6d0fb2c8c26d..cf5014f2302d 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -903,7 +903,7 @@ static int preview_config(struct isp_prev_device *prev, attr = &update_attrs[i]; bit = 0; - if (!(cfg->update & attr->cfg_bit)) + if (attr->cfg_bit == -1 || !(cfg->update & attr->cfg_bit)) continue; bit = cfg->flag & attr->cfg_bit; -- cgit v1.2.3 From f22926e06e1ab7aa5f5c532afa4591f3d9baf59c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 26 Mar 2012 10:24:50 -0300 Subject: [media] omap3isp: preview: Optimize parameters setup for the common case If no parameter needs to be modified, make preview_config() and preview_setup_hw() return immediately. This speeds up interrupt handling in the common case. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index cf5014f2302d..04c96899f005 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -889,6 +889,9 @@ static int preview_config(struct isp_prev_device *prev, struct preview_update *attr; int i, bit, rval = 0; + if (cfg->update == 0) + return 0; + params = &prev->params; if (prev->state != ISP_PIPELINE_STREAM_STOPPED) { @@ -944,6 +947,9 @@ static void preview_setup_hw(struct isp_prev_device *prev) int i, bit; void *param_ptr; + if (prev->update == 0) + return; + for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { attr = &update_attrs[i]; -- cgit v1.2.3 From 87920dd4d93924a4b4aba01078ab606406649a82 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 28 Mar 2012 08:53:15 -0300 Subject: [media] omap3isp: preview: Remove averager parameter update flag The flag isn't used, remove it. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h index 09686607973c..67723c7e07cf 100644 --- a/drivers/media/video/omap3isp/isppreview.h +++ b/drivers/media/video/omap3isp/isppreview.h @@ -66,8 +66,7 @@ #define PREV_CONTRAST (1 << 17) #define PREV_BRIGHTNESS (1 << 18) -#define PREV_AVERAGER (1 << 19) -#define PREV_FEATURES_END (1 << 20) +#define PREV_FEATURES_END (1 << 19) enum preview_input_entity { PREVIEW_INPUT_NONE, -- cgit v1.2.3 From 9c3444c1b2127ab037a10785b671aec6f0713a4a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 5 Apr 2012 11:24:04 -0300 Subject: [media] omap3isp: preview: Remove unused isptables_update structure definition Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.h | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h index 67723c7e07cf..b7f979a18dab 100644 --- a/drivers/media/video/omap3isp/isppreview.h +++ b/drivers/media/video/omap3isp/isppreview.h @@ -121,26 +121,6 @@ struct prev_params { u8 brightness; }; -/* - * struct isptables_update - Structure for Table Configuration. - * @update: Specifies which tables should be updated. - * @flag: Specifies which tables should be enabled. - * @nf: Pointer to structure for Noise Filter - * @lsc: Pointer to LSC gain table. (currently not used) - * @gamma: Pointer to gamma correction tables. - * @cfa: Pointer to color filter array configuration. - * @wbal: Pointer to colour and digital gain configuration. - */ -struct isptables_update { - u32 update; - u32 flag; - struct omap3isp_prev_nf *nf; - u32 *lsc; - struct omap3isp_prev_gtables *gamma; - struct omap3isp_prev_cfa *cfa; - struct omap3isp_prev_wbal *wbal; -}; - /* Sink and source previewer pads */ #define PREV_PAD_SINK 0 #define PREV_PAD_SOURCE 1 -- cgit v1.2.3 From 3108e0261111412e9c459124bc07b34fc0d28f32 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 5 Apr 2012 12:38:23 -0300 Subject: [media] omap3isp: preview: Merge configuration and feature bits The preview engine parameters are referenced by a value suitable for being used in a bitmask. Two macros named OMAP3ISP_PREV_* and PREV_* are declared for each parameter and are used interchangeably. Remove the private macro. Replace the configuration bit field in the parameter update attributes structure with a boolean that indicates whether the parameter can be updated through the preview engine configuration ioctl. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 103 +++++++++++++++--------------- drivers/media/video/omap3isp/isppreview.h | 26 ++------ 2 files changed, 57 insertions(+), 72 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 04c96899f005..a4009077e444 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -653,7 +653,7 @@ preview_update_contrast(struct isp_prev_device *prev, u8 contrast) if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) { params->contrast = contrast * ISPPRV_CONTRAST_UNITS; - prev->update |= PREV_CONTRAST; + prev->update |= OMAP3ISP_PREV_CONTRAST; } } @@ -685,7 +685,7 @@ preview_update_brightness(struct isp_prev_device *prev, u8 brightness) if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) { params->brightness = brightness * ISPPRV_BRIGHT_UNITS; - prev->update |= PREV_BRIGHTNESS; + prev->update |= OMAP3ISP_PREV_BRIGHTNESS; } } @@ -723,70 +723,70 @@ preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) /* preview parameters update structure */ struct preview_update { - int cfg_bit; int feature_bit; void (*config)(struct isp_prev_device *, const void *); void (*enable)(struct isp_prev_device *, u8); + bool skip; }; static struct preview_update update_attrs[] = { - {OMAP3ISP_PREV_LUMAENH, PREV_LUMA_ENHANCE, + {OMAP3ISP_PREV_LUMAENH, preview_config_luma_enhancement, preview_enable_luma_enhancement}, - {OMAP3ISP_PREV_INVALAW, PREV_INVERSE_ALAW, + {OMAP3ISP_PREV_INVALAW, NULL, preview_enable_invalaw}, - {OMAP3ISP_PREV_HRZ_MED, PREV_HORZ_MEDIAN_FILTER, + {OMAP3ISP_PREV_HRZ_MED, preview_config_hmed, preview_enable_hmed}, - {OMAP3ISP_PREV_CFA, PREV_CFA, + {OMAP3ISP_PREV_CFA, preview_config_cfa, preview_enable_cfa}, - {OMAP3ISP_PREV_CHROMA_SUPP, PREV_CHROMA_SUPPRESS, + {OMAP3ISP_PREV_CHROMA_SUPP, preview_config_chroma_suppression, preview_enable_chroma_suppression}, - {OMAP3ISP_PREV_WB, PREV_WB, + {OMAP3ISP_PREV_WB, preview_config_whitebalance, NULL}, - {OMAP3ISP_PREV_BLKADJ, PREV_BLKADJ, + {OMAP3ISP_PREV_BLKADJ, preview_config_blkadj, NULL}, - {OMAP3ISP_PREV_RGB2RGB, PREV_RGB2RGB, + {OMAP3ISP_PREV_RGB2RGB, preview_config_rgb_blending, NULL}, - {OMAP3ISP_PREV_COLOR_CONV, PREV_COLOR_CONV, + {OMAP3ISP_PREV_COLOR_CONV, preview_config_rgb_to_ycbcr, NULL}, - {OMAP3ISP_PREV_YC_LIMIT, PREV_YCLIMITS, + {OMAP3ISP_PREV_YC_LIMIT, preview_config_yc_range, NULL}, - {OMAP3ISP_PREV_DEFECT_COR, PREV_DEFECT_COR, + {OMAP3ISP_PREV_DEFECT_COR, preview_config_dcor, preview_enable_dcor}, - {OMAP3ISP_PREV_GAMMABYPASS, PREV_GAMMA_BYPASS, + {OMAP3ISP_PREV_GAMMABYPASS, NULL, preview_enable_gammabypass}, - {OMAP3ISP_PREV_DRK_FRM_CAPTURE, PREV_DARK_FRAME_CAPTURE, + {OMAP3ISP_PREV_DRK_FRM_CAPTURE, NULL, preview_enable_drkframe_capture}, - {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, PREV_DARK_FRAME_SUBTRACT, + {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, NULL, preview_enable_drkframe}, - {OMAP3ISP_PREV_LENS_SHADING, PREV_LENS_SHADING, + {OMAP3ISP_PREV_LENS_SHADING, preview_config_drkf_shadcomp, preview_enable_drkframe}, - {OMAP3ISP_PREV_NF, PREV_NOISE_FILTER, + {OMAP3ISP_PREV_NF, preview_config_noisefilter, preview_enable_noisefilter}, - {OMAP3ISP_PREV_GAMMA, PREV_GAMMA, + {OMAP3ISP_PREV_GAMMA, preview_config_gammacorrn, NULL}, - {-1, PREV_CONTRAST, + {OMAP3ISP_PREV_CONTRAST, preview_config_contrast, - NULL}, - {-1, PREV_BRIGHTNESS, + NULL, true}, + {OMAP3ISP_PREV_BRIGHTNESS, preview_config_brightness, - NULL}, + NULL, true}, }; /* @@ -810,59 +810,59 @@ __preview_get_ptrs(struct prev_params *params, void **param, } switch (bit) { - case PREV_HORZ_MEDIAN_FILTER: + case OMAP3ISP_PREV_HRZ_MED: *param = ¶ms->hmed; CHKARG(configs, config, hmed) return sizeof(params->hmed); - case PREV_NOISE_FILTER: + case OMAP3ISP_PREV_NF: *param = ¶ms->nf; CHKARG(configs, config, nf) return sizeof(params->nf); break; - case PREV_CFA: + case OMAP3ISP_PREV_CFA: *param = ¶ms->cfa; CHKARG(configs, config, cfa) return sizeof(params->cfa); - case PREV_LUMA_ENHANCE: + case OMAP3ISP_PREV_LUMAENH: *param = ¶ms->luma; CHKARG(configs, config, luma) return sizeof(params->luma); - case PREV_CHROMA_SUPPRESS: + case OMAP3ISP_PREV_CHROMA_SUPP: *param = ¶ms->csup; CHKARG(configs, config, csup) return sizeof(params->csup); - case PREV_DEFECT_COR: + case OMAP3ISP_PREV_DEFECT_COR: *param = ¶ms->dcor; CHKARG(configs, config, dcor) return sizeof(params->dcor); - case PREV_BLKADJ: + case OMAP3ISP_PREV_BLKADJ: *param = ¶ms->blk_adj; CHKARG(configs, config, blkadj) return sizeof(params->blk_adj); - case PREV_YCLIMITS: + case OMAP3ISP_PREV_YC_LIMIT: *param = ¶ms->yclimit; CHKARG(configs, config, yclimit) return sizeof(params->yclimit); - case PREV_RGB2RGB: + case OMAP3ISP_PREV_RGB2RGB: *param = ¶ms->rgb2rgb; CHKARG(configs, config, rgb2rgb) return sizeof(params->rgb2rgb); - case PREV_COLOR_CONV: + case OMAP3ISP_PREV_COLOR_CONV: *param = ¶ms->rgb2ycbcr; CHKARG(configs, config, csc) return sizeof(params->rgb2ycbcr); - case PREV_WB: + case OMAP3ISP_PREV_WB: *param = ¶ms->wbal; CHKARG(configs, config, wbal) return sizeof(params->wbal); - case PREV_GAMMA: + case OMAP3ISP_PREV_GAMMA: *param = ¶ms->gamma; CHKARG(configs, config, gamma) return sizeof(params->gamma); - case PREV_CONTRAST: + case OMAP3ISP_PREV_CONTRAST: *param = ¶ms->contrast; return 0; - case PREV_BRIGHTNESS: + case OMAP3ISP_PREV_BRIGHTNESS: *param = ¶ms->brightness; return 0; default: @@ -906,10 +906,10 @@ static int preview_config(struct isp_prev_device *prev, attr = &update_attrs[i]; bit = 0; - if (attr->cfg_bit == -1 || !(cfg->update & attr->cfg_bit)) + if (attr->skip || !(cfg->update & attr->feature_bit)) continue; - bit = cfg->flag & attr->cfg_bit; + bit = cfg->flag & attr->feature_bit; if (bit) { void *to = NULL, __user *from = NULL; unsigned long sz = 0; @@ -1039,23 +1039,24 @@ static void preview_config_input_size(struct isp_prev_device *prev) unsigned int slv = prev->crop.top; unsigned int elv = prev->crop.top + prev->crop.height - 1; - if (params->features & PREV_CFA) { + if (params->features & OMAP3ISP_PREV_CFA) { sph -= 2; eph += 2; slv -= 2; elv += 2; } - if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) { + if (params->features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) { sph -= 2; eph += 2; slv -= 2; elv += 2; } - if (params->features & PREV_HORZ_MEDIAN_FILTER) { + if (params->features & OMAP3ISP_PREV_HRZ_MED) { sph -= 2; eph += 2; } - if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE)) + if (params->features & (OMAP3ISP_PREV_CHROMA_SUPP | + OMAP3ISP_PREV_LUMAENH)) sph -= 2; isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, @@ -1190,7 +1191,7 @@ int omap3isp_preview_busy(struct isp_prev_device *prev) */ void omap3isp_preview_restore_context(struct isp_device *isp) { - isp->isp_prev.update = PREV_FEATURES_END - 1; + isp->isp_prev.update = OMAP3ISP_PREV_FEATURES_END - 1; preview_setup_hw(&isp->isp_prev); } @@ -1293,12 +1294,14 @@ static void preview_init_params(struct isp_prev_device *prev) params->yclimit.minY = ISPPRV_YC_MIN; params->yclimit.maxY = ISPPRV_YC_MAX; - params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER - | PREV_GAMMA | PREV_BLKADJ | PREV_YCLIMITS - | PREV_RGB2RGB | PREV_COLOR_CONV | PREV_WB - | PREV_BRIGHTNESS | PREV_CONTRAST; + params->features = OMAP3ISP_PREV_CFA | OMAP3ISP_PREV_DEFECT_COR + | OMAP3ISP_PREV_NF | OMAP3ISP_PREV_GAMMA + | OMAP3ISP_PREV_BLKADJ | OMAP3ISP_PREV_YC_LIMIT + | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV + | OMAP3ISP_PREV_WB | OMAP3ISP_PREV_BRIGHTNESS + | OMAP3ISP_PREV_CONTRAST; - prev->update = PREV_FEATURES_END - 1; + prev->update = OMAP3ISP_PREV_FEATURES_END - 1; } /* diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h index b7f979a18dab..a0d28070a853 100644 --- a/drivers/media/video/omap3isp/isppreview.h +++ b/drivers/media/video/omap3isp/isppreview.h @@ -45,28 +45,10 @@ #define ISPPRV_CONTRAST_HIGH 0xFF #define ISPPRV_CONTRAST_UNITS 0x1 -/* Features list */ -#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH -#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW -#define PREV_HORZ_MEDIAN_FILTER OMAP3ISP_PREV_HRZ_MED -#define PREV_CFA OMAP3ISP_PREV_CFA -#define PREV_CHROMA_SUPPRESS OMAP3ISP_PREV_CHROMA_SUPP -#define PREV_WB OMAP3ISP_PREV_WB -#define PREV_BLKADJ OMAP3ISP_PREV_BLKADJ -#define PREV_RGB2RGB OMAP3ISP_PREV_RGB2RGB -#define PREV_COLOR_CONV OMAP3ISP_PREV_COLOR_CONV -#define PREV_YCLIMITS OMAP3ISP_PREV_YC_LIMIT -#define PREV_DEFECT_COR OMAP3ISP_PREV_DEFECT_COR -#define PREV_GAMMA_BYPASS OMAP3ISP_PREV_GAMMABYPASS -#define PREV_DARK_FRAME_CAPTURE OMAP3ISP_PREV_DRK_FRM_CAPTURE -#define PREV_DARK_FRAME_SUBTRACT OMAP3ISP_PREV_DRK_FRM_SUBTRACT -#define PREV_LENS_SHADING OMAP3ISP_PREV_LENS_SHADING -#define PREV_NOISE_FILTER OMAP3ISP_PREV_NF -#define PREV_GAMMA OMAP3ISP_PREV_GAMMA - -#define PREV_CONTRAST (1 << 17) -#define PREV_BRIGHTNESS (1 << 18) -#define PREV_FEATURES_END (1 << 19) +/* Additional features not listed in linux/omap3isp.h */ +#define OMAP3ISP_PREV_CONTRAST (1 << 17) +#define OMAP3ISP_PREV_BRIGHTNESS (1 << 18) +#define OMAP3ISP_PREV_FEATURES_END (1 << 19) enum preview_input_entity { PREVIEW_INPUT_NONE, -- cgit v1.2.3 From 7ed5de97511a3443d543cf569fb70bea11563ff0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 5 Apr 2012 13:51:17 -0300 Subject: [media] omap3isp: preview: Remove update_attrs feature_bit field The feature_bit is always equal to 1 << array position, remove it and compute the value dynamically. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 107 +++++++++++++++--------------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index a4009077e444..755006b946cb 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -723,70 +723,71 @@ preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) /* preview parameters update structure */ struct preview_update { - int feature_bit; void (*config)(struct isp_prev_device *, const void *); void (*enable)(struct isp_prev_device *, u8); bool skip; }; +/* Keep the array indexed by the OMAP3ISP_PREV_* bit number. */ static struct preview_update update_attrs[] = { - {OMAP3ISP_PREV_LUMAENH, + /* OMAP3ISP_PREV_LUMAENH */ { preview_config_luma_enhancement, - preview_enable_luma_enhancement}, - {OMAP3ISP_PREV_INVALAW, + preview_enable_luma_enhancement, + }, /* OMAP3ISP_PREV_INVALAW */ { NULL, - preview_enable_invalaw}, - {OMAP3ISP_PREV_HRZ_MED, + preview_enable_invalaw, + }, /* OMAP3ISP_PREV_HRZ_MED */ { preview_config_hmed, - preview_enable_hmed}, - {OMAP3ISP_PREV_CFA, + preview_enable_hmed, + }, /* OMAP3ISP_PREV_CFA */ { preview_config_cfa, - preview_enable_cfa}, - {OMAP3ISP_PREV_CHROMA_SUPP, + preview_enable_cfa, + }, /* OMAP3ISP_PREV_CHROMA_SUPP */ { preview_config_chroma_suppression, - preview_enable_chroma_suppression}, - {OMAP3ISP_PREV_WB, + preview_enable_chroma_suppression, + }, /* OMAP3ISP_PREV_WB */ { preview_config_whitebalance, - NULL}, - {OMAP3ISP_PREV_BLKADJ, + NULL, + }, /* OMAP3ISP_PREV_BLKADJ */ { preview_config_blkadj, - NULL}, - {OMAP3ISP_PREV_RGB2RGB, + NULL, + }, /* OMAP3ISP_PREV_RGB2RGB */ { preview_config_rgb_blending, - NULL}, - {OMAP3ISP_PREV_COLOR_CONV, + NULL, + }, /* OMAP3ISP_PREV_COLOR_CONV */ { preview_config_rgb_to_ycbcr, - NULL}, - {OMAP3ISP_PREV_YC_LIMIT, + NULL, + }, /* OMAP3ISP_PREV_YC_LIMIT */ { preview_config_yc_range, - NULL}, - {OMAP3ISP_PREV_DEFECT_COR, + NULL, + }, /* OMAP3ISP_PREV_DEFECT_COR */ { preview_config_dcor, - preview_enable_dcor}, - {OMAP3ISP_PREV_GAMMABYPASS, + preview_enable_dcor, + }, /* OMAP3ISP_PREV_GAMMABYPASS */ { NULL, - preview_enable_gammabypass}, - {OMAP3ISP_PREV_DRK_FRM_CAPTURE, + preview_enable_gammabypass, + }, /* OMAP3ISP_PREV_DRK_FRM_CAPTURE */ { NULL, - preview_enable_drkframe_capture}, - {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, + preview_enable_drkframe_capture, + }, /* OMAP3ISP_PREV_DRK_FRM_SUBTRACT */ { NULL, - preview_enable_drkframe}, - {OMAP3ISP_PREV_LENS_SHADING, + preview_enable_drkframe, + }, /* OMAP3ISP_PREV_LENS_SHADING */ { preview_config_drkf_shadcomp, - preview_enable_drkframe}, - {OMAP3ISP_PREV_NF, + preview_enable_drkframe, + }, /* OMAP3ISP_PREV_NF */ { preview_config_noisefilter, - preview_enable_noisefilter}, - {OMAP3ISP_PREV_GAMMA, + preview_enable_noisefilter, + }, /* OMAP3ISP_PREV_GAMMA */ { preview_config_gammacorrn, - NULL}, - {OMAP3ISP_PREV_CONTRAST, + NULL, + }, /* OMAP3ISP_PREV_CONTRAST */ { preview_config_contrast, - NULL, true}, - {OMAP3ISP_PREV_BRIGHTNESS, + NULL, true, + }, /* OMAP3ISP_PREV_BRIGHTNESS */ { preview_config_brightness, - NULL, true}, + NULL, true, + }, }; /* @@ -904,30 +905,28 @@ static int preview_config(struct isp_prev_device *prev, for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { attr = &update_attrs[i]; - bit = 0; + bit = 1 << i; - if (attr->skip || !(cfg->update & attr->feature_bit)) + if (attr->skip || !(cfg->update & bit)) continue; - bit = cfg->flag & attr->feature_bit; - if (bit) { + if (cfg->flag & bit) { void *to = NULL, __user *from = NULL; unsigned long sz = 0; - sz = __preview_get_ptrs(params, &to, cfg, &from, - bit); + sz = __preview_get_ptrs(params, &to, cfg, &from, bit); if (to && from && sz) { if (copy_from_user(to, from, sz)) { rval = -EFAULT; break; } } - params->features |= attr->feature_bit; + params->features |= bit; } else { - params->features &= ~attr->feature_bit; + params->features &= ~bit; } - prev->update |= attr->feature_bit; + prev->update |= bit; } prev->shadow_update = 0; @@ -944,7 +943,8 @@ static void preview_setup_hw(struct isp_prev_device *prev) { struct prev_params *params = &prev->params; struct preview_update *attr; - int i, bit; + unsigned int bit; + unsigned int i; void *param_ptr; if (prev->update == 0) @@ -952,11 +952,12 @@ static void preview_setup_hw(struct isp_prev_device *prev) for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { attr = &update_attrs[i]; + bit = 1 << i; - if (!(prev->update & attr->feature_bit)) + if (!(prev->update & bit)) continue; - bit = params->features & attr->feature_bit; - if (bit) { + + if (params->features & bit) { if (attr->config) { __preview_get_ptrs(params, ¶m_ptr, NULL, NULL, bit); @@ -968,7 +969,7 @@ static void preview_setup_hw(struct isp_prev_device *prev) if (attr->enable) attr->enable(prev, 0); - prev->update &= ~attr->feature_bit; + prev->update &= ~bit; } } -- cgit v1.2.3 From 9b00184d1ce11fc00412d1c1840fda45e8357d1f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 5 Apr 2012 13:51:17 -0300 Subject: [media] omap3isp: preview: Rename prev_params fields to match userspace API Rename the blk_adj and rgb2ycbcr fields to blkadj and csc respectively. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 16 ++++++++-------- drivers/media/video/omap3isp/isppreview.h | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 755006b946cb..e294f13741f8 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -837,9 +837,9 @@ __preview_get_ptrs(struct prev_params *params, void **param, CHKARG(configs, config, dcor) return sizeof(params->dcor); case OMAP3ISP_PREV_BLKADJ: - *param = ¶ms->blk_adj; + *param = ¶ms->blkadj; CHKARG(configs, config, blkadj) - return sizeof(params->blk_adj); + return sizeof(params->blkadj); case OMAP3ISP_PREV_YC_LIMIT: *param = ¶ms->yclimit; CHKARG(configs, config, yclimit) @@ -849,9 +849,9 @@ __preview_get_ptrs(struct prev_params *params, void **param, CHKARG(configs, config, rgb2rgb) return sizeof(params->rgb2rgb); case OMAP3ISP_PREV_COLOR_CONV: - *param = ¶ms->rgb2ycbcr; + *param = ¶ms->csc; CHKARG(configs, config, csc) - return sizeof(params->rgb2ycbcr); + return sizeof(params->csc); case OMAP3ISP_PREV_WB: *param = ¶ms->wbal; CHKARG(configs, config, wbal) @@ -1285,11 +1285,11 @@ static void preview_init_params(struct isp_prev_device *prev) params->wbal.coef1 = FLR_WBAL_COEF; params->wbal.coef2 = FLR_WBAL_COEF; params->wbal.coef3 = FLR_WBAL_COEF; - params->blk_adj.red = FLR_BLKADJ_RED; - params->blk_adj.green = FLR_BLKADJ_GREEN; - params->blk_adj.blue = FLR_BLKADJ_BLUE; + params->blkadj.red = FLR_BLKADJ_RED; + params->blkadj.green = FLR_BLKADJ_GREEN; + params->blkadj.blue = FLR_BLKADJ_BLUE; params->rgb2rgb = flr_rgb2rgb; - params->rgb2ycbcr = flr_prev_csc; + params->csc = flr_prev_csc; params->yclimit.minC = ISPPRV_YC_MIN; params->yclimit.maxC = ISPPRV_YC_MAX; params->yclimit.minY = ISPPRV_YC_MIN; diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h index a0d28070a853..6ee830623d5d 100644 --- a/drivers/media/video/omap3isp/isppreview.h +++ b/drivers/media/video/omap3isp/isppreview.h @@ -77,9 +77,9 @@ enum preview_ycpos_mode { * @dcor: Noise filter coefficients. * @gamma: Gamma coefficients. * @wbal: White Balance parameters. - * @blk_adj: Black adjustment parameters. + * @blkadj: Black adjustment parameters. * @rgb2rgb: RGB blending parameters. - * @rgb2ycbcr: RGB to ycbcr parameters. + * @csc: Color space conversion (RGB to YCbCr) parameters. * @hmed: Horizontal median filter. * @yclimit: YC limits parameters. * @contrast: Contrast. @@ -94,9 +94,9 @@ struct prev_params { struct omap3isp_prev_dcor dcor; struct omap3isp_prev_gtables gamma; struct omap3isp_prev_wbal wbal; - struct omap3isp_prev_blkadj blk_adj; + struct omap3isp_prev_blkadj blkadj; struct omap3isp_prev_rgbtorgb rgb2rgb; - struct omap3isp_prev_csc rgb2ycbcr; + struct omap3isp_prev_csc csc; struct omap3isp_prev_hmed hmed; struct omap3isp_prev_yclimit yclimit; u8 contrast; -- cgit v1.2.3 From bac387efbb88cf0e8df6f46a38387897cea464ee Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 9 Apr 2012 09:25:34 -0300 Subject: [media] omap3isp: preview: Simplify configuration parameters access Instead of using a large switch/case statement to return offsets to and sizes of individual preview engine parameters in the parameters and configuration structures, store the information in the update_attrs table and use it at runtime. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 134 ++++++++++++++---------------- 1 file changed, 62 insertions(+), 72 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index e294f13741f8..fd1807d36d40 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -725,44 +725,77 @@ preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) struct preview_update { void (*config)(struct isp_prev_device *, const void *); void (*enable)(struct isp_prev_device *, u8); + unsigned int param_offset; + unsigned int param_size; + unsigned int config_offset; bool skip; }; /* Keep the array indexed by the OMAP3ISP_PREV_* bit number. */ -static struct preview_update update_attrs[] = { +static const struct preview_update update_attrs[] = { /* OMAP3ISP_PREV_LUMAENH */ { preview_config_luma_enhancement, preview_enable_luma_enhancement, + offsetof(struct prev_params, luma), + FIELD_SIZEOF(struct prev_params, luma), + offsetof(struct omap3isp_prev_update_config, luma), }, /* OMAP3ISP_PREV_INVALAW */ { NULL, preview_enable_invalaw, }, /* OMAP3ISP_PREV_HRZ_MED */ { preview_config_hmed, preview_enable_hmed, + offsetof(struct prev_params, hmed), + FIELD_SIZEOF(struct prev_params, hmed), + offsetof(struct omap3isp_prev_update_config, hmed), }, /* OMAP3ISP_PREV_CFA */ { preview_config_cfa, preview_enable_cfa, + offsetof(struct prev_params, cfa), + FIELD_SIZEOF(struct prev_params, cfa), + offsetof(struct omap3isp_prev_update_config, cfa), }, /* OMAP3ISP_PREV_CHROMA_SUPP */ { preview_config_chroma_suppression, preview_enable_chroma_suppression, + offsetof(struct prev_params, csup), + FIELD_SIZEOF(struct prev_params, csup), + offsetof(struct omap3isp_prev_update_config, csup), }, /* OMAP3ISP_PREV_WB */ { preview_config_whitebalance, NULL, + offsetof(struct prev_params, wbal), + FIELD_SIZEOF(struct prev_params, wbal), + offsetof(struct omap3isp_prev_update_config, wbal), }, /* OMAP3ISP_PREV_BLKADJ */ { preview_config_blkadj, NULL, + offsetof(struct prev_params, blkadj), + FIELD_SIZEOF(struct prev_params, blkadj), + offsetof(struct omap3isp_prev_update_config, blkadj), }, /* OMAP3ISP_PREV_RGB2RGB */ { preview_config_rgb_blending, NULL, + offsetof(struct prev_params, rgb2rgb), + FIELD_SIZEOF(struct prev_params, rgb2rgb), + offsetof(struct omap3isp_prev_update_config, rgb2rgb), }, /* OMAP3ISP_PREV_COLOR_CONV */ { preview_config_rgb_to_ycbcr, NULL, + offsetof(struct prev_params, csc), + FIELD_SIZEOF(struct prev_params, csc), + offsetof(struct omap3isp_prev_update_config, csc), }, /* OMAP3ISP_PREV_YC_LIMIT */ { preview_config_yc_range, NULL, + offsetof(struct prev_params, yclimit), + FIELD_SIZEOF(struct prev_params, yclimit), + offsetof(struct omap3isp_prev_update_config, yclimit), }, /* OMAP3ISP_PREV_DEFECT_COR */ { preview_config_dcor, preview_enable_dcor, + offsetof(struct prev_params, dcor), + FIELD_SIZEOF(struct prev_params, dcor), + offsetof(struct omap3isp_prev_update_config, dcor), }, /* OMAP3ISP_PREV_GAMMABYPASS */ { NULL, preview_enable_gammabypass, @@ -778,15 +811,25 @@ static struct preview_update update_attrs[] = { }, /* OMAP3ISP_PREV_NF */ { preview_config_noisefilter, preview_enable_noisefilter, + offsetof(struct prev_params, nf), + FIELD_SIZEOF(struct prev_params, nf), + offsetof(struct omap3isp_prev_update_config, nf), }, /* OMAP3ISP_PREV_GAMMA */ { preview_config_gammacorrn, NULL, + offsetof(struct prev_params, gamma), + FIELD_SIZEOF(struct prev_params, gamma), + offsetof(struct omap3isp_prev_update_config, gamma), }, /* OMAP3ISP_PREV_CONTRAST */ { preview_config_contrast, - NULL, true, + NULL, + offsetof(struct prev_params, contrast), + 0, true, }, /* OMAP3ISP_PREV_BRIGHTNESS */ { preview_config_brightness, - NULL, true, + NULL, + offsetof(struct prev_params, brightness), + 0, true, }, }; @@ -803,75 +846,22 @@ static struct preview_update update_attrs[] = { static u32 __preview_get_ptrs(struct prev_params *params, void **param, struct omap3isp_prev_update_config *configs, - void __user **config, u32 bit) + void __user **config, unsigned int index) { -#define CHKARG(cfgs, cfg, field) \ - if (cfgs && cfg) { \ - *(cfg) = (cfgs)->field; \ - } + const struct preview_update *info = &update_attrs[index]; - switch (bit) { - case OMAP3ISP_PREV_HRZ_MED: - *param = ¶ms->hmed; - CHKARG(configs, config, hmed) - return sizeof(params->hmed); - case OMAP3ISP_PREV_NF: - *param = ¶ms->nf; - CHKARG(configs, config, nf) - return sizeof(params->nf); - break; - case OMAP3ISP_PREV_CFA: - *param = ¶ms->cfa; - CHKARG(configs, config, cfa) - return sizeof(params->cfa); - case OMAP3ISP_PREV_LUMAENH: - *param = ¶ms->luma; - CHKARG(configs, config, luma) - return sizeof(params->luma); - case OMAP3ISP_PREV_CHROMA_SUPP: - *param = ¶ms->csup; - CHKARG(configs, config, csup) - return sizeof(params->csup); - case OMAP3ISP_PREV_DEFECT_COR: - *param = ¶ms->dcor; - CHKARG(configs, config, dcor) - return sizeof(params->dcor); - case OMAP3ISP_PREV_BLKADJ: - *param = ¶ms->blkadj; - CHKARG(configs, config, blkadj) - return sizeof(params->blkadj); - case OMAP3ISP_PREV_YC_LIMIT: - *param = ¶ms->yclimit; - CHKARG(configs, config, yclimit) - return sizeof(params->yclimit); - case OMAP3ISP_PREV_RGB2RGB: - *param = ¶ms->rgb2rgb; - CHKARG(configs, config, rgb2rgb) - return sizeof(params->rgb2rgb); - case OMAP3ISP_PREV_COLOR_CONV: - *param = ¶ms->csc; - CHKARG(configs, config, csc) - return sizeof(params->csc); - case OMAP3ISP_PREV_WB: - *param = ¶ms->wbal; - CHKARG(configs, config, wbal) - return sizeof(params->wbal); - case OMAP3ISP_PREV_GAMMA: - *param = ¶ms->gamma; - CHKARG(configs, config, gamma) - return sizeof(params->gamma); - case OMAP3ISP_PREV_CONTRAST: - *param = ¶ms->contrast; - return 0; - case OMAP3ISP_PREV_BRIGHTNESS: - *param = ¶ms->brightness; - return 0; - default: + if (index >= ARRAY_SIZE(update_attrs)) { + if (config) + *config = NULL; *param = NULL; - *config = NULL; - break; + return 0; } - return 0; + + if (configs && config) + *config = *(void **)((void *)configs + info->config_offset); + + *param = (void *)params + info->param_offset; + return info->param_size; } /* @@ -886,8 +876,8 @@ __preview_get_ptrs(struct prev_params *params, void **param, static int preview_config(struct isp_prev_device *prev, struct omap3isp_prev_update_config *cfg) { + const struct preview_update *attr; struct prev_params *params; - struct preview_update *attr; int i, bit, rval = 0; if (cfg->update == 0) @@ -914,7 +904,7 @@ static int preview_config(struct isp_prev_device *prev, void *to = NULL, __user *from = NULL; unsigned long sz = 0; - sz = __preview_get_ptrs(params, &to, cfg, &from, bit); + sz = __preview_get_ptrs(params, &to, cfg, &from, i); if (to && from && sz) { if (copy_from_user(to, from, sz)) { rval = -EFAULT; @@ -942,7 +932,7 @@ static int preview_config(struct isp_prev_device *prev, static void preview_setup_hw(struct isp_prev_device *prev) { struct prev_params *params = &prev->params; - struct preview_update *attr; + const struct preview_update *attr; unsigned int bit; unsigned int i; void *param_ptr; @@ -960,7 +950,7 @@ static void preview_setup_hw(struct isp_prev_device *prev) if (params->features & bit) { if (attr->config) { __preview_get_ptrs(params, ¶m_ptr, NULL, - NULL, bit); + NULL, i); attr->config(prev, param_ptr); } if (attr->enable) -- cgit v1.2.3 From b0b29e1e29ee5357f36f27411b5881e470484da5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 26 Mar 2012 10:24:50 -0300 Subject: [media] omap3isp: preview: Shorten shadow update delay When applications modify preview engine parameters, the new values are applied to the hardware by the preview engine interrupt handler during vertical blanking. If the parameters are being changed when the interrupt handler is called, it just delays applying the parameters until the next frame. If an application modifies the parameters for every frame, and the preview engine interrupt is triggerred synchronously, the parameters are never applied to the hardware. Fix this by storing new parameters in a shadow copy, and switch the active parameters with the shadow values atomically. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 299 ++++++++++++++++++++---------- drivers/media/video/omap3isp/isppreview.h | 21 ++- 2 files changed, 212 insertions(+), 108 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index fd1807d36d40..5bdf9e742c8e 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -649,12 +649,18 @@ preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) static void preview_update_contrast(struct isp_prev_device *prev, u8 contrast) { - struct prev_params *params = &prev->params; + struct prev_params *params; + unsigned long flags; + + spin_lock_irqsave(&prev->params.lock, flags); + params = (prev->params.active & OMAP3ISP_PREV_CONTRAST) + ? &prev->params.params[0] : &prev->params.params[1]; if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) { params->contrast = contrast * ISPPRV_CONTRAST_UNITS; - prev->update |= OMAP3ISP_PREV_CONTRAST; + params->update |= OMAP3ISP_PREV_CONTRAST; } + spin_unlock_irqrestore(&prev->params.lock, flags); } /* @@ -681,12 +687,18 @@ preview_config_contrast(struct isp_prev_device *prev, const void *params) static void preview_update_brightness(struct isp_prev_device *prev, u8 brightness) { - struct prev_params *params = &prev->params; + struct prev_params *params; + unsigned long flags; + + spin_lock_irqsave(&prev->params.lock, flags); + params = (prev->params.active & OMAP3ISP_PREV_BRIGHTNESS) + ? &prev->params.params[0] : &prev->params.params[1]; if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) { params->brightness = brightness * ISPPRV_BRIGHT_UNITS; - prev->update |= OMAP3ISP_PREV_BRIGHTNESS; + params->update |= OMAP3ISP_PREV_BRIGHTNESS; } + spin_unlock_irqrestore(&prev->params.lock, flags); } /* @@ -721,6 +733,75 @@ preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC); } +static u32 +preview_params_lock(struct isp_prev_device *prev, u32 update, bool shadow) +{ + u32 active = prev->params.active; + + if (shadow) { + /* Mark all shadow parameters we are going to touch as busy. */ + prev->params.params[0].busy |= ~active & update; + prev->params.params[1].busy |= active & update; + } else { + /* Mark all active parameters we are going to touch as busy. */ + update = (prev->params.params[0].update & active) + | (prev->params.params[1].update & ~active); + + prev->params.params[0].busy |= active & update; + prev->params.params[1].busy |= ~active & update; + } + + return update; +} + +static void +preview_params_unlock(struct isp_prev_device *prev, u32 update, bool shadow) +{ + u32 active = prev->params.active; + + if (shadow) { + /* Set the update flag for shadow parameters that have been + * updated and clear the busy flag for all shadow parameters. + */ + prev->params.params[0].update |= (~active & update); + prev->params.params[1].update |= (active & update); + prev->params.params[0].busy &= active; + prev->params.params[1].busy &= ~active; + } else { + /* Clear the update flag for active parameters that have been + * applied and the busy flag for all active parameters. + */ + prev->params.params[0].update &= ~(active & update); + prev->params.params[1].update &= ~(~active & update); + prev->params.params[0].busy &= ~active; + prev->params.params[1].busy &= active; + } +} + +static void preview_params_switch(struct isp_prev_device *prev) +{ + u32 to_switch; + + /* Switch active parameters with updated shadow parameters when the + * shadow parameter has been updated and neither the active not the + * shadow parameter is busy. + */ + to_switch = (prev->params.params[0].update & ~prev->params.active) + | (prev->params.params[1].update & prev->params.active); + to_switch &= ~(prev->params.params[0].busy | + prev->params.params[1].busy); + if (to_switch == 0) + return; + + prev->params.active ^= to_switch; + + /* Remove the update flag for the shadow copy of parameters we have + * switched. + */ + prev->params.params[0].update &= ~(~prev->params.active & to_switch); + prev->params.params[1].update &= ~(prev->params.active & to_switch); +} + /* preview parameters update structure */ struct preview_update { void (*config)(struct isp_prev_device *, const void *); @@ -833,37 +914,6 @@ static const struct preview_update update_attrs[] = { }, }; -/* - * __preview_get_ptrs - helper function which return pointers to members - * of params and config structures. - * @params - pointer to preview_params structure. - * @param - return pointer to appropriate structure field. - * @configs - pointer to update config structure. - * @config - return pointer to appropriate structure field. - * @bit - for which feature to return pointers. - * Return size of corresponding prev_params member - */ -static u32 -__preview_get_ptrs(struct prev_params *params, void **param, - struct omap3isp_prev_update_config *configs, - void __user **config, unsigned int index) -{ - const struct preview_update *info = &update_attrs[index]; - - if (index >= ARRAY_SIZE(update_attrs)) { - if (config) - *config = NULL; - *param = NULL; - return 0; - } - - if (configs && config) - *config = *(void **)((void *)configs + info->config_offset); - - *param = (void *)params + info->param_offset; - return info->param_size; -} - /* * preview_config - Copy and update local structure with userspace preview * configuration. @@ -876,37 +926,41 @@ __preview_get_ptrs(struct prev_params *params, void **param, static int preview_config(struct isp_prev_device *prev, struct omap3isp_prev_update_config *cfg) { - const struct preview_update *attr; - struct prev_params *params; - int i, bit, rval = 0; + unsigned long flags; + unsigned int i; + int rval = 0; + u32 update; + u32 active; if (cfg->update == 0) return 0; - params = &prev->params; + /* Mark the shadow parameters we're going to update as busy. */ + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_lock(prev, cfg->update, true); + active = prev->params.active; + spin_unlock_irqrestore(&prev->params.lock, flags); - if (prev->state != ISP_PIPELINE_STREAM_STOPPED) { - unsigned long flags; - - spin_lock_irqsave(&prev->lock, flags); - prev->shadow_update = 1; - spin_unlock_irqrestore(&prev->lock, flags); - } + update = 0; for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { - attr = &update_attrs[i]; - bit = 1 << i; + const struct preview_update *attr = &update_attrs[i]; + struct prev_params *params; + unsigned int bit = 1 << i; if (attr->skip || !(cfg->update & bit)) continue; + params = &prev->params.params[!!(active & bit)]; + if (cfg->flag & bit) { - void *to = NULL, __user *from = NULL; - unsigned long sz = 0; + void __user *from = *(void * __user *) + ((void *)cfg + attr->config_offset); + void *to = (void *)params + attr->param_offset; + size_t size = attr->param_size; - sz = __preview_get_ptrs(params, &to, cfg, &from, i); - if (to && from && sz) { - if (copy_from_user(to, from, sz)) { + if (to && from && size) { + if (copy_from_user(to, from, size)) { rval = -EFAULT; break; } @@ -916,50 +970,59 @@ static int preview_config(struct isp_prev_device *prev, params->features &= ~bit; } - prev->update |= bit; + update |= bit; } - prev->shadow_update = 0; + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_unlock(prev, update, true); + preview_params_switch(prev); + spin_unlock_irqrestore(&prev->params.lock, flags); + return rval; } /* * preview_setup_hw - Setup preview registers and/or internal memory * @prev: pointer to preview private structure + * @update: Bitmask of parameters to setup + * @active: Bitmask of parameters active in set 0 * Note: can be called from interrupt context * Return none */ -static void preview_setup_hw(struct isp_prev_device *prev) +static void preview_setup_hw(struct isp_prev_device *prev, u32 update, + u32 active) { - struct prev_params *params = &prev->params; - const struct preview_update *attr; - unsigned int bit; unsigned int i; - void *param_ptr; + u32 features; - if (prev->update == 0) + if (update == 0) return; + features = (prev->params.params[0].features & active) + | (prev->params.params[1].features & ~active); + for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { - attr = &update_attrs[i]; - bit = 1 << i; + const struct preview_update *attr = &update_attrs[i]; + struct prev_params *params; + unsigned int bit = 1 << i; + void *param_ptr; - if (!(prev->update & bit)) + if (!(update & bit)) continue; + params = &prev->params.params[!(active & bit)]; + if (params->features & bit) { if (attr->config) { - __preview_get_ptrs(params, ¶m_ptr, NULL, - NULL, i); + param_ptr = (void *)params + attr->param_offset; attr->config(prev, param_ptr); } if (attr->enable) attr->enable(prev, 1); - } else + } else { if (attr->enable) attr->enable(prev, 0); - - prev->update &= ~bit; + } } } @@ -997,13 +1060,17 @@ preview_config_ycpos(struct isp_prev_device *prev, static void preview_config_averager(struct isp_prev_device *prev, u8 average) { struct isp_device *isp = to_isp_device(prev); + struct prev_params *params; int reg = 0; - if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER) + params = (prev->params.active & OMAP3ISP_PREV_CFA) + ? &prev->params.params[0] : &prev->params.params[1]; + + if (params->cfa.format == OMAP3ISP_CFAFMT_BAYER) reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT | ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT | average; - else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) + else if (params->cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT | ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT | average; @@ -1021,33 +1088,35 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) * * See the explanation at the PREV_MARGIN_* definitions for more details. */ -static void preview_config_input_size(struct isp_prev_device *prev) +static void preview_config_input_size(struct isp_prev_device *prev, u32 active) { struct isp_device *isp = to_isp_device(prev); - struct prev_params *params = &prev->params; unsigned int sph = prev->crop.left; unsigned int eph = prev->crop.left + prev->crop.width - 1; unsigned int slv = prev->crop.top; unsigned int elv = prev->crop.top + prev->crop.height - 1; + u32 features; - if (params->features & OMAP3ISP_PREV_CFA) { + features = (prev->params.params[0].features & active) + | (prev->params.params[1].features & ~active); + + if (features & OMAP3ISP_PREV_CFA) { sph -= 2; eph += 2; slv -= 2; elv += 2; } - if (params->features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) { + if (features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) { sph -= 2; eph += 2; slv -= 2; elv += 2; } - if (params->features & OMAP3ISP_PREV_HRZ_MED) { + if (features & OMAP3ISP_PREV_HRZ_MED) { sph -= 2; eph += 2; } - if (params->features & (OMAP3ISP_PREV_CHROMA_SUPP | - OMAP3ISP_PREV_LUMAENH)) + if (features & (OMAP3ISP_PREV_CHROMA_SUPP | OMAP3ISP_PREV_LUMAENH)) sph -= 2; isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, @@ -1182,8 +1251,16 @@ int omap3isp_preview_busy(struct isp_prev_device *prev) */ void omap3isp_preview_restore_context(struct isp_device *isp) { - isp->isp_prev.update = OMAP3ISP_PREV_FEATURES_END - 1; - preview_setup_hw(&isp->isp_prev); + struct isp_prev_device *prev = &isp->isp_prev; + const u32 update = OMAP3ISP_PREV_FEATURES_END - 1; + + prev->params.params[0].update = prev->params.active & update; + prev->params.params[1].update = ~prev->params.active & update; + + preview_setup_hw(prev, update, prev->params.active); + + prev->params.params[0].update = 0; + prev->params.params[1].update = 0; } /* @@ -1242,12 +1319,21 @@ static void preview_print_status(struct isp_prev_device *prev) /* * preview_init_params - init image processing parameters. * @prev: pointer to previewer private structure - * return none */ static void preview_init_params(struct isp_prev_device *prev) { - struct prev_params *params = &prev->params; - int i = 0; + struct prev_params *params; + unsigned int i; + + spin_lock_init(&prev->params.lock); + + prev->params.active = ~0; + prev->params.params[0].busy = 0; + prev->params.params[0].update = OMAP3ISP_PREV_FEATURES_END - 1; + prev->params.params[1].busy = 0; + prev->params.params[1].update = 0; + + params = &prev->params.params[0]; /* Init values */ params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS; @@ -1291,8 +1377,6 @@ static void preview_init_params(struct isp_prev_device *prev) | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV | OMAP3ISP_PREV_WB | OMAP3ISP_PREV_BRIGHTNESS | OMAP3ISP_PREV_CONTRAST; - - prev->update = OMAP3ISP_PREV_FEATURES_END - 1; } /* @@ -1321,8 +1405,17 @@ static void preview_configure(struct isp_prev_device *prev) { struct isp_device *isp = to_isp_device(prev); struct v4l2_mbus_framefmt *format; + unsigned long flags; + u32 update; + u32 active; - preview_setup_hw(prev); + spin_lock_irqsave(&prev->params.lock, flags); + /* Mark all active parameters we are going to touch as busy. */ + update = preview_params_lock(prev, 0, false); + active = prev->params.active; + spin_unlock_irqrestore(&prev->params.lock, flags); + + preview_setup_hw(prev, update, active); if (prev->output & PREVIEW_OUTPUT_MEMORY) isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, @@ -1343,7 +1436,7 @@ static void preview_configure(struct isp_prev_device *prev) preview_adjust_bandwidth(prev); - preview_config_input_size(prev); + preview_config_input_size(prev, active); if (prev->input == PREVIEW_INPUT_CCDC) preview_config_inlineoffset(prev, 0); @@ -1360,6 +1453,10 @@ static void preview_configure(struct isp_prev_device *prev) preview_config_averager(prev, 0); preview_config_ycpos(prev, format->code); + + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_unlock(prev, update, false); + spin_unlock_irqrestore(&prev->params.lock, flags); } /* ----------------------------------------------------------------------------- @@ -1448,25 +1545,30 @@ static void preview_isr_buffer(struct isp_prev_device *prev) void omap3isp_preview_isr(struct isp_prev_device *prev) { unsigned long flags; + u32 update; + u32 active; if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping)) return; - spin_lock_irqsave(&prev->lock, flags); - if (prev->shadow_update) - goto done; + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_switch(prev); + update = preview_params_lock(prev, 0, false); + active = prev->params.active; + spin_unlock_irqrestore(&prev->params.lock, flags); - preview_setup_hw(prev); - preview_config_input_size(prev); - -done: - spin_unlock_irqrestore(&prev->lock, flags); + preview_setup_hw(prev, update, active); + preview_config_input_size(prev, active); if (prev->input == PREVIEW_INPUT_MEMORY || prev->output & PREVIEW_OUTPUT_MEMORY) preview_isr_buffer(prev); else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS) preview_enable_oneshot(prev); + + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_unlock(prev, update, false); + spin_unlock_irqrestore(&prev->params.lock, flags); } /* ----------------------------------------------------------------------------- @@ -1552,7 +1654,6 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable) struct isp_video *video_out = &prev->video_out; struct isp_device *isp = to_isp_device(prev); struct device *dev = to_device(prev); - unsigned long flags; if (prev->state == ISP_PIPELINE_STREAM_STOPPED) { if (enable == ISP_PIPELINE_STREAM_STOPPED) @@ -1589,11 +1690,9 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable) if (omap3isp_module_sync_idle(&sd->entity, &prev->wait, &prev->stopping)) dev_dbg(dev, "%s: stop timeout.\n", sd->name); - spin_lock_irqsave(&prev->lock, flags); omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ); omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW); - spin_unlock_irqrestore(&prev->lock, flags); isp_video_dmaqueue_flags_clr(video_out); break; } @@ -2201,7 +2300,7 @@ error_video_in: } /* - * isp_preview_init - Previewer initialization. + * omap3isp_preview_init - Previewer initialization. * @dev : Pointer to ISP device * return -ENOMEM or zero on success */ @@ -2209,8 +2308,8 @@ int omap3isp_preview_init(struct isp_device *isp) { struct isp_prev_device *prev = &isp->isp_prev; - spin_lock_init(&prev->lock); init_waitqueue_head(&prev->wait); + preview_init_params(prev); return preview_init_entities(prev); diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h index 6ee830623d5d..6663ab64e4b1 100644 --- a/drivers/media/video/omap3isp/isppreview.h +++ b/drivers/media/video/omap3isp/isppreview.h @@ -69,6 +69,8 @@ enum preview_ycpos_mode { /* * struct prev_params - Structure for all configuration + * @busy: Bitmask of busy parameters (being updated or used) + * @update: Bitmask of the parameters to be updated * @features: Set of features enabled. * @cfa: CFA coefficients. * @csup: Chroma suppression coefficients. @@ -86,6 +88,8 @@ enum preview_ycpos_mode { * @brightness: Brightness. */ struct prev_params { + u32 busy; + u32 update; u32 features; struct omap3isp_prev_cfa cfa; struct omap3isp_prev_csup csup; @@ -118,12 +122,11 @@ struct prev_params { * @output: Bitmask of the active output * @video_in: Input video entity * @video_out: Output video entity - * @params: Module configuration data - * @shadow_update: If set, update the hardware configured in the next interrupt + * @params.params : Active and shadow parameters sets + * @params.active: Bitmask of parameters active in set 0 + * @params.lock: Parameters lock, protects params.active and params.shadow * @underrun: Whether the preview entity has queued buffers on the output * @state: Current preview pipeline state - * @lock: Shadow update lock - * @update: Bitmask of the parameters to be updated * * This structure is used to store the OMAP ISP Preview module Information. */ @@ -140,13 +143,15 @@ struct isp_prev_device { struct isp_video video_in; struct isp_video video_out; - struct prev_params params; - unsigned int shadow_update:1; + struct { + struct prev_params params[2]; + u32 active; + spinlock_t lock; + } params; + enum isp_pipeline_stream_state state; wait_queue_head_t wait; atomic_t stopping; - spinlock_t lock; - u32 update; }; struct isp_device; -- cgit v1.2.3 From 6173850c95e3c2e51bd670011ae347862b098635 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Apr 2012 13:05:12 -0300 Subject: [media] omap3isp: preview: Rename last occurences of *_rgb_to_ycbcr to *_csc For consistency reasons, rename the last remaining occurences of rgb_to_ycbcr to csc (color space conversion). Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 5bdf9e742c8e..f839cf866fa9 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -608,12 +608,12 @@ preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb) } /* - * Configures the RGB-YCbYCr conversion matrix + * Configures the color space conversion (RGB toYCbYCr) matrix * @prev_csc: Structure containing the RGB to YCbYCr matrix and the * YCbCr offset. */ static void -preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) +preview_config_csc(struct isp_prev_device *prev, const void *prev_csc) { struct isp_device *isp = to_isp_device(prev); const struct omap3isp_prev_csc *csc = prev_csc; @@ -860,7 +860,7 @@ static const struct preview_update update_attrs[] = { FIELD_SIZEOF(struct prev_params, rgb2rgb), offsetof(struct omap3isp_prev_update_config, rgb2rgb), }, /* OMAP3ISP_PREV_COLOR_CONV */ { - preview_config_rgb_to_ycbcr, + preview_config_csc, NULL, offsetof(struct prev_params, csc), FIELD_SIZEOF(struct prev_params, csc), -- cgit v1.2.3 From b2da46e52fe7871cba36e1a435844502c0eccf39 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 19 Apr 2012 13:38:29 -0300 Subject: [media] omap3isp: preview: Add support for greyscale input Configure CFA interpolation automatically based on the input format. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 52 ++++++++++++++++++------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index f839cf866fa9..420b131282d8 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -440,23 +440,6 @@ preview_enable_dcor(struct isp_prev_device *prev, u8 enable) ISPPRV_PCR_DCOREN); } -/* - * preview_enable_cfa - Enable/Disable the CFA Interpolation. - * @enable: 1 - Enables the CFA. - */ -static void -preview_enable_cfa(struct isp_prev_device *prev, u8 enable) -{ - struct isp_device *isp = to_isp_device(prev); - - if (enable) - isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, - ISPPRV_PCR_CFAEN); - else - isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, - ISPPRV_PCR_CFAEN); -} - /* * preview_enable_gammabypass - Enables/Disables the GammaByPass * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB. @@ -831,7 +814,7 @@ static const struct preview_update update_attrs[] = { offsetof(struct omap3isp_prev_update_config, hmed), }, /* OMAP3ISP_PREV_CFA */ { preview_config_cfa, - preview_enable_cfa, + NULL, offsetof(struct prev_params, cfa), FIELD_SIZEOF(struct prev_params, cfa), offsetof(struct omap3isp_prev_update_config, cfa), @@ -1077,6 +1060,27 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); } +/* + * preview_config_input_format - Configure the input format + * @prev: The preview engine + * @format: Format on the preview engine sink pad + * + * Enable CFA interpolation for Bayer formats and disable it for greyscale + * formats. + */ +static void preview_config_input_format(struct isp_prev_device *prev, + const struct v4l2_mbus_framefmt *format) +{ + struct isp_device *isp = to_isp_device(prev); + + if (format->code != V4L2_MBUS_FMT_Y10_1X10) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAEN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAEN); +} + /* * preview_config_input_size - Configure the input frame size * @@ -1090,6 +1094,7 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) */ static void preview_config_input_size(struct isp_prev_device *prev, u32 active) { + const struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK]; struct isp_device *isp = to_isp_device(prev); unsigned int sph = prev->crop.left; unsigned int eph = prev->crop.left + prev->crop.width - 1; @@ -1097,15 +1102,16 @@ static void preview_config_input_size(struct isp_prev_device *prev, u32 active) unsigned int elv = prev->crop.top + prev->crop.height - 1; u32 features; - features = (prev->params.params[0].features & active) - | (prev->params.params[1].features & ~active); - - if (features & OMAP3ISP_PREV_CFA) { + if (format->code == V4L2_MBUS_FMT_Y10_1X10) { sph -= 2; eph += 2; slv -= 2; elv += 2; } + + features = (prev->params.params[0].features & active) + | (prev->params.params[1].features & ~active); + if (features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) { sph -= 2; eph += 2; @@ -1436,6 +1442,7 @@ static void preview_configure(struct isp_prev_device *prev) preview_adjust_bandwidth(prev); + preview_config_input_format(prev, format); preview_config_input_size(prev, active); if (prev->input == PREVIEW_INPUT_CCDC) @@ -1723,6 +1730,7 @@ __preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, /* previewer format descriptions */ static const unsigned int preview_input_fmts[] = { + V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, -- cgit v1.2.3 From 79c3a07ddae6155bf30908c413fcef678510f59e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 22 Apr 2012 12:26:41 -0300 Subject: [media] omap3isp: Mark probe and cleanup functions with __devinit and __devexit Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c index df6416c706fd..0307ac39e440 100644 --- a/drivers/media/video/omap3isp/isp.c +++ b/drivers/media/video/omap3isp/isp.c @@ -1978,7 +1978,7 @@ error_csiphy: * * Always returns 0. */ -static int isp_remove(struct platform_device *pdev) +static int __devexit isp_remove(struct platform_device *pdev) { struct isp_device *isp = platform_get_drvdata(pdev); int i; @@ -2059,7 +2059,7 @@ static int isp_map_mem_resource(struct platform_device *pdev, * -EINVAL if couldn't install ISR, * or clk_get return error value. */ -static int isp_probe(struct platform_device *pdev) +static int __devinit isp_probe(struct platform_device *pdev) { struct isp_platform_data *pdata = pdev->dev.platform_data; struct isp_device *isp; @@ -2227,7 +2227,7 @@ MODULE_DEVICE_TABLE(platform, omap3isp_id_table); static struct platform_driver omap3isp_driver = { .probe = isp_probe, - .remove = isp_remove, + .remove = __devexit_p(isp_remove), .id_table = omap3isp_id_table, .driver = { .owner = THIS_MODULE, -- cgit v1.2.3 From a64909b806b156fd4579c948bbbdb15095f55058 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 20 Apr 2012 05:47:49 -0300 Subject: [media] omap3isp: ccdc: Add selection support on output formatter source pad Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispccdc.c | 180 ++++++++++++++++++++++++++++++--- drivers/media/video/omap3isp/ispccdc.h | 2 + 2 files changed, 169 insertions(+), 13 deletions(-) diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 17207c7037f7..8c73197005c6 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -38,6 +38,9 @@ #include "ispreg.h" #include "ispccdc.h" +#define CCDC_MIN_WIDTH 32 +#define CCDC_MIN_HEIGHT 32 + static struct v4l2_mbus_framefmt * __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, unsigned int pad, enum v4l2_subdev_format_whence which); @@ -1118,6 +1121,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) struct isp_parallel_platform_data *pdata = NULL; struct v4l2_subdev *sensor; struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; const struct isp_format_info *fmt_info; struct v4l2_subdev_format fmt_src; unsigned int depth_out; @@ -1211,14 +1215,14 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); /* CCDC_PAD_SOURCE_OF */ - format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; + crop = &ccdc->crop; - isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | - ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), + isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) | + ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); - isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, + isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); - isp_reg_writel(isp, (format->height - 1) + isp_reg_writel(isp, (crop->height - 1) << ISPCCDC_VERT_LINES_NLV_SHIFT, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); @@ -1793,6 +1797,16 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, return &ccdc->formats[pad]; } +static struct v4l2_rect * +__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF); + else + return &ccdc->crop; +} + /* * ccdc_try_format - Try video format on a pad * @ccdc: ISP CCDC device @@ -1809,6 +1823,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, const struct isp_format_info *info; unsigned int width = fmt->width; unsigned int height = fmt->height; + struct v4l2_rect *crop; unsigned int i; switch (pad) { @@ -1834,14 +1849,10 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); - /* The data formatter truncates the number of horizontal output - * pixels to a multiple of 16. To avoid clipping data, allow - * callers to request an output size bigger than the input size - * up to the nearest multiple of 16. - */ - fmt->width = clamp_t(u32, width, 32, fmt->width + 15); - fmt->width &= ~15; - fmt->height = clamp_t(u32, height, 32, fmt->height); + /* Hardcode the output size to the crop rectangle size. */ + crop = __ccdc_get_crop(ccdc, fh, which); + fmt->width = crop->width; + fmt->height = crop->height; break; case CCDC_PAD_SOURCE_VP: @@ -1868,6 +1879,49 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, fmt->field = V4L2_FIELD_NONE; } +/* + * ccdc_try_crop - Validate a crop rectangle + * @ccdc: ISP CCDC device + * @sink: format on the sink pad + * @crop: crop rectangle to be validated + */ +static void ccdc_try_crop(struct isp_ccdc_device *ccdc, + const struct v4l2_mbus_framefmt *sink, + struct v4l2_rect *crop) +{ + const struct isp_format_info *info; + unsigned int max_width; + + /* For Bayer formats, restrict left/top and width/height to even values + * to keep the Bayer pattern. + */ + info = omap3isp_video_format_info(sink->code); + if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { + crop->left &= ~1; + crop->top &= ~1; + } + + crop->left = clamp_t(u32, crop->left, 0, sink->width - CCDC_MIN_WIDTH); + crop->top = clamp_t(u32, crop->top, 0, sink->height - CCDC_MIN_HEIGHT); + + /* The data formatter truncates the number of horizontal output pixels + * to a multiple of 16. To avoid clipping data, allow callers to request + * an output size bigger than the input size up to the nearest multiple + * of 16. + */ + max_width = (sink->width - crop->left + 15) & ~15; + crop->width = clamp_t(u32, crop->width, CCDC_MIN_WIDTH, max_width) + & ~15; + crop->height = clamp_t(u32, crop->height, CCDC_MIN_HEIGHT, + sink->height - crop->top); + + /* Odd width/height values don't make sense for Bayer formats. */ + if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { + crop->width &= ~1; + crop->height &= ~1; + } +} + /* * ccdc_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure @@ -1939,6 +1993,93 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, return 0; } +/* + * ccdc_get_selection - Retrieve a selection rectangle on a pad + * @sd: ISP CCDC V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangles are the crop rectangles on the output formatter + * source pad. + * + * Return 0 on success or a negative error code otherwise. + */ +static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (sel->pad != CCDC_PAD_SOURCE_OF) + return -EINVAL; + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = INT_MAX; + sel->r.height = INT_MAX; + + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + ccdc_try_crop(ccdc, format, &sel->r); + break; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ccdc_set_selection - Set a selection rectangle on a pad + * @sd: ISP CCDC V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangle is the actual crop rectangle on the output + * formatter source pad. + * + * Return 0 on success or a negative error code otherwise. + */ +static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != CCDC_PAD_SOURCE_OF) + return -EINVAL; + + /* The crop rectangle can't be changed while streaming. */ + if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) + return -EBUSY; + + /* Modifying the crop rectangle always changes the format on the source + * pad. If the KEEP_CONFIG flag is set, just return the current crop + * rectangle. + */ + if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + return 0; + } + + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + ccdc_try_crop(ccdc, format, &sel->r); + *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r; + + /* Update the source format. */ + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which); + ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which); + + return 0; +} + /* * ccdc_get_format - Retrieve the video format on a pad * @sd : ISP CCDC V4L2 subdevice @@ -1976,6 +2117,7 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); if (format == NULL) @@ -1986,6 +2128,16 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* Propagate the format from sink to source */ if (fmt->pad == CCDC_PAD_SINK) { + /* Reset the crop rectangle. */ + crop = __ccdc_get_crop(ccdc, fh, fmt->which); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + ccdc_try_crop(ccdc, &fmt->format, crop); + + /* Update the source formats. */ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, fmt->which); *format = fmt->format; @@ -2044,6 +2196,8 @@ static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { .enum_frame_size = ccdc_enum_frame_size, .get_fmt = ccdc_get_format, .set_fmt = ccdc_set_format, + .get_selection = ccdc_get_selection, + .set_selection = ccdc_set_selection, }; /* V4L2 subdev operations */ diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h index 6d0264bab75b..966bbf8a1262 100644 --- a/drivers/media/video/omap3isp/ispccdc.h +++ b/drivers/media/video/omap3isp/ispccdc.h @@ -147,6 +147,7 @@ struct ispccdc_lsc { * @subdev: V4L2 subdevice * @pads: Sink and source media entity pads * @formats: Active video formats + * @crop: Active crop rectangle on the OF source pad * @input: Active input * @output: Active outputs * @video_out: Output video node @@ -173,6 +174,7 @@ struct isp_ccdc_device { struct v4l2_subdev subdev; struct media_pad pads[CCDC_PADS_NUM]; struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; + struct v4l2_rect crop; enum ccdc_input_entity input; unsigned int output; -- cgit v1.2.3 From 684c8e26e755c0af49c1de554f863602c1803141 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 20 Apr 2012 05:47:49 -0300 Subject: [media] omap3isp: preview: Replace the crop API by the selection API Support for the legacy crop API is emulated in the subdev core. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 74 ++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 420b131282d8..cbc887010b28 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -1929,55 +1929,89 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, } /* - * preview_get_crop - Retrieve the crop rectangle on a pad + * preview_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice * @fh: V4L2 subdev file handle - * @crop: crop rectangle + * @sel: Selection rectangle + * + * The only supported rectangles are the crop rectangles on the sink pad. * * Return 0 on success or a negative error code otherwise. */ -static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int preview_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (sel->pad != PREV_PAD_SINK) + return -EINVAL; + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = INT_MAX; + sel->r.height = INT_MAX; + + format = __preview_get_format(prev, fh, PREV_PAD_SINK, + sel->which); + preview_try_crop(prev, format, &sel->r); + break; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + sel->r = *__preview_get_crop(prev, fh, sel->which); + break; - /* Cropping is only supported on the sink pad. */ - if (crop->pad != PREV_PAD_SINK) + default: return -EINVAL; + } - crop->rect = *__preview_get_crop(prev, fh, crop->which); return 0; } /* - * preview_set_crop - Retrieve the crop rectangle on a pad + * preview_set_selection - Set a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice * @fh: V4L2 subdev file handle - * @crop: crop rectangle + * @sel: Selection rectangle + * + * The only supported rectangle is the actual crop rectangle on the sink pad. * * Return 0 on success or a negative error code otherwise. */ -static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int preview_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - /* Cropping is only supported on the sink pad. */ - if (crop->pad != PREV_PAD_SINK) + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != PREV_PAD_SINK) return -EINVAL; /* The crop rectangle can't be changed while streaming. */ if (prev->state != ISP_PIPELINE_STREAM_STOPPED) return -EBUSY; - format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which); - preview_try_crop(prev, format, &crop->rect); - *__preview_get_crop(prev, fh, crop->which) = crop->rect; + /* Modifying the crop rectangle always changes the format on the source + * pad. If the KEEP_CONFIG flag is set, just return the current crop + * rectangle. + */ + if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + sel->r = *__preview_get_crop(prev, fh, sel->which); + return 0; + } + + format = __preview_get_format(prev, fh, PREV_PAD_SINK, sel->which); + preview_try_crop(prev, format, &sel->r); + *__preview_get_crop(prev, fh, sel->which) = sel->r; /* Update the source format. */ - format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which); - preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which); + format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, sel->which); + preview_try_format(prev, fh, PREV_PAD_SOURCE, format, sel->which); return 0; } @@ -2086,8 +2120,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = { .enum_frame_size = preview_enum_frame_size, .get_fmt = preview_get_format, .set_fmt = preview_set_format, - .get_crop = preview_get_crop, - .set_crop = preview_set_crop, + .get_selection = preview_get_selection, + .set_selection = preview_set_selection, }; /* subdev operations */ -- cgit v1.2.3 From 433f75ab3d09a666c745d4163e21e811582d8eda Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 20 Apr 2012 05:47:49 -0300 Subject: [media] omap3isp: resizer: Replace the crop API by the selection API Support for the legacy crop API is emulated in the subdev core. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispresizer.c | 138 +++++++++++++++++++----------- 1 file changed, 88 insertions(+), 50 deletions(-) diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index 6958a9e3dc22..d7341ab15ffe 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1187,32 +1187,6 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) return 0; } -/* - * resizer_g_crop - handle get crop subdev operation - * @sd : pointer to v4l2 subdev structure - * @pad : subdev pad - * @crop : pointer to crop structure - * @which : active or try format - * return zero - */ -static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct isp_res_device *res = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - struct resizer_ratio ratio; - - /* Only sink pad has crop capability */ - if (crop->pad != RESZ_PAD_SINK) - return -EINVAL; - - format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which); - crop->rect = *__resizer_get_crop(res, fh, crop->which); - resizer_calc_ratios(res, &crop->rect, format, &ratio); - - return 0; -} - /* * resizer_try_crop - mangles crop parameters. */ @@ -1223,7 +1197,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, const unsigned int spv = DEFAULT_PHASE; const unsigned int sph = DEFAULT_PHASE; - /* Crop rectangle is constrained to the output size so that zoom ratio + /* Crop rectangle is constrained by the output size so that zoom ratio * cannot exceed +/-4.0. */ unsigned int min_width = @@ -1248,51 +1222,115 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, } /* - * resizer_s_crop - handle set crop subdev operation - * @sd : pointer to v4l2 subdev structure - * @pad : subdev pad - * @crop : pointer to crop structure - * @which : active or try format - * return -EINVAL or zero when succeed + * resizer_get_selection - Retrieve a selection rectangle on a pad + * @sd: ISP resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangles are the crop rectangles on the sink pad. + * + * Return 0 on success or a negative error code otherwise. */ -static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int resizer_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format_source; + struct v4l2_mbus_framefmt *format_sink; + struct resizer_ratio ratio; + + if (sel->pad != RESZ_PAD_SINK) + return -EINVAL; + + format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, + sel->which); + format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, + sel->which); + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = INT_MAX; + sel->r.height = INT_MAX; + + resizer_try_crop(format_sink, format_source, &sel->r); + resizer_calc_ratios(res, &sel->r, format_source, &ratio); + break; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + sel->r = *__resizer_get_crop(res, fh, sel->which); + resizer_calc_ratios(res, &sel->r, format_source, &ratio); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * resizer_set_selection - Set a selection rectangle on a pad + * @sd: ISP resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangle is the actual crop rectangle on the sink pad. + * + * FIXME: This function currently behaves as if the KEEP_CONFIG selection flag + * was always set. + * + * Return 0 on success or a negative error code otherwise. + */ +static int resizer_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct isp_device *isp = to_isp_device(res); struct v4l2_mbus_framefmt *format_sink, *format_source; struct resizer_ratio ratio; - /* Only sink pad has crop capability */ - if (crop->pad != RESZ_PAD_SINK) + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != RESZ_PAD_SINK) return -EINVAL; format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, - crop->which); + sel->which); format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, - crop->which); + sel->which); dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, - crop->rect.left, crop->rect.top, crop->rect.width, - crop->rect.height, crop->which); + sel->r.left, sel->r.top, sel->r.width, sel->r.height, + sel->which); dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, format_sink->width, format_sink->height, format_source->width, format_source->height); - resizer_try_crop(format_sink, format_source, &crop->rect); - *__resizer_get_crop(res, fh, crop->which) = crop->rect; - resizer_calc_ratios(res, &crop->rect, format_source, &ratio); + /* Clamp the crop rectangle to the bounds, and then mangle it further to + * fulfill the TRM equations. Store the clamped but otherwise unmangled + * rectangle to avoid cropping the input multiple times: when an + * application sets the output format, the current crop rectangle is + * mangled during crop rectangle computation, which would lead to a new, + * smaller input crop rectangle every time the output size is set if we + * stored the mangled rectangle. + */ + resizer_try_crop(format_sink, format_source, &sel->r); + *__resizer_get_crop(res, fh, sel->which) = sel->r; + resizer_calc_ratios(res, &sel->r, format_source, &ratio); - if (crop->which == V4L2_SUBDEV_FORMAT_TRY) + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) return 0; res->ratio = ratio; - res->crop.active = crop->rect; + res->crop.active = sel->r; /* - * s_crop can be called while streaming is on. In this case - * the crop values will be set in the next IRQ. + * set_selection can be called while streaming is on. In this case the + * crop values will be set in the next IRQ. */ if (res->state != ISP_PIPELINE_STREAM_STOPPED) res->applycrop = 1; @@ -1530,8 +1568,8 @@ static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { .enum_frame_size = resizer_enum_frame_size, .get_fmt = resizer_get_format, .set_fmt = resizer_set_format, - .get_crop = resizer_g_crop, - .set_crop = resizer_s_crop, + .get_selection = resizer_get_selection, + .set_selection = resizer_set_selection, }; /* subdev operations */ -- cgit v1.2.3 From 31ed29f9812a9cb9a1c6d270ff4e19da60046da1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Apr 2012 09:59:25 -0300 Subject: [media] v4l: aptina-pll: Round up minimum multiplier factor value properly The mf_low value must be a multiple of mf_inc. Round it up to the nearest mf_inc multiple after computing it. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/aptina-pll.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c index 0bd3813bb59d..8153a449846b 100644 --- a/drivers/media/video/aptina-pll.c +++ b/drivers/media/video/aptina-pll.c @@ -148,9 +148,8 @@ int aptina_pll_calculate(struct device *dev, unsigned int mf_high; unsigned int mf_low; - mf_low = max(roundup(mf_min, mf_inc), - DIV_ROUND_UP(pll->ext_clock * p1, - limits->int_clock_max * div)); + mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1, + limits->int_clock_max * div)), mf_inc); mf_high = min(mf_max, pll->ext_clock * p1 / (limits->int_clock_min * div)); -- cgit v1.2.3 From b151d9a231f18e329a5d0619c1c11117295ce031 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 23 Apr 2012 10:02:42 -0300 Subject: [media] as3645a: move relevant code under __devinit/__devexit There is no needs to keep .remove under .exit.text. This driver is for a standalone chip that could be on any board and connected to any i2c bus. At the same time we don't need to keep the as3645a_probe() after initializing the device. Therefore we mark it and relevant functions with __devinit tag. Signed-off-by: Andy Shevchenko Cc: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/as3645a.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c index 7a3371f044fc..c4b03572dce8 100644 --- a/drivers/media/video/as3645a.c +++ b/drivers/media/video/as3645a.c @@ -713,7 +713,7 @@ static int as3645a_resume(struct device *dev) * The number of LEDs reported in platform data is used to compute default * limits. Parameters passed through platform data can override those limits. */ -static int as3645a_init_controls(struct as3645a *flash) +static int __devinit as3645a_init_controls(struct as3645a *flash) { const struct as3645a_platform_data *pdata = flash->pdata; struct v4l2_ctrl *ctrl; @@ -804,8 +804,8 @@ static int as3645a_init_controls(struct as3645a *flash) return flash->ctrls.error; } -static int as3645a_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int __devinit as3645a_probe(struct i2c_client *client, + const struct i2c_device_id *devid) { struct as3645a *flash; int ret; @@ -846,7 +846,7 @@ done: return ret; } -static int __exit as3645a_remove(struct i2c_client *client) +static int __devexit as3645a_remove(struct i2c_client *client) { struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct as3645a *flash = to_as3645a(subdev); @@ -877,7 +877,7 @@ static struct i2c_driver as3645a_i2c_driver = { .pm = &as3645a_pm_ops, }, .probe = as3645a_probe, - .remove = __exit_p(as3645a_remove), + .remove = __devexit_p(as3645a_remove), .id_table = as3645a_id_table, }; -- cgit v1.2.3 From d0df3c384766b11ca118ceac9a3acdc0d76239b1 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 24 Apr 2012 19:08:12 -0300 Subject: [media] media: videobuf2-dma-contig: include header for exported symbols Include the header to pickup the definitions of the exported symbols. Quiets the following sparse warnings: warning: symbol 'vb2_dma_contig_memops' was not declared. Should it be static? warning: symbol 'vb2_dma_contig_init_ctx' was not declared. Should it be static? warning: symbol 'vb2_dma_contig_cleanup_ctx' was not declared. Should it be static? Signed-off-by: H Hartley Sweeten Cc: Pawel Osciak Cc: Kyungmin Park Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-dma-contig.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index f17ad98fcc5f..a1bee6c0b31d 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -15,6 +15,7 @@ #include #include +#include #include struct vb2_dc_conf { -- cgit v1.2.3 From 121b3ddbe4ad17df77cb7284239be0a63d9a66bd Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 24 Apr 2012 19:12:48 -0300 Subject: [media] media: videobuf2-dma-contig: quiet sparse noise about plain integer as NULL pointer The function vb2_dma_contig_vaddr returns a void * not an integer. Quiets the sparse noise: warning: Using plain integer as NULL pointer Signed-off-by: H Hartley Sweeten Cc: Pawel Osciak Cc: Marek Szyprowski Cc: Kyungmin Park Cc: Mauro Carvalho Chehab Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-dma-contig.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index a1bee6c0b31d..4b7132660a93 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -86,7 +86,7 @@ static void *vb2_dma_contig_vaddr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; if (!buf) - return 0; + return NULL; return buf->vaddr; } -- cgit v1.2.3 From 6016af82eafcb6e086a8f2a2197b46029a843d68 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 10 May 2012 02:02:07 -0300 Subject: [media] v4l2: use __u32 rather than enums in ioctl() structs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V4L2 uses the enum type in IOCTL arguments in IOCTLs that were defined until the use of enum was considered less than ideal. Recently Rémi Denis-Courmont brought up the issue by proposing a patch to convert the enums to unsigned: This sparked a long discussion where another solution to the issue was proposed: two sets of IOCTL structures, one with __u32 and the other with enums, and conversion code between the two: Both approaches implement a complete solution that resolves the problem. The first one is simple but requires assuming enums and __u32 are the same in size (so we won't break the ABI) while the second one is more complex and less clean but does not require making that assumption. The issue boils down to whether enums are fundamentally different from __u32 or not, and can the former be substituted by the latter. During the discussion it was concluded that the __u32 has the same size as enums on all archs Linux is supported: it has not been shown that replacing those enums in IOCTL arguments would break neither source or binary compatibility. If no such reason is found, just replacing the enums with __u32s is the way to go. This is what this patch does. This patch is slightly different from Remi's first RFC (link above): it uses __u32 instead of unsigned and also changes the arguments of VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY. Signed-off-by: Rémi Denis-Courmont Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/io.xml | 12 ++-- .../DocBook/media/v4l/vidioc-create-bufs.xml | 10 ++-- Documentation/DocBook/media/v4l/vidioc-cropcap.xml | 4 +- .../DocBook/media/v4l/vidioc-enum-fmt.xml | 4 +- Documentation/DocBook/media/v4l/vidioc-g-crop.xml | 4 +- Documentation/DocBook/media/v4l/vidioc-g-fmt.xml | 2 +- .../DocBook/media/v4l/vidioc-g-frequency.xml | 6 +- Documentation/DocBook/media/v4l/vidioc-g-parm.xml | 5 +- .../DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml | 2 +- Documentation/DocBook/media/v4l/vidioc-g-tuner.xml | 2 +- .../DocBook/media/v4l/vidioc-queryctrl.xml | 2 +- Documentation/DocBook/media/v4l/vidioc-reqbufs.xml | 7 ++- .../DocBook/media/v4l/vidioc-s-hw-freq-seek.xml | 5 +- drivers/media/video/v4l2-compat-ioctl32.c | 12 ++-- include/linux/videodev2.h | 64 +++++++++++----------- 15 files changed, 75 insertions(+), 66 deletions(-) diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index b815929b5bba..fd6aca2922b6 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -543,12 +543,13 @@ and can range from zero to the number of buffers allocated with the &VIDIOC-REQBUFS; ioctl (&v4l2-requestbuffers; count) minus one. - &v4l2-buf-type; + __u32 type Type of the buffer, same as &v4l2-format; type or &v4l2-requestbuffers; -type, set by the application. +type, set by the application. See __u32 @@ -568,7 +569,7 @@ refers to an input stream, applications when an output stream. linkend="buffer-flags" />. - &v4l2-field; + __u32 field Indicates the field order of the image in the @@ -630,11 +631,12 @@ bandwidth. These devices identify by not enumerating any video standards, see . - &v4l2-memory; + __u32 memory This field must be set by applications and/or drivers -in accordance with the selected I/O method. +in accordance with the selected I/O method. See union diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml index 73ae8a6cd004..184cdfc13081 100644 --- a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml @@ -94,16 +94,18 @@ information. The number of buffers requested or granted. - &v4l2-memory; + __u32 memory Applications set this field to V4L2_MEMORY_MMAP or -V4L2_MEMORY_USERPTR. +V4L2_MEMORY_USERPTR. See - &v4l2-format; + __u32 format - Filled in by the application, preserved by the driver. + Filled in by the application, preserved by the driver. + See . __u32 diff --git a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml index b4f2f255211e..f1bac2c6e978 100644 --- a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml @@ -65,7 +65,7 @@ output. &cs-str; - &v4l2-buf-type; + __u32 type Type of the data stream, set by the application. Only these types are valid here: @@ -73,7 +73,7 @@ Only these types are valid here: V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_BUF_TYPE_VIDEO_OVERLAY, and custom (driver defined) types with code V4L2_BUF_TYPE_PRIVATE -and higher. +and higher. See . struct v4l2_rect diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml index 347d142e7431..81ebe48317fe 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml @@ -71,7 +71,7 @@ the application. This is in no way related to the pixelformat field. - &v4l2-buf-type; + __u32 type Type of the data stream, set by the application. Only these types are valid here: @@ -81,7 +81,7 @@ Only these types are valid here: V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_BUF_TYPE_VIDEO_OVERLAY, and custom (driver defined) types with code V4L2_BUF_TYPE_PRIVATE -and higher. +and higher. See . __u32 diff --git a/Documentation/DocBook/media/v4l/vidioc-g-crop.xml b/Documentation/DocBook/media/v4l/vidioc-g-crop.xml index 01a50640dce0..c4ff3b1887fb 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-crop.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-crop.xml @@ -100,14 +100,14 @@ changed and VIDIOC_S_CROP returns the &cs-str; - &v4l2-buf-type; + __u32 type Type of the data stream, set by the application. Only these types are valid here: V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_BUF_TYPE_VIDEO_OVERLAY, and custom (driver defined) types with code V4L2_BUF_TYPE_PRIVATE -and higher. +and higher. See . &v4l2-rect; diff --git a/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml index 17fbda15137b..52acff193a6f 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml @@ -116,7 +116,7 @@ this ioctl. - &v4l2-buf-type; + __u32 type Type of the data stream, see modulator field and the &v4l2-modulator; index field. - &v4l2-tuner-type; + __u32 type The tuner type. This is the same value as in the -&v4l2-tuner; type field. The type must be set +&v4l2-tuner; type field. See The type must be set to V4L2_TUNER_RADIO for /dev/radioX device nodes, and to V4L2_TUNER_ANALOG_TV for all others. The field is not applicable to modulators, &ie; ignored -by drivers. +by drivers. See __u32 diff --git a/Documentation/DocBook/media/v4l/vidioc-g-parm.xml b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml index 19b1d85dd668..f83d2cdd1185 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-parm.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml @@ -75,11 +75,12 @@ devices. &cs-ustr; - &v4l2-buf-type; + __u32 type The buffer (stream) type, same as &v4l2-format; -type, set by the application. +type, set by the application. See union diff --git a/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml b/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml index 71741daaf725..bd015d1563ff 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml @@ -148,7 +148,7 @@ using the &VIDIOC-S-FMT; ioctl as described in service_lines[1][0] to zero. - &v4l2-buf-type; + __u32 type Type of the data stream, see . Should be diff --git a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml index 91ec2fb658f8..62a1aa200a36 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml @@ -107,7 +107,7 @@ user. - &v4l2-tuner-type; + __u32 type Type of the tuner, see . diff --git a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml index 505f0206e5bd..e6645b996558 100644 --- a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml @@ -127,7 +127,7 @@ the first control with a higher ID. Drivers which do not support this flag yet always return an &EINVAL;. - &v4l2-ctrl-type; + __u32 type Type of control, see . diff --git a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml index 7be4b1d29b90..d7c95057bc51 100644 --- a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml @@ -92,18 +92,19 @@ streamoff.--> The number of buffers requested or granted. - &v4l2-buf-type; + __u32 type Type of the stream or buffers, this is the same as the &v4l2-format; type field. See for valid values. - &v4l2-memory; + __u32 memory Applications set this field to V4L2_MEMORY_MMAP or -V4L2_MEMORY_USERPTR. +V4L2_MEMORY_USERPTR. See . __u32 diff --git a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml index 18b1a8266f7c..407dfceb71f0 100644 --- a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml +++ b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml @@ -73,10 +73,11 @@ same value as in the &v4l2-input; tuner field and the &v4l2-tuner; index field. - &v4l2-tuner-type; + __u32 type The tuner type. This is the same value as in the -&v4l2-tuner; type field. +&v4l2-tuner; type field. See __u32 diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 2829d256e4b7..89ae433877e6 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -37,7 +37,7 @@ struct v4l2_clip32 { struct v4l2_window32 { struct v4l2_rect w; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ __u32 chromakey; compat_caddr_t clips; /* actually struct v4l2_clip32 * */ __u32 clipcount; @@ -147,7 +147,7 @@ static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, } struct v4l2_format32 { - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ union { struct v4l2_pix_format pix; struct v4l2_pix_format_mplane pix_mp; @@ -170,7 +170,7 @@ struct v4l2_format32 { struct v4l2_create_buffers32 { __u32 index; __u32 count; - enum v4l2_memory memory; + __u32 memory; /* enum v4l2_memory */ struct v4l2_format32 format; __u32 reserved[8]; }; @@ -311,16 +311,16 @@ struct v4l2_plane32 { struct v4l2_buffer32 { __u32 index; - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ __u32 bytesused; __u32 flags; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ struct compat_timeval timestamp; struct v4l2_timecode timecode; __u32 sequence; /* memory location */ - enum v4l2_memory memory; + __u32 memory; /* enum v4l2_memory */ union { __u32 offset; compat_long_t userptr; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 5a09ac3f7683..ace8ac000b9b 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -292,10 +292,10 @@ struct v4l2_pix_format { __u32 width; __u32 height; __u32 pixelformat; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ __u32 bytesperline; /* for padding, zero if unused */ __u32 sizeimage; - enum v4l2_colorspace colorspace; + __u32 colorspace; /* enum v4l2_colorspace */ __u32 priv; /* private data, depends on pixelformat */ }; @@ -432,7 +432,7 @@ struct v4l2_pix_format { */ struct v4l2_fmtdesc { __u32 index; /* Format number */ - enum v4l2_buf_type type; /* buffer type */ + __u32 type; /* enum v4l2_buf_type */ __u32 flags; __u8 description[32]; /* Description string */ __u32 pixelformat; /* Format fourcc */ @@ -573,8 +573,8 @@ struct v4l2_jpegcompression { */ struct v4l2_requestbuffers { __u32 count; - enum v4l2_buf_type type; - enum v4l2_memory memory; + __u32 type; /* enum v4l2_buf_type */ + __u32 memory; /* enum v4l2_memory */ __u32 reserved[2]; }; @@ -610,15 +610,17 @@ struct v4l2_plane { /** * struct v4l2_buffer - video buffer info * @index: id number of the buffer - * @type: buffer type (type == *_MPLANE for multiplanar buffers) + * @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for + * multiplanar buffers); * @bytesused: number of bytes occupied by data in the buffer (payload); * unused (set to 0) for multiplanar buffers * @flags: buffer informational flags - * @field: field order of the image in the buffer + * @field: enum v4l2_field; field order of the image in the buffer * @timestamp: frame timestamp * @timecode: frame timecode * @sequence: sequence count of this frame - * @memory: the method, in which the actual video data is passed + * @memory: enum v4l2_memory; the method, in which the actual video data is + * passed * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP; * offset from the start of the device memory for this plane, * (or a "cookie" that should be passed to mmap() as offset) @@ -636,16 +638,16 @@ struct v4l2_plane { */ struct v4l2_buffer { __u32 index; - enum v4l2_buf_type type; + __u32 type; __u32 bytesused; __u32 flags; - enum v4l2_field field; + __u32 field; struct timeval timestamp; struct v4l2_timecode timecode; __u32 sequence; /* memory location */ - enum v4l2_memory memory; + __u32 memory; union { __u32 offset; unsigned long userptr; @@ -708,7 +710,7 @@ struct v4l2_clip { struct v4l2_window { struct v4l2_rect w; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ __u32 chromakey; struct v4l2_clip __user *clips; __u32 clipcount; @@ -745,14 +747,14 @@ struct v4l2_outputparm { * I N P U T I M A G E C R O P P I N G */ struct v4l2_cropcap { - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ struct v4l2_rect bounds; struct v4l2_rect defrect; struct v4l2_fract pixelaspect; }; struct v4l2_crop { - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ struct v4l2_rect c; }; @@ -1040,7 +1042,7 @@ struct v4l2_input { __u8 name[32]; /* Label */ __u32 type; /* Type of input */ __u32 audioset; /* Associated audios (bitfield) */ - __u32 tuner; /* Associated tuner */ + __u32 tuner; /* enum v4l2_tuner_type */ v4l2_std_id std; __u32 status; __u32 capabilities; @@ -1157,7 +1159,7 @@ enum v4l2_ctrl_type { /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ struct v4l2_queryctrl { __u32 id; - enum v4l2_ctrl_type type; + __u32 type; /* enum v4l2_ctrl_type */ __u8 name[32]; /* Whatever */ __s32 minimum; /* Note signedness */ __s32 maximum; @@ -1792,7 +1794,7 @@ enum v4l2_jpeg_chroma_subsampling { struct v4l2_tuner { __u32 index; __u8 name[32]; - enum v4l2_tuner_type type; + __u32 type; /* enum v4l2_tuner_type */ __u32 capability; __u32 rangelow; __u32 rangehigh; @@ -1842,14 +1844,14 @@ struct v4l2_modulator { struct v4l2_frequency { __u32 tuner; - enum v4l2_tuner_type type; + __u32 type; /* enum v4l2_tuner_type */ __u32 frequency; __u32 reserved[8]; }; struct v4l2_hw_freq_seek { __u32 tuner; - enum v4l2_tuner_type type; + __u32 type; /* enum v4l2_tuner_type */ __u32 seek_upward; __u32 wrap_around; __u32 spacing; @@ -2060,7 +2062,7 @@ struct v4l2_sliced_vbi_cap { (equals frame lines 313-336 for 625 line video standards, 263-286 for 525 line standards) */ __u16 service_lines[2][24]; - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ __u32 reserved[3]; /* must be 0 */ }; @@ -2141,8 +2143,8 @@ struct v4l2_plane_pix_format { * @width: image width in pixels * @height: image height in pixels * @pixelformat: little endian four character code (fourcc) - * @field: field order (for interlaced video) - * @colorspace: supplemental to pixelformat + * @field: enum v4l2_field; field order (for interlaced video) + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat * @plane_fmt: per-plane information * @num_planes: number of planes for this format */ @@ -2150,8 +2152,8 @@ struct v4l2_pix_format_mplane { __u32 width; __u32 height; __u32 pixelformat; - enum v4l2_field field; - enum v4l2_colorspace colorspace; + __u32 field; + __u32 colorspace; struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES]; __u8 num_planes; @@ -2160,7 +2162,7 @@ struct v4l2_pix_format_mplane { /** * struct v4l2_format - stream data format - * @type: type of the data stream + * @type: enum v4l2_buf_type; type of the data stream * @pix: definition of an image format * @pix_mp: definition of a multiplanar image format * @win: definition of an overlaid image @@ -2169,7 +2171,7 @@ struct v4l2_pix_format_mplane { * @raw_data: placeholder for future extensions and custom formats */ struct v4l2_format { - enum v4l2_buf_type type; + __u32 type; union { struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ @@ -2183,7 +2185,7 @@ struct v4l2_format { /* Stream type-dependent parameters */ struct v4l2_streamparm { - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ union { struct v4l2_captureparm capture; struct v4l2_outputparm output; @@ -2296,14 +2298,14 @@ struct v4l2_dbg_chip_ident { * @index: on return, index of the first created buffer * @count: entry: number of requested buffers, * return: number of created buffers - * @memory: buffer memory type + * @memory: enum v4l2_memory; buffer memory type * @format: frame format, for which buffers are requested * @reserved: future extensions */ struct v4l2_create_buffers { __u32 index; __u32 count; - enum v4l2_memory memory; + __u32 memory; struct v4l2_format format; __u32 reserved[8]; }; @@ -2360,8 +2362,8 @@ struct v4l2_create_buffers { #define VIDIOC_TRY_FMT _IOWR('V', 64, struct v4l2_format) #define VIDIOC_ENUMAUDIO _IOWR('V', 65, struct v4l2_audio) #define VIDIOC_ENUMAUDOUT _IOWR('V', 66, struct v4l2_audioout) -#define VIDIOC_G_PRIORITY _IOR('V', 67, enum v4l2_priority) -#define VIDIOC_S_PRIORITY _IOW('V', 68, enum v4l2_priority) +#define VIDIOC_G_PRIORITY _IOR('V', 67, __u32) /* enum v4l2_priority */ +#define VIDIOC_S_PRIORITY _IOW('V', 68, __u32) /* enum v4l2_priority */ #define VIDIOC_G_SLICED_VBI_CAP _IOWR('V', 69, struct v4l2_sliced_vbi_cap) #define VIDIOC_LOG_STATUS _IO('V', 70) #define VIDIOC_G_EXT_CTRLS _IOWR('V', 71, struct v4l2_ext_controls) -- cgit v1.2.3 From 8c9d236ec64f1f1e7764385e7b5eae88eec7c02b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 4 Oct 2011 08:20:05 -0300 Subject: [media] v4l: Image source control class Add image source control class. This control class is intended to contain low level controls which deal with control of the image capture process --- the A/D converter in image sensors, for example. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 86 ++++++++++++++++++++++ .../DocBook/media/v4l/vidioc-g-ext-ctrls.xml | 6 ++ drivers/media/video/v4l2-ctrls.c | 7 ++ include/linux/videodev2.h | 9 +++ 4 files changed, 108 insertions(+) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 5e12257dfcef..43cd4958c9e8 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3599,4 +3599,90 @@ interface and may change in the future. to , , . + +
+ Image Source Control Reference + + + Experimental + + This is an experimental interface and may + change in the future. + + + + The Image Source control class is intended for low-level + control of image source devices such as image sensors. The + devices feature an analogue to digital converter and a bus + transmitter to transmit the image data out of the device. + + + + Image Source Control IDs + + + + + + + + + + + ID + Type + Description + + + + + + V4L2_CID_IMAGE_SOURCE_CLASS + class + + + The IMAGE_SOURCE class descriptor. + + + V4L2_CID_VBLANK + integer + + + Vertical blanking. The idle period + after every frame during which no image data is produced. + The unit of vertical blanking is a line. Every line has + length of the image width plus horizontal blanking at the + pixel rate defined by + V4L2_CID_PIXEL_RATE control in the + same sub-device. + + + V4L2_CID_HBLANK + integer + + + Horizontal blanking. The idle + period after every line of image data during which no + image data is produced. The unit of horizontal blanking is + pixels. + + + V4L2_CID_ANALOGUE_GAIN + integer + + + Analogue gain is gain affecting + all colour components in the pixel matrix. The gain + operation is performed in the analogue domain before A/D + conversion. + + + + + +
+ +
+ diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index 27e20bcbdf42..f2d2ec3f0e31 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -272,6 +272,12 @@ These controls are described in .
+ + V4L2_CTRL_CLASS_IMAGE_SOURCE + 0x9e0000 The class containing image + source controls. These controls are described in . + diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index ae544d870d7d..7a2f855d4e21 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -644,6 +644,12 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality"; case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers"; + /* Image source controls */ + case V4L2_CID_IMAGE_SOURCE_CLASS: return "Image Source Controls"; + case V4L2_CID_VBLANK: return "Vertical Blanking"; + case V4L2_CID_HBLANK: return "Horizontal Blanking"; + case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain"; + default: return NULL; } @@ -745,6 +751,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_FM_TX_CLASS: case V4L2_CID_FLASH_CLASS: case V4L2_CID_JPEG_CLASS: + case V4L2_CID_IMAGE_SOURCE_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index ace8ac000b9b..3cd13b2faab5 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1139,6 +1139,7 @@ struct v4l2_ext_controls { #define V4L2_CTRL_CLASS_FM_TX 0x009b0000 /* FM Modulator control class */ #define V4L2_CTRL_CLASS_FLASH 0x009c0000 /* Camera flash controls */ #define V4L2_CTRL_CLASS_JPEG 0x009d0000 /* JPEG-compression controls */ +#define V4L2_CTRL_CLASS_IMAGE_SOURCE 0x009e0000 /* Image source controls */ #define V4L2_CTRL_ID_MASK (0x0fffffff) #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) @@ -1788,6 +1789,14 @@ enum v4l2_jpeg_chroma_subsampling { #define V4L2_JPEG_ACTIVE_MARKER_DQT (1 << 17) #define V4L2_JPEG_ACTIVE_MARKER_DHT (1 << 18) +/* Image source controls */ +#define V4L2_CID_IMAGE_SOURCE_CLASS_BASE (V4L2_CTRL_CLASS_IMAGE_SOURCE | 0x900) +#define V4L2_CID_IMAGE_SOURCE_CLASS (V4L2_CTRL_CLASS_IMAGE_SOURCE | 1) + +#define V4L2_CID_VBLANK (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 1) +#define V4L2_CID_HBLANK (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 2) +#define V4L2_CID_ANALOGUE_GAIN (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 3) + /* * T U N I N G */ -- cgit v1.2.3 From c643ee135190389e03cffd80e762c9c71dc9a165 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 2 Feb 2012 20:17:54 -0300 Subject: [media] v4l: Image processing control class Add control class for image processing controls. The control class deals with controls processing image, for example digital gain or noise filtering, which can be present in any part of the pipeline. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 82 ++++++++++++++++++++++ .../DocBook/media/v4l/vidioc-g-ext-ctrls.xml | 6 ++ drivers/media/video/v4l2-ctrls.c | 14 +++- include/linux/videodev2.h | 8 +++ 4 files changed, 109 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 43cd4958c9e8..662127447aa6 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3685,4 +3685,86 @@ interface and may change in the future. +
+ Image Process Control Reference + + + Experimental + + This is an experimental interface and may + change in the future. + + + + The Image Source control class is intended for low-level control of + image processing functions. Unlike + V4L2_CID_IMAGE_SOURCE_CLASS, the controls in + this class affect processing the image, and do not control capturing + of it. + + + + Image Source Control IDs + + + + + + + + + + + ID + Type + Description + + + + + + V4L2_CID_IMAGE_PROC_CLASS + class + + + The IMAGE_PROC class descriptor. + + + V4L2_CID_LINK_FREQ + integer menu + + + Data bus frequency. Together with the + media bus pixel code, bus type (clock cycles per sample), the + data bus frequency defines the pixel rate + (V4L2_CID_PIXEL_RATE) in the + pixel array (or possibly elsewhere, if the device is not an + image sensor). The frame rate can be calculated from the pixel + clock, image width and height and horizontal and vertical + blanking. While the pixel rate control may be defined elsewhere + than in the subdev containing the pixel array, the frame rate + cannot be obtained from that information. This is because only + on the pixel array it can be assumed that the vertical and + horizontal blanking information is exact: no other blanking is + allowed in the pixel array. The selection of frame rate is + performed by selecting the desired horizontal and vertical + blanking. The unit of this control is Hz. + + + V4L2_CID_PIXEL_RATE + 64-bit integer + + + Pixel rate in the source pads of + the subdev. This control is read-only and its unit is + pixels / second. + + + + + +
+ +
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index f2d2ec3f0e31..0a4b90fcf2da 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -278,6 +278,12 @@ These controls are described in .
+ + V4L2_CTRL_CLASS_IMAGE_PROC + 0x9f0000 The class containing image + processing controls. These controls are described in . + diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 7a2f855d4e21..edb2a6a066ca 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -650,6 +650,11 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_HBLANK: return "Horizontal Blanking"; case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain"; + /* Image processing controls */ + case V4L2_CID_IMAGE_PROC_CLASS: return "Image Processing Controls"; + case V4L2_CID_LINK_FREQ: return "Link Frequency"; + case V4L2_CID_PIXEL_RATE: return "Pixel Rate"; + default: return NULL; } @@ -741,6 +746,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: *type = V4L2_CTRL_TYPE_MENU; break; + case V4L2_CID_LINK_FREQ: + *type = V4L2_CTRL_TYPE_INTEGER_MENU; + break; case V4L2_CID_RDS_TX_PS_NAME: case V4L2_CID_RDS_TX_RADIO_TEXT: *type = V4L2_CTRL_TYPE_STRING; @@ -752,6 +760,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_FLASH_CLASS: case V4L2_CID_JPEG_CLASS: case V4L2_CID_IMAGE_SOURCE_CLASS: + case V4L2_CID_IMAGE_PROC_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -775,8 +784,11 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_MPEG_VIDEO_DEC_FRAME: case V4L2_CID_MPEG_VIDEO_DEC_PTS: + *flags |= V4L2_CTRL_FLAG_VOLATILE; + /* Fall through */ + case V4L2_CID_PIXEL_RATE: *type = V4L2_CTRL_TYPE_INTEGER64; - *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE; + *flags |= V4L2_CTRL_FLAG_READ_ONLY; *min = *max = *step = *def = 0; break; default: diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 3cd13b2faab5..0ae6eb2fb1f3 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1140,6 +1140,7 @@ struct v4l2_ext_controls { #define V4L2_CTRL_CLASS_FLASH 0x009c0000 /* Camera flash controls */ #define V4L2_CTRL_CLASS_JPEG 0x009d0000 /* JPEG-compression controls */ #define V4L2_CTRL_CLASS_IMAGE_SOURCE 0x009e0000 /* Image source controls */ +#define V4L2_CTRL_CLASS_IMAGE_PROC 0x009f0000 /* Image processing controls */ #define V4L2_CTRL_ID_MASK (0x0fffffff) #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) @@ -1797,6 +1798,13 @@ enum v4l2_jpeg_chroma_subsampling { #define V4L2_CID_HBLANK (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 2) #define V4L2_CID_ANALOGUE_GAIN (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 3) +/* Image processing controls */ +#define V4L2_CID_IMAGE_PROC_CLASS_BASE (V4L2_CTRL_CLASS_IMAGE_PROC | 0x900) +#define V4L2_CID_IMAGE_PROC_CLASS (V4L2_CTRL_CLASS_IMAGE_PROC | 1) + +#define V4L2_CID_LINK_FREQ (V4L2_CID_IMAGE_PROC_CLASS_BASE + 1) +#define V4L2_CID_PIXEL_RATE (V4L2_CID_IMAGE_PROC_CLASS_BASE + 2) + /* * T U N I N G */ -- cgit v1.2.3 From 1e3afaea569cc12ef02cced0acb6c7e39067b169 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 17 Jan 2012 17:47:22 -0300 Subject: [media] v4l: Document raw bayer 4CC codes Document guidelines how 4CC codes should be named. Only raw bayer is included currently. Other formats should be documented later on. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/4CCs.txt | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Documentation/video4linux/4CCs.txt diff --git a/Documentation/video4linux/4CCs.txt b/Documentation/video4linux/4CCs.txt new file mode 100644 index 000000000000..41241af1ebfe --- /dev/null +++ b/Documentation/video4linux/4CCs.txt @@ -0,0 +1,32 @@ +Guidelines for Linux4Linux pixel format 4CCs +============================================ + +Guidelines for Video4Linux 4CC codes defined using v4l2_fourcc() are +specified in this document. First of the characters defines the nature of +the pixel format, compression and colour space. The interpretation of the +other three characters depends on the first one. + +Existing 4CCs may not obey these guidelines. + +Formats +======= + +Raw bayer +--------- + +The following first characters are used by raw bayer formats: + + B: raw bayer, uncompressed + b: raw bayer, DPCM compressed + a: A-law compressed + u: u-law compressed + +2nd character: pixel order + B: BGGR + G: GBRG + g: GRBG + R: RGGB + +3rd character: uncompressed bits-per-pixel 0--9, A-- + +4th character: compressed bits-per-pixel 0--9, A-- -- cgit v1.2.3 From 440f0fadd407c66abe285ce26ed8c31fb2403f0d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 28 Dec 2011 06:17:26 -0300 Subject: [media] v4l: Add DPCM compressed raw bayer pixel formats Add three other colour orders for 10-bit to 8-bit DPCM compressed raw bayer pixel formats. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/pixfmt-srggb10.xml | 2 +- .../DocBook/media/v4l/pixfmt-srggb10dpcm8.xml | 29 ++++++++++++++++++++++ Documentation/DocBook/media/v4l/pixfmt.xml | 6 +---- include/linux/videodev2.h | 3 +++ 4 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml index 7b274092e60c..c1c62a9acc2a 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml @@ -1,4 +1,4 @@ - + V4L2_PIX_FMT_SRGGB10 ('RG10'), V4L2_PIX_FMT_SGRBG10 ('BA10'), diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml new file mode 100644 index 000000000000..8eace3e2e7d4 --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml @@ -0,0 +1,29 @@ + + + + V4L2_PIX_FMT_SBGGR10DPCM8 ('bBA8'), + V4L2_PIX_FMT_SGBRG10DPCM8 ('bGA8'), + V4L2_PIX_FMT_SGRBG10DPCM8 ('BD10'), + V4L2_PIX_FMT_SRGGB10DPCM8 ('bRA8'), + + &manvol; + + + V4L2_PIX_FMT_SBGGR10DPCM8 + V4L2_PIX_FMT_SGBRG10DPCM8 + V4L2_PIX_FMT_SGRBG10DPCM8 + V4L2_PIX_FMT_SRGGB10DPCM8 + 10-bit Bayer formats compressed to 8 bits + + + Description + + The following four pixel formats are raw sRGB / Bayer formats + with 10 bits per colour compressed to 8 bits each, using DPCM + compression. DPCM, differential pulse-code modulation, is lossy. + Each colour component consumes 8 bits of memory. In other respects + this format is similar to . + + + diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 31eaae2469f9..f5ac15ed0549 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -673,6 +673,7 @@ access the palette, this must be done with ioctls of the Linux framebuffer API.< &sub-srggb8; &sub-sbggr16; &sub-srggb10; + &sub-srggb10dpcm8; &sub-srggb12; @@ -876,11 +877,6 @@ kernel sources in the file Documentation/video4linux/cx2341x/README.hm 'S561' Compressed GBRG Bayer format used by the gspca driver.
- - V4L2_PIX_FMT_SGRBG10DPCM8 - 'DB10' - 10 bit raw Bayer DPCM compressed to 8 bits. - V4L2_PIX_FMT_PAC207 'P207' diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 0ae6eb2fb1f3..7f75846a4a0c 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -378,7 +378,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */ #define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */ /* 10bit raw bayer DPCM compressed to 8 bits */ +#define V4L2_PIX_FMT_SBGGR10DPCM8 v4l2_fourcc('b', 'B', 'A', '8') +#define V4L2_PIX_FMT_SGBRG10DPCM8 v4l2_fourcc('b', 'G', 'A', '8') #define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0') +#define V4L2_PIX_FMT_SRGGB10DPCM8 v4l2_fourcc('b', 'R', 'A', '8') /* * 10bit raw bayer, expanded to 16 bits * xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb... -- cgit v1.2.3 From af88be3887c1a0b20d0792c3c237a67c73ef3286 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 11 Jan 2012 06:25:15 -0300 Subject: [media] media: Add link_validate() op to check links to the sink pad The purpose of the link_validate() op is to allow an entity driver to ensure that the properties of the pads at the both ends of the link are suitable for starting the pipeline. link_validate is called on sink pads on active links which belong to the active part of the graph. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/media-framework.txt | 19 +++++++++++++ drivers/media/media-entity.c | 57 +++++++++++++++++++++++++++++++++++++-- include/media/media-entity.h | 5 ++-- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 3a0f879533ce..802875413873 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -335,6 +335,9 @@ the media_entity pipe field. Calls to media_entity_pipeline_start() can be nested. The pipeline pointer must be identical for all nested calls to the function. +media_entity_pipeline_start() may return an error. In that case, it will +clean up any the changes it did by itself. + When stopping the stream, drivers must notify the entities with media_entity_pipeline_stop(struct media_entity *entity); @@ -351,3 +354,19 @@ If other operations need to be disallowed on streaming entities (such as changing entities configuration parameters) drivers can explicitly check the media_entity stream_count field to find out if an entity is streaming. This operation must be done with the media_device graph_mutex held. + + +Link validation +--------------- + +Link validation is performed by media_entity_pipeline_start() for any +entity which has sink pads in the pipeline. The +media_entity::link_validate() callback is used for that purpose. In +link_validate() callback, entity driver should check that the properties of +the source pad of the connected entity and its own sink pad match. It is up +to the type of the entity (and in the end, the properties of the hardware) +what matching actually means. + +Subsystems should facilitate link validation by providing subsystem specific +helper functions to provide easy access for commonly needed information, and +in the end provide a way to use driver-specific callbacks. diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 056138f63c7d..e1cd13283407 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -214,23 +214,76 @@ EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); * pipeline pointer must be identical for all nested calls to * media_entity_pipeline_start(). */ -void media_entity_pipeline_start(struct media_entity *entity, - struct media_pipeline *pipe) +__must_check int media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe) { struct media_device *mdev = entity->parent; struct media_entity_graph graph; + struct media_entity *entity_err = entity; + int ret; mutex_lock(&mdev->graph_mutex); media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { + unsigned int i; + entity->stream_count++; WARN_ON(entity->pipe && entity->pipe != pipe); entity->pipe = pipe; + + /* Already streaming --- no need to check. */ + if (entity->stream_count > 1) + continue; + + if (!entity->ops || !entity->ops->link_validate) + continue; + + for (i = 0; i < entity->num_links; i++) { + struct media_link *link = &entity->links[i]; + + /* Is this pad part of an enabled link? */ + if (!(link->flags & MEDIA_LNK_FL_ENABLED)) + continue; + + /* Are we the sink or not? */ + if (link->sink->entity != entity) + continue; + + ret = entity->ops->link_validate(link); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto error; + } } mutex_unlock(&mdev->graph_mutex); + + return 0; + +error: + /* + * Link validation on graph failed. We revert what we did and + * return the error. + */ + media_entity_graph_walk_start(&graph, entity_err); + + while ((entity_err = media_entity_graph_walk_next(&graph))) { + entity_err->stream_count--; + if (entity_err->stream_count == 0) + entity_err->pipe = NULL; + + /* + * We haven't increased stream_count further than this + * so we quit here. + */ + if (entity_err == entity) + break; + } + + mutex_unlock(&mdev->graph_mutex); + + return ret; } EXPORT_SYMBOL_GPL(media_entity_pipeline_start); diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 29e7bba78ffe..0c16f518ee09 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -46,6 +46,7 @@ struct media_entity_operations { int (*link_setup)(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags); + int (*link_validate)(struct media_link *link); }; struct media_entity { @@ -140,8 +141,8 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity *entity); struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph); -void media_entity_pipeline_start(struct media_entity *entity, - struct media_pipeline *pipe); +__must_check int media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe); void media_entity_pipeline_stop(struct media_entity *entity); #define media_entity_call(entity, operation, args...) \ -- cgit v1.2.3 From 48398f932b4ddcc8fa9a890595645cc14f6889a8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 23 Jan 2012 03:03:00 -0300 Subject: [media] v4l: Improve sub-device documentation for pad ops Document that format related configuration is done through pad ops in case the driver does use the media framework. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 369d4bc87828..493ffd1b1cf5 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -266,11 +266,16 @@ struct v4l2_subdev_video_ops { ... }; +struct v4l2_subdev_pad_ops { + ... +}; + struct v4l2_subdev_ops { const struct v4l2_subdev_core_ops *core; const struct v4l2_subdev_tuner_ops *tuner; const struct v4l2_subdev_audio_ops *audio; const struct v4l2_subdev_video_ops *video; + const struct v4l2_subdev_pad_ops *video; }; The core ops are common to all subdevs, the other categories are implemented @@ -307,6 +312,10 @@ Don't forget to cleanup the media entity before the sub-device is destroyed: media_entity_cleanup(&sd->entity); +If the subdev driver intends to process video and integrate with the media +framework, it must implement format related functionality using +v4l2_subdev_pad_ops instead of v4l2_subdev_video_ops. + A device (bridge) driver needs to register the v4l2_subdev with the v4l2_device: -- cgit v1.2.3 From 8227c92b69688403dee2adf5f399a49539ae5049 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 10 Oct 2011 17:01:25 -0300 Subject: [media] v4l: Implement v4l2_subdev_link_validate() v4l2_subdev_link_validate() is the default op for validating a link. In V4L2 subdev context, it is used to call a pad op which performs the proper link check without much extra work. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 12 ++++++ drivers/media/video/v4l2-subdev.c | 64 ++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 12 ++++++ 3 files changed, 88 insertions(+) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 493ffd1b1cf5..fe53177f0d3c 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -316,6 +316,18 @@ If the subdev driver intends to process video and integrate with the media framework, it must implement format related functionality using v4l2_subdev_pad_ops instead of v4l2_subdev_video_ops. +In that case, the subdev driver may set the link_validate field to provide +its own link validation function. The link validation function is called for +every link in the pipeline where both of the ends of the links are V4L2 +sub-devices. The driver is still responsible for validating the correctness +of the format configuration between sub-devices and video nodes. + +If link_validate op is not set, the default function +v4l2_subdev_link_validate_default() is used instead. This function ensures +that width, height and the media bus pixel code are equal on both source and +sink of the link. Subdev drivers are also free to use this function to +perform the checks mentioned above in addition to their own checks. + A device (bridge) driver needs to register the v4l2_subdev with the v4l2_device: diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 268d80584101..db6e859b93d4 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -387,6 +387,70 @@ const struct v4l2_file_operations v4l2_subdev_fops = { .poll = subdev_poll, }; +#ifdef CONFIG_MEDIA_CONTROLLER +int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + if (source_fmt->format.width != sink_fmt->format.width + || source_fmt->format.height != sink_fmt->format.height + || source_fmt->format.code != sink_fmt->format.code) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); + +static int +v4l2_subdev_link_validate_get_format(struct media_pad *pad, + struct v4l2_subdev_format *fmt) +{ + switch (media_entity_type(pad->entity)) { + case MEDIA_ENT_T_V4L2_SUBDEV: + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt->pad = pad->index; + return v4l2_subdev_call(media_entity_to_v4l2_subdev( + pad->entity), + pad, get_fmt, NULL, fmt); + default: + WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n", + media_entity_type(pad->entity), pad->entity->name); + /* Fall through */ + case MEDIA_ENT_T_DEVNODE_V4L: + return -EINVAL; + } +} + +int v4l2_subdev_link_validate(struct media_link *link) +{ + struct v4l2_subdev *sink; + struct v4l2_subdev_format sink_fmt, source_fmt; + int rval; + + rval = v4l2_subdev_link_validate_get_format( + link->source, &source_fmt); + if (rval < 0) + return 0; + + rval = v4l2_subdev_link_validate_get_format( + link->sink, &sink_fmt); + if (rval < 0) + return 0; + + sink = media_entity_to_v4l2_subdev(link->sink->entity); + + rval = v4l2_subdev_call(sink, pad, link_validate, link, + &source_fmt, &sink_fmt); + if (rval != -ENOIOCTLCMD) + return rval; + + return v4l2_subdev_link_validate_default( + sink, link, &source_fmt, &sink_fmt); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); +#endif /* CONFIG_MEDIA_CONTROLLER */ + void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) { INIT_LIST_HEAD(&sd->list); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 7e850355a6f0..1c2318b15bd2 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -470,6 +470,11 @@ struct v4l2_subdev_pad_ops { struct v4l2_subdev_selection *sel); int (*set_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_selection *sel); +#ifdef CONFIG_MEDIA_CONTROLLER + int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt); +#endif /* CONFIG_MEDIA_CONTROLLER */ }; struct v4l2_subdev_ops { @@ -602,6 +607,13 @@ static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd) return sd->host_priv; } +#ifdef CONFIG_MEDIA_CONTROLLER +int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt); +int v4l2_subdev_link_validate(struct media_link *link); +#endif /* CONFIG_MEDIA_CONTROLLER */ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops); -- cgit v1.2.3 From 77e7c4e624404c6edb5686b3d5f873c6008ed6b0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 24 Jan 2012 21:05:34 -0300 Subject: [media] v4l: Allow changing control handler lock Allow choosing the lock used by the control handler. This may be handy sometimes when a driver providing multiple subdevs does not want to use several locks to serialise its functions. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/adp1653.c | 8 ++++---- drivers/media/video/v4l2-ctrls.c | 39 ++++++++++++++++++++------------------- drivers/media/video/vivi.c | 4 ++-- include/media/v4l2-ctrls.h | 9 ++++++--- 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c index 24afc99d26e4..57e87090388d 100644 --- a/drivers/media/video/adp1653.c +++ b/drivers/media/video/adp1653.c @@ -281,19 +281,19 @@ adp1653_init_device(struct adp1653_flash *flash) return -EIO; } - mutex_lock(&flash->ctrls.lock); + mutex_lock(flash->ctrls.lock); /* Reset faults before reading new ones. */ flash->fault = 0; rval = adp1653_get_fault(flash); - mutex_unlock(&flash->ctrls.lock); + mutex_unlock(flash->ctrls.lock); if (rval > 0) { dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); return -EIO; } - mutex_lock(&flash->ctrls.lock); + mutex_lock(flash->ctrls.lock); rval = adp1653_update_hw(flash); - mutex_unlock(&flash->ctrls.lock); + mutex_unlock(flash->ctrls.lock); if (rval) { dev_err(&client->dev, "adp1653_update_hw failed at %s\n", __func__); diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index edb2a6a066ca..e5531ace5ee7 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -1177,7 +1177,8 @@ static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err) int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl, unsigned nr_of_controls_hint) { - mutex_init(&hdl->lock); + hdl->lock = &hdl->_lock; + mutex_init(hdl->lock); INIT_LIST_HEAD(&hdl->ctrls); INIT_LIST_HEAD(&hdl->ctrl_refs); hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8; @@ -1198,7 +1199,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) if (hdl == NULL || hdl->buckets == NULL) return; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* Free all nodes */ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) { list_del(&ref->node); @@ -1215,7 +1216,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) hdl->buckets = NULL; hdl->cached = NULL; hdl->error = 0; - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); } EXPORT_SYMBOL(v4l2_ctrl_handler_free); @@ -1280,9 +1281,9 @@ static struct v4l2_ctrl_ref *find_ref_lock( struct v4l2_ctrl_ref *ref = NULL; if (hdl) { - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); ref = find_ref(hdl, id); - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); } return ref; } @@ -1329,7 +1330,7 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, INIT_LIST_HEAD(&new_ref->node); - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* Add immediately at the end of the list if the list is empty, or if the last element in the list has a lower ID. @@ -1359,7 +1360,7 @@ insert_in_hash: hdl->buckets[bucket] = new_ref; unlock: - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return 0; } @@ -1445,9 +1446,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, kfree(ctrl); return NULL; } - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); list_add_tail(&ctrl->node, &hdl->ctrls); - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return ctrl; } @@ -1564,7 +1565,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, return 0; if (hdl->error) return hdl->error; - mutex_lock(&add->lock); + mutex_lock(add->lock); list_for_each_entry(ref, &add->ctrl_refs, node) { struct v4l2_ctrl *ctrl = ref->ctrl; @@ -1578,7 +1579,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, if (ret) break; } - mutex_unlock(&add->lock); + mutex_unlock(add->lock); return ret; } EXPORT_SYMBOL(v4l2_ctrl_add_handler); @@ -1742,11 +1743,11 @@ void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl, len = strlen(prefix); if (len && prefix[len - 1] != ' ') colon = ": "; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); list_for_each_entry(ctrl, &hdl->ctrls, node) if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED)) log_ctrl(ctrl, prefix, colon); - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); } EXPORT_SYMBOL(v4l2_ctrl_handler_log_status); @@ -1758,7 +1759,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) if (hdl == NULL) return 0; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); list_for_each_entry(ctrl, &hdl->ctrls, node) ctrl->done = false; @@ -1783,7 +1784,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) if (ret) break; } - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return ret; } EXPORT_SYMBOL(v4l2_ctrl_handler_setup); @@ -1798,7 +1799,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) if (hdl == NULL) return -EINVAL; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* Try to find it */ ref = find_ref(hdl, id); @@ -1823,7 +1824,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) break; } } - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); if (!ref) return -EINVAL; @@ -2000,7 +2001,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, belong to the same cluster. */ /* This has to be done with the handler lock taken. */ - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* First zero the helper field in the master control references */ for (i = 0; i < cs->count; i++) @@ -2022,7 +2023,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, /* Point the mref helper to the current helper struct. */ mref->helper = h; } - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return 0; } diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index d64d482f4f6b..6f2e354a242d 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -485,7 +485,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) gen_text(dev, vbuf, line++ * 16, 16, str); gain = v4l2_ctrl_g_ctrl(dev->gain); - mutex_lock(&dev->ctrl_handler.lock); + mutex_lock(dev->ctrl_handler.lock); snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ", dev->brightness->cur.val, dev->contrast->cur.val, @@ -509,7 +509,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->int_menu->qmenu_int[dev->int_menu->cur.val], dev->int_menu->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); - mutex_unlock(&dev->ctrl_handler.lock); + mutex_unlock(dev->ctrl_handler.lock); if (dev->button_pressed) { dev->button_pressed--; snprintf(str, sizeof(str), " button pressed!"); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index c6f6b4c2c5f2..dde6fbacc271 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -167,7 +167,9 @@ struct v4l2_ctrl_ref { /** struct v4l2_ctrl_handler - The control handler keeps track of all the * controls: both the controls owned by the handler and those inherited * from other handlers. + * @_lock: Default for "lock". * @lock: Lock to control access to this handler and its controls. + * May be replaced by the user right after init. * @ctrls: The list of controls owned by this handler. * @ctrl_refs: The list of control references. * @cached: The last found control reference. It is common that the same @@ -178,7 +180,8 @@ struct v4l2_ctrl_ref { * @error: The error code of the first failed control addition. */ struct v4l2_ctrl_handler { - struct mutex lock; + struct mutex _lock; + struct mutex *lock; struct list_head ctrls; struct list_head ctrl_refs; struct v4l2_ctrl_ref *cached; @@ -455,7 +458,7 @@ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); */ static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl) { - mutex_lock(&ctrl->handler->lock); + mutex_lock(ctrl->handler->lock); } /** v4l2_ctrl_lock() - Helper function to unlock the handler @@ -464,7 +467,7 @@ static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl) */ static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) { - mutex_unlock(&ctrl->handler->lock); + mutex_unlock(ctrl->handler->lock); } /** v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver. -- cgit v1.2.3 From 8f4f298e027bc58513811d7bbb7bfa457b0026c3 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 28 Dec 2011 06:28:41 -0300 Subject: [media] omap3isp: Support additional in-memory compressed bayer formats This also prevents accessing NULL pointer in csi2_try_format(). Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispvideo.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index 2107d99d523a..1fa29cc9bb6d 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -46,6 +46,10 @@ * Helper functions */ +/* + * NOTE: When adding new media bus codes, always remember to add + * corresponding in-memory formats to the table below!!! + */ static struct isp_format_info formats[] = { { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, @@ -68,9 +72,18 @@ static struct isp_format_info formats[] = { { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 8, }, + { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, + V4L2_MBUS_FMT_SBGGR10_1X10, 0, + V4L2_PIX_FMT_SBGGR10DPCM8, 8, }, + { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGBRG10_1X10, 0, + V4L2_PIX_FMT_SGBRG10DPCM8, 8, }, { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_1X10, 0, V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, + { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, + V4L2_MBUS_FMT_SRGGB10_1X10, 0, + V4L2_PIX_FMT_SRGGB10DPCM8, 8, }, { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR10, 10, }, -- cgit v1.2.3 From 618b055bc9c7253677520c1536a47540c3647a1a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 11 Dec 2011 09:57:51 -0300 Subject: [media] omap3isp: Move definitions required by board code under include/media XCLK definitions are often required by the board code. Move them to public include file. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isp.h | 4 ---- include/media/omap3isp.h | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h index f8d1f100fc19..38c6619a7723 100644 --- a/drivers/media/video/omap3isp/isp.h +++ b/drivers/media/video/omap3isp/isp.h @@ -238,10 +238,6 @@ void omap3isp_configure_bridge(struct isp_device *isp, const struct isp_parallel_platform_data *pdata, unsigned int shift); -#define ISP_XCLK_NONE 0 -#define ISP_XCLK_A 1 -#define ISP_XCLK_B 2 - struct isp_device *omap3isp_get(struct isp_device *isp); void omap3isp_put(struct isp_device *isp); diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h index 042849a34640..3f4928df6ed0 100644 --- a/include/media/omap3isp.h +++ b/include/media/omap3isp.h @@ -29,6 +29,10 @@ struct i2c_board_info; struct isp_device; +#define ISP_XCLK_NONE 0 +#define ISP_XCLK_A 1 +#define ISP_XCLK_B 2 + enum isp_interface_type { ISP_INTERFACE_PARALLEL, ISP_INTERFACE_CSI2A_PHY2, -- cgit v1.2.3 From b0cd79ed987c7b362a2e1a339ce9959192dc2f52 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 16 Jan 2012 18:59:02 -0300 Subject: [media] omap3isp: Move setting constaints above media_entity_pipeline_start The clock rate for l3_ick will soon be read during pipeline validation which is now part of media_entity_pipeline_start(). For that reason we set constraints earlier on. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispvideo.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index 1fa29cc9bb6d..5e55477f120d 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -304,7 +304,6 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) struct v4l2_subdev *subdev; int ret; - pipe->max_rate = pipe->l3_ick; pipe->entities = 0; subdev = isp_video_remote_subdev(pipe->output, NULL); @@ -997,6 +996,12 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) */ pipe = video->video.entity.pipe ? to_isp_pipeline(&video->video.entity) : &video->pipe; + + if (video->isp->pdata->set_constraints) + video->isp->pdata->set_constraints(video->isp, true); + pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); + pipe->max_rate = pipe->l3_ick; + media_entity_pipeline_start(&video->video.entity, &pipe->pipe); /* Verify that the currently configured format matches the output of @@ -1029,10 +1034,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe->output = far_end; } - if (video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, true); - pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); - /* Validate the pipeline and update its state. */ ret = isp_video_validate_pipeline(pipe); if (ret < 0) @@ -1078,9 +1079,9 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) error: if (ret < 0) { omap3isp_video_queue_streamoff(&vfh->queue); + media_entity_pipeline_stop(&video->video.entity); if (video->isp->pdata->set_constraints) video->isp->pdata->set_constraints(video->isp, false); - media_entity_pipeline_stop(&video->video.entity); /* The DMA queue must be emptied here, otherwise CCDC interrupts * that will get triggered the next time the CCDC is powered up * will try to access buffers that might have been freed but -- cgit v1.2.3 From da39257f0bc5a5780735abb8c8031e20a701d49a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 10 Oct 2011 17:05:24 -0300 Subject: [media] omap3isp: Assume media_entity_pipeline_start may fail Since media_entity_pipeline_start() now does link validation, it may actually fail. Perform the error handling. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispvideo.c | 52 ++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index 5e55477f120d..b6c407c37502 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -1002,14 +1002,16 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); pipe->max_rate = pipe->l3_ick; - media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + if (ret < 0) + goto err_pipeline_start; /* Verify that the currently configured format matches the output of * the connected subdev. */ ret = isp_video_check_format(video, vfh); if (ret < 0) - goto error; + goto err_check_format; video->bpl_padding = ret; video->bpl_value = vfh->format.fmt.pix.bytesperline; @@ -1026,7 +1028,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) } else { if (far_end == NULL) { ret = -EPIPE; - goto error; + goto err_check_format; } state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; @@ -1037,7 +1039,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) /* Validate the pipeline and update its state. */ ret = isp_video_validate_pipeline(pipe); if (ret < 0) - goto error; + goto err_check_format; pipe->error = false; @@ -1059,7 +1061,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) ret = omap3isp_video_queue_streamon(&vfh->queue); if (ret < 0) - goto error; + goto err_check_format; /* In sensor-to-memory mode, the stream can be started synchronously * to the stream on command. In memory-to-memory mode, it will be @@ -1069,32 +1071,34 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) ret = omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_CONTINUOUS); if (ret < 0) - goto error; + goto err_set_stream; spin_lock_irqsave(&video->queue->irqlock, flags); if (list_empty(&video->dmaqueue)) video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; spin_unlock_irqrestore(&video->queue->irqlock, flags); } -error: - if (ret < 0) { - omap3isp_video_queue_streamoff(&vfh->queue); - media_entity_pipeline_stop(&video->video.entity); - if (video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); - /* The DMA queue must be emptied here, otherwise CCDC interrupts - * that will get triggered the next time the CCDC is powered up - * will try to access buffers that might have been freed but - * still present in the DMA queue. This can easily get triggered - * if the above omap3isp_pipeline_set_stream() call fails on a - * system with a free-running sensor. - */ - INIT_LIST_HEAD(&video->dmaqueue); - video->queue = NULL; - } + video->streaming = 1; + + mutex_unlock(&video->stream_lock); + return 0; - if (!ret) - video->streaming = 1; +err_set_stream: + omap3isp_video_queue_streamoff(&vfh->queue); +err_check_format: + media_entity_pipeline_stop(&video->video.entity); +err_pipeline_start: + if (video->isp->pdata->set_constraints) + video->isp->pdata->set_constraints(video->isp, false); + /* The DMA queue must be emptied here, otherwise CCDC interrupts that + * will get triggered the next time the CCDC is powered up will try to + * access buffers that might have been freed but still present in the + * DMA queue. This can easily get triggered if the above + * omap3isp_pipeline_set_stream() call fails on a system with a + * free-running sensor. + */ + INIT_LIST_HEAD(&video->dmaqueue); + video->queue = NULL; mutex_unlock(&video->stream_lock); return ret; -- cgit v1.2.3 From fe6adc1991b6ce0f6d1c4ca74ec9f02d2d8cb3b4 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 10 Oct 2011 14:13:26 -0300 Subject: [media] omap3isp: Add lane configuration to platform data Add lane configuration (order of clock and data lane) to platform data on both CCP2 and CSI-2. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispcsiphy.h | 15 ++------------- include/media/omap3isp.h | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/media/video/omap3isp/ispcsiphy.h b/drivers/media/video/omap3isp/ispcsiphy.h index 9596dc6830a6..e93a661e65d9 100644 --- a/drivers/media/video/omap3isp/ispcsiphy.h +++ b/drivers/media/video/omap3isp/ispcsiphy.h @@ -27,22 +27,11 @@ #ifndef OMAP3_ISP_CSI_PHY_H #define OMAP3_ISP_CSI_PHY_H +#include + struct isp_csi2_device; struct regulator; -struct csiphy_lane { - u8 pos; - u8 pol; -}; - -#define ISP_CSIPHY2_NUM_DATA_LANES 2 -#define ISP_CSIPHY1_NUM_DATA_LANES 1 - -struct isp_csiphy_lanes_cfg { - struct csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; - struct csiphy_lane clk; -}; - struct isp_csiphy_dphy_cfg { u8 ths_term; u8 ths_settle; diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h index 3f4928df6ed0..4d94be5226af 100644 --- a/include/media/omap3isp.h +++ b/include/media/omap3isp.h @@ -90,6 +90,29 @@ enum { ISP_CCP2_MODE_CCP2 = 1, }; +/** + * struct isp_csiphy_lane: CCP2/CSI2 lane position and polarity + * @pos: position of the lane + * @pol: polarity of the lane + */ +struct isp_csiphy_lane { + u8 pos; + u8 pol; +}; + +#define ISP_CSIPHY1_NUM_DATA_LANES 1 +#define ISP_CSIPHY2_NUM_DATA_LANES 2 + +/** + * struct isp_csiphy_lanes_cfg - CCP2/CSI2 lane configuration + * @data: Configuration of one or two data lanes + * @clk: Clock lane configuration + */ +struct isp_csiphy_lanes_cfg { + struct isp_csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; + struct isp_csiphy_lane clk; +}; + /** * struct isp_ccp2_platform_data - CCP2 interface platform data * @strobe_clk_pol: Strobe/clock polarity @@ -109,6 +132,7 @@ struct isp_ccp2_platform_data { unsigned int ccp2_mode:1; unsigned int phy_layer:1; unsigned int vpclk_div:2; + struct isp_csiphy_lanes_cfg lanecfg; }; /** @@ -119,6 +143,7 @@ struct isp_ccp2_platform_data { struct isp_csi2_platform_data { unsigned crc:1; unsigned vpclk_div:2; + struct isp_csiphy_lanes_cfg lanecfg; }; struct isp_subdev_i2c_board_info { -- cgit v1.2.3 From ae5df813314a581225db9e9f5ca9c61f2b216775 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 5 Mar 2012 20:22:41 -0300 Subject: [media] omap3isp: Refactor collecting information on entities in pipeline Collect information on entities in pipeline in isp_video_far_end(), outside pipeline validation. As this causes the function to have side effects, rename the function accordingly. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispvideo.c | 61 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index b6c407c37502..3f5065ac9ad9 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -255,8 +255,8 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad) } /* Return a pointer to the ISP video instance at the far end of the pipeline. */ -static struct isp_video * -isp_video_far_end(struct isp_video *video) +static int isp_video_get_graph_data(struct isp_video *video, + struct isp_pipeline *pipe) { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; @@ -267,21 +267,38 @@ isp_video_far_end(struct isp_video *video) media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { + struct isp_video *__video; + + pipe->entities |= 1 << entity->id; + + if (far_end != NULL) + continue; + if (entity == &video->video.entity) continue; if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) continue; - far_end = to_isp_video(media_entity_to_video_device(entity)); - if (far_end->type != video->type) - break; - - far_end = NULL; + __video = to_isp_video(media_entity_to_video_device(entity)); + if (__video->type != video->type) + far_end = __video; } mutex_unlock(&mdev->graph_mutex); - return far_end; + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pipe->input = far_end; + pipe->output = video; + } else { + if (far_end == NULL) + return -EPIPE; + + pipe->input = video; + pipe->output = far_end; + } + + return 0; } /* @@ -304,8 +321,6 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) struct v4l2_subdev *subdev; int ret; - pipe->entities = 0; - subdev = isp_video_remote_subdev(pipe->output, NULL); if (subdev == NULL) return -EPIPE; @@ -313,8 +328,6 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) while (1) { unsigned int shifter_link; - pipe->entities |= 1U << subdev->entity.id; - /* Retrieve the sink format */ pad = &subdev->entity.pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) @@ -977,7 +990,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) struct isp_video *video = video_drvdata(file); enum isp_pipeline_state state; struct isp_pipeline *pipe; - struct isp_video *far_end; unsigned long flags; int ret; @@ -997,6 +1009,8 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe = video->video.entity.pipe ? to_isp_pipeline(&video->video.entity) : &video->pipe; + pipe->entities = 0; + if (video->isp->pdata->set_constraints) video->isp->pdata->set_constraints(video->isp, true); pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); @@ -1016,25 +1030,14 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) video->bpl_padding = ret; video->bpl_value = vfh->format.fmt.pix.bytesperline; - /* Find the ISP video node connected at the far end of the pipeline and - * update the pipeline. - */ - far_end = isp_video_far_end(video); + ret = isp_video_get_graph_data(video, pipe); + if (ret < 0) + goto err_check_format; - if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT; - pipe->input = far_end; - pipe->output = video; - } else { - if (far_end == NULL) { - ret = -EPIPE; - goto err_check_format; - } - + else state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; - pipe->input = video; - pipe->output = far_end; - } /* Validate the pipeline and update its state. */ ret = isp_video_validate_pipeline(pipe); -- cgit v1.2.3 From 80b37e7fab78ad849f6a5c1b581d6b8b54e76b45 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 16 Jan 2012 18:58:01 -0300 Subject: [media] omap3isp: Add information on external subdev to struct isp_pipeline Add pointer to external subdev, pixel rate of the external subdev and bpp of the format to struct isp_pipeline. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispvideo.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h index c9187cbc3557..5acc909500ec 100644 --- a/drivers/media/video/omap3isp/ispvideo.h +++ b/drivers/media/video/omap3isp/ispvideo.h @@ -104,6 +104,9 @@ struct isp_pipeline { bool do_propagation; /* of frame number */ bool error; struct v4l2_fract max_timeperframe; + struct v4l2_subdev *external; + unsigned int external_rate; + unsigned int external_bpp; }; #define to_isp_pipeline(__e) \ -- cgit v1.2.3 From ccddd916dcde141a6fb5612da622a8ae060c12b8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 17 Jan 2012 12:29:38 -0300 Subject: [media] omap3isp: Introduce isp_video_check_external_subdevs() isp_video_check_external_subdevs() will retrieve external subdev's bits-per-pixel and pixel rate for the use of other ISP subdevs at streamon time. isp_video_check_external_subdevs() is called after pipeline validation. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispvideo.c | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index 3f5065ac9ad9..ffad91e93b6e 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -952,6 +952,81 @@ isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) file->f_flags & O_NONBLOCK); } +static int isp_video_check_external_subdevs(struct isp_video *video, + struct isp_pipeline *pipe) +{ + struct isp_device *isp = video->isp; + struct media_entity *ents[] = { + &isp->isp_csi2a.subdev.entity, + &isp->isp_csi2c.subdev.entity, + &isp->isp_ccp2.subdev.entity, + &isp->isp_ccdc.subdev.entity + }; + struct media_pad *source_pad; + struct media_entity *source = NULL; + struct media_entity *sink; + struct v4l2_subdev_format fmt; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + unsigned int i; + int ret = 0; + + for (i = 0; i < ARRAY_SIZE(ents); i++) { + /* Is the entity part of the pipeline? */ + if (!(pipe->entities & (1 << ents[i]->id))) + continue; + + /* ISP entities have always sink pad == 0. Find source. */ + source_pad = media_entity_remote_source(&ents[i]->pads[0]); + if (source_pad == NULL) + continue; + + source = source_pad->entity; + sink = ents[i]; + break; + } + + if (!source) { + dev_warn(isp->dev, "can't find source, failing now\n"); + return ret; + } + + if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV) + return 0; + + pipe->external = media_entity_to_v4l2_subdev(source); + + fmt.pad = source_pad->index; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(sink), + pad, get_fmt, NULL, &fmt); + if (unlikely(ret < 0)) { + dev_warn(isp->dev, "get_fmt returned null!\n"); + return ret; + } + + pipe->external_bpp = omap3isp_video_format_info(fmt.format.code)->bpp; + + memset(&ctrls, 0, sizeof(ctrls)); + memset(&ctrl, 0, sizeof(ctrl)); + + ctrl.id = V4L2_CID_PIXEL_RATE; + + ctrls.count = 1; + ctrls.controls = &ctrl; + + ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls); + if (ret < 0) { + dev_warn(isp->dev, "no pixel rate control in subdev %s\n", + pipe->external->name); + return ret; + } + + pipe->external_rate = ctrl.value64; + + return 0; +} + /* * Stream management * @@ -1039,6 +1114,10 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) else state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; + ret = isp_video_check_external_subdevs(video, pipe); + if (ret < 0) + goto err_check_format; + /* Validate the pipeline and update its state. */ ret = isp_video_validate_pipeline(pipe); if (ret < 0) -- cgit v1.2.3 From c6c01f97b1733ba110993ec51600c06961e41bfe Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 25 Feb 2012 21:13:41 -0300 Subject: [media] omap3isp: Use external rate instead of vpcfg Access pipe->external_rate instead of isp_ccdc.vpcfg.pixelclk. Also remove means to set the value for isp_ccdc_vpcfg.pixelclk. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isp.c | 14 -------------- drivers/media/video/omap3isp/isp.h | 1 - drivers/media/video/omap3isp/ispccdc.c | 6 ++---- drivers/media/video/omap3isp/ispccdc.h | 10 ---------- drivers/media/video/omap3isp/ispvideo.c | 2 +- 5 files changed, 3 insertions(+), 30 deletions(-) diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c index 0307ac39e440..1c347633e663 100644 --- a/drivers/media/video/omap3isp/isp.c +++ b/drivers/media/video/omap3isp/isp.c @@ -329,19 +329,6 @@ void omap3isp_configure_bridge(struct isp_device *isp, isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); } -/** - * isp_set_pixel_clock - Configures the ISP pixel clock - * @isp: OMAP3 ISP device - * @pixelclk: Average pixel clock in Hz - * - * Set the average pixel clock required by the sensor. The ISP will use the - * lowest possible memory bandwidth settings compatible with the clock. - **/ -static void isp_set_pixel_clock(struct isp_device *isp, unsigned int pixelclk) -{ - isp->isp_ccdc.vpcfg.pixelclk = pixelclk; -} - void omap3isp_hist_dma_done(struct isp_device *isp) { if (omap3isp_ccdc_busy(&isp->isp_ccdc) || @@ -2077,7 +2064,6 @@ static int __devinit isp_probe(struct platform_device *pdev) isp->autoidle = autoidle; isp->platform_cb.set_xclk = isp_set_xclk; - isp->platform_cb.set_pixel_clock = isp_set_pixel_clock; mutex_init(&isp->isp_mutex); spin_lock_init(&isp->stat_lock); diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h index 38c6619a7723..fc7af3e32efd 100644 --- a/drivers/media/video/omap3isp/isp.h +++ b/drivers/media/video/omap3isp/isp.h @@ -129,7 +129,6 @@ struct isp_platform_callback { int (*csiphy_config)(struct isp_csiphy *phy, struct isp_csiphy_dphy_cfg *dphy, struct isp_csiphy_lanes_cfg *lanes); - void (*set_pixel_clock)(struct isp_device *isp, unsigned int pixelclk); }; /* diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 8c73197005c6..080fe8b513b1 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -839,8 +839,8 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc) if (pipe->input) div = DIV_ROUND_UP(l3_ick, pipe->max_rate); - else if (ccdc->vpcfg.pixelclk) - div = l3_ick / ccdc->vpcfg.pixelclk; + else if (pipe->external_rate) + div = l3_ick / pipe->external_rate; div = clamp(div, 2U, max_div); fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; @@ -2433,8 +2433,6 @@ int omap3isp_ccdc_init(struct isp_device *isp) ccdc->clamp.oblen = 0; ccdc->clamp.dcsubval = 0; - ccdc->vpcfg.pixelclk = 0; - ccdc->update = OMAP3ISP_CCDC_BLCLAMP; ccdc_apply_controls(ccdc); diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h index 966bbf8a1262..890f6b3a68fd 100644 --- a/drivers/media/video/omap3isp/ispccdc.h +++ b/drivers/media/video/omap3isp/ispccdc.h @@ -80,14 +80,6 @@ struct ispccdc_syncif { u8 bt_r656_en; }; -/* - * struct ispccdc_vp - Structure for Video Port parameters - * @pixelclk: Input pixel clock in Hz - */ -struct ispccdc_vp { - unsigned int pixelclk; -}; - enum ispccdc_lsc_state { LSC_STATE_STOPPED = 0, LSC_STATE_STOPPING = 1, @@ -162,7 +154,6 @@ struct ispccdc_lsc { * @update: Bitmask of controls to update during the next interrupt * @shadow_update: Controls update in progress by userspace * @syncif: Interface synchronization configuration - * @vpcfg: Video port configuration * @underrun: A buffer underrun occurred and a new buffer has been queued * @state: Streaming state * @lock: Serializes shadow_update with interrupt handler @@ -192,7 +183,6 @@ struct isp_ccdc_device { unsigned int shadow_update; struct ispccdc_syncif syncif; - struct ispccdc_vp vpcfg; unsigned int underrun:1; enum isp_pipeline_stream_state state; diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index ffad91e93b6e..66bc6749de66 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -352,7 +352,7 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) unsigned int rate = UINT_MAX; omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); - if (isp->isp_ccdc.vpcfg.pixelclk > rate) + if (pipe->external_rate > rate) return -ENOSPC; } -- cgit v1.2.3 From 20d4ab7bea8e79bb330c2d52da9c245911ea29ed Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 11 Jan 2012 13:27:02 -0300 Subject: [media] omap3isp: Default link validation for ccp2, csi2, preview and resizer Use default link validation for ccp2, csi2, preview and resizer. On ccp2, csi2 and ccdc we also collect information on external subdevs as one may be connected to those entities. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispccp2.c | 1 + drivers/media/video/omap3isp/ispcsi2.c | 1 + drivers/media/video/omap3isp/isppreview.c | 1 + drivers/media/video/omap3isp/ispresizer.c | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c index ee7dcda36b6d..85f0de85f37c 100644 --- a/drivers/media/video/omap3isp/ispccp2.c +++ b/drivers/media/video/omap3isp/ispccp2.c @@ -998,6 +998,7 @@ static int ccp2_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations ccp2_media_ops = { .link_setup = ccp2_link_setup, + .link_validate = v4l2_subdev_link_validate, }; /* diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c index 75ac6d466962..a1724362b6d5 100644 --- a/drivers/media/video/omap3isp/ispcsi2.c +++ b/drivers/media/video/omap3isp/ispcsi2.c @@ -1167,6 +1167,7 @@ static int csi2_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations csi2_media_ops = { .link_setup = csi2_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index cbc887010b28..8a4935ecc655 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -2217,6 +2217,7 @@ static int preview_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations preview_media_ops = { .link_setup = preview_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_preview_unregister_entities(struct isp_prev_device *prev) diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index d7341ab15ffe..14041c9c8643 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1641,6 +1641,7 @@ static int resizer_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations resizer_media_ops = { .link_setup = resizer_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_resizer_unregister_entities(struct isp_res_device *res) -- cgit v1.2.3 From a6d7a62dcd1fccb3140100551b205315491eadc5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 25 Feb 2012 20:42:07 -0300 Subject: [media] omap3isp: Move CCDC link validation to ccdc_link_validate() Perform CCDC link validation in ccdc_link_validate() instead of isp_video_validate_pipeline(). Also perform maximum data rate check in isp_video_check_external_subdevs(). Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispccdc.c | 65 ++++++++++++++++++++++ drivers/media/video/omap3isp/ispvideo.c | 95 ++++----------------------------- 2 files changed, 76 insertions(+), 84 deletions(-) diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 080fe8b513b1..7e32331b60fb 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -2154,6 +2154,69 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } +/* + * Decide whether desired output pixel code can be obtained with + * the lane shifter by shifting the input pixel code. + * @in: input pixelcode to shifter + * @out: output pixelcode from shifter + * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0] + * + * return true if the combination is possible + * return false otherwise + */ +static bool ccdc_is_shiftable(enum v4l2_mbus_pixelcode in, + enum v4l2_mbus_pixelcode out, + unsigned int additional_shift) +{ + const struct isp_format_info *in_info, *out_info; + + if (in == out) + return true; + + in_info = omap3isp_video_format_info(in); + out_info = omap3isp_video_format_info(out); + + if ((in_info->flavor == 0) || (out_info->flavor == 0)) + return false; + + if (in_info->flavor != out_info->flavor) + return false; + + return in_info->bpp - out_info->bpp + additional_shift <= 6; +} + +static int ccdc_link_validate(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + unsigned long parallel_shift; + + /* Check if the two ends match */ + if (source_fmt->format.width != sink_fmt->format.width || + source_fmt->format.height != sink_fmt->format.height) + return -EPIPE; + + /* We've got a parallel sensor here. */ + if (ccdc->input == CCDC_INPUT_PARALLEL) { + struct isp_parallel_platform_data *pdata = + &((struct isp_v4l2_subdevs_group *) + media_entity_to_v4l2_subdev(link->source->entity) + ->host_priv)->bus.parallel; + parallel_shift = pdata->data_lane_shift * 2; + } else { + parallel_shift = 0; + } + + /* Lane shifter may be used to drop bits on CCDC sink pad */ + if (!ccdc_is_shiftable(source_fmt->format.code, + sink_fmt->format.code, parallel_shift)) + return -EPIPE; + + return 0; +} + /* * ccdc_init_formats - Initialize formats on all pads * @sd: ISP CCDC V4L2 subdevice @@ -2198,6 +2261,7 @@ static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { .set_fmt = ccdc_set_format, .get_selection = ccdc_get_selection, .set_selection = ccdc_set_selection, + .link_validate = ccdc_link_validate, }; /* V4L2 subdev operations */ @@ -2307,6 +2371,7 @@ static int ccdc_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations ccdc_media_ops = { .link_setup = ccdc_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index 66bc6749de66..b37379d39cdd 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -129,37 +129,6 @@ omap3isp_video_format_info(enum v4l2_mbus_pixelcode code) return NULL; } -/* - * Decide whether desired output pixel code can be obtained with - * the lane shifter by shifting the input pixel code. - * @in: input pixelcode to shifter - * @out: output pixelcode from shifter - * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0] - * - * return true if the combination is possible - * return false otherwise - */ -static bool isp_video_is_shiftable(enum v4l2_mbus_pixelcode in, - enum v4l2_mbus_pixelcode out, - unsigned int additional_shift) -{ - const struct isp_format_info *in_info, *out_info; - - if (in == out) - return true; - - in_info = omap3isp_video_format_info(in); - out_info = omap3isp_video_format_info(out); - - if ((in_info->flavor == 0) || (out_info->flavor == 0)) - return false; - - if (in_info->flavor != out_info->flavor) - return false; - - return in_info->bpp - out_info->bpp + additional_shift <= 6; -} - /* * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format * @video: ISP video instance @@ -315,51 +284,24 @@ static int isp_video_get_graph_data(struct isp_video *video, static int isp_video_validate_pipeline(struct isp_pipeline *pipe) { struct isp_device *isp = pipe->output->isp; - struct v4l2_subdev_format fmt_source; - struct v4l2_subdev_format fmt_sink; struct media_pad *pad; struct v4l2_subdev *subdev; - int ret; subdev = isp_video_remote_subdev(pipe->output, NULL); if (subdev == NULL) return -EPIPE; while (1) { - unsigned int shifter_link; - /* Retrieve the sink format */ pad = &subdev->entity.pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - fmt_sink.pad = pad->index; - fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink); - if (ret < 0 && ret != -ENOIOCTLCMD) - return -EPIPE; - /* Update the maximum frame rate */ if (subdev == &isp->isp_res.subdev) omap3isp_resizer_max_rate(&isp->isp_res, &pipe->max_rate); - /* Check ccdc maximum data rate when data comes from sensor - * TODO: Include ccdc rate in pipe->max_rate and compare the - * total pipe rate with the input data rate from sensor. - */ - if (subdev == &isp->isp_ccdc.subdev && pipe->input == NULL) { - unsigned int rate = UINT_MAX; - - omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); - if (pipe->external_rate > rate) - return -ENOSPC; - } - - /* If sink pad is on CCDC, the link has the lane shifter - * in the middle of it. */ - shifter_link = subdev == &isp->isp_ccdc.subdev; - /* Retrieve the source format. Return an error if no source * entity can be found, and stop checking the pipeline if the * source entity isn't a subdev. @@ -372,32 +314,6 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) break; subdev = media_entity_to_v4l2_subdev(pad->entity); - - fmt_source.pad = pad->index; - fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); - if (ret < 0 && ret != -ENOIOCTLCMD) - return -EPIPE; - - /* Check if the two ends match */ - if (fmt_source.format.width != fmt_sink.format.width || - fmt_source.format.height != fmt_sink.format.height) - return -EPIPE; - - if (shifter_link) { - unsigned int parallel_shift = 0; - if (isp->isp_ccdc.input == CCDC_INPUT_PARALLEL) { - struct isp_parallel_platform_data *pdata = - &((struct isp_v4l2_subdevs_group *) - subdev->host_priv)->bus.parallel; - parallel_shift = pdata->data_lane_shift * 2; - } - if (!isp_video_is_shiftable(fmt_source.format.code, - fmt_sink.format.code, - parallel_shift)) - return -EPIPE; - } else if (fmt_source.format.code != fmt_sink.format.code) - return -EPIPE; } return 0; @@ -1024,6 +940,17 @@ static int isp_video_check_external_subdevs(struct isp_video *video, pipe->external_rate = ctrl.value64; + if (pipe->entities & (1 << isp->isp_ccdc.subdev.entity.id)) { + unsigned int rate = UINT_MAX; + /* + * Check that maximum allowed CCDC pixel rate isn't + * exceeded by the pixel rate. + */ + omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); + if (pipe->external_rate > rate) + return -ENOSPC; + } + return 0; } -- cgit v1.2.3 From cf1c5fae5f8a28d478b7177a2d83e42d25eab072 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 7 Dec 2011 13:45:25 -0300 Subject: [media] smiapp: Generic SMIA++/SMIA PLL calculator Calculate PLL configuration based on input data: sensor configuration, board properties and sensor-specific limits. [mchehab@redhat.com: Fix a Kconfig conflict affecting APTINA_PLL] Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 3 + drivers/media/video/Makefile | 2 + drivers/media/video/smiapp-pll.c | 417 +++++++++++++++++++++++++++++++++++++++ drivers/media/video/smiapp-pll.h | 103 ++++++++++ 4 files changed, 525 insertions(+) create mode 100644 drivers/media/video/smiapp-pll.c create mode 100644 drivers/media/video/smiapp-pll.h diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index b09982ee21d7..d3e879f64310 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -472,6 +472,9 @@ comment "Camera sensor devices" config VIDEO_APTINA_PLL tristate +config VIDEO_SMIAPP_PLL + tristate + config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 1f5e36ceb944..4e6c100cf583 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -82,6 +82,8 @@ obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o +obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o + obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c new file mode 100644 index 000000000000..a416e27a4282 --- /dev/null +++ b/drivers/media/video/smiapp-pll.c @@ -0,0 +1,417 @@ +/* + * drivers/media/video/smiapp-pll.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include + +#include "smiapp-pll.h" + +/* Return an even number or one. */ +static inline uint32_t clk_div_even(uint32_t a) +{ + return max_t(uint32_t, 1, a & ~1); +} + +/* Return an even number or one. */ +static inline uint32_t clk_div_even_up(uint32_t a) +{ + if (a == 1) + return 1; + return (a + 1) & ~1; +} + +static inline uint32_t is_one_or_even(uint32_t a) +{ + if (a == 1) + return 1; + if (a & 1) + return 0; + + return 1; +} + +static int bounds_check(struct device *dev, uint32_t val, + uint32_t min, uint32_t max, char *str) +{ + if (val >= min && val <= max) + return 0; + + dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); + + return -EINVAL; +} + +static void print_pll(struct device *dev, struct smiapp_pll *pll) +{ + dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); + dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); + if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); + dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); + } + dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); + dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); + + dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); + dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); + dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); + if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", + pll->op_sys_clk_freq_hz); + dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", + pll->op_pix_clk_freq_hz); + } + dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); + dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); +} + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll) +{ + uint32_t sys_div; + uint32_t best_pix_div = INT_MAX >> 1; + uint32_t vt_op_binning_div; + uint32_t lane_op_clock_ratio; + uint32_t mul, div; + uint32_t more_mul_min, more_mul_max; + uint32_t more_mul_factor; + uint32_t min_vt_div, max_vt_div, vt_div; + uint32_t min_sys_div, max_sys_div; + unsigned int i; + int rval; + + if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) + lane_op_clock_ratio = pll->lanes; + else + lane_op_clock_ratio = 1; + dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); + + dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, + pll->binning_vertical); + + /* CSI transfers 2 bits per clock per lane; thus times 2 */ + pll->pll_op_clk_freq_hz = pll->link_freq * 2 + * (pll->lanes / lane_op_clock_ratio); + + /* Figure out limits for pre-pll divider based on extclk */ + dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + limits->max_pre_pll_clk_div = + min_t(uint16_t, limits->max_pre_pll_clk_div, + clk_div_even(pll->ext_clk_freq_hz / + limits->min_pll_ip_freq_hz)); + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even(pll->ext_clk_freq_hz / + limits->max_pll_ip_freq_hz)); + dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); + mul = div_u64(pll->pll_op_clk_freq_hz, i); + div = pll->ext_clk_freq_hz / i; + dev_dbg(dev, "mul %d / div %d\n", mul, div); + + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, + limits->max_pll_op_freq_hz))); + dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) { + dev_err(dev, "unable to compute pre_pll divisor\n"); + return -EINVAL; + } + + pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; + + /* + * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be + * too high. + */ + dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div); + + /* Don't go above max pll multiplier. */ + more_mul_max = limits->max_pll_multiplier / mul; + dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", + more_mul_max); + /* Don't go above max pll op frequency. */ + more_mul_max = + min_t(int, + more_mul_max, + limits->max_pll_op_freq_hz + / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); + dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n", + more_mul_max); + /* Don't go above the division capability of op sys clock divider. */ + more_mul_max = min(more_mul_max, + limits->max_op_sys_clk_div * pll->pre_pll_clk_div + / div); + dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", + more_mul_max); + /* Ensure we won't go above min_pll_multiplier. */ + more_mul_max = min(more_mul_max, + DIV_ROUND_UP(limits->max_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", + more_mul_max); + + /* Ensure we won't go below min_pll_op_freq_hz. */ + more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, + pll->ext_clk_freq_hz / pll->pre_pll_clk_div + * mul); + dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n", + more_mul_min); + /* Ensure we won't go below min_pll_multiplier. */ + more_mul_min = max(more_mul_min, + DIV_ROUND_UP(limits->min_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", + more_mul_min); + + if (more_mul_min > more_mul_max) { + dev_warn(dev, + "unable to compute more_mul_min and more_mul_max"); + return -EINVAL; + } + + more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; + dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); + more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div); + dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", + more_mul_factor); + i = roundup(more_mul_min, more_mul_factor); + if (!is_one_or_even(i)) + i <<= 1; + + dev_dbg(dev, "final more_mul: %d\n", i); + if (i > more_mul_max) { + dev_warn(dev, "final more_mul is bad, max %d", more_mul_max); + return -EINVAL; + } + + pll->pll_multiplier = mul * i; + pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; + dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); + + pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz + / pll->pre_pll_clk_div; + + pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz + * pll->pll_multiplier; + + /* Derive pll_op_clk_freq_hz. */ + pll->op_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; + + pll->op_pix_clk_div = pll->bits_per_pixel; + dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); + + pll->op_pix_clk_freq_hz = + pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; + + /* + * Some sensors perform analogue binning and some do this + * digitally. The ones doing this digitally can be roughly be + * found out using this formula. The ones doing this digitally + * should run at higher clock rate, so smaller divisor is used + * on video timing side. + */ + if (limits->min_line_length_pck_bin > limits->min_line_length_pck + / pll->binning_horizontal) + vt_op_binning_div = pll->binning_horizontal; + else + vt_op_binning_div = 1; + dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); + + /* + * Profile 2 supports vt_pix_clk_div E [4, 10] + * + * Horizontal binning can be used as a base for difference in + * divisors. One must make sure that horizontal blanking is + * enough to accommodate the CSI-2 sync codes. + * + * Take scaling factor into account as well. + * + * Find absolute limits for the factor of vt divider. + */ + dev_dbg(dev, "scale_m: %d\n", pll->scale_m); + min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div + * pll->scale_n, + lane_op_clock_ratio * vt_op_binning_div + * pll->scale_m); + + /* Find smallest and biggest allowed vt divisor. */ + dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); + min_vt_div = max(min_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz)); + dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", + min_vt_div); + min_vt_div = max_t(uint32_t, min_vt_div, + limits->min_vt_pix_clk_div + * limits->min_vt_sys_clk_div); + dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); + + max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div; + dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); + max_vt_div = min(max_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", + max_vt_div); + + /* + * Find limitsits for sys_clk_div. Not all values are possible + * with all values of pix_clk_div. + */ + min_sys_div = limits->min_vt_sys_clk_div; + dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + DIV_ROUND_UP(min_vt_div, + limits->max_vt_pix_clk_div)); + dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + pll->pll_op_clk_freq_hz + / limits->max_vt_sys_clk_freq_hz); + dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); + min_sys_div = clk_div_even_up(min_sys_div); + dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div); + + max_sys_div = limits->max_vt_sys_clk_div; + dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(max_vt_div, + limits->min_vt_pix_clk_div)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); + + /* + * Find pix_div such that a legal pix_div * sys_div results + * into a value which is not smaller than div, the desired + * divisor. + */ + for (vt_div = min_vt_div; vt_div <= max_vt_div; + vt_div += 2 - (vt_div & 1)) { + for (sys_div = min_sys_div; + sys_div <= max_sys_div; + sys_div += 2 - (sys_div & 1)) { + int pix_div = DIV_ROUND_UP(vt_div, sys_div); + + if (pix_div < limits->min_vt_pix_clk_div + || pix_div > limits->max_vt_pix_clk_div) { + dev_dbg(dev, + "pix_div %d too small or too big (%d--%d)\n", + pix_div, + limits->min_vt_pix_clk_div, + limits->max_vt_pix_clk_div); + continue; + } + + /* Check if this one is better. */ + if (pix_div * sys_div + <= roundup(min_vt_div, best_pix_div)) + best_pix_div = pix_div; + } + if (best_pix_div < INT_MAX >> 1) + break; + } + + pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); + pll->vt_pix_clk_div = best_pix_div; + + pll->vt_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; + pll->vt_pix_clk_freq_hz = + pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; + + pll->pixel_rate_csi = + pll->op_pix_clk_freq_hz * lane_op_clock_ratio; + + print_pll(dev, pll); + + rval = bounds_check(dev, pll->pre_pll_clk_div, + limits->min_pre_pll_clk_div, + limits->max_pre_pll_clk_div, "pre_pll_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->pll_ip_clk_freq_hz, + limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz, + "pll_ip_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->pll_multiplier, + limits->min_pll_multiplier, limits->max_pll_multiplier, + "pll_multiplier"); + if (!rval) + rval = bounds_check( + dev, pll->pll_op_clk_freq_hz, + limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, + "pll_op_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_div, + limits->min_op_sys_clk_div, limits->max_op_sys_clk_div, + "op_sys_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_div, + limits->min_op_pix_clk_div, limits->max_op_pix_clk_div, + "op_pix_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_freq_hz, + limits->min_op_sys_clk_freq_hz, + limits->max_op_sys_clk_freq_hz, + "op_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_freq_hz, + limits->min_op_pix_clk_freq_hz, + limits->max_op_pix_clk_freq_hz, + "op_pix_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_sys_clk_freq_hz, + limits->min_vt_sys_clk_freq_hz, + limits->max_vt_sys_clk_freq_hz, + "vt_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_pix_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz, + "vt_pix_clk_freq_hz"); + + return rval; +} +EXPORT_SYMBOL_GPL(smiapp_pll_calculate); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp-pll.h b/drivers/media/video/smiapp-pll.h new file mode 100644 index 000000000000..9eab63f23afb --- /dev/null +++ b/drivers/media/video/smiapp-pll.h @@ -0,0 +1,103 @@ +/* + * drivers/media/video/smiapp-pll.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SMIAPP_PLL_H +#define SMIAPP_PLL_H + +#include + +struct smiapp_pll { + uint8_t lanes; + uint8_t binning_horizontal; + uint8_t binning_vertical; + uint8_t scale_m; + uint8_t scale_n; + uint8_t bits_per_pixel; + uint16_t flags; + uint32_t link_freq; + + uint16_t pre_pll_clk_div; + uint16_t pll_multiplier; + uint16_t op_sys_clk_div; + uint16_t op_pix_clk_div; + uint16_t vt_sys_clk_div; + uint16_t vt_pix_clk_div; + + uint32_t ext_clk_freq_hz; + uint32_t pll_ip_clk_freq_hz; + uint32_t pll_op_clk_freq_hz; + uint32_t op_sys_clk_freq_hz; + uint32_t op_pix_clk_freq_hz; + uint32_t vt_sys_clk_freq_hz; + uint32_t vt_pix_clk_freq_hz; + + uint32_t pixel_rate_csi; +}; + +struct smiapp_pll_limits { + /* Strict PLL limits */ + uint32_t min_ext_clk_freq_hz; + uint32_t max_ext_clk_freq_hz; + uint16_t min_pre_pll_clk_div; + uint16_t max_pre_pll_clk_div; + uint32_t min_pll_ip_freq_hz; + uint32_t max_pll_ip_freq_hz; + uint16_t min_pll_multiplier; + uint16_t max_pll_multiplier; + uint32_t min_pll_op_freq_hz; + uint32_t max_pll_op_freq_hz; + + uint16_t min_vt_sys_clk_div; + uint16_t max_vt_sys_clk_div; + uint32_t min_vt_sys_clk_freq_hz; + uint32_t max_vt_sys_clk_freq_hz; + uint16_t min_vt_pix_clk_div; + uint16_t max_vt_pix_clk_div; + uint32_t min_vt_pix_clk_freq_hz; + uint32_t max_vt_pix_clk_freq_hz; + + uint16_t min_op_sys_clk_div; + uint16_t max_op_sys_clk_div; + uint32_t min_op_sys_clk_freq_hz; + uint32_t max_op_sys_clk_freq_hz; + uint16_t min_op_pix_clk_div; + uint16_t max_op_pix_clk_div; + uint32_t min_op_pix_clk_freq_hz; + uint32_t max_op_pix_clk_freq_hz; + + /* Other relevant limits */ + uint32_t min_line_length_pck_bin; + uint32_t min_line_length_pck; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) + +struct device; + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll); + +#endif /* SMIAPP_PLL_H */ -- cgit v1.2.3 From ccfc97bdb5ae8b8edc55169ac6924e08449836ac Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 3 Mar 2012 17:19:52 -0300 Subject: [media] smiapp: Add driver Add driver for SMIA++/SMIA image sensors. The driver exposes the sensor as three subdevs, pixel array, binner and scaler --- in case the device has a scaler. Currently it relies on the board code for external clock handling. There is no fast way out of this dependency before the ISP drivers (omap3isp) among others will be able to export that clock through the clock framework instead. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 2 + drivers/media/video/Makefile | 1 + drivers/media/video/smiapp-pll.c | 2 + drivers/media/video/smiapp/Kconfig | 13 + drivers/media/video/smiapp/Makefile | 3 + drivers/media/video/smiapp/smiapp-core.c | 2832 ++++++++++++++++++++++++++ drivers/media/video/smiapp/smiapp-debug.h | 32 + drivers/media/video/smiapp/smiapp-limits.c | 132 ++ drivers/media/video/smiapp/smiapp-limits.h | 128 ++ drivers/media/video/smiapp/smiapp-quirk.c | 264 +++ drivers/media/video/smiapp/smiapp-quirk.h | 72 + drivers/media/video/smiapp/smiapp-reg-defs.h | 503 +++++ drivers/media/video/smiapp/smiapp-reg.h | 122 ++ drivers/media/video/smiapp/smiapp-regs.c | 213 ++ drivers/media/video/smiapp/smiapp-regs.h | 46 + drivers/media/video/smiapp/smiapp.h | 251 +++ include/media/smiapp.h | 83 + 17 files changed, 4699 insertions(+) create mode 100644 drivers/media/video/smiapp/Kconfig create mode 100644 drivers/media/video/smiapp/Makefile create mode 100644 drivers/media/video/smiapp/smiapp-core.c create mode 100644 drivers/media/video/smiapp/smiapp-debug.h create mode 100644 drivers/media/video/smiapp/smiapp-limits.c create mode 100644 drivers/media/video/smiapp/smiapp-limits.h create mode 100644 drivers/media/video/smiapp/smiapp-quirk.c create mode 100644 drivers/media/video/smiapp/smiapp-quirk.h create mode 100644 drivers/media/video/smiapp/smiapp-reg-defs.h create mode 100644 drivers/media/video/smiapp/smiapp-reg.h create mode 100644 drivers/media/video/smiapp/smiapp-regs.c create mode 100644 drivers/media/video/smiapp/smiapp-regs.h create mode 100644 drivers/media/video/smiapp/smiapp.h create mode 100644 include/media/smiapp.h diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index d3e879f64310..9fc7c5224ac8 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -559,6 +559,8 @@ config VIDEO_S5K6AA This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M camera sensor with an embedded SoC image signal processor. +source "drivers/media/video/smiapp/Kconfig" + comment "Flash devices" config VIDEO_ADP1653 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 4e6c100cf583..5a97da2ae33b 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c index a416e27a4282..501da413dfad 100644 --- a/drivers/media/video/smiapp-pll.c +++ b/drivers/media/video/smiapp-pll.c @@ -22,6 +22,8 @@ * */ +#include "smiapp/smiapp-debug.h" + #include #include #include diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/video/smiapp/Kconfig new file mode 100644 index 000000000000..9504c436a5ca --- /dev/null +++ b/drivers/media/video/smiapp/Kconfig @@ -0,0 +1,13 @@ +config VIDEO_SMIAPP + tristate "SMIA++/SMIA sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEO_SMIAPP_PLL + ---help--- + This is a generic driver for SMIA++/SMIA camera modules. + +config VIDEO_SMIAPP_DEBUG + bool "Enable debugging for the generic SMIA++/SMIA driver" + depends on VIDEO_SMIAPP + ---help--- + Enable debugging output in the generic SMIA++/SMIA driver. If you + are developing the driver you might want to enable this. diff --git a/drivers/media/video/smiapp/Makefile b/drivers/media/video/smiapp/Makefile new file mode 100644 index 000000000000..5a207eecd357 --- /dev/null +++ b/drivers/media/video/smiapp/Makefile @@ -0,0 +1,3 @@ +smiapp-objs += smiapp-core.o smiapp-regs.o \ + smiapp-quirk.o smiapp-limits.o +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c new file mode 100644 index 000000000000..3991c452acb2 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -0,0 +1,2832 @@ +/* + * drivers/media/video/smiapp/smiapp-core.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * Based on smiapp driver by Vimarsh Zutshi + * Based on jt8ev1.c by Vimarsh Zutshi + * Based on smia-sensor.c by Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "smiapp-debug.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "smiapp.h" + +#define SMIAPP_ALIGN_DIM(dim, flags) \ + ((flags) & V4L2_SUBDEV_SEL_FLAG_SIZE_GE \ + ? ALIGN((dim), 2) \ + : (dim) & ~1) + +/* + * smiapp_module_idents - supported camera modules + */ +static const struct smiapp_module_ident smiapp_module_idents[] = { + SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"), + SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"), + SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"), + SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), + SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), + SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), + SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"), + SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), + SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), +}; + +/* + * + * Dynamic Capability Identification + * + */ + +static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; + unsigned int i; + int rval; + int line_count = 0; + int embedded_start = -1, embedded_end = -1; + int image_start = 0; + + rval = smiapp_read(client, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, + &fmt_model_type); + if (rval) + return rval; + + rval = smiapp_read(client, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, + &fmt_model_subtype); + if (rval) + return rval; + + ncol_desc = (fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK) + >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT; + nrow_desc = fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK; + + dev_dbg(&client->dev, "format_model_type %s\n", + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE + ? "2 byte" : + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE + ? "4 byte" : "is simply bad"); + + for (i = 0; i < ncol_desc + nrow_desc; i++) { + u32 desc; + u32 pixelcode; + u32 pixels; + char *which; + char *what; + + if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { + rval = smiapp_read( + client, + SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK; + } else if (fmt_model_type + == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { + rval = smiapp_read( + client, + SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK; + } else { + dev_dbg(&client->dev, + "invalid frame format model type %d\n", + fmt_model_type); + return -EINVAL; + } + + if (i < ncol_desc) + which = "columns"; + else + which = "rows"; + + switch (pixelcode) { + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: + what = "embedded"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY: + what = "dummy"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK: + what = "black"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK: + what = "dark"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: + what = "visible"; + break; + default: + what = "invalid"; + dev_dbg(&client->dev, "pixelcode %d\n", pixelcode); + break; + } + + dev_dbg(&client->dev, "%s pixels: %d %s\n", + what, pixels, which); + + if (i < ncol_desc) + continue; + + /* Handle row descriptors */ + if (pixelcode + == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) { + embedded_start = line_count; + } else { + if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE + || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2) + image_start = line_count; + if (embedded_start != -1 && embedded_end == -1) + embedded_end = line_count; + } + line_count += pixels; + } + + if (embedded_start == -1 || embedded_end == -1) { + embedded_start = 0; + embedded_end = 0; + } + + dev_dbg(&client->dev, "embedded data from lines %d to %d\n", + embedded_start, embedded_end); + dev_dbg(&client->dev, "image data starts at line %d\n", image_start); + + return 0; +} + +static int smiapp_pll_configure(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_pll *pll = &sensor->pll; + int rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); + if (rval < 0) + return rval; + + /* Lane op clock ratio does not apply here. */ + rval = smiapp_write( + client, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, + DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); + if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) + return rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); + if (rval < 0) + return rval; + + return smiapp_write( + client, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); +} + +static int smiapp_pll_update(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_pll_limits lim = { + .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV], + .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV], + .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ], + .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ], + .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER], + .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER], + .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ], + .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ], + + .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV], + .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV], + .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV], + .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV], + .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ], + .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ], + .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ], + .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ], + + .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV], + .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV], + .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV], + .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV], + .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ], + .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ], + .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ], + .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ], + + .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], + .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], + }; + struct smiapp_pll *pll = &sensor->pll; + int rval; + + memset(&sensor->pll, 0, sizeof(sensor->pll)); + + pll->lanes = sensor->platform_data->lanes; + pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; + + if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) { + /* + * Fill in operational clock divisors limits from the + * video timing ones. On profile 0 sensors the + * requirements regarding them are essentially the + * same as on VT ones. + */ + lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div; + lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div; + lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div; + lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div; + lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz; + lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz; + lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz; + lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz; + /* Profile 0 sensors have no separate OP clock branch. */ + pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; + } + + if (smiapp_needs_quirk(sensor, + SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) + pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + + pll->binning_horizontal = sensor->binning_horizontal; + pll->binning_vertical = sensor->binning_vertical; + pll->link_freq = + sensor->link_freq->qmenu_int[sensor->link_freq->val]; + pll->scale_m = sensor->scale_m; + pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + pll->bits_per_pixel = sensor->csi_format->compressed; + + rval = smiapp_pll_calculate(&client->dev, &lim, pll); + if (rval < 0) + return rval; + + sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; + sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; + + return 0; +} + + +/* + * + * V4L2 Controls handling + * + */ + +static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *ctrl = sensor->exposure; + int max; + + max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val + - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; + + ctrl->maximum = max; + if (ctrl->default_value > max) + ctrl->default_value = max; + if (ctrl->val > max) + ctrl->val = max; + if (ctrl->cur.val > max) + ctrl->cur.val = max; +} + +/* + * Order matters. + * + * 1. Bits-per-pixel, descending. + * 2. Bits-per-pixel compressed, descending. + * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel + * orders must be defined. + */ +static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { + { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, +}; + +const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; + +#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ + - (unsigned long)smiapp_csi_data_formats) \ + / sizeof(*smiapp_csi_data_formats)) + +static u32 smiapp_pixel_order(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int flip = 0; + + if (sensor->hflip) { + if (sensor->hflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + } + + flip ^= sensor->hvflip_inv_mask; + + dev_dbg(&client->dev, "flip %d\n", flip); + return sensor->default_pixel_order ^ flip; +} + +static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int csi_format_idx = + to_csi_format_idx(sensor->csi_format) & ~3; + unsigned int internal_csi_format_idx = + to_csi_format_idx(sensor->internal_csi_format) & ~3; + unsigned int pixel_order = smiapp_pixel_order(sensor); + + sensor->mbus_frame_fmts = + sensor->default_mbus_frame_fmts << pixel_order; + sensor->csi_format = + &smiapp_csi_data_formats[csi_format_idx + pixel_order]; + sensor->internal_csi_format = + &smiapp_csi_data_formats[internal_csi_format_idx + + pixel_order]; + + BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order + >= ARRAY_SIZE(smiapp_csi_data_formats)); + BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); + + dev_dbg(&client->dev, "new pixel order %s\n", + pixel_order_str[pixel_order]); +} + +static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct smiapp_sensor *sensor = + container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) + ->sensor; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 orient = 0; + int exposure; + int rval; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + return smiapp_write( + client, + SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); + + case V4L2_CID_EXPOSURE: + return smiapp_write( + client, + SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + if (sensor->streaming) + return -EBUSY; + + if (sensor->hflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + + orient ^= sensor->hvflip_inv_mask; + rval = smiapp_write(client, + SMIAPP_REG_U8_IMAGE_ORIENTATION, + orient); + if (rval < 0) + return rval; + + smiapp_update_mbus_formats(sensor); + + return 0; + + case V4L2_CID_VBLANK: + exposure = sensor->exposure->val; + + __smiapp_update_exposure_limits(sensor); + + if (exposure > sensor->exposure->maximum) { + sensor->exposure->val = + sensor->exposure->maximum; + rval = smiapp_set_ctrl( + sensor->exposure); + if (rval < 0) + return rval; + } + + return smiapp_write( + client, SMIAPP_REG_U16_FRAME_LENGTH_LINES, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + ctrl->val); + + case V4L2_CID_HBLANK: + return smiapp_write( + client, SMIAPP_REG_U16_LINE_LENGTH_PCK, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + ctrl->val); + + case V4L2_CID_LINK_FREQ: + if (sensor->streaming) + return -EBUSY; + + return smiapp_pll_update(sensor); + + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { + .s_ctrl = smiapp_set_ctrl, +}; + +static int smiapp_init_controls(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct v4l2_ctrl_config cfg; + int rval; + + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); + if (rval) + return rval; + sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; + + sensor->analog_gain = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN], + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX], + max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U), + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]); + + /* Exposure limits will be updated soon, use just something here. */ + sensor->exposure = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0, 1, 0); + + sensor->hflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + sensor->vblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VBLANK, 0, 1, 1, 0); + + if (sensor->vblank) + sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->hblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HBLANK, 0, 1, 1, 0); + + if (sensor->hblank) + sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->pixel_rate_parray = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->pixel_array->ctrl_handler.error) { + dev_err(&client->dev, + "pixel array controls initialization failed (%d)\n", + sensor->pixel_array->ctrl_handler.error); + rval = sensor->pixel_array->ctrl_handler.error; + goto error; + } + + sensor->pixel_array->sd.ctrl_handler = + &sensor->pixel_array->ctrl_handler; + + v4l2_ctrl_cluster(2, &sensor->hflip); + + rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); + if (rval) + goto error; + sensor->src->ctrl_handler.lock = &sensor->mutex; + + memset(&cfg, 0, sizeof(cfg)); + + cfg.ops = &smiapp_ctrl_ops; + cfg.id = V4L2_CID_LINK_FREQ; + cfg.type = V4L2_CTRL_TYPE_INTEGER_MENU; + while (sensor->platform_data->op_sys_clock[cfg.max + 1]) + cfg.max++; + cfg.qmenu_int = sensor->platform_data->op_sys_clock; + + sensor->link_freq = v4l2_ctrl_new_custom( + &sensor->src->ctrl_handler, &cfg, NULL); + + sensor->pixel_rate_csi = v4l2_ctrl_new_std( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->src->ctrl_handler.error) { + dev_err(&client->dev, + "src controls initialization failed (%d)\n", + sensor->src->ctrl_handler.error); + rval = sensor->src->ctrl_handler.error; + goto error; + } + + sensor->src->sd.ctrl_handler = + &sensor->src->ctrl_handler; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); + v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); + + return rval; +} + +static void smiapp_free_controls(struct smiapp_sensor *sensor) +{ + unsigned int i; + + for (i = 0; i < sensor->ssds_used; i++) + v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); +} + +static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, + unsigned int n) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + u32 val; + int rval; + + for (i = 0; i < n; i++) { + rval = smiapp_read( + client, smiapp_reg_limits[limit[i]].addr, &val); + if (rval) + return rval; + sensor->limits[limit[i]] = val; + dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit[i]].addr, + smiapp_reg_limits[limit[i]].what, val, val); + } + + return 0; +} + +static int smiapp_get_all_limits(struct smiapp_sensor *sensor) +{ + unsigned int i; + int rval; + + for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { + rval = smiapp_get_limits(sensor, &i, 1); + if (rval < 0) + return rval; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0) + smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); + + return 0; +} + +static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) +{ + static u32 const limits[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, + }; + static u32 const limits_replace[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, + }; + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == + SMIAPP_BINNING_CAPABILITY_NO) { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(limits); i++) + sensor->limits[limits[i]] = + sensor->limits[limits_replace[i]]; + + return 0; + } + + return smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); +} + +static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int type, n; + unsigned int i, pixel_order; + int rval; + + rval = smiapp_read( + client, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); + if (rval) + return rval; + + dev_dbg(&client->dev, "data_format_model_type %d\n", type); + + rval = smiapp_read(client, SMIAPP_REG_U8_PIXEL_ORDER, + &pixel_order); + if (rval) + return rval; + + if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { + dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); + return -EINVAL; + } + + dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, + pixel_order_str[pixel_order]); + + switch (type) { + case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; + break; + case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N; + break; + default: + return -EINVAL; + } + + sensor->default_pixel_order = pixel_order; + sensor->mbus_frame_fmts = 0; + + for (i = 0; i < n; i++) { + unsigned int fmt, j; + + rval = smiapp_read( + client, + SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); + if (rval) + return rval; + + dev_dbg(&client->dev, "bpp %d, compressed %d\n", + fmt >> 8, (u8)fmt); + + for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { + const struct smiapp_csi_data_format *f = + &smiapp_csi_data_formats[j]; + + if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG) + continue; + + if (f->width != fmt >> 8 || f->compressed != (u8)fmt) + continue; + + dev_dbg(&client->dev, "jolly good! %d\n", j); + + sensor->default_mbus_frame_fmts |= 1 << j; + if (!sensor->csi_format) { + sensor->csi_format = f; + sensor->internal_csi_format = f; + } + } + } + + if (!sensor->csi_format) { + dev_err(&client->dev, "no supported mbus code found\n"); + return -EINVAL; + } + + smiapp_update_mbus_formats(sensor); + + return 0; +} + +static void smiapp_update_blanking(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *vblank = sensor->vblank; + struct v4l2_ctrl *hblank = sensor->hblank; + + vblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); + vblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; + + vblank->val = clamp_t(int, vblank->val, + vblank->minimum, vblank->maximum); + vblank->default_value = vblank->minimum; + vblank->val = vblank->val; + vblank->cur.val = vblank->val; + + hblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); + hblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; + + hblank->val = clamp_t(int, hblank->val, + hblank->minimum, hblank->maximum); + hblank->default_value = hblank->minimum; + hblank->val = hblank->val; + hblank->cur.val = hblank->val; + + __smiapp_update_exposure_limits(sensor); +} + +static int smiapp_update_mode(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int binning_mode; + int rval; + + dev_dbg(&client->dev, "frame size: %dx%d\n", + sensor->src->crop[SMIAPP_PAD_SRC].width, + sensor->src->crop[SMIAPP_PAD_SRC].height); + dev_dbg(&client->dev, "csi format width: %d\n", + sensor->csi_format->width); + + /* Binning has to be set up here; it affects limits */ + if (sensor->binning_horizontal == 1 && + sensor->binning_vertical == 1) { + binning_mode = 0; + } else { + u8 binning_type = + (sensor->binning_horizontal << 4) + | sensor->binning_vertical; + + rval = smiapp_write( + client, SMIAPP_REG_U8_BINNING_TYPE, binning_type); + if (rval < 0) + return rval; + + binning_mode = 1; + } + rval = smiapp_write(client, SMIAPP_REG_U8_BINNING_MODE, binning_mode); + if (rval < 0) + return rval; + + /* Get updated limits due to binning */ + rval = smiapp_get_limits_binning(sensor); + if (rval < 0) + return rval; + + rval = smiapp_pll_update(sensor); + if (rval < 0) + return rval; + + /* Output from pixel array, including blanking */ + smiapp_update_blanking(sensor); + + dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); + dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); + + dev_dbg(&client->dev, "real timeperframe\t100/%d\n", + sensor->pll.vt_pix_clk_freq_hz / + ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + sensor->hblank->val) * + (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val) / 100)); + + return 0; +} + +/* + * + * SMIA++ NVM handling + * + */ +static int smiapp_read_nvm(struct smiapp_sensor *sensor, + unsigned char *nvm) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 i, s, p, np, v; + int rval, rval2; + + np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; + for (p = 0; p < np; p++) { + rval = smiapp_write( + client, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); + if (rval) + goto out; + + rval = smiapp_write(client, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, + SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | + SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); + if (rval) + goto out; + + for (i = 0; i < 1000; i++) { + rval = smiapp_read( + client, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); + + if (rval) + goto out; + + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) + break; + + if (--i == 0) { + rval = -ETIMEDOUT; + goto out; + } + + } + + for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { + rval = smiapp_read( + client, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, + &v); + if (rval) + goto out; + + *nvm++ = v; + } + } + +out: + rval2 = smiapp_write(client, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); + if (rval < 0) + return rval; + else + return rval2; +} + +/* + * + * SMIA++ CCI address control + * + */ +static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + u32 val; + + client->addr = sensor->platform_data->i2c_addr_dfl; + + rval = smiapp_write(client, + SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, + sensor->platform_data->i2c_addr_alt << 1); + if (rval) + return rval; + + client->addr = sensor->platform_data->i2c_addr_alt; + + /* verify addr change went ok */ + rval = smiapp_read(client, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); + if (rval) + return rval; + + if (val != sensor->platform_data->i2c_addr_alt << 1) + return -ENODEV; + + return 0; +} + +/* + * + * SMIA++ Mode Control + * + */ +static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_flash_strobe_parms *strobe_setup; + unsigned int ext_freq = sensor->platform_data->ext_clk; + u32 tmp; + u32 strobe_adjustment; + u32 strobe_width_high_rs; + int rval; + + strobe_setup = sensor->platform_data->strobe_setup; + + /* + * How to calculate registers related to strobe length. Please + * do not change, or if you do at least know what you're + * doing. :-) + * + * Sakari Ailus 2010-10-25 + * + * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl + * / EXTCLK freq [Hz]) * flash_strobe_adjustment + * + * tFlash_strobe_width_ctrl E N, [1 - 0xffff] + * flash_strobe_adjustment E N, [1 - 0xff] + * + * The formula above is written as below to keep it on one + * line: + * + * l / 10^6 = w / e * a + * + * Let's mark w * a by x: + * + * x = w * a + * + * Thus, we get: + * + * x = l * e / 10^6 + * + * The strobe width must be at least as long as requested, + * thus rounding upwards is needed. + * + * x = (l * e + 10^6 - 1) / 10^6 + * ----------------------------- + * + * Maximum possible accuracy is wanted at all times. Thus keep + * a as small as possible. + * + * Calculate a, assuming maximum w, with rounding upwards: + * + * a = (x + (2^16 - 1) - 1) / (2^16 - 1) + * ------------------------------------- + * + * Thus, we also get w, with that a, with rounding upwards: + * + * w = (x + a - 1) / a + * ------------------- + * + * To get limits: + * + * x E [1, (2^16 - 1) * (2^8 - 1)] + * + * Substituting maximum x to the original formula (with rounding), + * the maximum l is thus + * + * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 + * + * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e + * -------------------------------------------------- + * + * flash_strobe_length must be clamped between 1 and + * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. + * + * Then, + * + * flash_strobe_adjustment = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) + * + * tFlash_strobe_width_ctrl = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + + * flash_strobe_adjustment - 1) / flash_strobe_adjustment + */ + tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - + 1000000 + 1, ext_freq); + strobe_setup->strobe_width_high_us = + clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); + + tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + + 1000000 - 1), 1000000ULL); + strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); + strobe_width_high_rs = (tmp + strobe_adjustment - 1) / + strobe_adjustment; + + rval = smiapp_write(client, SMIAPP_REG_U8_FLASH_MODE_RS, + strobe_setup->mode); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, + strobe_adjustment); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, + strobe_width_high_rs); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, + strobe_setup->strobe_delay); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, + strobe_setup->stobe_start_point); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U8_FLASH_TRIGGER_RS, + strobe_setup->trigger); + +out: + sensor->platform_data->strobe_setup->trigger = 0; + + return rval; +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int smiapp_power_on(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int sleep; + int rval; + + rval = regulator_enable(sensor->vana); + if (rval) { + dev_err(&client->dev, "failed to enable vana regulator\n"); + return rval; + } + usleep_range(1000, 1000); + + rval = sensor->platform_data->set_xclk(&sensor->src->sd, + sensor->platform_data->ext_clk); + if (rval < 0) { + dev_dbg(&client->dev, "failed to set xclk\n"); + goto out_xclk_fail; + } + usleep_range(1000, 1000); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 1); + + sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); + usleep_range(sleep, sleep); + + /* + * Failures to respond to the address change command have been noticed. + * Those failures seem to be caused by the sensor requiring a longer + * boot time than advertised. An additional 10ms delay seems to work + * around the issue, but the SMIA++ I2C write retry hack makes the delay + * unnecessary. The failures need to be investigated to find a proper + * fix, and a delay will likely need to be added here if the I2C write + * retry hack is reverted before the root cause of the boot time issue + * is found. + */ + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(client, SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + if (rval < 0) { + dev_err(&client->dev, "software reset failed\n"); + goto out_cci_addr_fail; + } + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(client, SMIAPP_REG_U16_COMPRESSION_MODE, + SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); + if (rval) { + dev_err(&client->dev, "compression mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write( + client, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, + sensor->platform_data->ext_clk / (1000000 / (1 << 8))); + if (rval) { + dev_err(&client->dev, "extclk frequency set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(client, SMIAPP_REG_U8_CSI_LANE_MODE, + sensor->platform_data->lanes - 1); + if (rval) { + dev_err(&client->dev, "csi lane mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(client, SMIAPP_REG_U8_FAST_STANDBY_CTRL, + SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); + if (rval) { + dev_err(&client->dev, "fast standby set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(client, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, + sensor->platform_data->csi_signalling_mode); + if (rval) { + dev_err(&client->dev, "csi signalling mode set failed\n"); + goto out_cci_addr_fail; + } + + /* DPHY control done by sensor based on requested link rate */ + rval = smiapp_write(client, SMIAPP_REG_U8_DPHY_CTRL, + SMIAPP_DPHY_CTRL_UI); + if (rval < 0) + return rval; + + rval = smiapp_call_quirk(sensor, post_poweron); + if (rval) { + dev_err(&client->dev, "post_poweron quirks failed\n"); + goto out_cci_addr_fail; + } + + /* Are we still initialising...? If yes, return here. */ + if (!sensor->pixel_array) + return 0; + + rval = v4l2_ctrl_handler_setup( + &sensor->pixel_array->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + mutex_lock(&sensor->mutex); + rval = smiapp_update_mode(sensor); + mutex_unlock(&sensor->mutex); + if (rval < 0) + goto out_cci_addr_fail; + + return 0; + +out_cci_addr_fail: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + +out_xclk_fail: + regulator_disable(sensor->vana); + return rval; +} + +static void smiapp_power_off(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + /* + * Currently power/clock to lens are enable/disabled separately + * but they are essentially the same signals. So if the sensor is + * powered off while the lens is powered on the sensor does not + * really see a power off and next time the cci address change + * will fail. So do a soft reset explicitly here. + */ + if (sensor->platform_data->i2c_addr_alt) + smiapp_write(client, + SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + usleep_range(5000, 5000); + regulator_disable(sensor->vana); + sensor->streaming = 0; +} + +static int smiapp_set_power(struct v4l2_subdev *subdev, int on) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret = 0; + + mutex_lock(&sensor->power_mutex); + + /* + * If the power count is modified from 0 to != 0 or from != 0 + * to 0, update the power state. + */ + if (!sensor->power_count == !on) + goto out; + + if (on) { + /* Power on and perform initialisation. */ + ret = smiapp_power_on(sensor); + if (ret < 0) + goto out; + } else { + smiapp_power_off(sensor); + } + + /* Update the power count. */ + sensor->power_count += on ? 1 : -1; + WARN_ON(sensor->power_count < 0); + +out: + mutex_unlock(&sensor->power_mutex); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Video stream management + */ + +static int smiapp_start_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + + rval = smiapp_write(client, SMIAPP_REG_U16_CSI_DATA_FORMAT, + (sensor->csi_format->width << 8) | + sensor->csi_format->compressed); + if (rval) + goto out; + + rval = smiapp_pll_configure(sensor); + if (rval) + goto out; + + /* Analog crop start coordinates */ + rval = smiapp_write(client, SMIAPP_REG_U16_X_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U16_Y_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); + if (rval < 0) + goto out; + + /* Analog crop end coordinates */ + rval = smiapp_write( + client, SMIAPP_REG_U16_X_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_Y_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); + if (rval < 0) + goto out; + + /* + * Output from pixel array, including blanking, is set using + * controls below. No need to set here. + */ + + /* Digital crop */ + if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + rval = smiapp_write( + client, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].left); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].top); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, + sensor->scaler->crop[SMIAPP_PAD_SINK].width); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, + sensor->scaler->crop[SMIAPP_PAD_SINK].height); + if (rval < 0) + goto out; + } + + /* Scaling */ + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + rval = smiapp_write(client, SMIAPP_REG_U16_SCALING_MODE, + sensor->scaling_mode); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U16_SCALE_M, + sensor->scale_m); + if (rval < 0) + goto out; + } + + /* Output size from sensor */ + rval = smiapp_write(client, SMIAPP_REG_U16_X_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].width); + if (rval < 0) + goto out; + rval = smiapp_write(client, SMIAPP_REG_U16_Y_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].height); + if (rval < 0) + goto out; + + if ((sensor->flash_capability & + (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | + SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && + sensor->platform_data->strobe_setup != NULL && + sensor->platform_data->strobe_setup->trigger != 0) { + rval = smiapp_setup_flash_strobe(sensor); + if (rval) + goto out; + } + + rval = smiapp_call_quirk(sensor, pre_streamon); + if (rval) { + dev_err(&client->dev, "pre_streamon quirks failed\n"); + goto out; + } + + rval = smiapp_write(client, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_STREAMING); + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int smiapp_stop_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + rval = smiapp_write(client, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); + if (rval) + goto out; + + rval = smiapp_call_quirk(sensor, post_streamoff); + if (rval) + dev_err(&client->dev, "post_streamoff quirks failed\n"); + +out: + mutex_unlock(&sensor->mutex); + return rval; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->streaming == enable) + return 0; + + if (enable) { + sensor->streaming = 1; + rval = smiapp_start_streaming(sensor); + if (rval < 0) + sensor->streaming = 0; + } else { + rval = smiapp_stop_streaming(sensor); + sensor->streaming = 0; + } + + return rval; +} + +static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + int idx = -1; + int rval = -EINVAL; + + mutex_lock(&sensor->mutex); + + dev_err(&client->dev, "subdev %s, pad %d, index %d\n", + subdev->name, code->pad, code->index); + + if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) { + if (code->index) + goto out; + + code->code = sensor->internal_csi_format->code; + rval = 0; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i)) + idx++; + + if (idx == code->index) { + code->code = smiapp_csi_data_formats[i].code; + dev_err(&client->dev, "found index %d, i %d, code %x\n", + code->index, i, code->code); + rval = 0; + break; + } + } + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, + unsigned int pad) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC) + return sensor->csi_format->code; + else + return sensor->internal_csi_format->code; +} + +static int __smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + } else { + struct v4l2_rect *r; + + if (fmt->pad == ssd->source_pad) + r = &ssd->crop[ssd->source_pad]; + else + r = &ssd->sink_fmt; + + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width = r->width; + fmt->format.height = r->height; + } + + return 0; +} + +static int smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_format(subdev, fh, fmt); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_rect **crops, + struct v4l2_rect **comps, int which) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + unsigned int i; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (crops) + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = &ssd->crop[i]; + if (comps) + *comps = &ssd->compose; + } else { + if (crops) { + for (i = 0; i < subdev->entity.num_pads; i++) { + crops[i] = v4l2_subdev_get_try_crop(fh, i); + BUG_ON(!crops[i]); + } + } + if (comps) { + *comps = v4l2_subdev_get_try_compose(fh, + SMIAPP_PAD_SINK); + BUG_ON(!*comps); + } + } +} + +/* Changes require propagation only on sink pad. */ +static void smiapp_propagate(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, int which, + int target) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, which); + + switch (target) { + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + comp->width = crops[SMIAPP_PAD_SINK]->width; + comp->height = crops[SMIAPP_PAD_SINK]->height; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (ssd == sensor->scaler) { + sensor->scale_m = + sensor->limits[ + SMIAPP_LIMIT_SCALER_N_MIN]; + sensor->scaling_mode = + SMIAPP_SCALING_MODE_NONE; + } else if (ssd == sensor->binner) { + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + } + } + /* Fall through */ + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + *crops[SMIAPP_PAD_SRC] = *comp; + break; + default: + BUG(); + } +} + +static const struct smiapp_csi_data_format +*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code) +{ + const struct smiapp_csi_data_format *csi_format = sensor->csi_format; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i) + && smiapp_csi_data_formats[i].code == code) + return &smiapp_csi_data_formats[i]; + } + + return csi_format; +} + +static int smiapp_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *crops[SMIAPP_PADS]; + + mutex_lock(&sensor->mutex); + + /* + * Media bus code is changeable on src subdev's source pad. On + * other source pads we just get format here. + */ + if (fmt->pad == ssd->source_pad) { + u32 code = fmt->format.code; + int rval = __smiapp_get_format(subdev, fh, fmt); + + if (!rval && subdev == &sensor->src->sd) { + const struct smiapp_csi_data_format *csi_format = + smiapp_validate_csi_data_format(sensor, code); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + sensor->csi_format = csi_format; + fmt->format.code = csi_format->code; + } + + mutex_unlock(&sensor->mutex); + return rval; + } + + /* Sink pad. Width and height are changeable here. */ + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width &= ~1; + fmt->format.height &= ~1; + + fmt->format.width = + clamp(fmt->format.width, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]); + fmt->format.height = + clamp(fmt->format.height, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); + + smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which); + + crops[ssd->sink_pad]->left = 0; + crops[ssd->sink_pad]->top = 0; + crops[ssd->sink_pad]->width = fmt->format.width; + crops[ssd->sink_pad]->height = fmt->format.height; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ssd->sink_fmt = *crops[ssd->sink_pad]; + smiapp_propagate(subdev, fh, fmt->which, + V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +/* + * Calculate goodness of scaled image size compared to expected image + * size and flags provided. + */ +#define SCALING_GOODNESS 100000 +#define SCALING_GOODNESS_EXTREME 100000000 +static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, + int h, int ask_h, u32 flags) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int val = 0; + + w &= ~1; + ask_w &= ~1; + h &= ~1; + ask_h &= ~1; + + if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_GE) { + if (w < ask_w) + val -= SCALING_GOODNESS; + if (h < ask_h) + val -= SCALING_GOODNESS; + } + + if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_LE) { + if (w > ask_w) + val -= SCALING_GOODNESS; + if (h > ask_h) + val -= SCALING_GOODNESS; + } + + val -= abs(w - ask_w); + val -= abs(h - ask_h); + + if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]) + val -= SCALING_GOODNESS_EXTREME; + + dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", + w, ask_h, h, ask_h, val); + + return val; +} + +static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + unsigned int binh = 1, binv = 1; + unsigned int best = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width, sel->r.width, + crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / sensor->binning_subtypes[i].horizontal, + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / sensor->binning_subtypes[i].vertical, + sel->r.height, sel->flags); + + if (this > best) { + binh = sensor->binning_subtypes[i].horizontal; + binv = sensor->binning_subtypes[i].vertical; + best = this; + } + } + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->binning_vertical = binv; + sensor->binning_horizontal = binh; + } + + sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1; + sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1; +} + +/* + * Calculate best scaling ratio and mode for given output resolution. + * + * Try all of these: horizontal ratio, vertical ratio and smallest + * size possible (horizontally). + * + * Also try whether horizontal scaler or full scaler gives a better + * result. + */ +static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + u32 min, max, a, b, max_m; + u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + int mode = SMIAPP_SCALING_MODE_HORIZONTAL; + u32 try[4]; + u32 ntry = 0; + unsigned int i; + int best = INT_MIN; + + sel->r.width = min_t(unsigned int, sel->r.width, + crops[SMIAPP_PAD_SINK]->width); + sel->r.height = min_t(unsigned int, sel->r.height, + crops[SMIAPP_PAD_SINK]->height); + + a = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width; + b = crops[SMIAPP_PAD_SINK]->height + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height; + max_m = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] + / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; + + a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + + dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); + + min = min(max_m, min(a, b)); + max = min(max_m, max(a, b)); + + try[ntry] = min; + ntry++; + if (min != max) { + try[ntry] = max; + ntry++; + } + if (max != max_m) { + try[ntry] = min + 1; + ntry++; + if (min != max) { + try[ntry] = max + 1; + ntry++; + } + } + + for (i = 0; i < ntry; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height, + sel->r.height, + sel->flags); + + dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_HORIZONTAL; + best = this; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + continue; + + this = scaling_goodness( + subdev, crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.height, + sel->flags); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_BOTH; + best = this; + } + } + + sel->r.width = + (crops[SMIAPP_PAD_SINK]->width + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1; + if (mode == SMIAPP_SCALING_MODE_BOTH) + sel->r.height = + (crops[SMIAPP_PAD_SINK]->height + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) + & ~1; + else + sel->r.height = crops[SMIAPP_PAD_SINK]->height; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->scale_m = scale_m; + sensor->scaling_mode = mode; + } +} +/* We're only called on source pads. This function sets scaling. */ +static int smiapp_set_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + sel->r.top = 0; + sel->r.left = 0; + + if (ssd == sensor->binner) + smiapp_set_compose_binner(subdev, fh, sel, crops, comp); + else + smiapp_set_compose_scaler(subdev, fh, sel, crops, comp); + + *comp = sel->r; + smiapp_propagate(subdev, fh, sel->which, + V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return smiapp_update_mode(sensor); + + return 0; +} + +static int __smiapp_sel_supported(struct v4l2_subdev *subdev, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + /* We only implement crop in three places. */ + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array + && sel->pad == SMIAPP_PA_PAD_SRC) + return 0; + if (ssd == sensor->src + && sel->pad == SMIAPP_PAD_SRC) + return 0; + if (ssd == sensor->scaler + && sel->pad == SMIAPP_PAD_SINK + && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) + return 0; + return -EINVAL; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + if (sel->pad == ssd->source_pad) + return -EINVAL; + if (ssd == sensor->binner) + return 0; + if (ssd == sensor->scaler + && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) + return 0; + /* Fall through */ + default: + return -EINVAL; + } +} + +static int smiapp_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; + struct v4l2_rect _r; + + smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->pad == ssd->sink_pad) + src_size = &ssd->sink_fmt; + else + src_size = &ssd->compose; + } else { + if (sel->pad == ssd->sink_pad) { + _r.left = 0; + _r.top = 0; + _r.width = v4l2_subdev_get_try_format(fh, sel->pad) + ->width; + _r.height = v4l2_subdev_get_try_format(fh, sel->pad) + ->height; + src_size = &_r; + } else { + src_size = + v4l2_subdev_get_try_compose( + fh, ssd->sink_pad); + } + } + + if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) { + sel->r.left = 0; + sel->r.top = 0; + } + + sel->r.width = min(sel->r.width, src_size->width); + sel->r.height = min(sel->r.height, src_size->height); + + sel->r.left = min(sel->r.left, src_size->width - sel->r.width); + sel->r.top = min(sel->r.top, src_size->height - sel->r.height); + + *crops[sel->pad] = sel->r; + + if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) + smiapp_propagate(subdev, fh, sel->which, + V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + + return 0; +} + +static int __smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + struct v4l2_rect sink_fmt; + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sink_fmt = ssd->sink_fmt; + } else { + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(fh, ssd->sink_pad); + + sink_fmt.left = 0; + sink_fmt.top = 0; + sink_fmt.width = fmt->width; + sink_fmt.height = fmt->height; + } + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array) { + sel->r.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + sel->r.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + } else if (sel->pad == ssd->sink_pad) { + sel->r = sink_fmt; + } else { + sel->r = *comp; + } + break; + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + sel->r = *crops[sel->pad]; + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + sel->r = *comp; + break; + } + + return 0; +} + +static int smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_selection(subdev, fh, sel); + mutex_unlock(&sensor->mutex); + + return rval; +} +static int smiapp_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + mutex_lock(&sensor->mutex); + + sel->r.left = max(0, sel->r.left & ~1); + sel->r.top = max(0, sel->r.top & ~1); + sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); + sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); + + sel->r.width = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sel->r.width); + sel->r.height = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sel->r.height); + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + ret = smiapp_set_crop(subdev, fh, sel); + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + ret = smiapp_set_compose(subdev, fh, sel); + break; + default: + BUG(); + } + + mutex_unlock(&sensor->mutex); + return ret; +} + +static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + *frames = sensor->frame_skip; + return 0; +} + +/* ----------------------------------------------------------------------------- + * sysfs attributes + */ + +static ssize_t +smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int nbytes; + + if (!sensor->dev_init_done) + return -EBUSY; + + if (!sensor->nvm_size) { + /* NVM not read yet - read it now */ + sensor->nvm_size = sensor->platform_data->nvm_size; + if (smiapp_set_power(subdev, 1) < 0) + return -ENODEV; + if (smiapp_read_nvm(sensor, sensor->nvm)) { + dev_err(&client->dev, "nvm read failed\n"); + return -ENODEV; + } + smiapp_set_power(subdev, 0); + } + /* + * NVM is still way below a PAGE_SIZE, so we can safely + * assume this for now. + */ + nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); + memcpy(buf, sensor->nvm, nbytes); + + return nbytes; +} +static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int smiapp_identify_module(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_module_info *minfo = &sensor->minfo; + unsigned int i; + int rval = 0; + + minfo->name = SMIAPP_NAME; + + /* Module info */ + rval = smiapp_read(client, SMIAPP_REG_U8_MANUFACTURER_ID, + &minfo->manufacturer_id); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U16_MODEL_ID, + &minfo->model_id); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, + &minfo->revision_number_major); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_REVISION_NUMBER_MINOR, + &minfo->revision_number_minor); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_MODULE_DATE_YEAR, + &minfo->module_year); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_MODULE_DATE_MONTH, + &minfo->module_month); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_MODULE_DATE_DAY, + &minfo->module_day); + + /* Sensor info */ + if (!rval) + rval = smiapp_read(client, + SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, + &minfo->sensor_manufacturer_id); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U16_SENSOR_MODEL_ID, + &minfo->sensor_model_id); + if (!rval) + rval = smiapp_read(client, + SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, + &minfo->sensor_revision_number); + if (!rval) + rval = smiapp_read(client, + SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, + &minfo->sensor_firmware_version); + + /* SMIA */ + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_SMIA_VERSION, + &minfo->smia_version); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_SMIAPP_VERSION, + &minfo->smiapp_version); + + if (rval) { + dev_err(&client->dev, "sensor detection failed\n"); + return -ENODEV; + } + + dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n", + minfo->manufacturer_id, minfo->model_id); + + dev_dbg(&client->dev, + "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n", + minfo->revision_number_major, minfo->revision_number_minor, + minfo->module_year, minfo->module_month, minfo->module_day); + + dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n", + minfo->sensor_manufacturer_id, minfo->sensor_model_id); + + dev_dbg(&client->dev, + "sensor revision 0x%2.2x firmware version 0x%2.2x\n", + minfo->sensor_revision_number, minfo->sensor_firmware_version); + + dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n", + minfo->smia_version, minfo->smiapp_version); + + /* + * Some modules have bad data in the lvalues below. Hope the + * rvalues have better stuff. The lvalues are module + * parameters whereas the rvalues are sensor parameters. + */ + if (!minfo->manufacturer_id && !minfo->model_id) { + minfo->manufacturer_id = minfo->sensor_manufacturer_id; + minfo->model_id = minfo->sensor_model_id; + minfo->revision_number_major = minfo->sensor_revision_number; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) { + if (smiapp_module_idents[i].manufacturer_id + != minfo->manufacturer_id) + continue; + if (smiapp_module_idents[i].model_id != minfo->model_id) + continue; + if (smiapp_module_idents[i].flags + & SMIAPP_MODULE_IDENT_FLAG_REV_LE) { + if (smiapp_module_idents[i].revision_number_major + < minfo->revision_number_major) + continue; + } else { + if (smiapp_module_idents[i].revision_number_major + != minfo->revision_number_major) + continue; + } + + minfo->name = smiapp_module_idents[i].name; + minfo->quirk = smiapp_module_idents[i].quirk; + break; + } + + if (i >= ARRAY_SIZE(smiapp_module_idents)) + dev_warn(&client->dev, + "no quirks for this module; let's hope it's fully compliant\n"); + + dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n", + minfo->name, minfo->manufacturer_id, minfo->model_id, + minfo->revision_number_major); + + strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); + + return 0; +} + +static const struct v4l2_subdev_ops smiapp_ops; +static const struct v4l2_subdev_internal_ops smiapp_internal_ops; +static const struct media_entity_operations smiapp_entity_ops; + +static int smiapp_registered(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_subdev *last = NULL; + u32 tmp; + unsigned int i; + int rval; + + sensor->vana = regulator_get(&client->dev, "VANA"); + if (IS_ERR(sensor->vana)) { + dev_err(&client->dev, "could not get regulator for vana\n"); + return -ENODEV; + } + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { + if (gpio_request_one(sensor->platform_data->xshutdown, 0, + "SMIA++ xshutdown") != 0) { + dev_err(&client->dev, + "unable to acquire reset gpio %d\n", + sensor->platform_data->xshutdown); + rval = -ENODEV; + goto out_gpio_request; + } + } + + rval = smiapp_power_on(sensor); + if (rval) { + rval = -ENODEV; + goto out_smiapp_power_on; + } + + rval = smiapp_identify_module(subdev); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + rval = smiapp_get_all_limits(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + /* + * Handle Sensor Module orientation on the board. + * + * The application of H-FLIP and V-FLIP on the sensor is modified by + * the sensor orientation on the board. + * + * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set + * both H-FLIP and V-FLIP for normal operation which also implies + * that a set/unset operation for user space HFLIP and VFLIP v4l2 + * controls will need to be internally inverted. + * + * Rotation also changes the bayer pattern. + */ + if (sensor->platform_data->module_board_orient == + SMIAPP_MODULE_BOARD_ORIENT_180) + sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | + SMIAPP_IMAGE_ORIENTATION_VFLIP; + + rval = smiapp_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { + u32 val; + + rval = smiapp_read(client, + SMIAPP_REG_U8_BINNING_SUBTYPES, &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->nbinning_subtypes = min_t(u8, val, + SMIAPP_BINNING_SUBTYPES); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + rval = smiapp_read( + client, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->binning_subtypes[i] = + *(struct smiapp_binning_subtype *)&val; + + dev_dbg(&client->dev, "binning %xx%x\n", + sensor->binning_subtypes[i].horizontal, + sensor->binning_subtypes[i].vertical); + } + } + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + + /* SMIA++ NVM initialization - it will be read from the sensor + * when it is first requested by userspace. + */ + if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) { + sensor->nvm = kzalloc(sensor->platform_data->nvm_size, + GFP_KERNEL); + if (sensor->nvm == NULL) { + dev_err(&client->dev, "nvm buf allocation failed\n"); + rval = -ENOMEM; + goto out_power_off; + } + + if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { + dev_err(&client->dev, "sysfs nvm entry failed\n"); + rval = -EBUSY; + goto out_power_off; + } + } + + rval = smiapp_call_quirk(sensor, limits); + if (rval) { + dev_err(&client->dev, "limits quirks failed\n"); + goto out_nvm_release; + } + + /* We consider this as profile 0 sensor if any of these are zero. */ + if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) { + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; + } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; + else + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } + sensor->binner = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + + sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + + for (i = 0; i < SMIAPP_SUBDEVS; i++) { + struct { + struct smiapp_subdev *ssd; + char *name; + } const __this[] = { + { sensor->scaler, "scaler", }, + { sensor->binner, "binner", }, + { sensor->pixel_array, "pixel array", }, + }, *_this = &__this[i]; + struct smiapp_subdev *this = _this->ssd; + + if (!this) + continue; + + if (this != sensor->src) + v4l2_subdev_init(&this->sd, &smiapp_ops); + + this->sensor = sensor; + + if (this == sensor->pixel_array) { + this->npads = 1; + } else { + this->npads = 2; + this->source_pad = 1; + } + + snprintf(this->sd.name, + sizeof(this->sd.name), "%s %s", + sensor->minfo.name, _this->name); + + this->sink_fmt.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + this->sink_fmt.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + this->compose.width = this->sink_fmt.width; + this->compose.height = this->sink_fmt.height; + this->crop[this->source_pad] = this->compose; + this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE; + if (this != sensor->pixel_array) { + this->crop[this->sink_pad] = this->compose; + this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK; + } + + this->sd.entity.ops = &smiapp_entity_ops; + + if (last == NULL) { + last = this; + continue; + } + + this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + this->sd.internal_ops = &smiapp_internal_ops; + this->sd.owner = NULL; + v4l2_set_subdevdata(&this->sd, client); + + rval = media_entity_init(&this->sd.entity, + this->npads, this->pads, 0); + if (rval) { + dev_err(&client->dev, + "media_entity_init failed\n"); + goto out_nvm_release; + } + + rval = media_entity_create_link(&this->sd.entity, + this->source_pad, + &last->sd.entity, + last->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval) { + dev_err(&client->dev, + "media_entity_create_link failed\n"); + goto out_nvm_release; + } + + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &this->sd); + if (rval) { + dev_err(&client->dev, + "v4l2_device_register_subdev failed\n"); + goto out_nvm_release; + } + + last = this; + } + + dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); + + sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + + /* final steps */ + smiapp_read_frame_fmt(sensor); + rval = smiapp_init_controls(sensor); + if (rval < 0) + goto out_nvm_release; + + rval = smiapp_update_mode(sensor); + if (rval) { + dev_err(&client->dev, "update mode failed\n"); + goto out_nvm_release; + } + + sensor->streaming = false; + sensor->dev_init_done = true; + + /* check flash capability */ + rval = smiapp_read(client, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); + sensor->flash_capability = tmp; + if (rval) + goto out_nvm_release; + + smiapp_power_off(sensor); + + return 0; + +out_nvm_release: + device_remove_file(&client->dev, &dev_attr_nvm); + +out_power_off: + kfree(sensor->nvm); + sensor->nvm = NULL; + smiapp_power_off(sensor); + +out_smiapp_power_on: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + +out_gpio_request: + regulator_put(sensor->vana); + sensor->vana = NULL; + return rval; +} + +static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(sd); + struct smiapp_sensor *sensor = ssd->sensor; + u32 mbus_code = + smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code; + unsigned int i; + + mutex_lock(&sensor->mutex); + + for (i = 0; i < ssd->npads; i++) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(fh, i); + struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i); + struct v4l2_rect *try_comp; + + try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + try_fmt->code = mbus_code; + + try_crop->top = 0; + try_crop->left = 0; + try_crop->width = try_fmt->width; + try_crop->height = try_fmt->height; + + if (ssd != sensor->pixel_array) + continue; + + try_comp = v4l2_subdev_get_try_compose(fh, i); + *try_comp = *try_crop; + } + + mutex_unlock(&sensor->mutex); + + return smiapp_set_power(sd, 1); +} + +static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return smiapp_set_power(sd, 0); +} + +static const struct v4l2_subdev_video_ops smiapp_video_ops = { + .s_stream = smiapp_set_stream, +}; + +static const struct v4l2_subdev_core_ops smiapp_core_ops = { + .s_power = smiapp_set_power, +}; + +static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { + .enum_mbus_code = smiapp_enum_mbus_code, + .get_fmt = smiapp_get_format, + .set_fmt = smiapp_set_format, + .get_selection = smiapp_get_selection, + .set_selection = smiapp_set_selection, +}; + +static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { + .g_skip_frames = smiapp_get_skip_frames, +}; + +static const struct v4l2_subdev_ops smiapp_ops = { + .core = &smiapp_core_ops, + .video = &smiapp_video_ops, + .pad = &smiapp_pad_ops, + .sensor = &smiapp_sensor_ops, +}; + +static const struct media_entity_operations smiapp_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = { + .registered = smiapp_registered, + .open = smiapp_open, + .close = smiapp_close, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { + .open = smiapp_open, + .close = smiapp_close, +}; + +/* ----------------------------------------------------------------------------- + * I2C Driver + */ + +#ifdef CONFIG_PM + +static int smiapp_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + bool streaming; + + BUG_ON(mutex_is_locked(&sensor->mutex)); + + if (sensor->power_count == 0) + return 0; + + if (sensor->streaming) + smiapp_stop_streaming(sensor); + + streaming = sensor->streaming; + + smiapp_power_off(sensor); + + /* save state for resume */ + sensor->streaming = streaming; + + return 0; +} + +static int smiapp_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->power_count == 0) + return 0; + + rval = smiapp_power_on(sensor); + if (rval) + return rval; + + if (sensor->streaming) + rval = smiapp_start_streaming(sensor); + + return rval; +} + +#else + +#define smiapp_suspend NULL +#define smiapp_resume NULL + +#endif /* CONFIG_PM */ + +static int smiapp_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct smiapp_sensor *sensor; + int rval; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + sensor->platform_data = client->dev.platform_data; + mutex_init(&sensor->mutex); + mutex_init(&sensor->power_mutex); + sensor->src = &sensor->ssds[sensor->ssds_used]; + + v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); + sensor->src->sd.internal_ops = &smiapp_internal_src_ops; + sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->src->sensor = sensor; + + sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; + rval = media_entity_init(&sensor->src->sd.entity, 2, + sensor->src->pads, 0); + if (rval < 0) + kfree(sensor); + + return rval; +} + +static int __exit smiapp_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + + if (sensor->power_count) { + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + sensor->power_count = 0; + } + + if (sensor->nvm) { + device_remove_file(&client->dev, &dev_attr_nvm); + kfree(sensor->nvm); + } + + for (i = 0; i < sensor->ssds_used; i++) { + media_entity_cleanup(&sensor->ssds[i].sd.entity); + v4l2_device_unregister_subdev(&sensor->ssds[i].sd); + } + smiapp_free_controls(sensor); + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + if (sensor->vana) + regulator_put(sensor->vana); + + kfree(sensor); + + return 0; +} + +static const struct i2c_device_id smiapp_id_table[] = { + { SMIAPP_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, smiapp_id_table); + +static const struct dev_pm_ops smiapp_pm_ops = { + .suspend = smiapp_suspend, + .resume = smiapp_resume, +}; + +static struct i2c_driver smiapp_i2c_driver = { + .driver = { + .name = SMIAPP_NAME, + .pm = &smiapp_pm_ops, + }, + .probe = smiapp_probe, + .remove = __exit_p(smiapp_remove), + .id_table = smiapp_id_table, +}; + +module_i2c_driver(smiapp_i2c_driver); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp/smiapp-debug.h b/drivers/media/video/smiapp/smiapp-debug.h new file mode 100644 index 000000000000..627809eed1d9 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-debug.h @@ -0,0 +1,32 @@ +/* + * drivers/media/video/smiapp/smiapp-debug.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SMIAPP_DEBUG_H +#define SMIAPP_DEBUG_H + +#ifdef CONFIG_VIDEO_SMIAPP_DEBUG +#define DEBUG +#endif + +#endif diff --git a/drivers/media/video/smiapp/smiapp-limits.c b/drivers/media/video/smiapp/smiapp-limits.c new file mode 100644 index 000000000000..0800e095724e --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-limits.c @@ -0,0 +1,132 @@ +/* + * drivers/media/video/smiapp/smiapp-limits.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "smiapp.h" + +struct smiapp_reg_limits smiapp_reg_limits[] = { + { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */ + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" }, + { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" }, + { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" }, + { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */ + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" }, + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */ + { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" }, + { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */ + { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" }, + { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" }, + { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" }, + { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */ + { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */ + { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" }, + { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */ + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" }, + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */ + { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" }, + { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" }, + { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */ + { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */ + { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" }, + { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" }, + { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" }, + { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" }, + { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */ + { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" }, + { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" }, + { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" }, + { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" }, + { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */ + { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" }, + { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" }, + { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" }, + { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" }, + { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */ + { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" }, + { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" }, + { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" }, + { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" }, + { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */ + { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" }, + { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" }, + { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" }, + { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" }, + { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */ + { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */ + { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" }, + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */ + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" }, + { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" }, + { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */ + { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" }, + { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" }, + { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" }, + { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" }, + { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */ + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" }, + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" }, + { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" }, + { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" }, + { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */ + { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" }, + { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" }, + { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" }, + { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" }, + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */ + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" }, + { 0, NULL }, +}; diff --git a/drivers/media/video/smiapp/smiapp-limits.h b/drivers/media/video/smiapp/smiapp-limits.h new file mode 100644 index 000000000000..7f4836bb78db --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-limits.h @@ -0,0 +1,128 @@ +/* + * drivers/media/video/smiapp/smiapp-limits.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2 +#define SMIAPP_LIMIT_THS_ZERO_MIN 3 +#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4 +#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9 +#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12 +#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13 +#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14 +#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15 +#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16 +#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17 +#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18 +#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19 +#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20 +#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21 +#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35 +#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45 +#define SMIAPP_LIMIT_X_ADDR_MIN 46 +#define SMIAPP_LIMIT_Y_ADDR_MIN 47 +#define SMIAPP_LIMIT_X_ADDR_MAX 48 +#define SMIAPP_LIMIT_Y_ADDR_MAX 49 +#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50 +#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51 +#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52 +#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53 +#define SMIAPP_LIMIT_MIN_EVEN_INC 54 +#define SMIAPP_LIMIT_MAX_EVEN_INC 55 +#define SMIAPP_LIMIT_MIN_ODD_INC 56 +#define SMIAPP_LIMIT_MAX_ODD_INC 57 +#define SMIAPP_LIMIT_SCALING_CAPABILITY 58 +#define SMIAPP_LIMIT_SCALER_M_MIN 59 +#define SMIAPP_LIMIT_SCALER_M_MAX 60 +#define SMIAPP_LIMIT_SCALER_N_MIN 61 +#define SMIAPP_LIMIT_SCALER_N_MAX 62 +#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63 +#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64 +#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65 +#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66 +#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67 +#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68 +#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69 +#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70 +#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75 +#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83 +#define SMIAPP_LIMIT_BINNING_CAPABILITY 84 +#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85 +#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86 +#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87 +#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88 +#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89 +#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92 +#define SMIAPP_LIMIT_EDOF_CAPABILITY 93 +#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94 +#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95 +#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96 +#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97 +#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98 +#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102 +#define SMIAPP_LIMIT_LAST 103 diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c new file mode 100644 index 000000000000..dae85a12f7ec --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-quirk.c @@ -0,0 +1,264 @@ +/* + * drivers/media/video/smiapp/smiapp-quirk.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "smiapp-debug.h" + +#include + +#include "smiapp.h" + +static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + return smiapp_write(client, (SMIA_REG_8BIT << 16) | reg, val); +} + +static int smiapp_write_8s(struct smiapp_sensor *sensor, + struct smiapp_reg_8 *regs, int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + for (; len > 0; len--, regs++) { + rval = smiapp_write_8(sensor, regs->reg, regs->val); + if (rval < 0) { + dev_err(&client->dev, + "error %d writing reg 0x%4.4x, val 0x%2.2x", + rval, regs->reg, regs->val); + return rval; + } + } + + return 0; +} + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit].addr, + smiapp_reg_limits[limit].what, val, val); + sensor->limits[limit] = val; +} + +int smiapp_replace_limit_at(struct smiapp_sensor *sensor, + u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int i; + + for (i = 0; smiapp_reg_limits[i].addr; i++) { + if ((smiapp_reg_limits[i].addr & 0xffff) != reg) + continue; + + smiapp_replace_limit(sensor, i, val); + + return 0; + } + + dev_dbg(&client->dev, "quirk: bad register 0x%4.4x\n", reg); + + return -EINVAL; +} + +static int jt8ew9_limits(struct smiapp_sensor *sensor) +{ + if (sensor->minfo.revision_number_major < 0x03) + sensor->frame_skip = 1; + + /* Below 24 gain doesn't have effect at all, */ + /* but ~59 is needed for full dynamic range */ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59); + smiapp_replace_limit( + sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000); + + return 0; +} + +static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) +{ + struct smiapp_reg_8 regs[] = { + { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ + { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */ + { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + /* Taken from v03. No idea what the rest are. */ + { 0x32e0, 0x05 }, + { 0x32e1, 0x05 }, + { 0x32e2, 0x04 }, + { 0x32e5, 0x04 }, + { 0x32e6, 0x04 }, + + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_jt8ew9_quirk = { + .limits = jt8ew9_limits, + .post_poweron = jt8ew9_post_poweron, +}; + +static int imx125es_post_poweron(struct smiapp_sensor *sensor) +{ + /* Taken from v02. No idea what the other two are. */ + struct smiapp_reg_8 regs[] = { + /* + * 0x3302: clk during frame blanking: + * 0x00 - HS mode, 0x01 - LP11 + */ + { 0x3302, 0x01 }, + { 0x302d, 0x00 }, + { 0x3b08, 0x8c }, + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_imx125es_quirk = { + .post_poweron = imx125es_post_poweron, +}; + +static int jt8ev1_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271); + smiapp_replace_limit(sensor, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184); + + return 0; +} + +static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + struct smiapp_reg_8 regs[] = { + { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ + { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ + { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ + { 0x3238, 0x43 }, + { 0x3301, 0x06 }, /* For analog bias for sensor */ + { 0x3302, 0x06 }, + { 0x3304, 0x00 }, + { 0x3305, 0x88 }, + { 0x332a, 0x14 }, + { 0x332c, 0x6b }, + { 0x3336, 0x01 }, + { 0x333f, 0x1f }, + { 0x3355, 0x00 }, + { 0x3356, 0x20 }, + { 0x33bf, 0x20 }, /* Adjust the FBC speed */ + { 0x33c9, 0x20 }, + { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */ + { 0x33cf, 0xec }, /* For Black sun */ + { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ + }; + + struct smiapp_reg_8 regs_96[] = { + { 0x30ae, 0x00 }, /* For control of ADC clock */ + { 0x30af, 0xd0 }, + { 0x30b0, 0x01 }, + }; + + rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); + if (rval < 0) + return rval; + + switch (sensor->platform_data->ext_clk) { + case 9600000: + return smiapp_write_8s(sensor, regs_96, + ARRAY_SIZE(regs_96)); + default: + dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", + sensor->platform_data->ext_clk); + return 0; + } +} + +static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor) +{ + return smiapp_write_8(sensor, 0x3328, 0x00); +} + +static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) +{ + int rval; + + /* Workaround: allows fast standby to work properly */ + rval = smiapp_write_8(sensor, 0x3205, 0x04); + if (rval < 0) + return rval; + + /* Wait for 1 ms + one line => 2 ms is likely enough */ + usleep_range(2000, 2000); + + /* Restore it */ + rval = smiapp_write_8(sensor, 0x3205, 0x00); + if (rval < 0) + return rval; + + return smiapp_write_8(sensor, 0x3328, 0x80); +} + +const struct smiapp_quirk smiapp_jt8ev1_quirk = { + .limits = jt8ev1_limits, + .post_poweron = jt8ev1_post_poweron, + .pre_streamon = jt8ev1_pre_streamon, + .post_streamoff = jt8ev1_post_streamoff, + .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, +}; + +static int tcm8500md_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000); + + return 0; +} + +const struct smiapp_quirk smiapp_tcm8500md_quirk = { + .limits = tcm8500md_limits, +}; diff --git a/drivers/media/video/smiapp/smiapp-quirk.h b/drivers/media/video/smiapp/smiapp-quirk.h new file mode 100644 index 000000000000..7a1b3a02a7bd --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-quirk.h @@ -0,0 +1,72 @@ +/* + * drivers/media/video/smiapp/smiapp-quirk.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_QUIRK__ +#define __SMIAPP_QUIRK__ + +struct smiapp_sensor; + +/** + * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard + * + * @limits: Replace sensor->limits with values which can't be read from + * sensor registers. Called the first time the sensor is powered up. + * @post_poweron: Called always after the sensor has been fully powered on. + * @pre_streamon: Called just before streaming is enabled. + * @post_streamon: Called right after stopping streaming. + */ +struct smiapp_quirk { + int (*limits)(struct smiapp_sensor *sensor); + int (*post_poweron)(struct smiapp_sensor *sensor); + int (*pre_streamon)(struct smiapp_sensor *sensor); + int (*post_streamoff)(struct smiapp_sensor *sensor); + unsigned long flags; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) + +struct smiapp_reg_8 { + u16 reg; + u8 val; +}; + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val); + +#define smiapp_call_quirk(_sensor, _quirk, ...) \ + (_sensor->minfo.quirk && \ + _sensor->minfo.quirk->_quirk ? \ + _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) + +#define smiapp_needs_quirk(_sensor, _quirk) \ + (_sensor->minfo.quirk ? \ + _sensor->minfo.quirk->flags & _quirk : 0) + +extern const struct smiapp_quirk smiapp_jt8ev1_quirk; +extern const struct smiapp_quirk smiapp_imx125es_quirk; +extern const struct smiapp_quirk smiapp_jt8ew9_quirk; +extern const struct smiapp_quirk smiapp_tcm8500md_quirk; + +#endif /* __SMIAPP_QUIRK__ */ diff --git a/drivers/media/video/smiapp/smiapp-reg-defs.h b/drivers/media/video/smiapp/smiapp-reg-defs.h new file mode 100644 index 000000000000..a089eb8161e1 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-reg-defs.h @@ -0,0 +1,503 @@ +/* + * drivers/media/video/smiapp/smiapp-reg-defs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) +#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) +#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) +#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) +#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003) +#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004) +#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005) +#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006) +#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008) +#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c) +#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010) +#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011) +#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012) +#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013) +#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014) +#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015) +#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016) +#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018) +#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019) +#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a) +#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041) +#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ +#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1) +#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1)) +#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100) +#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101) +#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103) +#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104) +#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105) +#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107) +#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108) +#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109) +#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111) +#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112) +#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114) +#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115) +#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116) +#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117) +#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118) +#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119) +#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a) +#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b) +#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c) +#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d) +#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e) +#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120) +#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130) +#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132) +#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134) +#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136) +#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138) +#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139) +#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e) +#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210) +#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214) +#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300) +#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302) +#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304) +#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306) +#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308) +#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a) +#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340) +#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342) +#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344) +#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346) +#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348) +#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a) +#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c) +#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e) +#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380) +#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382) +#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384) +#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386) +#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402) +#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404) +#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406) +#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408) +#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e) +#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500) +#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600) +#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602) +#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604) +#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606) +#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610) +#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700) +#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800) +#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801) +#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802) +#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803) +#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804) +#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805) +#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806) +#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807) +#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808) +#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820) +#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900) +#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901) +#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87) +#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00) +#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03) +#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16) +#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80) +#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83) +#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84) +#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85) +#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86) +#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a) +#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0) +#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2) +#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4) +#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6) +#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8) +#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03) +#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04) +#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e) +#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10) +#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12) +#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18) +#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a) +#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b) +#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c) +#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d) +#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e) +#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20) +#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22) +#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24) +#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e) +#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80) +#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82) +#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84) +#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86) +#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88) +#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a) +#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c) +#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e) +#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00) +#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01) +#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02) +#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03) +#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80) +#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89) +#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a) +#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00) +#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01) +#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02) +#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10) +#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff) +#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a) +#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086) +#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088) +#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100) +#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104) +#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108) +#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a) +#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c) +#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110) +#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114) +#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116) +#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118) +#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c) +#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120) +#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122) +#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124) +#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128) +#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c) +#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130) +#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134) +#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148) +#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a) +#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c) +#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160) +#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162) +#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164) +#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168) +#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c) +#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e) +#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170) +#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174) +#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180) +#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182) +#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184) +#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186) +#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188) +#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a) +#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c) +#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e) +#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0) +#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2) +#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4) +#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6) +#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200) +#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204) +#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206) +#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208) +#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c) +#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e) +#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410) +#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500) +#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502) +#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600) +#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602) +#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614) +#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c) +#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710) +#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711) +#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712) +#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */ +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800) +#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901) +#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906) +#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980) +#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981) +#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982) +#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983) +#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984) +#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985) +#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986) +#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987) +#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1) +#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2) +#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4) +#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00) +#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02) +#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02) +#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04) +#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40) +#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42) +#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01) +#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02) diff --git a/drivers/media/video/smiapp/smiapp-reg.h b/drivers/media/video/smiapp/smiapp-reg.h new file mode 100644 index 000000000000..d0167aa17534 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-reg.h @@ -0,0 +1,122 @@ +/* + * drivers/media/video/smiapp/smiapp-reg.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_REG_H_ +#define __SMIAPP_REG_H_ + +#include "smiapp-reg-defs.h" + +/* Bits for above register */ +#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) +#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) + +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) + +#define SMIAPP_SOFTWARE_RESET (1 << 0) + +#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) +#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) + +#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 +/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ +#define SMIAPP_DPHY_CTRL_UI 1 +#define SMIAPP_DPHY_CTRL_REGISTER 2 + +#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 +#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 + +#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 +#define SMIAPP_MODE_SELECT_STREAMING 1 + +#define SMIAPP_SCALING_MODE_NONE 0 +#define SMIAPP_SCALING_MODE_HORIZONTAL 1 +#define SMIAPP_SCALING_MODE_BOTH 2 + +#define SMIAPP_SCALING_CAPABILITY_NONE 0 +#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 +#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ + +/* digital crop right before scaler */ +#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 +#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 + +#define SMIAPP_BINNING_CAPABILITY_NO 0 +#define SMIAPP_BINNING_CAPABILITY_YES 1 + +/* Maximum number of binning subtypes */ +#define SMIAPP_BINNING_SUBTYPES 253 + +#define SMIAPP_PIXEL_ORDER_GRBG 0 +#define SMIAPP_PIXEL_ORDER_RGGB 1 +#define SMIAPP_PIXEL_ORDER_BGGR 2 +#define SMIAPP_PIXEL_ORDER_GBRG 3 + +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 + +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 + +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff + +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff + +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 + +#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 +#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 + +/* Scaling N factor */ +#define SMIAPP_SCALE_N 16 + +/* Image statistics registers */ +/* Registers 0x2000 to 0x2fff are reserved for future + * use for statistics features. + */ + +/* Manufacturer Specific Registers: 0x3000 to 0x3fff + * The manufacturer specifies these as a black box. + */ + +#endif /* __SMIAPP_REG_H_ */ diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c new file mode 100644 index 000000000000..4851ff710779 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-regs.c @@ -0,0 +1,213 @@ +/* + * drivers/media/video/smiapp/smiapp-regs.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "smiapp-debug.h" + +#include +#include + +#include "smiapp-regs.h" + +static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, + uint32_t phloat) +{ + int32_t exp; + uint64_t man; + + if (phloat >= 0x80000000) { + dev_err(&client->dev, "this is a negative number\n"); + return 0; + } + + if (phloat == 0x7f800000) + return ~0; /* Inf. */ + + if ((phloat & 0x7f800000) == 0x7f800000) { + dev_err(&client->dev, "NaN or other special number\n"); + return 0; + } + + /* Valid cases begin here */ + if (phloat == 0) + return 0; /* Valid zero */ + + if (phloat > 0x4f800000) + return ~0; /* larger than 4294967295 */ + + /* + * Unbias exponent (note how phloat is now guaranteed to + * have 0 in the high bit) + */ + exp = ((int32_t)phloat >> 23) - 127; + + /* Extract mantissa, add missing '1' bit and it's in MHz */ + man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; + + if (exp < 0) + man >>= -exp; + else + man <<= exp; + + man >>= 23; /* Remove mantissa bias */ + + return man & 0xffffffff; +} + + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_read(struct i2c_client *client, u32 reg, u32 *val) +{ + struct i2c_msg msg; + unsigned char data[4]; + unsigned int len = (u8)(reg >> 16); + u16 offset = reg; + int r; + + if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT + && len != SMIA_REG_32BIT) + return -EINVAL; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (offset >> 8); + data[1] = (u8) offset; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + msg.len = len; + msg.flags = I2C_M_RD; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + *val = 0; + /* high byte comes first */ + switch (len) { + case SMIA_REG_32BIT: + *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + + data[3]; + break; + case SMIA_REG_16BIT: + *val = (data[0] << 8) + data[1]; + break; + case SMIA_REG_8BIT: + *val = data[0]; + break; + default: + BUG(); + } + + if (reg & SMIA_REG_FLAG_FLOAT) + *val = float_to_u32_mul_1000000(client, *val); + + return 0; + +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); + + return r; +} + +/* + * Write to a 8/16-bit register. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_write(struct i2c_client *client, u32 reg, u32 val) +{ + struct i2c_msg msg; + unsigned char data[6]; + unsigned int retries; + unsigned int flags = reg >> 24; + unsigned int len = (u8)(reg >> 16); + u16 offset = reg; + int r; + + if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && + len != SMIA_REG_32BIT) || flags) + return -EINVAL; + + msg.addr = client->addr; + msg.flags = 0; /* Write */ + msg.len = 2 + len; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + + switch (len) { + case SMIA_REG_8BIT: + data[2] = val; + break; + case SMIA_REG_16BIT: + data[2] = val >> 8; + data[3] = val; + break; + case SMIA_REG_32BIT: + data[2] = val >> 24; + data[3] = val >> 16; + data[4] = val >> 8; + data[5] = val; + break; + default: + BUG(); + } + + for (retries = 0; retries < 5; retries++) { + /* + * Due to unknown reason sensor stops responding. This + * loop is a temporaty solution until the root cause + * is found. + */ + r = i2c_transfer(client->adapter, &msg, 1); + if (r == 1) { + if (retries) + dev_err(&client->dev, + "sensor i2c stall encountered. " + "retries: %d\n", retries); + return 0; + } + + usleep_range(2000, 2000); + } + + dev_err(&client->dev, + "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); + + return r; +} diff --git a/drivers/media/video/smiapp/smiapp-regs.h b/drivers/media/video/smiapp/smiapp-regs.h new file mode 100644 index 000000000000..58e8009d4aa5 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-regs.h @@ -0,0 +1,46 @@ +/* + * include/media/smiapp/smiapp-regs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SMIAPP_REGS_H +#define SMIAPP_REGS_H + +#include +#include + +/* Use upper 8 bits of the type field for flags */ +#define SMIA_REG_FLAG_FLOAT (1 << 24) + +#define SMIA_REG_8BIT 1 +#define SMIA_REG_16BIT 2 +#define SMIA_REG_32BIT 4 +struct smia_reg { + u16 type; + u16 reg; /* 16-bit offset */ + u32 val; /* 8/16/32-bit value */ +}; + +int smiapp_read(struct i2c_client *client, u32 reg, u32 *val); +int smiapp_write(struct i2c_client *client, u32 reg, u32 val); + +#endif diff --git a/drivers/media/video/smiapp/smiapp.h b/drivers/media/video/smiapp/smiapp.h new file mode 100644 index 000000000000..805d8c8a3c18 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp.h @@ -0,0 +1,251 @@ +/* + * drivers/media/video/smiapp/smiapp.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_PRIV_H_ +#define __SMIAPP_PRIV_H_ + +#include +#include +#include +#include + +#include "../smiapp-pll.h" +#include "smiapp-reg.h" +#include "smiapp-regs.h" +#include "smiapp-quirk.h" + +/* + * Standard SMIA++ constants + */ +#define SMIA_VERSION_1 10 +#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */ +#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */ +#define SMIAPP_VERSION_1 10 + +#define SMIAPP_PROFILE_0 0 +#define SMIAPP_PROFILE_1 1 +#define SMIAPP_PROFILE_2 2 + +#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */ + +#define SMIAPP_RESET_DELAY_CLOCKS 2400 +#define SMIAPP_RESET_DELAY(clk) \ + (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ + + (clk) / 1000 - 1) / ((clk) / 1000)) + +#include "smiapp-limits.h" + +struct smiapp_quirk; + +#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0) + +struct smiapp_module_ident { + u8 manufacturer_id; + u16 model_id; + u8 revision_number_major; + + u8 flags; + + char *name; + const struct smiapp_quirk *quirk; +}; + +struct smiapp_module_info { + u32 manufacturer_id; + u32 model_id; + u32 revision_number_major; + u32 revision_number_minor; + + u32 module_year; + u32 module_month; + u32 module_day; + + u32 sensor_manufacturer_id; + u32 sensor_model_id; + u32 sensor_revision_number; + u32 sensor_firmware_version; + + u32 smia_version; + u32 smiapp_version; + + u32 smiapp_profile; + + char *name; + const struct smiapp_quirk *quirk; +}; + +#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = fl, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, } + +#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, } + +struct smiapp_reg_limits { + u32 addr; + char *what; +}; + +extern struct smiapp_reg_limits smiapp_reg_limits[]; + +struct smiapp_csi_data_format { + u32 code; + u8 width; + u8 compressed; + u8 pixel_order; +}; + +#define SMIAPP_SUBDEVS 3 + +#define SMIAPP_PA_PAD_SRC 0 +#define SMIAPP_PAD_SINK 0 +#define SMIAPP_PAD_SRC 1 +#define SMIAPP_PADS 2 + +struct smiapp_binning_subtype { + u8 horizontal:4; + u8 vertical:4; +} __packed; + +struct smiapp_subdev { + struct v4l2_subdev sd; + struct media_pad pads[2]; + struct v4l2_rect sink_fmt; + struct v4l2_rect crop[2]; + struct v4l2_rect compose; /* compose on sink */ + unsigned short sink_pad; + unsigned short source_pad; + int npads; + struct smiapp_sensor *sensor; + struct v4l2_ctrl_handler ctrl_handler; +}; + +/* + * struct smiapp_sensor - Main device structure + */ +struct smiapp_sensor { + /* + * "mutex" is used to serialise access to all fields here + * except v4l2_ctrls at the end of the struct. "mutex" is also + * used to serialise access to file handle specific + * information. The exception to this rule is the power_mutex + * below. + */ + struct mutex mutex; + /* + * power_mutex is used to serialise power management related + * activities. Acquiring "mutex" at that time isn't necessary + * since there are no other users anyway. + */ + struct mutex power_mutex; + struct smiapp_subdev ssds[SMIAPP_SUBDEVS]; + u32 ssds_used; + struct smiapp_subdev *src; + struct smiapp_subdev *binner; + struct smiapp_subdev *scaler; + struct smiapp_subdev *pixel_array; + struct smiapp_platform_data *platform_data; + struct regulator *vana; + u32 limits[SMIAPP_LIMIT_LAST]; + u8 nbinning_subtypes; + struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; + u32 mbus_frame_fmts; + const struct smiapp_csi_data_format *csi_format; + const struct smiapp_csi_data_format *internal_csi_format; + u32 default_mbus_frame_fmts; + int default_pixel_order; + + u8 binning_horizontal; + u8 binning_vertical; + + u8 scale_m; + u8 scaling_mode; + + u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ + u8 flash_capability; + u8 frame_skip; + + int power_count; + + bool streaming; + bool dev_init_done; + + u8 *nvm; /* nvm memory buffer */ + unsigned int nvm_size; /* bytes */ + + struct smiapp_module_info minfo; + + struct smiapp_pll pll; + + /* Pixel array controls */ + struct v4l2_ctrl *analog_gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *pixel_rate_parray; + /* src controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate_csi; +}; + +#define to_smiapp_subdev(_sd) \ + container_of(_sd, struct smiapp_subdev, sd) + +#define to_smiapp_sensor(_sd) \ + (to_smiapp_subdev(_sd)->sensor) + +#endif /* __SMIAPP_PRIV_H_ */ diff --git a/include/media/smiapp.h b/include/media/smiapp.h new file mode 100644 index 000000000000..a7877cd0733d --- /dev/null +++ b/include/media/smiapp.h @@ -0,0 +1,83 @@ +/* + * include/media/smiapp.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_H_ +#define __SMIAPP_H_ + +#include + +#define SMIAPP_NAME "smiapp" + +#define SMIAPP_DFL_I2C_ADDR (0x20 >> 1) /* Default I2C Address */ +#define SMIAPP_ALT_I2C_ADDR (0x6e >> 1) /* Alternate I2C Address */ + +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK 0 +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE 1 +#define SMIAPP_CSI_SIGNALLING_MODE_CSI2 2 + +#define SMIAPP_NO_XSHUTDOWN -1 + +/* + * Sometimes due to board layout considerations the camera module can be + * mounted rotated. The typical rotation used is 180 degrees which can be + * corrected by giving a default H-FLIP and V-FLIP in the sensor readout. + * FIXME: rotation also changes the bayer pattern. + */ +enum smiapp_module_board_orient { + SMIAPP_MODULE_BOARD_ORIENT_0 = 0, + SMIAPP_MODULE_BOARD_ORIENT_180, +}; + +struct smiapp_flash_strobe_parms { + u8 mode; + u32 strobe_width_high_us; + u16 strobe_delay; + u16 stobe_start_point; + u8 trigger; +}; + +struct smiapp_platform_data { + /* + * Change the cci address if i2c_addr_alt is set. + * Both default and alternate cci addr need to be present + */ + unsigned short i2c_addr_dfl; /* Default i2c addr */ + unsigned short i2c_addr_alt; /* Alternate i2c addr */ + + unsigned int nvm_size; /* bytes */ + unsigned int ext_clk; /* sensor external clk */ + + unsigned int lanes; /* Number of CSI-2 lanes */ + u8 csi_signalling_mode; /* SMIAPP_CSI_SIGNALLING_MODE_* */ + const s64 *op_sys_clock; + + enum smiapp_module_board_orient module_board_orient; + + struct smiapp_flash_strobe_parms *strobe_setup; + + int (*set_xclk)(struct v4l2_subdev *sd, int hz); + int xshutdown; /* gpio or SMIAPP_NO_XSHUTDOWN */ +}; + +#endif /* __SMIAPP_H_ */ -- cgit v1.2.3 From 8ab75e3ecd8f232d9564510f0c601a6aa7a149ea Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 10 May 2012 02:51:31 -0300 Subject: [media] v4l2-dev: make it possible to skip locking for selected ioctls Using the V4L2 core lock is a very robust method that is usually very good at doing the right thing. But some drivers, particularly USB drivers, may want to prevent the core from taking the lock for specific ioctls, particularly buffer queuing ioctls. The reason is that certain commands like S_CTRL can take a long time to process over USB and all the time the core has the lock, preventing VIDIOC_DQBUF from proceeding, even though a frame may be ready in the queue. This introduces unwanted latency. Since the buffer queuing commands often have their own internal lock it is often not necessary to take the core lock. Drivers can now say that they don't want the core to take the lock for specific ioctls. As it is a specific opt-out it makes it clear to the reviewer that those ioctls will need more care when reviewing. Signed-off-by: Hans Verkuil Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 27 +++- drivers/media/video/v4l2-dev.c | 14 +- drivers/media/video/v4l2-ioctl.c | 189 +++++++++++++++------------ include/media/v4l2-dev.h | 11 ++ 4 files changed, 148 insertions(+), 93 deletions(-) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index fe53177f0d3c..e1e6a01d7ac6 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -580,19 +580,25 @@ allocated memory. You should also set these fields: - v4l2_dev: set to the v4l2_device parent device. + - name: set to something descriptive and unique. + - fops: set to the v4l2_file_operations struct. + - ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance (highly recommended to use this and it might become compulsory in the future!), then set this to your v4l2_ioctl_ops struct. + - lock: leave to NULL if you want to do all the locking in the driver. Otherwise you give it a pointer to a struct mutex_lock and before any of the v4l2_file_operations is called this lock will be taken by the - core and released afterwards. + core and released afterwards. See the next section for more details. + - prio: keeps track of the priorities. Used to implement VIDIOC_G/S_PRIORITY. If left to NULL, then it will use the struct v4l2_prio_state in v4l2_device. If you want to have a separate priority state per (group of) device node(s), then you can point it to your own struct v4l2_prio_state. + - parent: you only set this if v4l2_device was registered with NULL as the parent device struct. This only happens in cases where one hardware device has multiple PCI devices that all share the same v4l2_device core. @@ -602,6 +608,7 @@ You should also set these fields: (cx8802). Since the v4l2_device cannot be associated with a particular PCI device it is setup without a parent device. But when the struct video_device is setup you do know which parent PCI device to use. + - flags: optional. Set to V4L2_FL_USE_FH_PRIO if you want to let the framework handle the VIDIOC_G/S_PRIORITY ioctls. This requires that you use struct v4l2_fh. Eventually this flag will disappear once all drivers use the core @@ -634,8 +641,22 @@ v4l2_file_operations and locking -------------------------------- You can set a pointer to a mutex_lock in struct video_device. Usually this -will be either a top-level mutex or a mutex per device node. If you want -finer-grained locking then you have to set it to NULL and do you own locking. +will be either a top-level mutex or a mutex per device node. By default this +lock will be used for each file operation and ioctl, but you can disable +locking for selected ioctls by calling: + + void v4l2_dont_use_lock(struct video_device *vdev, unsigned int cmd); + +E.g.: v4l2_dont_use_lock(vdev, VIDIOC_DQBUF); + +You have to call this before you register the video_device. + +Particularly with USB drivers where certain commands such as setting controls +can take a long time you may want to do your own locking for the buffer queuing +ioctls. + +If you want still finer-grained locking then you have to set mutex_lock to NULL +and do you own locking completely. It is up to the driver developer to decide which method to use. However, if your driver has high-latency operations (for example, changing the exposure diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 70bec548d904..e4a9ed67bb2e 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -322,11 +322,19 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) int ret = -ENODEV; if (vdev->fops->unlocked_ioctl) { - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; + bool locked = false; + + if (vdev->lock) { + /* always lock unless the cmd is marked as "don't use lock" */ + locked = !v4l2_is_known_ioctl(cmd) || + !test_bit(_IOC_NR(cmd), vdev->dont_use_lock); + + if (locked && mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + } if (video_is_registered(vdev)) ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); - if (vdev->lock) + if (locked) mutex_unlock(vdev->lock); } else if (vdev->fops->ioctl) { /* This code path is a replacement for the BKL. It is a major diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 5b2ec1fd2d0a..ef44b084132a 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -195,93 +195,106 @@ static const char *v4l2_memory_names[] = { /* ------------------------------------------------------------------ */ /* debug help functions */ -static const char *v4l2_ioctls[] = { - [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP", - [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED", - [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT", - [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT", - [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT", - [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS", - [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF", - [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF", - [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF", - [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY", - [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF", - [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF", - [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON", - [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF", - [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM", - [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM", - [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD", - [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD", - [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD", - [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT", - [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL", - [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL", - [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER", - [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER", - [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO", - [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO", - [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL", - [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU", - [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT", - [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT", - [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT", - [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT", - [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT", - [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT", - [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT", - [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR", - [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR", - [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY", - [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY", - [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP", - [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP", - [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP", - [_IOC_NR(VIDIOC_G_SELECTION)] = "VIDIOC_G_SELECTION", - [_IOC_NR(VIDIOC_S_SELECTION)] = "VIDIOC_S_SELECTION", - [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP", - [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP", - [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD", - [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT", - [_IOC_NR(VIDIOC_ENUMAUDIO)] = "VIDIOC_ENUMAUDIO", - [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT", - [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY", - [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY", - [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP", - [_IOC_NR(VIDIOC_LOG_STATUS)] = "VIDIOC_LOG_STATUS", - [_IOC_NR(VIDIOC_G_EXT_CTRLS)] = "VIDIOC_G_EXT_CTRLS", - [_IOC_NR(VIDIOC_S_EXT_CTRLS)] = "VIDIOC_S_EXT_CTRLS", - [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS", -#if 1 - [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES", - [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS", - [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX", - [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD", - [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD", - - [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD", - [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD", - [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", - [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", - - [_IOC_NR(VIDIOC_DBG_G_CHIP_IDENT)] = "VIDIOC_DBG_G_CHIP_IDENT", - [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", -#endif - [_IOC_NR(VIDIOC_ENUM_DV_PRESETS)] = "VIDIOC_ENUM_DV_PRESETS", - [_IOC_NR(VIDIOC_S_DV_PRESET)] = "VIDIOC_S_DV_PRESET", - [_IOC_NR(VIDIOC_G_DV_PRESET)] = "VIDIOC_G_DV_PRESET", - [_IOC_NR(VIDIOC_QUERY_DV_PRESET)] = "VIDIOC_QUERY_DV_PRESET", - [_IOC_NR(VIDIOC_S_DV_TIMINGS)] = "VIDIOC_S_DV_TIMINGS", - [_IOC_NR(VIDIOC_G_DV_TIMINGS)] = "VIDIOC_G_DV_TIMINGS", - [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT", - [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT", - [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT", - [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS", - [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF", + +struct v4l2_ioctl_info { + unsigned int ioctl; + const char * const name; +}; + +#define IOCTL_INFO(_ioctl) [_IOC_NR(_ioctl)] = { \ + .ioctl = _ioctl, \ + .name = #_ioctl, \ +} + +static struct v4l2_ioctl_info v4l2_ioctls[] = { + IOCTL_INFO(VIDIOC_QUERYCAP), + IOCTL_INFO(VIDIOC_ENUM_FMT), + IOCTL_INFO(VIDIOC_G_FMT), + IOCTL_INFO(VIDIOC_S_FMT), + IOCTL_INFO(VIDIOC_REQBUFS), + IOCTL_INFO(VIDIOC_QUERYBUF), + IOCTL_INFO(VIDIOC_G_FBUF), + IOCTL_INFO(VIDIOC_S_FBUF), + IOCTL_INFO(VIDIOC_OVERLAY), + IOCTL_INFO(VIDIOC_QBUF), + IOCTL_INFO(VIDIOC_DQBUF), + IOCTL_INFO(VIDIOC_STREAMON), + IOCTL_INFO(VIDIOC_STREAMOFF), + IOCTL_INFO(VIDIOC_G_PARM), + IOCTL_INFO(VIDIOC_S_PARM), + IOCTL_INFO(VIDIOC_G_STD), + IOCTL_INFO(VIDIOC_S_STD), + IOCTL_INFO(VIDIOC_ENUMSTD), + IOCTL_INFO(VIDIOC_ENUMINPUT), + IOCTL_INFO(VIDIOC_G_CTRL), + IOCTL_INFO(VIDIOC_S_CTRL), + IOCTL_INFO(VIDIOC_G_TUNER), + IOCTL_INFO(VIDIOC_S_TUNER), + IOCTL_INFO(VIDIOC_G_AUDIO), + IOCTL_INFO(VIDIOC_S_AUDIO), + IOCTL_INFO(VIDIOC_QUERYCTRL), + IOCTL_INFO(VIDIOC_QUERYMENU), + IOCTL_INFO(VIDIOC_G_INPUT), + IOCTL_INFO(VIDIOC_S_INPUT), + IOCTL_INFO(VIDIOC_G_OUTPUT), + IOCTL_INFO(VIDIOC_S_OUTPUT), + IOCTL_INFO(VIDIOC_ENUMOUTPUT), + IOCTL_INFO(VIDIOC_G_AUDOUT), + IOCTL_INFO(VIDIOC_S_AUDOUT), + IOCTL_INFO(VIDIOC_G_MODULATOR), + IOCTL_INFO(VIDIOC_S_MODULATOR), + IOCTL_INFO(VIDIOC_G_FREQUENCY), + IOCTL_INFO(VIDIOC_S_FREQUENCY), + IOCTL_INFO(VIDIOC_CROPCAP), + IOCTL_INFO(VIDIOC_G_CROP), + IOCTL_INFO(VIDIOC_S_CROP), + IOCTL_INFO(VIDIOC_G_SELECTION), + IOCTL_INFO(VIDIOC_S_SELECTION), + IOCTL_INFO(VIDIOC_G_JPEGCOMP), + IOCTL_INFO(VIDIOC_S_JPEGCOMP), + IOCTL_INFO(VIDIOC_QUERYSTD), + IOCTL_INFO(VIDIOC_TRY_FMT), + IOCTL_INFO(VIDIOC_ENUMAUDIO), + IOCTL_INFO(VIDIOC_ENUMAUDOUT), + IOCTL_INFO(VIDIOC_G_PRIORITY), + IOCTL_INFO(VIDIOC_S_PRIORITY), + IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP), + IOCTL_INFO(VIDIOC_LOG_STATUS), + IOCTL_INFO(VIDIOC_G_EXT_CTRLS), + IOCTL_INFO(VIDIOC_S_EXT_CTRLS), + IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS), + IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES), + IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS), + IOCTL_INFO(VIDIOC_G_ENC_INDEX), + IOCTL_INFO(VIDIOC_ENCODER_CMD), + IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD), + IOCTL_INFO(VIDIOC_DECODER_CMD), + IOCTL_INFO(VIDIOC_TRY_DECODER_CMD), + IOCTL_INFO(VIDIOC_DBG_S_REGISTER), + IOCTL_INFO(VIDIOC_DBG_G_REGISTER), + IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT), + IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK), + IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS), + IOCTL_INFO(VIDIOC_S_DV_PRESET), + IOCTL_INFO(VIDIOC_G_DV_PRESET), + IOCTL_INFO(VIDIOC_QUERY_DV_PRESET), + IOCTL_INFO(VIDIOC_S_DV_TIMINGS), + IOCTL_INFO(VIDIOC_G_DV_TIMINGS), + IOCTL_INFO(VIDIOC_DQEVENT), + IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT), + IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT), + IOCTL_INFO(VIDIOC_CREATE_BUFS), + IOCTL_INFO(VIDIOC_PREPARE_BUF), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) +bool v4l2_is_known_ioctl(unsigned int cmd) +{ + if (_IOC_NR(cmd) >= V4L2_IOCTLS) + return false; + return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; +} + /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ void v4l_printk_ioctl(unsigned int cmd) @@ -297,7 +310,7 @@ void v4l_printk_ioctl(unsigned int cmd) type = "v4l2"; break; } - printk("%s", v4l2_ioctls[_IOC_NR(cmd)]); + printk("%s", v4l2_ioctls[_IOC_NR(cmd)].name); return; default: type = "unknown"; @@ -1948,9 +1961,9 @@ static long __video_do_ioctl(struct file *file, vfd->v4l2_dev->name); break; } -#ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_DBG_G_REGISTER: { +#ifdef CONFIG_VIDEO_ADV_DEBUG struct v4l2_dbg_register *p = arg; if (ops->vidioc_g_register) { @@ -1959,10 +1972,12 @@ static long __video_do_ioctl(struct file *file, else ret = ops->vidioc_g_register(file, fh, p); } +#endif break; } case VIDIOC_DBG_S_REGISTER: { +#ifdef CONFIG_VIDEO_ADV_DEBUG struct v4l2_dbg_register *p = arg; if (ops->vidioc_s_register) { @@ -1971,9 +1986,9 @@ static long __video_do_ioctl(struct file *file, else ret = ops->vidioc_s_register(file, fh, p); } +#endif break; } -#endif case VIDIOC_DBG_G_CHIP_IDENT: { struct v4l2_dbg_chip_ident *p = arg; diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 96d22215cc88..d00b9d3511f2 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -128,6 +128,7 @@ struct video_device const struct v4l2_ioctl_ops *ioctl_ops; /* serialization lock */ + DECLARE_BITMAP(dont_use_lock, BASE_VIDIOC_PRIVATE); struct mutex *lock; }; @@ -173,6 +174,16 @@ void video_device_release(struct video_device *vdev); a dubious construction at best. */ void video_device_release_empty(struct video_device *vdev); +/* returns true if cmd is a known V4L2 ioctl */ +bool v4l2_is_known_ioctl(unsigned int cmd); + +/* mark that this command shouldn't use core locking */ +static inline void v4l2_dont_use_lock(struct video_device *vdev, unsigned int cmd) +{ + if (_IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) + set_bit(_IOC_NR(cmd), vdev->dont_use_lock); +} + /* helper functions to access driver private data. */ static inline void *video_get_drvdata(struct video_device *vdev) { -- cgit v1.2.3 From 48ea0be06028d97b57602372f032afbec02e7e97 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 10 May 2012 05:36:00 -0300 Subject: [media] v4l2-dev/ioctl: determine the valid ioctls upfront Rather than testing whether an ioctl is implemented in the driver or not every time the ioctl is called, do it upfront when the device is registered. This also allows a driver to disable certain ioctls based on the capabilities of the detected board, something you can't do today without creating separate v4l2_ioctl_ops structs for each new variation. For the most part it is pretty straightforward, but for control ioctls a flag is needed since it is possible that you have per-filehandle controls, and that can't be determined upfront of course. Signed-off-by: Hans Verkuil Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-dev.c | 172 +++++++++++++++++ drivers/media/video/v4l2-ioctl.c | 389 ++++++++++++--------------------------- include/media/v4l2-dev.h | 11 ++ 3 files changed, 296 insertions(+), 276 deletions(-) diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index e4a9ed67bb2e..b1f0923212e6 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -516,6 +516,175 @@ static int get_index(struct video_device *vdev) return find_first_zero_bit(used, VIDEO_NUM_DEVICES); } +#define SET_VALID_IOCTL(ops, cmd, op) \ + if (ops->op) \ + set_bit(_IOC_NR(cmd), valid_ioctls) + +/* This determines which ioctls are actually implemented in the driver. + It's a one-time thing which simplifies video_ioctl2 as it can just do + a bit test. + + Note that drivers can override this by setting bits to 1 in + vdev->valid_ioctls. If an ioctl is marked as 1 when this function is + called, then that ioctl will actually be marked as unimplemented. + + It does that by first setting up the local valid_ioctls bitmap, and + at the end do a: + + vdev->valid_ioctls = valid_ioctls & ~(vdev->valid_ioctls) + */ +static void determine_valid_ioctls(struct video_device *vdev) +{ + DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); + const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops; + + bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE); + + SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap); + if (ops->vidioc_g_priority || + test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); + if (ops->vidioc_s_priority || + test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); + if (ops->vidioc_enum_fmt_vid_cap || + ops->vidioc_enum_fmt_vid_out || + ops->vidioc_enum_fmt_vid_cap_mplane || + ops->vidioc_enum_fmt_vid_out_mplane || + ops->vidioc_enum_fmt_vid_overlay || + ops->vidioc_enum_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); + if (ops->vidioc_g_fmt_vid_cap || + ops->vidioc_g_fmt_vid_out || + ops->vidioc_g_fmt_vid_cap_mplane || + ops->vidioc_g_fmt_vid_out_mplane || + ops->vidioc_g_fmt_vid_overlay || + ops->vidioc_g_fmt_vbi_cap || + ops->vidioc_g_fmt_vid_out_overlay || + ops->vidioc_g_fmt_vbi_out || + ops->vidioc_g_fmt_sliced_vbi_cap || + ops->vidioc_g_fmt_sliced_vbi_out || + ops->vidioc_g_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); + if (ops->vidioc_s_fmt_vid_cap || + ops->vidioc_s_fmt_vid_out || + ops->vidioc_s_fmt_vid_cap_mplane || + ops->vidioc_s_fmt_vid_out_mplane || + ops->vidioc_s_fmt_vid_overlay || + ops->vidioc_s_fmt_vbi_cap || + ops->vidioc_s_fmt_vid_out_overlay || + ops->vidioc_s_fmt_vbi_out || + ops->vidioc_s_fmt_sliced_vbi_cap || + ops->vidioc_s_fmt_sliced_vbi_out || + ops->vidioc_s_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); + if (ops->vidioc_try_fmt_vid_cap || + ops->vidioc_try_fmt_vid_out || + ops->vidioc_try_fmt_vid_cap_mplane || + ops->vidioc_try_fmt_vid_out_mplane || + ops->vidioc_try_fmt_vid_overlay || + ops->vidioc_try_fmt_vbi_cap || + ops->vidioc_try_fmt_vid_out_overlay || + ops->vidioc_try_fmt_vbi_out || + ops->vidioc_try_fmt_sliced_vbi_cap || + ops->vidioc_try_fmt_sliced_vbi_out || + ops->vidioc_try_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); + SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); + SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); + SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); + SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay); + SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf); + SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf); + SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); + SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); + if (vdev->tvnorms) + set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); + if (ops->vidioc_g_std || vdev->current_norm) + set_bit(_IOC_NR(VIDIOC_G_STD), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std); + SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd); + SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input); + SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input); + SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input); + SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output); + SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output); + SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output); + /* Note: the control handler can also be passed through the filehandle, + and that can't be tested here. If the bit for these control ioctls + is set, then the ioctl is valid. But if it is 0, then it can still + be valid if the filehandle passed the control handler. */ + if (vdev->ctrl_handler || ops->vidioc_queryctrl) + set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_querymenu) + set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio); + SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio); + SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio); + SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout); + SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout); + SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout); + SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator); + SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator); + if (ops->vidioc_g_crop || ops->vidioc_g_selection) + set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls); + if (ops->vidioc_s_crop || ops->vidioc_s_selection) + set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection); + SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection); + if (ops->vidioc_cropcap || ops->vidioc_g_selection) + set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp); + SET_VALID_IOCTL(ops, VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp); + SET_VALID_IOCTL(ops, VIDIOC_G_ENC_INDEX, vidioc_g_enc_index); + SET_VALID_IOCTL(ops, VIDIOC_ENCODER_CMD, vidioc_encoder_cmd); + SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd); + SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd); + SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd); + if (ops->vidioc_g_parm || vdev->current_norm) + set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm); + SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner); + SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner); + SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency); + SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency); + SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap); + SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status); +#ifdef CONFIG_VIDEO_ADV_DEBUG + SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register); + SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register); +#endif + SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident); + SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets); + SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset); + SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset); + SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset); + SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings); + SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings); + /* yes, really vidioc_subscribe_event */ + SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event); + SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event); + SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); + SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); + SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); + bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls, + BASE_VIDIOC_PRIVATE); +} + /** * __video_register_device - register video4linux devices * @vdev: video device structure we want to register @@ -663,6 +832,9 @@ int __video_register_device(struct video_device *vdev, int type, int nr, vdev->index = get_index(vdev); mutex_unlock(&videodev_lock); + if (vdev->ioctl_ops) + determine_valid_ioctls(vdev); + /* Part 3: Initialize the character device */ vdev->cdev = cdev_alloc(); if (vdev->cdev == NULL) { diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index ef44b084132a..3df1f80f1c5c 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -55,19 +55,6 @@ memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \ 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field)) -#define have_fmt_ops(foo) ( \ - ops->vidioc_##foo##_fmt_vid_cap || \ - ops->vidioc_##foo##_fmt_vid_out || \ - ops->vidioc_##foo##_fmt_vid_cap_mplane || \ - ops->vidioc_##foo##_fmt_vid_out_mplane || \ - ops->vidioc_##foo##_fmt_vid_overlay || \ - ops->vidioc_##foo##_fmt_vbi_cap || \ - ops->vidioc_##foo##_fmt_vid_out_overlay || \ - ops->vidioc_##foo##_fmt_vbi_out || \ - ops->vidioc_##foo##_fmt_sliced_vbi_cap || \ - ops->vidioc_##foo##_fmt_sliced_vbi_out || \ - ops->vidioc_##foo##_fmt_type_private) - struct std_descr { v4l2_std_id std; const char *descr; @@ -198,93 +185,98 @@ static const char *v4l2_memory_names[] = { struct v4l2_ioctl_info { unsigned int ioctl; + u16 flags; const char * const name; }; -#define IOCTL_INFO(_ioctl) [_IOC_NR(_ioctl)] = { \ - .ioctl = _ioctl, \ - .name = #_ioctl, \ +/* This control can be valid if the filehandle passes a control handler. */ +#define INFO_FL_CTRL (1 << 1) + +#define IOCTL_INFO(_ioctl, _flags) [_IOC_NR(_ioctl)] = { \ + .ioctl = _ioctl, \ + .flags = _flags, \ + .name = #_ioctl, \ } static struct v4l2_ioctl_info v4l2_ioctls[] = { - IOCTL_INFO(VIDIOC_QUERYCAP), - IOCTL_INFO(VIDIOC_ENUM_FMT), - IOCTL_INFO(VIDIOC_G_FMT), - IOCTL_INFO(VIDIOC_S_FMT), - IOCTL_INFO(VIDIOC_REQBUFS), - IOCTL_INFO(VIDIOC_QUERYBUF), - IOCTL_INFO(VIDIOC_G_FBUF), - IOCTL_INFO(VIDIOC_S_FBUF), - IOCTL_INFO(VIDIOC_OVERLAY), - IOCTL_INFO(VIDIOC_QBUF), - IOCTL_INFO(VIDIOC_DQBUF), - IOCTL_INFO(VIDIOC_STREAMON), - IOCTL_INFO(VIDIOC_STREAMOFF), - IOCTL_INFO(VIDIOC_G_PARM), - IOCTL_INFO(VIDIOC_S_PARM), - IOCTL_INFO(VIDIOC_G_STD), - IOCTL_INFO(VIDIOC_S_STD), - IOCTL_INFO(VIDIOC_ENUMSTD), - IOCTL_INFO(VIDIOC_ENUMINPUT), - IOCTL_INFO(VIDIOC_G_CTRL), - IOCTL_INFO(VIDIOC_S_CTRL), - IOCTL_INFO(VIDIOC_G_TUNER), - IOCTL_INFO(VIDIOC_S_TUNER), - IOCTL_INFO(VIDIOC_G_AUDIO), - IOCTL_INFO(VIDIOC_S_AUDIO), - IOCTL_INFO(VIDIOC_QUERYCTRL), - IOCTL_INFO(VIDIOC_QUERYMENU), - IOCTL_INFO(VIDIOC_G_INPUT), - IOCTL_INFO(VIDIOC_S_INPUT), - IOCTL_INFO(VIDIOC_G_OUTPUT), - IOCTL_INFO(VIDIOC_S_OUTPUT), - IOCTL_INFO(VIDIOC_ENUMOUTPUT), - IOCTL_INFO(VIDIOC_G_AUDOUT), - IOCTL_INFO(VIDIOC_S_AUDOUT), - IOCTL_INFO(VIDIOC_G_MODULATOR), - IOCTL_INFO(VIDIOC_S_MODULATOR), - IOCTL_INFO(VIDIOC_G_FREQUENCY), - IOCTL_INFO(VIDIOC_S_FREQUENCY), - IOCTL_INFO(VIDIOC_CROPCAP), - IOCTL_INFO(VIDIOC_G_CROP), - IOCTL_INFO(VIDIOC_S_CROP), - IOCTL_INFO(VIDIOC_G_SELECTION), - IOCTL_INFO(VIDIOC_S_SELECTION), - IOCTL_INFO(VIDIOC_G_JPEGCOMP), - IOCTL_INFO(VIDIOC_S_JPEGCOMP), - IOCTL_INFO(VIDIOC_QUERYSTD), - IOCTL_INFO(VIDIOC_TRY_FMT), - IOCTL_INFO(VIDIOC_ENUMAUDIO), - IOCTL_INFO(VIDIOC_ENUMAUDOUT), - IOCTL_INFO(VIDIOC_G_PRIORITY), - IOCTL_INFO(VIDIOC_S_PRIORITY), - IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP), - IOCTL_INFO(VIDIOC_LOG_STATUS), - IOCTL_INFO(VIDIOC_G_EXT_CTRLS), - IOCTL_INFO(VIDIOC_S_EXT_CTRLS), - IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS), - IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES), - IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS), - IOCTL_INFO(VIDIOC_G_ENC_INDEX), - IOCTL_INFO(VIDIOC_ENCODER_CMD), - IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD), - IOCTL_INFO(VIDIOC_DECODER_CMD), - IOCTL_INFO(VIDIOC_TRY_DECODER_CMD), - IOCTL_INFO(VIDIOC_DBG_S_REGISTER), - IOCTL_INFO(VIDIOC_DBG_G_REGISTER), - IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT), - IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK), - IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS), - IOCTL_INFO(VIDIOC_S_DV_PRESET), - IOCTL_INFO(VIDIOC_G_DV_PRESET), - IOCTL_INFO(VIDIOC_QUERY_DV_PRESET), - IOCTL_INFO(VIDIOC_S_DV_TIMINGS), - IOCTL_INFO(VIDIOC_G_DV_TIMINGS), - IOCTL_INFO(VIDIOC_DQEVENT), - IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT), - IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT), - IOCTL_INFO(VIDIOC_CREATE_BUFS), - IOCTL_INFO(VIDIOC_PREPARE_BUF), + IOCTL_INFO(VIDIOC_QUERYCAP, 0), + IOCTL_INFO(VIDIOC_ENUM_FMT, 0), + IOCTL_INFO(VIDIOC_G_FMT, 0), + IOCTL_INFO(VIDIOC_S_FMT, 0), + IOCTL_INFO(VIDIOC_REQBUFS, 0), + IOCTL_INFO(VIDIOC_QUERYBUF, 0), + IOCTL_INFO(VIDIOC_G_FBUF, 0), + IOCTL_INFO(VIDIOC_S_FBUF, 0), + IOCTL_INFO(VIDIOC_OVERLAY, 0), + IOCTL_INFO(VIDIOC_QBUF, 0), + IOCTL_INFO(VIDIOC_DQBUF, 0), + IOCTL_INFO(VIDIOC_STREAMON, 0), + IOCTL_INFO(VIDIOC_STREAMOFF, 0), + IOCTL_INFO(VIDIOC_G_PARM, 0), + IOCTL_INFO(VIDIOC_S_PARM, 0), + IOCTL_INFO(VIDIOC_G_STD, 0), + IOCTL_INFO(VIDIOC_S_STD, 0), + IOCTL_INFO(VIDIOC_ENUMSTD, 0), + IOCTL_INFO(VIDIOC_ENUMINPUT, 0), + IOCTL_INFO(VIDIOC_G_CTRL, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_S_CTRL, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_G_TUNER, 0), + IOCTL_INFO(VIDIOC_S_TUNER, 0), + IOCTL_INFO(VIDIOC_G_AUDIO, 0), + IOCTL_INFO(VIDIOC_S_AUDIO, 0), + IOCTL_INFO(VIDIOC_QUERYCTRL, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_QUERYMENU, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_G_INPUT, 0), + IOCTL_INFO(VIDIOC_S_INPUT, 0), + IOCTL_INFO(VIDIOC_G_OUTPUT, 0), + IOCTL_INFO(VIDIOC_S_OUTPUT, 0), + IOCTL_INFO(VIDIOC_ENUMOUTPUT, 0), + IOCTL_INFO(VIDIOC_G_AUDOUT, 0), + IOCTL_INFO(VIDIOC_S_AUDOUT, 0), + IOCTL_INFO(VIDIOC_G_MODULATOR, 0), + IOCTL_INFO(VIDIOC_S_MODULATOR, 0), + IOCTL_INFO(VIDIOC_G_FREQUENCY, 0), + IOCTL_INFO(VIDIOC_S_FREQUENCY, 0), + IOCTL_INFO(VIDIOC_CROPCAP, 0), + IOCTL_INFO(VIDIOC_G_CROP, 0), + IOCTL_INFO(VIDIOC_S_CROP, 0), + IOCTL_INFO(VIDIOC_G_SELECTION, 0), + IOCTL_INFO(VIDIOC_S_SELECTION, 0), + IOCTL_INFO(VIDIOC_G_JPEGCOMP, 0), + IOCTL_INFO(VIDIOC_S_JPEGCOMP, 0), + IOCTL_INFO(VIDIOC_QUERYSTD, 0), + IOCTL_INFO(VIDIOC_TRY_FMT, 0), + IOCTL_INFO(VIDIOC_ENUMAUDIO, 0), + IOCTL_INFO(VIDIOC_ENUMAUDOUT, 0), + IOCTL_INFO(VIDIOC_G_PRIORITY, 0), + IOCTL_INFO(VIDIOC_S_PRIORITY, 0), + IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, 0), + IOCTL_INFO(VIDIOC_LOG_STATUS, 0), + IOCTL_INFO(VIDIOC_G_EXT_CTRLS, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_S_EXT_CTRLS, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, 0), + IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, 0), + IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, 0), + IOCTL_INFO(VIDIOC_G_ENC_INDEX, 0), + IOCTL_INFO(VIDIOC_ENCODER_CMD, 0), + IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, 0), + IOCTL_INFO(VIDIOC_DECODER_CMD, 0), + IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, 0), + IOCTL_INFO(VIDIOC_DBG_S_REGISTER, 0), + IOCTL_INFO(VIDIOC_DBG_G_REGISTER, 0), + IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT, 0), + IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, 0), + IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS, 0), + IOCTL_INFO(VIDIOC_S_DV_PRESET, 0), + IOCTL_INFO(VIDIOC_G_DV_PRESET, 0), + IOCTL_INFO(VIDIOC_QUERY_DV_PRESET, 0), + IOCTL_INFO(VIDIOC_S_DV_TIMINGS, 0), + IOCTL_INFO(VIDIOC_G_DV_TIMINGS, 0), + IOCTL_INFO(VIDIOC_DQEVENT, 0), + IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, 0), + IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, 0), + IOCTL_INFO(VIDIOC_CREATE_BUFS, 0), + IOCTL_INFO(VIDIOC_PREPARE_BUF, 0), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -526,19 +518,26 @@ static long __video_do_ioctl(struct file *file, return ret; } - if ((vfd->debug & V4L2_DEBUG_IOCTL) && - !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { - v4l_print_ioctl(vfd->name, cmd); - printk(KERN_CONT "\n"); - } - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { vfh = file->private_data; use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + if (use_fh_prio) + ret_prio = v4l2_prio_check(vfd->prio, vfh->prio); } - if (use_fh_prio) - ret_prio = v4l2_prio_check(vfd->prio, vfh->prio); + if (v4l2_is_known_ioctl(cmd)) { + struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)]; + + if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && + !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) + return -ENOTTY; + } + + if ((vfd->debug & V4L2_DEBUG_IOCTL) && + !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { + v4l_print_ioctl(vfd->name, cmd); + printk(KERN_CONT "\n"); + } switch (cmd) { @@ -547,9 +546,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_capability *cap = (struct v4l2_capability *)arg; - if (!ops->vidioc_querycap) - break; - cap->version = LINUX_VERSION_CODE; ret = ops->vidioc_querycap(file, fh, cap); if (!ret) @@ -600,6 +596,7 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_fmtdesc *f = arg; + ret = -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (likely(ops->vidioc_enum_fmt_vid_cap)) @@ -632,7 +629,7 @@ static long __video_do_ioctl(struct file *file, default: break; } - if (likely (!ret)) + if (likely(!ret)) dbgarg(cmd, "index=%d, type=%d, flags=%d, " "pixelformat=%c%c%c%c, description='%s'\n", f->index, f->type, f->flags, @@ -641,14 +638,6 @@ static long __video_do_ioctl(struct file *file, (f->pixelformat >> 16) & 0xff, (f->pixelformat >> 24) & 0xff, f->description); - else if (ret == -ENOTTY && - (ops->vidioc_enum_fmt_vid_cap || - ops->vidioc_enum_fmt_vid_out || - ops->vidioc_enum_fmt_vid_cap_mplane || - ops->vidioc_enum_fmt_vid_out_mplane || - ops->vidioc_enum_fmt_vid_overlay || - ops->vidioc_enum_fmt_type_private)) - ret = -EINVAL; break; } case VIDIOC_G_FMT: @@ -658,6 +647,7 @@ static long __video_do_ioctl(struct file *file, /* FIXME: Should be one dump per type */ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); + ret = -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (ops->vidioc_g_fmt_vid_cap) @@ -719,17 +709,12 @@ static long __video_do_ioctl(struct file *file, fh, f); break; } - if (unlikely(ret == -ENOTTY && have_fmt_ops(g))) - ret = -EINVAL; - break; } case VIDIOC_S_FMT: { struct v4l2_format *f = (struct v4l2_format *)arg; - if (!have_fmt_ops(s)) - break; if (ret_prio) { ret = ret_prio; break; @@ -817,6 +802,7 @@ static long __video_do_ioctl(struct file *file, /* FIXME: Should be one dump per type */ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); + ret = -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.pix); @@ -889,8 +875,6 @@ static long __video_do_ioctl(struct file *file, fh, f); break; } - if (unlikely(ret == -ENOTTY && have_fmt_ops(try))) - ret = -EINVAL; break; } /* FIXME: Those buf reqs could be handled here, @@ -901,8 +885,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_requestbuffers *p = arg; - if (!ops->vidioc_reqbufs) - break; if (ret_prio) { ret = ret_prio; break; @@ -925,8 +907,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *p = arg; - if (!ops->vidioc_querybuf) - break; ret = check_fmt(ops, p->type); if (ret) break; @@ -940,8 +920,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *p = arg; - if (!ops->vidioc_qbuf) - break; ret = check_fmt(ops, p->type); if (ret) break; @@ -955,8 +933,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *p = arg; - if (!ops->vidioc_dqbuf) - break; ret = check_fmt(ops, p->type); if (ret) break; @@ -970,8 +946,6 @@ static long __video_do_ioctl(struct file *file, { int *i = arg; - if (!ops->vidioc_overlay) - break; if (ret_prio) { ret = ret_prio; break; @@ -984,8 +958,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_framebuffer *p = arg; - if (!ops->vidioc_g_fbuf) - break; ret = ops->vidioc_g_fbuf(file, fh, arg); if (!ret) { dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", @@ -999,8 +971,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_framebuffer *p = arg; - if (!ops->vidioc_s_fbuf) - break; if (ret_prio) { ret = ret_prio; break; @@ -1015,8 +985,6 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_buf_type i = *(int *)arg; - if (!ops->vidioc_streamon) - break; if (ret_prio) { ret = ret_prio; break; @@ -1029,8 +997,6 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_buf_type i = *(int *)arg; - if (!ops->vidioc_streamoff) - break; if (ret_prio) { ret = ret_prio; break; @@ -1104,9 +1070,6 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id); - if (!ops->vidioc_s_std) - break; - if (ret_prio) { ret = ret_prio; break; @@ -1128,8 +1091,6 @@ static long __video_do_ioctl(struct file *file, { v4l2_std_id *p = arg; - if (!ops->vidioc_querystd) - break; /* * If nothing detected, it should return all supported * Drivers just need to mask the std argument, in order @@ -1163,9 +1124,6 @@ static long __video_do_ioctl(struct file *file, if (ops->vidioc_s_dv_timings) p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS; - if (!ops->vidioc_enum_input) - break; - ret = ops->vidioc_enum_input(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, type=%d, " @@ -1181,8 +1139,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_g_input) - break; ret = ops->vidioc_g_input(file, fh, i); if (!ret) dbgarg(cmd, "value=%d\n", *i); @@ -1192,8 +1148,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_s_input) - break; if (ret_prio) { ret = ret_prio; break; @@ -1208,9 +1162,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_output *p = arg; - if (!ops->vidioc_enum_output) - break; - /* * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS & * CAP_STD here based on ioctl handler provided by the @@ -1237,8 +1188,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_g_output) - break; ret = ops->vidioc_g_output(file, fh, i); if (!ret) dbgarg(cmd, "value=%d\n", *i); @@ -1248,8 +1197,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_s_output) - break; if (ret_prio) { ret = ret_prio; break; @@ -1441,8 +1388,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (!ops->vidioc_enumaudio) - break; ret = ops->vidioc_enumaudio(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " @@ -1456,9 +1401,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (!ops->vidioc_g_audio) - break; - ret = ops->vidioc_g_audio(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " @@ -1472,8 +1414,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (!ops->vidioc_s_audio) - break; if (ret_prio) { ret = ret_prio; break; @@ -1488,8 +1428,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (!ops->vidioc_enumaudout) - break; dbgarg(cmd, "Enum for index=%d\n", p->index); ret = ops->vidioc_enumaudout(file, fh, p); if (!ret) @@ -1502,9 +1440,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (!ops->vidioc_g_audout) - break; - ret = ops->vidioc_g_audout(file, fh, p); if (!ret) dbgarg2("index=%d, name=%s, capability=%d, " @@ -1516,8 +1451,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (!ops->vidioc_s_audout) - break; if (ret_prio) { ret = ret_prio; break; @@ -1533,8 +1466,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_modulator *p = arg; - if (!ops->vidioc_g_modulator) - break; ret = ops->vidioc_g_modulator(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, " @@ -1549,8 +1480,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_modulator *p = arg; - if (!ops->vidioc_s_modulator) - break; if (ret_prio) { ret = ret_prio; break; @@ -1566,9 +1495,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_crop *p = arg; - if (!ops->vidioc_g_crop && !ops->vidioc_g_selection) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); if (ops->vidioc_g_crop) { @@ -1600,9 +1526,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_crop *p = arg; - if (!ops->vidioc_s_crop && !ops->vidioc_s_selection) - break; - if (ret_prio) { ret = ret_prio; break; @@ -1633,9 +1556,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_selection *p = arg; - if (!ops->vidioc_g_selection) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_g_selection(file, fh, p); @@ -1647,9 +1567,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_selection *p = arg; - if (!ops->vidioc_s_selection) - break; - if (ret_prio) { ret = ret_prio; break; @@ -1666,9 +1583,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_cropcap *p = arg; /*FIXME: Should also show v4l2_fract pixelaspect */ - if (!ops->vidioc_cropcap && !ops->vidioc_g_selection) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); if (ops->vidioc_cropcap) { ret = ops->vidioc_cropcap(file, fh, p); @@ -1712,9 +1626,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_jpegcompression *p = arg; - if (!ops->vidioc_g_jpegcomp) - break; - ret = ops->vidioc_g_jpegcomp(file, fh, p); if (!ret) dbgarg(cmd, "quality=%d, APPn=%d, " @@ -1728,8 +1639,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_jpegcompression *p = arg; - if (!ops->vidioc_g_jpegcomp) - break; if (ret_prio) { ret = ret_prio; break; @@ -1745,8 +1654,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_enc_idx *p = arg; - if (!ops->vidioc_g_enc_index) - break; ret = ops->vidioc_g_enc_index(file, fh, p); if (!ret) dbgarg(cmd, "entries=%d, entries_cap=%d\n", @@ -1757,8 +1664,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_encoder_cmd *p = arg; - if (!ops->vidioc_encoder_cmd) - break; if (ret_prio) { ret = ret_prio; break; @@ -1772,8 +1677,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_encoder_cmd *p = arg; - if (!ops->vidioc_try_encoder_cmd) - break; ret = ops->vidioc_try_encoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1783,8 +1686,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_decoder_cmd *p = arg; - if (!ops->vidioc_decoder_cmd) - break; if (ret_prio) { ret = ret_prio; break; @@ -1798,8 +1699,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_decoder_cmd *p = arg; - if (!ops->vidioc_try_decoder_cmd) - break; ret = ops->vidioc_try_decoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1809,8 +1708,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_streamparm *p = arg; - if (!ops->vidioc_g_parm && !vfd->current_norm) - break; if (ops->vidioc_g_parm) { ret = check_fmt(ops, p->type); if (ret) @@ -1838,8 +1735,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_streamparm *p = arg; - if (!ops->vidioc_s_parm) - break; if (ret_prio) { ret = ret_prio; break; @@ -1856,9 +1751,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_tuner *p = arg; - if (!ops->vidioc_g_tuner) - break; - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; ret = ops->vidioc_g_tuner(file, fh, p); @@ -1877,8 +1769,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_tuner *p = arg; - if (!ops->vidioc_s_tuner) - break; if (ret_prio) { ret = ret_prio; break; @@ -1900,9 +1790,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_frequency *p = arg; - if (!ops->vidioc_g_frequency) - break; - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; ret = ops->vidioc_g_frequency(file, fh, p); @@ -1916,8 +1803,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_frequency *p = arg; enum v4l2_tuner_type type; - if (!ops->vidioc_s_frequency) - break; if (ret_prio) { ret = ret_prio; break; @@ -1936,9 +1821,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_sliced_vbi_cap *p = arg; - if (!ops->vidioc_g_sliced_vbi_cap) - break; - /* Clear up to type, everything after type is zerod already */ memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type)); @@ -1950,8 +1832,6 @@ static long __video_do_ioctl(struct file *file, } case VIDIOC_LOG_STATUS: { - if (!ops->vidioc_log_status) - break; if (vfd->v4l2_dev) pr_info("%s: ================= START STATUS =================\n", vfd->v4l2_dev->name); @@ -1966,12 +1846,10 @@ static long __video_do_ioctl(struct file *file, #ifdef CONFIG_VIDEO_ADV_DEBUG struct v4l2_dbg_register *p = arg; - if (ops->vidioc_g_register) { - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else - ret = ops->vidioc_g_register(file, fh, p); - } + if (!capable(CAP_SYS_ADMIN)) + ret = -EPERM; + else + ret = ops->vidioc_g_register(file, fh, p); #endif break; } @@ -1980,12 +1858,10 @@ static long __video_do_ioctl(struct file *file, #ifdef CONFIG_VIDEO_ADV_DEBUG struct v4l2_dbg_register *p = arg; - if (ops->vidioc_s_register) { - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else - ret = ops->vidioc_s_register(file, fh, p); - } + if (!capable(CAP_SYS_ADMIN)) + ret = -EPERM; + else + ret = ops->vidioc_s_register(file, fh, p); #endif break; } @@ -1993,8 +1869,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dbg_chip_ident *p = arg; - if (!ops->vidioc_g_chip_ident) - break; p->ident = V4L2_IDENT_NONE; p->revision = 0; ret = ops->vidioc_g_chip_ident(file, fh, p); @@ -2007,8 +1881,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_hw_freq_seek *p = arg; enum v4l2_tuner_type type; - if (!ops->vidioc_s_hw_freq_seek) - break; if (ret_prio) { ret = ret_prio; break; @@ -2028,9 +1900,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_frmsizeenum *p = arg; - if (!ops->vidioc_enum_framesizes) - break; - ret = ops->vidioc_enum_framesizes(file, fh, p); dbgarg(cmd, "index=%d, pixelformat=%c%c%c%c, type=%d ", @@ -2064,9 +1933,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_frmivalenum *p = arg; - if (!ops->vidioc_enum_frameintervals) - break; - ret = ops->vidioc_enum_frameintervals(file, fh, p); dbgarg(cmd, "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ", @@ -2099,9 +1965,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_enum_preset *p = arg; - if (!ops->vidioc_enum_dv_presets) - break; - ret = ops->vidioc_enum_dv_presets(file, fh, p); if (!ret) dbgarg(cmd, @@ -2115,8 +1978,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (!ops->vidioc_s_dv_preset) - break; if (ret_prio) { ret = ret_prio; break; @@ -2130,9 +1991,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (!ops->vidioc_g_dv_preset) - break; - ret = ops->vidioc_g_dv_preset(file, fh, p); if (!ret) dbgarg(cmd, "preset=%d\n", p->preset); @@ -2142,9 +2000,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (!ops->vidioc_query_dv_preset) - break; - ret = ops->vidioc_query_dv_preset(file, fh, p); if (!ret) dbgarg(cmd, "preset=%d\n", p->preset); @@ -2154,8 +2009,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_timings *p = arg; - if (!ops->vidioc_s_dv_timings) - break; if (ret_prio) { ret = ret_prio; break; @@ -2188,9 +2041,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_timings *p = arg; - if (!ops->vidioc_g_dv_timings) - break; - ret = ops->vidioc_g_dv_timings(file, fh, p); if (!ret) { switch (p->type) { @@ -2222,9 +2072,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_event *ev = arg; - if (!ops->vidioc_subscribe_event) - break; - ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK); if (ret < 0) { dbgarg(cmd, "no pending events?"); @@ -2241,9 +2088,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_event_subscription *sub = arg; - if (!ops->vidioc_subscribe_event) - break; - ret = ops->vidioc_subscribe_event(fh, sub); if (ret < 0) { dbgarg(cmd, "failed, ret=%ld", ret); @@ -2256,9 +2100,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_event_subscription *sub = arg; - if (!ops->vidioc_unsubscribe_event) - break; - ret = ops->vidioc_unsubscribe_event(fh, sub); if (ret < 0) { dbgarg(cmd, "failed, ret=%ld", ret); @@ -2271,8 +2112,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_create_buffers *create = arg; - if (!ops->vidioc_create_bufs) - break; if (ret_prio) { ret = ret_prio; break; @@ -2290,8 +2129,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *b = arg; - if (!ops->vidioc_prepare_buf) - break; ret = check_fmt(ops, b->type); if (ret) break; diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index d00b9d3511f2..a5ecec66d3c8 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -126,6 +126,7 @@ struct video_device /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; + DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); /* serialization lock */ DECLARE_BITMAP(dont_use_lock, BASE_VIDIOC_PRIVATE); @@ -184,6 +185,16 @@ static inline void v4l2_dont_use_lock(struct video_device *vdev, unsigned int cm set_bit(_IOC_NR(cmd), vdev->dont_use_lock); } +/* Mark that this command isn't implemented, must be called before + video_device_register. See also the comments in determine_valid_ioctls(). + This function allows drivers to provide just one v4l2_ioctl_ops struct, but + disable ioctls based on the specific card that is actually found. */ +static inline void v4l2_dont_use_cmd(struct video_device *vdev, unsigned int cmd) +{ + if (_IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) + set_bit(_IOC_NR(cmd), vdev->valid_ioctls); +} + /* helper functions to access driver private data. */ static inline void *video_get_drvdata(struct video_device *vdev) { -- cgit v1.2.3 From 6539799599f50e9ce36da784ee0f545540a9732c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 10 May 2012 03:16:14 -0300 Subject: [media] tea575x-tuner: mark VIDIOC_S_HW_FREQ_SEEK as an invalid ioctl The tea575x-tuner framework can support the VIDIOC_S_HW_FREQ_SEEK for only some of the tea575x-based boards. Mark this ioctl as invalid if the board doesn't support it. This fixes an issue with S_HW_FREQ_SEEK in combination with priority handling: since the priority check is done first it could return -EBUSY, even though calling the S_HW_FREQ_SEEK ioctl would return -ENOTTY. It should always return ENOTTY in such a case. Signed-off-by: Hans Verkuil Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- sound/i2c/other/tea575x-tuner.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index a63faec5e7fd..6e9ca7bd0f11 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -375,6 +375,9 @@ int snd_tea575x_init(struct snd_tea575x *tea) tea->vd.v4l2_dev = tea->v4l2_dev; tea->vd.ctrl_handler = &tea->ctrl_handler; set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags); + /* disable hw_freq_seek if we can't use it */ + if (tea->cannot_read_data) + v4l2_dont_use_cmd(&tea->vd, VIDIOC_S_HW_FREQ_SEEK); v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); -- cgit v1.2.3 From 4b902fec7cd838d2376517455acb03a83898e262 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 10 May 2012 05:40:50 -0300 Subject: [media] v4l2-ioctl: handle priority handling based on a table lookup Rather than checking the priority for each ioctl that needs to, just mark such ioctls in the table and do it only once. Signed-off-by: Hans Verkuil Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ioctl.c | 182 +++++++++------------------------------ 1 file changed, 41 insertions(+), 141 deletions(-) diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 3df1f80f1c5c..623d280ce095 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -189,6 +189,8 @@ struct v4l2_ioctl_info { const char * const name; }; +/* This control needs a priority check */ +#define INFO_FL_PRIO (1 << 0) /* This control can be valid if the filehandle passes a control handler. */ #define INFO_FL_CTRL (1 << 1) @@ -202,80 +204,82 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO(VIDIOC_QUERYCAP, 0), IOCTL_INFO(VIDIOC_ENUM_FMT, 0), IOCTL_INFO(VIDIOC_G_FMT, 0), - IOCTL_INFO(VIDIOC_S_FMT, 0), - IOCTL_INFO(VIDIOC_REQBUFS, 0), + IOCTL_INFO(VIDIOC_S_FMT, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_REQBUFS, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_QUERYBUF, 0), IOCTL_INFO(VIDIOC_G_FBUF, 0), - IOCTL_INFO(VIDIOC_S_FBUF, 0), - IOCTL_INFO(VIDIOC_OVERLAY, 0), + IOCTL_INFO(VIDIOC_S_FBUF, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_OVERLAY, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_QBUF, 0), IOCTL_INFO(VIDIOC_DQBUF, 0), - IOCTL_INFO(VIDIOC_STREAMON, 0), - IOCTL_INFO(VIDIOC_STREAMOFF, 0), + IOCTL_INFO(VIDIOC_STREAMON, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_STREAMOFF, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_PARM, 0), - IOCTL_INFO(VIDIOC_S_PARM, 0), + IOCTL_INFO(VIDIOC_S_PARM, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_STD, 0), - IOCTL_INFO(VIDIOC_S_STD, 0), + IOCTL_INFO(VIDIOC_S_STD, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_ENUMSTD, 0), IOCTL_INFO(VIDIOC_ENUMINPUT, 0), IOCTL_INFO(VIDIOC_G_CTRL, INFO_FL_CTRL), - IOCTL_INFO(VIDIOC_S_CTRL, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_S_CTRL, INFO_FL_PRIO | INFO_FL_CTRL), IOCTL_INFO(VIDIOC_G_TUNER, 0), - IOCTL_INFO(VIDIOC_S_TUNER, 0), + IOCTL_INFO(VIDIOC_S_TUNER, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_AUDIO, 0), - IOCTL_INFO(VIDIOC_S_AUDIO, 0), + IOCTL_INFO(VIDIOC_S_AUDIO, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_QUERYCTRL, INFO_FL_CTRL), IOCTL_INFO(VIDIOC_QUERYMENU, INFO_FL_CTRL), IOCTL_INFO(VIDIOC_G_INPUT, 0), - IOCTL_INFO(VIDIOC_S_INPUT, 0), + IOCTL_INFO(VIDIOC_S_INPUT, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_OUTPUT, 0), - IOCTL_INFO(VIDIOC_S_OUTPUT, 0), + IOCTL_INFO(VIDIOC_S_OUTPUT, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_ENUMOUTPUT, 0), IOCTL_INFO(VIDIOC_G_AUDOUT, 0), - IOCTL_INFO(VIDIOC_S_AUDOUT, 0), + IOCTL_INFO(VIDIOC_S_AUDOUT, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_MODULATOR, 0), - IOCTL_INFO(VIDIOC_S_MODULATOR, 0), + IOCTL_INFO(VIDIOC_S_MODULATOR, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_FREQUENCY, 0), - IOCTL_INFO(VIDIOC_S_FREQUENCY, 0), + IOCTL_INFO(VIDIOC_S_FREQUENCY, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_CROPCAP, 0), IOCTL_INFO(VIDIOC_G_CROP, 0), - IOCTL_INFO(VIDIOC_S_CROP, 0), + IOCTL_INFO(VIDIOC_S_CROP, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_SELECTION, 0), - IOCTL_INFO(VIDIOC_S_SELECTION, 0), + IOCTL_INFO(VIDIOC_S_SELECTION, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_JPEGCOMP, 0), - IOCTL_INFO(VIDIOC_S_JPEGCOMP, 0), + IOCTL_INFO(VIDIOC_S_JPEGCOMP, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_QUERYSTD, 0), IOCTL_INFO(VIDIOC_TRY_FMT, 0), IOCTL_INFO(VIDIOC_ENUMAUDIO, 0), IOCTL_INFO(VIDIOC_ENUMAUDOUT, 0), IOCTL_INFO(VIDIOC_G_PRIORITY, 0), - IOCTL_INFO(VIDIOC_S_PRIORITY, 0), + IOCTL_INFO(VIDIOC_S_PRIORITY, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, 0), IOCTL_INFO(VIDIOC_LOG_STATUS, 0), IOCTL_INFO(VIDIOC_G_EXT_CTRLS, INFO_FL_CTRL), - IOCTL_INFO(VIDIOC_S_EXT_CTRLS, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_S_EXT_CTRLS, INFO_FL_PRIO | INFO_FL_CTRL), IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, 0), IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, 0), IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, 0), IOCTL_INFO(VIDIOC_G_ENC_INDEX, 0), - IOCTL_INFO(VIDIOC_ENCODER_CMD, 0), + IOCTL_INFO(VIDIOC_ENCODER_CMD, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, 0), - IOCTL_INFO(VIDIOC_DECODER_CMD, 0), + IOCTL_INFO(VIDIOC_DECODER_CMD, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, 0), +#ifdef CONFIG_VIDEO_ADV_DEBUG IOCTL_INFO(VIDIOC_DBG_S_REGISTER, 0), IOCTL_INFO(VIDIOC_DBG_G_REGISTER, 0), +#endif IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT, 0), - IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, 0), + IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS, 0), - IOCTL_INFO(VIDIOC_S_DV_PRESET, 0), + IOCTL_INFO(VIDIOC_S_DV_PRESET, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_DV_PRESET, 0), IOCTL_INFO(VIDIOC_QUERY_DV_PRESET, 0), - IOCTL_INFO(VIDIOC_S_DV_TIMINGS, 0), + IOCTL_INFO(VIDIOC_S_DV_TIMINGS, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_DV_TIMINGS, 0), IOCTL_INFO(VIDIOC_DQEVENT, 0), IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, 0), IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, 0), - IOCTL_INFO(VIDIOC_CREATE_BUFS, 0), + IOCTL_INFO(VIDIOC_CREATE_BUFS, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_PREPARE_BUF, 0), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -509,7 +513,6 @@ static long __video_do_ioctl(struct file *file, void *fh = file->private_data; struct v4l2_fh *vfh = NULL; int use_fh_prio = 0; - long ret_prio = 0; long ret = -ENOTTY; if (ops == NULL) { @@ -521,8 +524,6 @@ static long __video_do_ioctl(struct file *file, if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { vfh = file->private_data; use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); - if (use_fh_prio) - ret_prio = v4l2_prio_check(vfd->prio, vfh->prio); } if (v4l2_is_known_ioctl(cmd)) { @@ -531,6 +532,12 @@ static long __video_do_ioctl(struct file *file, if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) return -ENOTTY; + + if (use_fh_prio && (info->flags & INFO_FL_PRIO)) { + ret = v4l2_prio_check(vfd->prio, vfh->prio); + if (ret) + return ret; + } } if ((vfd->debug & V4L2_DEBUG_IOCTL) && @@ -579,14 +586,11 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_priority *p = arg; - if (!ops->vidioc_s_priority && !use_fh_prio) - break; dbgarg(cmd, "setting priority to %d\n", *p); if (ops->vidioc_s_priority) ret = ops->vidioc_s_priority(file, fh, *p); else - ret = ret_prio ? ret_prio : - v4l2_prio_change(&vfd->v4l2_dev->prio, + ret = v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p); break; } @@ -715,10 +719,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_format *f = (struct v4l2_format *)arg; - if (ret_prio) { - ret = ret_prio; - break; - } ret = -EINVAL; /* FIXME: Should be one dump per type */ @@ -885,10 +885,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_requestbuffers *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } ret = check_fmt(ops, p->type); if (ret) break; @@ -946,10 +942,6 @@ static long __video_do_ioctl(struct file *file, { int *i = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_overlay(file, fh, *i); break; @@ -971,10 +963,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_framebuffer *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", p->capability, p->flags, (unsigned long)p->base); v4l_print_pix_fmt(vfd, &p->fmt); @@ -985,10 +973,6 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_buf_type i = *(int *)arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); ret = ops->vidioc_streamon(file, fh, i); break; @@ -997,10 +981,6 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_buf_type i = *(int *)arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); ret = ops->vidioc_streamoff(file, fh, i); break; @@ -1070,10 +1050,6 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id); - if (ret_prio) { - ret = ret_prio; - break; - } ret = -EINVAL; norm = (*id) & vfd->tvnorms; if (vfd->tvnorms && !norm) /* Check if std is supported */ @@ -1148,10 +1124,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_s_input(file, fh, *i); break; @@ -1197,10 +1169,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_s_output(file, fh, *i); break; @@ -1270,10 +1238,6 @@ static long __video_do_ioctl(struct file *file, if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls) break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); @@ -1329,10 +1293,6 @@ static long __video_do_ioctl(struct file *file, if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls) break; - if (ret_prio) { - ret = ret_prio; - break; - } v4l_print_ext_ctrls(cmd, vfd, p, 1); if (vfh && vfh->ctrl_handler) ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p); @@ -1414,10 +1374,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " "mode=0x%x\n", p->index, p->name, p->capability, p->mode); @@ -1451,10 +1407,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "index=%d, name=%s, capability=%d, " "mode=%d\n", p->index, p->name, p->capability, p->mode); @@ -1480,10 +1432,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_modulator *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "index=%d, name=%s, capability=%d, " "rangelow=%d, rangehigh=%d, txsubchans=%d\n", p->index, p->name, p->capability, p->rangelow, @@ -1526,10 +1474,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_crop *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); dbgrect(vfd, "", &p->c); @@ -1567,10 +1511,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_selection *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); dbgrect(vfd, "", &p->r); @@ -1639,10 +1579,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_jpegcompression *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, " "COM_len=%d, jpeg_markers=%d\n", p->quality, p->APPn, p->APP_len, @@ -1664,10 +1600,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_encoder_cmd *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } ret = ops->vidioc_encoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1686,10 +1618,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_decoder_cmd *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } ret = ops->vidioc_decoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1735,10 +1663,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_streamparm *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } ret = check_fmt(ops, p->type); if (ret) break; @@ -1769,10 +1693,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_tuner *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, "index=%d, name=%s, type=%d, " @@ -1803,10 +1723,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_frequency *p = arg; enum v4l2_tuner_type type; - if (ret_prio) { - ret = ret_prio; - break; - } type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", @@ -1881,10 +1797,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_hw_freq_seek *p = arg; enum v4l2_tuner_type type; - if (ret_prio) { - ret = ret_prio; - break; - } type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, @@ -1978,11 +1890,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } - dbgarg(cmd, "preset=%d\n", p->preset); ret = ops->vidioc_s_dv_preset(file, fh, p); break; @@ -2009,11 +1916,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_timings *p = arg; - if (ret_prio) { - ret = ret_prio; - break; - } - switch (p->type) { case V4L2_DV_BT_656_1120: dbgarg2("bt-656/1120:interlaced=%d, pixelclock=%lld," @@ -2112,10 +2014,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_create_buffers *create = arg; - if (ret_prio) { - ret = ret_prio; - break; - } ret = check_fmt(ops, create->format.type); if (ret) break; @@ -2141,7 +2039,9 @@ static long __video_do_ioctl(struct file *file, default: if (!ops->vidioc_default) break; - ret = ops->vidioc_default(file, fh, ret_prio >= 0, cmd, arg); + ret = ops->vidioc_default(file, fh, use_fh_prio ? + v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, + cmd, arg); break; } /* switch */ -- cgit v1.2.3 From 5126f2590bee412e3053de851cb07f531e4be36a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 10 May 2012 04:57:22 -0300 Subject: [media] v4l2-dev: add flag to have the core lock all file operations This used to be the default if the lock pointer was set, but now that lock is by default only used for ioctl serialization. Those drivers that already used core locking have this flag set explicitly, except for some drivers where it was obvious that there was no need to serialize any file operations other than ioctl. The drivers that didn't need this flag were: drivers/media/radio/dsbr100.c drivers/media/radio/radio-isa.c drivers/media/radio/radio-keene.c drivers/media/radio/radio-miropcm20.c drivers/media/radio/radio-mr800.c drivers/media/radio/radio-tea5764.c drivers/media/radio/radio-timb.c drivers/media/video/vivi.c sound/i2c/other/tea575x-tuner.c The other drivers that use core locking and where it was not immediately obvious that this flag wasn't needed were changed so that the flag is set together with a comment that that driver needs work to avoid having to set that flag. This will often involve taking the core lock in the fops themselves. Eventually this flag should go and it should not be used in new drivers. There are a few reasons why we want to avoid core locking of non-ioctl fops: in the case of mmap this can lead to a deadlock in rare situations since when mmap is called the mmap_sem is held and it is possible for other parts of the code to take that lock as well (copy_from_user()/copy_to_user() perform a down_read(&mm->mmap_sem) when a page fault occurs). It is very unlikely that that happens since the core lock serializes all fops, but the kernel warns about it if lock validation is turned on. For poll it is also undesirable to take the core lock as that can introduce increased latency. The same is true for read/write. While it was possible to make flags or something to turn on/off taking the core lock for each file operation, in practice it is much simpler to just not take it at all except for ioctl and leave it to the driver to take the lock. There are only a handful fops compared to the zillion ioctls we have. I also wanted to make it obvious which drivers still take the lock for all fops, so that's why I chose to have drivers set it explicitly. Signed-off-by: Hans Verkuil Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 4 ++++ drivers/media/radio/wl128x/fmdrv_v4l2.c | 4 ++++ drivers/media/video/blackfin/bfin_capture.c | 4 ++++ drivers/media/video/cpia2/cpia2_v4l.c | 4 ++++ drivers/media/video/cx231xx/cx231xx-video.c | 4 ++++ drivers/media/video/davinci/vpbe_display.c | 4 ++++ drivers/media/video/davinci/vpif_capture.c | 4 ++++ drivers/media/video/davinci/vpif_display.c | 4 ++++ drivers/media/video/em28xx/em28xx-video.c | 4 ++++ drivers/media/video/fsl-viu.c | 4 ++++ drivers/media/video/ivtv/ivtv-streams.c | 4 ++++ drivers/media/video/mem2mem_testdev.c | 4 ++++ drivers/media/video/mx2_emmaprp.c | 4 ++++ drivers/media/video/s2255drv.c | 4 ++++ drivers/media/video/s5p-fimc/fimc-capture.c | 4 ++++ drivers/media/video/s5p-fimc/fimc-core.c | 4 ++++ drivers/media/video/s5p-g2d/g2d.c | 4 ++++ drivers/media/video/s5p-jpeg/jpeg-core.c | 8 +++++++ drivers/media/video/s5p-mfc/s5p_mfc.c | 6 +++++ drivers/media/video/s5p-tv/mixer_video.c | 4 ++++ drivers/media/video/sh_vou.c | 4 ++++ drivers/media/video/soc_camera.c | 4 ++++ drivers/media/video/tm6000/tm6000-video.c | 4 ++++ drivers/media/video/usbvision/usbvision-video.c | 4 ++++ drivers/media/video/v4l2-dev.c | 32 +++++++++++++++---------- drivers/staging/media/dt3155v4l/dt3155v4l.c | 4 ++++ include/media/v4l2-dev.h | 3 +++ 27 files changed, 129 insertions(+), 12 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 71f8e018e564..8d7df1a0bcd0 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -511,6 +511,10 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, vfd->fops = &video_fops; vfd->ioctl_ops = &dev->ext_vv_data->ops; vfd->release = video_device_release; + /* Locking in file operations other than ioctl should be done by + the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->v4l2_lock; vfd->tvnorms = 0; for (i = 0; i < dev->ext_vv_data->num_stds; i++) diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index 077d369a0173..080b96a61f1a 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -518,6 +518,10 @@ int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) video_set_drvdata(gradio_dev, fmdev); gradio_dev->lock = &fmdev->mutex; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &gradio_dev->flags); /* Register with V4L2 subsystem as RADIO device */ if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) { diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c index 514fcf742f5a..0aba45e34f70 100644 --- a/drivers/media/video/blackfin/bfin_capture.c +++ b/drivers/media/video/blackfin/bfin_capture.c @@ -942,6 +942,10 @@ static int __devinit bcap_probe(struct platform_device *pdev) INIT_LIST_HEAD(&bcap_dev->dma_queue); vfd->lock = &bcap_dev->mutex; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); /* register video device */ ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index bb4f1d0de829..55e92902a76c 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -1147,6 +1147,10 @@ int cpia2_register_camera(struct camera_data *cam) cam->vdev.ctrl_handler = hdl; cam->vdev.v4l2_dev = &cam->v4l2_dev; set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &cam->vdev.flags); reset_camera_struct_v4l(cam); diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 7f916f0685e9..2a04558699f8 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -2561,6 +2561,10 @@ static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c index 1f3b1c729252..e106b72810a9 100644 --- a/drivers/media/video/davinci/vpbe_display.c +++ b/drivers/media/video/davinci/vpbe_display.c @@ -1618,6 +1618,10 @@ static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, vbd->ioctl_ops = &vpbe_ioctl_ops; vbd->minor = -1; vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vbd->flags); vbd->lock = &vpbe_display_layer->opslock; if (disp_dev->vpbe_dev->current_timings.timings_type & diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 6504e40a31dd..96046957bf21 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -2228,6 +2228,10 @@ static __init int vpif_probe(struct platform_device *pdev) common = &(ch->common[VPIF_VIDEO_INDEX]); spin_lock_init(&common->irqlock); mutex_init(&common->lock); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags); ch->video_dev->lock = &common->lock; /* Initialize prio member of channel object */ v4l2_prio_init(&ch->prio); diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 7fa34b4fae26..e6488ee7db18 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -1778,6 +1778,10 @@ static __init int vpif_probe(struct platform_device *pdev) v4l2_prio_init(&ch->prio); ch->common[VPIF_VIDEO_INDEX].fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags); ch->video_dev->lock = &common->lock; /* register video device */ diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index bcc41603c193..308a1dd08cfb 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -2495,6 +2495,10 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c index 27e3e0c0b219..777486f7cadb 100644 --- a/drivers/media/video/fsl-viu.c +++ b/drivers/media/video/fsl-viu.c @@ -1544,6 +1544,10 @@ static int __devinit viu_of_probe(struct platform_device *op) /* initialize locks */ mutex_init(&viu_dev->lock); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &viu_dev->vdev->flags); viu_dev->vdev->lock = &viu_dev->lock; spin_lock_init(&viu_dev->slock); diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 7ea5ca7f012b..6738592aa35d 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -228,6 +228,10 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->release = video_device_release; s->vdev->tvnorms = V4L2_STD_ALL; s->vdev->lock = &itv->serialize_lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &s->vdev->flags); set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags); ivtv_set_funcs(s->vdev); return 0; diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 12897e8a3314..ee3efbd83bdb 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -958,6 +958,10 @@ static int m2mtest_probe(struct platform_device *pdev) } *vfd = m2mtest_videodev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->dev_mutex; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c index 55ac1735e85b..0bd5815de369 100644 --- a/drivers/media/video/mx2_emmaprp.c +++ b/drivers/media/video/mx2_emmaprp.c @@ -904,6 +904,10 @@ static int emmaprp_probe(struct platform_device *pdev) } *vfd = emmaprp_videodev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &pcdev->dev_mutex; video_set_drvdata(vfd, pcdev); diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 37845def41c5..ea974fadb5e2 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -1948,6 +1948,10 @@ static int s2255_probe_v4l(struct s2255_dev *dev) /* register 4 video devices */ channel->vdev = template; channel->vdev.lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &channel->vdev.flags); channel->vdev.v4l2_dev = &dev->v4l2_dev; video_set_drvdata(&channel->vdev, channel); if (video_nr == -1) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index dc18ba510986..72d51504ed21 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1516,6 +1516,10 @@ int fimc_register_capture_device(struct fimc_dev *fimc, vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); video_set_drvdata(vfd, fimc); vid_cap = &fimc->vid_cap; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 7b90a897beeb..c58dd9f8ce67 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1520,6 +1520,10 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev)); video_set_drvdata(vfd, fimc); diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index 789de74014e5..02605cecfd65 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -762,6 +762,10 @@ static int g2d_probe(struct platform_device *pdev) goto unreg_v4l2_dev; } *vfd = g2d_videodev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->mutex; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index 5a49c307f9c1..ecf7b0b04c78 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -1386,6 +1386,10 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_encoder->release = video_device_release; jpeg->vfd_encoder->lock = &jpeg->lock; jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_encoder->flags); ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { @@ -1413,6 +1417,10 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_decoder->release = video_device_release; jpeg->vfd_decoder->lock = &jpeg->lock; jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev; + /* Locking in file operations other than ioctl should be done by the driver, + not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_decoder->flags); ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1); if (ret) { diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index 83fe461af263..76008549b3f1 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -1048,6 +1048,10 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->ioctl_ops = get_dec_v4l2_ioctl_ops(); vfd->release = video_device_release, vfd->lock = &dev->mfc_mutex; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->v4l2_dev = &dev->v4l2_dev; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; @@ -1072,6 +1076,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->ioctl_ops = get_enc_v4l2_ioctl_ops(); vfd->release = video_device_release, vfd->lock = &dev->mfc_mutex; + /* This should not be necessary */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->v4l2_dev = &dev->v4l2_dev; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); dev->vfd_enc = vfd; diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index f7ca5cc143c6..c0eadd75c9ac 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -1069,6 +1069,10 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags); video_set_drvdata(&layer->vfd, layer); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &layer->vfd.flags); layer->vfd.lock = &layer->mutex; layer->vfd.v4l2_dev = &mdev->v4l2_dev; diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c index 9644bd861abc..8fd1874382c6 100644 --- a/drivers/media/video/sh_vou.c +++ b/drivers/media/video/sh_vou.c @@ -1390,6 +1390,10 @@ static int __devinit sh_vou_probe(struct platform_device *pdev) vdev->v4l2_dev = &vou_dev->v4l2_dev; vdev->release = video_device_release; vdev->lock = &vou_dev->fop_lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); vou_dev->vdev = vdev; video_set_drvdata(vdev, vou_dev); diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index eb25756a07af..c27bb6d0a136 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -1425,6 +1425,10 @@ static int video_dev_create(struct soc_camera_device *icd) vdev->tvnorms = V4L2_STD_UNKNOWN; vdev->ctrl_handler = &icd->ctrl_handler; vdev->lock = &icd->video_lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); icd->vdev = vdev; diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c index 1ba26d5b2ba6..375f26abd916 100644 --- a/drivers/media/video/tm6000/tm6000-video.c +++ b/drivers/media/video/tm6000/tm6000-video.c @@ -1731,6 +1731,10 @@ static struct video_device *vdev_init(struct tm6000_core *dev, vfd->release = video_device_release; vfd->debug = tm6000_debug; vfd->lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 5a74f5e07d7d..9bd8f084f348 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -1296,6 +1296,10 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, if (NULL == vdev) return NULL; *vdev = *vdev_template; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); vdev->lock = &usbvision->v4l2_lock; vdev->v4l2_dev = &usbvision->v4l2_dev; snprintf(vdev->name, sizeof(vdev->name), "%s", name); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index b1f0923212e6..2c4feffa4939 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -274,11 +274,12 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, if (!vdev->fops->read) return -EINVAL; - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->read(filp, buf, sz, off); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -291,11 +292,12 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, if (!vdev->fops->write) return -EINVAL; - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->write(filp, buf, sz, off); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -307,11 +309,11 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) if (!vdev->fops->poll) return DEFAULT_POLLMASK; - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_lock(vdev->lock); if (video_is_registered(vdev)) ret = vdev->fops->poll(filp, poll); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -399,11 +401,12 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) if (!vdev->fops->mmap) return ret; - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->mmap(filp, vm); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -426,7 +429,8 @@ static int v4l2_open(struct inode *inode, struct file *filp) video_get(vdev); mutex_unlock(&videodev_lock); if (vdev->fops->open) { - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) { + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) { ret = -ERESTARTSYS; goto err; } @@ -434,7 +438,7 @@ static int v4l2_open(struct inode *inode, struct file *filp) ret = vdev->fops->open(filp); else ret = -ENODEV; - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); } @@ -452,10 +456,10 @@ static int v4l2_release(struct inode *inode, struct file *filp) int ret = 0; if (vdev->fops->release) { - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_lock(vdev->lock); vdev->fops->release(filp); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); } /* decrease the refcount unconditionally since the release() @@ -831,6 +835,10 @@ int __video_register_device(struct video_device *vdev, int type, int nr, WARN_ON(video_device[vdev->minor] != NULL); vdev->index = get_index(vdev); mutex_unlock(&videodev_lock); + /* if no lock was passed, then make sure the LOCK_ALL_FOPS bit is + clear and warn if it wasn't. */ + if (vdev->lock == NULL) + WARN_ON(test_and_clear_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)); if (vdev->ioctl_ops) determine_valid_ioctls(vdev); diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c index 280c84ec4cc2..c365cdf714ea 100644 --- a/drivers/staging/media/dt3155v4l/dt3155v4l.c +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -898,6 +898,10 @@ dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&pd->dmaq); mutex_init(&pd->mux); pd->vdev->lock = &pd->mux; /* for locking v4l2_file_operations */ + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &pd->vdev->flags); spin_lock_init(&pd->lock); pd->csr2 = csr2_init; pd->config = config_init; diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index a5ecec66d3c8..b604a7a50940 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -39,6 +39,9 @@ struct v4l2_ctrl_handler; #define V4L2_FL_USES_V4L2_FH (1) /* Use the prio field of v4l2_fh for core priority checking */ #define V4L2_FL_USE_FH_PRIO (2) +/* If ioctl core locking is in use, then apply that also to all + file operations. */ +#define V4L2_FL_LOCK_ALL_FOPS (3) /* Priority helper functions */ -- cgit v1.2.3 From 1dd8728e2147820f653e5ea92802002fb26131f1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 10 May 2012 05:04:41 -0300 Subject: [media] v4l2-framework.txt: document v4l2_dont_use_cmd Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index e1e6a01d7ac6..0dace876b978 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -619,6 +619,16 @@ in your v4l2_file_operations struct. Do not use .ioctl! This is deprecated and will go away in the future. +In some cases you want to tell the core that a function you had specified in +your v4l2_ioctl_ops should be ignored. You can mark such ioctls by calling this +function before video_device_register is called: + +void v4l2_dont_use_cmd(struct video_device *vdev, unsigned int cmd); + +This tends to be needed if based on external factors (e.g. which card is +being used) you want to turns off certain features in v4l2_ioctl_ops without +having to make a new struct. + The v4l2_file_operations struct is a subset of file_operations. The main difference is that the inode argument is omitted since it is never used. -- cgit v1.2.3 From a67e17221429c0322e543ae4bf0b25259c3416a5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 8 May 2012 14:47:39 -0300 Subject: [media] videobuf2: Fix a bug in fileio emulation error handling Various error paths in fileio_init where not setting the request-count to 0 when unrequesting the buffers on error to init the fileio emulation. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 3786d88183eb..9d4e9edbd2e7 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -1857,7 +1857,6 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) * (multiplane buffers are not supported). */ if (q->bufs[0]->num_planes != 1) { - fileio->req.count = 0; ret = -EBUSY; goto err_reqbufs; } @@ -1904,6 +1903,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) return ret; err_reqbufs: + fileio->req.count = 0; vb2_reqbufs(q, &fileio->req); err_kfree: -- cgit v1.2.3 From ceede9fa8939e40ad0ddb4ad1355f45c6f1d3478 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 May 2012 04:43:12 -0300 Subject: [media] pwc: Fix locking My last locking rework for pwc mistakenly assumed that videbuf2 does its own locking, but it does not! This patch fixes the missing locking by moving over the the video_device lock, and introducing a separate lock for the videobuf2_queue. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-if.c | 190 ++++++++++++++++++++++++-------------- drivers/media/video/pwc/pwc-v4l.c | 143 ++++++++++++++-------------- drivers/media/video/pwc/pwc.h | 21 +++-- 3 files changed, 201 insertions(+), 153 deletions(-) diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index f3370a87cbc0..998e809765a7 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -357,6 +357,7 @@ handler_end: PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); } +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ static int pwc_isoc_init(struct pwc_device *pdev) { struct usb_device *udev; @@ -366,9 +367,6 @@ static int pwc_isoc_init(struct pwc_device *pdev) struct usb_host_interface *idesc = NULL; int compression = 0; /* 0..3 = uncompressed..high */ - if (pdev->iso_init) - return 0; - pdev->vsync = 0; pdev->vlast_packet_size = 0; pdev->fill_buf = NULL; @@ -418,7 +416,6 @@ retry: urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { PWC_ERROR("Failed to allocate urb %d\n", i); - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); return -ENOMEM; } @@ -435,7 +432,6 @@ retry: &urb->transfer_dma); if (urb->transfer_buffer == NULL) { PWC_ERROR("Failed to allocate urb buffer %d\n", i); - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); return -ENOMEM; } @@ -455,13 +451,11 @@ retry: ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL); if (ret == -ENOSPC && compression < 3) { compression++; - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); goto retry; } if (ret) { PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret); - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); return ret; } @@ -469,7 +463,6 @@ retry: } /* All is done... */ - pdev->iso_init = 1; PWC_DEBUG_OPEN("<< pwc_isoc_init()\n"); return 0; } @@ -507,21 +500,19 @@ static void pwc_iso_free(struct pwc_device *pdev) } } +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ static void pwc_isoc_cleanup(struct pwc_device *pdev) { PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); - if (pdev->iso_init == 0) - return; - pwc_iso_stop(pdev); pwc_iso_free(pdev); usb_set_interface(pdev->udev, 0, 0); - pdev->iso_init = 0; PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); } +/* Must be called with vb_queue_lock hold */ static void pwc_cleanup_queued_bufs(struct pwc_device *pdev) { unsigned long flags = 0; @@ -573,18 +564,13 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type) int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file) { - int r = 0; - - mutex_lock(&pdev->capt_file_lock); if (pdev->capt_file != NULL && - pdev->capt_file != file) { - r = -EBUSY; - goto leave; - } + pdev->capt_file != file) + return -EBUSY; + pdev->capt_file = file; -leave: - mutex_unlock(&pdev->capt_file_lock); - return r; + + return 0; } static void pwc_video_release(struct v4l2_device *v) @@ -592,6 +578,7 @@ static void pwc_video_release(struct v4l2_device *v) struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); v4l2_ctrl_handler_free(&pdev->ctrl_handler); + v4l2_device_unregister(&pdev->v4l2_dev); kfree(pdev->ctrl_buf); kfree(pdev); } @@ -600,10 +587,25 @@ static int pwc_video_close(struct file *file) { struct pwc_device *pdev = video_drvdata(file); + /* + * If we're still streaming vb2_queue_release will call stream_stop + * so we must take both the v4l2_lock and the vb_queue_lock. + */ + if (mutex_lock_interruptible(&pdev->v4l2_lock)) + return -ERESTARTSYS; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) { + mutex_unlock(&pdev->v4l2_lock); + return -ERESTARTSYS; + } + if (pdev->capt_file == file) { vb2_queue_release(&pdev->vb_queue); pdev->capt_file = NULL; } + + mutex_unlock(&pdev->vb_queue_lock); + mutex_unlock(&pdev->v4l2_lock); + return v4l2_fh_release(file); } @@ -611,44 +613,81 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct pwc_device *pdev = video_drvdata(file); + int lock_v4l2 = 0; + ssize_t ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret) + goto out; + + /* stream_start will get called so we must take the v4l2_lock */ + if (pdev->vb_queue.fileio == NULL) + lock_v4l2 = 1; - return vb2_read(&pdev->vb_queue, buf, count, ppos, - file->f_flags & O_NONBLOCK); + /* Use try_lock, since we're taking the locks in the *wrong* order! */ + if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) { + ret = -ERESTARTSYS; + goto out; + } + ret = vb2_read(&pdev->vb_queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (lock_v4l2) + mutex_unlock(&pdev->v4l2_lock); +out: + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static unsigned int pwc_video_poll(struct file *file, poll_table *wait) { struct pwc_device *pdev = video_drvdata(file); + struct vb2_queue *q = &pdev->vb_queue; unsigned long req_events = poll_requested_events(wait); + unsigned int ret = POLL_ERR; + int lock_v4l2 = 0; - if (!pdev->udev) + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) return POLL_ERR; + /* Will this start fileio and thus call start_stream? */ if ((req_events & (POLLIN | POLLRDNORM)) && - pdev->vb_queue.num_buffers == 0 && - !pdev->iso_init) { - /* This poll will start a read stream, check capt_file */ + q->num_buffers == 0 && !q->streaming && q->fileio == NULL) { if (pwc_test_n_set_capt_file(pdev, file)) - return POLL_ERR; + goto out; + lock_v4l2 = 1; } - return vb2_poll(&pdev->vb_queue, file, wait); + /* Use try_lock, since we're taking the locks in the *wrong* order! */ + if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) + goto out; + ret = vb2_poll(&pdev->vb_queue, file, wait); + if (lock_v4l2) + mutex_unlock(&pdev->v4l2_lock); + +out: + if (!pdev->udev) + ret |= POLLHUP; + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (pdev->capt_file != file) - return -EBUSY; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; + + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_mmap(&pdev->vb_queue, vma); - return vb2_mmap(&pdev->vb_queue, vma); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } /***************************************************************************/ @@ -724,12 +763,14 @@ static void buffer_queue(struct vb2_buffer *vb) struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); unsigned long flags = 0; - spin_lock_irqsave(&pdev->queued_bufs_lock, flags); /* Check the device has not disconnected between prep and queuing */ - if (pdev->udev) - list_add_tail(&buf->list, &pdev->queued_bufs); - else + if (!pdev->udev) { vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&pdev->queued_bufs_lock, flags); + list_add_tail(&buf->list, &pdev->queued_bufs); spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); } @@ -738,11 +779,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct pwc_device *pdev = vb2_get_drv_priv(vq); int r; - mutex_lock(&pdev->udevlock); - if (!pdev->udev) { - r = -ENODEV; - goto leave; - } + if (!pdev->udev) + return -ENODEV; /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); @@ -756,8 +794,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) /* And cleanup any queued bufs!! */ pwc_cleanup_queued_bufs(pdev); } -leave: - mutex_unlock(&pdev->udevlock); + return r; } @@ -765,19 +802,29 @@ static int stop_streaming(struct vb2_queue *vq) { struct pwc_device *pdev = vb2_get_drv_priv(vq); - mutex_lock(&pdev->udevlock); if (pdev->udev) { pwc_set_leds(pdev, 0, 0); pwc_camera_power(pdev, 0); pwc_isoc_cleanup(pdev); } - mutex_unlock(&pdev->udevlock); pwc_cleanup_queued_bufs(pdev); return 0; } +static void wait_prepare(struct vb2_queue *vq) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); + mutex_unlock(&pdev->vb_queue_lock); +} + +static void wait_finish(struct vb2_queue *vq) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); + mutex_lock(&pdev->vb_queue_lock); +} + static struct vb2_ops pwc_vb_queue_ops = { .queue_setup = queue_setup, .buf_init = buffer_init, @@ -787,6 +834,8 @@ static struct vb2_ops pwc_vb_queue_ops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, + .wait_prepare = wait_prepare, + .wait_finish = wait_finish, }; /***************************************************************************/ @@ -1066,8 +1115,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->features = features; pwc_construct(pdev); /* set min/max sizes correct */ - mutex_init(&pdev->capt_file_lock); - mutex_init(&pdev->udevlock); + mutex_init(&pdev->v4l2_lock); + mutex_init(&pdev->vb_queue_lock); spin_lock_init(&pdev->queued_bufs_lock); INIT_LIST_HEAD(&pdev->queued_bufs); @@ -1139,6 +1188,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler; pdev->vdev.v4l2_dev = &pdev->v4l2_dev; + pdev->vdev.lock = &pdev->v4l2_lock; + + /* + * Don't take v4l2_lock for these ioctls. This improves latency if + * v4l2_lock is taken for a long time, e.g. when changing a control + * value, and a new frame is ready to be dequeued. + */ + v4l2_dont_use_lock(&pdev->vdev, VIDIOC_DQBUF); + v4l2_dont_use_lock(&pdev->vdev, VIDIOC_QBUF); + v4l2_dont_use_lock(&pdev->vdev, VIDIOC_QUERYBUF); rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); if (rc < 0) { @@ -1194,16 +1253,20 @@ static void usb_pwc_disconnect(struct usb_interface *intf) struct v4l2_device *v = usb_get_intfdata(intf); struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); - mutex_lock(&pdev->udevlock); + mutex_lock(&pdev->v4l2_lock); + + mutex_lock(&pdev->vb_queue_lock); /* No need to keep the urbs around after disconnection */ - pwc_isoc_cleanup(pdev); + if (pdev->vb_queue.streaming) + pwc_isoc_cleanup(pdev); pdev->udev = NULL; - mutex_unlock(&pdev->udevlock); - pwc_cleanup_queued_bufs(pdev); + mutex_unlock(&pdev->vb_queue_lock); + v4l2_device_disconnect(&pdev->v4l2_dev); video_unregister_device(&pdev->vdev); - v4l2_device_unregister(&pdev->v4l2_dev); + + mutex_unlock(&pdev->v4l2_lock); #ifdef CONFIG_USB_PWC_INPUT_EVDEV if (pdev->button_dev) @@ -1238,15 +1301,4 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("pwcx"); MODULE_VERSION( PWC_VERSION ); -static int __init usb_pwc_init(void) -{ - return usb_register(&pwc_driver); -} - -static void __exit usb_pwc_exit(void) -{ - usb_deregister(&pwc_driver); -} - -module_init(usb_pwc_init); -module_exit(usb_pwc_exit); +module_usb_driver(pwc_driver); diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index c1ba1a060c93..c691e29cc36e 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -464,26 +464,24 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) struct pwc_device *pdev = video_drvdata(file); int ret, pixelformat, compression = 0; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; - ret = pwc_vidioc_try_fmt(pdev, f); if (ret < 0) return ret; - pixelformat = f->fmt.pix.pixelformat; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - mutex_lock(&pdev->udevlock); - if (!pdev->udev) { - ret = -ENODEV; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret) goto leave; - } - if (pdev->iso_init) { + if (pdev->vb_queue.streaming) { ret = -EBUSY; goto leave; } + pixelformat = f->fmt.pix.pixelformat; + PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d " "format=%c%c%c%c\n", f->fmt.pix.width, f->fmt.pix.height, pdev->vframes, @@ -499,7 +497,7 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); leave: - mutex_unlock(&pdev->udevlock); + mutex_unlock(&pdev->vb_queue_lock); return ret; } @@ -507,9 +505,6 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap { struct pwc_device *pdev = video_drvdata(file); - if (!pdev->udev) - return -ENODEV; - strcpy(cap->driver, PWC_NAME); strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card)); usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); @@ -540,15 +535,12 @@ static int pwc_s_input(struct file *file, void *fh, unsigned int i) return i ? -EINVAL : 0; } -static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl) +static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct pwc_device *pdev = container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - if (!pdev->udev) - return -ENODEV; - switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: if (pdev->color_bal_valid && @@ -615,18 +607,6 @@ static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl) return ret; } -static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct pwc_device *pdev = - container_of(ctrl->handler, struct pwc_device, ctrl_handler); - int ret; - - mutex_lock(&pdev->udevlock); - ret = pwc_g_volatile_ctrl_unlocked(ctrl); - mutex_unlock(&pdev->udevlock); - return ret; -} - static int pwc_set_awb(struct pwc_device *pdev) { int ret; @@ -648,7 +628,7 @@ static int pwc_set_awb(struct pwc_device *pdev) if (pdev->auto_white_balance->val == awb_indoor || pdev->auto_white_balance->val == awb_outdoor || pdev->auto_white_balance->val == awb_fl) - pwc_g_volatile_ctrl_unlocked(pdev->auto_white_balance); + pwc_g_volatile_ctrl(pdev->auto_white_balance); } if (pdev->auto_white_balance->val != awb_manual) return 0; @@ -812,13 +792,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - mutex_lock(&pdev->udevlock); - - if (!pdev->udev) { - ret = -ENODEV; - goto leave; - } - switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, @@ -915,8 +888,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) if (ret) PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret); -leave: - mutex_unlock(&pdev->udevlock); return ret; } @@ -949,11 +920,9 @@ static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - mutex_lock(&pdev->udevlock); /* To avoid race with s_fmt */ PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n", pdev->width, pdev->height); pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); - mutex_unlock(&pdev->udevlock); return 0; } @@ -968,70 +937,98 @@ static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - return vb2_reqbufs(&pdev->vb_queue, rb); + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_reqbufs(&pdev->vb_queue, rb); + + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + int ret; - return vb2_querybuf(&pdev->vb_queue, buf); + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; + + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_querybuf(&pdev->vb_queue, buf); + + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_qbuf(&pdev->vb_queue, buf); - return vb2_qbuf(&pdev->vb_queue, buf); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_dqbuf(&pdev->vb_queue, buf, + file->f_flags & O_NONBLOCK); - return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_streamon(&pdev->vb_queue, i); - return vb2_streamon(&pdev->vb_queue, i); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_streamoff(&pdev->vb_queue, i); - return vb2_streamoff(&pdev->vb_queue, i); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_enum_framesizes(struct file *file, void *fh, @@ -1119,19 +1116,17 @@ static int pwc_s_parm(struct file *file, void *fh, parm->parm.capture.timeperframe.numerator == 0) return -EINVAL; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; - fps = parm->parm.capture.timeperframe.denominator / parm->parm.capture.timeperframe.numerator; - mutex_lock(&pdev->udevlock); - if (!pdev->udev) { - ret = -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; + + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret) goto leave; - } - if (pdev->iso_init) { + if (pdev->vb_queue.streaming) { ret = -EBUSY; goto leave; } @@ -1142,7 +1137,7 @@ static int pwc_s_parm(struct file *file, void *fh, pwc_g_parm(file, fh, parm); leave: - mutex_unlock(&pdev->udevlock); + mutex_unlock(&pdev->vb_queue_lock); return ret; } diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index e4d4d711dd1f..d6b5b216b9d6 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -221,9 +221,17 @@ struct pwc_device struct video_device vdev; struct v4l2_device v4l2_dev; - /* Pointer to our usb_device, may be NULL after unplug */ - struct usb_device *udev; - struct mutex udevlock; + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ + + /* Pointer to our usb_device, will be NULL after unplug */ + struct usb_device *udev; /* Both mutexes most be hold when setting! */ /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int type; @@ -232,7 +240,6 @@ struct pwc_device /*** Video data ***/ struct file *capt_file; /* file doing video capture */ - struct mutex capt_file_lock; int vendpoint; /* video isoc endpoint */ int vcinterface; /* video control interface */ int valternate; /* alternate interface needed */ @@ -251,12 +258,6 @@ struct pwc_device unsigned char *ctrl_buf; struct urb *urbs[MAX_ISO_BUFS]; - char iso_init; - - /* videobuf2 queue and queued buffers list */ - struct vb2_queue vb_queue; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* * Frame currently being filled, this only gets touched by the -- cgit v1.2.3 From 62bba5dd8181bed282967f1054ed5749b2c499f9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:17 -0300 Subject: [media] gspca: Allow subdrivers to use the control framework Make the necessary changes to allow subdrivers to use the control framework. This does not add control event support, that comes later. It add a init_control cam_op that is called after init in probe that allows the subdriver to set up the controls. HdG: Call v4l2_ctrl_handler_setup from resume instead of gspca_set_default_mode, as we just want to resend the current ctrl values to the device. Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 36 +++++++++++++++++++++++++++--------- drivers/media/video/gspca/gspca.h | 1 + 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index ca5a2b139d0b..d85c30dd37ad 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "gspca.h" @@ -1006,6 +1007,8 @@ static void gspca_set_default_mode(struct gspca_dev *gspca_dev) /* set the current control values to their default values * which may have changed in sd_init() */ + /* does nothing if ctrl_handler == NULL */ + v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); ctrl = gspca_dev->cam.ctrls; if (ctrl != NULL) { for (i = 0; @@ -1323,6 +1326,7 @@ static void gspca_release(struct video_device *vfd) PDEBUG(D_PROBE, "%s released", video_device_node_name(&gspca_dev->vdev)); + v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); kfree(gspca_dev->usb_buf); kfree(gspca_dev); } @@ -2347,6 +2351,14 @@ int gspca_dev_probe2(struct usb_interface *intf, gspca_dev->sd_desc = sd_desc; gspca_dev->nbufread = 2; gspca_dev->empty_packet = -1; /* don't check the empty packets */ + gspca_dev->vdev = gspca_template; + gspca_dev->vdev.parent = &intf->dev; + gspca_dev->module = module; + gspca_dev->present = 1; + + mutex_init(&gspca_dev->usb_lock); + mutex_init(&gspca_dev->queue_lock); + init_waitqueue_head(&gspca_dev->wq); /* configure the subdriver and initialize the USB device */ ret = sd_desc->config(gspca_dev, id); @@ -2355,6 +2367,10 @@ int gspca_dev_probe2(struct usb_interface *intf, if (gspca_dev->cam.ctrls != NULL) ctrls_init(gspca_dev); ret = sd_desc->init(gspca_dev); + if (ret < 0) + goto out; + if (sd_desc->init_controls) + ret = sd_desc->init_controls(gspca_dev); if (ret < 0) goto out; gspca_set_default_mode(gspca_dev); @@ -2363,15 +2379,7 @@ int gspca_dev_probe2(struct usb_interface *intf, if (ret) goto out; - mutex_init(&gspca_dev->usb_lock); - mutex_init(&gspca_dev->queue_lock); - init_waitqueue_head(&gspca_dev->wq); - /* init video stuff */ - memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); - gspca_dev->vdev.parent = &intf->dev; - gspca_dev->module = module; - gspca_dev->present = 1; ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, -1); @@ -2391,6 +2399,7 @@ out: if (gspca_dev->input_dev) input_unregister_device(gspca_dev->input_dev); #endif + v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); kfree(gspca_dev->usb_buf); kfree(gspca_dev); return ret; @@ -2489,11 +2498,20 @@ EXPORT_SYMBOL(gspca_suspend); int gspca_resume(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + int streaming; gspca_dev->frozen = 0; gspca_dev->sd_desc->init(gspca_dev); gspca_input_create_urb(gspca_dev); - if (gspca_dev->streaming) + /* + * Most subdrivers send all ctrl values on sd_start and thus + * only write to the device registers on s_ctrl when streaming -> + * Clear streaming to avoid setting all ctrls twice. + */ + streaming = gspca_dev->streaming; + gspca_dev->streaming = 0; + v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); + if (streaming) return gspca_init_transfer(gspca_dev); return 0; } diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 589009f4496f..81404160ca43 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -115,6 +115,7 @@ struct sd_desc { /* mandatory operations */ cam_cf_op config; /* called on probe */ cam_op init; /* called on probe and resume */ + cam_op init_controls; /* called on probe */ cam_op start; /* called on stream on after URBs creation */ cam_pkt_op pkt_scan; /* optional operations */ -- cgit v1.2.3 From f4c1605a74c46100d93f465238b9962ac9499e36 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:18 -0300 Subject: [media] gspca: Use video_drvdata(file) instead of file->private_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for control events: free up file->private_data by using video_drvdata(file) to get to the gspca_dev struct. [mchehab@redhat.com: fix a compile error: ‘file’ undeclared] Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 64 +++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index d85c30dd37ad..dbebed90bbac 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1061,7 +1061,7 @@ static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { int ret; - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) return -EINVAL; @@ -1085,7 +1085,7 @@ static int vidioc_s_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { int ret; - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) return -EINVAL; @@ -1110,7 +1110,7 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, struct v4l2_dbg_chip_ident *chip) { int ret; - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) return -EINVAL; @@ -1130,7 +1130,7 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int i, j, index; __u32 fmt_tb[8]; @@ -1172,7 +1172,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int mode; mode = gspca_dev->curr_mode; @@ -1217,7 +1217,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; ret = try_fmt_vid_cap(gspca_dev, fmt); @@ -1229,7 +1229,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) @@ -1268,7 +1268,7 @@ out: static int vidioc_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int i; __u32 index = 0; @@ -1294,7 +1294,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, static int vidioc_enum_frameintervals(struct file *filp, void *priv, struct v4l2_frmivalenum *fival) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(filp); int mode = wxh_to_mode(gspca_dev, fival->width, fival->height); __u32 i; @@ -1333,10 +1333,9 @@ static void gspca_release(struct video_device *vfd) static int dev_open(struct file *file) { - struct gspca_dev *gspca_dev; + struct gspca_dev *gspca_dev = video_drvdata(file); PDEBUG(D_STREAM, "[%s] open", current->comm); - gspca_dev = (struct gspca_dev *) video_devdata(file); if (!gspca_dev->present) return -ENODEV; @@ -1344,7 +1343,6 @@ static int dev_open(struct file *file) if (!try_module_get(gspca_dev->module)) return -ENODEV; - file->private_data = gspca_dev; #ifdef GSPCA_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) @@ -1359,7 +1357,7 @@ static int dev_open(struct file *file) static int dev_close(struct file *file) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); PDEBUG(D_STREAM, "[%s] close", current->comm); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) @@ -1375,7 +1373,6 @@ static int dev_close(struct file *file) } frame_free(gspca_dev); } - file->private_data = NULL; module_put(gspca_dev->module); mutex_unlock(&gspca_dev->queue_lock); @@ -1387,7 +1384,7 @@ static int dev_close(struct file *file) static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; /* protect the access to the usb device */ @@ -1439,7 +1436,7 @@ static int get_ctrl(struct gspca_dev *gspca_dev, static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *q_ctrl) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; struct gspca_ctrl *gspca_ctrl; int i, idx; @@ -1482,7 +1479,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; struct gspca_ctrl *gspca_ctrl; int idx, ret; @@ -1531,7 +1528,7 @@ out: static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; int idx, ret; @@ -1562,7 +1559,7 @@ out: static int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *qmenu) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->querymenu) return -EINVAL; @@ -1572,7 +1569,7 @@ static int vidioc_querymenu(struct file *file, void *priv, static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *input) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (input->index != 0) return -EINVAL; @@ -1599,7 +1596,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int i, ret = 0, streaming; i = rb->memory; /* (avoid compilation warning) */ @@ -1670,7 +1667,7 @@ out: static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; if (v4l2_buf->index < 0 @@ -1685,7 +1682,7 @@ static int vidioc_querybuf(struct file *file, void *priv, static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type buf_type) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1726,7 +1723,7 @@ out: static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type buf_type) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1770,7 +1767,7 @@ out: static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (!gspca_dev->sd_desc->get_jcomp) @@ -1789,7 +1786,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, static int vidioc_s_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (!gspca_dev->sd_desc->set_jcomp) @@ -1808,7 +1805,7 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, static int vidioc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(filp); parm->parm.capture.readbuffers = gspca_dev->nbufread; @@ -1834,7 +1831,7 @@ static int vidioc_g_parm(struct file *filp, void *priv, static int vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(filp); int n; n = parm->parm.capture.readbuffers; @@ -1864,7 +1861,7 @@ static int vidioc_s_parm(struct file *filp, void *priv, static int dev_mmap(struct file *file, struct vm_area_struct *vma) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; struct page *page; unsigned long addr, start, size; @@ -1967,7 +1964,7 @@ static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; int i, j, ret; @@ -2043,7 +2040,7 @@ out: static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; int i, index, ret; @@ -2137,7 +2134,7 @@ static int read_alloc(struct gspca_dev *gspca_dev, static unsigned int dev_poll(struct file *file, poll_table *wait) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; PDEBUG(D_FRAM, "poll"); @@ -2168,7 +2165,7 @@ static unsigned int dev_poll(struct file *file, poll_table *wait) static ssize_t dev_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; struct v4l2_buffer v4l2_buf; struct timeval timestamp; @@ -2353,6 +2350,7 @@ int gspca_dev_probe2(struct usb_interface *intf, gspca_dev->empty_packet = -1; /* don't check the empty packets */ gspca_dev->vdev = gspca_template; gspca_dev->vdev.parent = &intf->dev; + video_set_drvdata(&gspca_dev->vdev, gspca_dev); gspca_dev->module = module; gspca_dev->present = 1; -- cgit v1.2.3 From 2333565d94c1efd199782bdedc9f4d9b06198583 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:19 -0300 Subject: [media] gscpa: Use v4l2_fh and add G/S_PRIORITY support In order to support control event gspca has to use struct v4l2_fh. As a bonus feature this also gives priority handling for free. Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 13 ++++++++++--- drivers/media/video/gspca/gspca.h | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index dbebed90bbac..ed33a8773f7e 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "gspca.h" @@ -1327,6 +1328,7 @@ static void gspca_release(struct video_device *vfd) video_device_node_name(&gspca_dev->vdev)); v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); + v4l2_device_unregister(&gspca_dev->v4l2_dev); kfree(gspca_dev->usb_buf); kfree(gspca_dev); } @@ -1352,7 +1354,7 @@ static int dev_open(struct file *file) gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG); #endif - return 0; + return v4l2_fh_open(file); } static int dev_close(struct file *file) @@ -1378,7 +1380,7 @@ static int dev_close(struct file *file) PDEBUG(D_STREAM, "close done"); - return 0; + return v4l2_fh_release(file); } static int vidioc_querycap(struct file *file, void *priv, @@ -2345,12 +2347,16 @@ int gspca_dev_probe2(struct usb_interface *intf, } } + ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); + if (ret) + goto out; gspca_dev->sd_desc = sd_desc; gspca_dev->nbufread = 2; gspca_dev->empty_packet = -1; /* don't check the empty packets */ gspca_dev->vdev = gspca_template; - gspca_dev->vdev.parent = &intf->dev; + gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; video_set_drvdata(&gspca_dev->vdev, gspca_dev); + set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags); gspca_dev->module = module; gspca_dev->present = 1; @@ -2462,6 +2468,7 @@ void gspca_disconnect(struct usb_interface *intf) /* the device is freed at exit of this function */ gspca_dev->dev = NULL; + v4l2_device_disconnect(&gspca_dev->v4l2_dev); mutex_unlock(&gspca_dev->usb_lock); usb_set_intfdata(intf, NULL); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 81404160ca43..c1ebf7cd45b3 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -6,6 +6,7 @@ #include #include #include +#include #include /* compilation option */ @@ -159,6 +160,7 @@ struct gspca_frame { struct gspca_dev { struct video_device vdev; /* !! must be the first item */ struct module *module; /* subdriver handling the device */ + struct v4l2_device v4l2_dev; struct usb_device *dev; struct file *capt_file; /* file doing video capture */ #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) -- cgit v1.2.3 From d57fb9f8a69deb521ffabac02d2fea484141e59c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:20 -0300 Subject: [media] gspca: Add support for control events Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index ed33a8773f7e..9fe723c6acb4 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "gspca.h" @@ -2158,6 +2159,7 @@ static unsigned int dev_poll(struct file *file, poll_table *wait) ret = POLLIN | POLLRDNORM; /* yes */ else ret = 0; + ret |= v4l2_ctrl_poll(file, wait); mutex_unlock(&gspca_dev->queue_lock); if (!gspca_dev->present) return POLLHUP; @@ -2269,6 +2271,8 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_register = vidioc_s_register, #endif .vidioc_g_chip_ident = vidioc_g_chip_ident, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static const struct video_device gspca_template = { -- cgit v1.2.3 From cc7b6f257d42eb9829b38e3a8807943426a89a87 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:21 -0300 Subject: [media] gspca: Fix querycap and incorrect return codes Add V4L2_CAP_DEVICE_CAPS support to querycap and replace -EINVAL by -ENOTTY whenever an ioctl is not supported. Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 9fe723c6acb4..7669f27238c3 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1066,10 +1066,10 @@ static int vidioc_g_register(struct file *file, void *priv, struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) - return -EINVAL; + return -ENOTTY; if (!gspca_dev->sd_desc->get_register) - return -EINVAL; + return -ENOTTY; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; @@ -1090,10 +1090,10 @@ static int vidioc_s_register(struct file *file, void *priv, struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) - return -EINVAL; + return -ENOTTY; if (!gspca_dev->sd_desc->set_register) - return -EINVAL; + return -ENOTTY; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; @@ -1115,7 +1115,7 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) - return -EINVAL; + return -ENOTTY; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; @@ -1410,9 +1410,10 @@ static int vidioc_querycap(struct file *file, void *priv, } usb_make_path(gspca_dev->dev, (char *) cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ret = 0; out: mutex_unlock(&gspca_dev->usb_lock); @@ -1565,7 +1566,7 @@ static int vidioc_querymenu(struct file *file, void *priv, struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->querymenu) - return -EINVAL; + return -ENOTTY; return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu); } @@ -1774,7 +1775,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, int ret; if (!gspca_dev->sd_desc->get_jcomp) - return -EINVAL; + return -ENOTTY; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; gspca_dev->usb_err = 0; @@ -1793,7 +1794,7 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, int ret; if (!gspca_dev->sd_desc->set_jcomp) - return -EINVAL; + return -ENOTTY; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; gspca_dev->usb_err = 0; -- cgit v1.2.3 From 254902b01d2acc6aced99ec17caa4c6cd890cdea Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:22 -0300 Subject: [media] gspca: Fix locking issues related to suspend/resume There are two bugs here: first the calls to stop0 (in gspca_suspend) and gspca_init_transfer (in gspca_resume) need to be called with the usb_lock held. That's true for the other places they are called and it is what subdrivers expect. Quite a few will unlock the usb_lock in stop0 while waiting for a worker thread to finish, and if usb_lock isn't held then that can cause a kernel oops. The other problem is that a worker thread needs to detect that it has to halt due to a suspend. Otherwise it will just go on looping. So add tests against gspca_dev->frozen in the worker threads that need it. Hdg, 2 minor changes: 1) The finepix device is ok with stopping reading a frame halfway through, so add frozen checks in all places where we also check if we're still streaming 2) Use gspca_dev->dev instead of gspca_dev->present to check for disconnect in all touched drivers. I plan to do this everywhere in the future, and most relevant lines in the touched drivers are already modified by this patch. Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/finepix.c | 8 +++++--- drivers/media/video/gspca/gspca.c | 16 +++++++++++----- drivers/media/video/gspca/jl2005bcd.c | 6 +++--- drivers/media/video/gspca/sq905.c | 8 ++++---- drivers/media/video/gspca/sq905c.c | 6 +++--- drivers/media/video/gspca/vicam.c | 4 ++-- drivers/media/video/gspca/zc3xx.c | 5 +++-- 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index 0107513cd728..d0befe981098 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -94,7 +94,7 @@ static void dostream(struct work_struct *work) /* loop reading a frame */ again: - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { /* request a frame */ mutex_lock(&gspca_dev->usb_lock); @@ -102,7 +102,8 @@ again: mutex_unlock(&gspca_dev->usb_lock); if (ret < 0) break; - if (!gspca_dev->present || !gspca_dev->streaming) + if (gspca_dev->frozen || !gspca_dev->dev || + !gspca_dev->streaming) break; /* the frame comes in parts */ @@ -117,7 +118,8 @@ again: * error. Just restart. */ goto again; } - if (!gspca_dev->present || !gspca_dev->streaming) + if (gspca_dev->frozen || !gspca_dev->dev || + !gspca_dev->streaming) goto out; if (len < FPIX_MAX_TRANSFER || (data[len - 2] == 0xff && diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 7669f27238c3..a14c8f71d48b 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -2499,8 +2499,11 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) destroy_urbs(gspca_dev); gspca_input_destroy_urb(gspca_dev); gspca_set_alt0(gspca_dev); - if (gspca_dev->sd_desc->stop0) + if (gspca_dev->sd_desc->stop0) { + mutex_lock(&gspca_dev->usb_lock); gspca_dev->sd_desc->stop0(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + } return 0; } EXPORT_SYMBOL(gspca_suspend); @@ -2508,7 +2511,7 @@ EXPORT_SYMBOL(gspca_suspend); int gspca_resume(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - int streaming; + int streaming, ret = 0; gspca_dev->frozen = 0; gspca_dev->sd_desc->init(gspca_dev); @@ -2521,9 +2524,12 @@ int gspca_resume(struct usb_interface *intf) streaming = gspca_dev->streaming; gspca_dev->streaming = 0; v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); - if (streaming) - return gspca_init_transfer(gspca_dev); - return 0; + if (streaming) { + mutex_lock(&gspca_dev->queue_lock); + ret = gspca_init_transfer(gspca_dev); + mutex_unlock(&gspca_dev->queue_lock); + } + return ret; } EXPORT_SYMBOL(gspca_resume); #endif diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c index 53f58ef367cf..e1fc2561e4bc 100644 --- a/drivers/media/video/gspca/jl2005bcd.c +++ b/drivers/media/video/gspca/jl2005bcd.c @@ -335,7 +335,7 @@ static void jl2005c_dostream(struct work_struct *work) goto quit_stream; } - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { /* Check if this is a new frame. If so, start the frame first */ if (!header_read) { mutex_lock(&gspca_dev->usb_lock); @@ -367,7 +367,7 @@ static void jl2005c_dostream(struct work_struct *work) buffer, act_len); header_read = 1; } - while (bytes_left > 0 && gspca_dev->present) { + while (bytes_left > 0 && gspca_dev->dev) { data_len = bytes_left > JL2005C_MAX_TRANSFER ? JL2005C_MAX_TRANSFER : bytes_left; ret = usb_bulk_msg(gspca_dev->dev, @@ -390,7 +390,7 @@ static void jl2005c_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->present) { + if (gspca_dev->dev) { mutex_lock(&gspca_dev->usb_lock); jl2005c_stop(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index 2fe3c29bd6b7..a144ce759b66 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -232,7 +232,7 @@ static void sq905_dostream(struct work_struct *work) frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + FRAME_HEADER_LEN; - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { /* request some data and then read it until we have * a complete frame. */ bytes_left = frame_sz; @@ -242,7 +242,7 @@ static void sq905_dostream(struct work_struct *work) we must finish reading an entire frame, otherwise the next time we stream we start reading in the middle of a frame. */ - while (bytes_left > 0 && gspca_dev->present) { + while (bytes_left > 0 && gspca_dev->dev) { data_len = bytes_left > SQ905_MAX_TRANSFER ? SQ905_MAX_TRANSFER : bytes_left; ret = sq905_read_data(gspca_dev, buffer, data_len, 1); @@ -274,7 +274,7 @@ static void sq905_dostream(struct work_struct *work) gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); } - if (gspca_dev->present) { + if (gspca_dev->dev) { /* acknowledge the frame */ mutex_lock(&gspca_dev->usb_lock); ret = sq905_ack_frame(gspca_dev); @@ -284,7 +284,7 @@ static void sq905_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->present) { + if (gspca_dev->dev) { mutex_lock(&gspca_dev->usb_lock); sq905_command(gspca_dev, SQ905_CLEAR); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index ae783634712f..720c187f6ec7 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -150,7 +150,7 @@ static void sq905c_dostream(struct work_struct *work) goto quit_stream; } - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { /* Request the header, which tells the size to download */ ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), @@ -169,7 +169,7 @@ static void sq905c_dostream(struct work_struct *work) packet_type = FIRST_PACKET; gspca_frame_add(gspca_dev, packet_type, buffer, FRAME_HEADER_LEN); - while (bytes_left > 0 && gspca_dev->present) { + while (bytes_left > 0 && gspca_dev->dev) { data_len = bytes_left > SQ905C_MAX_TRANSFER ? SQ905C_MAX_TRANSFER : bytes_left; ret = usb_bulk_msg(gspca_dev->dev, @@ -191,7 +191,7 @@ static void sq905c_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->present) { + if (gspca_dev->dev) { mutex_lock(&gspca_dev->usb_lock); sq905c_command(gspca_dev, SQ905C_CLEAR, 0); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c index e48ec4db6da4..432d6cd99cd6 100644 --- a/drivers/media/video/gspca/vicam.c +++ b/drivers/media/video/gspca/vicam.c @@ -225,7 +225,7 @@ static void vicam_dostream(struct work_struct *work) goto exit; } - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { ret = vicam_read_frame(gspca_dev, buffer, frame_sz); if (ret < 0) break; @@ -327,7 +327,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) dev->work_thread = NULL; mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->present) + if (gspca_dev->dev) vicam_set_camera_power(gspca_dev, 0); } diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 7d9a4f1be9dc..8f21bae46ef8 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6093,7 +6093,8 @@ static void transfer_update(struct work_struct *work) /* get the transfer status */ /* the bit 0 of the bridge register 11 indicates overflow */ mutex_lock(&gspca_dev->usb_lock); - if (!gspca_dev->present || !gspca_dev->streaming) + if (gspca_dev->frozen || !gspca_dev->dev || + !gspca_dev->streaming) goto err; reg11 = reg_r(gspca_dev, 0x0011); if (gspca_dev->usb_err < 0 @@ -6949,7 +6950,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) mutex_lock(&gspca_dev->usb_lock); sd->work_thread = NULL; } - if (!gspca_dev->present) + if (!gspca_dev->dev) return; send_unknown(gspca_dev, sd->sensor); } -- cgit v1.2.3 From a3d6e8cc0e6ddc8b3cfdeb3c979f07ed1aa528b3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:27 -0300 Subject: [media] gspca: Switch to V4L2 core locking, except for the buffer queuing ioctls Due to latency concerns the VIDIOC_QBUF, DQBUF and QUERYBUF do not use the core lock, instead they rely only on queue_lock. Changes by HdG: 1) Change release from the video_device to the v4l2_device, to avoid a race on disconnect. 2) Adjust for the V4L2 core changes which cause non ioctl fops to no longer take the V4L2 core lock. [mchehab@redhat.com: fix a merge conflict] Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 257 +++++++++++--------------------------- drivers/media/video/gspca/gspca.h | 7 +- 2 files changed, 82 insertions(+), 182 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index a14c8f71d48b..2687e76b0fb8 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -850,14 +850,6 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) struct ep_tb_s ep_tb[MAX_ALT]; int n, ret, xfer, alt, alt_idx; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - - if (!gspca_dev->present) { - ret = -ENODEV; - goto unlock; - } - /* reset the streaming variables */ gspca_dev->image = NULL; gspca_dev->image_len = 0; @@ -872,7 +864,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) if (gspca_dev->sd_desc->isoc_init) { ret = gspca_dev->sd_desc->isoc_init(gspca_dev); if (ret < 0) - goto unlock; + return ret; } xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK : USB_ENDPOINT_XFER_ISOC; @@ -883,8 +875,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer); if (ep == NULL) { pr_err("bad altsetting %d\n", gspca_dev->alt); - ret = -EIO; - goto out; + return -EIO; } ep_tb[0].alt = gspca_dev->alt; alt_idx = 1; @@ -895,8 +886,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb); if (alt_idx <= 0) { pr_err("no transfer endpoint found\n"); - ret = -EIO; - goto unlock; + return -EIO; } } @@ -991,8 +981,6 @@ retry: } out: gspca_input_create_urb(gspca_dev); -unlock: - mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -1062,7 +1050,6 @@ static int gspca_get_mode(struct gspca_dev *gspca_dev, static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - int ret; struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) @@ -1071,22 +1058,13 @@ static int vidioc_g_register(struct file *file, void *priv, if (!gspca_dev->sd_desc->get_register) return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->get_register(gspca_dev, reg); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - - return ret; + return gspca_dev->sd_desc->get_register(gspca_dev, reg); } static int vidioc_s_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - int ret; struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) @@ -1095,38 +1073,21 @@ static int vidioc_s_register(struct file *file, void *priv, if (!gspca_dev->sd_desc->set_register) return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->set_register(gspca_dev, reg); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - - return ret; + return gspca_dev->sd_desc->set_register(gspca_dev, reg); } #endif static int vidioc_g_chip_ident(struct file *file, void *priv, struct v4l2_dbg_chip_ident *chip) { - int ret; struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - - return ret; + return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); } static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, @@ -1321,9 +1282,10 @@ static int vidioc_enum_frameintervals(struct file *filp, void *priv, return -EINVAL; } -static void gspca_release(struct video_device *vfd) +static void gspca_release(struct v4l2_device *v4l2_device) { - struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev); + struct gspca_dev *gspca_dev = + container_of(v4l2_device, struct gspca_dev, v4l2_dev); PDEBUG(D_PROBE, "%s released", video_device_node_name(&gspca_dev->vdev)); @@ -1339,8 +1301,6 @@ static int dev_open(struct file *file) struct gspca_dev *gspca_dev = video_drvdata(file); PDEBUG(D_STREAM, "[%s] open", current->comm); - if (!gspca_dev->present) - return -ENODEV; /* protect the subdriver against rmmod */ if (!try_module_get(gspca_dev->module)) @@ -1363,21 +1323,27 @@ static int dev_close(struct file *file) struct gspca_dev *gspca_dev = video_drvdata(file); PDEBUG(D_STREAM, "[%s] close", current->comm); - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + + /* Needed for gspca_stream_off, always lock before queue_lock! */ + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) { + mutex_unlock(&gspca_dev->usb_lock); + return -ERESTARTSYS; + } + /* if the file did the capture, free the streaming resources */ if (gspca_dev->capt_file == file) { if (gspca_dev->streaming) { - mutex_lock(&gspca_dev->usb_lock); gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); } frame_free(gspca_dev); } module_put(gspca_dev->module); mutex_unlock(&gspca_dev->queue_lock); + mutex_unlock(&gspca_dev->usb_lock); PDEBUG(D_STREAM, "close done"); @@ -1388,15 +1354,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - /* protect the access to the usb device */ - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } strlcpy((char *) cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver); if (gspca_dev->dev->product != NULL) { @@ -1414,10 +1372,7 @@ static int vidioc_querycap(struct file *file, void *priv, | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - ret = 0; -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return 0; } static int get_ctrl(struct gspca_dev *gspca_dev, @@ -1486,7 +1441,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; struct gspca_ctrl *gspca_ctrl; - int idx, ret; + int idx; idx = get_ctrl(gspca_dev, ctrl->id); if (idx < 0) @@ -1506,27 +1461,16 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return -ERANGE; } PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } gspca_dev->usb_err = 0; - if (ctrls->set != NULL) { - ret = ctrls->set(gspca_dev, ctrl->value); - goto out; - } + if (ctrls->set != NULL) + return ctrls->set(gspca_dev, ctrl->value); if (gspca_ctrl != NULL) { gspca_ctrl->val = ctrl->value; if (ctrls->set_control != NULL && gspca_dev->streaming) ctrls->set_control(gspca_dev); } - ret = gspca_dev->usb_err; -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return gspca_dev->usb_err; } static int vidioc_g_ctrl(struct file *file, void *priv, @@ -1534,30 +1478,19 @@ static int vidioc_g_ctrl(struct file *file, void *priv, { struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; - int idx, ret; + int idx; idx = get_ctrl(gspca_dev, ctrl->id); if (idx < 0) return -EINVAL; ctrls = &gspca_dev->sd_desc->ctrls[idx]; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } gspca_dev->usb_err = 0; - if (ctrls->get != NULL) { - ret = ctrls->get(gspca_dev, &ctrl->value); - goto out; - } + if (ctrls->get != NULL) + return ctrls->get(gspca_dev, &ctrl->value); if (gspca_dev->cam.ctrls != NULL) ctrl->value = gspca_dev->cam.ctrls[idx].val; - ret = 0; -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return 0; } static int vidioc_querymenu(struct file *file, void *priv, @@ -1640,10 +1573,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, /* stop streaming */ streaming = gspca_dev->streaming; if (streaming) { - mutex_lock(&gspca_dev->usb_lock); gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); /* Don't restart the stream when switching from read * to mmap mode */ @@ -1748,13 +1679,8 @@ static int vidioc_streamoff(struct file *file, void *priv, } /* stop streaming */ - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { - ret = -ERESTARTSYS; - goto out; - } gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); /* In case another thread is waiting in dqbuf */ wake_up_interruptible(&gspca_dev->wq); @@ -1772,38 +1698,22 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; if (!gspca_dev->sd_desc->get_jcomp) return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); } static int vidioc_s_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; if (!gspca_dev->sd_desc->set_jcomp) return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); } static int vidioc_g_parm(struct file *filp, void *priv, @@ -1814,21 +1724,10 @@ static int vidioc_g_parm(struct file *filp, void *priv, parm->parm.capture.readbuffers = gspca_dev->nbufread; if (gspca_dev->sd_desc->get_streamparm) { - int ret; - - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (gspca_dev->present) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); - ret = gspca_dev->usb_err; - } else { - ret = -ENODEV; - } - mutex_unlock(&gspca_dev->usb_lock); - return ret; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; } - return 0; } @@ -1845,19 +1744,9 @@ static int vidioc_s_parm(struct file *filp, void *priv, gspca_dev->nbufread = n; if (gspca_dev->sd_desc->set_streamparm) { - int ret; - - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (gspca_dev->present) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); - ret = gspca_dev->usb_err; - } else { - ret = -ENODEV; - } - mutex_unlock(&gspca_dev->usb_lock); - return ret; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; } return 0; @@ -1877,10 +1766,6 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } if (gspca_dev->capt_file != file) { ret = -EINVAL; goto out; @@ -2008,14 +1893,6 @@ static int vidioc_dqbuf(struct file *file, void *priv, gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; - if (gspca_dev->sd_desc->dq_callback) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; - if (gspca_dev->present) - gspca_dev->sd_desc->dq_callback(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); PDEBUG(D_FRAM, "dqbuf %d", j); @@ -2032,6 +1909,15 @@ static int vidioc_dqbuf(struct file *file, void *priv, } out: mutex_unlock(&gspca_dev->queue_lock); + + if (ret == 0 && gspca_dev->sd_desc->dq_callback) { + mutex_lock(&gspca_dev->usb_lock); + gspca_dev->usb_err = 0; + if (gspca_dev->present) + gspca_dev->sd_desc->dq_callback(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + } + return ret; } @@ -2103,6 +1989,10 @@ static int read_alloc(struct gspca_dev *gspca_dev, int i, ret; PDEBUG(D_STREAM, "read alloc"); + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->nframes == 0) { struct v4l2_requestbuffers rb; @@ -2113,7 +2003,7 @@ static int read_alloc(struct gspca_dev *gspca_dev, ret = vidioc_reqbufs(file, gspca_dev, &rb); if (ret != 0) { PDEBUG(D_STREAM, "read reqbuf err %d", ret); - return ret; + goto out; } memset(&v4l2_buf, 0, sizeof v4l2_buf); v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -2123,16 +2013,17 @@ static int read_alloc(struct gspca_dev *gspca_dev, ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); if (ret != 0) { PDEBUG(D_STREAM, "read qbuf err: %d", ret); - return ret; + goto out; } } - gspca_dev->memory = GSPCA_MEMORY_READ; } /* start streaming */ ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); if (ret != 0) PDEBUG(D_STREAM, "read streamon err %d", ret); +out: + mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -2177,8 +2068,6 @@ static ssize_t dev_read(struct file *file, char __user *data, int n, ret, ret2; PDEBUG(D_FRAM, "read (%zd)", count); - if (!gspca_dev->present) - return -ENODEV; if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */ ret = read_alloc(gspca_dev, file); if (ret != 0) @@ -2280,7 +2169,7 @@ static const struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, - .release = gspca_release, + .release = video_device_release_empty, /* We use v4l2_dev.release */ }; /* initialize the controls */ @@ -2352,6 +2241,7 @@ int gspca_dev_probe2(struct usb_interface *intf, } } + gspca_dev->v4l2_dev.release = gspca_release; ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); if (ret) goto out; @@ -2366,6 +2256,7 @@ int gspca_dev_probe2(struct usb_interface *intf, gspca_dev->present = 1; mutex_init(&gspca_dev->usb_lock); + gspca_dev->vdev.lock = &gspca_dev->usb_lock; mutex_init(&gspca_dev->queue_lock); init_waitqueue_head(&gspca_dev->wq); @@ -2388,6 +2279,15 @@ int gspca_dev_probe2(struct usb_interface *intf, if (ret) goto out; + /* + * Don't take usb_lock for these ioctls. This improves latency if + * usb_lock is taken for a long time, e.g. when changing a control + * value, and a new frame is ready to be dequeued. + */ + v4l2_dont_use_lock(&gspca_dev->vdev, VIDIOC_DQBUF); + v4l2_dont_use_lock(&gspca_dev->vdev, VIDIOC_QBUF); + v4l2_dont_use_lock(&gspca_dev->vdev, VIDIOC_QUERYBUF); + /* init video stuff */ ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, @@ -2455,11 +2355,13 @@ void gspca_disconnect(struct usb_interface *intf) PDEBUG(D_PROBE, "%s disconnect", video_device_node_name(&gspca_dev->vdev)); + mutex_lock(&gspca_dev->usb_lock); + usb_set_intfdata(intf, NULL); + gspca_dev->dev = NULL; gspca_dev->present = 0; wake_up_interruptible(&gspca_dev->wq); - destroy_urbs(gspca_dev); #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) @@ -2471,18 +2373,13 @@ void gspca_disconnect(struct usb_interface *intf) } #endif - /* the device is freed at exit of this function */ - gspca_dev->dev = NULL; v4l2_device_disconnect(&gspca_dev->v4l2_dev); - mutex_unlock(&gspca_dev->usb_lock); + video_unregister_device(&gspca_dev->vdev); - usb_set_intfdata(intf, NULL); + mutex_unlock(&gspca_dev->usb_lock); - /* release the device */ /* (this will call gspca_release() immediately or on last close) */ - video_unregister_device(&gspca_dev->vdev); - -/* PDEBUG(D_PROBE, "disconnect complete"); */ + v4l2_device_put(&gspca_dev->v4l2_dev); } EXPORT_SYMBOL(gspca_disconnect); @@ -2493,17 +2390,16 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) if (!gspca_dev->streaming) return 0; + mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 1; /* avoid urb error messages */ if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); gspca_input_destroy_urb(gspca_dev); gspca_set_alt0(gspca_dev); - if (gspca_dev->sd_desc->stop0) { - mutex_lock(&gspca_dev->usb_lock); + if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } + mutex_unlock(&gspca_dev->usb_lock); return 0; } EXPORT_SYMBOL(gspca_suspend); @@ -2513,6 +2409,7 @@ int gspca_resume(struct usb_interface *intf) struct gspca_dev *gspca_dev = usb_get_intfdata(intf); int streaming, ret = 0; + mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 0; gspca_dev->sd_desc->init(gspca_dev); gspca_input_create_urb(gspca_dev); @@ -2524,11 +2421,9 @@ int gspca_resume(struct usb_interface *intf) streaming = gspca_dev->streaming; gspca_dev->streaming = 0; v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); - if (streaming) { - mutex_lock(&gspca_dev->queue_lock); + if (streaming) ret = gspca_init_transfer(gspca_dev); - mutex_unlock(&gspca_dev->queue_lock); - } + mutex_unlock(&gspca_dev->usb_lock); return ret; } EXPORT_SYMBOL(gspca_resume); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index c1ebf7cd45b3..436d881ce443 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -163,6 +163,7 @@ struct gspca_dev { struct v4l2_device v4l2_dev; struct usb_device *dev; struct file *capt_file; /* file doing video capture */ + /* protected by queue_lock */ #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) struct input_dev *input_dev; char phys[64]; /* physical device path */ @@ -192,7 +193,7 @@ struct gspca_dev { u8 fr_o; /* next frame to dequeue */ __u8 last_packet_type; __s8 empty_packet; /* if (-1) don't check empty packets */ - __u8 streaming; + __u8 streaming; /* protected by both mutexes (*) */ __u8 curr_mode; /* current camera mode */ __u32 pixfmt; /* current mode parameters */ @@ -214,6 +215,10 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ u8 audio; /* presence of audio device */ + + /* (*) These variables are proteced by both usb_lock and queue_lock, + that is any code setting them is holding *both*, which means that + any code getting them needs to hold at least one of them */ }; int gspca_dev_probe(struct usb_interface *intf, -- cgit v1.2.3 From 9a190c858cacd4b0110fefda5046ade8cd726261 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 May 2012 12:11:12 -0300 Subject: [media] gscpa: Clear usb_err before calling sd methods from suspend/resume Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 2687e76b0fb8..d9df30917374 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -2392,6 +2392,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) return 0; mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 1; /* avoid urb error messages */ + gspca_dev->usb_err = 0; if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); @@ -2411,6 +2412,7 @@ int gspca_resume(struct usb_interface *intf) mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 0; + gspca_dev->usb_err = 0; gspca_dev->sd_desc->init(gspca_dev); gspca_input_create_urb(gspca_dev); /* -- cgit v1.2.3 From a2d887c5fea33f341fd0d2a1487150473cf69014 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 7 May 2012 08:19:42 -0300 Subject: [media] gspca: Use req_events in poll So that we don't start a read stream when an app is only polling for control events. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 44 +++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index d9df30917374..8b97f777ddf4 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -2030,31 +2030,39 @@ out: static unsigned int dev_poll(struct file *file, poll_table *wait) { struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; + unsigned long req_events = poll_requested_events(wait); + int ret = 0; PDEBUG(D_FRAM, "poll"); - poll_wait(file, &gspca_dev->wq, wait); + if (req_events & POLLPRI) + ret |= v4l2_ctrl_poll(file, wait); - /* if reqbufs is not done, the user would use read() */ - if (gspca_dev->memory == GSPCA_MEMORY_NO) { - ret = read_alloc(gspca_dev, file); - if (ret != 0) - return POLLERR; - } + if (req_events & (POLLIN | POLLRDNORM)) { + /* if reqbufs is not done, the user would use read() */ + if (gspca_dev->memory == GSPCA_MEMORY_NO) { + if (read_alloc(gspca_dev, file) != 0) { + ret |= POLLERR; + goto out; + } + } - if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) - return POLLERR; + poll_wait(file, &gspca_dev->wq, wait); - /* check if an image has been received */ - if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) - ret = POLLIN | POLLRDNORM; /* yes */ - else - ret = 0; - ret |= v4l2_ctrl_poll(file, wait); - mutex_unlock(&gspca_dev->queue_lock); + /* check if an image has been received */ + if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) { + ret |= POLLERR; + goto out; + } + if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) + ret |= POLLIN | POLLRDNORM; + mutex_unlock(&gspca_dev->queue_lock); + } + +out: if (!gspca_dev->present) - return POLLHUP; + ret |= POLLHUP; + return ret; } -- cgit v1.2.3 From eb238732a52b100bdf4a766a50e11e6fd9bd1d83 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 7 May 2012 06:25:30 -0300 Subject: [media] gspca: Call sd_stop0 on disconnect This is necessary to ensure that worker-threads accessing the device are stopped before our disconnect handler returns. This causes a problem with stream_off calling sd_stop0 a second time when the device handle is closed. This is fixed by setting gscpa_dev->streaming to 0 on disconnect. Note that now stream_off will never be called on a disconnected device, and the present check can thus be removed from stream_off. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 8b97f777ddf4..b7cb9977f778 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -595,16 +595,12 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; - if (gspca_dev->present) { - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); - gspca_set_alt0(gspca_dev); - gspca_input_create_urb(gspca_dev); - } - - /* always call stop0 to free the subdriver's resources */ + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + gspca_set_alt0(gspca_dev); + gspca_input_create_urb(gspca_dev); if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); PDEBUG(D_STREAM, "stream off OK"); @@ -2369,7 +2365,6 @@ void gspca_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); gspca_dev->dev = NULL; gspca_dev->present = 0; - wake_up_interruptible(&gspca_dev->wq); destroy_urbs(gspca_dev); #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) @@ -2380,6 +2375,11 @@ void gspca_disconnect(struct usb_interface *intf) input_unregister_device(input_dev); } #endif + /* Free subdriver's streaming resources / stop sd workqueue(s) */ + if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) + gspca_dev->sd_desc->stop0(gspca_dev); + gspca_dev->streaming = 0; + wake_up_interruptible(&gspca_dev->wq); v4l2_device_disconnect(&gspca_dev->v4l2_dev); video_unregister_device(&gspca_dev->vdev); -- cgit v1.2.3 From a3cc74d4ec3f2462c6f2dfa527c126c096852f02 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 7 May 2012 06:44:21 -0300 Subject: [media] gspca: Set gspca_dev->usb_err to 0 at the begin of gspca_stream_off Just a small cleanup. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index b7cb9977f778..38b124ec23a3 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -595,6 +595,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; + gspca_dev->usb_err = 0; if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); @@ -1331,10 +1332,8 @@ static int dev_close(struct file *file) /* if the file did the capture, free the streaming resources */ if (gspca_dev->capt_file == file) { - if (gspca_dev->streaming) { - gspca_dev->usb_err = 0; + if (gspca_dev->streaming) gspca_stream_off(gspca_dev); - } frame_free(gspca_dev); } module_put(gspca_dev->module); @@ -1569,7 +1568,6 @@ static int vidioc_reqbufs(struct file *file, void *priv, /* stop streaming */ streaming = gspca_dev->streaming; if (streaming) { - gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); /* Don't restart the stream when switching from read @@ -1675,7 +1673,6 @@ static int vidioc_streamoff(struct file *file, void *priv, } /* stop streaming */ - gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); /* In case another thread is waiting in dqbuf */ wake_up_interruptible(&gspca_dev->wq); -- cgit v1.2.3 From 8cd058423f22072c1147d3e99161252e1e72333a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 May 2012 09:58:33 -0300 Subject: [media] gspca: Add autogain functions for use with control framework drivers Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/Makefile | 2 +- drivers/media/video/gspca/autogain_functions.c | 178 +++++++++++++++++++++++++ drivers/media/video/gspca/gspca.h | 15 +++ 3 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 drivers/media/video/gspca/autogain_functions.c diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 79ebe46e1ad7..c901da0bd657 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -43,7 +43,7 @@ obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o -gspca_main-objs := gspca.o +gspca_main-objs := gspca.o autogain_functions.o gspca_benq-objs := benq.o gspca_conex-objs := conex.o gspca_cpia1-objs := cpia1.o diff --git a/drivers/media/video/gspca/autogain_functions.c b/drivers/media/video/gspca/autogain_functions.c new file mode 100644 index 000000000000..67db674bb044 --- /dev/null +++ b/drivers/media/video/gspca/autogain_functions.c @@ -0,0 +1,178 @@ +/* + * Functions for auto gain. + * + * Copyright (C) 2010-2012 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "gspca.h" + +/* auto gain and exposure algorithm based on the knee algorithm described here: + http://ytse.tricolour.net/docs/LowLightOptimization.html + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +int gspca_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone, + int gain_knee, + int exposure_knee) +{ + s32 gain, orig_gain, exposure, orig_exposure; + int i, steps, retval = 0; + + if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) + return 0; + + orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); + orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = abs(desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + for (i = 0; i < steps; i++) { + if (avg_lum > desired_avg_lum) { + if (gain > gain_knee) + gain--; + else if (exposure > exposure_knee) + exposure--; + else if (gain > gspca_dev->gain->default_value) + gain--; + else if (exposure > gspca_dev->exposure->minimum) + exposure--; + else if (gain > gspca_dev->gain->minimum) + gain--; + else + break; + } else { + if (gain < gspca_dev->gain->default_value) + gain++; + else if (exposure < exposure_knee) + exposure++; + else if (gain < gain_knee) + gain++; + else if (exposure < gspca_dev->exposure->maximum) + exposure++; + else if (gain < gspca_dev->gain->maximum) + gain++; + else + break; + } + } + + if (gain != orig_gain) { + v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); + retval = 1; + } + if (exposure != orig_exposure) { + v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +EXPORT_SYMBOL(gspca_expo_autogain); + +/* Autogain + exposure algorithm for cameras with a coarse exposure control + (usually this means we can only control the clockdiv to change exposure) + As changing the clockdiv so that the fps drops from 30 to 15 fps for + example, will lead to a huge exposure change (it effectively doubles), + this algorithm normally tries to only adjust the gain (between 40 and + 80 %) and if that does not help, only then changes exposure. This leads + to a much more stable image then using the knee algorithm which at + certain points of the knee graph will only try to adjust exposure, + which leads to oscilating as one exposure step is huge. + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +int gspca_coarse_grained_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone) +{ + s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure; + int steps, retval = 0; + + if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) + return 0; + + orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); + orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + + gain_low = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + 5 * 2 + gspca_dev->gain->minimum; + gain_high = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + 5 * 4 + gspca_dev->gain->minimum; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = (desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + if ((gain + steps) > gain_high && + exposure < gspca_dev->exposure->maximum) { + gain = gain_high; + gspca_dev->exp_too_low_cnt++; + gspca_dev->exp_too_high_cnt = 0; + } else if ((gain + steps) < gain_low && + exposure > gspca_dev->exposure->minimum) { + gain = gain_low; + gspca_dev->exp_too_high_cnt++; + gspca_dev->exp_too_low_cnt = 0; + } else { + gain += steps; + if (gain > gspca_dev->gain->maximum) + gain = gspca_dev->gain->maximum; + else if (gain < gspca_dev->gain->minimum) + gain = gspca_dev->gain->minimum; + gspca_dev->exp_too_high_cnt = 0; + gspca_dev->exp_too_low_cnt = 0; + } + + if (gspca_dev->exp_too_high_cnt > 3) { + exposure--; + gspca_dev->exp_too_high_cnt = 0; + } else if (gspca_dev->exp_too_low_cnt > 3) { + exposure++; + gspca_dev->exp_too_low_cnt = 0; + } + + if (gain != orig_gain) { + v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); + retval = 1; + } + if (exposure != orig_exposure) { + v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 436d881ce443..449ff8e37fe7 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -174,6 +175,15 @@ struct gspca_dev { unsigned ctrl_dis; /* disabled controls (bit map) */ unsigned ctrl_inac; /* inactive controls (bit map) */ + /* autogain and exposure or gain control cluster, these are global as + the autogain/exposure functions in autogain_functions.c use them */ + struct { + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + int exp_too_low_cnt, exp_too_high_cnt; + }; + #define USB_BUF_SZ 64 __u8 *usb_buf; /* buffer for USB exchanges */ struct urb *urb[MAX_NURBS]; @@ -242,4 +252,9 @@ int gspca_resume(struct usb_interface *intf); #endif int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); +int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum, + int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); +int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev, + int avg_lum, int desired_avg_lum, int deadzone); + #endif /* GSPCAV2_H */ -- cgit v1.2.3 From 45432d41a2eebf5daaacb81de37fbfffc0a8faa7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 7 May 2012 06:53:27 -0300 Subject: [media] gspca_gl860: Add a present check to sd_stop0 The sensor specific dev_post_unset_alt functions all try to write to the bridge, and none free any memory, so they should be skipped if stop0 is called on disconnection. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gl860/gl860.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index c84e26006fc3..c549574c1c7e 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -405,6 +405,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (!sd->gspca_dev.present) + return; + return sd->dev_post_unset_alt(gspca_dev); } -- cgit v1.2.3 From fba11fed8aad86c10cde062ba0b76e8f8551f256 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 6 May 2012 09:28:23 -0300 Subject: [media] gspca_zc3xx: Fix setting of jpeg quality while streaming When the user changes the JPEG quality while the camera is streaming, the driver should not only change the JPEG headers send to userspace, but also actually tell the camera to use a different quantization table. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 8f21bae46ef8..33a2aab1dca1 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -5923,6 +5923,8 @@ static void setquality(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; s8 reg07; + jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]); + reg07 = 0; switch (sd->sensor) { case SENSOR_OV7620: @@ -6886,7 +6888,6 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } setquality(gspca_dev); - jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]); setlightfreq(gspca_dev); switch (sd->sensor) { @@ -7042,7 +7043,7 @@ static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val) sd->reg08 = i; sd->ctrls[QUALITY].val = jpeg_qual[i]; if (gspca_dev->streaming) - jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + setquality(gspca_dev); return gspca_dev->usb_err; } -- cgit v1.2.3 From 83fb2e2eaae5a3b4b3503f1e305451ed9d1a3761 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 6 May 2012 09:28:24 -0300 Subject: [media] gspca_zc3xx: Fix JPEG quality setting code The current code is using bits 0-1 of register 8 of the zc3xx controller to set the JPEG quality, but the correct bits are bits 1-2. Bit 0 selects between truncation or rounding in the quantization phase of the compression, since rounding generally gives better results it should thus always be 1. This patch also corrects the quality percentages which belong to the 4 different settings. Last this patch removes the different reg 8 defaults depending on the sensor type. Some of them where going for a default quality setting of 50%, which generally is not necessary in any way and results in poor image quality. 75% is a good default to use for all scenarios. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 64 ++++++++++++++------------------------- 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 33a2aab1dca1..3c6db02fe74b 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -32,7 +32,7 @@ MODULE_LICENSE("GPL"); static int force_sensor = -1; -#define REG08_DEF 3 /* default JPEG compression (70%) */ +#define REG08_DEF 3 /* default JPEG compression (75%) */ #include "zc3xx-reg.h" /* controls */ @@ -193,10 +193,10 @@ static const struct ctrl sd_ctrls[NCTRLS] = { .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Compression Quality", - .minimum = 40, - .maximum = 70, + .minimum = 50, + .maximum = 94, .step = 1, - .default_value = 70 /* updated in sd_init() */ + .default_value = 75, }, .set = sd_setquality }, @@ -241,8 +241,8 @@ static const struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; -/* bridge reg08 -> JPEG quality conversion table */ -static u8 jpeg_qual[] = {40, 50, 60, 70, /*80*/}; +/* bridge reg08 bits 1-2 -> JPEG quality conversion table */ +static u8 jpeg_qual[] = {50, 75, 87, 94}; /* usb exchanges */ struct usb_action { @@ -5923,7 +5923,7 @@ static void setquality(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; s8 reg07; - jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]); + jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]); reg07 = 0; switch (sd->sensor) { @@ -6079,11 +6079,12 @@ static void transfer_update(struct work_struct *work) struct sd *sd = container_of(work, struct sd, work); struct gspca_dev *gspca_dev = &sd->gspca_dev; int change, good; - u8 reg07, reg11; + u8 reg07, qual, reg11; /* synchronize with the main driver and initialize the registers */ mutex_lock(&gspca_dev->usb_lock); reg07 = 0; /* max */ + qual = sd->reg08 >> 1; reg_w(gspca_dev, reg07, 0x0007); reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); mutex_unlock(&gspca_dev->usb_lock); @@ -6109,9 +6110,9 @@ static void transfer_update(struct work_struct *work) case 0: /* max */ reg07 = sd->sensor == SENSOR_HV7131R ? 0x30 : 0x32; - if (sd->reg08 != 0) { + if (qual != 0) { change = 3; - sd->reg08--; + qual--; } break; case 0x32: @@ -6144,10 +6145,10 @@ static void transfer_update(struct work_struct *work) } } } else { /* reg07 max */ - if (sd->reg08 < sizeof jpeg_qual - 1) { + if (qual < sizeof jpeg_qual - 1) { good++; if (good > 10) { - sd->reg08++; + qual++; change = 2; } } @@ -6162,15 +6163,16 @@ static void transfer_update(struct work_struct *work) goto err; } if (change & 2) { + sd->reg08 = (qual << 1) | 1; reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); if (gspca_dev->usb_err < 0 || !gspca_dev->present || !gspca_dev->streaming) goto err; - sd->ctrls[QUALITY].val = jpeg_qual[sd->reg08]; + sd->ctrls[QUALITY].val = jpeg_qual[qual]; jpeg_set_qual(sd->jpeg_hdr, - jpeg_qual[sd->reg08]); + jpeg_qual[qual]); } } mutex_unlock(&gspca_dev->usb_lock); @@ -6562,27 +6564,6 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_PO2030] = 1, [SENSOR_TAS5130C] = 1, }; - static const u8 reg08_tb[SENSOR_MAX] = { - [SENSOR_ADCM2700] = 1, - [SENSOR_CS2102] = 3, - [SENSOR_CS2102K] = 3, - [SENSOR_GC0303] = 2, - [SENSOR_GC0305] = 3, - [SENSOR_HDCS2020] = 1, - [SENSOR_HV7131B] = 3, - [SENSOR_HV7131R] = 3, - [SENSOR_ICM105A] = 3, - [SENSOR_MC501CB] = 3, - [SENSOR_MT9V111_1] = 3, - [SENSOR_MT9V111_3] = 3, - [SENSOR_OV7620] = 1, - [SENSOR_OV7630C] = 3, - [SENSOR_PAS106] = 3, - [SENSOR_PAS202B] = 3, - [SENSOR_PB0330] = 3, - [SENSOR_PO2030] = 2, - [SENSOR_TAS5130C] = 3, - }; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) @@ -6734,8 +6715,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } sd->ctrls[GAMMA].def = gamma[sd->sensor]; - sd->reg08 = reg08_tb[sd->sensor]; - sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08]; + sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08 >> 1]; sd->ctrls[QUALITY].min = jpeg_qual[0]; sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1]; @@ -7030,17 +7010,17 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - int i; + int i, qual = sd->reg08 >> 1; - for (i = 0; i < ARRAY_SIZE(jpeg_qual) - 1; i++) { + for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) { if (val <= jpeg_qual[i]) break; } if (i > 0 - && i == sd->reg08 - && val < jpeg_qual[sd->reg08]) + && i == qual + && val < jpeg_qual[i]) i--; - sd->reg08 = i; + sd->reg08 = (i << 1) | 1; sd->ctrls[QUALITY].val = jpeg_qual[i]; if (gspca_dev->streaming) setquality(gspca_dev); -- cgit v1.2.3 From 55db765c54cd9506524c2945e3905fcf9215ea71 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 6 May 2012 09:28:25 -0300 Subject: [media] gspca_zc3xx: Always automatically adjust BRC as needed Always automatically adjust the Bit Rate Control setting as needed, independent of the sensor type. BRC is needed to not run out of bandwidth with higher quality settings independent of the sensor. Also only automatically adjust BRC, and don't adjust the JPEG quality control automatically, as that is not needed and leads to ugly flashes when it is changed. Note that before this patch-set the quality was never changed either due to the bugs in the quality handling fixed in previous patches in this set. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 159 +++++++++++++------------------------- 1 file changed, 53 insertions(+), 106 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 3c6db02fe74b..54735dd61bb8 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -5921,22 +5921,8 @@ static void setexposure(struct gspca_dev *gspca_dev) static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - s8 reg07; - jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]); - - reg07 = 0; - switch (sd->sensor) { - case SENSOR_OV7620: - reg07 = 0x30; - break; - case SENSOR_HV7131R: - case SENSOR_PAS202B: - return; /* done by work queue */ - } reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); - if (reg07 != 0) - reg_w(gspca_dev, reg07, 0x0007); } /* Matches the sensor's internal frame rate to the lighting frequency. @@ -6070,110 +6056,63 @@ static void setautogain(struct gspca_dev *gspca_dev) reg_w(gspca_dev, autoval, 0x0180); } -/* update the transfer parameters */ -/* This function is executed from a work queue. */ -/* The exact use of the bridge registers 07 and 08 is not known. - * The following algorithm has been adapted from ms-win traces */ +/* + * Update the transfer parameters. + * This function is executed from a work queue. + */ static void transfer_update(struct work_struct *work) { struct sd *sd = container_of(work, struct sd, work); struct gspca_dev *gspca_dev = &sd->gspca_dev; int change, good; - u8 reg07, qual, reg11; + u8 reg07, reg11; - /* synchronize with the main driver and initialize the registers */ - mutex_lock(&gspca_dev->usb_lock); - reg07 = 0; /* max */ - qual = sd->reg08 >> 1; - reg_w(gspca_dev, reg07, 0x0007); - reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); - mutex_unlock(&gspca_dev->usb_lock); + /* reg07 gets set to 0 by sd_start before starting us */ + reg07 = 0; good = 0; for (;;) { msleep(100); - /* get the transfer status */ - /* the bit 0 of the bridge register 11 indicates overflow */ mutex_lock(&gspca_dev->usb_lock); if (gspca_dev->frozen || !gspca_dev->dev || !gspca_dev->streaming) goto err; + + /* Bit 0 of register 11 indicates FIFO overflow */ + gspca_dev->usb_err = 0; reg11 = reg_r(gspca_dev, 0x0011); - if (gspca_dev->usb_err < 0 - || !gspca_dev->present || !gspca_dev->streaming) + if (gspca_dev->usb_err) goto err; change = reg11 & 0x01; if (change) { /* overflow */ - switch (reg07) { - case 0: /* max */ - reg07 = sd->sensor == SENSOR_HV7131R - ? 0x30 : 0x32; - if (qual != 0) { - change = 3; - qual--; - } - break; - case 0x32: - reg07 -= 4; - break; - default: - reg07 -= 2; - break; - case 2: - change = 0; /* already min */ - break; - } good = 0; + + if (reg07 == 0) /* Bit Rate Control not enabled? */ + reg07 = 0x32; /* Allow 98 bytes / unit */ + else if (reg07 > 2) + reg07 -= 2; /* Decrease allowed bytes / unit */ + else + change = 0; } else { /* no overflow */ - if (reg07 != 0) { /* if not max */ - good++; - if (good >= 10) { - good = 0; + good++; + if (good >= 10) { + good = 0; + if (reg07) { /* BRC enabled? */ change = 1; - reg07 += 2; - switch (reg07) { - case 0x30: - if (sd->sensor == SENSOR_PAS202B) - reg07 += 2; - break; - case 0x32: - case 0x34: + if (reg07 < 0x32) + reg07 += 2; + else reg07 = 0; - break; - } - } - } else { /* reg07 max */ - if (qual < sizeof jpeg_qual - 1) { - good++; - if (good > 10) { - qual++; - change = 2; - } } } } if (change) { - if (change & 1) { - reg_w(gspca_dev, reg07, 0x0007); - if (gspca_dev->usb_err < 0 - || !gspca_dev->present - || !gspca_dev->streaming) - goto err; - } - if (change & 2) { - sd->reg08 = (qual << 1) | 1; - reg_w(gspca_dev, sd->reg08, - ZC3XX_R008_CLOCKSETTING); - if (gspca_dev->usb_err < 0 - || !gspca_dev->present - || !gspca_dev->streaming) - goto err; - sd->ctrls[QUALITY].val = jpeg_qual[qual]; - jpeg_set_qual(sd->jpeg_hdr, - jpeg_qual[qual]); - } + gspca_dev->usb_err = 0; + reg_w(gspca_dev, reg07, 0x0007); + if (gspca_dev->usb_err) + goto err; } mutex_unlock(&gspca_dev->usb_lock); } @@ -6721,14 +6660,10 @@ static int sd_init(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_HV7131R: - gspca_dev->ctrl_dis = (1 << QUALITY); break; case SENSOR_OV7630C: gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE); break; - case SENSOR_PAS202B: - gspca_dev->ctrl_dis = (1 << QUALITY) | (1 << EXPOSURE); - break; default: gspca_dev->ctrl_dis = (1 << EXPOSURE); break; @@ -6743,6 +6678,13 @@ static int sd_init(struct gspca_dev *gspca_dev) return gspca_dev->usb_err; } +static int sd_pre_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + gspca_dev->cam.needs_full_bandwidth = (sd->reg08 >= 4) ? 1 : 0; + return 0; +} + static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -6868,6 +6810,8 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } setquality(gspca_dev); + /* Start with BRC disabled, transfer_update will enable it if needed */ + reg_w(gspca_dev, 0x00, 0x0007); setlightfreq(gspca_dev); switch (sd->sensor) { @@ -6905,19 +6849,14 @@ static int sd_start(struct gspca_dev *gspca_dev) setautogain(gspca_dev); - /* start the transfer update thread if needed */ - if (gspca_dev->usb_err >= 0) { - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_PAS202B: - sd->work_thread = - create_singlethread_workqueue(KBUILD_MODNAME); - queue_work(sd->work_thread, &sd->work); - break; - } - } + if (gspca_dev->usb_err < 0) + return gspca_dev->usb_err; - return gspca_dev->usb_err; + /* Start the transfer parameters update thread */ + sd->work_thread = create_singlethread_workqueue(KBUILD_MODNAME); + queue_work(sd->work_thread, &sd->work); + + return 0; } /* called on streamoff with alt 0 and on disconnect */ @@ -7020,8 +6959,15 @@ static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val) && i == qual && val < jpeg_qual[i]) i--; + + /* With high quality settings we need max bandwidth */ + if (i >= 2 && gspca_dev->streaming && + !gspca_dev->cam.needs_full_bandwidth) + return -EBUSY; + sd->reg08 = (i << 1) | 1; sd->ctrls[QUALITY].val = jpeg_qual[i]; + if (gspca_dev->streaming) setquality(gspca_dev); return gspca_dev->usb_err; @@ -7071,6 +7017,7 @@ static const struct sd_desc sd_desc = { .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .isoc_init = sd_pre_start, .start = sd_start, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, -- cgit v1.2.3 From 1b3bbcf5ab7250527a629ec1ffc36c8d1ea12681 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 6 May 2012 09:28:26 -0300 Subject: [media] gspca_zc3xx: Disable the highest quality setting as it is not usable Even with BRC the highest quality setting is not usable, BRC strips so much data from each MCU that the quality becomes worse then using a lower quality setting to begin with. Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 54735dd61bb8..998017eacc1f 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -194,7 +194,7 @@ static const struct ctrl sd_ctrls[NCTRLS] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Compression Quality", .minimum = 50, - .maximum = 94, + .maximum = 87, .step = 1, .default_value = 75, }, @@ -241,8 +241,11 @@ static const struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; -/* bridge reg08 bits 1-2 -> JPEG quality conversion table */ -static u8 jpeg_qual[] = {50, 75, 87, 94}; +/* + * Bridge reg08 bits 1-2 -> JPEG quality conversion table. Note the highest + * quality setting is not usable as USB 1 does not have enough bandwidth. + */ +static u8 jpeg_qual[] = {50, 75, 87, /* 94 */}; /* usb exchanges */ struct usb_action { -- cgit v1.2.3 From c06ba2804a50076cc0c5a4d65466a37b8eaa4455 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:28 -0300 Subject: [media] gspca_zc3xx: Convert to the control framework The initial version was done by HV, corrections were made by HdG, and some final small changes again by HV. Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 438 +++++++++++++++----------------------- 1 file changed, 170 insertions(+), 268 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 998017eacc1f..a8839fb3c1d6 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -35,26 +35,23 @@ static int force_sensor = -1; #define REG08_DEF 3 /* default JPEG compression (75%) */ #include "zc3xx-reg.h" -/* controls */ -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - EXPOSURE, - GAMMA, - AUTOGAIN, - LIGHTFREQ, - SHARPNESS, - QUALITY, - NCTRLS /* number of controls */ -}; - -#define AUTOGAIN_DEF 1 - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct gspca_ctrl ctrls[NCTRLS]; + struct v4l2_ctrl_handler ctrl_handler; + struct { /* gamma/brightness/contrast control cluster */ + struct v4l2_ctrl *gamma; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + }; + struct { /* autogain/exposure control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + }; + struct v4l2_ctrl *plfreq; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *jpegqual; struct work_struct work; struct workqueue_struct *work_thread; @@ -94,114 +91,6 @@ enum sensors { SENSOR_MAX }; -/* V4L2 controls supported by the driver */ -static void setcontrast(struct gspca_dev *gspca_dev); -static void setexposure(struct gspca_dev *gspca_dev); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static void setlightfreq(struct gspca_dev *gspca_dev); -static void setsharpness(struct gspca_dev *gspca_dev); -static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - }, - .set_control = setcontrast - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - }, - .set_control = setcontrast - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0x30d, - .maximum = 0x493e, - .step = 1, - .default_value = 0x927 - }, - .set_control = setexposure - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 1, - .maximum = 6, - .step = 1, - .default_value = 4, - }, - .set_control = setcontrast - }, -[AUTOGAIN] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = AUTOGAIN_DEF, - .flags = V4L2_CTRL_FLAG_UPDATE - }, - .set = sd_setautogain - }, -[LIGHTFREQ] = { - { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Light frequency filter", - .minimum = 0, - .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ - .step = 1, - .default_value = 0, - }, - .set_control = setlightfreq - }, -[SHARPNESS] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 2, - }, - .set_control = setsharpness - }, -[QUALITY] = { - { - .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Compression Quality", - .minimum = 50, - .maximum = 87, - .step = 1, - .default_value = 75, - }, - .set = sd_setquality - }, -}; - static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, @@ -5821,10 +5710,8 @@ static void setmatrix(struct gspca_dev *gspca_dev) reg_w(gspca_dev, matrix[i], 0x010a + i); } -static void setsharpness(struct gspca_dev *gspca_dev) +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - int sharpness; static const u8 sharpness_tb[][2] = { {0x02, 0x03}, {0x04, 0x07}, @@ -5832,19 +5719,18 @@ static void setsharpness(struct gspca_dev *gspca_dev) {0x10, 0x1e} }; - sharpness = sd->ctrls[SHARPNESS].val; - reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6); + reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6); reg_r(gspca_dev, 0x01c8); reg_r(gspca_dev, 0x01c9); reg_r(gspca_dev, 0x01ca); - reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb); + reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb); } -static void setcontrast(struct gspca_dev *gspca_dev) +static void setcontrast(struct gspca_dev *gspca_dev, + s32 gamma, s32 brightness, s32 contrast) { - struct sd *sd = (struct sd *) gspca_dev; const u8 *Tgamma; - int g, i, brightness, contrast, adj, gp1, gp2; + int g, i, adj, gp1, gp2; u8 gr[16]; static const u8 delta_b[16] = /* delta for brightness */ {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d, @@ -5867,10 +5753,10 @@ static void setcontrast(struct gspca_dev *gspca_dev) 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}, }; - Tgamma = gamma_tb[sd->ctrls[GAMMA].val - 1]; + Tgamma = gamma_tb[gamma - 1]; - contrast = ((int) sd->ctrls[CONTRAST].val - 128); /* -128 / 127 */ - brightness = ((int) sd->ctrls[BRIGHTNESS].val - 128); /* -128 / 92 */ + contrast -= 128; /* -128 / 127 */ + brightness -= 128; /* -128 / 92 */ adj = 0; gp1 = gp2 = 0; for (i = 0; i < 16; i++) { @@ -5897,25 +5783,15 @@ static void setcontrast(struct gspca_dev *gspca_dev) reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */ } -static void getexposure(struct gspca_dev *gspca_dev) +static s32 getexposure(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor != SENSOR_HV7131R) - return; - sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9) + return (i2c_read(gspca_dev, 0x25) << 9) | (i2c_read(gspca_dev, 0x26) << 1) | (i2c_read(gspca_dev, 0x27) >> 7); } -static void setexposure(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - int val; - - if (sd->sensor != SENSOR_HV7131R) - return; - val = sd->ctrls[EXPOSURE].val; i2c_write(gspca_dev, 0x25, val >> 9, 0x00); i2c_write(gspca_dev, 0x26, val >> 1, 0x00); i2c_write(gspca_dev, 0x27, val << 7, 0x00); @@ -5934,7 +5810,7 @@ static void setquality(struct gspca_dev *gspca_dev) * 60Hz, for American lighting * 0 = No Fliker (for outdoore usage) */ -static void setlightfreq(struct gspca_dev *gspca_dev) +static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) { struct sd *sd = (struct sd *) gspca_dev; int i, mode; @@ -6018,7 +5894,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev) tas5130c_60HZ, tas5130c_60HZScale}, }; - i = sd->ctrls[LIGHTFREQ].val * 2; + i = val * 2; mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; if (mode) i++; /* 320x240 */ @@ -6028,14 +5904,14 @@ static void setlightfreq(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, zc3_freq); switch (sd->sensor) { case SENSOR_GC0305: - if (mode /* if 320x240 */ - && sd->ctrls[LIGHTFREQ].val == 1) /* and 50Hz */ + if (mode /* if 320x240 */ + && val == 1) /* and 50Hz */ reg_w(gspca_dev, 0x85, 0x018d); /* win: 0x80, 0x018d */ break; case SENSOR_OV7620: - if (!mode) { /* if 640x480 */ - if (sd->ctrls[LIGHTFREQ].val != 0) /* and filter */ + if (!mode) { /* if 640x480 */ + if (val != 0) /* and filter */ reg_w(gspca_dev, 0x40, 0x0002); else reg_w(gspca_dev, 0x44, 0x0002); @@ -6047,16 +5923,9 @@ static void setlightfreq(struct gspca_dev *gspca_dev) } } -static void setautogain(struct gspca_dev *gspca_dev) +static void setautogain(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - u8 autoval; - - if (sd->ctrls[AUTOGAIN].val) - autoval = 0x42; - else - autoval = 0x02; - reg_w(gspca_dev, autoval, 0x0180); + reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180); } /* @@ -6450,7 +6319,6 @@ static int sd_config(struct gspca_dev *gspca_dev, /* define some sensors from the vendor/product */ sd->sensor = id->driver_info; - gspca_dev->cam.ctrls = sd->ctrls; sd->reg08 = REG08_DEF; INIT_WORK(&sd->work, transfer_update); @@ -6458,12 +6326,85 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) +static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - int sensor; + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + gspca_dev->usb_err = 0; + if (ctrl->val && sd->exposure && gspca_dev->streaming) + sd->exposure->val = getexposure(gspca_dev); + return gspca_dev->usb_err; + } + return -EINVAL; +} + +static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + int i, qual; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) { + qual = sd->reg08 >> 1; + + for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) { + if (ctrl->val <= jpeg_qual[i]) + break; + } + if (i > 0 && i == qual && ctrl->val < jpeg_qual[i]) + i--; + + /* With high quality settings we need max bandwidth */ + if (i >= 2 && gspca_dev->streaming && + !gspca_dev->cam.needs_full_bandwidth) + return -EBUSY; + + sd->reg08 = (i << 1) | 1; + ctrl->val = jpeg_qual[i]; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* gamma/brightness/contrast cluster */ + case V4L2_CID_GAMMA: + setcontrast(gspca_dev, sd->gamma->val, + sd->brightness->val, sd->contrast->val); + break; + /* autogain/exposure cluster */ + case V4L2_CID_AUTOGAIN: + setautogain(gspca_dev, ctrl->val); + if (!gspca_dev->usb_err && !ctrl->val && sd->exposure) + setexposure(gspca_dev, sd->exposure->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setlightfreq(gspca_dev, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + setquality(gspca_dev); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops zcxx_ctrl_ops = { + .g_volatile_ctrl = zcxx_g_volatile_ctrl, + .s_ctrl = zcxx_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; static const u8 gamma[SENSOR_MAX] = { [SENSOR_ADCM2700] = 4, [SENSOR_CS2102] = 4, @@ -6485,6 +6426,48 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_PO2030] = 4, [SENSOR_TAS5130C] = 3, }; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 8); + sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]); + if (sd->sensor == SENSOR_HV7131R) + sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927); + sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (sd->sensor != SENSOR_OV7630C) + sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); + sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 3, 1, + sd->sensor == SENSOR_PO2030 ? 0 : 2); + sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1, + jpeg_qual[REG08_DEF >> 1]); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(3, &sd->gamma); + if (sd->sensor == SENSOR_HV7131R) + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true); + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + int sensor; static const u8 mode_tb[SENSOR_MAX] = { [SENSOR_ADCM2700] = 2, [SENSOR_CS2102] = 1, @@ -6614,7 +6597,6 @@ static int sd_init(struct gspca_dev *gspca_dev) case 0x2030: PDEBUG(D_PROBE, "Find Sensor PO2030"); sd->sensor = SENSOR_PO2030; - sd->ctrls[SHARPNESS].def = 0; /* from win traces */ break; case 0x7620: PDEBUG(D_PROBE, "Find Sensor OV7620"); @@ -6656,26 +6638,6 @@ static int sd_init(struct gspca_dev *gspca_dev) break; } - sd->ctrls[GAMMA].def = gamma[sd->sensor]; - sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08 >> 1]; - sd->ctrls[QUALITY].min = jpeg_qual[0]; - sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1]; - - switch (sd->sensor) { - case SENSOR_HV7131R: - break; - case SENSOR_OV7630C: - gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE); - break; - default: - gspca_dev->ctrl_dis = (1 << EXPOSURE); - break; - } -#if AUTOGAIN_DEF - if (sd->ctrls[AUTOGAIN].val) - gspca_dev->ctrl_inac = (1 << EXPOSURE); -#endif - /* switch off the led */ reg_w(gspca_dev, 0x01, 0x0000); return gspca_dev->usb_err; @@ -6792,7 +6754,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x03, 0x0008); break; } - setsharpness(gspca_dev); + setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); /* set the gamma tables when not set */ switch (sd->sensor) { @@ -6801,7 +6763,9 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OV7630C: break; default: - setcontrast(gspca_dev); + setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma), + v4l2_ctrl_g_ctrl(sd->brightness), + v4l2_ctrl_g_ctrl(sd->contrast)); break; } setmatrix(gspca_dev); /* one more time? */ @@ -6815,7 +6779,8 @@ static int sd_start(struct gspca_dev *gspca_dev) setquality(gspca_dev); /* Start with BRC disabled, transfer_update will enable it if needed */ reg_w(gspca_dev, 0x00, 0x0007); - setlightfreq(gspca_dev); + if (sd->plfreq) + setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq)); switch (sd->sensor) { case SENSOR_ADCM2700: @@ -6826,7 +6791,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x40, 0x0117); break; case SENSOR_HV7131R: - setexposure(gspca_dev); + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN); break; case SENSOR_GC0305: @@ -6850,7 +6815,7 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } - setautogain(gspca_dev); + setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); if (gspca_dev->usb_err < 0) return gspca_dev->usb_err; @@ -6911,79 +6876,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->ctrls[AUTOGAIN].val = val; - if (val) { - gspca_dev->ctrl_inac |= (1 << EXPOSURE); - } else { - gspca_dev->ctrl_inac &= ~(1 << EXPOSURE); - if (gspca_dev->streaming) - getexposure(gspca_dev); - } - if (gspca_dev->streaming) - setautogain(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_querymenu(struct gspca_dev *gspca_dev, - struct v4l2_querymenu *menu) -{ - switch (menu->id) { - case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ - strcpy((char *) menu->name, "NoFliker"); - return 0; - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; - } - return -EINVAL; -} - -static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, qual = sd->reg08 >> 1; - - for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) { - if (val <= jpeg_qual[i]) - break; - } - if (i > 0 - && i == qual - && val < jpeg_qual[i]) - i--; - - /* With high quality settings we need max bandwidth */ - if (i >= 2 && gspca_dev->streaming && - !gspca_dev->cam.needs_full_bandwidth) - return -EBUSY; - - sd->reg08 = (i << 1) | 1; - sd->ctrls[QUALITY].val = jpeg_qual[i]; - - if (gspca_dev->streaming) - setquality(gspca_dev); - return gspca_dev->usb_err; -} - static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; + int ret; - sd_setquality(gspca_dev, jcomp->quality); - jcomp->quality = sd->ctrls[QUALITY].val; - return gspca_dev->usb_err; + ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + if (ret) + return ret; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); + return 0; } static int sd_get_jcomp(struct gspca_dev *gspca_dev, @@ -6992,7 +6895,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->ctrls[QUALITY].val; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; @@ -7016,15 +6919,13 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, static const struct sd_desc sd_desc = { .name = KBUILD_MODNAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .isoc_init = sd_pre_start, .start = sd_start, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, - .querymenu = sd_querymenu, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) @@ -7108,6 +7009,7 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, + .reset_resume = gspca_resume, #endif }; -- cgit v1.2.3 From 63069da1c8ef0abcdb74b0ea1c461d23fb9181d9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:29 -0300 Subject: [media] gcpca_sn9c20x: Convert to the control framework HdG: Small fix: don't register some controls for sensors which don't have an implementation for them. Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 495 +++++++++++++++--------------------- 1 file changed, 212 insertions(+), 283 deletions(-) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 5285a519c1f3..f35f1a0d1caf 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -66,28 +66,32 @@ MODULE_LICENSE("GPL"); #define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */ #define FLIP_DETECT 0x4 -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - SATURATION, - HUE, - GAMMA, - BLUE, - RED, - VFLIP, - HFLIP, - EXPOSURE, - GAIN, - AUTOGAIN, - QUALITY, - NCTRLS /* number of controls */ -}; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; - struct gspca_ctrl ctrls[NCTRLS]; + struct v4l2_ctrl_handler ctrl_handler; + struct { /* color control cluster */ + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + }; + struct { /* blue/red balance control cluster */ + struct v4l2_ctrl *blue; + struct v4l2_ctrl *red; + }; + struct { /* h/vflip control cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + struct v4l2_ctrl *gamma; + struct { /* autogain and exposure or gain control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *jpegqual; struct work_struct work; struct workqueue_struct *work_thread; @@ -167,175 +171,6 @@ static const struct dmi_system_id flip_dmi_table[] = { {} }; -static void set_cmatrix(struct gspca_dev *gspca_dev); -static void set_gamma(struct gspca_dev *gspca_dev); -static void set_redblue(struct gspca_dev *gspca_dev); -static void set_hvflip(struct gspca_dev *gspca_dev); -static void set_exposure(struct gspca_dev *gspca_dev); -static void set_gain(struct gspca_dev *gspca_dev); -static void set_quality(struct gspca_dev *gspca_dev); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f - }, - .set_control = set_cmatrix - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f - }, - .set_control = set_cmatrix - }, -[SATURATION] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f - }, - .set_control = set_cmatrix - }, -[HUE] = { - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -180, - .maximum = 180, - .step = 1, - .default_value = 0 - }, - .set_control = set_cmatrix - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x10 - }, - .set_control = set_gamma - }, -[BLUE] = { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 0x7f, - .step = 1, - .default_value = 0x28 - }, - .set_control = set_redblue - }, -[RED] = { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 0x7f, - .step = 1, - .default_value = 0x28 - }, - .set_control = set_redblue - }, -[HFLIP] = { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Horizontal Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = set_hvflip - }, -[VFLIP] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = set_hvflip - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 0x1780, - .step = 1, - .default_value = 0x33, - }, - .set_control = set_exposure - }, -[GAIN] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 28, - .step = 1, - .default_value = 0, - }, - .set_control = set_gain - }, -[AUTOGAIN] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - }, -[QUALITY] = { - { - .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Compression Quality", -#define QUALITY_MIN 50 -#define QUALITY_MAX 90 -#define QUALITY_DEF 80 - .minimum = QUALITY_MIN, - .maximum = QUALITY_MAX, - .step = 1, - .default_value = QUALITY_DEF, - }, - .set_control = set_quality - }, -}; - static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -1296,8 +1131,6 @@ static void ov9655_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("OV9655 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 2; } @@ -1312,9 +1145,6 @@ static void soi968_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("SOI968 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP) - | (1 << EXPOSURE); sd->hstart = 60; sd->vstart = 11; } @@ -1342,8 +1172,6 @@ static void ov7670_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("OV7670 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 0; sd->vstart = 1; } @@ -1380,9 +1208,6 @@ static void mt9v_init_sensor(struct gspca_dev *gspca_dev) pr_err("MT9V111 sensor initialization failed\n"); return; } - gspca_dev->ctrl_dis = (1 << EXPOSURE) - | (1 << AUTOGAIN) - | (1 << GAIN); sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V111; @@ -1424,8 +1249,6 @@ static void mt9m112_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("MT9M112 sensor initialization failed\n"); - gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) - | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; } @@ -1438,8 +1261,6 @@ static void mt9m111_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("MT9M111 sensor initialization failed\n"); - gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) - | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; } @@ -1472,8 +1293,6 @@ static void mt9m001_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("MT9M001 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 1; } @@ -1490,20 +1309,18 @@ static void hv7131r_init_sensor(struct gspca_dev *gspca_dev) sd->vstart = 1; } -static void set_cmatrix(struct gspca_dev *gspca_dev) +static void set_cmatrix(struct gspca_dev *gspca_dev, + s32 brightness, s32 contrast, s32 satur, s32 hue) { - struct sd *sd = (struct sd *) gspca_dev; - int satur; - s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val; + s32 hue_coord, hue_index = 180 + hue; u8 cmatrix[21]; memset(cmatrix, 0, sizeof cmatrix); - cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26; + cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26; cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; - cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80; + cmatrix[18] = brightness - 0x80; - satur = sd->ctrls[SATURATION].val; hue_coord = (hsv_red_x[hue_index] * satur) >> 8; cmatrix[6] = hue_coord; cmatrix[7] = (hue_coord >> 8) & 0x0f; @@ -1531,11 +1348,10 @@ static void set_cmatrix(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x10e1, cmatrix, 21); } -static void set_gamma(struct gspca_dev *gspca_dev) +static void set_gamma(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; u8 gamma[17]; - u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100; + u8 gval = val * 0xb8 / 0x100; gamma[0] = 0x0a; gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); @@ -1558,26 +1374,21 @@ static void set_gamma(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x1190, gamma, 17); } -static void set_redblue(struct gspca_dev *gspca_dev) +static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red) { - struct sd *sd = (struct sd *) gspca_dev; - - reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val); - reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val); + reg_w1(gspca_dev, 0x118c, red); + reg_w1(gspca_dev, 0x118f, blue); } -static void set_hvflip(struct gspca_dev *gspca_dev) +static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) { - u8 value, tslb, hflip, vflip; + u8 value, tslb; u16 value2; struct sd *sd = (struct sd *) gspca_dev; if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) { - hflip = !sd->ctrls[HFLIP].val; - vflip = !sd->ctrls[VFLIP].val; - } else { - hflip = sd->ctrls[HFLIP].val; - vflip = sd->ctrls[VFLIP].val; + hflip = !hflip; + vflip = !vflip; } switch (sd->sensor) { @@ -1640,17 +1451,16 @@ static void set_hvflip(struct gspca_dev *gspca_dev) } } -static void set_exposure(struct gspca_dev *gspca_dev) +static void set_exposure(struct gspca_dev *gspca_dev, s32 expo) { struct sd *sd = (struct sd *) gspca_dev; u8 exp[8] = {sd->i2c_intf, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; - int expo, expo2; + int expo2; if (gspca_dev->streaming) exp[7] = 0x1e; - expo = sd->ctrls[EXPOSURE].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: @@ -1697,17 +1507,15 @@ static void set_exposure(struct gspca_dev *gspca_dev) i2c_w(gspca_dev, exp); } -static void set_gain(struct gspca_dev *gspca_dev) +static void set_gain(struct gspca_dev *gspca_dev, s32 g) { struct sd *sd = (struct sd *) gspca_dev; u8 gain[8] = {sd->i2c_intf, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; - int g; if (gspca_dev->streaming) gain[7] = 0x15; /* or 1d ? */ - g = sd->ctrls[GAIN].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: @@ -1746,11 +1554,11 @@ static void set_gain(struct gspca_dev *gspca_dev) i2c_w(gspca_dev, gain); } -static void set_quality(struct gspca_dev *gspca_dev) +static void set_quality(struct gspca_dev *gspca_dev, s32 val) { struct sd *sd = (struct sd *) gspca_dev; - jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + jpeg_set_qual(sd->jpeg_hdr, val); reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */ reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */ reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); @@ -1879,13 +1687,132 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->older_step = 0; sd->exposure_step = 16; - gspca_dev->cam.ctrls = sd->ctrls; - INIT_WORK(&sd->work, qual_upd); return 0; } +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* color control cluster */ + case V4L2_CID_BRIGHTNESS: + set_cmatrix(&sd->gspca_dev, sd->brightness->val, + sd->contrast->val, sd->saturation->val, sd->hue->val); + break; + case V4L2_CID_GAMMA: + set_gamma(&sd->gspca_dev, ctrl->val); + break; + /* blue/red balance cluster */ + case V4L2_CID_BLUE_BALANCE: + set_redblue(&sd->gspca_dev, sd->blue->val, sd->red->val); + break; + /* h/vflip cluster */ + case V4L2_CID_HFLIP: + set_hvflip(&sd->gspca_dev, sd->hflip->val, sd->vflip->val); + break; + /* standalone exposure control */ + case V4L2_CID_EXPOSURE: + set_exposure(&sd->gspca_dev, ctrl->val); + break; + /* standalone gain control */ + case V4L2_CID_GAIN: + set_gain(&sd->gspca_dev, ctrl->val); + break; + /* autogain + exposure or gain control cluster */ + case V4L2_CID_AUTOGAIN: + if (sd->sensor == SENSOR_SOI968) + set_gain(&sd->gspca_dev, sd->gain->val); + else + set_exposure(&sd->gspca_dev, sd->exposure->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + set_quality(&sd->gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 13); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 127); + sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 127); + sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HUE, -180, 180, 1, 0); + v4l2_ctrl_cluster(4, &sd->brightness); + + sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAMMA, 0, 255, 1, 0x10); + + sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28); + sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28); + v4l2_ctrl_cluster(2, &sd->blue); + + if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 && + sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 && + sd->sensor != SENSOR_MT9VPRB) { + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &sd->hflip); + } + + if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB && + sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 && + sd->sensor != SENSOR_MT9V111) + sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33); + + if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 && + sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) { + sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 28, 1, 0); + sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (sd->sensor == SENSOR_SOI968) + /* this sensor doesn't have the exposure control and + autogain is clustered with gain instead. This works + because sd->exposure == NULL. */ + v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false); + else + /* Otherwise autogain is clustered with exposure. */ + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); + } + + sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1978,7 +1905,6 @@ static int sd_init(struct gspca_dev *gspca_dev) pr_err("Unsupported sensor\n"); gspca_dev->usb_err = -ENODEV; } - return gspca_dev->usb_err; } @@ -2096,7 +2022,7 @@ static int sd_start(struct gspca_dev *gspca_dev) jpeg_define(sd->jpeg_hdr, height, width, 0x21); - jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); if (mode & MODE_RAW) fmt = 0x2d; @@ -2133,12 +2059,17 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x1189, scale); reg_w1(gspca_dev, 0x10e0, fmt); - set_cmatrix(gspca_dev); - set_gamma(gspca_dev); - set_redblue(gspca_dev); - set_gain(gspca_dev); - set_exposure(gspca_dev); - set_hvflip(gspca_dev); + set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness), + v4l2_ctrl_g_ctrl(sd->contrast), + v4l2_ctrl_g_ctrl(sd->saturation), + v4l2_ctrl_g_ctrl(sd->hue)); + set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); + set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue), + v4l2_ctrl_g_ctrl(sd->red)); + set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain)); + set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); + set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), + v4l2_ctrl_g_ctrl(sd->vflip)); reg_w1(gspca_dev, 0x1007, 0x20); reg_w1(gspca_dev, 0x1061, 0x03); @@ -2177,6 +2108,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev) static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; + s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure); + s32 max = sd->exposure->maximum - sd->exposure_step; + s32 min = sd->exposure->minimum + sd->exposure_step; s16 new_exp; /* @@ -2185,16 +2119,15 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) * and exposure steps */ if (avg_lum < MIN_AVG_LUM) { - if (sd->ctrls[EXPOSURE].val > 0x1770) + if (cur_exp > max) return; - new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step; - if (new_exp > 0x1770) - new_exp = 0x1770; - if (new_exp < 0x10) - new_exp = 0x10; - sd->ctrls[EXPOSURE].val = new_exp; - set_exposure(gspca_dev); + new_exp = cur_exp + sd->exposure_step; + if (new_exp > max) + new_exp = max; + if (new_exp < min) + new_exp = min; + v4l2_ctrl_s_ctrl(sd->exposure, new_exp); sd->older_step = sd->old_step; sd->old_step = 1; @@ -2205,15 +2138,14 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) sd->exposure_step += 2; } if (avg_lum > MAX_AVG_LUM) { - if (sd->ctrls[EXPOSURE].val < 0x10) + if (cur_exp < min) return; - new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step; - if (new_exp > 0x1700) - new_exp = 0x1770; - if (new_exp < 0x10) - new_exp = 0x10; - sd->ctrls[EXPOSURE].val = new_exp; - set_exposure(gspca_dev); + new_exp = cur_exp - sd->exposure_step; + if (new_exp > max) + new_exp = max; + if (new_exp < min) + new_exp = min; + v4l2_ctrl_s_ctrl(sd->exposure, new_exp); sd->older_step = sd->old_step; sd->old_step = 0; @@ -2227,19 +2159,12 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; + s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain); - if (avg_lum < MIN_AVG_LUM) { - if (sd->ctrls[GAIN].val + 1 <= 28) { - sd->ctrls[GAIN].val++; - set_gain(gspca_dev); - } - } - if (avg_lum > MAX_AVG_LUM) { - if (sd->ctrls[GAIN].val > 0) { - sd->ctrls[GAIN].val--; - set_gain(gspca_dev); - } - } + if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum) + v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1); + if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum) + v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1); } static void sd_dqcallback(struct gspca_dev *gspca_dev) @@ -2247,7 +2172,7 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int avg_lum; - if (!sd->ctrls[AUTOGAIN].val) + if (!v4l2_ctrl_g_ctrl(sd->autogain)) return; avg_lum = atomic_read(&sd->avg_lum); @@ -2263,10 +2188,11 @@ static void qual_upd(struct work_struct *work) { struct sd *sd = container_of(work, struct sd, work); struct gspca_dev *gspca_dev = &sd->gspca_dev; + s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual); mutex_lock(&gspca_dev->usb_lock); - PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val); - set_quality(gspca_dev); + PDEBUG(D_STREAM, "qual_upd %d%%", qual); + set_quality(gspca_dev, qual); mutex_unlock(&gspca_dev->usb_lock); } @@ -2315,14 +2241,18 @@ static void transfer_check(struct gspca_dev *gspca_dev, if (new_qual != 0) { sd->nchg += new_qual; if (sd->nchg < -6 || sd->nchg >= 12) { + /* Note: we are in interrupt context, so we can't + use v4l2_ctrl_g/s_ctrl here. Access the value + directly instead. */ + s32 curqual = sd->jpegqual->cur.val; sd->nchg = 0; - new_qual += sd->ctrls[QUALITY].val; - if (new_qual < QUALITY_MIN) - new_qual = QUALITY_MIN; - else if (new_qual > QUALITY_MAX) - new_qual = QUALITY_MAX; - if (new_qual != sd->ctrls[QUALITY].val) { - sd->ctrls[QUALITY].val = new_qual; + new_qual += curqual; + if (new_qual < sd->jpegqual->minimum) + new_qual = sd->jpegqual->minimum; + else if (new_qual > sd->jpegqual->maximum) + new_qual = sd->jpegqual->maximum; + if (new_qual != curqual) { + sd->jpegqual->cur.val = new_qual; queue_work(sd->work_thread, &sd->work); } } @@ -2402,10 +2332,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* sub-driver description */ static const struct sd_desc sd_desc = { .name = KBUILD_MODNAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .isoc_init = sd_isoc_init, .start = sd_start, .stopN = sd_stopN, -- cgit v1.2.3 From 7135d88495b0043c0d61bd85408542dc0cb0bca0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 12 May 2012 05:43:49 -0300 Subject: [media] gspca_sn9c20x: Whitespace fixes Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index f35f1a0d1caf..1758ed98cf16 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -1980,8 +1980,8 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev) if (intf->num_altsetting != 9) { pr_warn("sn9c20x camera with unknown number of alt " - "settings (%d), please report!\n", - intf->num_altsetting); + "settings (%d), please report!\n", + intf->num_altsetting); gspca_dev->alt = intf->num_altsetting; return 0; } -- cgit v1.2.3 From dec9c51442b5a524c137b6f4a54ec109a197a65a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:30 -0300 Subject: [media] gspca_stv06xx: Convert to the control framework HdG: 1) Let the gspca-core cleanup the controls on control-init error, like with the other converted sub drivers 2) Note this also fixes a bug in the hdcs1020 support which was wrongly reporting an exposure range of 0-65535, even though the effective range was only 0-255 Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/stv06xx/stv06xx.c | 18 +- drivers/media/video/gspca/stv06xx/stv06xx.h | 3 + drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c | 142 ++------ drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h | 7 +- drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c | 357 ++++++--------------- drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h | 12 +- drivers/media/video/gspca/stv06xx/stv06xx_sensor.h | 4 +- drivers/media/video/gspca/stv06xx/stv06xx_st6422.c | 234 ++++---------- drivers/media/video/gspca/stv06xx/stv06xx_st6422.h | 4 +- drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c | 197 +++--------- drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h | 8 +- 11 files changed, 275 insertions(+), 711 deletions(-) diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 91d99b4cc57b..3a8d034421fc 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -261,6 +261,17 @@ static int stv06xx_init(struct gspca_dev *gspca_dev) return (err < 0) ? err : 0; } +/* this function is called at probe time */ +static int stv06xx_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_PROBE, "Initializing controls"); + + gspca_dev->vdev.ctrl_handler = &sd->ctrl_handler; + return sd->sensor->init_controls(sd); +} + /* Start the camera */ static int stv06xx_start(struct gspca_dev *gspca_dev) { @@ -512,6 +523,7 @@ static const struct sd_desc sd_desc = { .name = MODULE_NAME, .config = stv06xx_config, .init = stv06xx_init, + .init_controls = stv06xx_init_controls, .start = stv06xx_start, .stopN = stv06xx_stopN, .pkt_scan = stv06xx_pkt_scan, @@ -594,11 +606,12 @@ static void sd_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); struct sd *sd = (struct sd *) gspca_dev; + void *priv = sd->sensor_priv; PDEBUG(D_PROBE, "Disconnecting the stv06xx device"); - if (sd->sensor->disconnect) - sd->sensor->disconnect(sd); + sd->sensor = NULL; gspca_disconnect(intf); + kfree(priv); } static struct usb_driver sd_driver = { @@ -609,6 +622,7 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, + .reset_resume = gspca_resume, #endif }; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index d270a5981afe..b338edcdbe0c 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -89,6 +89,9 @@ struct sd { /* A pointer to the currently connected sensor */ const struct stv06xx_sensor *sensor; + /* Control handler */ + struct v4l2_ctrl_handler ctrl_handler; + /* A pointer to the sd_desc struct */ struct sd_desc desc; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index a8698b7a7566..74cbd386600b 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -32,36 +32,6 @@ #include "stv06xx_hdcs.h" -static const struct ctrl hdcs1x00_ctrl[] = { - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_EXPOSURE, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_exposure, - .get = hdcs_get_exposure - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_gain, - .get = hdcs_get_gain - } -}; - static struct v4l2_pix_format hdcs1x00_mode[] = { { HDCS_1X00_DEF_WIDTH, @@ -76,36 +46,6 @@ static struct v4l2_pix_format hdcs1x00_mode[] = { } }; -static const struct ctrl hdcs1020_ctrl[] = { - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xffff, - .step = 0x1, - .default_value = HDCS_DEFAULT_EXPOSURE, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_exposure, - .get = hdcs_get_exposure - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_gain, - .get = hdcs_get_gain - } -}; - static struct v4l2_pix_format hdcs1020_mode[] = { { HDCS_1020_DEF_WIDTH, @@ -150,7 +90,6 @@ struct hdcs { } exp; int psmp; - u8 exp_cache, gain_cache; }; static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) @@ -232,16 +171,6 @@ static int hdcs_reset(struct sd *sd) return err; } -static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct hdcs *hdcs = sd->sensor_priv; - - *val = hdcs->exp_cache; - - return 0; -} - static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -260,9 +189,6 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) int cycles, err; u8 exp[14]; - val &= 0xff; - hdcs->exp_cache = val; - cycles = val * HDCS_CLK_FREQ_MHZ * 257; ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); @@ -336,12 +262,9 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) static int hdcs_set_gains(struct sd *sd, u8 g) { - struct hdcs *hdcs = sd->sensor_priv; int err; u8 gains[4]; - hdcs->gain_cache = g; - /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ if (g > 127) g = 0x80 | (g / 2); @@ -352,17 +275,7 @@ static int hdcs_set_gains(struct sd *sd, u8 g) gains[3] = g; err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); - return err; -} - -static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct hdcs *hdcs = sd->sensor_priv; - - *val = hdcs->gain_cache; - - return 0; + return err; } static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -420,6 +333,38 @@ static int hdcs_set_size(struct sd *sd, return err; } +static int hdcs_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err = hdcs_set_gain(&sd->gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = hdcs_set_exposure(&sd->gspca_dev, ctrl->val); + break; + } + return err; +} + +static const struct v4l2_ctrl_ops hdcs_ctrl_ops = { + .s_ctrl = hdcs_s_ctrl, +}; + +static int hdcs_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 2); + v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0xff, 1, HDCS_DEFAULT_EXPOSURE); + v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, + V4L2_CID_GAIN, 0, 0xff, 1, HDCS_DEFAULT_GAIN); + return hdl->error; +} + static int hdcs_probe_1x00(struct sd *sd) { struct hdcs *hdcs; @@ -434,8 +379,6 @@ static int hdcs_probe_1x00(struct sd *sd) sd->gspca_dev.cam.cam_mode = hdcs1x00_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode); - sd->desc.ctrls = hdcs1x00_ctrl; - sd->desc.nctrls = ARRAY_SIZE(hdcs1x00_ctrl); hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); if (!hdcs) @@ -493,8 +436,6 @@ static int hdcs_probe_1020(struct sd *sd) sd->gspca_dev.cam.cam_mode = hdcs1020_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode); - sd->desc.ctrls = hdcs1020_ctrl; - sd->desc.nctrls = ARRAY_SIZE(hdcs1020_ctrl); hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); if (!hdcs) @@ -537,12 +478,6 @@ static int hdcs_stop(struct sd *sd) return hdcs_set_state(sd, HDCS_STATE_SLEEP); } -static void hdcs_disconnect(struct sd *sd) -{ - PDEBUG(D_PROBE, "Disconnecting the sensor"); - kfree(sd->sensor_priv); -} - static int hdcs_init(struct sd *sd) { struct hdcs *hdcs = sd->sensor_priv; @@ -587,16 +522,7 @@ static int hdcs_init(struct sd *sd) if (err < 0) return err; - err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN); - if (err < 0) - return err; - - err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); - if (err < 0) - return err; - - err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE); - return err; + return hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); } static int hdcs_dump(struct sd *sd) diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h index a14a84a5079b..1ba9158d0102 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h @@ -131,14 +131,12 @@ static int hdcs_probe_1x00(struct sd *sd); static int hdcs_probe_1020(struct sd *sd); static int hdcs_start(struct sd *sd); static int hdcs_init(struct sd *sd); +static int hdcs_init_controls(struct sd *sd); static int hdcs_stop(struct sd *sd); static int hdcs_dump(struct sd *sd); -static void hdcs_disconnect(struct sd *sd); -static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val); static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val); const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { .name = "HP HDCS-1000/1100", @@ -152,10 +150,10 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { .max_packet_size = { 847 }, .init = hdcs_init, + .init_controls = hdcs_init_controls, .probe = hdcs_probe_1x00, .start = hdcs_start, .stop = hdcs_stop, - .disconnect = hdcs_disconnect, .dump = hdcs_dump, }; @@ -171,6 +169,7 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = { .max_packet_size = { 847 }, .init = hdcs_init, + .init_controls = hdcs_init_controls, .probe = hdcs_probe_1020, .start = hdcs_start, .stop = hdcs_stop, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c index 26f14fc4a135..c03b13e70b56 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c @@ -48,105 +48,16 @@ #include "stv06xx_pb0100.h" -static const struct ctrl pb0100_ctrl[] = { -#define GAIN_IDX 0 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128 - }, - .set = pb0100_set_gain, - .get = pb0100_get_gain - }, -#define RED_BALANCE_IDX 1 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = -255, - .maximum = 255, - .step = 1, - .default_value = 0 - }, - .set = pb0100_set_red_balance, - .get = pb0100_get_red_balance - }, -#define BLUE_BALANCE_IDX 2 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = -255, - .maximum = 255, - .step = 1, - .default_value = 0 - }, - .set = pb0100_set_blue_balance, - .get = pb0100_get_blue_balance - }, -#define EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 511, - .step = 1, - .default_value = 12 - }, - .set = pb0100_set_exposure, - .get = pb0100_get_exposure - }, -#define AUTOGAIN_IDX 4 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic Gain and Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = pb0100_set_autogain, - .get = pb0100_get_autogain - }, -#define AUTOGAIN_TARGET_IDX 5 - { - { - .id = V4L2_CTRL_CLASS_USER + 0x1000, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Automatic Gain Target", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128 - }, - .set = pb0100_set_autogain_target, - .get = pb0100_get_autogain_target - }, -#define NATURAL_IDX 6 - { - { - .id = V4L2_CTRL_CLASS_USER + 0x1001, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Natural Light Source", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = pb0100_set_natural, - .get = pb0100_get_natural - } +struct pb0100_ctrls { + struct { /* one big happy control cluster... */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *red; + struct v4l2_ctrl *blue; + struct v4l2_ctrl *natural; + }; + struct v4l2_ctrl *target; }; static struct v4l2_pix_format pb0100_mode[] = { @@ -174,38 +85,102 @@ static struct v4l2_pix_format pb0100_mode[] = { } }; +static int pb0100_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct pb0100_ctrls *ctrls = sd->sensor_priv; + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + err = pb0100_set_autogain(&sd->gspca_dev, ctrl->val); + if (err) + break; + if (ctrl->val) + break; + err = pb0100_set_gain(&sd->gspca_dev, ctrls->gain->val); + if (err) + break; + err = pb0100_set_exposure(&sd->gspca_dev, ctrls->exposure->val); + break; + case V4L2_CTRL_CLASS_USER + 0x1001: + err = pb0100_set_autogain_target(&sd->gspca_dev, ctrl->val); + break; + } + return err; +} + +static const struct v4l2_ctrl_ops pb0100_ctrl_ops = { + .s_ctrl = pb0100_s_ctrl, +}; + +static int pb0100_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + struct pb0100_ctrls *ctrls; + static const struct v4l2_ctrl_config autogain_target = { + .ops = &pb0100_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER + 0x1000, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Automatic Gain Target", + .max = 255, + .step = 1, + .def = 128, + }; + static const struct v4l2_ctrl_config natural_light = { + .ops = &pb0100_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER + 0x1001, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Natural Light Source", + .max = 1, + .step = 1, + .def = 1, + }; + + ctrls = kzalloc(sizeof(*ctrls), GFP_KERNEL); + if (!ctrls) + return -ENOMEM; + + v4l2_ctrl_handler_init(hdl, 6); + ctrls->autogain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + ctrls->exposure = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 511, 1, 12); + ctrls->gain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 128); + ctrls->red = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_RED_BALANCE, -255, 255, 1, 0); + ctrls->blue = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_BLUE_BALANCE, -255, 255, 1, 0); + ctrls->natural = v4l2_ctrl_new_custom(hdl, &natural_light, NULL); + ctrls->target = v4l2_ctrl_new_custom(hdl, &autogain_target, NULL); + if (hdl->error) { + kfree(ctrls); + return hdl->error; + } + sd->sensor_priv = ctrls; + v4l2_ctrl_auto_cluster(5, &ctrls->autogain, 0, false); + return 0; +} + static int pb0100_probe(struct sd *sd) { u16 sensor; - int i, err; - s32 *sensor_settings; + int err; err = stv06xx_read_sensor(sd, PB_IDENT, &sensor); if (err < 0) return -ENODEV; + if ((sensor >> 8) != 0x64) + return -ENODEV; - if ((sensor >> 8) == 0x64) { - sensor_settings = kmalloc( - ARRAY_SIZE(pb0100_ctrl) * sizeof(s32), - GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - - pr_info("Photobit pb0100 sensor detected\n"); - - sd->gspca_dev.cam.cam_mode = pb0100_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); - sd->desc.ctrls = pb0100_ctrl; - sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl); - for (i = 0; i < sd->desc.nctrls; i++) - sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; + pr_info("Photobit pb0100 sensor detected\n"); - return 0; - } + sd->gspca_dev.cam.cam_mode = pb0100_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); - return -ENODEV; + return 0; } static int pb0100_start(struct sd *sd) @@ -214,7 +189,6 @@ static int pb0100_start(struct sd *sd) struct usb_host_interface *alt; struct usb_interface *intf; struct cam *cam = &sd->gspca_dev.cam; - s32 *sensor_settings = sd->sensor_priv; u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); @@ -255,13 +229,6 @@ static int pb0100_start(struct sd *sd) stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); } - /* set_gain also sets red and blue balance */ - pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]); - pb0100_set_autogain_target(&sd->gspca_dev, - sensor_settings[AUTOGAIN_TARGET_IDX]); - pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]); - err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1)); PDEBUG(D_STREAM, "Started stream, status: %d", err); @@ -285,12 +252,6 @@ out: return (err < 0) ? err : 0; } -static void pb0100_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - /* FIXME: Sort the init commands out and put them into tables, this is only for getting the camera to work */ /* FIXME: No error handling for now, @@ -362,62 +323,32 @@ static int pb0100_dump(struct sd *sd) return 0; } -static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - - return 0; -} - static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[GAIN_IDX] = val; err = stv06xx_write_sensor(sd, PB_G1GAIN, val); if (!err) err = stv06xx_write_sensor(sd, PB_G2GAIN, val); PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err); if (!err) - err = pb0100_set_red_balance(gspca_dev, - sensor_settings[RED_BALANCE_IDX]); + err = pb0100_set_red_balance(gspca_dev, ctrls->red->val); if (!err) - err = pb0100_set_blue_balance(gspca_dev, - sensor_settings[BLUE_BALANCE_IDX]); + err = pb0100_set_blue_balance(gspca_dev, ctrls->blue->val); return err; } -static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[RED_BALANCE_IDX]; - - return 0; -} - static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[RED_BALANCE_IDX] = val; - val += sensor_settings[GAIN_IDX]; + val += ctrls->gain->val; if (val < 0) val = 0; else if (val > 255) @@ -429,27 +360,13 @@ static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BLUE_BALANCE_IDX]; - - return 0; -} - static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[BLUE_BALANCE_IDX] = val; - val += sensor_settings[GAIN_IDX]; + val += ctrls->gain->val; if (val < 0) val = 0; else if (val > 255) @@ -461,51 +378,25 @@ static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - - return 0; -} - static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { - int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; + int err; - sensor_settings[EXPOSURE_IDX] = val; err = stv06xx_write_sensor(sd, PB_RINTTIME, val); PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err); return err; } -static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTOGAIN_IDX]; - - return 0; -} - static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - sensor_settings[AUTOGAIN_IDX] = val; - if (sensor_settings[AUTOGAIN_IDX]) { - if (sensor_settings[NATURAL_IDX]) + if (val) { + if (ctrls->natural->val) val = BIT(6)|BIT(4)|BIT(0); else val = BIT(4)|BIT(0); @@ -514,29 +405,15 @@ static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d", - sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX], - err); + val, ctrls->natural->val, err); return err; } -static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTOGAIN_TARGET_IDX]; - - return 0; -} - static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) { int err, totalpixels, brightpixels, darkpixels; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[AUTOGAIN_TARGET_IDX] = val; /* Number of pixels counted by the sensor when subsampling the pixels. * Slightly larger than the real value to avoid oscillation */ @@ -553,23 +430,3 @@ static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) return err; } - -static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[NATURAL_IDX]; - - return 0; -} - -static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[NATURAL_IDX] = val; - - return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]); -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h index 757de246dc75..5071e5353fd3 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h @@ -112,25 +112,17 @@ static int pb0100_probe(struct sd *sd); static int pb0100_start(struct sd *sd); static int pb0100_init(struct sd *sd); +static int pb0100_init_controls(struct sd *sd); static int pb0100_stop(struct sd *sd); static int pb0100_dump(struct sd *sd); -static void pb0100_disconnect(struct sd *sd); /* V4L2 controls supported by the driver */ -static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val); -static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val); const struct stv06xx_sensor stv06xx_sensor_pb0100 = { .name = "PB-0100", @@ -142,11 +134,11 @@ const struct stv06xx_sensor stv06xx_sensor_pb0100 = { .max_packet_size = { 847, 923 }, .init = pb0100_init, + .init_controls = pb0100_init_controls, .probe = pb0100_probe, .start = pb0100_start, .stop = pb0100_stop, .dump = pb0100_dump, - .disconnect = pb0100_disconnect, }; #endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h index fb229d8ded58..3a498c2495c6 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h @@ -63,8 +63,8 @@ struct stv06xx_sensor { /* Performs a initialization sequence */ int (*init)(struct sd *sd); - /* Executed at device disconnect */ - void (*disconnect)(struct sd *sd); + /* Initializes the controls */ + int (*init_controls)(struct sd *sd); /* Reads a sensor register */ int (*read_sensor)(struct sd *sd, const u8 address, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c index 9940e035b3ab..bbfe8210d590 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c @@ -30,20 +30,6 @@ #include "stv06xx_st6422.h" -/* controls */ -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - GAIN, - EXPOSURE, - NCTRLS /* number of controls */ -}; - -/* sensor settings */ -struct st6422_settings { - struct gspca_ctrl ctrls[NCTRLS]; -}; - static struct v4l2_pix_format st6422_mode[] = { /* Note we actually get 124 lines of data, of which we skip the 4st 4 as they are garbage */ @@ -74,83 +60,68 @@ static struct v4l2_pix_format st6422_mode[] = { }; /* V4L2 controls supported by the driver */ -static void st6422_set_brightness(struct gspca_dev *gspca_dev); -static void st6422_set_contrast(struct gspca_dev *gspca_dev); -static void st6422_set_gain(struct gspca_dev *gspca_dev); -static void st6422_set_exposure(struct gspca_dev *gspca_dev); - -static const struct ctrl st6422_ctrl[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 31, - .step = 1, - .default_value = 3 - }, - .set_control = st6422_set_brightness - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 15, - .step = 1, - .default_value = 11 - }, - .set_control = st6422_set_contrast - }, -[GAIN] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 64 - }, - .set_control = st6422_set_gain - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, -#define EXPOSURE_MAX 1023 - .maximum = EXPOSURE_MAX, - .step = 1, - .default_value = 256 - }, - .set_control = st6422_set_exposure - }, +static int setbrightness(struct sd *sd, s32 val); +static int setcontrast(struct sd *sd, s32 val); +static int setgain(struct sd *sd, u8 gain); +static int setexposure(struct sd *sd, s16 expo); + +static int st6422_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + err = setbrightness(sd, ctrl->val); + break; + case V4L2_CID_CONTRAST: + err = setcontrast(sd, ctrl->val); + break; + case V4L2_CID_GAIN: + err = setgain(sd, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = setexposure(sd, ctrl->val); + break; + } + + /* commit settings */ + if (err >= 0) + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + sd->gspca_dev.usb_err = err; + return err; +} + +static const struct v4l2_ctrl_ops st6422_ctrl_ops = { + .s_ctrl = st6422_s_ctrl, }; -static int st6422_probe(struct sd *sd) +static int st6422_init_controls(struct sd *sd) { - struct st6422_settings *sensor_settings; + struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 31, 1, 3); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_CONTRAST, 0, 15, 1, 11); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 1023, 1, 256); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 64); + + return hdl->error; +} +static int st6422_probe(struct sd *sd) +{ if (sd->bridge != BRIDGE_ST6422) return -ENODEV; pr_info("st6422 sensor detected\n"); - sensor_settings = kmalloc(sizeof *sensor_settings, GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - sd->gspca_dev.cam.cam_mode = st6422_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode); - sd->gspca_dev.cam.ctrls = sensor_settings->ctrls; - sd->desc.ctrls = st6422_ctrl; - sd->desc.nctrls = ARRAY_SIZE(st6422_ctrl); - sd->sensor_priv = sensor_settings; - return 0; } @@ -239,38 +210,22 @@ static int st6422_init(struct sd *sd) return err; } -static void st6422_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static int setbrightness(struct sd *sd) +static int setbrightness(struct sd *sd, s32 val) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - /* val goes from 0 -> 31 */ - return stv06xx_write_bridge(sd, 0x1432, - sensor_settings->ctrls[BRIGHTNESS].val); + return stv06xx_write_bridge(sd, 0x1432, val); } -static int setcontrast(struct sd *sd) +static int setcontrast(struct sd *sd, s32 val) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - /* Val goes from 0 -> 15 */ - return stv06xx_write_bridge(sd, 0x143a, - sensor_settings->ctrls[CONTRAST].val | 0xf0); + return stv06xx_write_bridge(sd, 0x143a, val | 0xf0); } -static int setgain(struct sd *sd) +static int setgain(struct sd *sd, u8 gain) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - u8 gain; int err; - gain = sensor_settings->ctrls[GAIN].val; - /* Set red, green, blue, gain */ err = stv06xx_write_bridge(sd, 0x0509, gain); if (err < 0) @@ -292,13 +247,10 @@ static int setgain(struct sd *sd) return stv06xx_write_bridge(sd, 0x050d, 0x01); } -static int setexposure(struct sd *sd) +static int setexposure(struct sd *sd, s16 expo) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - u16 expo; int err; - expo = sensor_settings->ctrls[EXPOSURE].val; err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff); if (err < 0) return err; @@ -318,22 +270,6 @@ static int st6422_start(struct sd *sd) if (err < 0) return err; - err = setbrightness(sd); - if (err < 0) - return err; - - err = setcontrast(sd); - if (err < 0) - return err; - - err = setexposure(sd); - if (err < 0) - return err; - - err = setgain(sd); - if (err < 0) - return err; - /* commit settings */ err = stv06xx_write_bridge(sd, 0x143f, 0x01); return (err < 0) ? err : 0; @@ -345,59 +281,3 @@ static int st6422_stop(struct sd *sd) return 0; } - -static void st6422_set_brightness(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setbrightness(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} - -static void st6422_set_contrast(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setcontrast(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} - -static void st6422_set_gain(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setgain(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} - -static void st6422_set_exposure(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setexposure(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h index d7498e06432b..8f20fbf30f33 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h @@ -34,8 +34,8 @@ static int st6422_probe(struct sd *sd); static int st6422_start(struct sd *sd); static int st6422_init(struct sd *sd); +static int st6422_init_controls(struct sd *sd); static int st6422_stop(struct sd *sd); -static void st6422_disconnect(struct sd *sd); const struct stv06xx_sensor stv06xx_sensor_st6422 = { .name = "ST6422", @@ -43,10 +43,10 @@ const struct stv06xx_sensor stv06xx_sensor_st6422 = { .min_packet_size = { 300, 847 }, .max_packet_size = { 300, 847 }, .init = st6422_init, + .init_controls = st6422_init_controls, .probe = st6422_probe, .start = st6422_start, .stop = st6422_stop, - .disconnect = st6422_disconnect, }; #endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c index a5c69d9ebdd4..1b7a68a5a43d 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c @@ -44,130 +44,82 @@ static struct v4l2_pix_format vv6410_mode[] = { } }; -static const struct ctrl vv6410_ctrl[] = { -#define HFLIP_IDX 0 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = vv6410_set_hflip, - .get = vv6410_get_hflip - }, -#define VFLIP_IDX 1 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = vv6410_set_vflip, - .get = vv6410_get_vflip - }, -#define GAIN_IDX 2 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "analog gain", - .minimum = 0, - .maximum = 15, - .step = 1, - .default_value = 10 - }, - .set = vv6410_set_analog_gain, - .get = vv6410_get_analog_gain - }, -#define EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0, - .maximum = 32768, - .step = 1, - .default_value = 20000 - }, - .set = vv6410_set_exposure, - .get = vv6410_get_exposure +static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + err = vv6410_set_hflip(&sd->gspca_dev, ctrl->val); + break; + case V4L2_CID_VFLIP: + err = vv6410_set_vflip(&sd->gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + err = vv6410_set_analog_gain(&sd->gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = vv6410_set_exposure(&sd->gspca_dev, ctrl->val); + break; } - }; + return err; +} + +static const struct v4l2_ctrl_ops vv6410_ctrl_ops = { + .s_ctrl = vv6410_s_ctrl, +}; static int vv6410_probe(struct sd *sd) { u16 data; - int err, i; - s32 *sensor_settings; + int err; err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); if (err < 0) return -ENODEV; - if (data == 0x19) { - pr_info("vv6410 sensor detected\n"); + if (data != 0x19) + return -ENODEV; - sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32), - GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; + pr_info("vv6410 sensor detected\n"); - sd->gspca_dev.cam.cam_mode = vv6410_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); - sd->desc.ctrls = vv6410_ctrl; - sd->desc.nctrls = ARRAY_SIZE(vv6410_ctrl); + sd->gspca_dev.cam.cam_mode = vv6410_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); + return 0; +} - for (i = 0; i < sd->desc.nctrls; i++) - sensor_settings[i] = vv6410_ctrl[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; - return 0; - } - return -ENODEV; +static int vv6410_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_GAIN, 0, 15, 1, 10); + return hdl->error; } static int vv6410_init(struct sd *sd) { int err = 0, i; - s32 *sensor_settings = sd->sensor_priv; - for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) { + for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data); - } if (err < 0) return err; err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, ARRAY_SIZE(vv6410_sensor_init)); - if (err < 0) - return err; - - err = vv6410_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = vv6410_set_analog_gain(&sd->gspca_dev, - sensor_settings[GAIN_IDX]); - return (err < 0) ? err : 0; } -static void vv6410_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - static int vv6410_start(struct sd *sd) { int err; @@ -233,25 +185,12 @@ static int vv6410_dump(struct sd *sd) return (err < 0) ? err : 0; } -static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - - return 0; -} - static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[HFLIP_IDX] = val; err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); if (err < 0) return err; @@ -267,25 +206,12 @@ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) return (err < 0) ? err : 0; } -static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[VFLIP_IDX] = val; err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); if (err < 0) return err; @@ -301,52 +227,23 @@ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) return (err < 0) ? err : 0; } -static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - - PDEBUG(D_V4L2, "Read analog gain %d", *val); - - return 0; -} - static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[GAIN_IDX] = val; PDEBUG(D_V4L2, "Set analog gain to %d", val); err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); return (err < 0) ? err : 0; } -static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - - PDEBUG(D_V4L2, "Read exposure %d", *val); - - return 0; -} - static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; unsigned int fine, coarse; - sensor_settings[EXPOSURE_IDX] = val; - val = (val * val >> 14) + val / 4; fine = val % VV6410_CIF_LINELENGTH; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h index a25b8873f2e6..53e67b40ca05 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h @@ -178,18 +178,14 @@ static int vv6410_probe(struct sd *sd); static int vv6410_start(struct sd *sd); static int vv6410_init(struct sd *sd); +static int vv6410_init_controls(struct sd *sd); static int vv6410_stop(struct sd *sd); static int vv6410_dump(struct sd *sd); -static void vv6410_disconnect(struct sd *sd); /* V4L2 controls supported by the driver */ -static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val); const struct stv06xx_sensor stv06xx_sensor_vv6410 = { @@ -202,11 +198,11 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = { .min_packet_size = { 1023 }, .max_packet_size = { 1023 }, .init = vv6410_init, + .init_controls = vv6410_init_controls, .probe = vv6410_probe, .start = vv6410_start, .stop = vv6410_stop, .dump = vv6410_dump, - .disconnect = vv6410_disconnect, }; /* If NULL, only single value to write, stored in len */ -- cgit v1.2.3 From d67a1adaea29da942291145fa3964a453e017274 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 May 2012 11:22:47 -0300 Subject: [media] gscpa_stv06xx: Make sd_desc const Now that stv06xx is using the control framework it is no longer necessary to have a (non const) copy of sd_desc inside the sd specific data struct. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/stv06xx/stv06xx.c | 3 +-- drivers/media/video/gspca/stv06xx/stv06xx.h | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 3a8d034421fc..cebd615effeb 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -542,9 +542,8 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Configuring camera"); - sd->desc = sd_desc; sd->bridge = id->driver_info; - gspca_dev->sd_desc = &sd->desc; + gspca_dev->sd_desc = &sd_desc; if (dump_bridge) stv06xx_dump_bridge(sd); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index b338edcdbe0c..b7cbc6b878d3 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -92,9 +92,6 @@ struct sd { /* Control handler */ struct v4l2_ctrl_handler ctrl_handler; - /* A pointer to the sd_desc struct */ - struct sd_desc desc; - /* Sensor private data */ void *sensor_priv; -- cgit v1.2.3 From 98684298f9ed89554cdaa4afe2554fca8276d1e9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 09:28:31 -0300 Subject: [media] gspca_mars: Convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/mars.c | 292 +++++++++++++++++---------------------- 1 file changed, 126 insertions(+), 166 deletions(-) diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index b0231465afae..2950347b8265 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -30,22 +30,20 @@ MODULE_AUTHOR("Michel Xhaard "); MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver"); MODULE_LICENSE("GPL"); -/* controls */ -enum e_ctrl { - BRIGHTNESS, - COLORS, - GAMMA, - SHARPNESS, - ILLUM_TOP, - ILLUM_BOT, - NCTRLS /* number of controls */ -}; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct gspca_ctrl ctrls[NCTRLS]; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *gamma; + struct { /* illuminator control cluster */ + struct v4l2_ctrl *illum_top; + struct v4l2_ctrl *illum_bottom; + }; + struct v4l2_ctrl *jpegqual; u8 quality; #define QUALITY_MIN 40 @@ -56,89 +54,10 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static void setbrightness(struct gspca_dev *gspca_dev); -static void setcolors(struct gspca_dev *gspca_dev); -static void setgamma(struct gspca_dev *gspca_dev); -static void setsharpness(struct gspca_dev *gspca_dev); -static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val); -static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 30, - .step = 1, - .default_value = 15, - }, - .set_control = setbrightness - }, -[COLORS] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Color", - .minimum = 1, - .maximum = 255, - .step = 1, - .default_value = 200, - }, - .set_control = setcolors - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 1, - }, - .set_control = setgamma - }, -[SHARPNESS] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 2, - .step = 1, - .default_value = 1, - }, - .set_control = setsharpness - }, -[ILLUM_TOP] = { - { - .id = V4L2_CID_ILLUMINATORS_1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Top illuminator", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_UPDATE, - }, - .set = sd_setilluminator1 - }, -[ILLUM_BOT] = { - { - .id = V4L2_CID_ILLUMINATORS_2, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Bottom illuminator", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_UPDATE, - }, - .set = sd_setilluminator2 - }, -}; +static void setbrightness(struct gspca_dev *gspca_dev, s32 val); +static void setcolors(struct gspca_dev *gspca_dev, s32 val); +static void setgamma(struct gspca_dev *gspca_dev, s32 val); +static void setsharpness(struct gspca_dev *gspca_dev, s32 val); static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -198,59 +117,129 @@ static void mi_w(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 4); } -static void setbrightness(struct gspca_dev *gspca_dev) +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = 0x61; - gspca_dev->usb_buf[1] = sd->ctrls[BRIGHTNESS].val; + gspca_dev->usb_buf[1] = val; reg_w(gspca_dev, 2); } -static void setcolors(struct gspca_dev *gspca_dev) +static void setcolors(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - s16 val; - - val = sd->ctrls[COLORS].val; gspca_dev->usb_buf[0] = 0x5f; gspca_dev->usb_buf[1] = val << 3; gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04; reg_w(gspca_dev, 3); } -static void setgamma(struct gspca_dev *gspca_dev) +static void setgamma(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = 0x06; - gspca_dev->usb_buf[1] = sd->ctrls[GAMMA].val * 0x40; + gspca_dev->usb_buf[1] = val * 0x40; reg_w(gspca_dev, 2); } -static void setsharpness(struct gspca_dev *gspca_dev) +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = 0x67; - gspca_dev->usb_buf[1] = sd->ctrls[SHARPNESS].val * 4 + 3; + gspca_dev->usb_buf[1] = val * 4 + 3; reg_w(gspca_dev, 2); } -static void setilluminators(struct gspca_dev *gspca_dev) +static void setilluminators(struct gspca_dev *gspca_dev, bool top, bool bottom) { - struct sd *sd = (struct sd *) gspca_dev; - + /* both are off if not streaming */ gspca_dev->usb_buf[0] = 0x22; - if (sd->ctrls[ILLUM_TOP].val) + if (top) gspca_dev->usb_buf[1] = 0x76; - else if (sd->ctrls[ILLUM_BOT].val) + else if (bottom) gspca_dev->usb_buf[1] = 0x7a; else gspca_dev->usb_buf[1] = 0x7e; reg_w(gspca_dev, 2); } +static int mars_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_ILLUMINATORS_1) { + /* only one can be on at a time */ + if (ctrl->is_new && ctrl->val) + sd->illum_bottom->val = 0; + if (sd->illum_bottom->is_new && sd->illum_bottom->val) + sd->illum_top->val = 0; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(&sd->gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(&sd->gspca_dev, ctrl->val); + break; + case V4L2_CID_GAMMA: + setgamma(&sd->gspca_dev, ctrl->val); + break; + case V4L2_CID_ILLUMINATORS_1: + setilluminators(&sd->gspca_dev, sd->illum_top->val, + sd->illum_bottom->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(&sd->gspca_dev, ctrl->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + jpeg_set_qual(sd->jpeg_hdr, ctrl->val); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops mars_ctrl_ops = { + .s_ctrl = mars_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 7); + sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 30, 1, 15); + sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 200); + sd->gamma = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_GAMMA, 0, 3, 1, 1); + sd->sharpness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 2, 1, 1); + sd->illum_top = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); + sd->illum_top->flags |= V4L2_CTRL_FLAG_UPDATE; + sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); + sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE; + sd->jpegqual = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(2, &sd->illum_top); + return 0; +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -261,7 +250,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - cam->ctrls = sd->ctrls; sd->quality = QUALITY_DEF; return 0; } @@ -269,7 +257,6 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT); return 0; } @@ -282,7 +269,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); data = gspca_dev->usb_buf; @@ -301,7 +288,7 @@ static int sd_start(struct gspca_dev *gspca_dev) data[5] = 0x30; /* reg 4, MI, PAS5101 : * 0x30 for 24mhz , 0x28 for 12mhz */ data[6] = 0x02; /* reg 5, H start - was 0x04 */ - data[7] = sd->ctrls[GAMMA].val * 0x40; /* reg 0x06: gamma */ + data[7] = v4l2_ctrl_g_ctrl(sd->gamma) * 0x40; /* reg 0x06: gamma */ data[8] = 0x01; /* reg 7, V start - was 0x03 */ /* if (h_size == 320 ) */ /* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ @@ -333,16 +320,16 @@ static int sd_start(struct gspca_dev *gspca_dev) /* reg 0x5f/0x60 (LE) = saturation */ /* h (60): xxxx x100 * l (5f): xxxx x000 */ - data[2] = sd->ctrls[COLORS].val << 3; - data[3] = ((sd->ctrls[COLORS].val >> 2) & 0xf8) | 0x04; - data[4] = sd->ctrls[BRIGHTNESS].val; /* reg 0x61 = brightness */ + data[2] = v4l2_ctrl_g_ctrl(sd->saturation) << 3; + data[3] = ((v4l2_ctrl_g_ctrl(sd->saturation) >> 2) & 0xf8) | 0x04; + data[4] = v4l2_ctrl_g_ctrl(sd->brightness); /* reg 0x61 = brightness */ data[5] = 0x00; reg_w(gspca_dev, 6); data[0] = 0x67; /*jfm: from win trace*/ - data[1] = sd->ctrls[SHARPNESS].val * 4 + 3; + data[1] = v4l2_ctrl_g_ctrl(sd->sharpness) * 4 + 3; data[2] = 0x14; reg_w(gspca_dev, 3); @@ -365,7 +352,9 @@ static int sd_start(struct gspca_dev *gspca_dev) data[1] = 0x4d; /* ISOC transferring enable... */ reg_w(gspca_dev, 2); - gspca_dev->ctrl_inac = 0; /* activate the illuminator controls */ + setilluminators(gspca_dev, v4l2_ctrl_g_ctrl(sd->illum_top), + v4l2_ctrl_g_ctrl(sd->illum_bottom)); + return gspca_dev->usb_err; } @@ -373,11 +362,9 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT); - if (sd->ctrls[ILLUM_TOP].val || sd->ctrls[ILLUM_BOT].val) { - sd->ctrls[ILLUM_TOP].val = 0; - sd->ctrls[ILLUM_BOT].val = 0; - setilluminators(gspca_dev); + if (v4l2_ctrl_g_ctrl(sd->illum_top) || + v4l2_ctrl_g_ctrl(sd->illum_bottom)) { + setilluminators(gspca_dev, false, false); msleep(20); } @@ -424,43 +411,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* only one illuminator may be on */ - sd->ctrls[ILLUM_TOP].val = val; - if (val) - sd->ctrls[ILLUM_BOT].val = 0; - setilluminators(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* only one illuminator may be on */ - sd->ctrls[ILLUM_BOT].val = val; - if (val) - sd->ctrls[ILLUM_TOP].val = 0; - setilluminators(gspca_dev); - return gspca_dev->usb_err; -} - static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; + int ret; - if (jcomp->quality < QUALITY_MIN) - sd->quality = QUALITY_MIN; - else if (jcomp->quality > QUALITY_MAX) - sd->quality = QUALITY_MAX; - else - sd->quality = jcomp->quality; - if (gspca_dev->streaming) - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + if (ret) + return ret; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); return 0; } @@ -470,7 +430,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->quality; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; @@ -479,10 +439,9 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = NCTRLS, .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, @@ -513,6 +472,7 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, + .reset_resume = gspca_resume, #endif }; -- cgit v1.2.3 From a8a478601ac1d8877e23cb832fe4b44042ce6f20 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 May 2012 11:19:00 -0300 Subject: [media] gscpa: Move ctrl_handler to gspca_dev We intend to eventually port all sub-drivers to the control-framework. At which point it will make more sense to have the ctrl_handler in gspca_dev then to have it in the subdrivers. Lets move it there now, to avoid a lot of work to move it later. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.h | 1 + drivers/media/video/gspca/mars.c | 20 ++++++++--------- drivers/media/video/gspca/sn9c20x.c | 26 +++++++++++----------- drivers/media/video/gspca/stv06xx/stv06xx.c | 2 +- drivers/media/video/gspca/stv06xx/stv06xx.h | 3 --- drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c | 9 ++++---- drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c | 14 +++++++----- drivers/media/video/gspca/stv06xx/stv06xx_st6422.c | 6 +++-- drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c | 13 ++++++----- drivers/media/video/gspca/zc3xx.c | 13 ++++++----- 10 files changed, 56 insertions(+), 51 deletions(-) diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 449ff8e37fe7..dd15e07d4666 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -174,6 +174,7 @@ struct gspca_dev { const struct sd_desc *sd_desc; /* subdriver description */ unsigned ctrl_dis; /* disabled controls (bit map) */ unsigned ctrl_inac; /* inactive controls (bit map) */ + struct v4l2_ctrl_handler ctrl_handler; /* autogain and exposure or gain control cluster, these are global as the autogain/exposure functions in autogain_functions.c use them */ diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 2950347b8265..ec7b21ee79fb 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -34,7 +34,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *brightness; struct v4l2_ctrl *saturation; struct v4l2_ctrl *sharpness; @@ -161,8 +160,9 @@ static void setilluminators(struct gspca_dev *gspca_dev, bool top, bool bottom) static int mars_s_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); - struct gspca_dev *gspca_dev = &sd->gspca_dev; + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; gspca_dev->usb_err = 0; @@ -179,20 +179,20 @@ static int mars_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - setbrightness(&sd->gspca_dev, ctrl->val); + setbrightness(gspca_dev, ctrl->val); break; case V4L2_CID_SATURATION: - setcolors(&sd->gspca_dev, ctrl->val); + setcolors(gspca_dev, ctrl->val); break; case V4L2_CID_GAMMA: - setgamma(&sd->gspca_dev, ctrl->val); + setgamma(gspca_dev, ctrl->val); break; case V4L2_CID_ILLUMINATORS_1: - setilluminators(&sd->gspca_dev, sd->illum_top->val, - sd->illum_bottom->val); + setilluminators(gspca_dev, sd->illum_top->val, + sd->illum_bottom->val); break; case V4L2_CID_SHARPNESS: - setsharpness(&sd->gspca_dev, ctrl->val); + setsharpness(gspca_dev, ctrl->val); break; case V4L2_CID_JPEG_COMPRESSION_QUALITY: jpeg_set_qual(sd->jpeg_hdr, ctrl->val); @@ -211,7 +211,7 @@ static const struct v4l2_ctrl_ops mars_ctrl_ops = { static int sd_init_controls(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; gspca_dev->vdev.ctrl_handler = hdl; v4l2_ctrl_handler_init(hdl, 7); diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 1758ed98cf16..ad098202d7f0 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -70,7 +70,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; - struct v4l2_ctrl_handler ctrl_handler; struct { /* color control cluster */ struct v4l2_ctrl *brightness; struct v4l2_ctrl *contrast; @@ -1694,8 +1693,9 @@ static int sd_config(struct gspca_dev *gspca_dev, static int sd_s_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); - struct gspca_dev *gspca_dev = &sd->gspca_dev; + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; gspca_dev->usb_err = 0; @@ -1705,37 +1705,37 @@ static int sd_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { /* color control cluster */ case V4L2_CID_BRIGHTNESS: - set_cmatrix(&sd->gspca_dev, sd->brightness->val, + set_cmatrix(gspca_dev, sd->brightness->val, sd->contrast->val, sd->saturation->val, sd->hue->val); break; case V4L2_CID_GAMMA: - set_gamma(&sd->gspca_dev, ctrl->val); + set_gamma(gspca_dev, ctrl->val); break; /* blue/red balance cluster */ case V4L2_CID_BLUE_BALANCE: - set_redblue(&sd->gspca_dev, sd->blue->val, sd->red->val); + set_redblue(gspca_dev, sd->blue->val, sd->red->val); break; /* h/vflip cluster */ case V4L2_CID_HFLIP: - set_hvflip(&sd->gspca_dev, sd->hflip->val, sd->vflip->val); + set_hvflip(gspca_dev, sd->hflip->val, sd->vflip->val); break; /* standalone exposure control */ case V4L2_CID_EXPOSURE: - set_exposure(&sd->gspca_dev, ctrl->val); + set_exposure(gspca_dev, ctrl->val); break; /* standalone gain control */ case V4L2_CID_GAIN: - set_gain(&sd->gspca_dev, ctrl->val); + set_gain(gspca_dev, ctrl->val); break; /* autogain + exposure or gain control cluster */ case V4L2_CID_AUTOGAIN: if (sd->sensor == SENSOR_SOI968) - set_gain(&sd->gspca_dev, sd->gain->val); + set_gain(gspca_dev, sd->gain->val); else - set_exposure(&sd->gspca_dev, sd->exposure->val); + set_exposure(gspca_dev, sd->exposure->val); break; case V4L2_CID_JPEG_COMPRESSION_QUALITY: - set_quality(&sd->gspca_dev, ctrl->val); + set_quality(gspca_dev, ctrl->val); break; } return gspca_dev->usb_err; @@ -1748,7 +1748,7 @@ static const struct v4l2_ctrl_ops sd_ctrl_ops = { static int sd_init_controls(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; gspca_dev->vdev.ctrl_handler = hdl; v4l2_ctrl_handler_init(hdl, 13); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index cebd615effeb..999ec7764449 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -268,7 +268,7 @@ static int stv06xx_init_controls(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "Initializing controls"); - gspca_dev->vdev.ctrl_handler = &sd->ctrl_handler; + gspca_dev->vdev.ctrl_handler = &gspca_dev->ctrl_handler; return sd->sensor->init_controls(sd); } diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index b7cbc6b878d3..34957a4ec150 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -89,9 +89,6 @@ struct sd { /* A pointer to the currently connected sensor */ const struct stv06xx_sensor *sensor; - /* Control handler */ - struct v4l2_ctrl_handler ctrl_handler; - /* Sensor private data */ void *sensor_priv; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index 74cbd386600b..06fa54c5efb2 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -335,15 +335,16 @@ static int hdcs_set_size(struct sd *sd, static int hdcs_s_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); int err = -EINVAL; switch (ctrl->id) { case V4L2_CID_GAIN: - err = hdcs_set_gain(&sd->gspca_dev, ctrl->val); + err = hdcs_set_gain(gspca_dev, ctrl->val); break; case V4L2_CID_EXPOSURE: - err = hdcs_set_exposure(&sd->gspca_dev, ctrl->val); + err = hdcs_set_exposure(gspca_dev, ctrl->val); break; } return err; @@ -355,7 +356,7 @@ static const struct v4l2_ctrl_ops hdcs_ctrl_ops = { static int hdcs_init_controls(struct sd *sd) { - struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; v4l2_ctrl_handler_init(hdl, 2); v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c index c03b13e70b56..cdfc3d05ab6b 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c @@ -87,24 +87,26 @@ static struct v4l2_pix_format pb0100_mode[] = { static int pb0100_s_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; struct pb0100_ctrls *ctrls = sd->sensor_priv; int err = -EINVAL; switch (ctrl->id) { case V4L2_CID_AUTOGAIN: - err = pb0100_set_autogain(&sd->gspca_dev, ctrl->val); + err = pb0100_set_autogain(gspca_dev, ctrl->val); if (err) break; if (ctrl->val) break; - err = pb0100_set_gain(&sd->gspca_dev, ctrls->gain->val); + err = pb0100_set_gain(gspca_dev, ctrls->gain->val); if (err) break; - err = pb0100_set_exposure(&sd->gspca_dev, ctrls->exposure->val); + err = pb0100_set_exposure(gspca_dev, ctrls->exposure->val); break; case V4L2_CTRL_CLASS_USER + 0x1001: - err = pb0100_set_autogain_target(&sd->gspca_dev, ctrl->val); + err = pb0100_set_autogain_target(gspca_dev, ctrl->val); break; } return err; @@ -116,7 +118,7 @@ static const struct v4l2_ctrl_ops pb0100_ctrl_ops = { static int pb0100_init_controls(struct sd *sd) { - struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; struct pb0100_ctrls *ctrls; static const struct v4l2_ctrl_config autogain_target = { .ops = &pb0100_ctrl_ops, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c index bbfe8210d590..8a57990dfe0f 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c @@ -67,7 +67,9 @@ static int setexposure(struct sd *sd, s16 expo); static int st6422_s_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; int err = -EINVAL; switch (ctrl->id) { @@ -98,7 +100,7 @@ static const struct v4l2_ctrl_ops st6422_ctrl_ops = { static int st6422_init_controls(struct sd *sd) { - struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; v4l2_ctrl_handler_init(hdl, 4); v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c index 1b7a68a5a43d..748e1421d6d8 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c @@ -46,21 +46,22 @@ static struct v4l2_pix_format vv6410_mode[] = { static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); int err = -EINVAL; switch (ctrl->id) { case V4L2_CID_HFLIP: - err = vv6410_set_hflip(&sd->gspca_dev, ctrl->val); + err = vv6410_set_hflip(gspca_dev, ctrl->val); break; case V4L2_CID_VFLIP: - err = vv6410_set_vflip(&sd->gspca_dev, ctrl->val); + err = vv6410_set_vflip(gspca_dev, ctrl->val); break; case V4L2_CID_GAIN: - err = vv6410_set_analog_gain(&sd->gspca_dev, ctrl->val); + err = vv6410_set_analog_gain(gspca_dev, ctrl->val); break; case V4L2_CID_EXPOSURE: - err = vv6410_set_exposure(&sd->gspca_dev, ctrl->val); + err = vv6410_set_exposure(gspca_dev, ctrl->val); break; } return err; @@ -91,7 +92,7 @@ static int vv6410_probe(struct sd *sd) static int vv6410_init_controls(struct sd *sd) { - struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; v4l2_ctrl_handler_init(hdl, 4); v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index a8839fb3c1d6..0d504a7c512c 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -39,7 +39,6 @@ static int force_sensor = -1; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct v4l2_ctrl_handler ctrl_handler; struct { /* gamma/brightness/contrast control cluster */ struct v4l2_ctrl *gamma; struct v4l2_ctrl *brightness; @@ -6328,8 +6327,9 @@ static int sd_config(struct gspca_dev *gspca_dev, static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); - struct gspca_dev *gspca_dev = &sd->gspca_dev; + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; switch (ctrl->id) { case V4L2_CID_AUTOGAIN: @@ -6343,8 +6343,9 @@ static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl) static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler); - struct gspca_dev *gspca_dev = &sd->gspca_dev; + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; int i, qual; gspca_dev->usb_err = 0; @@ -6404,7 +6405,7 @@ static const struct v4l2_ctrl_ops zcxx_ctrl_ops = { static int sd_init_controls(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; static const u8 gamma[SENSOR_MAX] = { [SENSOR_ADCM2700] = 4, [SENSOR_CS2102] = 4, -- cgit v1.2.3 From cb0988cb82f216cf7e38c374d6eaf3131debf031 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 May 2012 12:13:29 -0300 Subject: [media] gscpa_pac207: use usb_err for error handling Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac207.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 3844c49f269c..c70b258a0ae3 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -167,39 +167,44 @@ static const __u8 pac207_sensor_init[][8] = { {0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00}, }; -static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, +static void pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, const u8 *buffer, u16 length) { struct usb_device *udev = gspca_dev->dev; int err; + if (gspca_dev->usb_err < 0) + return; + memcpy(gspca_dev->usb_buf, buffer, length); err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, index, gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT); - if (err < 0) + if (err < 0) { pr_err("Failed to write registers to index 0x%04X, error %d\n", index, err); - - return err; + gspca_dev->usb_err = err; + } } - -static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) +static void pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) { struct usb_device *udev = gspca_dev->dev; int err; + if (gspca_dev->usb_err < 0) + return; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, value, index, NULL, 0, PAC207_CTRL_TIMEOUT); - if (err) + if (err) { pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", index, value, err); - - return err; + gspca_dev->usb_err = err; + } } static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) @@ -207,6 +212,9 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) struct usb_device *udev = gspca_dev->dev; int res; + if (gspca_dev->usb_err < 0) + return 0; + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, index, @@ -214,7 +222,8 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) if (res < 0) { pr_err("Failed to read a register (index 0x%04X, error %d)\n", index, res); - return res; + gspca_dev->usb_err = res; + return 0; } return gspca_dev->usb_buf[0]; @@ -264,7 +273,7 @@ static int sd_init(struct gspca_dev *gspca_dev) * Bit_2=Compression test mode enable */ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ - return 0; + return gspca_dev->usb_err; } /* -- start the camera -- */ @@ -308,7 +317,7 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->sof_read = 0; sd->autogain_ignore_frames = 0; atomic_set(&sd->avg_lum, -1); - return 0; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) -- cgit v1.2.3 From cd92c1a6d9133b34995a3a8dfe984240b2fae77f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 10 May 2012 08:34:42 -0300 Subject: [media] gspca_pac207: Convert to the control framework Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac207.c | 300 ++++++++++++------------------------- 1 file changed, 95 insertions(+), 205 deletions(-) diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index c70b258a0ae3..44ad57aa4058 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -29,6 +29,8 @@ #include #include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Pixart PAC207"); @@ -39,16 +41,19 @@ MODULE_LICENSE("GPL"); #define PAC207_BRIGHTNESS_MIN 0 #define PAC207_BRIGHTNESS_MAX 255 #define PAC207_BRIGHTNESS_DEFAULT 46 +#define PAC207_BRIGHTNESS_REG 0x08 #define PAC207_EXPOSURE_MIN 3 #define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */ #define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */ #define PAC207_EXPOSURE_KNEE 9 /* fps: 90 / exposure -> 9: 10 fps */ +#define PAC207_EXPOSURE_REG 0x02 #define PAC207_GAIN_MIN 0 #define PAC207_GAIN_MAX 31 #define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */ #define PAC207_GAIN_KNEE 15 +#define PAC207_GAIN_REG 0x0e #define PAC207_AUTOGAIN_DEADZONE 30 @@ -56,13 +61,9 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - u8 mode; - - u8 brightness; - u8 exposure; - u8 autogain; - u8 gain; + struct v4l2_ctrl *brightness; + u8 mode; u8 sof_read; u8 header_read; u8 autogain_ignore_frames; @@ -70,80 +71,6 @@ struct sd { atomic_t avg_lum; }; -/* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); - -static const struct ctrl sd_ctrls[] = { -#define SD_BRIGHTNESS 0 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = PAC207_BRIGHTNESS_MIN, - .maximum = PAC207_BRIGHTNESS_MAX, - .step = 1, - .default_value = PAC207_BRIGHTNESS_DEFAULT, - .flags = 0, - }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, -#define SD_EXPOSURE 1 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = PAC207_EXPOSURE_MIN, - .maximum = PAC207_EXPOSURE_MAX, - .step = 1, - .default_value = PAC207_EXPOSURE_DEFAULT, - .flags = 0, - }, - .set = sd_setexposure, - .get = sd_getexposure, - }, -#define SD_AUTOGAIN 2 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, -#define AUTOGAIN_DEF 1 - .default_value = AUTOGAIN_DEF, - .flags = 0, - }, - .set = sd_setautogain, - .get = sd_getautogain, - }, -#define SD_GAIN 3 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = PAC207_GAIN_MIN, - .maximum = PAC207_GAIN_MAX, - .step = 1, - .default_value = PAC207_GAIN_DEFAULT, - .flags = 0, - }, - .set = sd_setgain, - .get = sd_getgain, - }, -}; - static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, .bytesperline = 176, @@ -233,7 +160,6 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { - struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; u8 idreg[2]; @@ -256,10 +182,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); - sd->brightness = PAC207_BRIGHTNESS_DEFAULT; - sd->exposure = PAC207_EXPOSURE_DEFAULT; - sd->gain = PAC207_GAIN_DEFAULT; - sd->autogain = AUTOGAIN_DEF; return 0; } @@ -276,6 +198,87 @@ static int sd_init(struct gspca_dev *gspca_dev) return gspca_dev->usb_err; } +static void setcontrol(struct gspca_dev *gspca_dev, u16 reg, u16 val) +{ + pac207_write_reg(gspca_dev, reg, val); + pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ + pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + gspca_dev->exposure->val = PAC207_EXPOSURE_DEFAULT; + gspca_dev->gain->val = PAC207_GAIN_DEFAULT; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setcontrol(gspca_dev, PAC207_BRIGHTNESS_REG, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setcontrol(gspca_dev, PAC207_EXPOSURE_REG, + gspca_dev->exposure->val); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setcontrol(gspca_dev, PAC207_GAIN_REG, + gspca_dev->gain->val); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, + PAC207_BRIGHTNESS_MIN, PAC207_BRIGHTNESS_MAX, + 1, PAC207_BRIGHTNESS_DEFAULT); + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, + PAC207_EXPOSURE_MIN, PAC207_EXPOSURE_MAX, + 1, PAC207_EXPOSURE_DEFAULT); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, + PAC207_GAIN_MIN, PAC207_GAIN_MAX, + 1, PAC207_GAIN_DEFAULT); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + return 0; +} + /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { @@ -294,11 +297,13 @@ static int sd_start(struct gspca_dev *gspca_dev) else pac207_write_reg(gspca_dev, 0x4a, 0x30); pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */ - pac207_write_reg(gspca_dev, 0x08, sd->brightness); + pac207_write_reg(gspca_dev, 0x08, v4l2_ctrl_g_ctrl(sd->brightness)); /* PGA global gain (Bit 4-0) */ - pac207_write_reg(gspca_dev, 0x0e, sd->gain); - pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */ + pac207_write_reg(gspca_dev, 0x0e, + v4l2_ctrl_g_ctrl(gspca_dev->gain)); + pac207_write_reg(gspca_dev, 0x02, + v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ if (gspca_dev->width == 176) { /* 176x144 */ @@ -327,8 +332,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ } -/* Include pac common sof detection functions */ -#include "pac_common.h" static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) { @@ -340,7 +343,7 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; - else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, + else if (gspca_expo_autogain(gspca_dev, avg_lum, 90, PAC207_AUTOGAIN_DEADZONE, PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; @@ -393,118 +396,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - pac207_write_reg(gspca_dev, 0x08, sd->brightness); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -static void setexposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - pac207_write_reg(gspca_dev, 0x02, sd->exposure); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -static void setgain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - pac207_write_reg(gspca_dev, 0x0e, sd->gain); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightness(gspca_dev); - return 0; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - setexposure(gspca_dev); - return 0; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->exposure; - return 0; -} - -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - setgain(gspca_dev); - return 0; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->gain; - return 0; -} - -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - if (sd->autogain) { - sd->exposure = PAC207_EXPOSURE_DEFAULT; - sd->gain = PAC207_GAIN_DEFAULT; - if (gspca_dev->streaming) { - sd->autogain_ignore_frames = - PAC_AUTOGAIN_IGNORE_FRAMES; - setexposure(gspca_dev); - setgain(gspca_dev); - } - } - - return 0; -} - -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->autogain; - return 0; -} - #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ @@ -527,10 +418,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, .dq_callback = pac207_do_auto_gain, -- cgit v1.2.3 From 0413d3b286076b052f624275897d243d5865e0d2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 10 May 2012 08:52:37 -0300 Subject: [media] gscpa_pac207: Switch to coarse_grained_expo auto gain algorithm The pac207's exposure control is a clock-divider, so it goes with quite big steps. So lets use an autogain algorithm optimised for that. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac207.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 44ad57aa4058..fa661c6d6d55 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -46,13 +46,11 @@ MODULE_LICENSE("GPL"); #define PAC207_EXPOSURE_MIN 3 #define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */ #define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */ -#define PAC207_EXPOSURE_KNEE 9 /* fps: 90 / exposure -> 9: 10 fps */ #define PAC207_EXPOSURE_REG 0x02 #define PAC207_GAIN_MIN 0 #define PAC207_GAIN_MAX 31 #define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */ -#define PAC207_GAIN_KNEE 15 #define PAC207_GAIN_REG 0x0e #define PAC207_AUTOGAIN_DEADZONE 30 @@ -343,9 +341,8 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; - else if (gspca_expo_autogain(gspca_dev, avg_lum, - 90, PAC207_AUTOGAIN_DEADZONE, - PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE)) + else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + 90, PAC207_AUTOGAIN_DEADZONE)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } -- cgit v1.2.3 From 2421b3dd7c5069938543cc1d6599fd29c52ac147 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 10 May 2012 08:39:28 -0300 Subject: [media] gspca: Remove gspca_auto_gain_n_exposure function Now that the pac207 driver has been converted to the control framework, there are no remaining users. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 89 --------------------------------------- drivers/media/video/gspca/gspca.h | 2 - 2 files changed, 91 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 38b124ec23a3..2b393b2cf62d 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -2435,95 +2435,6 @@ int gspca_resume(struct usb_interface *intf) } EXPORT_SYMBOL(gspca_resume); #endif -/* -- cam driver utility functions -- */ - -/* auto gain and exposure algorithm based on the knee algorithm described here: - http://ytse.tricolour.net/docs/LowLightOptimization.html - - Returns 0 if no changes were made, 1 if the gain and or exposure settings - where changed. */ -int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, - int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee) -{ - int i, steps, gain, orig_gain, exposure, orig_exposure, autogain; - const struct ctrl *gain_ctrl = NULL; - const struct ctrl *exposure_ctrl = NULL; - const struct ctrl *autogain_ctrl = NULL; - int retval = 0; - - for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (gspca_dev->ctrl_dis & (1 << i)) - continue; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN) - gain_ctrl = &gspca_dev->sd_desc->ctrls[i]; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE) - exposure_ctrl = &gspca_dev->sd_desc->ctrls[i]; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN) - autogain_ctrl = &gspca_dev->sd_desc->ctrls[i]; - } - if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) { - PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called " - "on cam without (auto)gain/exposure"); - return 0; - } - - if (gain_ctrl->get(gspca_dev, &gain) || - exposure_ctrl->get(gspca_dev, &exposure) || - autogain_ctrl->get(gspca_dev, &autogain) || !autogain) - return 0; - - orig_gain = gain; - orig_exposure = exposure; - - /* If we are of a multiple of deadzone, do multiple steps to reach the - desired lumination fast (with the risc of a slight overshoot) */ - steps = abs(desired_avg_lum - avg_lum) / deadzone; - - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", - avg_lum, desired_avg_lum, steps); - - for (i = 0; i < steps; i++) { - if (avg_lum > desired_avg_lum) { - if (gain > gain_knee) - gain--; - else if (exposure > exposure_knee) - exposure--; - else if (gain > gain_ctrl->qctrl.default_value) - gain--; - else if (exposure > exposure_ctrl->qctrl.minimum) - exposure--; - else if (gain > gain_ctrl->qctrl.minimum) - gain--; - else - break; - } else { - if (gain < gain_ctrl->qctrl.default_value) - gain++; - else if (exposure < exposure_knee) - exposure++; - else if (gain < gain_knee) - gain++; - else if (exposure < exposure_ctrl->qctrl.maximum) - exposure++; - else if (gain < gain_ctrl->qctrl.maximum) - gain++; - else - break; - } - } - - if (gain != orig_gain) { - gain_ctrl->set(gspca_dev, gain); - retval = 1; - } - if (exposure != orig_exposure) { - exposure_ctrl->set(gspca_dev, exposure); - retval = 1; - } - - return retval; -} -EXPORT_SYMBOL(gspca_auto_gain_n_exposure); /* -- module insert / remove -- */ static int __init gspca_init(void) diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index dd15e07d4666..dc688c7f5e48 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -251,8 +251,6 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, int gspca_suspend(struct usb_interface *intf, pm_message_t message); int gspca_resume(struct usb_interface *intf); #endif -int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, - int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum, int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev, -- cgit v1.2.3 From 43f52bf2e0eded5908052fd403ca45c5cecc78c7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 10 May 2012 10:52:54 -0300 Subject: [media] gspca_pac7311: Convert to the control framework Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 234 +++++++++++++++++------------------- 1 file changed, 108 insertions(+), 126 deletions(-) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 5ccf0b41ffc5..a07c0893a546 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -64,25 +64,21 @@ /* Include pac common sof detection functions */ #include "pac_common.h" +#define PAC7311_GAIN_DEFAULT 122 +#define PAC7311_EXPOSURE_DEFAULT 3 /* 20 fps, avoid using high compr. */ + MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); MODULE_LICENSE("GPL"); -enum e_ctrl { - CONTRAST, - GAIN, - EXPOSURE, - AUTOGAIN, - HFLIP, - VFLIP, - NCTRLS /* number of controls */ -}; - struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct gspca_ctrl ctrls[NCTRLS]; - int exp_too_low_cnt; - int exp_too_high_cnt; + + struct v4l2_ctrl *contrast; + struct { /* flip cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; u8 sof_read; u8 autogain_ignore_frames; @@ -90,86 +86,6 @@ struct sd { atomic_t avg_lum; }; -/* V4L2 controls supported by the driver */ -static void setcontrast(struct gspca_dev *gspca_dev); -static void setgain(struct gspca_dev *gspca_dev); -static void setexposure(struct gspca_dev *gspca_dev); -static void sethvflip(struct gspca_dev *gspca_dev); - -static const struct ctrl sd_ctrls[] = { -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 15, - .step = 1, - .default_value = 7, - }, - .set_control = setcontrast - }, -[GAIN] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 244, - .step = 1, - .default_value = 122, - }, - .set_control = setgain, - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 2, - .maximum = 63, - .step = 1, - .default_value = 3, /* 20 fps, avoid using high compr. */ - }, - .set_control = setexposure, - }, -[AUTOGAIN] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - }, -[HFLIP] = { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = sethvflip, - }, -[VFLIP] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vflip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = sethvflip, - }, -}; - static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -371,45 +287,36 @@ static void reg_w_var(struct gspca_dev *gspca_dev, static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { - struct sd *sd = (struct sd *) gspca_dev; struct cam *cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - gspca_dev->cam.ctrls = sd->ctrls; - return 0; } -static void setcontrast(struct gspca_dev *gspca_dev) +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x10, sd->ctrls[CONTRAST].val); + reg_w(gspca_dev, 0x10, val); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } -static void setgain(struct gspca_dev *gspca_dev) +static void setgain(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, sd->ctrls[GAIN].max - sd->ctrls[GAIN].val + 1); + reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } -static void setexposure(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x02, sd->ctrls[EXPOSURE].val); + reg_w(gspca_dev, 0x02, val); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); @@ -419,7 +326,7 @@ static void setexposure(struct gspca_dev *gspca_dev) * 640x480 mode and page 4 reg 2 <= 3 then it must be 9 */ reg_w(gspca_dev, 0xff, 0x01); - if (gspca_dev->width != 640 && sd->ctrls[EXPOSURE].val <= 3) + if (gspca_dev->width != 640 && val <= 3) reg_w(gspca_dev, 0x08, 0x09); else reg_w(gspca_dev, 0x08, 0x08); @@ -430,7 +337,7 @@ static void setexposure(struct gspca_dev *gspca_dev) * camera to use higher compression or we may run out of * bandwidth. */ - if (gspca_dev->width == 640 && sd->ctrls[EXPOSURE].val == 2) + if (gspca_dev->width == 640 && val == 2) reg_w(gspca_dev, 0x80, 0x01); else reg_w(gspca_dev, 0x80, 0x1c); @@ -439,14 +346,13 @@ static void setexposure(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x11, 0x01); } -static void sethvflip(struct gspca_dev *gspca_dev) +static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) { - struct sd *sd = (struct sd *) gspca_dev; __u8 data; reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - data = (sd->ctrls[HFLIP].val ? 0x04 : 0x00) | - (sd->ctrls[VFLIP].val ? 0x08 : 0x00); + data = (hflip ? 0x04 : 0x00) | + (vflip ? 0x08 : 0x00); reg_w(gspca_dev, 0x21, data); /* load registers to sensor (Bit 0, auto clear) */ @@ -460,6 +366,85 @@ static int sd_init(struct gspca_dev *gspca_dev) return gspca_dev->usb_err; } +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + gspca_dev->exposure->val = PAC7311_EXPOSURE_DEFAULT; + gspca_dev->gain->val = PAC7311_GAIN_DEFAULT; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setexposure(gspca_dev, gspca_dev->exposure->val); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setgain(gspca_dev, gspca_dev->gain->val); + break; + case V4L2_CID_HFLIP: + sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 15, 1, 7); + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 2, 63, 1, + PAC7311_EXPOSURE_DEFAULT); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 244, 1, + PAC7311_GAIN_DEFAULT); + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + v4l2_ctrl_cluster(2, &sd->hflip); + return 0; +} + +/* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -468,10 +453,11 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w_var(gspca_dev, start_7311, page4_7311, sizeof(page4_7311)); - setcontrast(gspca_dev); - setgain(gspca_dev); - setexposure(gspca_dev); - sethvflip(gspca_dev); + setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast)); + setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain)); + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); + sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), + v4l2_ctrl_g_ctrl(sd->vflip)); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { @@ -517,16 +503,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ } -#define WANT_COARSE_EXPO_AUTOGAIN -#include "autogain_functions.h" - static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int avg_lum = atomic_read(&sd->avg_lum); int desired_lum, deadzone; - if (sd->ctrls[AUTOGAIN].val == 0 || avg_lum == -1) + if (avg_lum == -1) return; desired_lum = 200; @@ -534,8 +517,8 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; - else if (coarse_grained_expo_autogain(gspca_dev, avg_lum, desired_lum, - deadzone)) + else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + desired_lum, deadzone)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } @@ -674,10 +657,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, -- cgit v1.2.3 From 42f85d0a9612b559dc3110cc0d468050ac22cf77 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 10 May 2012 12:10:56 -0300 Subject: [media] gspca_pac7311: Set register page at start of init Our init sequence was not setting the register page to point to bank 1 before setting what should be the control reg. This causes the camera to sometimes have its LED on after init. First selecting register bank 1, rather then assuming the current register bank is bank 1, fixes this. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index a07c0893a546..910d881d3694 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -108,6 +108,7 @@ static const struct v4l2_pix_format vga_mode[] = { #define END_OF_SEQUENCE 0 static const __u8 init_7311[] = { + 0xff, 0x01, 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ 0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */ -- cgit v1.2.3 From 6a6c70b8f26e1a59fa884e87b304ac50d4214602 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 10 May 2012 12:13:16 -0300 Subject: [media] gspca_pac7311: Remove vflip control Enabling vflip leads to a much better image, with vflip disabled the image looks washed out as if there is a too high brightness setting. Since we don't know how to lower the brightness setting when not vflipping, simply always vflip and tell userspace to flip the image back, resulting in a much better (less washed out) image. Since the image is now no longer too bright, also modify the luminance level the auto-gain algorithm aims for. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 910d881d3694..2cb7d95f7be7 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -50,6 +50,8 @@ * 0x0f Master gain 1-245, low value = high gain * 0x10 Another gain 0-15, limited influence (1-2x gain I guess) * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + * Note setting vflip disabled leads to a much lower image quality, + * so we always vflip, and tell userspace to flip it back * 0x27 Seems to toggle various gains on / off, Setting bit 7 seems to * completely disable the analog amplification block. Set to 0x68 * for max gain, 0x14 for minimal gain. @@ -75,10 +77,7 @@ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ struct v4l2_ctrl *contrast; - struct { /* flip cluster */ - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - }; + struct v4l2_ctrl *hflip; u8 sof_read; u8 autogain_ignore_frames; @@ -292,6 +291,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + cam->input_flags = V4L2_IN_ST_VFLIP; return 0; } @@ -399,7 +399,7 @@ static int sd_s_ctrl(struct v4l2_ctrl *ctrl) setgain(gspca_dev, gspca_dev->gain->val); break; case V4L2_CID_HFLIP: - sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val); + sethvflip(gspca_dev, sd->hflip->val, 1); break; default: return -EINVAL; @@ -432,8 +432,6 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) PAC7311_GAIN_DEFAULT); sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); if (hdl->error) { pr_err("Could not initialize controls\n"); @@ -441,7 +439,6 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) } v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); - v4l2_ctrl_cluster(2, &sd->hflip); return 0; } @@ -457,8 +454,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast)); setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain)); setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); - sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), - v4l2_ctrl_g_ctrl(sd->vflip)); + sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { @@ -513,7 +509,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (avg_lum == -1) return; - desired_lum = 200; + desired_lum = 170; deadzone = 20; if (sd->autogain_ignore_frames > 0) -- cgit v1.2.3 From e0fde595e3fbf8138a7f5b0c877ab90a0d07a347 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 14 May 2012 08:07:43 -0300 Subject: [media] gspca - ov534: Add Saturation control Also merge the "COLORS" control into it as it was V4L2_CID_SATURATION anyway. Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov534.c | 83 +++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 04753391de3e..c15cf23d7758 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -53,6 +53,7 @@ MODULE_LICENSE("GPL"); /* controls */ enum e_ctrl { + SATURATION, BRIGHTNESS, CONTRAST, GAIN, @@ -63,7 +64,6 @@ enum e_ctrl { SHARPNESS, HFLIP, VFLIP, - COLORS, LIGHTFREQ, NCTRLS /* number of controls */ }; @@ -87,6 +87,7 @@ enum sensors { }; /* V4L2 controls supported by the driver */ +static void setsaturation(struct gspca_dev *gspca_dev); static void setbrightness(struct gspca_dev *gspca_dev); static void setcontrast(struct gspca_dev *gspca_dev); static void setgain(struct gspca_dev *gspca_dev); @@ -96,13 +97,24 @@ static void setawb(struct gspca_dev *gspca_dev); static void setaec(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); static void sethvflip(struct gspca_dev *gspca_dev); -static void setcolors(struct gspca_dev *gspca_dev); static void setlightfreq(struct gspca_dev *gspca_dev); static int sd_start(struct gspca_dev *gspca_dev); static void sd_stopN(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[] = { +[SATURATION] = { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + }, + .set_control = setsaturation + }, [BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, @@ -223,18 +235,6 @@ static const struct ctrl sd_ctrls[] = { }, .set_control = sethvflip }, -[COLORS] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 6, - .step = 1, - .default_value = 3, - }, - .set_control = setcolors - }, [LIGHTFREQ] = { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -684,7 +684,7 @@ static const u8 sensor_init_772x[][2] = { { 0x9c, 0x20 }, { 0x9e, 0x81 }, - { 0xa6, 0x04 }, + { 0xa6, 0x06 }, { 0x7e, 0x0c }, { 0x7f, 0x16 }, { 0x80, 0x2a }, @@ -955,6 +955,32 @@ static void set_frame_rate(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "frame_rate: %d", r->fps); } +static void setsaturation(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; + + val = sd->ctrls[SATURATION].val; + if (sd->sensor == SENSOR_OV767x) { + int i; + static u8 color_tb[][6] = { + {0x42, 0x42, 0x00, 0x11, 0x30, 0x41}, + {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52}, + {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66}, + {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80}, + {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a}, + {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8}, + {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd}, + }; + + for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++) + sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]); + } else { + sccb_reg_write(gspca_dev, 0xa7, val); /* U saturation */ + sccb_reg_write(gspca_dev, 0xa8, val); /* V saturation */ + } +} + static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1132,26 +1158,6 @@ static void sethvflip(struct gspca_dev *gspca_dev) } } -static void setcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 val; - int i; - static u8 color_tb[][6] = { - {0x42, 0x42, 0x00, 0x11, 0x30, 0x41}, - {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52}, - {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66}, - {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80}, - {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a}, - {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8}, - {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd}, - }; - - val = sd->ctrls[COLORS].val; - for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++) - sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]); -} - static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1228,6 +1234,9 @@ static int sd_init(struct gspca_dev *gspca_dev) gspca_dev->ctrl_dis = (1 << GAIN) | (1 << AGC) | (1 << SHARPNESS); /* auto */ + sd->ctrls[SATURATION].min = 0, + sd->ctrls[SATURATION].max = 6, + sd->ctrls[SATURATION].def = 3, sd->ctrls[BRIGHTNESS].min = -127; sd->ctrls[BRIGHTNESS].max = 127; sd->ctrls[BRIGHTNESS].def = 0; @@ -1243,7 +1252,6 @@ static int sd_init(struct gspca_dev *gspca_dev) gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode); } else { sd->sensor = SENSOR_OV772x; - gspca_dev->ctrl_dis = (1 << COLORS); gspca_dev->cam.bulk = 1; gspca_dev->cam.bulk_size = 16384; gspca_dev->cam.bulk_nurbs = 2; @@ -1302,6 +1310,7 @@ static int sd_start(struct gspca_dev *gspca_dev) set_frame_rate(gspca_dev); + setsaturation(gspca_dev); if (!(gspca_dev->ctrl_dis & (1 << AGC))) setagc(gspca_dev); setawb(gspca_dev); @@ -1314,8 +1323,6 @@ static int sd_start(struct gspca_dev *gspca_dev) if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS))) setsharpness(gspca_dev); sethvflip(gspca_dev); - if (!(gspca_dev->ctrl_dis & (1 << COLORS))) - setcolors(gspca_dev); setlightfreq(gspca_dev); ov534_set_led(gspca_dev, 1); -- cgit v1.2.3 From c8e1fb4a67eed95364a50f33f5201a88877c5215 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 14 May 2012 08:07:44 -0300 Subject: [media] Input: move drivers/input/fixp-arith.h to include/linux Move drivers/input/fixp-arith.h to include/linux so that the functions defined there can be used by other subsystems, for instance some video devices ISPs can control the output HUE value by setting registers for sin(HUE) and cos(HUE). Signed-off-by: Antonio Ospite Acked-by: Dmitry Torokhov Signed-off-by: Mauro Carvalho Chehab --- drivers/input/ff-memless.c | 3 +- drivers/input/fixp-arith.h | 87 ---------------------------------------------- include/linux/fixp-arith.h | 87 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 89 deletions(-) delete mode 100644 drivers/input/fixp-arith.h create mode 100644 include/linux/fixp-arith.h diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 117a59aaa70e..5f558851d646 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -31,8 +31,7 @@ #include #include #include - -#include "fixp-arith.h" +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anssi Hannula "); diff --git a/drivers/input/fixp-arith.h b/drivers/input/fixp-arith.h deleted file mode 100644 index 3089d7382325..000000000000 --- a/drivers/input/fixp-arith.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef _FIXP_ARITH_H -#define _FIXP_ARITH_H - -/* - * Simplistic fixed-point arithmetics. - * Hmm, I'm probably duplicating some code :( - * - * Copyright (c) 2002 Johann Deneux - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#include - -/* The type representing fixed-point values */ -typedef s16 fixp_t; - -#define FRAC_N 8 -#define FRAC_MASK ((1< 123.0 */ -static inline fixp_t fixp_new(s16 a) -{ - return a< -1.0 - 0x8000 -> 1.0 - 0x0000 -> 0.0 -*/ -static inline fixp_t fixp_new16(s16 a) -{ - return ((s32)a)>>(16-FRAC_N); -} - -static inline fixp_t fixp_cos(unsigned int degrees) -{ - int quadrant = (degrees / 90) & 3; - unsigned int i = degrees % 90; - - if (quadrant == 1 || quadrant == 3) - i = 90 - i; - - i >>= 1; - - return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i]; -} - -static inline fixp_t fixp_sin(unsigned int degrees) -{ - return -fixp_cos(degrees + 90); -} - -static inline fixp_t fixp_mult(fixp_t a, fixp_t b) -{ - return ((s32)(a*b))>>FRAC_N; -} - -#endif diff --git a/include/linux/fixp-arith.h b/include/linux/fixp-arith.h new file mode 100644 index 000000000000..3089d7382325 --- /dev/null +++ b/include/linux/fixp-arith.h @@ -0,0 +1,87 @@ +#ifndef _FIXP_ARITH_H +#define _FIXP_ARITH_H + +/* + * Simplistic fixed-point arithmetics. + * Hmm, I'm probably duplicating some code :( + * + * Copyright (c) 2002 Johann Deneux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so by + * e-mail - mail your message to + */ + +#include + +/* The type representing fixed-point values */ +typedef s16 fixp_t; + +#define FRAC_N 8 +#define FRAC_MASK ((1< 123.0 */ +static inline fixp_t fixp_new(s16 a) +{ + return a< -1.0 + 0x8000 -> 1.0 + 0x0000 -> 0.0 +*/ +static inline fixp_t fixp_new16(s16 a) +{ + return ((s32)a)>>(16-FRAC_N); +} + +static inline fixp_t fixp_cos(unsigned int degrees) +{ + int quadrant = (degrees / 90) & 3; + unsigned int i = degrees % 90; + + if (quadrant == 1 || quadrant == 3) + i = 90 - i; + + i >>= 1; + + return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i]; +} + +static inline fixp_t fixp_sin(unsigned int degrees) +{ + return -fixp_cos(degrees + 90); +} + +static inline fixp_t fixp_mult(fixp_t a, fixp_t b) +{ + return ((s32)(a*b))>>FRAC_N; +} + +#endif -- cgit v1.2.3 From e89fca923f32de26b69bf4cd604f7b960b161551 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 14 May 2012 08:07:45 -0300 Subject: [media] gspca - ov534: Add Hue control Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov534.c | 65 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index c15cf23d7758..b5acb1e4b4e7 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -34,6 +34,8 @@ #include "gspca.h" +#include + #define OV534_REG_ADDRESS 0xf1 /* sensor address */ #define OV534_REG_SUBADDR 0xf2 #define OV534_REG_WRITE 0xf3 @@ -53,6 +55,7 @@ MODULE_LICENSE("GPL"); /* controls */ enum e_ctrl { + HUE, SATURATION, BRIGHTNESS, CONTRAST, @@ -87,6 +90,7 @@ enum sensors { }; /* V4L2 controls supported by the driver */ +static void sethue(struct gspca_dev *gspca_dev); static void setsaturation(struct gspca_dev *gspca_dev); static void setbrightness(struct gspca_dev *gspca_dev); static void setcontrast(struct gspca_dev *gspca_dev); @@ -103,6 +107,18 @@ static int sd_start(struct gspca_dev *gspca_dev); static void sd_stopN(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[] = { +[HUE] = { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -90, + .maximum = 90, + .step = 1, + .default_value = 0, + }, + .set_control = sethue + }, [SATURATION] = { { .id = V4L2_CID_SATURATION, @@ -684,7 +700,7 @@ static const u8 sensor_init_772x[][2] = { { 0x9c, 0x20 }, { 0x9e, 0x81 }, - { 0xa6, 0x06 }, + { 0xa6, 0x07 }, { 0x7e, 0x0c }, { 0x7f, 0x16 }, { 0x80, 0x2a }, @@ -955,6 +971,48 @@ static void set_frame_rate(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "frame_rate: %d", r->fps); } +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; + + val = sd->ctrls[HUE].val; + if (sd->sensor == SENSOR_OV767x) { + /* TBD */ + } else { + s16 huesin; + s16 huecos; + + /* fixp_sin and fixp_cos accept only positive values, while + * our val is between -90 and 90 + */ + val += 360; + + /* According to the datasheet the registers expect HUESIN and + * HUECOS to be the result of the trigonometric functions, + * scaled by 0x80. + * + * The 0x100 here represents the maximun absolute value + * returned byt fixp_sin and fixp_cos, so the scaling will + * consider the result like in the interval [-1.0, 1.0]. + */ + huesin = fixp_sin(val) * 0x80 / 0x100; + huecos = fixp_cos(val) * 0x80 / 0x100; + + if (huesin < 0) { + sccb_reg_write(gspca_dev, 0xab, + sccb_reg_read(gspca_dev, 0xab) | 0x2); + huesin = -huesin; + } else { + sccb_reg_write(gspca_dev, 0xab, + sccb_reg_read(gspca_dev, 0xab) & ~0x2); + + } + sccb_reg_write(gspca_dev, 0xa9, (u8)huecos); + sccb_reg_write(gspca_dev, 0xaa, (u8)huesin); + } +} + static void setsaturation(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1231,7 +1289,8 @@ static int sd_init(struct gspca_dev *gspca_dev) if ((sensor_id & 0xfff0) == 0x7670) { sd->sensor = SENSOR_OV767x; - gspca_dev->ctrl_dis = (1 << GAIN) | + gspca_dev->ctrl_dis = (1 << HUE) | + (1 << GAIN) | (1 << AGC) | (1 << SHARPNESS); /* auto */ sd->ctrls[SATURATION].min = 0, @@ -1310,6 +1369,8 @@ static int sd_start(struct gspca_dev *gspca_dev) set_frame_rate(gspca_dev); + if (!(gspca_dev->ctrl_dis & (1 << HUE))) + sethue(gspca_dev); setsaturation(gspca_dev); if (!(gspca_dev->ctrl_dis & (1 << AGC))) setagc(gspca_dev); -- cgit v1.2.3 From 5be4fe633a5ddc268afe71257e82a64773ea2f9d Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 21 Apr 2012 18:46:30 -0300 Subject: [media] s5p-fimc: Fix locking in subdev set_crop op When setting TRY crop on the sub-device the mutex was erroneously acquired rather than released on exit path. This bug is present in kernels starting from v3.2. Cc: stable@vger.kernel.org Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 72d51504ed21..520217758c04 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1385,7 +1385,7 @@ static int fimc_subdev_set_crop(struct v4l2_subdev *sd, fimc_capture_try_crop(ctx, r, crop->pad); if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { - mutex_lock(&fimc->lock); + mutex_unlock(&fimc->lock); *v4l2_subdev_get_try_crop(fh, crop->pad) = *r; return 0; } -- cgit v1.2.3 From 4b25524c7b329f6d7a3f6d14b0f036fd6091def2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 10:41:54 -0300 Subject: [media] pms: update to the latest V4L2 frameworks Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pms.c | 237 ++++++++++++++++++++++++---------------------- 1 file changed, 123 insertions(+), 114 deletions(-) diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index e753b5e4d2ce..8b80f9808911 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -30,15 +30,19 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include #include MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.4"); +MODULE_VERSION("0.0.5"); #define MOTOROLA 1 #define PHILIPS2 2 /* SAA7191 */ @@ -55,11 +59,11 @@ struct i2c_info { struct pms { struct v4l2_device v4l2_dev; struct video_device vdev; + struct v4l2_ctrl_handler hdl; int height; int width; int depth; int input; - s32 brightness, saturation, hue, contrast; struct mutex lock; int i2c_count; struct i2c_info i2cinfo[64]; @@ -72,8 +76,6 @@ struct pms { void __iomem *mem; }; -static struct pms pms_card; - /* * I/O ports and Shared Memory */ @@ -676,8 +678,10 @@ static int pms_querycap(struct file *file, void *priv, strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card)); - strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + snprintf(vcap->bus_info, sizeof(vcap->bus_info), + "ISA:%s", dev->v4l2_dev.name); + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -716,11 +720,9 @@ static int pms_s_input(struct file *file, void *fh, unsigned int inp) if (inp > 3) return -EINVAL; - mutex_lock(&dev->lock); dev->input = inp; pms_videosource(dev, inp & 1); pms_vcrinput(dev, inp >> 1); - mutex_unlock(&dev->lock); return 0; } @@ -738,7 +740,6 @@ static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) int ret = 0; dev->std = *std; - mutex_lock(&dev->lock); if (dev->std & V4L2_STD_NTSC) { pms_framerate(dev, 30); pms_secamcross(dev, 0); @@ -762,81 +763,31 @@ static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) pms_format(dev, 0); break; }*/ - mutex_unlock(&dev->lock); - return 0; -} - -static int pms_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0); - } - return -EINVAL; -} - -static int pms_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct pms *dev = video_drvdata(file); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dev->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dev->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dev->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dev->hue; - break; - default: - ret = -EINVAL; - break; - } return ret; } -static int pms_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int pms_s_ctrl(struct v4l2_ctrl *ctrl) { - struct pms *dev = video_drvdata(file); + struct pms *dev = container_of(ctrl->handler, struct pms, hdl); int ret = 0; - mutex_lock(&dev->lock); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - dev->brightness = ctrl->value; - pms_brightness(dev, dev->brightness); + pms_brightness(dev, ctrl->val); break; case V4L2_CID_CONTRAST: - dev->contrast = ctrl->value; - pms_contrast(dev, dev->contrast); + pms_contrast(dev, ctrl->val); break; case V4L2_CID_SATURATION: - dev->saturation = ctrl->value; - pms_saturation(dev, dev->saturation); + pms_saturation(dev, ctrl->val); break; case V4L2_CID_HUE: - dev->hue = ctrl->value; - pms_hue(dev, dev->hue); + pms_hue(dev, ctrl->val); break; default: ret = -EINVAL; break; } - mutex_unlock(&dev->lock); return ret; } @@ -884,13 +835,11 @@ static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fm if (ret) return ret; - mutex_lock(&dev->lock); dev->width = pix->width; dev->height = pix->height; dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16; pms_resolution(dev, dev->width, dev->height); /* Ok we figured out what to use from our wide choice */ - mutex_unlock(&dev->lock); return 0; } @@ -901,7 +850,7 @@ static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc "RGB 5:5:5", V4L2_PIX_FMT_RGB555, { 0, 0, 0, 0 } }, - { 0, 0, 0, + { 1, 0, 0, "RGB 5:6:5", V4L2_PIX_FMT_RGB565, { 0, 0, 0, 0 } }, @@ -922,32 +871,43 @@ static ssize_t pms_read(struct file *file, char __user *buf, struct pms *dev = video_drvdata(file); int len; - mutex_lock(&dev->lock); len = pms_capture(dev, buf, (dev->depth == 15), count); - mutex_unlock(&dev->lock); return len; } +static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait) +{ + struct v4l2_fh *fh = file->private_data; + unsigned int res = POLLIN | POLLRDNORM; + + if (v4l2_event_pending(fh)) + res |= POLLPRI; + poll_wait(file, &fh->wait, wait); + return res; +} + static const struct v4l2_file_operations pms_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = pms_poll, .unlocked_ioctl = video_ioctl2, .read = pms_read, }; static const struct v4l2_ioctl_ops pms_ioctl_ops = { - .vidioc_querycap = pms_querycap, - .vidioc_g_input = pms_g_input, - .vidioc_s_input = pms_s_input, - .vidioc_enum_input = pms_enum_input, - .vidioc_g_std = pms_g_std, - .vidioc_s_std = pms_s_std, - .vidioc_queryctrl = pms_queryctrl, - .vidioc_g_ctrl = pms_g_ctrl, - .vidioc_s_ctrl = pms_s_ctrl, - .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap, + .vidioc_querycap = pms_querycap, + .vidioc_g_input = pms_g_input, + .vidioc_s_input = pms_s_input, + .vidioc_enum_input = pms_enum_input, + .vidioc_g_std = pms_g_std, + .vidioc_s_std = pms_s_std, + .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* @@ -1068,76 +1028,125 @@ static int enable; module_param(enable, int, 0); #endif -static int __init pms_init(void) +static const struct v4l2_ctrl_ops pms_ctrl_ops = { + .s_ctrl = pms_s_ctrl, +}; + +static int pms_probe(struct device *pdev, unsigned int card) { - struct pms *dev = &pms_card; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct pms *dev; + struct v4l2_device *v4l2_dev; + struct v4l2_ctrl_handler *hdl; int res; - strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name)); - - v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n"); - #ifndef MODULE if (!enable) { - v4l2_err(v4l2_dev, - "PMS: not enabled, use pms.enable=1 to probe\n"); + pr_err("PMS: not enabled, use pms.enable=1 to probe\n"); return -ENODEV; } #endif + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + dev->decoder = PHILIPS2; dev->io = io_port; dev->data = io_port + 1; + v4l2_dev = &dev->v4l2_dev; + hdl = &dev->hdl; - if (init_mediavision(dev)) { + res = v4l2_device_register(pdev, v4l2_dev); + if (res < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + goto free_dev; + } + v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n"); + + res = init_mediavision(dev); + if (res) { v4l2_err(v4l2_dev, "Board not found.\n"); - return -ENODEV; + goto free_io; } - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return res; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 139); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 70); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 64); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 0); + if (hdl->error) { + res = hdl->error; + goto free_hdl; } + mutex_init(&dev->lock); strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); dev->vdev.v4l2_dev = v4l2_dev; + dev->vdev.ctrl_handler = hdl; dev->vdev.fops = &pms_fops; dev->vdev.ioctl_ops = &pms_ioctl_ops; dev->vdev.release = video_device_release_empty; + dev->vdev.lock = &dev->lock; + dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); - mutex_init(&dev->lock); dev->std = V4L2_STD_NTSC_M; dev->height = 240; dev->width = 320; - dev->depth = 15; - dev->brightness = 139; - dev->contrast = 70; - dev->hue = 0; - dev->saturation = 64; + dev->depth = 16; pms_swsense(dev, 75); pms_resolution(dev, 320, 240); pms_videosource(dev, 0); pms_vcrinput(dev, 0); - if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - v4l2_device_unregister(&dev->v4l2_dev); - release_region(dev->io, 3); - release_region(0x9a01, 1); - iounmap(dev->mem); - return -EINVAL; - } - return 0; + v4l2_ctrl_handler_setup(hdl); + res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr); + if (res >= 0) + return 0; + +free_hdl: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); +free_io: + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); +free_dev: + kfree(dev); + return res; } -static void __exit pms_exit(void) +static int pms_remove(struct device *pdev, unsigned int card) { - struct pms *dev = &pms_card; + struct pms *dev = dev_get_drvdata(pdev); video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->hdl); release_region(dev->io, 3); release_region(0x9a01, 1); iounmap(dev->mem); + return 0; +} + +static struct isa_driver pms_driver = { + .probe = pms_probe, + .remove = pms_remove, + .driver = { + .name = "pms", + }, +}; + +static int __init pms_init(void) +{ + return isa_register_driver(&pms_driver, 1); +} + +static void __exit pms_exit(void) +{ + isa_unregister_driver(&pms_driver); } module_init(pms_init); -- cgit v1.2.3 From 6fb39c50a04aca7a6bb6c5b0a10204fa7fbe749c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 6 Apr 2012 16:32:30 -0300 Subject: [media] af9035: various small changes for af9035_ctrl_msg() Fix USB buffer len to maximum possible. Various log writing fixes, remove extra new lines and excessive type casts. Rename and type change some variables. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index e1d6e6efa77b..a68ae5385790 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -57,17 +57,16 @@ static u16 af9035_checksum(const u8 *buf, size_t len) static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) { -#define BUF_LEN 63 +#define BUF_LEN 64 #define REQ_HDR_LEN 4 /* send header size */ #define ACK_HDR_LEN 3 /* rece header size */ #define CHECKSUM_LEN 2 #define USB_TIMEOUT 2000 - int ret, act_len; + int ret, msg_len, act_len; u8 buf[BUF_LEN]; - u32 msg_len; static u8 seq; /* packet sequence number */ - u16 checksum, tmpsum; + u16 checksum, tmp_checksum; /* buffer overflow check */ if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || @@ -89,14 +88,14 @@ static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) /* calc and add checksum */ checksum = af9035_checksum(buf, buf[0] - 1); - buf[buf[0]-1] = (checksum >> 8); - buf[buf[0]-0] = (checksum & 0xff); + buf[buf[0] - 1] = (checksum >> 8); + buf[buf[0] - 0] = (checksum & 0xff); msg_len = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN ; /* send req */ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len, - &act_len, USB_TIMEOUT); + &act_len, USB_TIMEOUT); if (ret < 0) err("bulk message failed=%d (%d/%d)", ret, msg_len, act_len); else @@ -112,29 +111,29 @@ static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) /* receive ack and data if read req */ msg_len = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN; ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, - &act_len, USB_TIMEOUT); + &act_len, USB_TIMEOUT); if (ret < 0) { err("recv bulk message failed=%d", ret); ret = -EIO; goto err_mutex_unlock; } + if (act_len != msg_len) { - err("recv bulk message truncated (%d != %u)\n", - act_len, (unsigned int)msg_len); + err("recv bulk message truncated (%d != %d)", act_len, msg_len); ret = -EIO; goto err_mutex_unlock; } /* verify checksum */ checksum = af9035_checksum(buf, act_len - 2); - tmpsum = (buf[act_len - 2] << 8) | buf[act_len - 1]; - if (tmpsum != checksum) { - err("%s: command=%02X checksum mismatch (%04X != %04X)\n", - __func__, req->cmd, - (unsigned int)tmpsum, (unsigned int)checksum); + tmp_checksum = (buf[act_len - 2] << 8) | buf[act_len - 1]; + if (tmp_checksum != checksum) { + err("%s: command=%02x checksum mismatch (%04x != %04x)", + __func__, req->cmd, tmp_checksum, checksum); ret = -EIO; goto err_mutex_unlock; } + /* check status */ if (buf[2]) { pr_debug("%s: command=%02x failed fw error=%d\n", __func__, @@ -400,7 +399,7 @@ static int af9035_download_firmware(struct usb_device *udev, struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; u8 hdr_core; u16 hdr_addr, hdr_data_len, hdr_checksum; - #define MAX_DATA 57 + #define MAX_DATA 58 #define HDR_SIZE 7 /* -- cgit v1.2.3 From 52560b72845a4ff21d3ad1dd509b13d9c0b46585 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 6 Apr 2012 19:05:11 -0300 Subject: [media] af9035: remove unused struct Not used anymore since new firmware downloader. I forget to remove those earlier when changed firmware downloader. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h index 031bd9cf0cb2..eece4ed8c99e 100644 --- a/drivers/media/dvb/dvb-usb/af9035.h +++ b/drivers/media/dvb/dvb-usb/af9035.h @@ -52,20 +52,6 @@ struct config { bool hw_not_supported; }; -struct fw_segment { -#define SEGMENT_FW_DL 0 -#define SEGMENT_ROM_COPY 1 -#define SEGMENT_DIRECT_CMD 2 - u8 type; - u32 len; -}; - -struct fw_header { -#define SEGMENT_MAX_COUNT 6 - u8 segment_count; - struct fw_segment segment[SEGMENT_MAX_COUNT]; -}; - u32 clock_lut[] = { 20480000, /* FPGA */ 16384000, /* 16.38 MHz */ -- cgit v1.2.3 From 3234bd2f193936da6180a7dc6699a75191bc44d1 Mon Sep 17 00:00:00 2001 From: Hans-Frieder Vogt Date: Sat, 21 Apr 2012 18:23:16 -0300 Subject: [media] af9035: add remote control support Signed-off-by: Hans-Frieder Vogt Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 65 ++++++++++++++++++++++++++++++++++++++ drivers/media/dvb/dvb-usb/af9035.h | 1 + 2 files changed, 66 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index a68ae5385790..fc9e68cc5d6a 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -313,6 +313,37 @@ static struct i2c_algorithm af9035_i2c_algo = { .functionality = af9035_i2c_functionality, }; +#define AF9035_POLL 250 +static int af9035_rc_query(struct dvb_usb_device *d) +{ + unsigned int key; + unsigned char b[4]; + int ret; + struct usb_req req = { CMD_IR_GET, 0, 0, NULL, 4, b }; + + ret = af9035_ctrl_msg(d->udev, &req); + if (ret < 0) + goto err; + + if ((b[2] + b[3]) == 0xff) { + if ((b[0] + b[1]) == 0xff) { + /* NEC */ + key = b[0] << 8 | b[2]; + } else { + /* ext. NEC */ + key = b[0] << 16 | b[1] << 8 | b[2]; + } + } else { + key = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; + } + + rc_keydown(d->rc_dev, key, 0); + +err: + /* ignore errors */ + return 0; +} + static int af9035_init(struct dvb_usb_device *d) { int ret, i; @@ -627,6 +658,32 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) for (i = 0; i < af9035_properties[0].num_adapters; i++) af9035_af9033_config[i].clock = clock_lut[tmp]; + ret = af9035_rd_reg(d, EEPROM_IR_MODE, &tmp); + if (ret < 0) + goto err; + pr_debug("%s: ir_mode=%02x\n", __func__, tmp); + + /* don't activate rc if in HID mode or if not available */ + if (tmp == 5) { + ret = af9035_rd_reg(d, EEPROM_IR_TYPE, &tmp); + if (ret < 0) + goto err; + pr_debug("%s: ir_type=%02x\n", __func__, tmp); + + switch (tmp) { + case 0: /* NEC */ + default: + d->props.rc.core.protocol = RC_TYPE_NEC; + d->props.rc.core.allowed_protos = RC_TYPE_NEC; + break; + case 1: /* RC6 */ + d->props.rc.core.protocol = RC_TYPE_RC6; + d->props.rc.core.allowed_protos = RC_TYPE_RC6; + break; + } + d->props.rc.core.rc_query = af9035_rc_query; + } + return 0; err: @@ -1003,6 +1060,14 @@ static struct dvb_usb_device_properties af9035_properties[] = { .i2c_algo = &af9035_i2c_algo, + .rc.core = { + .protocol = RC_TYPE_UNKNOWN, + .module_name = "af9035", + .rc_query = NULL, + .rc_interval = AF9035_POLL, + .allowed_protos = RC_TYPE_UNKNOWN, + .rc_codes = RC_MAP_EMPTY, + }, .num_device_descs = 5, .devices = { { diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h index eece4ed8c99e..27a484bdc961 100644 --- a/drivers/media/dvb/dvb-usb/af9035.h +++ b/drivers/media/dvb/dvb-usb/af9035.h @@ -96,6 +96,7 @@ u32 clock_lut_it9135[] = { #define CMD_MEM_WR 0x01 #define CMD_I2C_RD 0x02 #define CMD_I2C_WR 0x03 +#define CMD_IR_GET 0x18 #define CMD_FW_DL 0x21 #define CMD_FW_QUERYINFO 0x22 #define CMD_FW_BOOT 0x23 -- cgit v1.2.3 From 47eafa5427c2da51f676e4c0b48bc851df8779f8 Mon Sep 17 00:00:00 2001 From: Hans-Frieder Vogt Date: Sat, 7 Apr 2012 10:34:34 -0300 Subject: [media] af9033: implement ber and ucb functions af9033: implement read_ber and read_ucblocks functions. Version 2 of patch that reflects my findings on the behaviour of abort_cnt, err_cnt and bit_cnt: - bit_cnt is always 0x2710 (10000) - abort_cnt is between 0 and 0x2710 - err_cnt is between 0 and 640000 (= 0x2710 * 8 * 8) in the current implementation BER is calculated as the number of bit errors per processed bits, ignoring those bits that are already discarded and counted in abort_cnt, i.e. UCBLOCKS. Signed-off-by: Hans-Frieder Vogt Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/af9033.c | 65 ++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c index 2cb1f8d6955e..a38998286260 100644 --- a/drivers/media/dvb/frontends/af9033.c +++ b/drivers/media/dvb/frontends/af9033.c @@ -29,6 +29,10 @@ struct af9033_state { u32 bandwidth_hz; bool ts_mode_parallel; bool ts_mode_serial; + + u32 ber; + u32 ucb; + unsigned long last_stat_check; }; /* write multiple registers */ @@ -772,16 +776,73 @@ err: return ret; } +static int af9033_update_ch_stat(struct af9033_state *state) +{ + int ret = 0; + u32 err_cnt, bit_cnt; + u16 abort_cnt; + u8 buf[7]; + + /* only update data every half second */ + if (time_after(jiffies, state->last_stat_check + msecs_to_jiffies(500))) { + ret = af9033_rd_regs(state, 0x800032, buf, sizeof(buf)); + if (ret < 0) + goto err; + /* in 8 byte packets? */ + abort_cnt = (buf[1] << 8) + buf[0]; + /* in bits */ + err_cnt = (buf[4] << 16) + (buf[3] << 8) + buf[2]; + /* in 8 byte packets? always(?) 0x2710 = 10000 */ + bit_cnt = (buf[6] << 8) + buf[5]; + + if (bit_cnt < abort_cnt) { + abort_cnt = 1000; + state->ber = 0xffffffff; + } else { + /* 8 byte packets, that have not been rejected already */ + bit_cnt -= (u32)abort_cnt; + if (bit_cnt == 0) { + state->ber = 0xffffffff; + } else { + err_cnt -= (u32)abort_cnt * 8 * 8; + bit_cnt *= 8 * 8; + state->ber = err_cnt * (0xffffffff / bit_cnt); + } + } + state->ucb += abort_cnt; + state->last_stat_check = jiffies; + } + + return 0; +err: + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber) { - *ber = 0; + struct af9033_state *state = fe->demodulator_priv; + int ret; + + ret = af9033_update_ch_stat(state); + if (ret < 0) + return ret; + + *ber = state->ber; return 0; } static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { - *ucblocks = 0; + struct af9033_state *state = fe->demodulator_priv; + int ret; + + ret = af9033_update_ch_stat(state); + if (ret < 0) + return ret; + + *ucblocks = state->ucb; return 0; } -- cgit v1.2.3 From 2a79eefa82d010b64b36efeebec04397ad494f22 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 7 May 2012 14:50:40 -0300 Subject: [media] af9035: move device configuration to the state Fixes most problems when having more than one device connected as demod and tuner configurations are not shared. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 62 ++++++++++++++++++++------------------ drivers/media/dvb/dvb-usb/af9035.h | 9 +++++- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index fc9e68cc5d6a..d2c84ee290a8 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -20,24 +20,11 @@ */ #include "af9035.h" -#include "af9033.h" -#include "tua9001.h" -#include "fc0011.h" -#include "mxl5007t.h" -#include "tda18218.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static DEFINE_MUTEX(af9035_usb_mutex); -static struct config af9035_config; static struct dvb_usb_device_properties af9035_properties[2]; static int af9035_properties_count = ARRAY_SIZE(af9035_properties); -static struct af9033_config af9035_af9033_config[] = { - { - .ts_mode = AF9033_TS_MODE_USB, - }, { - .ts_mode = AF9033_TS_MODE_SERIAL, - } -}; static u16 af9035_checksum(const u8 *buf, size_t len) { @@ -218,6 +205,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct state *state = d->priv; int ret; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) @@ -244,7 +232,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40 || msg[1].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; - } else if (msg[0].addr == af9035_af9033_config[0].i2c_addr) { + } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { /* integrated demod */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; @@ -267,7 +255,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; - } else if (msg[0].addr == af9035_af9033_config[0].i2c_addr) { + } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { /* integrated demod */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; @@ -346,6 +334,7 @@ err: static int af9035_init(struct dvb_usb_device *d) { + struct state *state = d->priv; int ret, i; u16 frame_size = 87 * 188 / 4; u8 packet_size = 512 / 4; @@ -360,7 +349,7 @@ static int af9035_init(struct dvb_usb_device *d) { 0x00dd88, (frame_size >> 0) & 0xff, 0xff}, { 0x00dd89, (frame_size >> 8) & 0xff, 0xff}, { 0x00dd0c, packet_size, 0xff}, - { 0x00dd11, af9035_config.dual_mode << 6, 0x40 }, + { 0x00dd11, state->dual_mode << 6, 0x40 }, { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff}, { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff}, { 0x00dd0d, packet_size, 0xff }, @@ -596,6 +585,7 @@ err: /* abuse that callback as there is no better one for reading eeprom */ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) { + struct state *state = d->priv; int ret, i, eeprom_shift = 0; u8 tmp; u16 tmp16; @@ -605,8 +595,8 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) if (ret < 0) goto err; - af9035_config.dual_mode = tmp; - pr_debug("%s: dual mode=%d\n", __func__, af9035_config.dual_mode); + state->dual_mode = tmp; + pr_debug("%s: dual mode=%d\n", __func__, state->dual_mode); for (i = 0; i < af9035_properties[0].num_adapters; i++) { /* tuner */ @@ -614,7 +604,7 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) if (ret < 0) goto err; - af9035_af9033_config[i].tuner = tmp; + state->af9033_config[i].tuner = tmp; pr_debug("%s: [%d]tuner=%02x\n", __func__, i, tmp); switch (tmp) { @@ -622,10 +612,10 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) case AF9033_TUNER_FC0011: case AF9033_TUNER_MXL5007T: case AF9033_TUNER_TDA18218: - af9035_af9033_config[i].spec_inv = 1; + state->af9033_config[i].spec_inv = 1; break; default: - af9035_config.hw_not_supported = true; + state->hw_not_supported = true; warn("tuner ID=%02x not supported, please report!", tmp); }; @@ -656,7 +646,7 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) tmp = (tmp >> 0) & 0x0f; for (i = 0; i < af9035_properties[0].num_adapters; i++) - af9035_af9033_config[i].clock = clock_lut[tmp]; + state->af9033_config[i].clock = clock_lut[tmp]; ret = af9035_rd_reg(d, EEPROM_IR_MODE, &tmp); if (ret < 0) @@ -695,10 +685,11 @@ err: /* abuse that callback as there is no better one for reading eeprom */ static int af9035_read_mac_address_it9135(struct dvb_usb_device *d, u8 mac[6]) { + struct state *state = d->priv; int ret, i; u8 tmp; - af9035_config.dual_mode = 0; + state->dual_mode = false; /* get demod clock */ ret = af9035_rd_reg(d, 0x00d800, &tmp); @@ -708,7 +699,7 @@ static int af9035_read_mac_address_it9135(struct dvb_usb_device *d, u8 mac[6]) tmp = (tmp >> 0) & 0x0f; for (i = 0; i < af9035_properties[0].num_adapters; i++) - af9035_af9033_config[i].clock = clock_lut_it9135[tmp]; + state->af9033_config[i].clock = clock_lut_it9135[tmp]; return 0; @@ -785,7 +776,9 @@ err: static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { - switch (af9035_af9033_config[0].tuner) { + struct state *state = d->priv; + + switch (state->af9033_config[0].tuner) { case AF9033_TUNER_FC0011: return af9035_fc0011_tuner_callback(d, cmd, arg); default: @@ -813,28 +806,32 @@ static int af9035_frontend_callback(void *adapter_priv, int component, static int af9035_frontend_attach(struct dvb_usb_adapter *adap) { + struct state *state = adap->dev->priv; int ret; - if (af9035_config.hw_not_supported) { + if (state->hw_not_supported) { ret = -ENODEV; goto err; } if (adap->id == 0) { + state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB; + state->af9033_config[1].ts_mode = AF9033_TS_MODE_SERIAL; + ret = af9035_wr_reg(adap->dev, 0x00417f, - af9035_af9033_config[1].i2c_addr); + state->af9033_config[1].i2c_addr); if (ret < 0) goto err; ret = af9035_wr_reg(adap->dev, 0x00d81a, - af9035_config.dual_mode); + state->dual_mode); if (ret < 0) goto err; } /* attach demodulator */ adap->fe_adap[0].fe = dvb_attach(af9033_attach, - &af9035_af9033_config[adap->id], &adap->dev->i2c_adap); + &state->af9033_config[adap->id], &adap->dev->i2c_adap); if (adap->fe_adap[0].fe == NULL) { ret = -ENODEV; goto err; @@ -876,10 +873,11 @@ static struct tda18218_config af9035_tda18218_config = { static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { + struct state *state = adap->dev->priv; int ret; struct dvb_frontend *fe; - switch (af9035_af9033_config[adap->id].tuner) { + switch (state->af9033_config[adap->id].tuner) { case AF9033_TUNER_TUA9001: /* AF9035 gpiot3 = TUA9001 RESETN AF9035 gpiot2 = TUA9001 RXEN */ @@ -1032,6 +1030,8 @@ static struct dvb_usb_device_properties af9035_properties[] = { .firmware = "dvb-usb-af9035-02.fw", .no_reconnect = 1, + .size_of_priv = sizeof(struct state), + .num_adapters = 1, .adapter = { { @@ -1109,6 +1109,8 @@ static struct dvb_usb_device_properties af9035_properties[] = { .firmware = "dvb-usb-it9135-01.fw", .no_reconnect = 1, + .size_of_priv = sizeof(struct state), + .num_adapters = 1, .adapter = { { diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h index 27a484bdc961..262cc3f4b008 100644 --- a/drivers/media/dvb/dvb-usb/af9035.h +++ b/drivers/media/dvb/dvb-usb/af9035.h @@ -26,6 +26,11 @@ #define DVB_USB_LOG_PREFIX "af9035" #include "dvb-usb.h" +#include "af9033.h" +#include "tua9001.h" +#include "fc0011.h" +#include "mxl5007t.h" +#include "tda18218.h" struct reg_val { u32 reg; @@ -47,9 +52,11 @@ struct usb_req { u8 *rbuf; }; -struct config { +struct state { bool dual_mode; bool hw_not_supported; + + struct af9033_config af9033_config[2]; }; u32 clock_lut[] = { -- cgit v1.2.3 From 1cbabf9c751f8795a35885fad9ceaec7de71a29e Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 7 May 2012 14:59:55 -0300 Subject: [media] af9035: remove one config parameter We can use tuner ID instead of HW not supported flag. Lesser code is better code. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 4 ++-- drivers/media/dvb/dvb-usb/af9035.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index d2c84ee290a8..d97f45e39f4f 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -615,7 +615,6 @@ static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) state->af9033_config[i].spec_inv = 1; break; default: - state->hw_not_supported = true; warn("tuner ID=%02x not supported, please report!", tmp); }; @@ -809,7 +808,8 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) struct state *state = adap->dev->priv; int ret; - if (state->hw_not_supported) { + if (!state->af9033_config[adap->id].tuner) { + /* unsupported tuner */ ret = -ENODEV; goto err; } diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h index 262cc3f4b008..481a1a43dd2a 100644 --- a/drivers/media/dvb/dvb-usb/af9035.h +++ b/drivers/media/dvb/dvb-usb/af9035.h @@ -54,7 +54,6 @@ struct usb_req { struct state { bool dual_mode; - bool hw_not_supported; struct af9033_config af9033_config[2]; }; -- cgit v1.2.3 From d281c1f1443b36890c35de71d9fdaa5cd3899003 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 7 May 2012 15:16:36 -0300 Subject: [media] af9035: add few new reference design USB IDs Add all known reference design USB IDs. Rename two earlier reference design USB IDs. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 16 ++++++++++++++-- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 7 +++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index d97f45e39f4f..e83b39d3993c 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -990,7 +990,10 @@ err: enum af9035_id_entry { AF9035_15A4_9035, + AF9035_15A4_1000, AF9035_15A4_1001, + AF9035_15A4_1002, + AF9035_15A4_1003, AF9035_0CCD_0093, AF9035_07CA_A835, AF9035_07CA_B835, @@ -1001,9 +1004,15 @@ enum af9035_id_entry { static struct usb_device_id af9035_id[] = { [AF9035_15A4_9035] = { - USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035)}, + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_9035)}, + [AF9035_15A4_1000] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1000)}, [AF9035_15A4_1001] = { - USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_2)}, + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1001)}, + [AF9035_15A4_1002] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1002)}, + [AF9035_15A4_1003] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1003)}, [AF9035_0CCD_0093] = { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)}, [AF9035_07CA_A835] = { @@ -1074,7 +1083,10 @@ static struct dvb_usb_device_properties af9035_properties[] = { .name = "Afatech AF9035 reference design", .cold_ids = { &af9035_id[AF9035_15A4_9035], + &af9035_id[AF9035_15A4_1000], &af9035_id[AF9035_15A4_1001], + &af9035_id[AF9035_15A4_1002], + &af9035_id[AF9035_15A4_1003], }, }, { .name = "TerraTec Cinergy T Stick", diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 2418e41ed0dc..7a6160bf54ba 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -76,8 +76,11 @@ #define USB_PID_AFATECH_AF9005 0x9020 #define USB_PID_AFATECH_AF9015_9015 0x9015 #define USB_PID_AFATECH_AF9015_9016 0x9016 -#define USB_PID_AFATECH_AF9035 0x9035 -#define USB_PID_AFATECH_AF9035_2 0x1001 +#define USB_PID_AFATECH_AF9035_1000 0x1000 +#define USB_PID_AFATECH_AF9035_1001 0x1001 +#define USB_PID_AFATECH_AF9035_1002 0x1002 +#define USB_PID_AFATECH_AF9035_1003 0x1003 +#define USB_PID_AFATECH_AF9035_9035 0x9035 #define USB_PID_TREKSTOR_DVBT 0x901b #define USB_VID_ALINK_DTU 0xf170 #define USB_PID_ANSONIC_DVBT_USB 0x6000 -- cgit v1.2.3 From 1c542ba85461f4f4f456eeee4fa7e90a3d138c6a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Mar 2012 10:42:52 -0300 Subject: [media] mt9p031: Identify color/mono models using I2C device name Instead of passing a color/monochrome flag through platform data, rely on the I2C device name to identify the chip model. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9p031.c | 14 +++++++++++--- include/media/mt9p031.h | 6 ------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c index c81eaf4fbe01..5b8a3968035d 100644 --- a/drivers/media/video/mt9p031.c +++ b/drivers/media/video/mt9p031.c @@ -99,6 +99,11 @@ #define MT9P031_TEST_PATTERN_RED 0xa2 #define MT9P031_TEST_PATTERN_BLUE 0xa3 +enum mt9p031_model { + MT9P031_MODEL_COLOR, + MT9P031_MODEL_MONOCHROME, +}; + struct mt9p031 { struct v4l2_subdev subdev; struct media_pad pad; @@ -109,6 +114,7 @@ struct mt9p031 { struct mutex power_lock; /* lock to protect power_count */ int power_count; + enum mt9p031_model model; struct aptina_pll pll; /* Registers cache */ @@ -764,7 +770,7 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) format = v4l2_subdev_get_try_format(fh, 0); - if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION) + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) format->code = V4L2_MBUS_FMT_Y12_1X12; else format->code = V4L2_MBUS_FMT_SGRBG12_1X12; @@ -842,6 +848,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->pdata = pdata; mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; + mt9p031->model = did->driver_data; v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4); @@ -882,7 +889,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->crop.left = MT9P031_COLUMN_START_DEF; mt9p031->crop.top = MT9P031_ROW_START_DEF; - if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION) + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; else mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; @@ -918,7 +925,8 @@ static int mt9p031_remove(struct i2c_client *client) } static const struct i2c_device_id mt9p031_id[] = { - { "mt9p031", 0 }, + { "mt9p031", MT9P031_MODEL_COLOR }, + { "mt9p031m", MT9P031_MODEL_MONOCHROME }, { } }; MODULE_DEVICE_TABLE(i2c, mt9p031_id); diff --git a/include/media/mt9p031.h b/include/media/mt9p031.h index 96448c7a318b..5b5090fb9c28 100644 --- a/include/media/mt9p031.h +++ b/include/media/mt9p031.h @@ -3,17 +3,11 @@ struct v4l2_subdev; -enum { - MT9P031_COLOR_VERSION, - MT9P031_MONOCHROME_VERSION, -}; - struct mt9p031_platform_data { int (*set_xclk)(struct v4l2_subdev *subdev, int hz); int (*reset)(struct v4l2_subdev *subdev, int active); int ext_freq; /* input frequency to the mt9p031 for PLL dividers */ int target_freq; /* frequency target for the PLL */ - int version; /* MT9P031_COLOR_VERSION or MT9P031_MONOCHROME_VERSION */ }; #endif -- cgit v1.2.3 From 15693b57931b19f3bb4664cb4fa3f6f966058749 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Mar 2012 10:59:41 -0300 Subject: [media] mt9p031: Replace the reset board callback by a GPIO number Use the GPIO from the sensor driver instead of calling back to board code. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9p031.c | 29 +++++++++++++++++++++++------ include/media/mt9p031.h | 13 ++++++++++--- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c index 5b8a3968035d..3a9363118e83 100644 --- a/drivers/media/video/mt9p031.c +++ b/drivers/media/video/mt9p031.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -116,6 +117,7 @@ struct mt9p031 { enum mt9p031_model model; struct aptina_pll pll; + int reset; /* Registers cache */ u16 output_control; @@ -247,8 +249,8 @@ static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) static int mt9p031_power_on(struct mt9p031 *mt9p031) { /* Ensure RESET_BAR is low */ - if (mt9p031->pdata->reset) { - mt9p031->pdata->reset(&mt9p031->subdev, 1); + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); usleep_range(1000, 2000); } @@ -258,8 +260,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) mt9p031->pdata->ext_freq); /* Now RESET_BAR must be high */ - if (mt9p031->pdata->reset) { - mt9p031->pdata->reset(&mt9p031->subdev, 0); + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 1); usleep_range(1000, 2000); } @@ -268,8 +270,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) static void mt9p031_power_off(struct mt9p031 *mt9p031) { - if (mt9p031->pdata->reset) { - mt9p031->pdata->reset(&mt9p031->subdev, 1); + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); usleep_range(1000, 2000); } @@ -849,6 +851,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; mt9p031->model = did->driver_data; + mt9p031->reset = -1; v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4); @@ -899,10 +902,22 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->format.field = V4L2_FIELD_NONE; mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; + if (pdata->reset != -1) { + ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, + "mt9p031_rst"); + if (ret < 0) + goto done; + + mt9p031->reset = pdata->reset; + } + ret = mt9p031_pll_setup(mt9p031); done: if (ret < 0) { + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); + v4l2_ctrl_handler_free(&mt9p031->ctrls); media_entity_cleanup(&mt9p031->subdev.entity); kfree(mt9p031); @@ -919,6 +934,8 @@ static int mt9p031_remove(struct i2c_client *client) v4l2_ctrl_handler_free(&mt9p031->ctrls); v4l2_device_unregister_subdev(subdev); media_entity_cleanup(&subdev->entity); + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); kfree(mt9p031); return 0; diff --git a/include/media/mt9p031.h b/include/media/mt9p031.h index 5b5090fb9c28..0c97b19af293 100644 --- a/include/media/mt9p031.h +++ b/include/media/mt9p031.h @@ -3,11 +3,18 @@ struct v4l2_subdev; +/* + * struct mt9p031_platform_data - MT9P031 platform data + * @set_xclk: Clock frequency set callback + * @reset: Chip reset GPIO (set to -1 if not used) + * @ext_freq: Input clock frequency + * @target_freq: Pixel clock frequency + */ struct mt9p031_platform_data { int (*set_xclk)(struct v4l2_subdev *subdev, int hz); - int (*reset)(struct v4l2_subdev *subdev, int active); - int ext_freq; /* input frequency to the mt9p031 for PLL dividers */ - int target_freq; /* frequency target for the PLL */ + int reset; + int ext_freq; + int target_freq; }; #endif -- cgit v1.2.3 From dfea00191aec19dc1115934a3c06e97fd8338e2e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Mar 2012 21:02:57 -0300 Subject: [media] mt9p031: Implement black level compensation control Add four new controls to configure black level compensation (BLC): - V4L2_CID_BLC_AUTO selects between manual and auto BLC - V4L2_CID_BLC_TARGET_LEVEL sets the target level for auto BLC - V4L2_CID_BLC_ANALOG_OFFSET sets the analog offset for manual BLC - V4L2_CID_BLC_DIGITAL_OFFSET sets the digital offset for manual BLC Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9p031.c | 118 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 7 deletions(-) diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c index 3a9363118e83..8f061d9ac443 100644 --- a/drivers/media/video/mt9p031.c +++ b/drivers/media/video/mt9p031.c @@ -91,7 +91,14 @@ #define MT9P031_GLOBAL_GAIN_MAX 1024 #define MT9P031_GLOBAL_GAIN_DEF 8 #define MT9P031_GLOBAL_GAIN_MULT (1 << 6) +#define MT9P031_ROW_BLACK_TARGET 0x49 #define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b +#define MT9P031_GREEN1_OFFSET 0x60 +#define MT9P031_GREEN2_OFFSET 0x61 +#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 +#define MT9P031_BLC_MANUAL_BLC (1 << 0) +#define MT9P031_RED_OFFSET 0x63 +#define MT9P031_BLUE_OFFSET 0x64 #define MT9P031_TEST_PATTERN 0xa0 #define MT9P031_TEST_PATTERN_SHIFT 3 #define MT9P031_TEST_PATTERN_ENABLE (1 << 0) @@ -110,7 +117,6 @@ struct mt9p031 { struct media_pad pad; struct v4l2_rect crop; /* Sensor window */ struct v4l2_mbus_framefmt format; - struct v4l2_ctrl_handler ctrls; struct mt9p031_platform_data *pdata; struct mutex power_lock; /* lock to protect power_count */ int power_count; @@ -119,6 +125,10 @@ struct mt9p031 { struct aptina_pll pll; int reset; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *blc_auto; + struct v4l2_ctrl *blc_offset; + /* Registers cache */ u16 output_control; u16 mode2; @@ -565,6 +575,10 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev, */ #define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) +#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) +#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) { @@ -629,11 +643,17 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TEST_PATTERN: if (!ctrl->val) { - ret = mt9p031_set_mode2(mt9p031, - 0, MT9P031_READ_MODE_2_ROW_BLC); - if (ret < 0) - return ret; - + /* Restore the black level compensation settings. */ + if (mt9p031->blc_auto->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_auto); + if (ret < 0) + return ret; + } + if (mt9p031->blc_offset->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_offset); + if (ret < 0) + return ret; + } return mt9p031_write(client, MT9P031_TEST_PATTERN, MT9P031_TEST_PATTERN_DISABLE); } @@ -648,10 +668,14 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) if (ret < 0) return ret; + /* Disable digital black level compensation when using a test + * pattern. + */ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, 0); if (ret < 0) return ret; + ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); if (ret < 0) return ret; @@ -659,7 +683,40 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) return mt9p031_write(client, MT9P031_TEST_PATTERN, ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) | MT9P031_TEST_PATTERN_ENABLE); + + case V4L2_CID_BLC_AUTO: + ret = mt9p031_set_mode2(mt9p031, + ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, + ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, + ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); + + case V4L2_CID_BLC_TARGET_LEVEL: + return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, + ctrl->val); + + case V4L2_CID_BLC_ANALOG_OFFSET: + data = ctrl->val & ((1 << 9) - 1); + + ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); + if (ret < 0) + return ret; + return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); + + case V4L2_CID_BLC_DIGITAL_OFFSET: + return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, + ctrl->val & ((1 << 12) - 1)); } + return 0; } @@ -693,6 +750,46 @@ static const struct v4l2_ctrl_config mt9p031_ctrls[] = { .flags = 0, .menu_skip_mask = 0, .qmenu = mt9p031_test_pattern_menu, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "BLC, Auto", + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_TARGET_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Target Level", + .min = 0, + .max = 4095, + .step = 1, + .def = 168, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_ANALOG_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Analog Offset", + .min = -255, + .max = 255, + .step = 1, + .def = 32, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_DIGITAL_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Digital Offset", + .min = -2048, + .max = 2047, + .step = 1, + .def = 40, + .flags = 0, } }; @@ -872,9 +969,16 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; - if (mt9p031->ctrls.error) + if (mt9p031->ctrls.error) { printk(KERN_INFO "%s: control initialization error %d\n", __func__, mt9p031->ctrls.error); + ret = mt9p031->ctrls.error; + goto done; + } + + mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); + mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, + V4L2_CID_BLC_DIGITAL_OFFSET); mutex_init(&mt9p031->power_lock); v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); -- cgit v1.2.3 From b1e1179cea0e50ae3fead8c6bd064a985dae8f8b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Sun, 8 Apr 2012 17:31:24 -0300 Subject: [media] mt9m032: fix two dead-locks Fix a copy-paste typo and a nested locking function call in mt9m032. Signed-off-by: Guennadi Liakhovetski Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9m032.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c index 7636672c3548..645973c5feb0 100644 --- a/drivers/media/video/mt9m032.c +++ b/drivers/media/video/mt9m032.c @@ -392,10 +392,11 @@ static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, } /* Scaling is not supported, the format is thus fixed. */ - ret = mt9m032_get_pad_format(subdev, fh, fmt); + fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + ret = 0; done: - mutex_lock(&sensor->lock); + mutex_unlock(&sensor->lock); return ret; } -- cgit v1.2.3 From 5bfa474b54622f646f03e19c3c9192a95ca65ae2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 18 Apr 2012 04:59:01 -0300 Subject: [media] mt9m032: fix compilation breakage Fix the following compilation failure: linux-2.6/drivers/media/video/mt9m032.c: In function '__mt9m032_get_pad_crop': linux-2.6/drivers/media/video/mt9m032.c:337: error: implicit declaration of function 'v4l2_subdev_get_try_crop' linux-2.6/drivers/media/video/mt9m032.c:337: warning: return makes pointer from integer without a cast linux-2.6/drivers/media/video/mt9m032.c: In function '__mt9m032_get_pad_format': linux-2.6/drivers/media/video/mt9m032.c:359: error: implicit declaration of function 'v4l2_subdev_get_try_format' linux-2.6/drivers/media/video/mt9m032.c:359: warning: return makes pointer from integer without a cast linux-2.6/drivers/media/video/mt9m032.c: In function 'mt9m032_probe': linux-2.6/drivers/media/video/mt9m032.c:767: error: 'struct v4l2_subdev' has no member named 'entity' linux-2.6/drivers/media/video/mt9m032.c:826: error: 'struct v4l2_subdev' has no member named 'entity' linux-2.6/drivers/media/video/mt9m032.c: In function 'mt9m032_remove': linux-2.6/drivers/media/video/mt9m032.c:842: error: 'struct v4l2_subdev' has no member named 'entity' make[4]: *** [drivers/media/video/mt9m032.o] Error 1 by adding a dependency on VIDEO_V4L2_SUBDEV_API. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9fc7c5224ac8..3dc0ea7ba927 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -495,7 +495,7 @@ config VIDEO_VS6624 config VIDEO_MT9M032 tristate "MT9M032 camera sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select VIDEO_APTINA_PLL ---help--- This driver supports MT9M032 camera sensors from Aptina, monochrome -- cgit v1.2.3 From b22b9f3200fcde1817e5ccad9b8aaf794fb46119 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 18 Apr 2012 05:00:52 -0300 Subject: [media] mt9m032: use the available subdev pointer, don't re-calculate it Signed-off-by: Guennadi Liakhovetski Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9m032.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c index 645973c5feb0..3c1e626139b7 100644 --- a/drivers/media/video/mt9m032.c +++ b/drivers/media/video/mt9m032.c @@ -838,9 +838,9 @@ static int mt9m032_remove(struct i2c_client *client) struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct mt9m032 *sensor = to_mt9m032(subdev); - v4l2_device_unregister_subdev(&sensor->subdev); + v4l2_device_unregister_subdev(subdev); v4l2_ctrl_handler_free(&sensor->ctrls); - media_entity_cleanup(&sensor->subdev.entity); + media_entity_cleanup(&subdev->entity); mutex_destroy(&sensor->lock); kfree(sensor); return 0; -- cgit v1.2.3 From 5c37598142621ca8aadfd115d8e5f51c5337f8d5 Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Wed, 2 May 2012 19:19:08 -0300 Subject: [media] mt9v032: Correct the logic for the auto-exposure setting The driver uses the ctrl value passed in as a bool to determine whether to enable auto-exposure, but the auto-exposure setting is defined as an enum where AUTO has a value of 0 and MANUAL has a value of 1. This leads to a reversed logic where if you send in AUTO, it actually sets manual exposure and vice-versa. Signed-off-by: Kartik Mohta Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9v032.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c index 75e253a343c5..4ba4884c016e 100644 --- a/drivers/media/video/mt9v032.c +++ b/drivers/media/video/mt9v032.c @@ -481,7 +481,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE_AUTO: return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, - ctrl->val); + !ctrl->val); case V4L2_CID_EXPOSURE: return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, -- cgit v1.2.3 From 528f0f785c042c80294708c5ae2c8005b4a0ee60 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Apr 2012 08:20:35 -0300 Subject: [media] v4l: v4l2-ctrls: moves the forward declaration of struct file This fixes the following warning: In file included from drivers/media/video/v4l2-subdev.c:29: include/media/v4l2-ctrls.h:501: warning: 'struct file' declared inside parameter list include/media/v4l2-ctrls.h:501: warning: its scope is only this definition or declaration, which is probably not what you want include/media/v4l2-ctrls.h:509: warning: 'struct file' declared inside parameter list Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-ctrls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index dde6fbacc271..5edd64daa425 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -25,6 +25,7 @@ #include /* forward references */ +struct file; struct v4l2_ctrl_handler; struct v4l2_ctrl_helper; struct v4l2_ctrl; @@ -498,7 +499,6 @@ extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops; void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new); void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new); -struct file; /* Can be used as a vidioc_log_status function that just dumps all controls associated with the filehandle. */ int v4l2_ctrl_log_status(struct file *file, void *fh); -- cgit v1.2.3 From 4967d53dbbcebf590c5ae07c0aea38619ac51954 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 4 May 2012 09:16:57 -0300 Subject: [media] si470x: Clean up, introduce the control framework This cleans up the code and si470x now uses the proper v4l2 frameworks and passes most of the v4l2-compliance tests. Signed-off-by: Hans Verkuil Acked-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-common.c | 193 +++-------------------- drivers/media/radio/si470x/radio-si470x-i2c.c | 65 ++------ drivers/media/radio/si470x/radio-si470x-usb.c | 146 +++++++---------- drivers/media/radio/si470x/radio-si470x.h | 14 +- 4 files changed, 105 insertions(+), 313 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 0e740c98786c..de9475f4139e 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -196,9 +196,9 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) } if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - dev_warn(&radio->videodev->dev, "tune does not complete\n"); + dev_warn(&radio->videodev.dev, "tune does not complete\n"); if (timed_out) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "tune timed out after %u ms\n", tune_timeout); stop: @@ -344,12 +344,12 @@ static int si470x_set_seek(struct si470x_device *radio, } if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - dev_warn(&radio->videodev->dev, "seek does not complete\n"); + dev_warn(&radio->videodev.dev, "seek does not complete\n"); if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "seek failed / band limit reached\n"); if (timed_out) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "seek timed out after %u ms\n", seek_timeout); stop: @@ -463,7 +463,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, unsigned int block_count = 0; /* switch on rds reception */ - mutex_lock(&radio->lock); if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) si470x_rds_on(radio); @@ -505,7 +504,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, } done: - mutex_unlock(&radio->lock); return retval; } @@ -521,10 +519,8 @@ static unsigned int si470x_fops_poll(struct file *file, /* switch on rds reception */ - mutex_lock(&radio->lock); if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) si470x_rds_on(radio); - mutex_unlock(&radio->lock); poll_wait(file, &radio->read_queue, pts); @@ -553,134 +549,27 @@ static const struct v4l2_file_operations si470x_fops = { * Video4Linux Interface **************************************************************************/ -/* - * si470x_vidioc_queryctrl - enumerate control items - */ -static int si470x_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = -EINVAL; - - /* abort if qc->id is below V4L2_CID_BASE */ - if (qc->id < V4L2_CID_BASE) - goto done; - - /* search video control */ - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15); - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - /* disable unsupported base controls */ - /* to satisfy kradio and such apps */ - if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) { - qc->flags = V4L2_CTRL_FLAG_DISABLED; - retval = 0; - } - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "query controls failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_g_ctrl - get the value of a control - */ -static int si470x_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int si470x_s_ctrl(struct v4l2_ctrl *ctrl) { - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = radio->registers[SYSCONFIG2] & - SYSCONFIG2_VOLUME; - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = ((radio->registers[POWERCFG] & - POWERCFG_DMUTE) == 0) ? 1 : 0; - break; - default: - retval = -EINVAL; - } - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "get control failed with %d\n", retval); - - mutex_unlock(&radio->lock); - return retval; -} - - -/* - * si470x_vidioc_s_ctrl - set the value of a control - */ -static int si470x_vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; + struct si470x_device *radio = + container_of(ctrl->handler, struct si470x_device, hdl); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; - radio->registers[SYSCONFIG2] |= ctrl->value; - retval = si470x_set_register(radio, SYSCONFIG2); - break; + radio->registers[SYSCONFIG2] |= ctrl->val; + return si470x_set_register(radio, SYSCONFIG2); case V4L2_CID_AUDIO_MUTE: - if (ctrl->value == 1) + if (ctrl->val) radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; else radio->registers[POWERCFG] |= POWERCFG_DMUTE; - retval = si470x_set_register(radio, POWERCFG); + return si470x_set_register(radio, POWERCFG); break; default: - retval = -EINVAL; + return -EINVAL; } - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "set control failed with %d\n", retval); - mutex_unlock(&radio->lock); - return retval; -} - - -/* - * si470x_vidioc_g_audio - get audio attributes - */ -static int si470x_vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - /* driver constants */ - audio->index = 0; - strcpy(audio->name, "Radio"); - audio->capability = V4L2_AUDCAP_STEREO; - audio->mode = 0; - - return 0; } @@ -693,12 +582,6 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct si470x_device *radio = video_drvdata(file); int retval = 0; - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; - if (tuner->index != 0) { retval = -EINVAL; goto done; @@ -737,7 +620,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) tuner->rxsubchans = V4L2_TUNER_SUB_MONO; else - tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + tuner->rxsubchans = V4L2_TUNER_SUB_STEREO; /* If there is a reliable method of detecting an RDS channel, then this code should check for that before setting this RDS subchannel. */ @@ -761,9 +644,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, done: if (retval < 0) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "get tuner failed with %d\n", retval); - mutex_unlock(&radio->lock); return retval; } @@ -777,12 +659,6 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct si470x_device *radio = video_drvdata(file); int retval = 0; - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; - if (tuner->index != 0) goto done; @@ -802,9 +678,8 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, done: if (retval < 0) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "set tuner failed with %d\n", retval); - mutex_unlock(&radio->lock); return retval; } @@ -818,12 +693,6 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, struct si470x_device *radio = video_drvdata(file); int retval = 0; - /* safety checks */ - mutex_lock(&radio->lock); - retval = si470x_disconnect_check(radio); - if (retval) - goto done; - if (freq->tuner != 0) { retval = -EINVAL; goto done; @@ -834,9 +703,8 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, done: if (retval < 0) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "get frequency failed with %d\n", retval); - mutex_unlock(&radio->lock); return retval; } @@ -850,12 +718,6 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, struct si470x_device *radio = video_drvdata(file); int retval = 0; - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; - if (freq->tuner != 0) { retval = -EINVAL; goto done; @@ -865,9 +727,8 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, done: if (retval < 0) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "set frequency failed with %d\n", retval); - mutex_unlock(&radio->lock); return retval; } @@ -881,12 +742,6 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, struct si470x_device *radio = video_drvdata(file); int retval = 0; - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; - if (seek->tuner != 0) { retval = -EINVAL; goto done; @@ -896,22 +751,20 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, done: if (retval < 0) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "set hardware frequency seek failed with %d\n", retval); - mutex_unlock(&radio->lock); return retval; } +const struct v4l2_ctrl_ops si470x_ctrl_ops = { + .s_ctrl = si470x_s_ctrl, +}; /* * si470x_ioctl_ops - video device ioctl operations */ static const struct v4l2_ioctl_ops si470x_ioctl_ops = { .vidioc_querycap = si470x_vidioc_querycap, - .vidioc_queryctrl = si470x_vidioc_queryctrl, - .vidioc_g_ctrl = si470x_vidioc_g_ctrl, - .vidioc_s_ctrl = si470x_vidioc_s_ctrl, - .vidioc_g_audio = si470x_vidioc_g_audio, .vidioc_g_tuner = si470x_vidioc_g_tuner, .vidioc_s_tuner = si470x_vidioc_s_tuner, .vidioc_g_frequency = si470x_vidioc_g_frequency, @@ -926,6 +779,6 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = { struct video_device si470x_viddev_template = { .fops = &si470x_fops, .name = DRIVER_NAME, - .release = video_device_release, + .release = video_device_release_empty, .ioctl_ops = &si470x_ioctl_ops, }; diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 9b546a5523f3..a80044c5874e 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -161,20 +161,6 @@ static int si470x_get_all_registers(struct si470x_device *radio) -/************************************************************************** - * General Driver Functions - DISCONNECT_CHECK - **************************************************************************/ - -/* - * si470x_disconnect_check - check whether radio disconnects - */ -int si470x_disconnect_check(struct si470x_device *radio) -{ - return 0; -} - - - /************************************************************************** * File Operations Interface **************************************************************************/ @@ -185,12 +171,12 @@ int si470x_disconnect_check(struct si470x_device *radio) int si470x_fops_open(struct file *file) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; + int retval = v4l2_fh_open(file); - mutex_lock(&radio->lock); - radio->users++; + if (retval) + return retval; - if (radio->users == 1) { + if (v4l2_fh_is_singular_file(file)) { /* start radio */ retval = si470x_start(radio); if (retval < 0) @@ -205,7 +191,8 @@ int si470x_fops_open(struct file *file) } done: - mutex_unlock(&radio->lock); + if (retval) + v4l2_fh_release(file); return retval; } @@ -216,21 +203,12 @@ done: int si470x_fops_release(struct file *file) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety check */ - if (!radio) - return -ENODEV; - mutex_lock(&radio->lock); - radio->users--; - if (radio->users == 0) + if (v4l2_fh_is_singular_file(file)) /* stop radio */ - retval = si470x_stop(radio); + si470x_stop(radio); - mutex_unlock(&radio->lock); - - return retval; + return v4l2_fh_release(file); } @@ -371,32 +349,25 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, goto err_initial; } - radio->users = 0; radio->client = client; mutex_init(&radio->lock); - /* video device allocation and initialization */ - radio->videodev = video_device_alloc(); - if (!radio->videodev) { - retval = -ENOMEM; - goto err_radio; - } - memcpy(radio->videodev, &si470x_viddev_template, - sizeof(si470x_viddev_template)); - video_set_drvdata(radio->videodev, radio); + /* video device initialization */ + radio->videodev = si470x_viddev_template; + video_set_drvdata(&radio->videodev, radio); /* power up : need 110ms */ radio->registers[POWERCFG] = POWERCFG_ENABLE; if (si470x_set_register(radio, POWERCFG) < 0) { retval = -EIO; - goto err_video; + goto err_radio; } msleep(110); /* get device and chip versions */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; - goto err_video; + goto err_radio; } dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); @@ -427,7 +398,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); if (!radio->buffer) { retval = -EIO; - goto err_video; + goto err_radio; } /* rds buffer configuration */ @@ -447,7 +418,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, } /* register video device */ - retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, + retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); if (retval) { dev_warn(&client->dev, "Could not register video device\n"); @@ -460,8 +431,6 @@ err_all: free_irq(client->irq, radio); err_rds: kfree(radio->buffer); -err_video: - video_device_release(radio->videodev); err_radio: kfree(radio); err_initial: @@ -477,7 +446,7 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client) struct si470x_device *radio = i2c_get_clientdata(client); free_irq(client->irq, radio); - video_unregister_device(radio->videodev); + video_unregister_device(&radio->videodev); kfree(radio); return 0; diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index b7debb67932a..f133c3dea648 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -366,23 +366,6 @@ static int si470x_get_scratch_page_versions(struct si470x_device *radio) -/************************************************************************** - * General Driver Functions - DISCONNECT_CHECK - **************************************************************************/ - -/* - * si470x_disconnect_check - check whether radio disconnects - */ -int si470x_disconnect_check(struct si470x_device *radio) -{ - if (radio->disconnected) - return -EIO; - else - return 0; -} - - - /************************************************************************** * RDS Driver Functions **************************************************************************/ @@ -414,9 +397,6 @@ static void si470x_int_in_callback(struct urb *urb) } } - /* safety checks */ - if (radio->disconnected) - return; if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) goto resubmit; @@ -512,19 +492,16 @@ resubmit: int si470x_fops_open(struct file *file) { struct si470x_device *radio = video_drvdata(file); - int retval; + int retval = v4l2_fh_open(file); - mutex_lock(&radio->lock); - radio->users++; + if (retval) + return retval; retval = usb_autopm_get_interface(radio->intf); - if (retval < 0) { - radio->users--; - retval = -EIO; + if (retval < 0) goto done; - } - if (radio->users == 1) { + if (v4l2_fh_is_singular_file(file)) { /* start radio */ retval = si470x_start(radio); if (retval < 0) { @@ -555,7 +532,8 @@ int si470x_fops_open(struct file *file) } done: - mutex_unlock(&radio->lock); + if (retval) + v4l2_fh_release(file); return retval; } @@ -566,45 +544,36 @@ done: int si470x_fops_release(struct file *file) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - /* safety check */ - if (!radio) { - retval = -ENODEV; - goto done; - } - - mutex_lock(&radio->lock); - radio->users--; - if (radio->users == 0) { + if (v4l2_fh_is_singular_file(file)) { /* shutdown interrupt handler */ if (radio->int_in_running) { radio->int_in_running = 0; - if (radio->int_in_urb) - usb_kill_urb(radio->int_in_urb); - } - - if (radio->disconnected) { - video_unregister_device(radio->videodev); - kfree(radio->int_in_buffer); - kfree(radio->buffer); - mutex_unlock(&radio->lock); - kfree(radio); - goto done; + if (radio->int_in_urb) + usb_kill_urb(radio->int_in_urb); } /* cancel read processes */ wake_up_interruptible(&radio->read_queue); /* stop radio */ - retval = si470x_stop(radio); + si470x_stop(radio); usb_autopm_put_interface(radio->intf); } - mutex_unlock(&radio->lock); -done: - return retval; + return v4l2_fh_release(file); } +static void si470x_usb_release(struct video_device *vdev) +{ + struct si470x_device *radio = video_get_drvdata(vdev); + + usb_free_urb(radio->int_in_urb); + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(&radio->v4l2_dev); + kfree(radio->int_in_buffer); + kfree(radio->buffer); + kfree(radio); +} /************************************************************************** @@ -623,9 +592,9 @@ int si470x_vidioc_querycap(struct file *file, void *priv, strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); usb_make_path(radio->usbdev, capability->bus_info, sizeof(capability->bus_info)); - capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | + capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; - + capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -653,8 +622,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, retval = -ENOMEM; goto err_initial; } - radio->users = 0; - radio->disconnected = 0; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; mutex_init(&radio->lock); @@ -691,20 +658,34 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, goto err_intbuffer; } - /* video device allocation and initialization */ - radio->videodev = video_device_alloc(); - if (!radio->videodev) { - retval = -ENOMEM; + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register v4l2_device\n"); goto err_urb; } - memcpy(radio->videodev, &si470x_viddev_template, - sizeof(si470x_viddev_template)); - video_set_drvdata(radio->videodev, radio); + + v4l2_ctrl_handler_init(&radio->hdl, 2); + v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 15); + if (radio->hdl.error) { + retval = radio->hdl.error; + dev_err(&intf->dev, "couldn't register control\n"); + goto err_dev; + } + radio->videodev = si470x_viddev_template; + radio->videodev.ctrl_handler = &radio->hdl; + radio->videodev.lock = &radio->lock; + radio->videodev.v4l2_dev = &radio->v4l2_dev; + radio->videodev.release = si470x_usb_release; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); + video_set_drvdata(&radio->videodev, radio); /* get device and chip versions */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; - goto err_video; + goto err_ctrl; } dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); @@ -721,7 +702,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* get software and hardware versions */ if (si470x_get_scratch_page_versions(radio) < 0) { retval = -EIO; - goto err_video; + goto err_ctrl; } dev_info(&intf->dev, "software version %d, hardware version %d\n", radio->software_version, radio->hardware_version); @@ -764,28 +745,30 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); if (!radio->buffer) { retval = -EIO; - goto err_video; + goto err_ctrl; } /* rds buffer configuration */ radio->wr_index = 0; radio->rd_index = 0; init_waitqueue_head(&radio->read_queue); + usb_set_intfdata(intf, radio); /* register video device */ - retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, + retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); if (retval) { dev_warn(&intf->dev, "Could not register video device\n"); goto err_all; } - usb_set_intfdata(intf, radio); return 0; err_all: kfree(radio->buffer); -err_video: - video_device_release(radio->videodev); +err_ctrl: + v4l2_ctrl_handler_free(&radio->hdl); +err_dev: + v4l2_device_unregister(&radio->v4l2_dev); err_urb: usb_free_urb(radio->int_in_urb); err_intbuffer: @@ -828,23 +811,10 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) struct si470x_device *radio = usb_get_intfdata(intf); mutex_lock(&radio->lock); - radio->disconnected = 1; + v4l2_device_disconnect(&radio->v4l2_dev); + video_unregister_device(&radio->videodev); usb_set_intfdata(intf, NULL); - if (radio->users == 0) { - /* set led to disconnect state */ - si470x_set_led_state(radio, BLINK_ORANGE_LED); - - /* Free data structures. */ - usb_free_urb(radio->int_in_urb); - - kfree(radio->int_in_buffer); - video_unregister_device(radio->videodev); - kfree(radio->buffer); - mutex_unlock(&radio->lock); - kfree(radio); - } else { - mutex_unlock(&radio->lock); - } + mutex_unlock(&radio->lock); } diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h index f300a55ed85c..4921cab8e0fa 100644 --- a/drivers/media/radio/si470x/radio-si470x.h +++ b/drivers/media/radio/si470x/radio-si470x.h @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #include @@ -141,10 +144,9 @@ * si470x_device - private data */ struct si470x_device { - struct video_device *videodev; - - /* driver management */ - unsigned int users; + struct v4l2_device v4l2_dev; + struct video_device videodev; + struct v4l2_ctrl_handler hdl; /* Silabs internal registers (0..15) */ unsigned short registers[RADIO_REGISTER_NUM]; @@ -174,9 +176,6 @@ struct si470x_device { /* scratch page */ unsigned char software_version; unsigned char hardware_version; - - /* driver management */ - unsigned char disconnected; #endif #if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE) @@ -213,6 +212,7 @@ struct si470x_device { * Common Functions **************************************************************************/ extern struct video_device si470x_viddev_template; +extern const struct v4l2_ctrl_ops si470x_ctrl_ops; int si470x_get_register(struct si470x_device *radio, int regnr); int si470x_set_register(struct si470x_device *radio, int regnr); int si470x_disconnect_check(struct si470x_device *radio); -- cgit v1.2.3 From eae63ae007fd72d5040d6749339d68fbfd99d8c7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 4 May 2012 09:20:53 -0300 Subject: [media] si470x: add control event support and more v4l2 compliancy fixes Signed-off-by: Hans Verkuil Acked-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-common.c | 45 +++++++++++++++--------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index de9475f4139e..e70badf7c6e9 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -262,7 +262,7 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) */ int si470x_set_freq(struct si470x_device *radio, unsigned int freq) { - unsigned int spacing, band_bottom; + unsigned int spacing, band_bottom, band_top; unsigned short chan; /* Spacing (kHz) */ @@ -278,19 +278,26 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq) spacing = 0.050 * FREQ_MUL; break; }; - /* Bottom of Band (MHz) */ + /* Bottom/Top of Band (MHz) */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { /* 0: 87.5 - 108 MHz (USA, Europe) */ case 0: - band_bottom = 87.5 * FREQ_MUL; break; + band_bottom = 87.5 * FREQ_MUL; + band_top = 108 * FREQ_MUL; + break; /* 1: 76 - 108 MHz (Japan wide band) */ default: - band_bottom = 76 * FREQ_MUL; break; + band_bottom = 76 * FREQ_MUL; + band_top = 108 * FREQ_MUL; + break; /* 2: 76 - 90 MHz (Japan) */ case 2: - band_bottom = 76 * FREQ_MUL; break; + band_bottom = 76 * FREQ_MUL; + band_top = 90 * FREQ_MUL; + break; }; + freq = clamp(freq, band_bottom, band_top); /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ chan = (freq - band_bottom) / spacing; @@ -515,17 +522,19 @@ static unsigned int si470x_fops_poll(struct file *file, struct poll_table_struct *pts) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* switch on rds reception */ + unsigned long req_events = poll_requested_events(pts); + int retval = v4l2_ctrl_poll(file, pts); - if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) - si470x_rds_on(radio); + if (req_events & (POLLIN | POLLRDNORM)) { + /* switch on rds reception */ + if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) + si470x_rds_on(radio); - poll_wait(file, &radio->read_queue, pts); + poll_wait(file, &radio->read_queue, pts); - if (radio->rd_index != radio->wr_index) - retval = POLLIN | POLLRDNORM; + if (radio->rd_index != radio->wr_index) + retval |= POLLIN | POLLRDNORM; + } return retval; } @@ -637,6 +646,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); /* the ideal factor is 0xffff/75 = 873,8 */ tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); + if (tuner->signal > 0xffff) + tuner->signal = 0xffff; /* automatic frequency control: -1: freq to low, 1 freq to high */ /* AFCRL does only indicate that freq. differs, not if too low/high */ @@ -660,7 +671,7 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, int retval = 0; if (tuner->index != 0) - goto done; + return -EINVAL; /* mono/stereo selector */ switch (tuner->audmode) { @@ -668,15 +679,13 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ break; case V4L2_TUNER_MODE_STEREO: + default: radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ break; - default: - goto done; } retval = si470x_set_register(radio, POWERCFG); -done: if (retval < 0) dev_warn(&radio->videodev.dev, "set tuner failed with %d\n", retval); @@ -770,6 +779,8 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = { .vidioc_g_frequency = si470x_vidioc_g_frequency, .vidioc_s_frequency = si470x_vidioc_s_frequency, .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -- cgit v1.2.3 From 340bd4c16cead8bf8f5205c7d283a3bd9d699f76 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 30 Apr 2012 19:49:27 -0300 Subject: [media] radio-si470x-common.c: remove unnecessary kernel log spam There is no need to report an error in the log, you are already returning that error to userspace after all. Signed-off-by: Hans Verkuil Acked-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-common.c | 78 ++++++------------------ 1 file changed, 17 insertions(+), 61 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index e70badf7c6e9..b9a44d4a032f 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -327,7 +327,7 @@ static int si470x_set_seek(struct si470x_device *radio, radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; retval = si470x_set_register(radio, POWERCFG); if (retval < 0) - goto done; + return retval; /* currently I2C driver only uses interrupt way to seek */ if (radio->stci_enabled) { @@ -355,20 +355,15 @@ static int si470x_set_seek(struct si470x_device *radio, if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) dev_warn(&radio->videodev.dev, "seek failed / band limit reached\n"); - if (timed_out) - dev_warn(&radio->videodev.dev, - "seek timed out after %u ms\n", seek_timeout); stop: /* stop seeking */ radio->registers[POWERCFG] &= ~POWERCFG_SEEK; retval = si470x_set_register(radio, POWERCFG); -done: /* try again, if timed out */ - if ((retval == 0) && timed_out) - retval = -EAGAIN; - + if (retval == 0 && timed_out) + return -EAGAIN; return retval; } @@ -589,16 +584,14 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; + int retval; - if (tuner->index != 0) { - retval = -EINVAL; - goto done; - } + if (tuner->index != 0) + return -EINVAL; retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) - goto done; + return retval; /* driver constants */ strcpy(tuner->name, "FM"); @@ -653,10 +646,6 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, /* AFCRL does only indicate that freq. differs, not if too low/high */ tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; -done: - if (retval < 0) - dev_warn(&radio->videodev.dev, - "get tuner failed with %d\n", retval); return retval; } @@ -668,7 +657,6 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; if (tuner->index != 0) return -EINVAL; @@ -684,12 +672,7 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, break; } - retval = si470x_set_register(radio, POWERCFG); - - if (retval < 0) - dev_warn(&radio->videodev.dev, - "set tuner failed with %d\n", retval); - return retval; + return si470x_set_register(radio, POWERCFG); } @@ -700,21 +683,12 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - if (freq->tuner != 0) { - retval = -EINVAL; - goto done; - } + if (freq->tuner != 0) + return -EINVAL; freq->type = V4L2_TUNER_RADIO; - retval = si470x_get_freq(radio, &freq->frequency); - -done: - if (retval < 0) - dev_warn(&radio->videodev.dev, - "get frequency failed with %d\n", retval); - return retval; + return si470x_get_freq(radio, &freq->frequency); } @@ -725,20 +699,11 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - if (freq->tuner != 0) { - retval = -EINVAL; - goto done; - } - retval = si470x_set_freq(radio, freq->frequency); + if (freq->tuner != 0) + return -EINVAL; -done: - if (retval < 0) - dev_warn(&radio->videodev.dev, - "set frequency failed with %d\n", retval); - return retval; + return si470x_set_freq(radio, freq->frequency); } @@ -749,20 +714,11 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, struct v4l2_hw_freq_seek *seek) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - if (seek->tuner != 0) { - retval = -EINVAL; - goto done; - } - retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); + if (seek->tuner != 0) + return -EINVAL; -done: - if (retval < 0) - dev_warn(&radio->videodev.dev, - "set hardware frequency seek failed with %d\n", retval); - return retval; + return si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); } const struct v4l2_ctrl_ops si470x_ctrl_ops = { -- cgit v1.2.3 From 6fd522a6d12d0ebac528f2d4553c4bf51c0c3275 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 4 May 2012 09:42:29 -0300 Subject: [media] radio-si470x-usb: remove autosuspend, implement suspend/resume The radio-si470x-usb driver supported both autosuspend and it stopped the radio the moment the last user of the radio device closed it. However, that was very confusing since if you play the audio from the device (e.g. through arecord -D ... | aplay) then no sound would play unless you had the radio device open at the same time, even though there is no need to do anything with that node. On the other hand, the actual suspend/resume functions didn't do anything, which would fail if you *did* have the radio node open at that time. So: - remove autosuspend (bad idea in general for USB radio devices) - move the start/stop out of the open/release functions into the resume/suspend functions. Signed-off-by: Hans Verkuil Acked-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-common.c | 1 - drivers/media/radio/si470x/radio-si470x-usb.c | 149 +++++++++++------------ 2 files changed, 70 insertions(+), 80 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index b9a44d4a032f..969cf494d85b 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -570,7 +570,6 @@ static int si470x_s_ctrl(struct v4l2_ctrl *ctrl) else radio->registers[POWERCFG] |= POWERCFG_DMUTE; return si470x_set_register(radio, POWERCFG); - break; default: return -EINVAL; } diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index f133c3dea648..e9f638761296 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -481,91 +481,20 @@ resubmit: } - -/************************************************************************** - * File Operations Interface - **************************************************************************/ - -/* - * si470x_fops_open - file open - */ int si470x_fops_open(struct file *file) { - struct si470x_device *radio = video_drvdata(file); - int retval = v4l2_fh_open(file); - - if (retval) - return retval; - - retval = usb_autopm_get_interface(radio->intf); - if (retval < 0) - goto done; - - if (v4l2_fh_is_singular_file(file)) { - /* start radio */ - retval = si470x_start(radio); - if (retval < 0) { - usb_autopm_put_interface(radio->intf); - goto done; - } - - /* initialize interrupt urb */ - usb_fill_int_urb(radio->int_in_urb, radio->usbdev, - usb_rcvintpipe(radio->usbdev, - radio->int_in_endpoint->bEndpointAddress), - radio->int_in_buffer, - le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), - si470x_int_in_callback, - radio, - radio->int_in_endpoint->bInterval); - - radio->int_in_running = 1; - mb(); - - retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); - if (retval) { - dev_info(&radio->intf->dev, - "submitting int urb failed (%d)\n", retval); - radio->int_in_running = 0; - usb_autopm_put_interface(radio->intf); - } - } - -done: - if (retval) - v4l2_fh_release(file); - return retval; + return v4l2_fh_open(file); } - -/* - * si470x_fops_release - file release - */ int si470x_fops_release(struct file *file) { - struct si470x_device *radio = video_drvdata(file); - - if (v4l2_fh_is_singular_file(file)) { - /* shutdown interrupt handler */ - if (radio->int_in_running) { - radio->int_in_running = 0; - if (radio->int_in_urb) - usb_kill_urb(radio->int_in_urb); - } - - /* cancel read processes */ - wake_up_interruptible(&radio->read_queue); - - /* stop radio */ - si470x_stop(radio); - usb_autopm_put_interface(radio->intf); - } return v4l2_fh_release(file); } -static void si470x_usb_release(struct video_device *vdev) +static void si470x_usb_release(struct v4l2_device *v4l2_dev) { - struct si470x_device *radio = video_get_drvdata(vdev); + struct si470x_device *radio = + container_of(v4l2_dev, struct si470x_device, v4l2_dev); usb_free_urb(radio->int_in_urb); v4l2_ctrl_handler_free(&radio->hdl); @@ -599,6 +528,38 @@ int si470x_vidioc_querycap(struct file *file, void *priv, } +static int si470x_start_usb(struct si470x_device *radio) +{ + int retval; + + /* start radio */ + retval = si470x_start(radio); + if (retval < 0) + return retval; + + v4l2_ctrl_handler_setup(&radio->hdl); + + /* initialize interrupt urb */ + usb_fill_int_urb(radio->int_in_urb, radio->usbdev, + usb_rcvintpipe(radio->usbdev, + radio->int_in_endpoint->bEndpointAddress), + radio->int_in_buffer, + le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), + si470x_int_in_callback, + radio, + radio->int_in_endpoint->bInterval); + + radio->int_in_running = 1; + mb(); + + retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); + if (retval) { + dev_info(&radio->intf->dev, + "submitting int urb failed (%d)\n", retval); + radio->int_in_running = 0; + } + return retval; +} /************************************************************************** * USB Interface @@ -658,6 +619,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, goto err_intbuffer; } + radio->v4l2_dev.release = si470x_usb_release; retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); if (retval < 0) { dev_err(&intf->dev, "couldn't register v4l2_device\n"); @@ -678,7 +640,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, radio->videodev.ctrl_handler = &radio->hdl; radio->videodev.lock = &radio->lock; radio->videodev.v4l2_dev = &radio->v4l2_dev; - radio->videodev.release = si470x_usb_release; + radio->videodev.release = video_device_release_empty; set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); video_set_drvdata(&radio->videodev, radio); @@ -754,11 +716,16 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, init_waitqueue_head(&radio->read_queue); usb_set_intfdata(intf, radio); + /* start radio */ + retval = si470x_start_usb(radio); + if (retval < 0) + goto err_all; + /* register video device */ retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); if (retval) { - dev_warn(&intf->dev, "Could not register video device\n"); + dev_err(&intf->dev, "Could not register video device\n"); goto err_all; } @@ -786,8 +753,22 @@ err_initial: static int si470x_usb_driver_suspend(struct usb_interface *intf, pm_message_t message) { + struct si470x_device *radio = usb_get_intfdata(intf); + dev_info(&intf->dev, "suspending now...\n"); + /* shutdown interrupt handler */ + if (radio->int_in_running) { + radio->int_in_running = 0; + if (radio->int_in_urb) + usb_kill_urb(radio->int_in_urb); + } + + /* cancel read processes */ + wake_up_interruptible(&radio->read_queue); + + /* stop radio */ + si470x_stop(radio); return 0; } @@ -797,9 +778,12 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf, */ static int si470x_usb_driver_resume(struct usb_interface *intf) { + struct si470x_device *radio = usb_get_intfdata(intf); + dev_info(&intf->dev, "resuming now...\n"); - return 0; + /* start radio */ + return si470x_start_usb(radio); } @@ -815,11 +799,18 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) video_unregister_device(&radio->videodev); usb_set_intfdata(intf, NULL); mutex_unlock(&radio->lock); + v4l2_device_put(&radio->v4l2_dev); } /* * si470x_usb_driver - usb driver interface + * + * A note on suspend/resume: this driver had only empty suspend/resume + * functions, and when I tried to test suspend/resume it always disconnected + * instead of resuming (using my ADS InstantFM stick). So I've decided to + * remove these callbacks until someone else with better hardware can + * implement and test this. */ static struct usb_driver si470x_usb_driver = { .name = DRIVER_NAME, @@ -827,8 +818,8 @@ static struct usb_driver si470x_usb_driver = { .disconnect = si470x_usb_driver_disconnect, .suspend = si470x_usb_driver_suspend, .resume = si470x_usb_driver_resume, + .reset_resume = si470x_usb_driver_resume, .id_table = si470x_usb_driver_id_table, - .supports_autosuspend = 1, }; module_usb_driver(si470x_usb_driver); -- cgit v1.2.3 From 6491d1adfbf0e2ffbdfcda8cef60edc01b6700b3 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 2 Apr 2012 06:40:19 -0300 Subject: [media] V4L: Extend V4L2_CID_COLORFX with more image effects This patch adds definition of additional color effects: - V4L2_COLORFX_AQUA, - V4L2_COLORFX_ART_FREEZE, - V4L2_COLORFX_SILHOUETTE, - V4L2_COLORFX_SOLARIZATION, - V4L2_COLORFX_ANTIQUE, - V4L2_COLORFX_SET_CBCR. The new V4L2_COLORFX_CBCR control is added to allow setting the fixed Cb, Cr values that replace chroma Cb/Cr coefficients in case of V4L2_COLORFX_SET_CBCR effect. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 13 ++++ Documentation/DocBook/media/v4l/controls.xml | 98 ++++++++++++++++++++++++---- Documentation/DocBook/media/v4l/v4l2.xml | 5 +- drivers/media/video/v4l2-ctrls.c | 7 ++ include/linux/videodev2.h | 29 ++++---- 5 files changed, 127 insertions(+), 25 deletions(-) diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 87339b2aad78..149f65dfaa72 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2422,6 +2422,19 @@ details. &VIDIOC-SUBDEV-G-SELECTION; and &VIDIOC-SUBDEV-S-SELECTION;. + + Added V4L2_COLORFX_ANTIQUE, + V4L2_COLORFX_ART_FREEZE, + V4L2_COLORFX_AQUA, + V4L2_COLORFX_SILHOUETTE, + V4L2_COLORFX_SOLARIZATION, + V4L2_COLORFX_VIVID and + V4L2_COLORFX_ARBITRARY_CBCR menu items + to the V4L2_CID_COLORFX control. + + + Added V4L2_CID_COLORFX_CBCR control. + diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 662127447aa6..e2ff0f98f08d 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -285,18 +285,92 @@ minimum value disables backlight compensation. V4L2_CID_COLORFX enum - Selects a color effect. Possible values for -enum v4l2_colorfx are: -V4L2_COLORFX_NONE (0), -V4L2_COLORFX_BW (1), -V4L2_COLORFX_SEPIA (2), -V4L2_COLORFX_NEGATIVE (3), -V4L2_COLORFX_EMBOSS (4), -V4L2_COLORFX_SKETCH (5), -V4L2_COLORFX_SKY_BLUE (6), -V4L2_COLORFX_GRASS_GREEN (7), -V4L2_COLORFX_SKIN_WHITEN (8) and -V4L2_COLORFX_VIVID (9). + Selects a color effect. The following values are defined: + + + + + + + + V4L2_COLORFX_NONE  + Color effect is disabled. + + + V4L2_COLORFX_ANTIQUE  + An aging (old photo) effect. + + + V4L2_COLORFX_ART_FREEZE  + Frost color effect. + + + V4L2_COLORFX_AQUA  + Water color, cool tone. + + + V4L2_COLORFX_BW  + Black and white. + + + V4L2_COLORFX_EMBOSS  + Emboss, the highlights and shadows replace light/dark boundaries + and low contrast areas are set to a gray background. + + + V4L2_COLORFX_GRASS_GREEN  + Grass green. + + + V4L2_COLORFX_NEGATIVE  + Negative. + + + V4L2_COLORFX_SEPIA  + Sepia tone. + + + V4L2_COLORFX_SKETCH  + Sketch. + + + V4L2_COLORFX_SKIN_WHITEN  + Skin whiten. + + + V4L2_COLORFX_SKY_BLUE  + Sky blue. + + + V4L2_COLORFX_SOLARIZATION  + Solarization, the image is partially reversed in tone, + only color values above or below a certain threshold are inverted. + + + + V4L2_COLORFX_SILHOUETTE  + Silhouette (outline). + + + V4L2_COLORFX_VIVID  + Vivid colors. + + + V4L2_COLORFX_SET_CBCR  + The Cb and Cr chroma components are replaced by fixed + coefficients determined by V4L2_CID_COLORFX_CBCR + control. + + + + + + V4L2_CID_COLORFX_CBCR + integer + Determines the Cb and Cr coefficients for V4L2_COLORFX_SET_CBCR + color effect. Bits [7:0] of the supplied 32 bit value are interpreted as + Cr component, bits [15:8] as Cb component and bits [31:16] must be zero. + V4L2_CID_ROTATE diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index fbf808d242f7..63242e2adc02 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -141,9 +141,10 @@ applications. --> 3.5 2012-04-02 - sa + sa, sn Added V4L2_CTRL_TYPE_INTEGER_MENU and V4L2 subdev - selections API. + selections API. Improved the description of V4L2_CID_COLORFX + control, added V4L2_CID_COLORFX_CBCR control. diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index e5531ace5ee7..9bd8a92419e1 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -241,6 +241,12 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Grass Green", "Skin Whiten", "Vivid", + "Aqua", + "Art Freeze", + "Silhouette", + "Solarization", + "Antique", + "Set Cb/Cr", NULL }; static const char * const tune_preemphasis[] = { @@ -493,6 +499,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Min Number of Capture Buffers"; case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Min Number of Output Buffers"; case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component"; + case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr"; /* MPEG controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 7f75846a4a0c..07bce86f3548 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1248,16 +1248,22 @@ enum v4l2_power_line_frequency { #define V4L2_CID_COLOR_KILLER (V4L2_CID_BASE+30) #define V4L2_CID_COLORFX (V4L2_CID_BASE+31) enum v4l2_colorfx { - V4L2_COLORFX_NONE = 0, - V4L2_COLORFX_BW = 1, - V4L2_COLORFX_SEPIA = 2, - V4L2_COLORFX_NEGATIVE = 3, - V4L2_COLORFX_EMBOSS = 4, - V4L2_COLORFX_SKETCH = 5, - V4L2_COLORFX_SKY_BLUE = 6, - V4L2_COLORFX_GRASS_GREEN = 7, - V4L2_COLORFX_SKIN_WHITEN = 8, - V4L2_COLORFX_VIVID = 9, + V4L2_COLORFX_NONE = 0, + V4L2_COLORFX_BW = 1, + V4L2_COLORFX_SEPIA = 2, + V4L2_COLORFX_NEGATIVE = 3, + V4L2_COLORFX_EMBOSS = 4, + V4L2_COLORFX_SKETCH = 5, + V4L2_COLORFX_SKY_BLUE = 6, + V4L2_COLORFX_GRASS_GREEN = 7, + V4L2_COLORFX_SKIN_WHITEN = 8, + V4L2_COLORFX_VIVID = 9, + V4L2_COLORFX_AQUA = 10, + V4L2_COLORFX_ART_FREEZE = 11, + V4L2_COLORFX_SILHOUETTE = 12, + V4L2_COLORFX_SOLARIZATION = 13, + V4L2_COLORFX_ANTIQUE = 14, + V4L2_COLORFX_SET_CBCR = 15, }; #define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32) #define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33) @@ -1274,9 +1280,10 @@ enum v4l2_colorfx { #define V4L2_CID_MIN_BUFFERS_FOR_OUTPUT (V4L2_CID_BASE+40) #define V4L2_CID_ALPHA_COMPONENT (V4L2_CID_BASE+41) +#define V4L2_CID_COLORFX_CBCR (V4L2_CID_BASE+42) /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+42) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+43) /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) -- cgit v1.2.3 From 515f32879a05bdb69f9b3f86f53db4c04b95e845 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 6 May 2012 15:30:44 -0300 Subject: [media] V4L: Add helper function for standard integer menu controls This patch adds v4l2_ctrl_new_int_menu() helper function which can be used in drivers for creating standard integer menu control with driver-specific menu item list. It is similar to v4l2_ctrl_new_std_menu(), except it doesn't have a mask parameter and an additional qmenu parameter allows passing an array of signed 64-bit integers as the menu item list. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Sakari Ailus Tested-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-controls.txt | 21 +++++++++++++++++++++ drivers/media/video/v4l2-ctrls.c | 21 +++++++++++++++++++++ include/media/v4l2-ctrls.h | 17 +++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index e2492a9d1027..43da22b89728 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -130,8 +130,18 @@ Menu controls are added by calling v4l2_ctrl_new_std_menu: const struct v4l2_ctrl_ops *ops, u32 id, s32 max, s32 skip_mask, s32 def); +Or alternatively for integer menu controls, by calling v4l2_ctrl_new_int_menu: + + struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s32 max, s32 def, const s64 *qmenu_int); + These functions are typically called right after the v4l2_ctrl_handler_init: + static const s64 exp_bias_qmenu[] = { + -2, -1, 0, 1, 2 + }; + v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls); v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); @@ -141,6 +151,11 @@ These functions are typically called right after the v4l2_ctrl_handler_init: V4L2_CID_POWER_LINE_FREQUENCY, V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); + v4l2_ctrl_new_int_menu(&foo->ctrl_handler, &foo_ctrl_ops, + V4L2_CID_EXPOSURE_BIAS, + ARRAY_SIZE(exp_bias_qmenu) - 1, + ARRAY_SIZE(exp_bias_qmenu) / 2 - 1, + exp_bias_qmenu); ... if (foo->ctrl_handler.error) { int err = foo->ctrl_handler.error; @@ -164,6 +179,12 @@ controls. There is no min argument since that is always 0 for menu controls, and instead of a step there is a skip_mask argument: if bit X is 1, then menu item X is skipped. +The v4l2_ctrl_new_int_menu function creates a new standard integer menu +control with driver-specific items in the menu. It differs from +v4l2_ctrl_new_std_menu in that it doesn't have the mask argument and takes +as the last argument an array of signed 64-bit integers that form an exact +menu item list. + Note that if something fails, the function will return NULL or an error and set ctrl_handler->error to the error code. If ctrl_handler->error was already set, then it will just return and do nothing. This is also true for diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 9bd8a92419e1..fdcb9e21d9d3 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -1544,6 +1544,27 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); +/* Helper function for standard integer menu controls */ +struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s32 max, s32 def, const s64 *qmenu_int) +{ + const char *name; + enum v4l2_ctrl_type type; + s32 min; + s32 step; + u32 flags; + + v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); + if (type != V4L2_CTRL_TYPE_INTEGER_MENU) { + handler_set_err(hdl, -EINVAL); + return NULL; + } + return v4l2_ctrl_new(hdl, ops, id, name, type, + 0, max, 0, def, flags, NULL, qmenu_int, NULL); +} +EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); + /* Add a control from another handler to this handler */ struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 5edd64daa425..776605f1cbe2 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -351,6 +351,23 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s32 max, s32 mask, s32 def); +/** v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control. + * @hdl: The control handler. + * @ops: The control ops. + * @id: The control ID. + * @max: The control's maximum value. + * @def: The control's default value. + * @qmenu_int: The control's menu entries. + * + * Same as v4l2_ctrl_new_std_menu(), but @mask is set to 0 and it additionaly + * takes as an argument an array of integers determining the menu items. + * + * If @id refers to a non-integer-menu control, then this function will return NULL. + */ +struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s32 max, s32 def, const s64 *qmenu_int); + /** v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler. * @hdl: The control handler. * @ctrl: The control to add. -- cgit v1.2.3 From d58083c949b3d76aba225be9f303ab5dab585064 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 6 Mar 2012 07:06:55 -0300 Subject: [media] V4L: Add camera exposure bias control The camera may in some conditions incorrectly determine the exposure, and a manual automatic exposure correction may be needed. This patch adds V4L2_CID_AUTO_EXPOSURE_BIAS control which allows to add some offset in the automatic exposure control loop, to compensate for frame under- or over-exposure. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 16 ++++++++++++++++ drivers/media/video/v4l2-ctrls.c | 4 ++++ include/linux/videodev2.h | 2 ++ 3 files changed, 22 insertions(+) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index e2ff0f98f08d..745b611c3593 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -2848,6 +2848,22 @@ remain constant. + + V4L2_CID_EXPOSURE_BIAS  + integer menu + Determines the automatic +exposure compensation, it is effective only when V4L2_CID_EXPOSURE_AUTO +control is set to AUTO, SHUTTER_PRIORITY +or APERTURE_PRIORITY. +It is expressed in terms of EV, drivers should interpret the values as 0.001 EV +units, where the value 1000 stands for +1 EV. +Increasing the exposure compensation value is equivalent to decreasing +the exposure value (EV) and will increase the amount of light at the image +sensor. The camera performs the exposure compensation by adjusting absolute +exposure time and/or aperture. + + + V4L2_CID_PAN_RELATIVE  integer diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index fdcb9e21d9d3..5bfef90e88ee 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -604,6 +604,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_PRIVACY: return "Privacy"; case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute"; case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative"; + case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -760,6 +761,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RDS_TX_RADIO_TEXT: *type = V4L2_CTRL_TYPE_STRING; break; + case V4L2_CID_AUTO_EXPOSURE_BIAS: + *type = V4L2_CTRL_TYPE_INTEGER_MENU; + break; case V4L2_CID_USER_CLASS: case V4L2_CID_CAMERA_CLASS: case V4L2_CID_MPEG_CLASS: diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 07bce86f3548..dfd209816df6 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1707,6 +1707,8 @@ enum v4l2_exposure_auto_type { #define V4L2_CID_IRIS_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+17) #define V4L2_CID_IRIS_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+18) +#define V4L2_CID_AUTO_EXPOSURE_BIAS (V4L2_CID_CAMERA_CLASS_BASE+19) + /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) #define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1) -- cgit v1.2.3 From e40a05736d4503950ec303610a51f838bd59cdc1 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 6 Mar 2012 07:04:26 -0300 Subject: [media] V4L: Add an extended camera white balance control This patch adds V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE control which is an extended version of the V4L2_CID_AUTO_WHITE_BALANCE control, including white balance presets. The following presets are defined: - V4L2_WHITE_BALANCE_INCANDESCENT, - V4L2_WHITE_BALANCE_FLUORESCENT, - V4L2_WHITE_BALANCE_FLUORESCENT_H, - V4L2_WHITE_BALANCE_HORIZON, - V4L2_WHITE_BALANCE_DAYLIGHT, - V4L2_WHITE_BALANCE_FLASH, - V4L2_WHITE_BALANCE_CLOUDY, - V4L2_WHITE_BALANCE_SHADE. Signed-off-by: HeungJun Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 70 ++++++++++++++++++++++++++++ drivers/media/video/v4l2-ctrls.c | 17 +++++++ include/linux/videodev2.h | 14 ++++++ 3 files changed, 101 insertions(+) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 745b611c3593..56dfdf02c76b 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3022,6 +3022,76 @@ camera sensor on or off, or specify its strength. Such band-stop filters can be used, for example, to filter out the fluorescent light component. + + + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE  + enum v4l2_auto_n_preset_white_balance + Sets white balance to automatic, +manual or a preset. The presets determine color temperature of the light as +a hint to the camera for white balance adjustments resulting in most accurate +color representation. The following white balance presets are listed in order +of increasing color temperature. + + + + + + V4L2_WHITE_BALANCE_MANUAL  + Manual white balance. + + + V4L2_WHITE_BALANCE_AUTO  + Automatic white balance adjustments. + + + V4L2_WHITE_BALANCE_INCANDESCENT  + White balance setting for incandescent (tungsten) lighting. +It generally cools down the colors and corresponds approximately to 2500...3500 K +color temperature range. + + + V4L2_WHITE_BALANCE_FLUORESCENT  + White balance preset for fluorescent lighting. +It corresponds approximately to 4000...5000 K color temperature. + + + V4L2_WHITE_BALANCE_FLUORESCENT_H  + With this setting the camera will compensate for +fluorescent H lighting. + + + V4L2_WHITE_BALANCE_HORIZON  + White balance setting for horizon daylight. +It corresponds approximately to 5000 K color temperature. + + + V4L2_WHITE_BALANCE_DAYLIGHT  + White balance preset for daylight (with clear sky). +It corresponds approximately to 5000...6500 K color temperature. + + + V4L2_WHITE_BALANCE_FLASH  + With this setting the camera will compensate for the flash +light. It slightly warms up the colors and corresponds roughly to 5000...5500 K +color temperature. + + + V4L2_WHITE_BALANCE_CLOUDY  + White balance preset for moderately overcast sky. +This option corresponds approximately to 6500...8000 K color temperature +range. + + + V4L2_WHITE_BALANCE_SHADE  + White balance preset for shade or heavily overcast +sky. It corresponds approximately to 9000...10000 K color temperature. + + + + + + + diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 5bfef90e88ee..56ac71c8ba37 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -249,6 +249,19 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Set Cb/Cr", NULL }; + static const char * const auto_n_preset_white_balance[] = { + "Manual", + "Auto", + "Incandescent", + "Fluorescent", + "Fluorescent H", + "Horizon", + "Daylight", + "Flash", + "Cloudy", + "Shade", + NULL, + }; static const char * const tune_preemphasis[] = { "No Preemphasis", "50 Microseconds", @@ -418,6 +431,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return camera_exposure_auto; case V4L2_CID_COLORFX: return colorfx; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + return auto_n_preset_white_balance; case V4L2_CID_TUNE_PREEMPHASIS: return tune_preemphasis; case V4L2_CID_FLASH_LED_MODE: @@ -605,6 +620,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute"; case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative"; case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias"; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -739,6 +755,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_STREAM_VBI_FMT: case V4L2_CID_EXPOSURE_AUTO: case V4L2_CID_COLORFX: + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: case V4L2_CID_TUNE_PREEMPHASIS: case V4L2_CID_FLASH_LED_MODE: case V4L2_CID_FLASH_STROBE_SOURCE: diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index dfd209816df6..85c4e8f1090f 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1709,6 +1709,20 @@ enum v4l2_exposure_auto_type { #define V4L2_CID_AUTO_EXPOSURE_BIAS (V4L2_CID_CAMERA_CLASS_BASE+19) +#define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE (V4L2_CID_CAMERA_CLASS_BASE+20) +enum v4l2_auto_n_preset_white_balance { + V4L2_WHITE_BALANCE_MANUAL = 0, + V4L2_WHITE_BALANCE_AUTO = 1, + V4L2_WHITE_BALANCE_INCANDESCENT = 2, + V4L2_WHITE_BALANCE_FLUORESCENT = 3, + V4L2_WHITE_BALANCE_FLUORESCENT_H = 4, + V4L2_WHITE_BALANCE_HORIZON = 5, + V4L2_WHITE_BALANCE_DAYLIGHT = 6, + V4L2_WHITE_BALANCE_FLASH = 7, + V4L2_WHITE_BALANCE_CLOUDY = 8, + V4L2_WHITE_BALANCE_SHADE = 9, +}; + /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) #define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1) -- cgit v1.2.3 From 44d44a1acde974dbc91c19815a41d3a895a44daf Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 6 Mar 2012 07:05:45 -0300 Subject: [media] V4L: Add camera wide dynamic range control Add V4L2_CID_WIDE_DYNAMIC_RANGE camera class control for the camera wide dynamic range (WDR, HDR) feature. This control can be used to enable/disable wide dynamic range. It might get converted to a menu control in future if more options are needed. Signed-off-by: HeungJun Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 15 +++++++++++++++ drivers/media/video/v4l2-ctrls.c | 2 ++ include/linux/videodev2.h | 2 ++ 3 files changed, 19 insertions(+) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 56dfdf02c76b..16c7aab595af 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3092,6 +3092,21 @@ sky. It corresponds approximately to 9000...10000 K color temperature. + + V4L2_CID_WIDE_DYNAMIC_RANGE + boolean + + + Enables or disables the camera's wide dynamic +range feature. This feature allows to obtain clear images in situations where +intensity of the illumination varies significantly throughout the scene, i.e. +there are simultaneously very dark and very bright areas. It is most commonly +realized in cameras by combining two subsequent frames with different exposure +times. This control may be changed to a menu +control in the future, if more options are required. + + + diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 56ac71c8ba37..0c18c82e0b59 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -621,6 +621,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative"; case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias"; case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset"; + case V4L2_CID_WIDE_DYNAMIC_RANGE: return "Wide Dynamic Range"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -723,6 +724,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: + case V4L2_CID_WIDE_DYNAMIC_RANGE: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 85c4e8f1090f..d93e42bc0348 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1723,6 +1723,8 @@ enum v4l2_auto_n_preset_white_balance { V4L2_WHITE_BALANCE_SHADE = 9, }; +#define V4L2_CID_WIDE_DYNAMIC_RANGE (V4L2_CID_CAMERA_CLASS_BASE+21) + /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) #define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1) -- cgit v1.2.3 From 82b3056c5a77b687097bd7f36a598a0b37af24a2 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 1 May 2012 17:38:09 -0300 Subject: [media] V4L: Add camera image stabilization control Add V4L2_CID_IMAGE_STABILIZATION control for the camera image stabilization feature. This control can be used to enable/disable image stabilization. It might get converted to a menu control in future if more options are needed. Signed-off-by: HeungJun Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 10 ++++++++++ drivers/media/video/v4l2-ctrls.c | 2 ++ include/linux/videodev2.h | 1 + 3 files changed, 13 insertions(+) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 16c7aab595af..74876b423bc3 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3107,6 +3107,16 @@ control in the future, if more options are required.
+ + V4L2_CID_IMAGE_STABILIZATION + boolean + + + Enables or disables image stabilization. + + + + diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 0c18c82e0b59..2da8b98ac757 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -622,6 +622,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias"; case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset"; case V4L2_CID_WIDE_DYNAMIC_RANGE: return "Wide Dynamic Range"; + case V4L2_CID_IMAGE_STABILIZATION: return "Image Stabilization"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -725,6 +726,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: case V4L2_CID_WIDE_DYNAMIC_RANGE: + case V4L2_CID_IMAGE_STABILIZATION: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index d93e42bc0348..e94601a7ae5f 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1724,6 +1724,7 @@ enum v4l2_auto_n_preset_white_balance { }; #define V4L2_CID_WIDE_DYNAMIC_RANGE (V4L2_CID_CAMERA_CLASS_BASE+21) +#define V4L2_CID_IMAGE_STABILIZATION (V4L2_CID_CAMERA_CLASS_BASE+22) /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) -- cgit v1.2.3 From 7f84ad8bdb63a8bfcbb83755e487e06be5db54cf Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 1 May 2012 17:39:45 -0300 Subject: [media] V4L: Add camera ISO sensitivity controls Add ISO sensitivity and ISO auto/manual controls. The sensitivity values are related to level of amplification of the analog signal between image sensor and ADC. These controls allow to support sensors exposing an interface to accept the ISO values directly. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/biblio.xml | 11 ++++++++ Documentation/DocBook/media/v4l/controls.xml | 38 ++++++++++++++++++++++++++++ drivers/media/video/v4l2-ctrls.c | 11 ++++++++ include/linux/videodev2.h | 7 +++++ 4 files changed, 67 insertions(+) diff --git a/Documentation/DocBook/media/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml index 7dc65c592a87..66a0ef251c79 100644 --- a/Documentation/DocBook/media/v4l/biblio.xml +++ b/Documentation/DocBook/media/v4l/biblio.xml @@ -197,4 +197,15 @@ in the frequency range from 87,5 to 108,0 MHz NTSC-4: United States RBDS Standard + + ISO 12232:2006 + + International Organization for Standardization +(http://www.iso.org) + + Photography — Digital still cameras — Determination + of exposure index, ISO speed ratings, standard output sensitivity, and + recommended exposure index + + diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 74876b423bc3..8fccfe1b2058 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3117,6 +3117,44 @@ control in the future, if more options are required.
+ + V4L2_CID_ISO_SENSITIVITY  + integer menu + Determines ISO equivalent of an +image sensor indicating the sensor's sensitivity to light. The numbers are +expressed in arithmetic scale, as per standard, +where doubling the sensor sensitivity is represented by doubling the numerical +ISO value. Applications should interpret the values as standard ISO values +multiplied by 1000, e.g. control value 800 stands for ISO 0.8. Drivers will +usually support only a subset of standard ISO values. The effect of setting +this control while the V4L2_CID_ISO_SENSITIVITY_AUTO +control is set to a value other than V4L2_CID_ISO_SENSITIVITY_MANUAL + is undefined, drivers should ignore such requests. + + + + + V4L2_CID_ISO_SENSITIVITY_AUTO  + enum v4l2_iso_sensitivity_type + Enables or disables automatic ISO +sensitivity adjustments. + + + + + + V4L2_CID_ISO_SENSITIVITY_MANUAL  + Manual ISO sensitivity. + + + V4L2_CID_ISO_SENSITIVITY_AUTO  + Automatic ISO sensitivity adjustments. + + + + + + diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 2da8b98ac757..debaf9fb9004 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -262,6 +262,11 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Shade", NULL, }; + static const char * const camera_iso_sensitivity_auto[] = { + "Manual", + "Auto", + NULL + }; static const char * const tune_preemphasis[] = { "No Preemphasis", "50 Microseconds", @@ -433,6 +438,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return colorfx; case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return auto_n_preset_white_balance; + case V4L2_CID_ISO_SENSITIVITY_AUTO: + return camera_iso_sensitivity_auto; case V4L2_CID_TUNE_PREEMPHASIS: return tune_preemphasis; case V4L2_CID_FLASH_LED_MODE: @@ -623,6 +630,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset"; case V4L2_CID_WIDE_DYNAMIC_RANGE: return "Wide Dynamic Range"; case V4L2_CID_IMAGE_STABILIZATION: return "Image Stabilization"; + case V4L2_CID_ISO_SENSITIVITY: return "ISO Sensitivity"; + case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -773,6 +782,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + case V4L2_CID_ISO_SENSITIVITY_AUTO: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -782,6 +792,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RDS_TX_RADIO_TEXT: *type = V4L2_CTRL_TYPE_STRING; break; + case V4L2_CID_ISO_SENSITIVITY: case V4L2_CID_AUTO_EXPOSURE_BIAS: *type = V4L2_CTRL_TYPE_INTEGER_MENU; break; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e94601a7ae5f..593a1bd33111 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1726,6 +1726,13 @@ enum v4l2_auto_n_preset_white_balance { #define V4L2_CID_WIDE_DYNAMIC_RANGE (V4L2_CID_CAMERA_CLASS_BASE+21) #define V4L2_CID_IMAGE_STABILIZATION (V4L2_CID_CAMERA_CLASS_BASE+22) +#define V4L2_CID_ISO_SENSITIVITY (V4L2_CID_CAMERA_CLASS_BASE+23) +#define V4L2_CID_ISO_SENSITIVITY_AUTO (V4L2_CID_CAMERA_CLASS_BASE+24) +enum v4l2_iso_sensitivity_auto_type { + V4L2_ISO_SENSITIVITY_MANUAL = 0, + V4L2_ISO_SENSITIVITY_AUTO = 1, +}; + /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) #define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1) -- cgit v1.2.3 From cf072139c7952e267a2eff334f224a62c949ee96 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 30 Apr 2012 04:34:10 -0300 Subject: [media] V4L: Add camera exposure metering control The V4L2_CID_EXPOSURE_METERING control allows to determine a method used by the camera for measuring the amount of light available for automatic exposure. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 29 ++++++++++++++++++++++++++++ drivers/media/video/v4l2-ctrls.c | 10 ++++++++++ include/linux/videodev2.h | 7 +++++++ 3 files changed, 46 insertions(+) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 8fccfe1b2058..e8c63e04741f 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -2864,6 +2864,35 @@ exposure time and/or aperture.
+ + V4L2_CID_EXPOSURE_METERING  + enum v4l2_exposure_metering + Determines how the camera measures +the amount of light available for the frame exposure. Possible values are: + + + + + + V4L2_EXPOSURE_METERING_AVERAGE  + Use the light information coming from the entire frame +and average giving no weighting to any particular portion of the metered area. + + + + V4L2_EXPOSURE_METERING_CENTER_WEIGHTED  + Average the light information coming from the entire frame +giving priority to the center of the metered area. + + + V4L2_EXPOSURE_METERING_SPOT  + Measure only very small area at the center of the frame. + + + + + + V4L2_CID_PAN_RELATIVE  integer diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index debaf9fb9004..55dd813b7341 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -230,6 +230,12 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Aperture Priority Mode", NULL }; + static const char * const camera_exposure_metering[] = { + "Average", + "Center Weighted", + "Spot", + NULL + }; static const char * const colorfx[] = { "None", "Black & White", @@ -434,6 +440,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return camera_power_line_frequency; case V4L2_CID_EXPOSURE_AUTO: return camera_exposure_auto; + case V4L2_CID_EXPOSURE_METERING: + return camera_exposure_metering; case V4L2_CID_COLORFX: return colorfx; case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: @@ -632,6 +640,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_IMAGE_STABILIZATION: return "Image Stabilization"; case V4L2_CID_ISO_SENSITIVITY: return "ISO Sensitivity"; case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto"; + case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -783,6 +792,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: case V4L2_CID_ISO_SENSITIVITY_AUTO: + case V4L2_CID_EXPOSURE_METERING: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 593a1bd33111..a3e47ad60a6d 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1733,6 +1733,13 @@ enum v4l2_iso_sensitivity_auto_type { V4L2_ISO_SENSITIVITY_AUTO = 1, }; +#define V4L2_CID_EXPOSURE_METERING (V4L2_CID_CAMERA_CLASS_BASE+25) +enum v4l2_exposure_metering { + V4L2_EXPOSURE_METERING_AVERAGE = 0, + V4L2_EXPOSURE_METERING_CENTER_WEIGHTED = 1, + V4L2_EXPOSURE_METERING_SPOT = 2, +}; + /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) #define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1) -- cgit v1.2.3 From 0bf6b7dc5fd1e60e93657d4fe4b3d788216b9d5e Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 16 Apr 2012 10:45:44 -0300 Subject: [media] V4L: Add camera scene mode control Add control for the scene mode feature available in image sensor with more advanced ISP firmware. The V4L2_CID_SCENE_MODE menu control allows to select a set of parameters or a specific image processing and capture control algorithm optimized for common image capture conditions. Signed-off-by: HeungJun Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 117 +++++++++++++++++++++++++++ drivers/media/video/v4l2-ctrls.c | 21 +++++ include/linux/videodev2.h | 18 +++++ 3 files changed, 156 insertions(+) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index e8c63e04741f..ad0d53d3ffdd 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3184,6 +3184,123 @@ sensitivity adjustments. + + V4L2_CID_SCENE_MODE  + enum v4l2_scene_mode + This control allows to select +scene programs as the camera automatic modes optimized for common shooting +scenes. Within these modes the camera determines best exposure, aperture, +focusing, light metering, white balance and equivalent sensitivity. The +controls of those parameters are influenced by the scene mode control. +An exact behavior in each mode is subject to the camera specification. + +When the scene mode feature is not used, this control should be set to +V4L2_SCENE_MODE_NONE to make sure the other possibly +related controls are accessible. The following scene programs are defined: + + + + + + + + V4L2_SCENE_MODE_NONE  + The scene mode feature is disabled. + + + V4L2_SCENE_MODE_BACKLIGHT  + Backlight. Compensates for dark shadows when light is + coming from behind a subject, also by automatically turning + on the flash. + + + V4L2_SCENE_MODE_BEACH_SNOW  + Beach and snow. This mode compensates for all-white or +bright scenes, which tend to look gray and low contrast, when camera's automatic +exposure is based on an average scene brightness. To compensate, this mode +automatically slightly overexposes the frames. The white balance may also be +adjusted to compensate for the fact that reflected snow looks bluish rather +than white. + + + V4L2_SCENE_MODE_CANDLELIGHT  + Candle light. The camera generally raises the ISO +sensitivity and lowers the shutter speed. This mode compensates for relatively +close subject in the scene. The flash is disabled in order to preserve the +ambiance of the light. + + + V4L2_SCENE_MODE_DAWN_DUSK  + Dawn and dusk. Preserves the colors seen in low +natural light before dusk and after down. The camera may turn off the flash, +and automatically focus at infinity. It will usually boost saturation and +lower the shutter speed. + + + V4L2_SCENE_MODE_FALL_COLORS  + Fall colors. Increases saturation and adjusts white +balance for color enhancement. Pictures of autumn leaves get saturated reds +and yellows. + + + V4L2_SCENE_MODE_FIREWORKS  + Fireworks. Long exposure times are used to capture +the expanding burst of light from a firework. The camera may invoke image +stabilization. + + + V4L2_SCENE_MODE_LANDSCAPE  + Landscape. The camera may choose a small aperture to +provide deep depth of field and long exposure duration to help capture detail +in dim light conditions. The focus is fixed at infinity. Suitable for distant +and wide scenery. + + + V4L2_SCENE_MODE_NIGHT  + Night, also known as Night Landscape. Designed for low +light conditions, it preserves detail in the dark areas without blowing out bright +objects. The camera generally sets itself to a medium-to-high ISO sensitivity, +with a relatively long exposure time, and turns flash off. As such, there will be +increased image noise and the possibility of blurred image. + + + V4L2_SCENE_MODE_PARTY_INDOOR  + Party and indoor. Designed to capture indoor scenes +that are lit by indoor background lighting as well as the flash. The camera +usually increases ISO sensitivity, and adjusts exposure for the low light +conditions. + + + V4L2_SCENE_MODE_PORTRAIT  + Portrait. The camera adjusts the aperture so that the +depth of field is reduced, which helps to isolate the subject against a smooth +background. Most cameras recognize the presence of faces in the scene and focus +on them. The color hue is adjusted to enhance skin tones. The intensity of the +flash is often reduced. + + + V4L2_SCENE_MODE_SPORTS  + Sports. Significantly increases ISO and uses a fast +shutter speed to freeze motion of rapidly-moving subjects. Increased image +noise may be seen in this mode. + + + V4L2_SCENE_MODE_SUNSET  + Sunset. Preserves deep hues seen in sunsets and +sunrises. It bumps up the saturation. + + + V4L2_SCENE_MODE_TEXT  + Text. It applies extra contrast and sharpness, it is +typically a black-and-white mode optimized for readability. Automatic focus +may be switched to close-up mode and this setting may also involve some +lens-distortion correction. + + + + + + diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 55dd813b7341..d4dc8ba7b755 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -273,6 +273,23 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Auto", NULL }; + static const char * const scene_mode[] = { + "None", + "Backlight", + "Beach/Snow", + "Candle Light", + "Dusk/Dawn", + "Fall Colors", + "Fireworks", + "Landscape", + "Night", + "Party/Indoor", + "Portrait", + "Sports", + "Sunset", + "Text", + NULL + }; static const char * const tune_preemphasis[] = { "No Preemphasis", "50 Microseconds", @@ -448,6 +465,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return auto_n_preset_white_balance; case V4L2_CID_ISO_SENSITIVITY_AUTO: return camera_iso_sensitivity_auto; + case V4L2_CID_SCENE_MODE: + return scene_mode; case V4L2_CID_TUNE_PREEMPHASIS: return tune_preemphasis; case V4L2_CID_FLASH_LED_MODE: @@ -641,6 +660,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_ISO_SENSITIVITY: return "ISO Sensitivity"; case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto"; case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode"; + case V4L2_CID_SCENE_MODE: return "Scene Mode"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -793,6 +813,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: case V4L2_CID_ISO_SENSITIVITY_AUTO: case V4L2_CID_EXPOSURE_METERING: + case V4L2_CID_SCENE_MODE: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index a3e47ad60a6d..092bf5aaf348 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1740,6 +1740,24 @@ enum v4l2_exposure_metering { V4L2_EXPOSURE_METERING_SPOT = 2, }; +#define V4L2_CID_SCENE_MODE (V4L2_CID_CAMERA_CLASS_BASE+26) +enum v4l2_scene_mode { + V4L2_SCENE_MODE_NONE = 0, + V4L2_SCENE_MODE_BACKLIGHT = 1, + V4L2_SCENE_MODE_BEACH_SNOW = 2, + V4L2_SCENE_MODE_CANDLE_LIGHT = 3, + V4L2_SCENE_MODE_DAWN_DUSK = 4, + V4L2_SCENE_MODE_FALL_COLORS = 5, + V4L2_SCENE_MODE_FIREWORKS = 6, + V4L2_SCENE_MODE_LANDSCAPE = 7, + V4L2_SCENE_MODE_NIGHT = 8, + V4L2_SCENE_MODE_PARTY_INDOOR = 9, + V4L2_SCENE_MODE_PORTRAIT = 10, + V4L2_SCENE_MODE_SPORTS = 11, + V4L2_SCENE_MODE_SUNSET = 12, + V4L2_SCENE_MODE_TEXT = 13, +}; + /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) #define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1) -- cgit v1.2.3 From fc162a099e7b34bfe3501028c919ff5d43e5e3d3 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 2 May 2012 06:24:33 -0300 Subject: [media] V4L: Add camera 3A lock control The V4L2_CID_3A_LOCK bitmask control allows applications to pause or resume the automatic exposure, focus and wite balance adjustments. It can be used, for example, to lock the 3A adjustments right before a still image is captured, for pre-focus, etc. The applications can control each of the algorithms independently, through a corresponding control bit, if driver allows that. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 39 ++++++++++++++++++++++++++++ drivers/media/video/v4l2-ctrls.c | 2 ++ include/linux/videodev2.h | 5 ++++ 3 files changed, 46 insertions(+) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index ad0d53d3ffdd..f38f06169795 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3301,6 +3301,45 @@ lens-distortion correction. + + V4L2_CID_3A_LOCK + bitmask + + + This control locks or unlocks the automatic +focus, exposure and white balance. The automatic adjustments can be paused +independently by setting the corresponding lock bit to 1. The camera then retains +the settings until the lock bit is cleared. The following lock bits are defined: + + + + + + + V4L2_LOCK_EXPOSURE + Automatic exposure adjustments lock. + + + V4L2_LOCK_WHITE_BALANCE + Automatic white balance adjustments lock. + + + V4L2_LOCK_FOCUS + Automatic focus lock. + + + + + +When a given algorithm is not enabled, drivers should ignore requests +to lock it and should return no error. An example might be an application +setting bit V4L2_LOCK_WHITE_BALANCE when the +V4L2_CID_AUTO_WHITE_BALANCE control is set to +FALSE. The value of this control may be changed +by exposure, white balance or focus controls. + + + diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index d4dc8ba7b755..a47b29270ba3 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -661,6 +661,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto"; case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode"; case V4L2_CID_SCENE_MODE: return "Scene Mode"; + case V4L2_CID_3A_LOCK: return "3A Lock"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -849,6 +850,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_FLASH_FAULT: case V4L2_CID_JPEG_ACTIVE_MARKER: + case V4L2_CID_3A_LOCK: *type = V4L2_CTRL_TYPE_BITMASK; break; case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 092bf5aaf348..a9b03ae44037 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1758,6 +1758,11 @@ enum v4l2_scene_mode { V4L2_SCENE_MODE_TEXT = 13, }; +#define V4L2_CID_3A_LOCK (V4L2_CID_CAMERA_CLASS_BASE+27) +#define V4L2_LOCK_EXPOSURE (1 << 0) +#define V4L2_LOCK_WHITE_BALANCE (1 << 1) +#define V4L2_LOCK_FOCUS (1 << 2) + /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) #define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1) -- cgit v1.2.3 From 2272ab657b508ece04bf015da6c23f61711bac81 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 11 May 2012 06:37:03 -0300 Subject: [media] V4L: Add camera auto focus controls Add following auto focus controls: - V4L2_CID_AUTO_FOCUS_START - single-shot auto focus start - V4L2_CID_AUTO_FOCUS_STOP - single-shot auto focus stop - V4L2_CID_AUTO_FOCUS_STATUS - automatic focus status - V4L2_CID_AUTO_FOCUS_RANGE - automatic focus scan range selection Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 19 ++++++ Documentation/DocBook/media/v4l/controls.xml | 98 +++++++++++++++++++++++++++- Documentation/DocBook/media/v4l/v4l2.xml | 9 ++- drivers/media/video/v4l2-ctrls.c | 20 +++++- include/linux/videodev2.h | 16 +++++ 5 files changed, 158 insertions(+), 4 deletions(-) diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 149f65dfaa72..dc61b013b8a8 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2435,6 +2435,21 @@ details. Added V4L2_CID_COLORFX_CBCR control. + + Added camera controls V4L2_CID_AUTO_EXPOSURE_BIAS, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + V4L2_CID_IMAGE_STABILIZATION, + V4L2_CID_ISO_SENSITIVITY, + V4L2_CID_ISO_SENSITIVITY_AUTO, + V4L2_CID_EXPOSURE_METERING, + V4L2_CID_SCENE_MODE, + V4L2_CID_3A_LOCK, + V4L2_CID_AUTO_FOCUS_START, + V4L2_CID_AUTO_FOCUS_STOP, + V4L2_CID_AUTO_FOCUS_STATUS and + V4L2_CID_AUTO_FOCUS_RANGE. + + @@ -2555,6 +2570,10 @@ ioctls. Sub-device selection API: &VIDIOC-SUBDEV-G-SELECTION; and &VIDIOC-SUBDEV-S-SELECTION; ioctls. + + + V4L2_CID_AUTO_FOCUS_AREA control. + diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index f38f06169795..132b0cc29832 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -2976,12 +2976,106 @@ negative values towards infinity. This is a write-only control. V4L2_CID_FOCUS_AUTO  boolean - Enables automatic focus -adjustments. The effect of manual focus adjustments while this feature + Enables continuous automatic +focus adjustments. The effect of manual focus adjustments while this feature is enabled is undefined, drivers should ignore such requests. + + V4L2_CID_AUTO_FOCUS_START  + button + Starts single auto focus process. +The effect of setting this control when V4L2_CID_FOCUS_AUTO +is set to TRUE (1) is undefined, drivers should ignore +such requests. + + + + + V4L2_CID_AUTO_FOCUS_STOP  + button + Aborts automatic focusing +started with V4L2_CID_AUTO_FOCUS_START control. It is +effective only when the continuous autofocus is disabled, that is when +V4L2_CID_FOCUS_AUTO control is set to FALSE + (0). + + + + + + V4L2_CID_AUTO_FOCUS_STATUS  + bitmask + + The automatic focus status. This is a read-only + control. + + + + + + V4L2_AUTO_FOCUS_STATUS_IDLE  + Automatic focus is not active. + + + V4L2_AUTO_FOCUS_STATUS_BUSY  + Automatic focusing is in progress. + + + V4L2_AUTO_FOCUS_STATUS_REACHED  + Focus has been reached. + + + V4L2_AUTO_FOCUS_STATUS_FAILED  + Automatic focus has failed, the driver will not + transition from this state until another action is + performed by an application. + + + + + +Setting V4L2_LOCK_FOCUS lock bit of the V4L2_CID_3A_LOCK + control may stop updates of the V4L2_CID_AUTO_FOCUS_STATUS +control value. + + + + + + V4L2_CID_AUTO_FOCUS_RANGE  + enum v4l2_auto_focus_range + + Determines auto focus distance range +for which lens may be adjusted. + + + + + + V4L2_AUTO_FOCUS_RANGE_AUTO  + The camera automatically selects the focus range. + + + V4L2_AUTO_FOCUS_RANGE_NORMAL  + Normal distance range, limited for best automatic focus +performance. + + + V4L2_AUTO_FOCUS_RANGE_MACRO  + Macro (close-up) auto focus. The camera will +use its minimum possible distance for auto focus. + + + V4L2_AUTO_FOCUS_RANGE_INFINITY  + The lens is set to focus on an object at infinite distance. + + + + + + V4L2_CID_ZOOM_ABSOLUTE  integer diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 63242e2adc02..e6fbbc6c17e1 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -140,11 +140,18 @@ applications. --> 3.5 - 2012-04-02 + 2012-05-07 sa, sn Added V4L2_CTRL_TYPE_INTEGER_MENU and V4L2 subdev selections API. Improved the description of V4L2_CID_COLORFX control, added V4L2_CID_COLORFX_CBCR control. + Added camera controls V4L2_CID_AUTO_EXPOSURE_BIAS, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, V4L2_CID_IMAGE_STABILIZATION, + V4L2_CID_ISO_SENSITIVITY, V4L2_CID_ISO_SENSITIVITY_AUTO, + V4L2_CID_EXPOSURE_METERING, V4L2_CID_SCENE_MODE, + V4L2_CID_3A_LOCK, V4L2_CID_AUTO_FOCUS_START, + V4L2_CID_AUTO_FOCUS_STOP, V4L2_CID_AUTO_FOCUS_STATUS + and V4L2_CID_AUTO_FOCUS_RANGE. diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index a47b29270ba3..a5fbace4c059 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -236,6 +236,13 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Spot", NULL }; + static const char * const camera_auto_focus_range[] = { + "Auto", + "Normal", + "Macro", + "Infinity", + NULL + }; static const char * const colorfx[] = { "None", "Black & White", @@ -459,6 +466,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return camera_exposure_auto; case V4L2_CID_EXPOSURE_METERING: return camera_exposure_metering; + case V4L2_CID_AUTO_FOCUS_RANGE: + return camera_auto_focus_range; case V4L2_CID_COLORFX: return colorfx; case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: @@ -646,7 +655,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute"; case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute"; case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative"; - case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic"; + case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic Continuous"; case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute"; case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative"; case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; @@ -662,6 +671,10 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode"; case V4L2_CID_SCENE_MODE: return "Scene Mode"; case V4L2_CID_3A_LOCK: return "3A Lock"; + case V4L2_CID_AUTO_FOCUS_START: return "Auto Focus, Start"; + case V4L2_CID_AUTO_FOCUS_STOP: return "Auto Focus, Stop"; + case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status"; + case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -774,6 +787,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_TILT_RESET: case V4L2_CID_FLASH_STROBE: case V4L2_CID_FLASH_STROBE_STOP: + case V4L2_CID_AUTO_FOCUS_START: + case V4L2_CID_AUTO_FOCUS_STOP: *type = V4L2_CTRL_TYPE_BUTTON; *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; *min = *max = *step = *def = 0; @@ -797,6 +812,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_STREAM_TYPE: case V4L2_CID_MPEG_STREAM_VBI_FMT: case V4L2_CID_EXPOSURE_AUTO: + case V4L2_CID_AUTO_FOCUS_RANGE: case V4L2_CID_COLORFX: case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: case V4L2_CID_TUNE_PREEMPHASIS: @@ -851,6 +867,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_FLASH_FAULT: case V4L2_CID_JPEG_ACTIVE_MARKER: case V4L2_CID_3A_LOCK: + case V4L2_CID_AUTO_FOCUS_STATUS: *type = V4L2_CTRL_TYPE_BITMASK; break; case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: @@ -913,6 +930,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; break; case V4L2_CID_FLASH_STROBE_STATUS: + case V4L2_CID_AUTO_FOCUS_STATUS: case V4L2_CID_FLASH_READY: *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index a9b03ae44037..dc3e3ea28f99 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1763,6 +1763,22 @@ enum v4l2_scene_mode { #define V4L2_LOCK_WHITE_BALANCE (1 << 1) #define V4L2_LOCK_FOCUS (1 << 2) +#define V4L2_CID_AUTO_FOCUS_START (V4L2_CID_CAMERA_CLASS_BASE+28) +#define V4L2_CID_AUTO_FOCUS_STOP (V4L2_CID_CAMERA_CLASS_BASE+29) +#define V4L2_CID_AUTO_FOCUS_STATUS (V4L2_CID_CAMERA_CLASS_BASE+30) +#define V4L2_AUTO_FOCUS_STATUS_IDLE (0 << 0) +#define V4L2_AUTO_FOCUS_STATUS_BUSY (1 << 0) +#define V4L2_AUTO_FOCUS_STATUS_REACHED (1 << 1) +#define V4L2_AUTO_FOCUS_STATUS_FAILED (1 << 2) + +#define V4L2_CID_AUTO_FOCUS_RANGE (V4L2_CID_CAMERA_CLASS_BASE+31) +enum v4l2_auto_focus_range { + V4L2_AUTO_FOCUS_RANGE_AUTO = 0, + V4L2_AUTO_FOCUS_RANGE_NORMAL = 1, + V4L2_AUTO_FOCUS_RANGE_MACRO = 2, + V4L2_AUTO_FOCUS_RANGE_INFINITY = 3, +}; + /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) #define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1) -- cgit v1.2.3 From 91f4eb0acb99a34e4b7fe9aee65ff9790be523c0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 6 Mar 2012 07:08:56 -0300 Subject: [media] m5mols: Convert macros to inline functions Make to_sd and to_m5mols macros static inline functions for better type safety. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 4b021e1ee5f2..0acc3d69b851 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -21,11 +21,6 @@ extern int m5mols_debug; -#define to_m5mols(__sd) container_of(__sd, struct m5mols_info, sd) - -#define to_sd(__ctrl) \ - (&container_of(__ctrl->handler, struct m5mols_info, handle)->sd) - enum m5mols_restype { M5MOLS_RESTYPE_MONITOR, M5MOLS_RESTYPE_CAPTURE, @@ -296,4 +291,16 @@ int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); int m5mols_update_fw(struct v4l2_subdev *sd, int (*set_power)(struct m5mols_info *, bool)); +static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct m5mols_info, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + struct m5mols_info *info = container_of(ctrl->handler, + struct m5mols_info, handle); + return &info->sd; +} + #endif /* M5MOLS_H */ -- cgit v1.2.3 From 3c5da0baaada8e8c7176a3e310dfdb4362f74d80 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 9 Apr 2012 13:51:56 -0300 Subject: [media] m5mols: Refactored controls handling This patch is a prerequisite for the new controls addition. It consolidates the control handling code, which is moved to m5mols_controls.c and staticized. The controls initialization is reordered to better reflect the control clusters and make the diffs smaller when new controls are added. To make the code easier to follow when more controls is added use separate set function for each control. Rewrite the image effect registers handling. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 24 +-- drivers/media/video/m5mols/m5mols_capture.c | 4 +- drivers/media/video/m5mols/m5mols_controls.c | 227 ++++++++++++++++++++------- drivers/media/video/m5mols/m5mols_core.c | 93 ++--------- 4 files changed, 198 insertions(+), 150 deletions(-) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 0acc3d69b851..ed75bbeddc15 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -160,12 +160,12 @@ struct m5mols_version { * @irq_waitq: waitqueue for the capture * @flags: state variable for the interrupt handler * @handle: control handler - * @autoexposure: Auto Exposure control - * @exposure: Exposure control + * @auto_exposure: auto/manual exposure control + * @exposure: manual exposure control * @autowb: Auto White Balance control - * @colorfx: Color effect control - * @saturation: Saturation control - * @zoom: Zoom control + * @colorfx: color effect control + * @saturation: saturation control + * @zoom: zoom control * @ver: information of the version * @cap: the capture mode attributes * @power: current sensor's power status @@ -188,12 +188,13 @@ struct m5mols_info { atomic_t irq_done; struct v4l2_ctrl_handler handle; + struct { + /* exposure/auto-exposure cluster */ + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure; + }; - /* Autoexposure/exposure control cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - - struct v4l2_ctrl *autowb; + struct v4l2_ctrl *auto_wb; struct v4l2_ctrl *colorfx; struct v4l2_ctrl *saturation; struct v4l2_ctrl *zoom; @@ -277,7 +278,7 @@ int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, * The available executing order between each modes are as follows: * PARAMETER <---> MONITOR <---> CAPTURE */ -int m5mols_mode(struct m5mols_info *info, u8 mode); +int m5mols_set_mode(struct m5mols_info *info, u8 mode); int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); @@ -286,6 +287,7 @@ int m5mols_start_capture(struct m5mols_info *info); int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); int m5mols_lock_3a(struct m5mols_info *info, bool lock); int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); +int m5mols_init_controls(struct v4l2_subdev *sd); /* The firmware function */ int m5mols_update_fw(struct v4l2_subdev *sd, diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c index ba25e8e2ba4c..4f27aed11722 100644 --- a/drivers/media/video/m5mols/m5mols_capture.c +++ b/drivers/media/video/m5mols/m5mols_capture.c @@ -114,7 +114,7 @@ int m5mols_start_capture(struct m5mols_info *info) * format. The frame capture is initiated during switching from Monitor * to Capture mode. */ - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_restore_controls(info); if (!ret) @@ -124,7 +124,7 @@ int m5mols_start_capture(struct m5mols_info *info) if (!ret) ret = m5mols_lock_3a(info, true); if (!ret) - ret = m5mols_mode(info, REG_CAPTURE); + ret = m5mols_set_mode(info, REG_CAPTURE); if (!ret) /* Wait until a frame is captured to ISP internal memory */ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index d135d20d09cf..464ec0cdfb78 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -169,7 +169,7 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) if (!ret) ret = m5mols_write(sd, AE_ISO, scenemode.iso); if (!ret) - ret = m5mols_mode(info, REG_CAPTURE); + ret = m5mols_set_mode(info, REG_CAPTURE); if (!ret) ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); if (!ret) @@ -181,7 +181,7 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) if (!ret) ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); if (!ret) - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); return ret; } @@ -227,73 +227,194 @@ int m5mols_lock_3a(struct m5mols_info *info, bool lock) return ret; } -/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */ -int m5mols_set_ctrl(struct v4l2_ctrl *ctrl) +/* Set exposure/auto exposure cluster */ +static int m5mols_set_exposure(struct m5mols_info *info, int exposure) +{ + struct v4l2_subdev *sd = &info->sd; + int ret; + + ret = m5mols_lock_ae(info, exposure != V4L2_EXPOSURE_AUTO); + if (ret < 0) + return ret; + + if (exposure == V4L2_EXPOSURE_AUTO) { + ret = m5mols_write(sd, AE_MODE, REG_AE_ALL); + if (ret < 0) + return ret; + } + + if (exposure == V4L2_EXPOSURE_MANUAL) { + ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_MON, + info->exposure->val); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_CAP, + info->exposure->val); + } + + return ret; +} + +static int m5mols_set_white_balance(struct m5mols_info *info, int awb) +{ + int ret; + + ret = m5mols_lock_awb(info, !awb); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, AWB_MODE, awb ? REG_AWB_AUTO : + REG_AWB_PRESET); +} + +static int m5mols_set_saturation(struct m5mols_info *info, int val) +{ + int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); +} + +static int m5mols_set_color_effect(struct m5mols_info *info, int val) +{ + unsigned int m_effect = REG_COLOR_EFFECT_OFF; + unsigned int p_effect = REG_EFFECT_OFF; + unsigned int cfix_r = 0, cfix_b = 0; + struct v4l2_subdev *sd = &info->sd; + int ret = 0; + + switch (val) { + case V4L2_COLORFX_BW: + m_effect = REG_COLOR_EFFECT_ON; + break; + case V4L2_COLORFX_NEGATIVE: + p_effect = REG_EFFECT_NEGA; + break; + case V4L2_COLORFX_EMBOSS: + p_effect = REG_EFFECT_EMBOSS; + break; + case V4L2_COLORFX_SEPIA: + m_effect = REG_COLOR_EFFECT_ON; + cfix_r = REG_CFIXR_SEPIA; + cfix_b = REG_CFIXB_SEPIA; + break; + } + + ret = m5mols_write(sd, PARM_EFFECT, p_effect); + if (!ret) + ret = m5mols_write(sd, MON_EFFECT, m_effect); + + if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { + ret = m5mols_write(sd, MON_CFIXR, cfix_r); + if (!ret) + ret = m5mols_write(sd, MON_CFIXB, cfix_b); + } + + v4l2_dbg(1, m5mols_debug, sd, + "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", + p_effect, m_effect, cfix_r, cfix_b, ret); + + return ret; +} + +static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); struct m5mols_info *info = to_m5mols(sd); + int ispstate = info->mode; int ret; + /* + * If needed, defer restoring the controls until + * the device is fully initialized. + */ + if (!info->isp_ready) { + info->ctrl_sync = 0; + return 0; + } + + ret = m5mols_mode(info, REG_PARAMETER); + if (ret < 0) + return ret; + switch (ctrl->id) { case V4L2_CID_ZOOM_ABSOLUTE: - return m5mols_write(sd, MON_ZOOM, ctrl->val); + ret = m5mols_write(sd, MON_ZOOM, ctrl->val); + break; case V4L2_CID_EXPOSURE_AUTO: - ret = m5mols_lock_ae(info, - ctrl->val == V4L2_EXPOSURE_AUTO ? false : true); - if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO) - ret = m5mols_write(sd, AE_MODE, REG_AE_ALL); - if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) { - int val = info->exposure->val; - ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); - if (!ret) - ret = m5mols_write(sd, AE_MAN_GAIN_MON, val); - if (!ret) - ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val); - } - return ret; + ret = m5mols_set_exposure(info, ctrl->val); + break; case V4L2_CID_AUTO_WHITE_BALANCE: - ret = m5mols_lock_awb(info, ctrl->val ? false : true); - if (!ret) - ret = m5mols_write(sd, AWB_MODE, ctrl->val ? - REG_AWB_AUTO : REG_AWB_PRESET); - return ret; + ret = m5mols_set_white_balance(info, ctrl->val); + break; case V4L2_CID_SATURATION: - ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val); - if (!ret) - ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON); - return ret; + ret = m5mols_set_saturation(info, ctrl->val); + break; case V4L2_CID_COLORFX: - /* - * This control uses two kinds of registers: normal & color. - * The normal effect belongs to category 1, while the color - * one belongs to category 2. - * - * The normal effect uses one register: CAT1_EFFECT. - * The color effect uses three registers: - * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB. - */ - ret = m5mols_write(sd, PARM_EFFECT, - ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA : - ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS : - REG_EFFECT_OFF); - if (!ret) - ret = m5mols_write(sd, MON_EFFECT, - ctrl->val == V4L2_COLORFX_SEPIA ? - REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF); - if (!ret) - ret = m5mols_write(sd, MON_CFIXR, - ctrl->val == V4L2_COLORFX_SEPIA ? - REG_CFIXR_SEPIA : 0); - if (!ret) - ret = m5mols_write(sd, MON_CFIXB, - ctrl->val == V4L2_COLORFX_SEPIA ? - REG_CFIXB_SEPIA : 0); + ret = m5mols_set_color_effect(info, ctrl->val); + break; + } + if (ret < 0) + return ret; + + return m5mols_mode(info, ispstate); +} + +static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { + .s_ctrl = m5mols_s_ctrl, +}; + +int m5mols_init_controls(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + u16 exposure_max; + u16 zoom_step; + int ret; + + /* Determine the firmware dependant control range and step values */ + ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); + if (ret < 0) + return ret; + + zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; + + v4l2_ctrl_handler_init(&info->handle, 6); + + info->auto_wb = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + + info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, + 1, ~0x03, V4L2_EXPOSURE_AUTO); + + info->exposure = v4l2_ctrl_new_std(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, + 0, exposure_max, 1, exposure_max / 2); + + info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_SATURATION, 1, 5, 1, 3); + + info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); + + info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); + + if (info->handle.error) { + int ret = info->handle.error; + v4l2_err(sd, "Failed to initialize controls: %d\n", ret); + v4l2_ctrl_handler_free(&info->handle); return ret; } - return -EINVAL; + v4l2_ctrl_auto_cluster(2, &info->auto_exposure, 1, false); + sd->ctrl_handler = &info->handle; + + return 0; } diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c index d718aee01c77..ac7d28b6ddf2 100644 --- a/drivers/media/video/m5mols/m5mols_core.c +++ b/drivers/media/video/m5mols/m5mols_core.c @@ -362,14 +362,14 @@ static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) } /** - * m5mols_mode - manage the M-5MOLS's mode + * m5mols_set_mode - set the M-5MOLS controller mode * @mode: the required operation mode * * The commands of M-5MOLS are grouped into specific modes. Each functionality - * can be guaranteed only when the sensor is operating in mode which which - * a command belongs to. + * can be guaranteed only when the sensor is operating in mode which a command + * belongs to. */ -int m5mols_mode(struct m5mols_info *info, u8 mode) +int m5mols_set_mode(struct m5mols_info *info, u8 mode) { struct v4l2_subdev *sd = &info->sd; int ret = -EINVAL; @@ -645,13 +645,13 @@ static int m5mols_start_monitor(struct m5mols_info *info) struct v4l2_subdev *sd = &info->sd; int ret; - ret = m5mols_mode(info, REG_PARAMETER); + ret = m5mols_set_mode(info, REG_PARAMETER); if (!ret) ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); if (!ret) ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); if (!ret) - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_restore_controls(info); @@ -674,42 +674,13 @@ static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) return ret; } - return m5mols_mode(info, REG_PARAMETER); + return m5mols_set_mode(info, REG_PARAMETER); } static const struct v4l2_subdev_video_ops m5mols_video_ops = { .s_stream = m5mols_s_stream, }; -static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct m5mols_info *info = to_m5mols(sd); - int ispstate = info->mode; - int ret; - - /* - * If needed, defer restoring the controls until - * the device is fully initialized. - */ - if (!info->isp_ready) { - info->ctrl_sync = 0; - return 0; - } - - ret = m5mols_mode(info, REG_PARAMETER); - if (ret < 0) - return ret; - ret = m5mols_set_ctrl(ctrl); - if (ret < 0) - return ret; - return m5mols_mode(info, ispstate); -} - -static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { - .s_ctrl = m5mols_s_ctrl, -}; - static int m5mols_sensor_power(struct m5mols_info *info, bool enable) { struct v4l2_subdev *sd = &info->sd; @@ -802,52 +773,6 @@ static int m5mols_fw_start(struct v4l2_subdev *sd) return ret; } -static int m5mols_init_controls(struct m5mols_info *info) -{ - struct v4l2_subdev *sd = &info->sd; - u16 max_exposure; - u16 step_zoom; - int ret; - - /* Determine value's range & step of controls for various FW version */ - ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &max_exposure); - if (!ret) - step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; - if (ret) - return ret; - - v4l2_ctrl_handler_init(&info->handle, 6); - info->autowb = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, - 0, 1, 1, 0); - info->saturation = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_SATURATION, - 1, 5, 1, 3); - info->zoom = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE, - 1, 70, step_zoom, 1); - info->exposure = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, - 0, max_exposure, 1, (int)max_exposure/2); - info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_COLORFX, - 4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE); - info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, - 1, 0, V4L2_EXPOSURE_AUTO); - - sd->ctrl_handler = &info->handle; - if (info->handle.error) { - v4l2_err(sd, "Failed to initialize controls: %d\n", ret); - v4l2_ctrl_handler_free(&info->handle); - return info->handle.error; - } - - v4l2_ctrl_cluster(2, &info->autoexposure); - - return 0; -} - /** * m5mols_s_power - Main sensor power control function * @@ -868,7 +793,7 @@ static int m5mols_s_power(struct v4l2_subdev *sd, int on) } if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); if (!ret) @@ -1010,7 +935,7 @@ static int __devinit m5mols_probe(struct i2c_client *client, ret = m5mols_fw_start(sd); if (!ret) - ret = m5mols_init_controls(info); + ret = m5mols_init_controls(sd); m5mols_sensor_power(info, false); if (!ret) -- cgit v1.2.3 From 0373e97496793fd91cd7fb1a26840ee43d70f77e Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 9 Apr 2012 13:52:06 -0300 Subject: [media] m5mols: Use proper sensor mode for the controls The parameters corresponding to the V4L controls can be reconfigured only in associated M-5MOLS operation mode. Use struct v4l2_ctrl priv field to assign the working mode to the controls which can be modified only in certain conditions. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 11 +++++++++++ drivers/media/video/m5mols/m5mols_controls.c | 28 ++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index ed75bbeddc15..87684c47d372 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -305,4 +305,15 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) return &info->sd; } +static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, + unsigned int mode) +{ + ctrl->priv = (void *)mode; +} + +static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) +{ + return (unsigned int)ctrl->priv; +} + #endif /* M5MOLS_H */ diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index 464ec0cdfb78..477660132c01 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -321,10 +321,11 @@ static int m5mols_set_color_effect(struct m5mols_info *info, int val) static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) { + unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); struct v4l2_subdev *sd = to_sd(ctrl); struct m5mols_info *info = to_m5mols(sd); - int ispstate = info->mode; - int ret; + int last_mode = info->mode; + int ret = 0; /* * If needed, defer restoring the controls until @@ -335,9 +336,14 @@ static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } - ret = m5mols_mode(info, REG_PARAMETER); - if (ret < 0) - return ret; + v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %#x\n", + __func__, ctrl->name, ctrl->val, (int)ctrl->priv); + + if (ctrl_mode && ctrl_mode != info->mode) { + ret = m5mols_set_mode(info, ctrl_mode); + if (ret < 0) + return ret; + } switch (ctrl->id) { case V4L2_CID_ZOOM_ABSOLUTE: @@ -360,10 +366,11 @@ static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) ret = m5mols_set_color_effect(info, ctrl->val); break; } - if (ret < 0) - return ret; - return m5mols_mode(info, ispstate); + if (ret == 0 && info->mode != last_mode) + ret = m5mols_set_mode(info, last_mode); + + return ret; } static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { @@ -414,6 +421,11 @@ int m5mols_init_controls(struct v4l2_subdev *sd) } v4l2_ctrl_auto_cluster(2, &info->auto_exposure, 1, false); + + m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); + m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); + m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); + sd->ctrl_handler = &info->handle; return 0; -- cgit v1.2.3 From 44b153ca639f7299a94c27fc48708bbbeccf5050 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 9 Apr 2012 13:19:25 -0300 Subject: [media] m5mols: Add ISO sensitivity controls Add controls for manual/auto ISO sensitivity. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 7 ++++ drivers/media/video/m5mols/m5mols_controls.c | 56 ++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 87684c47d372..598bb0fe6036 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -162,6 +162,8 @@ struct m5mols_version { * @handle: control handler * @auto_exposure: auto/manual exposure control * @exposure: manual exposure control + * @auto_iso: auto/manual ISO sensitivity control + * @iso: manual ISO sensitivity control * @autowb: Auto White Balance control * @colorfx: color effect control * @saturation: saturation control @@ -193,6 +195,11 @@ struct m5mols_info { struct v4l2_ctrl *auto_exposure; struct v4l2_ctrl *exposure; }; + struct { + /* iso/auto iso cluster */ + struct v4l2_ctrl *auto_iso; + struct v4l2_ctrl *iso; + }; struct v4l2_ctrl *auto_wb; struct v4l2_ctrl *colorfx; diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index 477660132c01..f1c88507de4b 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -319,6 +319,39 @@ static int m5mols_set_color_effect(struct m5mols_info *info, int val) return ret; } +static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) +{ + u32 iso = auto_iso ? 0 : info->iso->val + 1; + + return m5mols_write(&info->sd, AE_ISO, iso); +} + +static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct m5mols_info *info = to_m5mols(sd); + int ret = 0; + u8 status; + + v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", + __func__, ctrl->name, info->isp_ready); + + if (!info->isp_ready) + return -EBUSY; + + switch (ctrl->id) { + case V4L2_CID_ISO_SENSITIVITY_AUTO: + ret = m5mols_read_u8(sd, AE_ISO, &status); + if (ret == 0) + ctrl->val = !status; + if (status != REG_ISO_AUTO) + info->iso->val = status - 1; + break; + } + + return ret; +} + static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) { unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); @@ -354,6 +387,10 @@ static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) ret = m5mols_set_exposure(info, ctrl->val); break; + case V4L2_CID_ISO_SENSITIVITY: + ret = m5mols_set_iso(info, ctrl->val); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: ret = m5mols_set_white_balance(info, ctrl->val); break; @@ -374,9 +411,16 @@ static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) } static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { + .g_volatile_ctrl = m5mols_g_volatile_ctrl, .s_ctrl = m5mols_s_ctrl, }; +/* Supported manual ISO values */ +static const s64 iso_qmenu[] = { + /* AE_ISO: 0x01...0x07 */ + 50, 100, 200, 400, 800, 1600, 3200 +}; + int m5mols_init_controls(struct v4l2_subdev *sd) { struct m5mols_info *info = to_m5mols(sd); @@ -404,6 +448,14 @@ int m5mols_init_controls(struct v4l2_subdev *sd) &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, 0, exposure_max, 1, exposure_max / 2); + /* ISO control cluster */ + info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); + + info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, + ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); + info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, V4L2_CID_SATURATION, 1, 5, 1, 3); @@ -422,6 +474,10 @@ int m5mols_init_controls(struct v4l2_subdev *sd) v4l2_ctrl_auto_cluster(2, &info->auto_exposure, 1, false); + info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_UPDATE; + v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); + m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); -- cgit v1.2.3 From d7a87e4cc39f7ee4f6d1a1a8b8fffc10a7b0c1e9 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 8 May 2012 06:56:35 -0300 Subject: [media] m5mols: Add auto and preset white balance control Replace the V4L2_CID_AUTO_WHITE_BALANCE control with its extended version - V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE so the white balance presets feature is exposed to user land. Acked-by: HeungJun Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 4 +-- drivers/media/video/m5mols/m5mols_controls.c | 47 ++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 598bb0fe6036..fa1f2c145852 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -164,7 +164,7 @@ struct m5mols_version { * @exposure: manual exposure control * @auto_iso: auto/manual ISO sensitivity control * @iso: manual ISO sensitivity control - * @autowb: Auto White Balance control + * @auto_wb: auto white balance control * @colorfx: color effect control * @saturation: saturation control * @zoom: zoom control @@ -200,8 +200,8 @@ struct m5mols_info { struct v4l2_ctrl *auto_iso; struct v4l2_ctrl *iso; }; - struct v4l2_ctrl *auto_wb; + struct v4l2_ctrl *colorfx; struct v4l2_ctrl *saturation; struct v4l2_ctrl *zoom; diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index f1c88507de4b..1ab21f0afa8a 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -256,16 +256,42 @@ static int m5mols_set_exposure(struct m5mols_info *info, int exposure) return ret; } -static int m5mols_set_white_balance(struct m5mols_info *info, int awb) +static int m5mols_set_white_balance(struct m5mols_info *info, int val) { - int ret; + static const unsigned short wb[][2] = { + { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, + { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, + { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, + { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, + { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, + { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, + { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, + { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, + { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, + }; + int i; + struct v4l2_subdev *sd = &info->sd; + int ret = -EINVAL; - ret = m5mols_lock_awb(info, !awb); - if (ret < 0) - return ret; + for (i = 0; i < ARRAY_SIZE(wb); i++) { + int awb; + if (wb[i][0] != val) + continue; + + v4l2_dbg(1, m5mols_debug, sd, + "Setting white balance to: %#x\n", wb[i][0]); - return m5mols_write(&info->sd, AWB_MODE, awb ? REG_AWB_AUTO : - REG_AWB_PRESET); + awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; + ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : + REG_AWB_PRESET); + if (ret < 0) + return ret; + + if (!awb) + ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); + } + + return ret; } static int m5mols_set_saturation(struct m5mols_info *info, int val) @@ -391,7 +417,7 @@ static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) ret = m5mols_set_iso(info, ctrl->val); break; - case V4L2_CID_AUTO_WHITE_BALANCE: + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: ret = m5mols_set_white_balance(info, ctrl->val); break; @@ -437,8 +463,9 @@ int m5mols_init_controls(struct v4l2_subdev *sd) v4l2_ctrl_handler_init(&info->handle, 6); - info->auto_wb = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, -- cgit v1.2.3 From 48311db6768040970d4a28faab17583b5177af84 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 6 Mar 2012 07:10:00 -0300 Subject: [media] m5mols: Add exposure bias control Add integer menu control for exposure bias. The control value range is -2.0 EV to +2.0 EV, in 0.5 EV steps. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 4 +++- drivers/media/video/m5mols/m5mols_controls.c | 24 ++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index fa1f2c145852..03421fc1a965 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -161,6 +161,7 @@ struct m5mols_version { * @flags: state variable for the interrupt handler * @handle: control handler * @auto_exposure: auto/manual exposure control + * @exposure_bias: exposure compensation control * @exposure: manual exposure control * @auto_iso: auto/manual ISO sensitivity control * @iso: manual ISO sensitivity control @@ -191,8 +192,9 @@ struct m5mols_info { struct v4l2_ctrl_handler handle; struct { - /* exposure/auto-exposure cluster */ + /* exposure/exposure bias/auto exposure cluster */ struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure_bias; struct v4l2_ctrl *exposure; }; struct { diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index 1ab21f0afa8a..797836315225 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -241,6 +241,11 @@ static int m5mols_set_exposure(struct m5mols_info *info, int exposure) ret = m5mols_write(sd, AE_MODE, REG_AE_ALL); if (ret < 0) return ret; + + v4l2_dbg(1, m5mols_debug, sd, "%s: exposure bias: %#x\n", + __func__, info->exposure_bias->val); + + return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); } if (exposure == V4L2_EXPOSURE_MANUAL) { @@ -251,6 +256,9 @@ static int m5mols_set_exposure(struct m5mols_info *info, int exposure) if (ret == 0) ret = m5mols_write(sd, AE_MAN_GAIN_CAP, info->exposure->val); + + v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", + __func__, info->exposure->val); } return ret; @@ -447,6 +455,12 @@ static const s64 iso_qmenu[] = { 50, 100, 200, 400, 800, 1600, 3200 }; +/* Supported Exposure Bias values, -2.0EV...+2.0EV */ +static const s64 ev_bias_qmenu[] = { + /* AE_INDEX: 0x00...0x08 */ + -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 +}; + int m5mols_init_controls(struct v4l2_subdev *sd) { struct m5mols_info *info = to_m5mols(sd); @@ -467,6 +481,7 @@ int m5mols_init_controls(struct v4l2_subdev *sd) &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); + /* Exposure control cluster */ info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, ~0x03, V4L2_EXPOSURE_AUTO); @@ -475,6 +490,12 @@ int m5mols_init_controls(struct v4l2_subdev *sd) &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, 0, exposure_max, 1, exposure_max / 2); + info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(ev_bias_qmenu) - 1, + ARRAY_SIZE(ev_bias_qmenu)/2 - 1, + ev_bias_qmenu); + /* ISO control cluster */ info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); @@ -499,8 +520,7 @@ int m5mols_init_controls(struct v4l2_subdev *sd) return ret; } - v4l2_ctrl_auto_cluster(2, &info->auto_exposure, 1, false); - + v4l2_ctrl_auto_cluster(3, &info->auto_exposure, 1, false); info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_UPDATE; v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); -- cgit v1.2.3 From 4eb3419d401579b262f61d4d03cf10598ab0e2a2 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 6 Mar 2012 07:10:13 -0300 Subject: [media] m5mols: Add wide dynamic range control Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 2 ++ drivers/media/video/m5mols/m5mols_controls.c | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 03421fc1a965..978a4abd3722 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -169,6 +169,7 @@ struct m5mols_version { * @colorfx: color effect control * @saturation: saturation control * @zoom: zoom control + * @wdr: wide dynamic range control * @ver: information of the version * @cap: the capture mode attributes * @power: current sensor's power status @@ -207,6 +208,7 @@ struct m5mols_info { struct v4l2_ctrl *colorfx; struct v4l2_ctrl *saturation; struct v4l2_ctrl *zoom; + struct v4l2_ctrl *wdr; struct m5mols_version ver; struct m5mols_capture cap; diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index 797836315225..512c3603b280 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -360,6 +360,21 @@ static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) return m5mols_write(&info->sd, AE_ISO, iso); } +static int m5mols_set_wdr(struct m5mols_info *info, int wdr) +{ + int ret; + + ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); + if (ret < 0) + return ret; + + ret = m5mols_set_mode(info, REG_CAPTURE); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); +} + static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); @@ -436,6 +451,10 @@ static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_COLORFX: ret = m5mols_set_color_effect(info, ctrl->val); break; + + case V4L2_CID_WIDE_DYNAMIC_RANGE: + ret = m5mols_set_wdr(info, ctrl->val); + break; } if (ret == 0 && info->mode != last_mode) @@ -513,6 +532,9 @@ int m5mols_init_controls(struct v4l2_subdev *sd) info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); + info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); + if (info->handle.error) { int ret = info->handle.error; v4l2_err(sd, "Failed to initialize controls: %d\n", ret); -- cgit v1.2.3 From efcb07c1bbd43846aff192f28ad755fc8c93ad81 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 6 Mar 2012 07:10:27 -0300 Subject: [media] m5mols: Add image stabilization control Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 2 ++ drivers/media/video/m5mols/m5mols_controls.c | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 978a4abd3722..305a75bc6a9f 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -170,6 +170,7 @@ struct m5mols_version { * @saturation: saturation control * @zoom: zoom control * @wdr: wide dynamic range control + * @stabilization: image stabilization control * @ver: information of the version * @cap: the capture mode attributes * @power: current sensor's power status @@ -209,6 +210,7 @@ struct m5mols_info { struct v4l2_ctrl *saturation; struct v4l2_ctrl *zoom; struct v4l2_ctrl *wdr; + struct v4l2_ctrl *stabilization; struct m5mols_version ver; struct m5mols_capture cap; diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index 512c3603b280..6c607d467c6e 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -375,6 +375,19 @@ static int m5mols_set_wdr(struct m5mols_info *info, int wdr) return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); } +static int m5mols_set_stabilization(struct m5mols_info *info, int val) +{ + struct v4l2_subdev *sd = &info->sd; + unsigned int evp = val ? 0xe : 0x0; + int ret; + + ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); + if (ret < 0) + return ret; + + return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); +} + static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); @@ -455,6 +468,10 @@ static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_WIDE_DYNAMIC_RANGE: ret = m5mols_set_wdr(info, ctrl->val); break; + + case V4L2_CID_IMAGE_STABILIZATION: + ret = m5mols_set_stabilization(info, ctrl->val); + break; } if (ret == 0 && info->mode != last_mode) @@ -535,6 +552,9 @@ int m5mols_init_controls(struct v4l2_subdev *sd) info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); + info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); + if (info->handle.error) { int ret = info->handle.error; v4l2_err(sd, "Failed to initialize controls: %d\n", ret); -- cgit v1.2.3 From 50d3f93e39d86d57af5df5f06c5b18ffe25ece79 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 9 Apr 2012 14:51:49 -0300 Subject: [media] m5mols: Add exposure metering control This patch adds V4L2_CID_EXPOSURE_METERING control which allows to select the light metering mode for automatic exposure as one of the following modes: spot (small area at the frame center), center weighted and frame averaged. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 2 ++ drivers/media/video/m5mols/m5mols_controls.c | 34 ++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 305a75bc6a9f..b7834654dc6c 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -163,6 +163,7 @@ struct m5mols_version { * @auto_exposure: auto/manual exposure control * @exposure_bias: exposure compensation control * @exposure: manual exposure control + * @metering: exposure metering control * @auto_iso: auto/manual ISO sensitivity control * @iso: manual ISO sensitivity control * @auto_wb: auto white balance control @@ -198,6 +199,7 @@ struct m5mols_info { struct v4l2_ctrl *auto_exposure; struct v4l2_ctrl *exposure_bias; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *metering; }; struct { /* iso/auto iso cluster */ diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index 6c607d467c6e..ebc3a904b960 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -227,7 +227,25 @@ int m5mols_lock_3a(struct m5mols_info *info, bool lock) return ret; } -/* Set exposure/auto exposure cluster */ +static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) +{ + unsigned int metering; + + switch (mode) { + case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: + metering = REG_AE_CENTER; + break; + case V4L2_EXPOSURE_METERING_SPOT: + metering = REG_AE_SPOT; + break; + default: + metering = REG_AE_ALL; + break; + } + + return m5mols_write(&info->sd, AE_MODE, metering); +} + static int m5mols_set_exposure(struct m5mols_info *info, int exposure) { struct v4l2_subdev *sd = &info->sd; @@ -238,12 +256,14 @@ static int m5mols_set_exposure(struct m5mols_info *info, int exposure) return ret; if (exposure == V4L2_EXPOSURE_AUTO) { - ret = m5mols_write(sd, AE_MODE, REG_AE_ALL); + ret = m5mols_set_metering_mode(info, info->metering->val); if (ret < 0) return ret; - v4l2_dbg(1, m5mols_debug, sd, "%s: exposure bias: %#x\n", - __func__, info->exposure_bias->val); + v4l2_dbg(1, m5mols_debug, sd, + "%s: exposure bias: %#x, metering: %#x\n", + __func__, info->exposure_bias->val, + info->metering->val); return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); } @@ -532,6 +552,10 @@ int m5mols_init_controls(struct v4l2_subdev *sd) ARRAY_SIZE(ev_bias_qmenu)/2 - 1, ev_bias_qmenu); + info->metering = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, + 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); + /* ISO control cluster */ info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); @@ -562,7 +586,7 @@ int m5mols_init_controls(struct v4l2_subdev *sd) return ret; } - v4l2_ctrl_auto_cluster(3, &info->auto_exposure, 1, false); + v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_UPDATE; v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); -- cgit v1.2.3 From dd9c471dd37c25127236dd36cd1f55c488228cf1 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 28 Feb 2012 06:29:01 -0300 Subject: [media] m5mols: Add JPEG compression quality control Add JPEG compression quality control for snapshot capture. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 2 ++ drivers/media/video/m5mols/m5mols_controls.c | 10 ++++++++-- drivers/media/video/m5mols/m5mols_reg.h | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index b7834654dc6c..00f8d3162bdc 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -172,6 +172,7 @@ struct m5mols_version { * @zoom: zoom control * @wdr: wide dynamic range control * @stabilization: image stabilization control + * @jpeg_quality: JPEG compression quality control * @ver: information of the version * @cap: the capture mode attributes * @power: current sensor's power status @@ -213,6 +214,7 @@ struct m5mols_info { struct v4l2_ctrl *zoom; struct v4l2_ctrl *wdr; struct v4l2_ctrl *stabilization; + struct v4l2_ctrl *jpeg_quality; struct m5mols_version ver; struct m5mols_capture cap; diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index ebc3a904b960..1c3b1e0a7bba 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -492,6 +492,10 @@ static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_IMAGE_STABILIZATION: ret = m5mols_set_stabilization(info, ctrl->val); break; + + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); + break; } if (ret == 0 && info->mode != last_mode) @@ -530,8 +534,7 @@ int m5mols_init_controls(struct v4l2_subdev *sd) return ret; zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; - - v4l2_ctrl_handler_init(&info->handle, 6); + v4l2_ctrl_handler_init(&info->handle, 20); info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, @@ -579,6 +582,9 @@ int m5mols_init_controls(struct v4l2_subdev *sd) info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); + info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); + if (info->handle.error) { int ret = info->handle.error; v4l2_err(sd, "Failed to initialize controls: %d\n", ret); diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h index ae4aced0f9b2..14d4be72aeff 100644 --- a/drivers/media/video/m5mols/m5mols_reg.h +++ b/drivers/media/video/m5mols/m5mols_reg.h @@ -310,6 +310,7 @@ #define REG_JPEG 0x10 #define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) +#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) #define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) #define REG_MCC_OFF 0x00 -- cgit v1.2.3 From 9346459ae0317ec6a6930b4566417e033f74a2bf Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 9 May 2012 11:53:09 -0300 Subject: [media] m5mols: Add 3A lock control Add control for locking automatic exposure, focus and white balance adjustments. While at it, tidy up the data structure documentation. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/m5mols/m5mols.h | 10 ++-- drivers/media/video/m5mols/m5mols_capture.c | 7 +-- drivers/media/video/m5mols/m5mols_controls.c | 89 +++++++++++++++++----------- 3 files changed, 60 insertions(+), 46 deletions(-) diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 00f8d3162bdc..bb589917b65b 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -158,7 +158,7 @@ struct m5mols_version { * @ffmt: current fmt according to resolution type * @res_type: current resolution type * @irq_waitq: waitqueue for the capture - * @flags: state variable for the interrupt handler + * @irq_done: set to 1 in the interrupt handler * @handle: control handler * @auto_exposure: auto/manual exposure control * @exposure_bias: exposure compensation control @@ -167,6 +167,7 @@ struct m5mols_version { * @auto_iso: auto/manual ISO sensitivity control * @iso: manual ISO sensitivity control * @auto_wb: auto white balance control + * @lock_3a: 3A lock control * @colorfx: color effect control * @saturation: saturation control * @zoom: zoom control @@ -175,11 +176,9 @@ struct m5mols_version { * @jpeg_quality: JPEG compression quality control * @ver: information of the version * @cap: the capture mode attributes - * @power: current sensor's power status * @isp_ready: 1 when the ISP controller has completed booting + * @power: current sensor's power status * @ctrl_sync: 1 when the control handler state is restored in H/W - * @lock_ae: true means the Auto Exposure is locked - * @lock_awb: true means the Aut WhiteBalance is locked * @resolution: register value for current resolution * @mode: register value for current operation mode * @set_power: optional power callback to the board code @@ -209,6 +208,7 @@ struct m5mols_info { }; struct v4l2_ctrl *auto_wb; + struct v4l2_ctrl *lock_3a; struct v4l2_ctrl *colorfx; struct v4l2_ctrl *saturation; struct v4l2_ctrl *zoom; @@ -223,8 +223,6 @@ struct m5mols_info { unsigned int power:1; unsigned int ctrl_sync:1; - bool lock_ae; - bool lock_awb; u8 resolution; u8 mode; diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c index 4f27aed11722..cb243bd278ce 100644 --- a/drivers/media/video/m5mols/m5mols_capture.c +++ b/drivers/media/video/m5mols/m5mols_capture.c @@ -106,7 +106,6 @@ static int m5mols_capture_info(struct m5mols_info *info) int m5mols_start_capture(struct m5mols_info *info) { struct v4l2_subdev *sd = &info->sd; - u8 resolution = info->resolution; int ret; /* @@ -120,16 +119,12 @@ int m5mols_start_capture(struct m5mols_info *info) if (!ret) ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); if (!ret) - ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution); - if (!ret) - ret = m5mols_lock_3a(info, true); + ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); if (!ret) ret = m5mols_set_mode(info, REG_CAPTURE); if (!ret) /* Wait until a frame is captured to ISP internal memory */ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); - if (!ret) - ret = m5mols_lock_3a(info, false); if (ret) return ret; diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index 1c3b1e0a7bba..392a028730e2 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -139,7 +139,7 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) if (mode > REG_SCENE_CANDLE) return -EINVAL; - ret = m5mols_lock_3a(info, false); + ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); if (!ret) ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); if (!ret) @@ -186,42 +186,34 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) return ret; } -static int m5mols_lock_ae(struct m5mols_info *info, bool lock) +static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) { + bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; int ret = 0; - if (info->lock_ae != lock) - ret = m5mols_write(&info->sd, AE_LOCK, - lock ? REG_AE_LOCK : REG_AE_UNLOCK); - if (!ret) - info->lock_ae = lock; + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { + bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; - return ret; -} - -static int m5mols_lock_awb(struct m5mols_info *info, bool lock) -{ - int ret = 0; + ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? + REG_AE_LOCK : REG_AE_UNLOCK); + if (ret) + return ret; + } - if (info->lock_awb != lock) - ret = m5mols_write(&info->sd, AWB_LOCK, - lock ? REG_AWB_LOCK : REG_AWB_UNLOCK); - if (!ret) - info->lock_awb = lock; + if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) + && info->auto_wb->val) { + bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; - return ret; -} + ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? + REG_AWB_LOCK : REG_AWB_UNLOCK); + if (ret) + return ret; + } -/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */ -int m5mols_lock_3a(struct m5mols_info *info, bool lock) -{ - int ret; + if (!info->ver.af || !af_lock) + return ret; - ret = m5mols_lock_ae(info, lock); - if (!ret) - ret = m5mols_lock_awb(info, lock); - /* Don't need to handle unlocking AF */ - if (!ret && is_available_af(info) && lock) + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); return ret; @@ -249,13 +241,13 @@ static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) static int m5mols_set_exposure(struct m5mols_info *info, int exposure) { struct v4l2_subdev *sd = &info->sd; - int ret; - - ret = m5mols_lock_ae(info, exposure != V4L2_EXPOSURE_AUTO); - if (ret < 0) - return ret; + int ret = 0; if (exposure == V4L2_EXPOSURE_AUTO) { + /* Unlock auto exposure */ + info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; + m5mols_3a_lock(info, info->lock_3a); + ret = m5mols_set_metering_mode(info, info->metering->val); if (ret < 0) return ret; @@ -429,6 +421,26 @@ static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) if (status != REG_ISO_AUTO) info->iso->val = status - 1; break; + + case V4L2_CID_3A_LOCK: + ctrl->val &= ~0x7; + + ret = m5mols_read_u8(sd, AE_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AWB_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AF_EXECUTE, &status); + if (!status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + break; } return ret; @@ -461,6 +473,10 @@ static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) } switch (ctrl->id) { + case V4L2_CID_3A_LOCK: + ret = m5mols_3a_lock(info, ctrl); + break; + case V4L2_CID_ZOOM_ABSOLUTE: ret = m5mols_write(sd, MON_ZOOM, ctrl->val); break; @@ -585,6 +601,9 @@ int m5mols_init_controls(struct v4l2_subdev *sd) info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); + info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); + if (info->handle.error) { int ret = info->handle.error; v4l2_err(sd, "Failed to initialize controls: %d\n", ret); @@ -597,6 +616,8 @@ int m5mols_init_controls(struct v4l2_subdev *sd) V4L2_CTRL_FLAG_UPDATE; v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); + info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; + m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); -- cgit v1.2.3 From d51dbecc85618a75ec0c76dad27c45d9b8d64c66 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Apr 2012 03:30:11 -0300 Subject: [media] dw2102: fix compile warnings media_build/v4l/dw2102.c:151:13: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/dw2102.c:224:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/dw2102.c:279:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/dw2102.c:352:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/dw2102.c:435:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/dw2102.c:499:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dw2102.c | 76 ++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 451c5a7adfb2..9382895b1b88 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -148,7 +148,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int i = 0, ret = 0; + int i = 0; u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0}; u16 value; @@ -162,7 +162,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], /* read stv0299 register */ value = msg[0].buf[0];/* register */ for (i = 0; i < msg[1].len; i++) { - ret = dw210x_op_rw(d->udev, 0xb5, value + i, 0, + dw210x_op_rw(d->udev, 0xb5, value + i, 0, buf6, 2, DW210X_READ_MSG); msg[1].buf[i] = buf6[0]; } @@ -174,7 +174,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], buf6[0] = 0x2a; buf6[1] = msg[0].buf[0]; buf6[2] = msg[0].buf[1]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, buf6, 3, DW210X_WRITE_MSG); break; case 0x60: @@ -187,17 +187,17 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], buf6[4] = msg[0].buf[1]; buf6[5] = msg[0].buf[2]; buf6[6] = msg[0].buf[3]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, buf6, 7, DW210X_WRITE_MSG); } else { /* read from tuner */ - ret = dw210x_op_rw(d->udev, 0xb5, 0, 0, + dw210x_op_rw(d->udev, 0xb5, 0, 0, buf6, 1, DW210X_READ_MSG); msg[0].buf[0] = buf6[0]; } break; case (DW2102_RC_QUERY): - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, buf6, 2, DW210X_READ_MSG); msg[0].buf[0] = buf6[0]; msg[0].buf[1] = buf6[1]; @@ -205,7 +205,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], case (DW2102_VOLTAGE_CTRL): buf6[0] = 0x30; buf6[1] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, buf6, 2, DW210X_WRITE_MSG); break; } @@ -221,7 +221,6 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int ret = 0; u8 buf6[] = {0, 0, 0, 0, 0, 0, 0}; if (!d) @@ -235,10 +234,10 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, buf6[0] = msg[0].addr << 1; buf6[1] = msg[0].len; buf6[2] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, msg[0].len + 2, DW210X_WRITE_MSG); /* read si2109 register */ - ret = dw210x_op_rw(d->udev, 0xc3, 0xd0, 0, + dw210x_op_rw(d->udev, 0xc3, 0xd0, 0, buf6, msg[1].len + 2, DW210X_READ_MSG); memcpy(msg[1].buf, buf6 + 2, msg[1].len); @@ -250,11 +249,11 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, buf6[0] = msg[0].addr << 1; buf6[1] = msg[0].len; memcpy(buf6 + 2, msg[0].buf, msg[0].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, + dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, msg[0].len + 2, DW210X_WRITE_MSG); break; case(DW2102_RC_QUERY): - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, buf6, 2, DW210X_READ_MSG); msg[0].buf[0] = buf6[0]; msg[0].buf[1] = buf6[1]; @@ -262,7 +261,7 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, case(DW2102_VOLTAGE_CTRL): buf6[0] = 0x30; buf6[1] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, buf6, 2, DW210X_WRITE_MSG); break; } @@ -276,7 +275,6 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int ret = 0; if (!d) return -ENODEV; @@ -291,10 +289,10 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; obuf[2] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); /* second read registers */ - ret = dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0, + dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0, ibuf, msg[1].len + 2, DW210X_READ_MSG); memcpy(msg[1].buf, ibuf + 2, msg[1].len); @@ -308,7 +306,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; memcpy(obuf + 2, msg[0].buf, msg[0].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); break; } @@ -318,13 +316,13 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; memcpy(obuf + 2, msg[0].buf, msg[0].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); break; } case(DW2102_RC_QUERY): { u8 ibuf[2]; - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, ibuf, 2, DW210X_READ_MSG); memcpy(msg[0].buf, ibuf , 2); break; @@ -333,7 +331,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms u8 obuf[2]; obuf[0] = 0x30; obuf[1] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, obuf, 2, DW210X_WRITE_MSG); break; } @@ -349,7 +347,6 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int ret = 0; int len, i, j; if (!d) @@ -361,7 +358,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i switch (msg[j].addr) { case(DW2102_RC_QUERY): { u8 ibuf[2]; - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, ibuf, 2, DW210X_READ_MSG); memcpy(msg[j].buf, ibuf , 2); break; @@ -370,7 +367,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i u8 obuf[2]; obuf[0] = 0x30; obuf[1] = msg[j].buf[0]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, obuf, 2, DW210X_WRITE_MSG); break; } @@ -382,7 +379,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i if (msg[j].flags == I2C_M_RD) { /* read registers */ u8 ibuf[msg[j].len + 2]; - ret = dw210x_op_rw(d->udev, 0xc3, + dw210x_op_rw(d->udev, 0xc3, (msg[j].addr << 1) + 1, 0, ibuf, msg[j].len + 2, DW210X_READ_MSG); @@ -402,7 +399,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i do { memcpy(obuf + 3, msg[j].buf + i, (len > 16 ? 16 : len)); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG); i += 16; @@ -414,7 +411,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i obuf[0] = msg[j].addr << 1; obuf[1] = msg[j].len; memcpy(obuf + 2, msg[j].buf, msg[j].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[j].len + 2, DW210X_WRITE_MSG); } @@ -432,7 +429,7 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int ret = 0, i; + int i; if (!d) return -ENODEV; @@ -447,10 +444,10 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; obuf[2] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); /* second read registers */ - ret = dw210x_op_rw(d->udev, 0xc3, 0x19 , 0, + dw210x_op_rw(d->udev, 0xc3, 0x19 , 0, ibuf, msg[1].len + 2, DW210X_READ_MSG); memcpy(msg[1].buf, ibuf + 2, msg[1].len); @@ -465,13 +462,13 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; memcpy(obuf + 2, msg[0].buf, msg[0].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); break; } case(DW2102_RC_QUERY): { u8 ibuf[2]; - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, ibuf, 2, DW210X_READ_MSG); memcpy(msg[0].buf, ibuf , 2); break; @@ -496,7 +493,6 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], { struct dvb_usb_device *d = i2c_get_adapdata(adap); struct usb_device *udev; - int ret = 0; int len, i, j; if (!d) @@ -509,7 +505,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], switch (msg[j].addr) { case (DW2102_RC_QUERY): { u8 ibuf[5]; - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, ibuf, 5, DW210X_READ_MSG); memcpy(msg[j].buf, ibuf + 3, 2); break; @@ -519,11 +515,11 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = 1; obuf[1] = msg[j].buf[1];/* off-on */ - ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, + dw210x_op_rw(d->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); obuf[0] = 3; obuf[1] = msg[j].buf[0];/* 13v-18v */ - ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, + dw210x_op_rw(d->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); break; } @@ -532,7 +528,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = 5; obuf[1] = msg[j].buf[0]; - ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, + dw210x_op_rw(d->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); break; } @@ -545,7 +541,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (msg[j].flags == I2C_M_RD) { /* read registers */ u8 ibuf[msg[j].len]; - ret = dw210x_op_rw(d->udev, 0x91, 0, 0, + dw210x_op_rw(d->udev, 0x91, 0, 0, ibuf, msg[j].len, DW210X_READ_MSG); memcpy(msg[j].buf, ibuf, msg[j].len); @@ -563,7 +559,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], do { memcpy(obuf + 3, msg[j].buf + i, (len > 16 ? 16 : len)); - ret = dw210x_op_rw(d->udev, 0x80, 0, 0, + dw210x_op_rw(d->udev, 0x80, 0, 0, obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG); i += 16; @@ -575,7 +571,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = msg[j + 1].len; obuf[1] = (msg[j].addr << 1); memcpy(obuf + 2, msg[j].buf, msg[j].len); - ret = dw210x_op_rw(d->udev, + dw210x_op_rw(d->udev, udev->descriptor.idProduct == 0x7500 ? 0x92 : 0x90, 0, 0, obuf, msg[j].len + 2, @@ -587,7 +583,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = msg[j].len + 1; obuf[1] = (msg[j].addr << 1); memcpy(obuf + 2, msg[j].buf, msg[j].len); - ret = dw210x_op_rw(d->udev, 0x80, 0, 0, + dw210x_op_rw(d->udev, 0x80, 0, 0, obuf, msg[j].len + 2, DW210X_WRITE_MSG); break; -- cgit v1.2.3 From da983503ba485e3ef50662a65d0d03e80a586387 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 May 2012 10:14:53 -0300 Subject: [media] cx231xx: fix compiler warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-audio.c: In function ‘snd_cx231xx_hw_capture_params’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-audio.c:527:6: warning: variable ‘ret’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-audio.c:526:31: warning: variable ‘format’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-audio.c:526:25: warning: variable ‘rate’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-audio.c:526:15: warning: variable ‘channels’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-audio.c: In function ‘snd_cx231xx_capture_trigger’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-audio.c:589:6: warning: variable ‘retval’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-video.c: In function ‘cx231xx_isoc_copy’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-video.c:331:17: warning: variable ‘outp’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-video.c: In function ‘cx231xx_bulk_copy’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-video.c:434:17: warning: variable ‘outp’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-video.c: In function ‘cx231xx_reset_video_buffer’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-video.c:704:7: warning: variable ‘outp’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c: In function ‘cx231xx_set_mode’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c:699:6: warning: variable ‘errCode’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c: In function ‘cx231xx_ep5_bulkout’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c:763:6: warning: variable ‘errCode’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c: In function ‘cx231xx_isoc_irq_callback’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c:800:6: warning: variable ‘rc’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c: In function ‘cx231xx_bulk_irq_callback’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c:846:6: warning: variable ‘rc’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c: In function ‘cx231xx_stop_TS1’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c:1234:6: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c: In function ‘cx231xx_start_TS1’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-core.c:1254:6: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c: In function ‘cx231xx_enable656’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c:937:6: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c: In function ‘cx231xx_disable656’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c:955:6: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c: In function ‘cx231xx_dump_HH_reg’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c:1323:5: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c: In function ‘cx231xx_dump_SC_reg’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c:1358:6: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c: In function ‘cx231xx_Setup_AFE_for_LowIF’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c:1444:5: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c: In function ‘cx231xx_set_Colibri_For_LowIF’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c:1504:5: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c: In function ‘cx231xx_set_DIF_bandpass’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c:1559:6: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c: In function ‘cx231xx_gpio_i2c_write’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-avcore.c:3093:6: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-417.c: In function ‘cx231xx_initialize_codec’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-417.c:1098:9: warning: variable ‘data’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-417.c: In function ‘vidioc_streamon’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-417.c:1795:6: warning: variable ‘rc’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-vbi.c: In function ‘cx231xx_isoc_vbi_copy’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-vbi.c:86:25: warning: variable ‘buf’ set but not used [-Wunused-but-set-variable] v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-vbi.c: In function ‘cx231xx_irq_vbi_callback’: v4l-dvb-git/drivers/media/video/cx231xx/cx231xx-vbi.c:313:6: warning: variable ‘rc’ set but not used [-Wunused-but-set-variable] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx231xx/cx231xx-417.c | 18 ++-- drivers/media/video/cx231xx/cx231xx-audio.c | 18 ++-- drivers/media/video/cx231xx/cx231xx-avcore.c | 144 ++++++++++++--------------- drivers/media/video/cx231xx/cx231xx-core.c | 76 +++++++------- drivers/media/video/cx231xx/cx231xx-vbi.c | 6 +- drivers/media/video/cx231xx/cx231xx-video.c | 16 --- 6 files changed, 124 insertions(+), 154 deletions(-) diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c index d4327dab5a36..ce2f62238a19 100644 --- a/drivers/media/video/cx231xx/cx231xx-417.c +++ b/drivers/media/video/cx231xx/cx231xx-417.c @@ -1095,7 +1095,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) { int version; int retval; - u32 i, data[7]; + u32 i; u32 val = 0; dprintk(1, "%s()\n", __func__); @@ -1154,6 +1154,11 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); */ + +#if 0 + /* TODO */ + u32 data[7]; + /* Setup to capture VBI */ data[0] = 0x0001BD00; data[1] = 1; /* frames per interrupt */ @@ -1162,7 +1167,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) data[4] = 0x206080C0; /* stop codes */ data[5] = 6; /* lines */ data[6] = 64; /* BPL */ -/* + cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1], data[2], data[3], data[4], data[5], data[6]); @@ -1175,7 +1180,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i | 0x80000000, valid, 0, 0, 0); } -*/ +#endif /* cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE); msleep(60); */ @@ -1792,17 +1797,16 @@ static int vidioc_streamon(struct file *file, void *priv, struct cx231xx_fh *fh = file->private_data; struct cx231xx *dev = fh->dev; - int rc = 0; dprintk(3, "enter vidioc_streamon()\n"); cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); if (dev->USE_ISO) - rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, CX231XX_NUM_BUFS, dev->video_mode.max_pkt_size, cx231xx_isoc_copy); else { - rc = cx231xx_init_bulk(dev, 320, + cx231xx_init_bulk(dev, 320, 5, dev->ts1_mode.max_pkt_size, cx231xx_bulk_copy); diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c index a2c2b7d343ec..068f78dc5d13 100644 --- a/drivers/media/video/cx231xx/cx231xx-audio.c +++ b/drivers/media/video/cx231xx/cx231xx-audio.c @@ -523,21 +523,24 @@ static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - unsigned int channels, rate, format; int ret; dprintk("Setting capture parameters\n"); ret = snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); +#if 0 + /* TODO: set up cx231xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono */ + unsigned int channels, rate, format; + format = params_format(hw_params); rate = params_rate(hw_params); channels = params_channels(hw_params); +#endif - /* TODO: set up cx231xx audio chip to deliver the correct audio format, - current default is 48000hz multiplexed => 96000hz mono - which shouldn't matter since analogue TV only supports mono */ - return 0; + return ret; } static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream) @@ -586,7 +589,7 @@ static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct cx231xx *dev = snd_pcm_substream_chip(substream); - int retval; + int retval = 0; if (dev->state & DEV_DISCONNECTED) return -ENODEV; @@ -601,12 +604,13 @@ static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, break; default: retval = -EINVAL; + break; } spin_unlock(&dev->adev.slock); schedule_work(&dev->wq_trigger); - return 0; + return retval; } static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index 53ff26e7abf7..6e4bca251862 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -934,33 +934,29 @@ int cx231xx_set_decoder_video_input(struct cx231xx *dev, void cx231xx_enable656(struct cx231xx *dev) { u8 temp = 0; - int status; /*enable TS1 data[0:7] as output to export 656*/ - status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF); + vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF); /*enable TS1 clock as output to export 656*/ - status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); + vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); temp = temp|0x04; - status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); - + vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); } EXPORT_SYMBOL_GPL(cx231xx_enable656); void cx231xx_disable656(struct cx231xx *dev) { u8 temp = 0; - int status; - - status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00); + vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00); - status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); + vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); temp = temp&0xFB; - status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); + vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); } EXPORT_SYMBOL_GPL(cx231xx_disable656); @@ -1320,117 +1316,115 @@ void update_HH_register_after_set_DIF(struct cx231xx *dev) void cx231xx_dump_HH_reg(struct cx231xx *dev) { - u8 status = 0; u32 value = 0; u16 i = 0; value = 0x45005390; - status = vid_blk_write_word(dev, 0x104, value); + vid_blk_write_word(dev, 0x104, value); for (i = 0x100; i < 0x140; i++) { - status = vid_blk_read_word(dev, i, &value); + vid_blk_read_word(dev, i, &value); cx231xx_info("reg0x%x=0x%x\n", i, value); i = i+3; } for (i = 0x300; i < 0x400; i++) { - status = vid_blk_read_word(dev, i, &value); + vid_blk_read_word(dev, i, &value); cx231xx_info("reg0x%x=0x%x\n", i, value); i = i+3; } for (i = 0x400; i < 0x440; i++) { - status = vid_blk_read_word(dev, i, &value); + vid_blk_read_word(dev, i, &value); cx231xx_info("reg0x%x=0x%x\n", i, value); i = i+3; } - status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); + vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390); - status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); + vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); } void cx231xx_dump_SC_reg(struct cx231xx *dev) { u8 value[4] = { 0, 0, 0, 0 }; - int status = 0; cx231xx_info("cx231xx_dump_SC_reg!\n"); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0], value[1], value[2], value[3]); @@ -1441,18 +1435,15 @@ void cx231xx_dump_SC_reg(struct cx231xx *dev) void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev) { - u8 status = 0; u8 value = 0; - - - status = afe_read_byte(dev, ADC_STATUS2_CH3, &value); + afe_read_byte(dev, ADC_STATUS2_CH3, &value); value = (value & 0xFE)|0x01; - status = afe_write_byte(dev, ADC_STATUS2_CH3, value); + afe_write_byte(dev, ADC_STATUS2_CH3, value); - status = afe_read_byte(dev, ADC_STATUS2_CH3, &value); + afe_read_byte(dev, ADC_STATUS2_CH3, &value); value = (value & 0xFE)|0x00; - status = afe_write_byte(dev, ADC_STATUS2_CH3, value); + afe_write_byte(dev, ADC_STATUS2_CH3, value); /* @@ -1464,44 +1455,43 @@ void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev) for low-if agc defect */ - status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value); + afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value); value = (value & 0xFC)|0x00; - status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value); + afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value); - status = afe_read_byte(dev, ADC_INPUT_CH3, &value); + afe_read_byte(dev, ADC_INPUT_CH3, &value); value = (value & 0xF9)|0x02; - status = afe_write_byte(dev, ADC_INPUT_CH3, value); + afe_write_byte(dev, ADC_INPUT_CH3, value); - status = afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value); + afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value); value = (value & 0xFB)|0x04; - status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, value); + afe_write_byte(dev, ADC_FB_FRCRST_CH3, value); - status = afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value); + afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value); value = (value & 0xFC)|0x03; - status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value); + afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value); - status = afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value); + afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value); value = (value & 0xFB)|0x04; - status = afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value); + afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value); - status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); + afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); value = (value & 0xF8)|0x06; - status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); + afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); - status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); + afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); value = (value & 0x8F)|0x40; - status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); + afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); - status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value); + afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value); value = (value & 0xDF)|0x20; - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value); + afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value); } void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, u8 spectral_invert, u32 mode) { u32 colibri_carrier_offset = 0; - u8 status = 0; u32 func_mode = 0x01; /* Device has a DIF if this function is called */ u32 standard = 0; u8 value[4] = { 0, 0, 0, 0 }; @@ -1511,15 +1501,15 @@ void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, value[1] = (u8) 0x6F; value[2] = (u8) 0x6F; value[3] = (u8) 0x6F; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, value, 4); /*Set colibri for low IF*/ - status = cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF); + cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF); /* Set C2HH for low IF operation.*/ standard = dev->norm; - status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, + cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, func_mode, standard); /* Get colibri offsets.*/ @@ -1556,7 +1546,6 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, u8 spectral_invert, u32 mode) { unsigned long pll_freq_word; - int status = 0; u32 dif_misc_ctrl_value = 0; u64 pll_freq_u64 = 0; u32 i = 0; @@ -1567,7 +1556,7 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, if (mode == TUNER_MODE_FM_RADIO) { pll_freq_word = 0x905A1CAC; - status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); + vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); } else /*KSPROPERTY_TUNER_MODE_TV*/{ /* Calculate the PLL frequency word based on the adjusted if_freq*/ @@ -1576,23 +1565,23 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, do_div(pll_freq_u64, 50000000); pll_freq_word = (u32)pll_freq_u64; /*pll_freq_word = 0x3463497;*/ - status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); + vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); if (spectral_invert) { if_freq -= 400000; /* Enable Spectral Invert*/ - status = vid_blk_read_word(dev, DIF_MISC_CTRL, + vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value); dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000; - status = vid_blk_write_word(dev, DIF_MISC_CTRL, + vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value); } else { if_freq += 400000; /* Disable Spectral Invert*/ - status = vid_blk_read_word(dev, DIF_MISC_CTRL, + vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value); dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF; - status = vid_blk_write_word(dev, DIF_MISC_CTRL, + vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value); } @@ -1609,7 +1598,7 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, sizeof(Dif_set_array)/sizeof(struct dif_settings)); for (i = 0; i < sizeof(Dif_set_array)/sizeof(struct dif_settings); i++) { if (Dif_set_array[i].if_freq == if_freq) { - status = vid_blk_write_word(dev, + vid_blk_write_word(dev, Dif_set_array[i].register_address, Dif_set_array[i].value); } } @@ -3090,31 +3079,30 @@ int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) */ int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) { - int status = 0; int i = 0; /* get the lock */ mutex_lock(&dev->gpio_i2c_lock); /* start */ - status = cx231xx_gpio_i2c_start(dev); + cx231xx_gpio_i2c_start(dev); /* write dev_addr */ - status = cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); + cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); /* read Ack */ - status = cx231xx_gpio_i2c_read_ack(dev); + cx231xx_gpio_i2c_read_ack(dev); for (i = 0; i < len; i++) { /* Write data */ - status = cx231xx_gpio_i2c_write_byte(dev, buf[i]); + cx231xx_gpio_i2c_write_byte(dev, buf[i]); /* read Ack */ - status = cx231xx_gpio_i2c_read_ack(dev); + cx231xx_gpio_i2c_read_ack(dev); } /* write End */ - status = cx231xx_gpio_i2c_end(dev); + cx231xx_gpio_i2c_end(dev); /* release the lock */ mutex_unlock(&dev->gpio_i2c_lock); diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c index 08dd930f882a..05358d486135 100644 --- a/drivers/media/video/cx231xx/cx231xx-core.c +++ b/drivers/media/video/cx231xx/cx231xx-core.c @@ -754,7 +754,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) } } - return 0; + return errCode ? -EINVAL : 0; } EXPORT_SYMBOL_GPL(cx231xx_set_mode); @@ -764,7 +764,7 @@ int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size) int actlen, ret = -ENOMEM; u32 *buffer; -buffer = kzalloc(4096, GFP_KERNEL); + buffer = kzalloc(4096, GFP_KERNEL); if (buffer == NULL) { cx231xx_info("out of mem\n"); return -ENOMEM; @@ -772,16 +772,16 @@ buffer = kzalloc(4096, GFP_KERNEL); memcpy(&buffer[0], firmware, 4096); ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 5), - buffer, 4096, &actlen, 2000); + buffer, 4096, &actlen, 2000); if (ret) cx231xx_info("bulk message failed: %d (%d/%d)", ret, - size, actlen); + size, actlen); else { errCode = actlen != size ? -1 : 0; } -kfree(buffer); - return 0; + kfree(buffer); + return errCode; } /***************************************************************** @@ -797,7 +797,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); - int rc, i; + int i; switch (urb->status) { case 0: /* success */ @@ -814,7 +814,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->video_mode.slock); - rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb); + dev->video_mode.isoc_ctl.isoc_copy(dev, urb); spin_unlock(&dev->video_mode.slock); /* Reset urb buffers */ @@ -822,7 +822,6 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } - urb->status = 0; urb->status = usb_submit_urb(urb, GFP_ATOMIC); if (urb->status) { @@ -843,7 +842,6 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); - int rc; switch (urb->status) { case 0: /* success */ @@ -860,12 +858,10 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->video_mode.slock); - rc = dev->video_mode.bulk_ctl.bulk_copy(dev, urb); + dev->video_mode.bulk_ctl.bulk_copy(dev, urb); spin_unlock(&dev->video_mode.slock); /* Reset urb buffers */ - urb->status = 0; - urb->status = usb_submit_urb(urb, GFP_ATOMIC); if (urb->status) { cx231xx_isocdbg("urb resubmit failed (error=%i)\n", @@ -1231,42 +1227,40 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, EXPORT_SYMBOL_GPL(cx231xx_init_bulk); void cx231xx_stop_TS1(struct cx231xx *dev) { - int status = 0; u8 val[4] = { 0, 0, 0, 0 }; - val[0] = 0x00; - val[1] = 0x03; - val[2] = 0x00; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS_MODE_REG, val, 4); - - val[0] = 0x00; - val[1] = 0x70; - val[2] = 0x04; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_CFG_REG, val, 4); + val[0] = 0x00; + val[1] = 0x03; + val[2] = 0x00; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS_MODE_REG, val, 4); + + val[0] = 0x00; + val[1] = 0x70; + val[2] = 0x04; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_CFG_REG, val, 4); } /* EXPORT_SYMBOL_GPL(cx231xx_stop_TS1); */ void cx231xx_start_TS1(struct cx231xx *dev) { - int status = 0; u8 val[4] = { 0, 0, 0, 0 }; - val[0] = 0x03; - val[1] = 0x03; - val[2] = 0x00; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS_MODE_REG, val, 4); - - val[0] = 0x04; - val[1] = 0xA3; - val[2] = 0x3B; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_CFG_REG, val, 4); + val[0] = 0x03; + val[1] = 0x03; + val[2] = 0x00; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS_MODE_REG, val, 4); + + val[0] = 0x04; + val[1] = 0xA3; + val[2] = 0x3B; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_CFG_REG, val, 4); } /* EXPORT_SYMBOL_GPL(cx231xx_start_TS1); */ /***************************************************************** diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c index 8cdee5f78f13..3d15314e1f88 100644 --- a/drivers/media/video/cx231xx/cx231xx-vbi.c +++ b/drivers/media/video/cx231xx/cx231xx-vbi.c @@ -83,7 +83,6 @@ static inline void print_err_status(struct cx231xx *dev, int packet, int status) */ static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) { - struct cx231xx_buffer *buf; struct cx231xx_dmaqueue *dma_q = urb->context; int rc = 1; unsigned char *p_buffer; @@ -102,8 +101,6 @@ static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) return 0; } - buf = dev->vbi_mode.bulk_ctl.buf; - /* get buffer pointer and length */ p_buffer = urb->transfer_buffer; buffer_size = urb->actual_length; @@ -310,7 +307,6 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); - int rc; switch (urb->status) { case 0: /* success */ @@ -328,7 +324,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->vbi_mode.slock); - rc = dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); + dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); spin_unlock(&dev->vbi_mode.slock); /* Reset status */ diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 2a04558699f8..523aa49d6b86 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -326,9 +326,7 @@ static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, */ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) { - struct cx231xx_buffer *buf; struct cx231xx_dmaqueue *dma_q = urb->context; - unsigned char *outp = NULL; int i, rc = 1; unsigned char *p_buffer; u32 bytes_parsed = 0, buffer_size = 0; @@ -346,10 +344,6 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) return 0; } - buf = dev->video_mode.isoc_ctl.buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status; @@ -429,9 +423,7 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) { - struct cx231xx_buffer *buf; struct cx231xx_dmaqueue *dma_q = urb->context; - unsigned char *outp = NULL; int rc = 1; unsigned char *p_buffer; u32 bytes_parsed = 0, buffer_size = 0; @@ -449,10 +441,6 @@ static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) return 0; } - buf = dev->video_mode.bulk_ctl.buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - if (1) { /* get buffer pointer and length */ @@ -701,13 +689,9 @@ void cx231xx_reset_video_buffer(struct cx231xx *dev, buf = dev->video_mode.bulk_ctl.buf; if (buf == NULL) { - u8 *outp = NULL; /* first try to get the buffer */ get_next_buf(dma_q, &buf); - if (buf) - outp = videobuf_to_vmalloc(&buf->vb); - dma_q->pos = 0; dma_q->field1_done = 0; dma_q->current_field = -1; -- cgit v1.2.3 From 932205a7ea64bab9edda04b9d555a277b57943fd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 23 Apr 2012 08:25:20 -0300 Subject: [media] ivtv/cx18: fix compiler warnings media_build/v4l/cx18-alsa-main.c: In function 'cx18_alsa_exit': media_build/v4l/cx18-alsa-main.c:282:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/cx18-mailbox.c: In function 'cx18_api_call': media_build/v4l/cx18-mailbox.c:598:6: warning: variable 'state' set but not used [-Wunused-but-set-variable] media_build/v4l/cx18-alsa-pcm.c: In function 'snd_cx18_pcm_capture_open': media_build/v4l/cx18-alsa-pcm.c:156:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/cx18-alsa-pcm.c: In function 'snd_cx18_pcm_capture_close': media_build/v4l/cx18-alsa-pcm.c:202:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/cx18-alsa-pcm.c: In function 'snd_cx18_pcm_hw_params': media_build/v4l/cx18-alsa-pcm.c:255:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/cx18-streams.c: In function 'cx18_stop_v4l2_encode_stream': media_build/v4l/cx18-streams.c:983:16: warning: variable 'then' set but not used [-Wunused-but-set-variable] media_build/v4l/ivtv-ioctl.c: In function 'ivtv_set_speed': media_build/v4l/ivtv-ioctl.c:138:22: warning: variable 's' set but not used [-Wunused-but-set-variable] media_build/v4l/ivtvfb.c: In function 'ivtvfb_init': media_build/v4l/ivtvfb.c:1286:6: warning: variable 'err' set but not used [-Wunused-but-set-variable] media_build/v4l/ivtvfb.c: In function 'ivtvfb_cleanup': media_build/v4l/ivtvfb.c:1306:6: warning: variable 'err' set but not used [-Wunused-but-set-variable] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-main.c | 1 + drivers/media/video/cx18/cx18-alsa-pcm.c | 10 +++------- drivers/media/video/cx18/cx18-mailbox.c | 6 +----- drivers/media/video/cx18/cx18-streams.c | 3 --- drivers/media/video/ivtv/ivtv-ioctl.c | 3 --- drivers/media/video/ivtv/ivtvfb.c | 2 ++ 6 files changed, 7 insertions(+), 18 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index e118361c2e7b..6d2a98246b6d 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -285,6 +285,7 @@ static void __exit cx18_alsa_exit(void) drv = driver_find("cx18", &pci_bus_type); ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); + (void)ret; /* suppress compiler warning */ cx18_ext_init = NULL; printk(KERN_INFO "cx18-alsa: module unload complete\n"); diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index 82d195be9197..7a5b84a86bb3 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -190,7 +190,7 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) ret = cx18_start_v4l2_encode_stream(s); snd_cx18_unlock(cxsc); - return 0; + return ret; } static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) @@ -199,12 +199,11 @@ static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; struct cx18 *cx = to_cx18(v4l2_dev); struct cx18_stream *s; - int ret; /* Instruct the cx18 to stop sending packets */ snd_cx18_lock(cxsc); s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; - ret = cx18_stop_v4l2_encode_stream(s, 0); + cx18_stop_v4l2_encode_stream(s, 0); clear_bit(CX18_F_S_STREAMING, &s->s_flags); cx18_release_stream(s); @@ -252,13 +251,10 @@ static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - int ret; - dprintk("%s called\n", __func__); - ret = snd_pcm_alloc_vmalloc_buffer(substream, + return snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(params)); - return 0; } static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 0c7796e76ac0..ed8118390b02 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -595,9 +595,8 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) { const struct cx18_api_info *info = find_api_info(cmd); - u32 state, irq, req, ack, err; + u32 irq, req, ack, err; struct cx18_mailbox __iomem *mb; - u32 __iomem *xpu_state; wait_queue_head_t *waitq; struct mutex *mb_lock; unsigned long int t0, timeout, ret; @@ -628,14 +627,12 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) mb_lock = &cx->epu2apu_mb_lock; irq = IRQ_EPU_TO_APU; mb = &cx->scb->epu2apu_mb; - xpu_state = &cx->scb->apu_state; break; case CPU: waitq = &cx->mb_cpu_waitq; mb_lock = &cx->epu2cpu_mb_lock; irq = IRQ_EPU_TO_CPU; mb = &cx->scb->epu2cpu_mb; - xpu_state = &cx->scb->cpu_state; break; default: CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); @@ -653,7 +650,6 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) * by a signal, we may get here and find a busy mailbox. After waiting, * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. */ - state = cx18_readl(cx, xpu_state); req = cx18_readl(cx, &mb->request); timeout = msecs_to_jiffies(10); ret = wait_event_timeout(*waitq, diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 638cca156b58..4185bcb80ca3 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -980,7 +980,6 @@ void cx18_stop_all_captures(struct cx18 *cx) int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) { struct cx18 *cx = s->cx; - unsigned long then; if (!cx18_stream_enabled(s)) return -EINVAL; @@ -999,8 +998,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) else cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); - then = jiffies; - if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); } diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 70cd802c9ca8..f7d57b3f2842 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -135,7 +135,6 @@ void ivtv_set_osd_alpha(struct ivtv *itv) int ivtv_set_speed(struct ivtv *itv, int speed) { u32 data[CX2341X_MBOX_MAX_DATA]; - struct ivtv_stream *s; int single_step = (speed == 1 || speed == -1); DEFINE_WAIT(wait); @@ -145,8 +144,6 @@ int ivtv_set_speed(struct ivtv *itv, int speed) if (speed == itv->speed && !single_step) return 0; - s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; - if (single_step && (speed < 0) == (itv->speed < 0)) { /* Single step video and no need to change direction */ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index e5e7fa9e737b..05b94aa8ba32 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -1293,6 +1293,7 @@ static int __init ivtvfb_init(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); + (void)err; /* suppress compiler warning */ if (!registered) { printk(KERN_ERR "ivtvfb: no cards found\n"); return -ENODEV; @@ -1309,6 +1310,7 @@ static void ivtvfb_cleanup(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); + (void)err; /* suppress compiler warning */ } module_init(ivtvfb_init); -- cgit v1.2.3 From 30fdf035874a3b3f5d54f75147bddc2467cb0b7d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Apr 2012 06:26:19 -0300 Subject: [media] cx25821: fix compiler warnings media_build/v4l/cx25821-core.c: In function 'cx_i2c_read_print': media_build/v4l/cx25821-core.c:386:6: warning: variable 'value' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-core.c: In function 'cx25821_dev_setup': media_build/v4l/cx25821-core.c:899:6: warning: variable 'io_size' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-core.c: In function 'cx25821_irq': media_build/v4l/cx25821-core.c:1321:18: warning: variable 'pci_mask' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-i2c.c: In function 'cx25821_i2c_read': media_build/v4l/cx25821-i2c.c:365:6: warning: variable 'retval' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-medusa-video.c: In function 'medusa_enable_bluefield_output': media_build/v4l/cx25821-medusa-video.c:39:6: warning: variable 'ret_val' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-medusa-video.c: In function 'medusa_set_resolution': media_build/v4l/cx25821-medusa-video.c:435:6: warning: variable 'ret_val' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-medusa-video.c: In function 'medusa_set_decoderduration': media_build/v4l/cx25821-medusa-video.c:498:6: warning: variable 'ret_val' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-video.c: In function 'cx25821_dump_video_queue': media_build/v4l/cx25821-video.c:116:25: warning: variable 'buf' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-video.c: In function 'cx25821_buffer_prepare': media_build/v4l/cx25821-video.c:561:20: warning: variable 'line1_offset' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-video.c: In function 'video_ioctl_set': media_build/v4l/cx25821-video.c:1834:6: warning: variable 'value' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-video-upstream.c: In function 'cx25821_upstream_irq': media_build/v4l/cx25821-video-upstream.c:641:6: warning: variable 'msk_stat' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-video-upstream-ch2.c: In function 'cx25821_upstream_irq_ch2': media_build/v4l/cx25821-video-upstream-ch2.c:591:6: warning: variable 'msk_stat' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-audio-upstream.c: In function 'cx25821_upstream_irq_audio': media_build/v4l/cx25821-audio-upstream.c:589:6: warning: variable 'msk_stat' set but not used [-Wunused-but-set-variable] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/video/cx25821/cx25821-audio-upstream.c | 3 +-- drivers/media/video/cx25821/cx25821-core.c | 14 ++---------- drivers/media/video/cx25821/cx25821-i2c.c | 3 +-- drivers/media/video/cx25821/cx25821-medusa-video.c | 13 +++++------ .../video/cx25821/cx25821-video-upstream-ch2.c | 3 +-- .../media/video/cx25821/cx25821-video-upstream.c | 3 +-- drivers/media/video/cx25821/cx25821-video.c | 25 ++-------------------- drivers/media/video/cx25821/cx25821-video.h | 2 -- 8 files changed, 13 insertions(+), 53 deletions(-) diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.c b/drivers/media/video/cx25821/cx25821-audio-upstream.c index 20c7ca3351a8..8b2a99975c23 100644 --- a/drivers/media/video/cx25821/cx25821-audio-upstream.c +++ b/drivers/media/video/cx25821/cx25821-audio-upstream.c @@ -585,7 +585,7 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 msk_stat, audio_status; + u32 audio_status; int handled = 0; struct sram_channel *sram_ch; @@ -594,7 +594,6 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels; - msk_stat = cx_read(sram_ch->int_mstat); audio_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c index 7930ca58349f..83c1aa6b2e6c 100644 --- a/drivers/media/video/cx25821/cx25821-core.c +++ b/drivers/media/video/cx25821/cx25821-core.c @@ -379,14 +379,6 @@ static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) return cx_read(bus->reg_stat) & 0x01; } -void cx_i2c_read_print(struct cx25821_dev *dev, u32 reg, const char *reg_string) -{ - int tmp = 0; - u32 value = 0; - - value = cx25821_i2c_read(&dev->i2c_bus[0], reg, &tmp); -} - static void cx25821_registers_init(struct cx25821_dev *dev) { u32 tmp; @@ -895,7 +887,7 @@ static void cx25821_iounmap(struct cx25821_dev *dev) static int cx25821_dev_setup(struct cx25821_dev *dev) { - int io_size = 0, i; + int i; pr_info("\n***********************************\n"); pr_info("cx25821 set up\n"); @@ -960,7 +952,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) /* PCIe stuff */ dev->base_io_addr = pci_resource_start(dev->pci, 0); - io_size = pci_resource_len(dev->pci, 0); if (!dev->base_io_addr) { CX25821_ERR("No PCI Memory resources, exiting!\n"); @@ -1317,13 +1308,12 @@ void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) static irqreturn_t cx25821_irq(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 pci_status, pci_mask; + u32 pci_status; u32 vid_status; int i, handled = 0; u32 mask[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; pci_status = cx_read(PCI_INT_STAT); - pci_mask = cx_read(PCI_INT_MSK); if (pci_status == 0) goto out; diff --git a/drivers/media/video/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c index 12d7300fa1e9..6311180f430c 100644 --- a/drivers/media/video/cx25821/cx25821-i2c.c +++ b/drivers/media/video/cx25821/cx25821-i2c.c @@ -361,7 +361,6 @@ void cx25821_av_clk(struct cx25821_dev *dev, int enable) int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value) { struct i2c_client *client = &bus->i2c_client; - int retval = 0; int v = 0; u8 addr[2] = { 0, 0 }; u8 buf[4] = { 0, 0, 0, 0 }; @@ -385,7 +384,7 @@ int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value) msgs[0].addr = 0x44; msgs[1].addr = 0x44; - retval = i2c_xfer(client->adapter, msgs, 2); + i2c_xfer(client->adapter, msgs, 2); v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; *value = v; diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c index 298a68d98c2f..313fb20a0b47 100644 --- a/drivers/media/video/cx25821/cx25821-medusa-video.c +++ b/drivers/media/video/cx25821/cx25821-medusa-video.c @@ -35,7 +35,6 @@ static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, int enable) { - int ret_val = 1; u32 value = 0; u32 tmp = 0; int out_ctrl = OUT_CTRL1; @@ -79,13 +78,13 @@ static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, value &= 0xFFFFFF7F; /* clear BLUE_FIELD_EN */ if (enable) value |= 0x00000080; /* set BLUE_FIELD_EN */ - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); + cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp); value &= 0xFFFFFF7F; if (enable) value |= 0x00000080; /* set BLUE_FIELD_EN */ - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); + cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); } static int medusa_initialize_ntsc(struct cx25821_dev *dev) @@ -431,7 +430,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, { int decoder = 0; int decoder_count = 0; - int ret_val = 0; u32 hscale = 0x0; u32 vscale = 0x0; const int MAX_WIDTH = 720; @@ -482,9 +480,9 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, for (; decoder < decoder_count; decoder++) { /* write scaling values for each decoder */ - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], + cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL + (0x200 * decoder), hscale); - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], + cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL + (0x200 * decoder), vscale); } @@ -494,7 +492,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, int duration) { - int ret_val = 0; u32 fld_cnt = 0; u32 tmp = 0; u32 disp_cnt_reg = DISP_AB_CNT; @@ -537,7 +534,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, fld_cnt |= ((u32) duration) << 16; } - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); + cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); mutex_unlock(&dev->lock); } diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c index 5a157cf4a95e..c8c94fbf5d8d 100644 --- a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c +++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c @@ -587,7 +587,7 @@ int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 msk_stat, vid_status; + u32 vid_status; int handled = 0; int channel_num = 0; struct sram_channel *sram_ch; @@ -598,7 +598,6 @@ static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) channel_num = VID_UPSTREAM_SRAM_CHANNEL_J; sram_ch = dev->channels[channel_num].sram_channels; - msk_stat = cx_read(sram_ch->int_mstat); vid_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.c b/drivers/media/video/cx25821/cx25821-video-upstream.c index 21e7d657f049..52c13e0b6492 100644 --- a/drivers/media/video/cx25821/cx25821-video-upstream.c +++ b/drivers/media/video/cx25821/cx25821-video-upstream.c @@ -637,7 +637,7 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 msk_stat, vid_status; + u32 vid_status; int handled = 0; int channel_num = 0; struct sram_channel *sram_ch; @@ -649,7 +649,6 @@ static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) sram_ch = dev->channels[channel_num].sram_channels; - msk_stat = cx_read(sram_ch->int_mstat); vid_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ diff --git a/drivers/media/video/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c index ffd8bc79c02e..b38d4379cc36 100644 --- a/drivers/media/video/cx25821/cx25821-video.c +++ b/drivers/media/video/cx25821/cx25821-video.c @@ -109,25 +109,6 @@ struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) return NULL; } -void cx25821_dump_video_queue(struct cx25821_dev *dev, - struct cx25821_dmaqueue *q) -{ - struct cx25821_buffer *buf; - struct list_head *item; - dprintk(1, "%s()\n", __func__); - - if (!list_empty(&q->active)) { - list_for_each(item, &q->active) - buf = list_entry(item, struct cx25821_buffer, vb.queue); - } - - if (!list_empty(&q->queued)) { - list_for_each(item, &q->queued) - buf = list_entry(item, struct cx25821_buffer, vb.queue); - } - -} - void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, u32 count) { @@ -557,7 +538,7 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); int rc, init_buffer = 0; - u32 line0_offset, line1_offset; + u32 line0_offset; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int bpl_local = LINE_SIZE_D1; int channel_opened = fh->channel_id; @@ -639,7 +620,6 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, case V4L2_FIELD_INTERLACED: /* All other formats are top field first */ line0_offset = 0; - line1_offset = buf->bpl; dprintk(1, "top field first\n"); cx25821_risc_buffer(dev->pci, &buf->risc, @@ -1830,7 +1810,6 @@ static long video_ioctl_set(struct file *file, unsigned int cmd, int i = 0; int cif_enable = 0; int cif_width = 0; - u32 value = 0; data_from_user = (struct downstream_user_struct *)arg; @@ -1914,7 +1893,7 @@ static long video_ioctl_set(struct file *file, unsigned int cmd, cx_write(data_from_user->reg_address, data_from_user->reg_data); break; case MEDUSA_READ: - value = cx25821_i2c_read(&dev->i2c_bus[0], + cx25821_i2c_read(&dev->i2c_bus[0], (u16) data_from_user->reg_address, &data_from_user->reg_data); break; diff --git a/drivers/media/video/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h index d0d9538ca5b3..9652a5e35ba2 100644 --- a/drivers/media/video/cx25821/cx25821-video.h +++ b/drivers/media/video/cx25821/cx25821-video.h @@ -86,8 +86,6 @@ extern struct cx25821_fmt formats[]; extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc); extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; -extern void cx25821_dump_video_queue(struct cx25821_dev *dev, - struct cx25821_dmaqueue *q); extern void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, u32 count); -- cgit v1.2.3 From e92ba2830b62ba6315d48083bb7f02d3148d77db Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 May 2012 10:17:35 -0300 Subject: [media] v4l: fix compiler warnings media_build/v4l/au0828-video.c: In function 'au0828_irq_callback': media_build/v4l/au0828-video.c:123:6: warning: variable 'rc' set but not used [-Wunused-but-set-variable] media_build/v4l/cx23888-ir.c: In function 'pulse_clocks_to_clock_divider': media_build/v4l/cx23888-ir.c:334:6: warning: variable 'rem' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25840-ir.c: In function 'pulse_clocks_to_clock_divider': media_build/v4l/cx25840-ir.c:319:6: warning: variable 'rem' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25840-ir.c: In function 'cx25840_ir_tx_write': media_build/v4l/cx25840-ir.c:863:21: warning: variable 'c' set but not used [-Wunused-but-set-variable] media_build/v4l/em28xx-audio.c: In function 'snd_em28xx_hw_capture_params': media_build/v4l/em28xx-audio.c:346:31: warning: variable 'format' set but not used [-Wunused-but-set-variable] media_build/v4l/em28xx-audio.c:346:25: warning: variable 'rate' set but not used [-Wunused-but-set-variable] media_build/v4l/em28xx-audio.c:346:15: warning: variable 'channels' set but not used [-Wunused-but-set-variable] media_build/v4l/hdpvr-control.c: In function 'get_input_lines_info': media_build/v4l/hdpvr-control.c:98:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/hdpvr-video.c: In function 'hdpvr_try_ctrl': media_build/v4l/hdpvr-video.c:955:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/saa7134-video.c: In function 'saa7134_s_tuner': media_build/v4l/saa7134-video.c:2030:6: warning: variable 'rx' set but not used [-Wunused-but-set-variable] media_build/v4l/sn9c102_core.c: In function 'sn9c102_stream_interrupt': media_build/v4l/sn9c102_core.c:998:7: warning: variable 'timeout' set but not used [-Wunused-but-set-variable] Signed-off-by: Hans Verkuil Acked-by: Janne Grunau Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/au0828/au0828-video.c | 4 ++-- drivers/media/video/cx23885/cx23888-ir.c | 4 +--- drivers/media/video/cx25840/cx25840-ir.c | 6 +----- drivers/media/video/em28xx/em28xx-audio.c | 11 +++++++---- drivers/media/video/hdpvr/hdpvr-control.c | 2 ++ drivers/media/video/hdpvr/hdpvr-video.c | 2 +- drivers/media/video/saa7134/saa7134-video.c | 2 +- drivers/media/video/sn9c102/sn9c102_core.c | 4 +--- 8 files changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 141f9c23a5ca..ac3dd733ab81 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -120,7 +120,7 @@ static void au0828_irq_callback(struct urb *urb) struct au0828_dmaqueue *dma_q = urb->context; struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); unsigned long flags = 0; - int rc, i; + int i; switch (urb->status) { case 0: /* success */ @@ -138,7 +138,7 @@ static void au0828_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock_irqsave(&dev->slock, flags); - rc = dev->isoc_ctl.isoc_copy(dev, urb); + dev->isoc_ctl.isoc_copy(dev, urb); spin_unlock_irqrestore(&dev->slock, flags); /* Reset urb buffers */ diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c index bb1ce346425d..c2bc39c58f82 100644 --- a/drivers/media/video/cx23885/cx23888-ir.c +++ b/drivers/media/video/cx23885/cx23888-ir.c @@ -331,9 +331,7 @@ static u64 ns_to_pulse_clocks(u32 ns) static u16 pulse_clocks_to_clock_divider(u64 count) { - u32 rem; - - rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + do_div(count, (FIFO_RXTX << 2) | 0x3); /* net result needs to be rounded down and decremented by 1 */ if (count > RXCLK_RCD + 1) diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c index 13c380ebb562..38ce76ed1924 100644 --- a/drivers/media/video/cx25840/cx25840-ir.c +++ b/drivers/media/video/cx25840/cx25840-ir.c @@ -316,9 +316,7 @@ static u64 ns_to_pulse_clocks(u32 ns) static u16 pulse_clocks_to_clock_divider(u64 count) { - u32 rem; - - rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + do_div(count, (FIFO_RXTX << 2) | 0x3); /* net result needs to be rounded down and decremented by 1 */ if (count > RXCLK_RCD + 1) @@ -860,12 +858,10 @@ static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, ssize_t *num) { struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; if (ir_state == NULL) return -ENODEV; - c = ir_state->c; #if 0 /* * FIXME - the code below is an incomplete and untested sketch of what diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index e2a7b77c39c7..d7e2a3dc5525 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -343,7 +343,6 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - unsigned int channels, rate, format; int ret; dprintk("Setting capture parameters\n"); @@ -352,13 +351,17 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); if (ret < 0) return ret; +#if 0 + /* TODO: set up em28xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono */ + unsigned int channels, rate, format; + format = params_format(hw_params); rate = params_rate(hw_params); channels = params_channels(hw_params); +#endif - /* TODO: set up em28xx audio chip to deliver the correct audio format, - current default is 48000hz multiplexed => 96000hz mono - which shouldn't matter since analogue TV only supports mono */ return 0; } diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c index 068df4ba3f51..ae8f229d1141 100644 --- a/drivers/media/video/hdpvr/hdpvr-control.c +++ b/drivers/media/video/hdpvr/hdpvr-control.c @@ -113,6 +113,8 @@ int get_input_lines_info(struct hdpvr_device *dev) "get input lines info returned: %d, %s\n", ret, print_buf); } +#else + (void)ret; /* suppress compiler warning */ #endif lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; mutex_unlock(&dev->usbc_mutex); diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 11ffe9cc1780..0e9e156bb2aa 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -994,7 +994,7 @@ static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) default: return -EINVAL; } - return 0; + return ret; } static int vidioc_try_ext_ctrls(struct file *file, void *priv, diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 417034eb6ad2..6de10b1e7251 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -2036,7 +2036,7 @@ static int saa7134_s_tuner(struct file *file, void *priv, mode = dev->thread.mode; if (UNSET == mode) { rx = saa7134_tvaudio_getstereo(dev); - mode = saa7134_tvaudio_rx2mode(t->rxsubchans); + mode = saa7134_tvaudio_rx2mode(rx); } if (mode != t->audmode) dev->thread.mode = t->audmode; diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index c2882fa5be85..19ea780b16ff 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -995,10 +995,8 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam) static int sn9c102_stream_interrupt(struct sn9c102_device* cam) { - long timeout; - cam->stream = STREAM_INTERRUPT; - timeout = wait_event_timeout(cam->wait_stream, + wait_event_timeout(cam->wait_stream, (cam->stream == STREAM_OFF) || (cam->state & DEV_DISCONNECTED), SN9C102_URB_TIMEOUT); -- cgit v1.2.3 From 8173090acb33500496b69ca20c7f33c3bf665958 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Apr 2012 07:30:48 -0300 Subject: [media] v4l: fix compiler warnings media_build/v4l/adv7343.c: In function 'adv7343_setstd': media_build/v4l/adv7343.c:133:6: warning: variable 'output_idx' set but not used [-Wunused-but-set-variable] media_build/v4l/tvp5150.c: In function 'tvp5150_mbus_fmt': media_build/v4l/tvp5150.c:833:14: warning: variable 'std' set but not used [-Wunused-but-set-variable] media_build/v4l/tvp7002.c: In function 'tvp7002_query_dv_preset': media_build/v4l/tvp7002.c:673:18: warning: variable 'device' set but not used [-Wunused-but-set-variable] media_build/v4l/c-qcam.c: In function 'qc_capture': media_build/v4l/c-qcam.c:381:33: warning: variable 'bitsperxfer' set but not used [-Wunused-but-set-variable] media_build/v4l/pms.c: In function 'pms_s_std': media_build/v4l/pms.c:738:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/pms.c: In function 'init_mediavision': media_build/v4l/pms.c:959:6: warning: variable 'id' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25821-alsa.c: In function 'cx25821_irq': media_build/v4l/cx25821-alsa.c:294:6: warning: variable 'audint_count' set but not used [-Wunused-but-set-variable] media_build/v4l/zr364xx.c: In function 'zr364xx_fillbuff': media_build/v4l/zr364xx.c:510:25: warning: variable 'frm' set but not used [-Wunused-but-set-variable] media_build/v4l/s2255drv.c: In function 's2255_fillbuff': media_build/v4l/s2255drv.c:637:23: warning: variable 'frm' set but not used [-Wunused-but-set-variable] media_build/v4l/s2255drv.c: In function 'vidioc_s_fmt_vid_cap': media_build/v4l/s2255drv.c:990:6: warning: variable 'norm' set but not used [-Wunused-but-set-variable] media_build/v4l/tm6000-stds.c: In function 'tm6000_set_audio_std': media_build/v4l/tm6000-stds.c:341:10: warning: variable 'nicam_flag' set but not used [-Wunused-but-set-variable] media_build/v4l/tm6000-video.c: In function 'get_next_buf': media_build/v4l/tm6000-video.c:172:8: warning: variable 'outp' set but not used [-Wunused-but-set-variable] media_build/v4l/tm6000-video.c: In function 'copy_streams': media_build/v4l/tm6000-video.c:214:36: warning: variable 'c' set but not used [-Wunused-but-set-variable] media_build/v4l/tm6000-input.c: In function 'tm6000_ir_urb_received': media_build/v4l/tm6000-input.c:171:6: warning: variable 'rc' set but not used [-Wunused-but-set-variable] media_build/v4l/usbvision-core.c: In function 'usbvision_decompress': media_build/v4l/usbvision-core.c:604:23: warning: variable 'max_pos' set but not used [-Wunused-but-set-variable] media_build/v4l/usbvision-core.c: In function 'usbvision_parse_compress': media_build/v4l/usbvision-core.c:705:39: warning: variable 'rc' set but not used [-Wunused-but-set-variable] media_build/v4l/usbvision-core.c:705:22: warning: variable 'bytes_per_pixel' set but not used [-Wunused-but-set-variable] media_build/v4l/zoran_device.c: In function 'write_overlay_mask': media_build/v4l/zoran_device.c:545:6: warning: variable 'reg' set but not used [-Wunused-but-set-variable] media_build/v4l/go7007-v4l2.c: In function 'go7007_streamoff': media_build/v4l/go7007-v4l2.c:79:6: warning: variable 'retval' set but not used [-Wunused-but-set-variable] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/adv7343.c | 4 +--- drivers/media/video/c-qcam.c | 3 +-- drivers/media/video/cx25821/cx25821-alsa.c | 2 -- drivers/media/video/pms.c | 2 -- drivers/media/video/s2255drv.c | 4 ---- drivers/media/video/tm6000/tm6000-input.c | 3 +-- drivers/media/video/tm6000/tm6000-stds.c | 2 -- drivers/media/video/tm6000/tm6000-video.c | 9 +-------- drivers/media/video/tvp5150.c | 7 ------- drivers/media/video/tvp7002.c | 3 --- drivers/media/video/usbvision/usbvision-core.c | 12 +++++------- drivers/media/video/zoran/zoran_device.c | 2 -- drivers/media/video/zr364xx.c | 2 -- drivers/staging/media/go7007/go7007-v4l2.c | 2 -- 14 files changed, 9 insertions(+), 48 deletions(-) diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c index 119b60401bf3..2b5aa676a84e 100644 --- a/drivers/media/video/adv7343.c +++ b/drivers/media/video/adv7343.c @@ -130,14 +130,12 @@ static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std) { struct adv7343_state *state = to_state(sd); struct adv7343_std_info *std_info; - int output_idx, num_std; + int num_std; char *fsc_ptr; u8 reg, val; int err = 0; int i = 0; - output_idx = state->output; - std_info = (struct adv7343_std_info *)stdinfo; num_std = ARRAY_SIZE(stdinfo); diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index fda32f52554a..73c65c2bf178 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -378,7 +378,7 @@ get_fragment: static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len) { struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; - unsigned lines, pixelsperline, bitsperxfer; + unsigned lines, pixelsperline; unsigned int is_bi_dir = qcam->bidirectional; size_t wantlen, outptr = 0; char tmpbuf[BUFSZ]; @@ -404,7 +404,6 @@ static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len) lines = qcam->height; pixelsperline = qcam->width; - bitsperxfer = (is_bi_dir) ? 24 : 8; if (is_bi_dir) { /* Turn the port around */ diff --git a/drivers/media/video/cx25821/cx25821-alsa.c b/drivers/media/video/cx25821/cx25821-alsa.c index 03cfac476b03..1858a45dd081 100644 --- a/drivers/media/video/cx25821/cx25821-alsa.c +++ b/drivers/media/video/cx25821/cx25821-alsa.c @@ -290,11 +290,9 @@ static irqreturn_t cx25821_irq(int irq, void *dev_id) u32 status, pci_status; u32 audint_status, audint_mask; int loop, handled = 0; - int audint_count = 0; audint_status = cx_read(AUD_A_INT_STAT); audint_mask = cx_read(AUD_A_INT_MSK); - audint_count = cx_read(AUD_A_GPCNT); status = cx_read(PCI_INT_STAT); for (loop = 0; loop < 1; loop++) { diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index 8b80f9808911..af2d9086d7e8 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -916,7 +916,6 @@ static const struct v4l2_ioctl_ops pms_ioctl_ops = { static int init_mediavision(struct pms *dev) { - int id; int idec, decst; int i; static const unsigned char i2c_defs[] = { @@ -948,7 +947,6 @@ static int init_mediavision(struct pms *dev) outb(dev->io >> 4, 0x9a01); /* Set IO port */ - id = mvv_read(dev, 3); decst = pms_i2c_stat(dev, 0x43); if (decst != -1) diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index ea974fadb5e2..01c2179f0520 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -634,13 +634,11 @@ static void s2255_fillbuff(struct s2255_channel *channel, const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; - struct s2255_framei *frm; if (!vbuf) return; last_frame = channel->last_frame; if (last_frame != -1) { - frm = &channel->buffer.frame[last_frame]; tmpbuf = (const char *)channel->buffer.frame[last_frame].lpvbits; switch (buf->fmt->fourcc) { @@ -987,7 +985,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct videobuf_queue *q = &fh->vb_vidq; struct s2255_mode mode; int ret; - int norm; ret = vidioc_try_fmt_vid_cap(file, fh, f); @@ -1018,7 +1015,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, channel->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - norm = norm_minw(&channel->vdev); if (channel->width > norm_minw(&channel->vdev)) { if (channel->height > norm_minh(&channel->vdev)) { if (channel->cap_parm.capturemode & diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c index 859eb90e4d56..e80b7e190471 100644 --- a/drivers/media/video/tm6000/tm6000-input.c +++ b/drivers/media/video/tm6000/tm6000-input.c @@ -168,7 +168,6 @@ static void tm6000_ir_urb_received(struct urb *urb) struct tm6000_IR *ir = dev->ir; struct tm6000_ir_poll_result poll_result; char *buf; - int rc; dprintk(2, "%s\n",__func__); if (urb->status < 0 || urb->actual_length <= 0) { @@ -192,7 +191,7 @@ static void tm6000_ir_urb_received(struct urb *urb) dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); rc_keydown(ir->rc, poll_result.rc_data, 0); - rc = usb_submit_urb(urb, GFP_ATOMIC); + usb_submit_urb(urb, GFP_ATOMIC); /* * Flash the led. We can't do it here, as it is running on IRQ context. * So, use the scheduler to do it, in a few ms. diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c index 9dc0831d813f..5e28d6a2412f 100644 --- a/drivers/media/video/tm6000/tm6000-stds.c +++ b/drivers/media/video/tm6000/tm6000-stds.c @@ -338,7 +338,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev) uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */ uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */ uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */ - uint8_t nicam_flag = 0; /* No NICAM */ if (dev->radio) { tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); @@ -398,7 +397,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev) } else { areg_05 = 0x07; } - nicam_flag = 1; break; /* other */ case 3: diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c index 375f26abd916..f7034df94e0a 100644 --- a/drivers/media/video/tm6000/tm6000-video.c +++ b/drivers/media/video/tm6000/tm6000-video.c @@ -169,7 +169,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, struct tm6000_buffer **buf) { struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - char *outp; if (list_empty(&dma_q->active)) { dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); @@ -179,11 +178,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, *buf = list_entry(dma_q->active.next, struct tm6000_buffer, vb.queue); - - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - - return; } /* @@ -211,7 +205,7 @@ static int copy_streams(u8 *data, unsigned long len, { struct tm6000_dmaqueue *dma_q = urb->context; struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - u8 *ptr = data, *endp = data+len, c; + u8 *ptr = data, *endp = data+len; unsigned long header = 0; int rc = 0; unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; @@ -264,7 +258,6 @@ static int copy_streams(u8 *data, unsigned long len, } /* split the header fields */ - c = (header >> 24) & 0xff; size = ((header & 0x7e) << 1); if (size > 0) size -= 4; diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 1326e11cf4a9..476a6a0bd108 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -830,19 +830,12 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) { struct tvp5150 *decoder = to_tvp5150(sd); - v4l2_std_id std; if (f == NULL) return -EINVAL; tvp5150_reset(sd, 0); - /* Calculate height and width based on current standard */ - if (decoder->norm == V4L2_STD_ALL) - std = tvp5150_read_std(sd); - else - std = decoder->norm; - f->width = decoder->rect.width; f->height = decoder->rect.height; diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index d7676d85c4df..408b65e782e5 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -670,7 +670,6 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, struct v4l2_dv_preset *qpreset) { const struct tvp7002_preset_definition *presets = tvp7002_presets; - struct tvp7002 *device; u8 progressive; u32 lpfr; u32 cpln; @@ -684,8 +683,6 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, /* Return invalid preset if no active input is detected */ qpreset->preset = V4L2_DV_INVALID; - device = to_tvp7002(sd); - /* Read standards from device registers */ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index f344411a4578..c9b2042f8bdf 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -601,13 +601,12 @@ static int usbvision_decompress(struct usb_usbvision *usbvision, unsigned char * unsigned char *decompressed, int *start_pos, int *block_typestart_pos, int len) { - int rest_pixel, idx, max_pos, pos, extra_pos, block_len, block_type_pos, block_type_len; + int rest_pixel, idx, pos, extra_pos, block_len, block_type_pos, block_type_len; unsigned char block_byte, block_code, block_type, block_type_byte, integrator; integrator = 0; pos = *start_pos; block_type_pos = *block_typestart_pos; - max_pos = 396; /* pos + len; */ extra_pos = pos; block_len = 0; block_byte = 0; @@ -702,7 +701,7 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision unsigned char strip_data[USBVISION_STRIP_LEN_MAX]; unsigned char strip_header[USBVISION_STRIP_HEADER_LEN]; int idx, idx_end, strip_len, strip_ptr, startblock_pos, block_pos, block_type_pos; - int clipmask_index, bytes_per_pixel, rc; + int clipmask_index; int image_size; unsigned char rv, gv, bv; static unsigned char *Y, *U, *V; @@ -769,7 +768,6 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision return parse_state_next_frame; } - bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; clipmask_index = frame->curline * MAX_FRAME_WIDTH; scratch_get(usbvision, strip_data, strip_len); @@ -781,14 +779,14 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision usbvision->block_pos = block_pos; - rc = usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end); + usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end); if (strip_len > usbvision->max_strip_len) usbvision->max_strip_len = strip_len; if (frame->curline % 2) - rc = usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2); + usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2); else - rc = usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2); + usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2); if (block_pos > usbvision->comprblock_pos) usbvision->comprblock_pos = block_pos; diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c index e86173bd1327..a4cd504b8eee 100644 --- a/drivers/media/video/zoran/zoran_device.c +++ b/drivers/media/video/zoran/zoran_device.c @@ -542,11 +542,9 @@ void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count) u32 *mask; int x, y, width, height; unsigned i, j, k; - u32 reg; /* fill mask with one bits */ memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); - reg = 0; for (i = 0; i < count; ++i) { /* pick up local copy of clip */ diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index cd2e39fc4bf0..e44cb330bbc8 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -507,14 +507,12 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; - struct zr364xx_framei *frm; if (!vbuf) return; last_frame = cam->last_frame; if (last_frame != -1) { - frm = &cam->buffer.frame[last_frame]; tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; switch (buf->fmt->fourcc) { case V4L2_PIX_FMT_JPEG: diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 3ef4cd8b4de3..c184ad30fbd8 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -76,7 +76,6 @@ static void abort_queued(struct go7007 *go) static int go7007_streamoff(struct go7007 *go) { - int retval = -EINVAL; unsigned long flags; mutex_lock(&go->hw_lock); @@ -87,7 +86,6 @@ static int go7007_streamoff(struct go7007 *go) abort_queued(go); spin_unlock_irqrestore(&go->spinlock, flags); go7007_reset_encoder(go); - retval = 0; } mutex_unlock(&go->hw_lock); return 0; -- cgit v1.2.3 From fdf07b027b2d3eee9a561898b9c427cc3e457af4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 20 Apr 2012 08:04:48 -0300 Subject: [media] v4l/dvb: fix compiler warnings media_build/v4l/stb6100.c: In function 'stb6100_read_reg': media_build/v4l/stb6100.c:161:6: warning: variable 'rc' set but not used [-Wunused-but-set-variable] media_build/v4l/cx24110.c: In function 'cx24110_read_ucblocks': media_build/v4l/cx24110.c:515:6: warning: variable 'lastbyer' set but not used [-Wunused-but-set-variable] media_build/v4l/dib9000.c: In function 'dib9000_mbx_process': media_build/v4l/dib9000.c:711:6: warning: variable 'tmp' set but not used [-Wunused-but-set-variable] media_build/v4l/zl10353.c: In function 'zl10353_init': media_build/v4l/zl10353.c:562:6: warning: variable 'rc' set but not used [-Wunused-but-set-variable] media_build/v4l/stv0297.c: In function 'stv0297_set_frontend': media_build/v4l/stv0297.c:417:16: warning: variable 'starttime' set but not used [-Wunused-but-set-variable] media_build/v4l/lgs8gxx.c: In function 'lgs8gxx_set_mode_manual': media_build/v4l/lgs8gxx.c:265:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/af9013.c: In function 'af9013_statistics_work': media_build/v4l/af9013.c:517:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/stv090x.c: In function 'stv090x_optimize_track': media_build/v4l/stv090x.c:2845:23: warning: variable 'rolloff' set but not used [-Wunused-but-set-variable] media_build/v4l/stv090x.c: In function 'stv090x_algo': media_build/v4l/stv090x.c:3177:28: warning: variable 'no_signal' set but not used [-Wunused-but-set-variable] media_build/v4l/it913x-fe.c: In function 'it913x_fe_read_ber': media_build/v4l/it913x-fe.c:636:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/it913x-fe.c: In function 'it913x_fe_get_frontend': media_build/v4l/it913x-fe.c:661:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/it913x-fe.c: In function 'it913x_fe_set_frontend': media_build/v4l/it913x-fe.c:694:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/m88rs2000.c: In function 'm88rs2000_set_fec': media_build/v4l/m88rs2000.c:657:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/dst_ca.c: In function 'ca_send_message': media_build/v4l/dst_ca.c:480:15: warning: variable 'ca_message_header_len' set but not used [-Wunused-but-set-variable] media_build/v4l/smssdio.c: In function 'smssdio_interrupt': media_build/v4l/smssdio.c:117:11: warning: variable 'isr' set but not used [-Wunused-but-set-variable] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/bt8xx/dst_ca.c | 2 -- drivers/media/dvb/frontends/af9013.c | 13 +++++-------- drivers/media/dvb/frontends/cx24110.c | 7 +++---- drivers/media/dvb/frontends/dib9000.c | 3 +-- drivers/media/dvb/frontends/it913x-fe.c | 26 ++++++++++++-------------- drivers/media/dvb/frontends/lgs8gxx.c | 3 +-- drivers/media/dvb/frontends/m88rs2000.c | 3 +-- drivers/media/dvb/frontends/stb6100.c | 3 +-- drivers/media/dvb/frontends/stv0297.c | 2 -- drivers/media/dvb/frontends/stv090x.c | 2 -- drivers/media/dvb/frontends/zl10353.c | 3 +-- drivers/media/dvb/siano/smssdio.c | 4 ++-- 12 files changed, 27 insertions(+), 44 deletions(-) diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c index 48e48e8af55a..66f52f116b60 100644 --- a/drivers/media/dvb/bt8xx/dst_ca.c +++ b/drivers/media/dvb/bt8xx/dst_ca.c @@ -477,7 +477,6 @@ static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) { int i = 0; - unsigned int ca_message_header_len; u32 command = 0; struct ca_msg *hw_buffer; @@ -496,7 +495,6 @@ static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, if (p_ca_message->msg) { - ca_message_header_len = p_ca_message->length; /* Restore it back when you are done */ /* EN50221 tag */ command = 0; diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index 6bcbcf543b38..5bc570d77846 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -514,7 +514,6 @@ err: static void af9013_statistics_work(struct work_struct *work) { - int ret; struct af9013_state *state = container_of(work, struct af9013_state, statistics_work.work); unsigned int next_msec; @@ -530,27 +529,27 @@ static void af9013_statistics_work(struct work_struct *work) default: state->statistics_step = 0; case 0: - ret = af9013_statistics_signal_strength(&state->fe); + af9013_statistics_signal_strength(&state->fe); state->statistics_step++; next_msec = 300; break; case 1: - ret = af9013_statistics_snr_start(&state->fe); + af9013_statistics_snr_start(&state->fe); state->statistics_step++; next_msec = 200; break; case 2: - ret = af9013_statistics_ber_unc_start(&state->fe); + af9013_statistics_ber_unc_start(&state->fe); state->statistics_step++; next_msec = 1000; break; case 3: - ret = af9013_statistics_snr_result(&state->fe); + af9013_statistics_snr_result(&state->fe); state->statistics_step++; next_msec = 400; break; case 4: - ret = af9013_statistics_ber_unc_result(&state->fe); + af9013_statistics_ber_unc_result(&state->fe); state->statistics_step++; next_msec = 100; break; @@ -558,8 +557,6 @@ static void af9013_statistics_work(struct work_struct *work) schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(next_msec)); - - return; } static int af9013_get_tune_settings(struct dvb_frontend *fe, diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c index 5101f10f2d7a..98ecaf0900d6 100644 --- a/drivers/media/dvb/frontends/cx24110.c +++ b/drivers/media/dvb/frontends/cx24110.c @@ -512,14 +512,13 @@ static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr) static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { struct cx24110_state *state = fe->demodulator_priv; - u32 lastbyer; if(cx24110_readreg(state,0x10)&0x40) { /* the RS error counter has finished one counting window */ cx24110_writereg(state,0x10,0x60); /* select the byer reg */ - lastbyer=cx24110_readreg(state,0x12)| - (cx24110_readreg(state,0x13)<<8)| - (cx24110_readreg(state,0x14)<<16); + cx24110_readreg(state, 0x12) | + (cx24110_readreg(state, 0x13) << 8) | + (cx24110_readreg(state, 0x14) << 16); cx24110_writereg(state,0x10,0x70); /* select the bler reg */ state->lastbler=cx24110_readreg(state,0x12)| (cx24110_readreg(state,0x13)<<8)| diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c index 80848b4c15d4..0661da919b50 100644 --- a/drivers/media/dvb/frontends/dib9000.c +++ b/drivers/media/dvb/frontends/dib9000.c @@ -708,7 +708,6 @@ static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr) static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) { int ret = 0; - u16 tmp; if (!state->platform.risc.fw_is_running) return -1; @@ -721,7 +720,7 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */ ret = dib9000_mbx_fetch_to_cache(state, attr); - tmp = dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ + dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ /* if (tmp) */ /* dprintk( "cleared IRQ: %x", tmp); */ DibReleaseLock(&state->platform.risc.mbx_lock); diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c index 84df03c29179..708cbf197913 100644 --- a/drivers/media/dvb/frontends/it913x-fe.c +++ b/drivers/media/dvb/frontends/it913x-fe.c @@ -633,10 +633,9 @@ static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr) static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber) { struct it913x_fe_state *state = fe->demodulator_priv; - int ret; u8 reg[5]; /* Read Aborted Packets and Pre-Viterbi error rate 5 bytes */ - ret = it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg)); + it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg)); state->ucblocks += (u32)(reg[1] << 8) | reg[0]; *ber = (u32)(reg[4] << 16) | (reg[3] << 8) | reg[2]; return 0; @@ -658,10 +657,9 @@ static int it913x_fe_get_frontend(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct it913x_fe_state *state = fe->demodulator_priv; - int ret; u8 reg[8]; - ret = it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg)); + it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg)); if (reg[3] < 3) p->modulation = fe_con[reg[3]]; @@ -691,25 +689,25 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct it913x_fe_state *state = fe->demodulator_priv; - int ret, i; + int i; u8 empty_ch, last_ch; state->it913x_status = 0; /* Set bw*/ - ret = it913x_fe_select_bw(state, p->bandwidth_hz, + it913x_fe_select_bw(state, p->bandwidth_hz, state->adcFrequency); /* Training Mode Off */ - ret = it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0); + it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0); /* Clear Empty Channel */ - ret = it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0); + it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0); /* Clear bits */ - ret = it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0); + it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0); /* LED on */ - ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); + it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); /* Select Band*/ if ((p->frequency >= 51000000) && (p->frequency <= 230000000)) i = 0; @@ -720,7 +718,7 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe) else return -EOPNOTSUPP; - ret = it913x_write_reg(state, PRO_DMOD, FREE_BAND, i); + it913x_write_reg(state, PRO_DMOD, FREE_BAND, i); deb_info("Frontend Set Tuner Type %02x", state->tuner_type); switch (state->tuner_type) { @@ -730,7 +728,7 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe) case IT9135_60: case IT9135_61: case IT9135_62: - ret = it9137_set_tuner(state, + it9137_set_tuner(state, p->bandwidth_hz, p->frequency); break; default: @@ -742,9 +740,9 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe) break; } /* LED off */ - ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); + it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); /* Trigger ofsm */ - ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); + it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); last_ch = 2; for (i = 0; i < 40; ++i) { empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS); diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c index 4de1d3520cd2..568363a10a31 100644 --- a/drivers/media/dvb/frontends/lgs8gxx.c +++ b/drivers/media/dvb/frontends/lgs8gxx.c @@ -262,7 +262,6 @@ static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) { - int ret = 0; u8 t; if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { @@ -296,7 +295,7 @@ static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) if (priv->config->prod == LGS8GXX_PROD_LGS8913) lgs8gxx_write_reg(priv, 0xC1, 0); - ret = lgs8gxx_read_reg(priv, 0xC5, &t); + lgs8gxx_read_reg(priv, 0xC5, &t); t = (t & 0xE0) | 0x06; lgs8gxx_write_reg(priv, 0xC5, t); diff --git a/drivers/media/dvb/frontends/m88rs2000.c b/drivers/media/dvb/frontends/m88rs2000.c index 045ee5a6f7ae..82cc14542479 100644 --- a/drivers/media/dvb/frontends/m88rs2000.c +++ b/drivers/media/dvb/frontends/m88rs2000.c @@ -654,7 +654,6 @@ static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset) static int m88rs2000_set_fec(struct m88rs2000_state *state, fe_code_rate_t fec) { - int ret; u16 fec_set; switch (fec) { /* This is not confirmed kept for reference */ @@ -677,7 +676,7 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state, default: fec_set = 0x08; } - ret = m88rs2000_demod_write(state, 0x76, fec_set); + m88rs2000_demod_write(state, 0x76, fec_set); return 0; } diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb/frontends/stb6100.c index def88abb30bf..2e93e65d2cdb 100644 --- a/drivers/media/dvb/frontends/stb6100.c +++ b/drivers/media/dvb/frontends/stb6100.c @@ -158,7 +158,6 @@ static int stb6100_read_regs(struct stb6100_state *state, u8 regs[]) static int stb6100_read_reg(struct stb6100_state *state, u8 reg) { u8 regs[STB6100_NUMREGS]; - int rc; struct i2c_msg msg = { .addr = state->config->tuner_address + reg, @@ -167,7 +166,7 @@ static int stb6100_read_reg(struct stb6100_state *state, u8 reg) .len = 1 }; - rc = i2c_transfer(state->i2c, &msg, 1); + i2c_transfer(state->i2c, &msg, 1); if (unlikely(reg >= STB6100_NUMREGS)) { dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c index 85c157a1fe5e..d40f226160ef 100644 --- a/drivers/media/dvb/frontends/stv0297.c +++ b/drivers/media/dvb/frontends/stv0297.c @@ -414,7 +414,6 @@ static int stv0297_set_frontend(struct dvb_frontend *fe) int delay; int sweeprate; int carrieroffset; - unsigned long starttime; unsigned long timeout; fe_spectral_inversion_t inversion; @@ -543,7 +542,6 @@ static int stv0297_set_frontend(struct dvb_frontend *fe) stv0297_writereg_mask(state, 0x43, 0x10, 0x10); /* wait for WGAGC lock */ - starttime = jiffies; timeout = jiffies + msecs_to_jiffies(2000); while (time_before(jiffies, timeout)) { msleep(10); diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 4aef1877ed42..d79e69f65cbb 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -2842,7 +2842,6 @@ static int stv090x_optimize_track(struct stv090x_state *state) { struct dvb_frontend *fe = &state->frontend; - enum stv090x_rolloff rolloff; enum stv090x_modcod modcod; s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0; @@ -2966,7 +2965,6 @@ static int stv090x_optimize_track(struct stv090x_state *state) f_1 = STV090x_READ_DEMOD(state, CFR2); f_0 = STV090x_READ_DEMOD(state, CFR1); reg = STV090x_READ_DEMOD(state, TMGOBS); - rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD); if (state->algo == STV090x_BLIND_SEARCH) { STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00); diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c index ac7237891374..4de3691610bc 100644 --- a/drivers/media/dvb/frontends/zl10353.c +++ b/drivers/media/dvb/frontends/zl10353.c @@ -559,7 +559,6 @@ static int zl10353_init(struct dvb_frontend *fe) { struct zl10353_state *state = fe->demodulator_priv; u8 zl10353_reset_attach[6] = { 0x50, 0x03, 0x64, 0x46, 0x15, 0x0F }; - int rc = 0; if (debug_regs) zl10353_dump_regs(fe); @@ -573,7 +572,7 @@ static int zl10353_init(struct dvb_frontend *fe) /* Do a "hard" reset if not already done */ if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] || zl10353_read_register(state, 0x51) != zl10353_reset_attach[2]) { - rc = zl10353_write(fe, zl10353_reset_attach, + zl10353_write(fe, zl10353_reset_attach, sizeof(zl10353_reset_attach)); if (debug_regs) zl10353_dump_regs(fe); diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c index 91f8c8291e2b..d6f3f100699a 100644 --- a/drivers/media/dvb/siano/smssdio.c +++ b/drivers/media/dvb/siano/smssdio.c @@ -114,7 +114,7 @@ out: static void smssdio_interrupt(struct sdio_func *func) { - int ret, isr; + int ret; struct smssdio_device *smsdev; struct smscore_buffer_t *cb; @@ -127,7 +127,7 @@ static void smssdio_interrupt(struct sdio_func *func) * The interrupt register has no defined meaning. It is just * a way of turning of the level triggered interrupt. */ - isr = sdio_readb(func, SMSSDIO_INT, &ret); + (void)sdio_readb(func, SMSSDIO_INT, &ret); if (ret) { sms_err("Unable to read interrupt register!\n"); return; -- cgit v1.2.3 From 5becbc58a01f1adaf34703c18287d9f7b46a17f6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 May 2012 10:22:58 -0300 Subject: [media] v4l/dvb: fix compiler warnings media_build/v4l/drxk_hard.c: In function 'DownloadMicrocode': media_build/v4l/drxk_hard.c:1388:6: warning: variable 'BlockCRC' set but not used [-Wunused-but-set-variable] media_build/v4l/drxk_hard.c:1384:6: warning: variable 'Drain' set but not used [-Wunused-but-set-variable] media_build/v4l/drxk_hard.c:1383:6: warning: variable 'Flags' set but not used [-Wunused-but-set-variable] media_build/v4l/lmedm04.c: In function 'lme2510_probe': media_build/v4l/lmedm04.c:1208:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/hopper_cards.c: In function 'hopper_irq_handler': media_build/v4l/hopper_cards.c:68:26: warning: variable 'lstat' set but not used [-Wunused-but-set-variable] media_build/v4l/mantis_cards.c: In function 'mantis_irq_handler': media_build/v4l/mantis_cards.c:76:26: warning: variable 'lstat' set but not used [-Wunused-but-set-variable] media_build/v4l/mantis_dma.c: In function 'mantis_dma_stop': media_build/v4l/mantis_dma.c:202:16: warning: variable 'mask' set but not used [-Wunused-but-set-variable] media_build/v4l/mantis_dma.c:202:6: warning: variable 'stat' set but not used [-Wunused-but-set-variable] media_build/v4l/mantis_evm.c: In function 'mantis_hifevm_work': media_build/v4l/mantis_evm.c:44:17: warning: variable 'gpif_mask' set but not used [-Wunused-but-set-variable] media_build/v4l/stb0899_drv.c: In function 'stb0899_init_calc': media_build/v4l/stb0899_drv.c:640:5: warning: variable 'agc1cn' set but not used [-Wunused-but-set-variable] media_build/v4l/stb0899_drv.c: In function 'stb0899_diseqc_init': media_build/v4l/stb0899_drv.c:830:13: warning: variable 'f22_rx' set but not used [-Wunused-but-set-variable] media_build/v4l/stb0899_drv.c:826:31: warning: variable 'tx_data' set but not used [-Wunused-but-set-variable] media_build/v4l/stv0900_sw.c: In function 'stv0900_track_optimization': media_build/v4l/stv0900_sw.c:838:26: warning: variable 'rolloff' set but not used [-Wunused-but-set-variable] media_build/v4l/ir-sanyo-decoder.c: In function 'ir_sanyo_decode': media_build/v4l/ir-sanyo-decoder.c:59:14: warning: variable 'not_address' set but not used [-Wunused-but-set-variable] media_build/v4l/mceusb.c: In function 'mceusb_dev_printdata': media_build/v4l/mceusb.c:523:46: warning: variable 'data5' set but not used [-Wunused-but-set-variable] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/lmedm04.c | 3 +-- drivers/media/dvb/frontends/drxk_hard.c | 14 ++++++++++---- drivers/media/dvb/frontends/stb0899_drv.c | 8 +------- drivers/media/dvb/frontends/stv0900_sw.c | 2 -- drivers/media/dvb/mantis/hopper_cards.c | 3 +-- drivers/media/dvb/mantis/mantis_cards.c | 3 +-- drivers/media/dvb/mantis/mantis_dma.c | 4 ---- drivers/media/dvb/mantis/mantis_evm.c | 3 +-- drivers/media/rc/ir-sanyo-decoder.c | 4 ++-- drivers/media/rc/mceusb.c | 3 +-- drivers/media/video/videobuf-dvb.c | 3 +-- 11 files changed, 19 insertions(+), 31 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index 5dde06d066ff..003fb9bb7117 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -1205,14 +1205,13 @@ static int lme2510_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); - int ret = 0; usb_reset_configuration(udev); usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 1); if (udev->speed != USB_SPEED_HIGH) { - ret = usb_reset_device(udev); + usb_reset_device(udev); info("DEV Failed to connect in HIGH SPEED mode"); return -ENODEV; } diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c index 36d11756492f..88bd1c888ca8 100644 --- a/drivers/media/dvb/frontends/drxk_hard.c +++ b/drivers/media/dvb/frontends/drxk_hard.c @@ -1380,20 +1380,20 @@ static int DownloadMicrocode(struct drxk_state *state, const u8 pMCImage[], u32 Length) { const u8 *pSrc = pMCImage; - u16 Flags; - u16 Drain; u32 Address; u16 nBlocks; u16 BlockSize; - u16 BlockCRC; u32 offset = 0; u32 i; int status = 0; dprintk(1, "\n"); - /* down the drain (we don care about MAGIC_WORD) */ + /* down the drain (we don't care about MAGIC_WORD) */ +#if 0 + /* For future reference */ Drain = (pSrc[0] << 8) | pSrc[1]; +#endif pSrc += sizeof(u16); offset += sizeof(u16); nBlocks = (pSrc[0] << 8) | pSrc[1]; @@ -1410,11 +1410,17 @@ static int DownloadMicrocode(struct drxk_state *state, pSrc += sizeof(u16); offset += sizeof(u16); +#if 0 + /* For future reference */ Flags = (pSrc[0] << 8) | pSrc[1]; +#endif pSrc += sizeof(u16); offset += sizeof(u16); +#if 0 + /* For future reference */ BlockCRC = (pSrc[0] << 8) | pSrc[1]; +#endif pSrc += sizeof(u16); offset += sizeof(u16); diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c index dd08f4ac64a8..8b0dc74a3298 100644 --- a/drivers/media/dvb/frontends/stb0899_drv.c +++ b/drivers/media/dvb/frontends/stb0899_drv.c @@ -637,11 +637,9 @@ static void stb0899_init_calc(struct stb0899_state *state) struct stb0899_internal *internal = &state->internal; int master_clk; u8 agc[2]; - u8 agc1cn; u32 reg; /* Read registers (in burst mode) */ - agc1cn = stb0899_read_reg(state, STB0899_AGC1CN); stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */ /* Initial calculations */ @@ -823,15 +821,12 @@ static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t static int stb0899_diseqc_init(struct stb0899_state *state) { - struct dvb_diseqc_master_cmd tx_data; /* struct dvb_diseqc_slave_reply rx_data; */ - u8 f22_tx, f22_rx, reg; + u8 f22_tx, reg; u32 mclk, tx_freq = 22000;/* count = 0, i; */ - tx_data.msg[0] = 0xe2; - tx_data.msg_len = 3; reg = stb0899_read_reg(state, STB0899_DISCNTRL2); STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0); stb0899_write_reg(state, STB0899_DISCNTRL2, reg); @@ -849,7 +844,6 @@ static int stb0899_diseqc_init(struct stb0899_state *state) f22_tx = mclk / (tx_freq * 32); stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */ state->rx_freq = 20000; - f22_rx = mclk / (state->rx_freq * 32); return 0; } diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c index ba0709b2d433..4af20780fb9c 100644 --- a/drivers/media/dvb/frontends/stv0900_sw.c +++ b/drivers/media/dvb/frontends/stv0900_sw.c @@ -835,7 +835,6 @@ static void stv0900_track_optimization(struct dvb_frontend *fe) blind_tun_sw = 0, modulation; - enum fe_stv0900_rolloff rolloff; enum fe_stv0900_modcode foundModcod; dprintk("%s\n", __func__); @@ -940,7 +939,6 @@ static void stv0900_track_optimization(struct dvb_frontend *fe) freq1 = stv0900_read_reg(intp, CFR2); freq0 = stv0900_read_reg(intp, CFR1); - rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { stv0900_write_reg(intp, SFRSTEP, 0x00); stv0900_write_bits(intp, SCAN_ENABLE, 0); diff --git a/drivers/media/dvb/mantis/hopper_cards.c b/drivers/media/dvb/mantis/hopper_cards.c index 71622f65c037..cc0251e01077 100644 --- a/drivers/media/dvb/mantis/hopper_cards.c +++ b/drivers/media/dvb/mantis/hopper_cards.c @@ -65,7 +65,7 @@ static int devs; static irqreturn_t hopper_irq_handler(int irq, void *dev_id) { - u32 stat = 0, mask = 0, lstat = 0; + u32 stat = 0, mask = 0; u32 rst_stat = 0, rst_mask = 0; struct mantis_pci *mantis; @@ -80,7 +80,6 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id) stat = mmread(MANTIS_INT_STAT); mask = mmread(MANTIS_INT_MASK); - lstat = stat & ~MANTIS_INT_RISCSTAT; if (!(stat & mask)) return IRQ_NONE; diff --git a/drivers/media/dvb/mantis/mantis_cards.c b/drivers/media/dvb/mantis/mantis_cards.c index c2bb90b3e529..095cf3a994e2 100644 --- a/drivers/media/dvb/mantis/mantis_cards.c +++ b/drivers/media/dvb/mantis/mantis_cards.c @@ -73,7 +73,7 @@ static char *label[10] = { static irqreturn_t mantis_irq_handler(int irq, void *dev_id) { - u32 stat = 0, mask = 0, lstat = 0; + u32 stat = 0, mask = 0; u32 rst_stat = 0, rst_mask = 0; struct mantis_pci *mantis; @@ -88,7 +88,6 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id) stat = mmread(MANTIS_INT_STAT); mask = mmread(MANTIS_INT_MASK); - lstat = stat & ~MANTIS_INT_RISCSTAT; if (!(stat & mask)) return IRQ_NONE; diff --git a/drivers/media/dvb/mantis/mantis_dma.c b/drivers/media/dvb/mantis/mantis_dma.c index c61ca7d3daea..566c407175a4 100644 --- a/drivers/media/dvb/mantis/mantis_dma.c +++ b/drivers/media/dvb/mantis/mantis_dma.c @@ -199,10 +199,6 @@ void mantis_dma_start(struct mantis_pci *mantis) void mantis_dma_stop(struct mantis_pci *mantis) { - u32 stat = 0, mask = 0; - - stat = mmread(MANTIS_INT_STAT); - mask = mmread(MANTIS_INT_MASK); dprintk(MANTIS_DEBUG, 1, "Mantis Stop DMA engine"); mmwrite((mmread(MANTIS_GPIF_ADDR) & (~(MANTIS_GPIF_HIFRDWRN))), MANTIS_GPIF_ADDR); diff --git a/drivers/media/dvb/mantis/mantis_evm.c b/drivers/media/dvb/mantis/mantis_evm.c index 36f2256ebb0e..71ce52875c38 100644 --- a/drivers/media/dvb/mantis/mantis_evm.c +++ b/drivers/media/dvb/mantis/mantis_evm.c @@ -41,10 +41,9 @@ static void mantis_hifevm_work(struct work_struct *work) struct mantis_ca *ca = container_of(work, struct mantis_ca, hif_evm_work); struct mantis_pci *mantis = ca->ca_priv; - u32 gpif_stat, gpif_mask; + u32 gpif_stat; gpif_stat = mmread(MANTIS_GPIF_STATUS); - gpif_mask = mmread(MANTIS_GPIF_IRQCFG); if (gpif_stat & MANTIS_GPIF_DETSTAT) { if (gpif_stat & MANTIS_CARD_PLUGIN) { diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index d38fbdd0b25a..7e54ec57bcf9 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -56,7 +56,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) { struct sanyo_dec *data = &dev->raw->sanyo; u32 scancode; - u8 address, not_address, command, not_command; + u8 address, command, not_command; if (!(dev->raw->enabled_protocols & RC_TYPE_SANYO)) return 0; @@ -154,7 +154,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) break; address = bitrev16((data->bits >> 29) & 0x1fff) >> 3; - not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3; + /* not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3; */ command = bitrev8((data->bits >> 8) & 0xff); not_command = bitrev8((data->bits >> 0) & 0xff); diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index e150a2e29a4b..3e29cc55fdd1 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -520,7 +520,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, { char codes[USB_BUFLEN * 3 + 1]; char inout[9]; - u8 cmd, subcmd, data1, data2, data3, data4, data5; + u8 cmd, subcmd, data1, data2, data3, data4; struct device *dev = ir->dev; int i, start, skip = 0; u32 carrier, period; @@ -553,7 +553,6 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, data2 = buf[start + 3] & 0xff; data3 = buf[start + 4] & 0xff; data4 = buf[start + 5] & 0xff; - data5 = buf[start + 6] & 0xff; switch (cmd) { case MCE_CMD_NULL: diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index 59cb54aa2946..94d83a41381b 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -45,7 +45,6 @@ static int videobuf_dvb_thread(void *data) struct videobuf_dvb *dvb = data; struct videobuf_buffer *buf; unsigned long flags; - int err; void *outp; dprintk("dvb thread started\n"); @@ -57,7 +56,7 @@ static int videobuf_dvb_thread(void *data) buf = list_entry(dvb->dvbq.stream.next, struct videobuf_buffer, stream); list_del(&buf->stream); - err = videobuf_waiton(&dvb->dvbq, buf, 0, 1); + videobuf_waiton(&dvb->dvbq, buf, 0, 1); /* no more feeds left or stop_feed() asked us to quit */ if (0 == dvb->nfeeds) -- cgit v1.2.3 From 6e65ca942b9664a987866ac0c62e7e450777056b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 29 Apr 2012 16:47:47 -0300 Subject: [media] mxb/saa7146: first round of cleanups Convert to the control framework, fix the easy v4l2-compliance failures. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 27 +++++ drivers/media/common/saa7146_video.c | 210 +++++++---------------------------- drivers/media/video/mxb.c | 173 ++++++++++++++--------------- drivers/media/video/mxb.h | 29 ----- include/media/saa7146.h | 2 + include/media/saa7146_vv.h | 1 + 6 files changed, 153 insertions(+), 289 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 8d7df1a0bcd0..f14e218bed16 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -429,8 +429,13 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status) } } +static const struct v4l2_ctrl_ops saa7146_ctrl_ops = { + .s_ctrl = saa7146_s_ctrl, +}; + int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; struct saa7146_vv *vv; int err; @@ -438,9 +443,28 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if (err) return err; + v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (hdl->error) { + err = hdl->error; + v4l2_ctrl_handler_free(hdl); + return err; + } + dev->v4l2_dev.ctrl_handler = hdl; + vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL); if (vv == NULL) { ERR("out of memory. aborting.\n"); + v4l2_ctrl_handler_free(hdl); return -ENOMEM; } ext_vv->ops = saa7146_video_ioctl_ops; @@ -463,6 +487,7 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if( NULL == vv->d_clipping.cpu_addr ) { ERR("out of memory. aborting.\n"); kfree(vv); + v4l2_ctrl_handler_free(hdl); return -1; } memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM); @@ -486,6 +511,7 @@ int saa7146_vv_release(struct saa7146_dev* dev) v4l2_device_unregister(&dev->v4l2_dev); pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); + v4l2_ctrl_handler_free(&dev->ctrl_handler); kfree(vv); dev->vv_data = NULL; dev->vv_callback = NULL; @@ -516,6 +542,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, This driver needs auditing so that this flag can be removed. */ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->v4l2_lock; + vfd->v4l2_dev = &dev->v4l2_dev; vfd->tvnorms = 0; for (i = 0; i < dev->ext_vv_data->num_stds; i++) vfd->tvnorms |= dev->ext_vv_data->stds[i].id; diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index ce30533fd972..8818e661a42f 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -201,65 +201,6 @@ int saa7146_stop_preview(struct saa7146_fh *fh) } EXPORT_SYMBOL_GPL(saa7146_stop_preview); -/********************************************************************************/ -/* device controls */ - -static struct v4l2_queryctrl controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - .type = V4L2_CTRL_TYPE_INTEGER, - .flags = V4L2_CTRL_FLAG_SLIDER, - },{ - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .type = V4L2_CTRL_TYPE_INTEGER, - .flags = V4L2_CTRL_FLAG_SLIDER, - },{ - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .type = V4L2_CTRL_TYPE_INTEGER, - .flags = V4L2_CTRL_FLAG_SLIDER, - },{ - .id = V4L2_CID_VFLIP, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_HFLIP, - .name = "Horizontal Flip", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, -}; -static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl); - -#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0) - -static struct v4l2_queryctrl* ctrl_by_id(int id) -{ - int i; - - for (i = 0; i < NUM_CONTROLS; i++) - if (controls[i].id == id) - return controls+i; - return NULL; -} - /********************************************************************************/ /* common pagetable functions */ @@ -510,12 +451,13 @@ static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability * strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci)); cap->version = SAA7146_VERSION_CODE; - cap->capabilities = + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities |= dev->ext_vv_data->capabilities; + cap->device_caps |= dev->ext_vv_data->capabilities; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -579,135 +521,58 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtd return 0; } -static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) -{ - const struct v4l2_queryctrl *ctrl; - - if ((c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) && - (c->id < V4L2_CID_PRIVATE_BASE || - c->id >= V4L2_CID_PRIVATE_LASTP1)) - return -EINVAL; - - ctrl = ctrl_by_id(c->id); - if (ctrl == NULL) - return -EINVAL; - - DEB_EE("VIDIOC_QUERYCTRL: id:%d\n", c->id); - *c = *ctrl; - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) +int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) { - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); struct saa7146_vv *vv = dev->vv_data; - const struct v4l2_queryctrl *ctrl; - u32 value = 0; + u32 val; - ctrl = ctrl_by_id(c->id); - if (NULL == ctrl) - return -EINVAL; - switch (c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - value = saa7146_read(dev, BCS_CTRL); - c->value = 0xff & (value >> 24); - DEB_D("V4L2_CID_BRIGHTNESS: %d\n", c->value); - break; - case V4L2_CID_CONTRAST: - value = saa7146_read(dev, BCS_CTRL); - c->value = 0x7f & (value >> 16); - DEB_D("V4L2_CID_CONTRAST: %d\n", c->value); - break; - case V4L2_CID_SATURATION: - value = saa7146_read(dev, BCS_CTRL); - c->value = 0x7f & (value >> 0); - DEB_D("V4L2_CID_SATURATION: %d\n", c->value); - break; - case V4L2_CID_VFLIP: - c->value = vv->vflip; - DEB_D("V4L2_CID_VFLIP: %d\n", c->value); - break; - case V4L2_CID_HFLIP: - c->value = vv->hflip; - DEB_D("V4L2_CID_HFLIP: %d\n", c->value); - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - const struct v4l2_queryctrl *ctrl; - - ctrl = ctrl_by_id(c->id); - if (NULL == ctrl) { - DEB_D("unknown control %d\n", c->id); - return -EINVAL; - } - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_INTEGER: - if (c->value < ctrl->minimum) - c->value = ctrl->minimum; - if (c->value > ctrl->maximum) - c->value = ctrl->maximum; - break; - default: - /* nothing */; - } - - switch (c->id) { - case V4L2_CID_BRIGHTNESS: { - u32 value = saa7146_read(dev, BCS_CTRL); - value &= 0x00ffffff; - value |= (c->value << 24); - saa7146_write(dev, BCS_CTRL, value); + val = saa7146_read(dev, BCS_CTRL); + val &= 0x00ffffff; + val |= (ctrl->val << 24); + saa7146_write(dev, BCS_CTRL, val); saa7146_write(dev, MC2, MASK_22 | MASK_06); break; - } - case V4L2_CID_CONTRAST: { - u32 value = saa7146_read(dev, BCS_CTRL); - value &= 0xff00ffff; - value |= (c->value << 16); - saa7146_write(dev, BCS_CTRL, value); + + case V4L2_CID_CONTRAST: + val = saa7146_read(dev, BCS_CTRL); + val &= 0xff00ffff; + val |= (ctrl->val << 16); + saa7146_write(dev, BCS_CTRL, val); saa7146_write(dev, MC2, MASK_22 | MASK_06); break; - } - case V4L2_CID_SATURATION: { - u32 value = saa7146_read(dev, BCS_CTRL); - value &= 0xffffff00; - value |= (c->value << 0); - saa7146_write(dev, BCS_CTRL, value); + + case V4L2_CID_SATURATION: + val = saa7146_read(dev, BCS_CTRL); + val &= 0xffffff00; + val |= (ctrl->val << 0); + saa7146_write(dev, BCS_CTRL, val); saa7146_write(dev, MC2, MASK_22 | MASK_06); break; - } + case V4L2_CID_HFLIP: /* fixme: we can support changing VFLIP and HFLIP here... */ - if (IS_CAPTURE_ACTIVE(fh) != 0) { - DEB_D("V4L2_CID_HFLIP while active capture\n"); + if ((vv->video_status & STATUS_CAPTURE)) return -EBUSY; - } - vv->hflip = c->value; + vv->hflip = ctrl->val; break; + case V4L2_CID_VFLIP: - if (IS_CAPTURE_ACTIVE(fh) != 0) { - DEB_D("V4L2_CID_VFLIP while active capture\n"); + if ((vv->video_status & STATUS_CAPTURE)) return -EBUSY; - } - vv->vflip = c->value; + vv->vflip = ctrl->val; break; + default: return -EINVAL; } - if (IS_OVERLAY_ACTIVE(fh) != 0) { + if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */ + struct saa7146_fh *fh = vv->video_fh; + saa7146_stop_preview(fh); saa7146_start_preview(fh); } @@ -720,6 +585,8 @@ static int vidioc_g_parm(struct file *file, void *fh, struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct saa7146_vv *vv = dev->vv_data; + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; parm->parm.capture.readbuffers = 1; v4l2_video_std_frame_period(vv->standard->id, &parm->parm.capture.timeperframe); @@ -787,6 +654,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma } f->fmt.pix.field = field; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; if (f->fmt.pix.width > maxw) f->fmt.pix.width = maxw; if (f->fmt.pix.height > maxh) @@ -1141,9 +1009,6 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { .vidioc_dqbuf = vidioc_dqbuf, .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_parm = vidioc_g_parm, @@ -1338,6 +1203,7 @@ static int video_open(struct saa7146_dev *dev, struct file *file) fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24; fh->video_fmt.bytesperline = 0; fh->video_fmt.field = V4L2_FIELD_ANY; + fh->video_fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; sfmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8; diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index ca3f70f0bad5..2bed92ff9476 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -64,8 +64,8 @@ enum { TUNER, AUX1, AUX3, AUX3_YC }; static struct v4l2_input mxb_inputs[MXB_INPUTS] = { { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 8, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 8, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, }; /* this array holds the information, which port of the saa7146 each @@ -90,6 +90,36 @@ struct mxb_routing { u32 output; }; +/* these are the available audio sources, which can switched + to the line- and cd-output individually */ +static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { + { + .index = 0, + .name = "Tuner", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 1, + .name = "AUX1", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 2, + .name = "AUX2", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 3, + .name = "AUX3", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 4, + .name = "Radio (X9)", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 5, + .name = "CD-ROM (X10)", + .capability = V4L2_AUDCAP_STEREO, + } +}; + /* These are the necessary input-output-pins for bringing one audio source (see above) to the CD-output. Note that gain is set to 0 in this table. */ static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { @@ -114,11 +144,6 @@ static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { { { 6, 3 }, { 6, 2 } } /* Mute */ }; -#define MAXCONTROLS 1 -static struct v4l2_queryctrl mxb_controls[] = { - { V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 }, -}; - struct mxb { struct video_device *video_dev; @@ -168,16 +193,45 @@ static inline void tea6420_route_line(struct mxb *mxb, int idx) static struct saa7146_extension extension; +static int mxb_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); + struct mxb *mxb = dev->ext_priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + mxb->cur_mute = ctrl->val; + /* switch the audio-source */ + tea6420_route_line(mxb, ctrl->val ? 6 : + video_audio_connect[mxb->cur_input]); + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops mxb_ctrl_ops = { + .s_ctrl = mxb_s_ctrl, +}; + static int mxb_probe(struct saa7146_dev *dev) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; struct mxb *mxb = NULL; + v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + if (hdl->error) + return hdl->error; mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); if (mxb == NULL) { DEB_D("not enough kernel memory\n"); return -ENOMEM; } + snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); @@ -385,69 +439,6 @@ void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) } */ -static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - int i; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == qc->id) { - *qc = mxb_controls[i]; - DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id); - return 0; - } - } - return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc); -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - int i; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_AUDIO_MUTE) { - vc->value = mxb->cur_mute; - DEB_D("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value); - return 0; - } - - DEB_EE("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - int i = 0; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_AUDIO_MUTE) { - mxb->cur_mute = vc->value; - /* switch the audio-source */ - tea6420_route_line(mxb, vc->value ? 6 : - video_audio_connect[mxb->cur_input]); - DEB_EE("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d\n", vc->value); - } - return 0; -} - static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) { DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); @@ -568,12 +559,8 @@ static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; - if (mxb->cur_input) { - DEB_D("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", - mxb->cur_input); + if (f->tuner) return -EINVAL; - } - *f = mxb->cur_freq; DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency); @@ -592,17 +579,16 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency if (V4L2_TUNER_ANALOG_TV != f->type) return -EINVAL; - if (mxb->cur_input) { - DEB_D("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", - mxb->cur_input); - return -EINVAL; - } - - mxb->cur_freq = *f; DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency); /* tune in desired frequency */ - tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); + tuner_call(mxb, tuner, s_frequency, f); + /* let the tuner subdev clamp the frequency to the tuner range */ + tuner_call(mxb, tuner, g_frequency, f); + mxb->cur_freq = *f; + + if (mxb->cur_input) + return 0; /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ spin_lock(&dev->slock); @@ -612,6 +598,14 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency return 0; } +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + if (a->index >= MXB_AUDIOS) + return -EINVAL; + *a = mxb_audios[a->index]; + return 0; +} + static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; @@ -629,8 +623,13 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) { + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + DEB_D("VIDIOC_S_AUDIO %d\n", a->index); - return 0; + if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) + return 0; + return -EINVAL; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -709,9 +708,6 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data } mxb = (struct mxb *)dev->ext_priv; - vv_data.ops.vidioc_queryctrl = vidioc_queryctrl; - vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl; - vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl; vv_data.ops.vidioc_enum_input = vidioc_enum_input; vv_data.ops.vidioc_g_input = vidioc_g_input; vv_data.ops.vidioc_s_input = vidioc_s_input; @@ -719,6 +715,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data vv_data.ops.vidioc_s_tuner = vidioc_s_tuner; vv_data.ops.vidioc_g_frequency = vidioc_g_frequency; vv_data.ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data.ops.vidioc_enumaudio = vidioc_enumaudio; vv_data.ops.vidioc_g_audio = vidioc_g_audio; vv_data.ops.vidioc_s_audio = vidioc_s_audio; #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -836,7 +833,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_ext_vv vv_data = { .inputs = MXB_INPUTS, - .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO, .stds = &standard[0], .num_stds = sizeof(standard)/sizeof(struct saa7146_standard), .std_callback = &std_callback, diff --git a/drivers/media/video/mxb.h b/drivers/media/video/mxb.h index 400a57ba62ec..dfa4b1cca23a 100644 --- a/drivers/media/video/mxb.h +++ b/drivers/media/video/mxb.h @@ -10,33 +10,4 @@ #define MXB_AUDIOS 6 -/* these are the available audio sources, which can switched - to the line- and cd-output individually */ -static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { - { - .index = 0, - .name = "Tuner", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 1, - .name = "AUX1", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 2, - .name = "AUX2", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 3, - .name = "AUX3", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 4, - .name = "Radio (X9)", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 5, - .name = "CD-ROM (X10)", - .capability = V4L2_AUDCAP_STEREO, - } -}; #endif diff --git a/include/media/saa7146.h b/include/media/saa7146.h index 0f037e8edf9a..c791940c579b 100644 --- a/include/media/saa7146.h +++ b/include/media/saa7146.h @@ -13,6 +13,7 @@ #include #include #include +#include #include /* for vmalloc() */ #include /* for vmalloc_to_page() */ @@ -121,6 +122,7 @@ struct saa7146_dev struct list_head item; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; /* different device locks */ spinlock_t slock; diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 4aeff96ff7d8..b4761edafa69 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -206,6 +206,7 @@ extern struct saa7146_use_ops saa7146_video_uops; int saa7146_start_preview(struct saa7146_fh *fh); int saa7146_stop_preview(struct saa7146_fh *fh); long saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); +int saa7146_s_ctrl(struct v4l2_ctrl *ctrl); /* from saa7146_vbi.c */ extern struct saa7146_use_ops saa7146_vbi_uops; -- cgit v1.2.3 From d69f4a517c0a9b0b4795f4604aed8cf96dcd5223 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 29 Apr 2012 18:59:38 -0300 Subject: [media] mxb: fix initial audio + ntsc/secam support Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mxb.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 2bed92ff9476..aa3d75c1e351 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -268,6 +268,8 @@ static int mxb_probe(struct saa7146_dev *dev) /* we store the pointer in our private data field */ dev->ext_priv = mxb; + v4l2_ctrl_handler_setup(hdl); + return 0; } @@ -374,7 +376,7 @@ static int mxb_init_done(struct saa7146_dev* dev) /* the rest for mxb */ mxb->cur_input = 0; - mxb->cur_mute = 1; + mxb->cur_mute = 0; mxb->cur_mode = V4L2_TUNER_MODE_STEREO; @@ -749,6 +751,10 @@ static int mxb_detach(struct saa7146_dev *dev) DEB_EE("dev:%p\n", dev); + /* mute audio on tea6420s */ + tea6420_route_line(mxb, 6); + tea6420_route_cd(mxb, 6); + saa7146_unregister_device(&mxb->video_dev,dev); if (MXB_BOARD_CAN_DO_VBI(dev)) saa7146_unregister_device(&mxb->vbi_dev, dev); @@ -774,16 +780,22 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa saa7146_write(dev, GPIO_CTRL, 0x00404050); /* unset the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 0); - tuner_call(mxb, core, s_std, std); + saa7111a_call(mxb, core, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, core, s_std, std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; + if (mxb->cur_input) + std = standard->id; DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* set the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 1); - tuner_call(mxb, core, s_std, std); + saa7111a_call(mxb, core, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, core, s_std, std); } return 0; } -- cgit v1.2.3 From 6680427791c94e611220a6cb34ae47dac9e3aa98 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 30 Apr 2012 04:58:27 -0300 Subject: [media] mxb: fix audio handling Instead of using custom ioctls use the VIDIOC_ENUM/G/S/_AUDIO ioctls. Also send the same audio to both CDROM-out and line-out. This is what you would expect and anyway, the CDROM-out connector is unlikely to be used these days as it is obsolete. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mxb.c | 90 ++++++++++++++--------------------------------- drivers/media/video/mxb.h | 13 ------- 2 files changed, 26 insertions(+), 77 deletions(-) delete mode 100644 drivers/media/video/mxb.h diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index aa3d75c1e351..a698c24bea20 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -31,10 +31,11 @@ #include #include -#include "mxb.h" #include "tea6415c.h" #include "tea6420.h" +#define MXB_AUDIOS 6 + #define I2C_SAA7111A 0x24 #define I2C_TDA9840 0x42 #define I2C_TEA6415C 0x43 @@ -62,10 +63,14 @@ MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); enum { TUNER, AUX1, AUX3, AUX3_YC }; static struct v4l2_input mxb_inputs[MXB_INPUTS] = { - { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 8, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 8, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x2f, 0, + V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, + { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x2f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x2f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x2f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; /* this array holds the information, which port of the saa7146 each @@ -160,6 +165,7 @@ struct mxb int cur_mode; /* current audio mode (mono, stereo, ...) */ int cur_input; /* current input */ + int cur_audinput; /* current audio input */ int cur_mute; /* current mute status */ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ }; @@ -175,16 +181,12 @@ struct mxb #define call_all(dev, o, f, args...) \ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) -static inline void tea6420_route_cd(struct mxb *mxb, int idx) +static inline void tea6420_route(struct mxb *mxb, int idx) { v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); -} - -static inline void tea6420_route_line(struct mxb *mxb, int idx) -{ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, @@ -203,7 +205,7 @@ static int mxb_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_AUDIO_MUTE: mxb->cur_mute = ctrl->val; /* switch the audio-source */ - tea6420_route_line(mxb, ctrl->val ? 6 : + tea6420_route(mxb, ctrl->val ? 6 : video_audio_connect[mxb->cur_input]); break; default: @@ -365,8 +367,7 @@ static int mxb_init_done(struct saa7146_dev* dev) tuner_call(mxb, core, s_std, std); /* mute audio on tea6420s */ - tea6420_route_line(mxb, 6); - tea6420_route_cd(mxb, 6); + tea6420_route(mxb, 6); /* switch to tuner-channel on tea6415c */ tea6415c_call(mxb, video, s_routing, 3, 17, 0); @@ -376,6 +377,7 @@ static int mxb_init_done(struct saa7146_dev* dev) /* the rest for mxb */ mxb->cur_input = 0; + mxb->cur_audinput = video_audio_connect[mxb->cur_input]; mxb->cur_mute = 0; mxb->cur_mode = V4L2_TUNER_MODE_STEREO; @@ -512,9 +514,10 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) pr_err("VIDIOC_S_INPUT: could not address saa7111a\n"); + mxb->cur_audinput = video_audio_connect[input]; /* switch the audio-source only if necessary */ if (0 == mxb->cur_mute) - tea6420_route_line(mxb, video_audio_connect[input]); + tea6420_route(mxb, mxb->cur_audinput); return 0; } @@ -619,7 +622,7 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) } DEB_EE("VIDIOC_G_AUDIO %d\n", a->index); - memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); + memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_audinput]], sizeof(struct v4l2_audio)); return 0; } @@ -629,8 +632,13 @@ static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) struct mxb *mxb = (struct mxb *)dev->ext_priv; DEB_D("VIDIOC_S_AUDIO %d\n", a->index); - if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) + if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) { + if (mxb->cur_audinput != a->index) { + mxb->cur_audinput = a->index; + tea6420_route(mxb, a->index); + } return 0; + } return -EINVAL; } @@ -650,50 +658,6 @@ static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_regist } #endif -static long vidioc_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - switch (cmd) { - case MXB_S_AUDIO_CD: - { - int i = *(int *)arg; - - if (i < 0 || i >= MXB_AUDIOS) { - DEB_D("invalid argument to MXB_S_AUDIO_CD: i:%d\n", i); - return -EINVAL; - } - - DEB_EE("MXB_S_AUDIO_CD: i:%d\n", i); - - tea6420_route_cd(mxb, i); - return 0; - } - case MXB_S_AUDIO_LINE: - { - int i = *(int *)arg; - - if (i < 0 || i >= MXB_AUDIOS) { - DEB_D("invalid argument to MXB_S_AUDIO_LINE: i:%d\n", - i); - return -EINVAL; - } - - DEB_EE("MXB_S_AUDIO_LINE: i:%d\n", i); - tea6420_route_line(mxb, i); - return 0; - } - default: -/* - DEB2(pr_err("does not handle this ioctl\n")); -*/ - return -ENOTTY; - } - return 0; -} - static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ @@ -724,7 +688,6 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data vv_data.ops.vidioc_g_register = vidioc_g_register; vv_data.ops.vidioc_s_register = vidioc_s_register; #endif - vv_data.ops.vidioc_default = vidioc_default; if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { ERR("cannot register capture v4l2 device. skipping.\n"); saa7146_vv_release(dev); @@ -752,8 +715,7 @@ static int mxb_detach(struct saa7146_dev *dev) DEB_EE("dev:%p\n", dev); /* mute audio on tea6420s */ - tea6420_route_line(mxb, 6); - tea6420_route_cd(mxb, 6); + tea6420_route(mxb, 6); saa7146_unregister_device(&mxb->video_dev,dev); if (MXB_BOARD_CAN_DO_VBI(dev)) @@ -852,7 +814,7 @@ static struct saa7146_ext_vv vv_data = { }; static struct saa7146_extension extension = { - .name = MXB_IDENTIFIER, + .name = "Multimedia eXtension Board", .flags = SAA7146_USE_I2C_IRQ, .pci_tbl = &pci_tbl[0], diff --git a/drivers/media/video/mxb.h b/drivers/media/video/mxb.h deleted file mode 100644 index dfa4b1cca23a..000000000000 --- a/drivers/media/video/mxb.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __MXB__ -#define __MXB__ - -#define BASE_VIDIOC_MXB 10 - -#define MXB_S_AUDIO_CD _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+0, int) -#define MXB_S_AUDIO_LINE _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+1, int) - -#define MXB_IDENTIFIER "Multimedia eXtension Board" - -#define MXB_AUDIOS 6 - -#endif -- cgit v1.2.3 From aaab4951a688e4c3f43e0ce09855718e9ad5060d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 30 Apr 2012 05:02:11 -0300 Subject: [media] mxb: simplify a line that was too long Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mxb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index a698c24bea20..d26781871b1a 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -622,7 +622,7 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) } DEB_EE("VIDIOC_G_AUDIO %d\n", a->index); - memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_audinput]], sizeof(struct v4l2_audio)); + *a = mxb_audios[video_audio_connect[mxb->cur_audinput]]; return 0; } -- cgit v1.2.3 From 8bb5bafa8057a91613d50f7b0bec025852360237 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 10:06:40 -0300 Subject: [media] tda9840: fix setting of the audio mode You have to first detect the current rxsubchans (mono, stereo or bilingual), and depending on that you can set the audio mode. It does not automatically switch when the audio channels change. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tda9840.c | 75 +++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 465d7086babf..3d7ddd93282d 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -66,29 +66,53 @@ static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) val, reg); } +static int tda9840_status(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 byte; + + if (1 != i2c_master_recv(client, &byte, 1)) { + v4l2_dbg(1, debug, sd, + "i2c_master_recv() failed\n"); + return -EIO; + } + + if (byte & 0x80) { + v4l2_dbg(1, debug, sd, + "TDA9840_DETECT: register contents invalid\n"); + return -EINVAL; + } + + v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); + return byte & 0x60; +} + static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) { + int stat = tda9840_status(sd); int byte; if (t->index) return -EINVAL; - switch (t->audmode) { - case V4L2_TUNER_MODE_STEREO: - byte = TDA9840_SET_STEREO; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - byte = TDA9840_SET_BOTH; - break; - case V4L2_TUNER_MODE_LANG1: - byte = TDA9840_SET_LANG1; - break; - case V4L2_TUNER_MODE_LANG2: - byte = TDA9840_SET_LANG2; - break; - default: + stat = stat < 0 ? 0 : stat; + if (stat == 0 || stat == 0x60) /* mono input */ byte = TDA9840_SET_MONO; - break; + else if (stat == 0x40) /* stereo input */ + byte = (t->audmode == V4L2_TUNER_MODE_MONO) ? + TDA9840_SET_MONO : TDA9840_SET_STEREO; + else { /* bilingual */ + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG1_LANG2: + byte = TDA9840_SET_BOTH; + break; + case V4L2_TUNER_MODE_LANG2: + byte = TDA9840_SET_LANG2; + break; + default: + byte = TDA9840_SET_LANG1; + break; + } } v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); tda9840_write(sd, SWITCH, byte); @@ -97,25 +121,14 @@ static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 byte; - - t->rxsubchans = V4L2_TUNER_SUB_MONO; - if (1 != i2c_master_recv(client, &byte, 1)) { - v4l2_dbg(1, debug, sd, - "i2c_master_recv() failed\n"); - return -EIO; - } + int stat = tda9840_status(sd); - if (byte & 0x80) { - v4l2_dbg(1, debug, sd, - "TDA9840_DETECT: register contents invalid\n"); - return -EINVAL; - } + if (stat < 0) + return stat; - v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); + t->rxsubchans = V4L2_TUNER_SUB_MONO; - switch (byte & 0x60) { + switch (stat & 0x60) { case 0x00: t->rxsubchans = V4L2_TUNER_SUB_MONO; break; -- cgit v1.2.3 From 4894b709d14c593466cc05e55ee493af931205e8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 10:12:22 -0300 Subject: [media] mxb: fix audio and standard handling Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mxb.c | 53 +++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index d26781871b1a..db0c5ddec87f 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -63,13 +63,13 @@ MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); enum { TUNER, AUX1, AUX3, AUX3_YC }; static struct v4l2_input mxb_inputs[MXB_INPUTS] = { - { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x2f, 0, + { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0, V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, - { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x2f, 0, + { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x2f, 0, + { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x2f, 0, + { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; @@ -181,6 +181,15 @@ struct mxb #define call_all(dev, o, f, args...) \ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) +static void mxb_update_audmode(struct mxb *mxb) +{ + struct v4l2_tuner t = { + .audmode = mxb->cur_mode, + }; + + tda9840_call(mxb, tuner, s_tuner, &t); +} + static inline void tea6420_route(struct mxb *mxb, int idx) { v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, @@ -224,7 +233,7 @@ static int mxb_probe(struct saa7146_dev *dev) struct mxb *mxb = NULL; v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); if (hdl->error) return hdl->error; mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); @@ -344,6 +353,9 @@ static int mxb_init_done(struct saa7146_dev* dev) int i = 0, err = 0; + /* mute audio on tea6420s */ + tea6420_route(mxb, 6); + /* select video mode in saa7111a */ saa7111a_call(mxb, core, s_std, std); @@ -364,11 +376,12 @@ static int mxb_init_done(struct saa7146_dev* dev) tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); /* set a default video standard */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ + saa7146_write(dev, GPIO_CTRL, 0x00404050); + saa7111a_call(mxb, core, s_gpio, 1); + saa7111a_call(mxb, core, s_std, std); tuner_call(mxb, core, s_std, std); - /* mute audio on tea6420s */ - tea6420_route(mxb, 6); - /* switch to tuner-channel on tea6415c */ tea6415c_call(mxb, video, s_routing, 3, 17, 0); @@ -378,9 +391,10 @@ static int mxb_init_done(struct saa7146_dev* dev) /* the rest for mxb */ mxb->cur_input = 0; mxb->cur_audinput = video_audio_connect[mxb->cur_input]; - mxb->cur_mute = 0; + mxb->cur_mute = 1; mxb->cur_mode = V4L2_TUNER_MODE_STEREO; + mxb_update_audmode(mxb); /* check if the saa7740 (aka 'sound arena module') is present on the mxb. if so, we must initialize it. due to lack of @@ -518,6 +532,8 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) /* switch the audio-source only if necessary */ if (0 == mxb->cur_mute) tea6420_route(mxb, mxb->cur_audinput); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); return 0; } @@ -591,6 +607,8 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency /* let the tuner subdev clamp the frequency to the tuner range */ tuner_call(mxb, tuner, g_frequency, f); mxb->cur_freq = *f; + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); if (mxb->cur_input) return 0; @@ -616,13 +634,8 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; - if (a->index > MXB_INPUTS) { - DEB_D("VIDIOC_G_AUDIO %d out of range\n", a->index); - return -EINVAL; - } - - DEB_EE("VIDIOC_G_AUDIO %d\n", a->index); - *a = mxb_audios[video_audio_connect[mxb->cur_audinput]]; + DEB_EE("VIDIOC_G_AUDIO\n"); + *a = mxb_audios[mxb->cur_audinput]; return 0; } @@ -636,6 +649,8 @@ static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) if (mxb->cur_audinput != a->index) { mxb->cur_audinput = a->index; tea6420_route(mxb, a->index); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); } return 0; } @@ -738,9 +753,8 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa v4l2_std_id std = V4L2_STD_PAL_I; DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n"); - /* set the 7146 gpio register -- I don't know what this does exactly */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); - /* unset the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 0); saa7111a_call(mxb, core, s_std, std); if (mxb->cur_input == 0) @@ -751,9 +765,8 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa if (mxb->cur_input) std = standard->id; DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); - /* set the 7146 gpio register -- I don't know what this does exactly */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); - /* set the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 1); saa7111a_call(mxb, core, s_std, std); if (mxb->cur_input == 0) -- cgit v1.2.3 From 5da545ad08a3c6ea71d3ba074adc7582e7e9a024 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 11:06:44 -0300 Subject: [media] saa7146: move overlay information from saa7146_fh into saa7146_vv This is global information, not per-filehandle information. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 10 ++++++++++ drivers/media/common/saa7146_hlp.c | 23 +++++++++++----------- drivers/media/common/saa7146_video.c | 37 +++++++++++++++++++----------------- include/media/saa7146_vv.h | 5 +---- 4 files changed, 43 insertions(+), 32 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index f14e218bed16..68047c93fd9d 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -436,6 +436,7 @@ static const struct v4l2_ctrl_ops saa7146_ctrl_ops = { int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) { struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + struct v4l2_pix_format *fmt; struct saa7146_vv *vv; int err; @@ -496,6 +497,15 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) saa7146_vbi_uops.init(dev,vv); + fmt = &vv->ov_fb.fmt; + fmt->width = vv->standard->h_max_out; + fmt->height = vv->standard->v_max_out; + fmt->pixelformat = V4L2_PIX_FMT_RGB565; + fmt->bytesperline = 2 * fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; + vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; dev->vv_data = vv; dev->vv_callback = &vv_callback; diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c index bc1f545c95cb..be746d1aee9a 100644 --- a/drivers/media/common/saa7146_hlp.c +++ b/drivers/media/common/saa7146_hlp.c @@ -343,9 +343,9 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa struct saa7146_vv *vv = dev->vv_data; __le32 *clipping = vv->d_clipping.cpu_addr; - int width = fh->ov.win.w.width; - int height = fh->ov.win.w.height; - int clipcount = fh->ov.nclips; + int width = vv->ov.win.w.width; + int height = vv->ov.win.w.height; + int clipcount = vv->ov.nclips; u32 line_list[32]; u32 pixel_list[32]; @@ -365,10 +365,10 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa for(i = 0; i < clipcount; i++) { int l = 0, r = 0, t = 0, b = 0; - x[i] = fh->ov.clips[i].c.left; - y[i] = fh->ov.clips[i].c.top; - w[i] = fh->ov.clips[i].c.width; - h[i] = fh->ov.clips[i].c.height; + x[i] = vv->ov.clips[i].c.left; + y[i] = vv->ov.clips[i].c.top; + w[i] = vv->ov.clips[i].c.width; + h[i] = vv->ov.clips[i].c.height; if( w[i] < 0) { x[i] += w[i]; w[i] = -w[i]; @@ -485,13 +485,14 @@ static void saa7146_disable_clipping(struct saa7146_dev *dev) static void saa7146_set_clipping_rect(struct saa7146_fh *fh) { struct saa7146_dev *dev = fh->dev; - enum v4l2_field field = fh->ov.win.field; + struct saa7146_vv *vv = dev->vv_data; + enum v4l2_field field = vv->ov.win.field; struct saa7146_video_dma vdma2; u32 clip_format; u32 arbtr_ctrl; /* check clipcount, disable clipping if clipcount == 0*/ - if( fh->ov.nclips == 0 ) { + if (vv->ov.nclips == 0) { saa7146_disable_clipping(dev); return; } @@ -651,8 +652,8 @@ int saa7146_enable_overlay(struct saa7146_fh *fh) struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; - saa7146_set_window(dev, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field); - saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field, vv->ov_fmt->pixelformat); + saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field); + saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat); saa7146_set_output_format(dev, vv->ov_fmt->trans); saa7146_set_clipping_rect(fh); diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 8818e661a42f..e1b639b76e38 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -112,8 +112,8 @@ int saa7146_start_preview(struct saa7146_fh *fh) DEB_EE("dev:%p, fh:%p\n", dev, fh); - /* check if we have overlay informations */ - if( NULL == fh->ov.fh ) { + /* check if we have overlay information */ + if (vv->ov.fh == NULL) { DEB_D("no overlay data available. try S_FMT first.\n"); return -EAGAIN; } @@ -139,19 +139,18 @@ int saa7146_start_preview(struct saa7146_fh *fh) return -EBUSY; } - fmt.fmt.win = fh->ov.win; + fmt.fmt.win = vv->ov.win; err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt); if (0 != err) { saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); return -EBUSY; } - fh->ov.win = fmt.fmt.win; - vv->ov_data = &fh->ov; + vv->ov.win = fmt.fmt.win; DEB_D("%dx%d+%d+%d %s field=%s\n", - fh->ov.win.w.width, fh->ov.win.w.height, - fh->ov.win.w.left, fh->ov.win.w.top, - vv->ov_fmt->name, v4l2_field_names[fh->ov.win.field]); + vv->ov.win.w.width, vv->ov.win.w.height, + vv->ov.win.w.left, vv->ov.win.w.top, + vv->ov_fmt->name, v4l2_field_names[vv->ov.win.field]); if (0 != (ret = saa7146_enable_overlay(fh))) { DEB_D("enabling overlay failed: %d\n", ret); @@ -468,6 +467,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f *fb = vv->ov_fb; fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + fb->flags = V4L2_FBUF_FLAG_PRIMARY; return 0; } @@ -601,7 +601,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.win = ((struct saa7146_fh *)fh)->ov.win; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.win = vv->ov.win; return 0; } @@ -768,17 +771,17 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_f err = vidioc_try_fmt_vid_overlay(file, fh, f); if (0 != err) return err; - fh->ov.win = f->fmt.win; - fh->ov.nclips = f->fmt.win.clipcount; - if (fh->ov.nclips > 16) - fh->ov.nclips = 16; - if (copy_from_user(fh->ov.clips, f->fmt.win.clips, - sizeof(struct v4l2_clip) * fh->ov.nclips)) { + vv->ov.win = f->fmt.win; + vv->ov.nclips = f->fmt.win.clipcount; + if (vv->ov.nclips > 16) + vv->ov.nclips = 16; + if (copy_from_user(vv->ov.clips, f->fmt.win.clips, + sizeof(struct v4l2_clip) * vv->ov.nclips)) { return -EFAULT; } - /* fh->ov.fh is used to indicate that we have valid overlay informations, too */ - fh->ov.fh = fh; + /* vv->ov.fh is used to indicate that we have valid overlay informations, too */ + vv->ov.fh = fh; /* check if our current overlay is active */ if (IS_OVERLAY_ACTIVE(fh) != 0) { diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index b4761edafa69..feb444b554aa 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -88,9 +88,6 @@ struct saa7146_fh { /* if this is a vbi or capture open */ enum v4l2_buf_type type; - /* video overlay */ - struct saa7146_overlay ov; - /* video capture */ struct videobuf_queue video_q; struct v4l2_pix_format video_fmt; @@ -119,9 +116,9 @@ struct saa7146_vv struct saa7146_fh *video_fh; /* video overlay */ + struct saa7146_overlay ov; struct v4l2_framebuffer ov_fb; struct saa7146_format *ov_fmt; - struct saa7146_overlay *ov_data; struct saa7146_fh *ov_suspend; /* video capture */ -- cgit v1.2.3 From fd74d6eb4c2c1caa18208df32d9d38b5fe9738fc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 11:17:35 -0300 Subject: [media] saa7146: move video_fmt from saa7146_fh to saa7146_vv This is a global structure and does not belong to saa7146_fh. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 10 ++++++ drivers/media/common/saa7146_video.c | 62 ++++++++++++++++-------------------- include/media/saa7146_vv.h | 2 +- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 68047c93fd9d..b8d0d7df5d67 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -504,6 +504,16 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) fmt->bytesperline = 2 * fmt->width; fmt->sizeimage = fmt->bytesperline * fmt->height; fmt->colorspace = V4L2_COLORSPACE_SRGB; + + fmt = &vv->video_fmt; + fmt->width = 384; + fmt->height = 288; + fmt->pixelformat = V4L2_PIX_FMT_BGR24; + fmt->field = V4L2_FIELD_ANY; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->bytesperline = 3 * fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height; + vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; dev->vv_data = vv; diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index e1b639b76e38..f57dccf851c1 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -353,7 +353,7 @@ static int video_begin(struct saa7146_fh *fh) } } - fmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); /* we need to have a valid format set here */ BUG_ON(NULL == fmt); @@ -405,7 +405,7 @@ static int video_end(struct saa7146_fh *fh, struct file *file) return -EBUSY; } - fmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); /* we need to have a valid format set here */ BUG_ON(NULL == fmt); @@ -595,7 +595,10 @@ static int vidioc_g_parm(struct file *file, void *fh, static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.pix = ((struct saa7146_fh *)fh)->video_fmt; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.pix = vv->video_fmt; return 0; } @@ -754,9 +757,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_forma err = vidioc_try_fmt_vid_cap(file, fh, f); if (0 != err) return err; - fh->video_fmt = f->fmt.pix; + vv->video_fmt = f->fmt.pix; DEB_EE("set to pixelformat '%4.4s'\n", - (char *)&fh->video_fmt.pixelformat); + (char *)&vv->video_fmt.pixelformat); return 0; } @@ -1053,44 +1056,44 @@ static int buffer_prepare(struct videobuf_queue *q, DEB_CAP("vbuf:%p\n", vb); /* sanity checks */ - if (fh->video_fmt.width < 48 || - fh->video_fmt.height < 32 || - fh->video_fmt.width > vv->standard->h_max_out || - fh->video_fmt.height > vv->standard->v_max_out) { + if (vv->video_fmt.width < 48 || + vv->video_fmt.height < 32 || + vv->video_fmt.width > vv->standard->h_max_out || + vv->video_fmt.height > vv->standard->v_max_out) { DEB_D("w (%d) / h (%d) out of bounds\n", - fh->video_fmt.width, fh->video_fmt.height); + vv->video_fmt.width, vv->video_fmt.height); return -EINVAL; } - size = fh->video_fmt.sizeimage; + size = vv->video_fmt.sizeimage; if (0 != buf->vb.baddr && buf->vb.bsize < size) { DEB_D("size mismatch\n"); return -EINVAL; } DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", - fh->video_fmt.width, fh->video_fmt.height, - size, v4l2_field_names[fh->video_fmt.field]); - if (buf->vb.width != fh->video_fmt.width || - buf->vb.bytesperline != fh->video_fmt.bytesperline || - buf->vb.height != fh->video_fmt.height || + vv->video_fmt.width, vv->video_fmt.height, + size, v4l2_field_names[vv->video_fmt.field]); + if (buf->vb.width != vv->video_fmt.width || + buf->vb.bytesperline != vv->video_fmt.bytesperline || + buf->vb.height != vv->video_fmt.height || buf->vb.size != size || buf->vb.field != field || - buf->vb.field != fh->video_fmt.field || - buf->fmt != &fh->video_fmt) { + buf->vb.field != vv->video_fmt.field || + buf->fmt != &vv->video_fmt) { saa7146_dma_free(dev,q,buf); } if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { struct saa7146_format *sfmt; - buf->vb.bytesperline = fh->video_fmt.bytesperline; - buf->vb.width = fh->video_fmt.width; - buf->vb.height = fh->video_fmt.height; + buf->vb.bytesperline = vv->video_fmt.bytesperline; + buf->vb.width = vv->video_fmt.width; + buf->vb.height = vv->video_fmt.height; buf->vb.size = size; buf->vb.field = field; - buf->fmt = &fh->video_fmt; - buf->vb.field = fh->video_fmt.field; + buf->fmt = &vv->video_fmt; + buf->vb.field = vv->video_fmt.field; sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); @@ -1126,11 +1129,12 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned { struct file *file = q->priv_data; struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) *count = MAX_SAA7146_CAPTURE_BUFFERS; - *size = fh->video_fmt.sizeimage; + *size = vv->video_fmt.sizeimage; /* check if we exceed the "max_memory" parameter */ if( (*count * *size) > (max_memory*1048576) ) { @@ -1199,16 +1203,6 @@ static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) static int video_open(struct saa7146_dev *dev, struct file *file) { struct saa7146_fh *fh = file->private_data; - struct saa7146_format *sfmt; - - fh->video_fmt.width = 384; - fh->video_fmt.height = 288; - fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24; - fh->video_fmt.bytesperline = 0; - fh->video_fmt.field = V4L2_FIELD_ANY; - fh->video_fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; - sfmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); - fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8; videobuf_queue_sg_init(&fh->video_q, &video_qops, &dev->pci->dev, &dev->slock, diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index feb444b554aa..7f61645a1c7b 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -90,7 +90,6 @@ struct saa7146_fh { /* video capture */ struct videobuf_queue video_q; - struct v4l2_pix_format video_fmt; /* vbi capture */ struct videobuf_queue vbi_q; @@ -123,6 +122,7 @@ struct saa7146_vv /* video capture */ struct saa7146_dmaqueue video_q; + struct v4l2_pix_format video_fmt; enum v4l2_field last_field; /* common: fixme? shouldn't this be in saa7146_fh? -- cgit v1.2.3 From fca3469aa844e2ae993445aa5f41397003199be7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 11:28:20 -0300 Subject: [media] saa7146: move vbi fields from saa7146_fh to saa7146_vv This fields are global and don't belong in a fh struct. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 15 +++++++++++++++ drivers/media/common/saa7146_vbi.c | 23 +++++------------------ drivers/media/common/saa7146_video.c | 5 ++++- include/media/saa7146_vv.h | 4 ++-- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index b8d0d7df5d67..776dfc35be57 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -437,6 +437,7 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) { struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; struct v4l2_pix_format *fmt; + struct v4l2_vbi_format *vbi; struct saa7146_vv *vv; int err; @@ -514,6 +515,20 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) fmt->bytesperline = 3 * fmt->width; fmt->sizeimage = fmt->bytesperline * fmt->height; + vbi = &vv->vbi_fmt; + vbi->sampling_rate = 27000000; + vbi->offset = 248; /* todo */ + vbi->samples_per_line = 720 * 2; + vbi->sample_format = V4L2_PIX_FMT_GREY; + + /* fixme: this only works for PAL */ + vbi->start[0] = 5; + vbi->count[0] = 16; + vbi->start[1] = 312; + vbi->count[1] = 16; + + init_timer(&vv->vbi_read_timeout); + vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; dev->vv_data = vv; diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c index b2e718343739..c930aa01f023 100644 --- a/drivers/media/common/saa7146_vbi.c +++ b/drivers/media/common/saa7146_vbi.c @@ -344,7 +344,7 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file) vv->vbi_streaming = NULL; del_timer(&vv->vbi_q.timeout); - del_timer(&fh->vbi_read_timeout); + del_timer(&vv->vbi_read_timeout); spin_unlock_irqrestore(&dev->slock, flags); } @@ -377,6 +377,7 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) static int vbi_open(struct saa7146_dev *dev, struct file *file) { struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); int ret = 0; @@ -395,19 +396,6 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); saa7146_write(dev, MC2, (MASK_04|MASK_20)); - memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt)); - - fh->vbi_fmt.sampling_rate = 27000000; - fh->vbi_fmt.offset = 248; /* todo */ - fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture; - fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY; - - /* fixme: this only works for PAL */ - fh->vbi_fmt.start[0] = 5; - fh->vbi_fmt.count[0] = 16; - fh->vbi_fmt.start[1] = 312; - fh->vbi_fmt.count[1] = 16; - videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops, &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VBI_CAPTURE, @@ -415,9 +403,8 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) sizeof(struct saa7146_buf), file, &dev->v4l2_lock); - init_timer(&fh->vbi_read_timeout); - fh->vbi_read_timeout.function = vbi_read_timeout; - fh->vbi_read_timeout.data = (unsigned long)file; + vv->vbi_read_timeout.function = vbi_read_timeout; + vv->vbi_read_timeout.data = (unsigned long)file; /* initialize the brs */ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { @@ -488,7 +475,7 @@ static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff return -EBUSY; } - mod_timer(&fh->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1, file->f_flags & O_NONBLOCK); /* diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index f57dccf851c1..9a99835e1e44 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -613,7 +613,10 @@ static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_for static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.vbi = ((struct saa7146_fh *)fh)->vbi_fmt; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.vbi = vv->vbi_fmt; return 0; } diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 7f61645a1c7b..658ae8361d5e 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -93,8 +93,6 @@ struct saa7146_fh { /* vbi capture */ struct videobuf_queue vbi_q; - struct v4l2_vbi_format vbi_fmt; - struct timer_list vbi_read_timeout; unsigned int resources; /* resource management for device open */ }; @@ -106,6 +104,8 @@ struct saa7146_vv { /* vbi capture */ struct saa7146_dmaqueue vbi_q; + struct v4l2_vbi_format vbi_fmt; + struct timer_list vbi_read_timeout; /* vbi workaround interrupt queue */ wait_queue_head_t vbi_wq; int vbi_fieldcount; -- cgit v1.2.3 From 6694a5608f909584c2aec99b0c07bfbdd6b02464 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 11:39:08 -0300 Subject: [media] saa7146: remove the unneeded type field from saa7146_fh This information can also be retrieved from struct video_device. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 30 +++++++++++++++++------------- include/media/saa7146_vv.h | 2 -- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 776dfc35be57..66f6e79dd97b 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -229,9 +229,8 @@ static int fops_open(struct file *file) file->private_data = fh; fh->dev = dev; - fh->type = type; - if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (vdev->vfl_type == VFL_TYPE_VBI) { DEB_S("initializing vbi...\n"); if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) result = saa7146_vbi_uops.open(dev,file); @@ -263,6 +262,7 @@ out: static int fops_release(struct file *file) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; struct saa7146_dev *dev = fh->dev; @@ -271,7 +271,7 @@ static int fops_release(struct file *file) if (mutex_lock_interruptible(&saa7146_devices_lock)) return -ERESTARTSYS; - if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (vdev->vfl_type == VFL_TYPE_VBI) { if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) saa7146_vbi_uops.release(dev,file); if (dev->ext_vv_data->vbi_fops.release) @@ -291,17 +291,18 @@ static int fops_release(struct file *file) static int fops_mmap(struct file *file, struct vm_area_struct * vma) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; struct videobuf_queue *q; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: { DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", file, vma); q = &fh->video_q; break; } - case V4L2_BUF_TYPE_VBI_CAPTURE: { + case VFL_TYPE_VBI: { DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n", file, vma); q = &fh->vbi_q; @@ -317,13 +318,14 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma) static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; struct videobuf_buffer *buf = NULL; struct videobuf_queue *q; DEB_EE("file:%p, poll:%p\n", file, wait); - if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { + if (vdev->vfl_type == VFL_TYPE_VBI) { if( 0 == fh->vbi_q.streaming ) return videobuf_poll_stream(file, &fh->vbi_q, wait); q = &fh->vbi_q; @@ -352,16 +354,17 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: /* DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count); */ return saa7146_video_uops.read(file,data,count,ppos); - case V4L2_BUF_TYPE_VBI_CAPTURE: + case VFL_TYPE_VBI: /* DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count); @@ -377,12 +380,13 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: return -EINVAL; - case V4L2_BUF_TYPE_VBI_CAPTURE: + case VFL_TYPE_VBI: if (fh->dev->ext_vv_data->vbi_fops.write) return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); else diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 658ae8361d5e..e9f434c74072 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -85,8 +85,6 @@ struct saa7146_overlay { /* per open data */ struct saa7146_fh { struct saa7146_dev *dev; - /* if this is a vbi or capture open */ - enum v4l2_buf_type type; /* video capture */ struct videobuf_queue video_q; -- cgit v1.2.3 From 9bb601935b8495cd4ef8aea389df77233d6be5dd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 11:45:27 -0300 Subject: [media] saa7146: rename vbi/video_q to vbi/video_dmaq There was also a vbi_q and video_q in saa7146_fh, so that was confusing. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_vbi.c | 31 +++++++++++++++---------------- drivers/media/common/saa7146_video.c | 16 ++++++++-------- include/media/saa7146_vv.h | 4 ++-- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c index c930aa01f023..1e71e374bbfe 100644 --- a/drivers/media/common/saa7146_vbi.c +++ b/drivers/media/common/saa7146_vbi.c @@ -211,7 +211,7 @@ static int buffer_activate(struct saa7146_dev *dev, DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next); saa7146_set_vbi_capture(dev,buf,next); - mod_timer(&vv->vbi_q.timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT); return 0; } @@ -294,7 +294,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_VBI("vb:%p\n", vb); - saa7146_buffer_queue(dev,&vv->vbi_q,buf); + saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf); } static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) @@ -335,15 +335,14 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file) /* shut down dma 3 transfers */ saa7146_write(dev, MC1, MASK_20); - if (vv->vbi_q.curr) { - saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE); - } + if (vv->vbi_dmaq.curr) + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); videobuf_queue_cancel(&fh->vbi_q); vv->vbi_streaming = NULL; - del_timer(&vv->vbi_q.timeout); + del_timer(&vv->vbi_dmaq.timeout); del_timer(&vv->vbi_read_timeout); spin_unlock_irqrestore(&dev->slock, flags); @@ -364,12 +363,12 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) { DEB_VBI("dev:%p\n", dev); - INIT_LIST_HEAD(&vv->vbi_q.queue); + INIT_LIST_HEAD(&vv->vbi_dmaq.queue); - init_timer(&vv->vbi_q.timeout); - vv->vbi_q.timeout.function = saa7146_buffer_timeout; - vv->vbi_q.timeout.data = (unsigned long)(&vv->vbi_q); - vv->vbi_q.dev = dev; + init_timer(&vv->vbi_dmaq.timeout); + vv->vbi_dmaq.timeout.function = saa7146_buffer_timeout; + vv->vbi_dmaq.timeout.data = (unsigned long)(&vv->vbi_dmaq); + vv->vbi_dmaq.dev = dev; init_waitqueue_head(&vv->vbi_wq); } @@ -440,16 +439,16 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) struct saa7146_vv *vv = dev->vv_data; spin_lock(&dev->slock); - if (vv->vbi_q.curr) { - DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_q.curr); + if (vv->vbi_dmaq.curr) { + DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr); /* this must be += 2, one count for each field */ vv->vbi_fieldcount+=2; - vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount; - saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE); + vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount; + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); } else { DEB_VBI("dev:%p\n", dev); } - saa7146_buffer_next(dev,&vv->vbi_q,1); + saa7146_buffer_next(dev, &vv->vbi_dmaq, 1); spin_unlock(&dev->slock); } diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 9a99835e1e44..850799051bef 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -1035,7 +1035,7 @@ static int buffer_activate (struct saa7146_dev *dev, buf->vb.state = VIDEOBUF_ACTIVE; saa7146_set_capture(dev,buf,next); - mod_timer(&vv->video_q.timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); return 0; } @@ -1158,7 +1158,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_CAP("vbuf:%p\n", vb); - saa7146_buffer_queue(fh->dev,&vv->video_q,buf); + saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf); } static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) @@ -1187,12 +1187,12 @@ static struct videobuf_queue_ops video_qops = { static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) { - INIT_LIST_HEAD(&vv->video_q.queue); + INIT_LIST_HEAD(&vv->video_dmaq.queue); - init_timer(&vv->video_q.timeout); - vv->video_q.timeout.function = saa7146_buffer_timeout; - vv->video_q.timeout.data = (unsigned long)(&vv->video_q); - vv->video_q.dev = dev; + init_timer(&vv->video_dmaq.timeout); + vv->video_dmaq.timeout.function = saa7146_buffer_timeout; + vv->video_dmaq.timeout.data = (unsigned long)(&vv->video_dmaq); + vv->video_dmaq.dev = dev; /* set some default values */ vv->standard = &dev->ext_vv_data->stds[0]; @@ -1237,7 +1237,7 @@ static void video_close(struct saa7146_dev *dev, struct file *file) static void video_irq_done(struct saa7146_dev *dev, unsigned long st) { struct saa7146_vv *vv = dev->vv_data; - struct saa7146_dmaqueue *q = &vv->video_q; + struct saa7146_dmaqueue *q = &vv->video_dmaq; spin_lock(&dev->slock); DEB_CAP("called\n"); diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index e9f434c74072..2cc32c514173 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -101,7 +101,7 @@ struct saa7146_fh { struct saa7146_vv { /* vbi capture */ - struct saa7146_dmaqueue vbi_q; + struct saa7146_dmaqueue vbi_dmaq; struct v4l2_vbi_format vbi_fmt; struct timer_list vbi_read_timeout; /* vbi workaround interrupt queue */ @@ -119,7 +119,7 @@ struct saa7146_vv struct saa7146_fh *ov_suspend; /* video capture */ - struct saa7146_dmaqueue video_q; + struct saa7146_dmaqueue video_dmaq; struct v4l2_pix_format video_fmt; enum v4l2_field last_field; -- cgit v1.2.3 From 537fa492e084af58d16899f8d0f3f3516a4fbe7c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 12:04:52 -0300 Subject: [media] saa7146: support control events and priority handling Use v4l2_fh which gives you control events and priority handling for free. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 18 ++++++++++++------ drivers/media/common/saa7146_video.c | 4 ++++ include/media/saa7146_vv.h | 3 +++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 66f6e79dd97b..dfb396568ab6 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -198,7 +198,6 @@ static int fops_open(struct file *file) struct saa7146_dev *dev = video_drvdata(file); struct saa7146_fh *fh = NULL; int result = 0; - enum v4l2_buf_type type; DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev)); @@ -227,7 +226,9 @@ static int fops_open(struct file *file) goto out; } - file->private_data = fh; + v4l2_fh_init(&fh->fh, vdev); + + file->private_data = &fh->fh; fh->dev = dev; if (vdev->vfl_type == VFL_TYPE_VBI) { @@ -251,6 +252,7 @@ static int fops_open(struct file *file) } result = 0; + v4l2_fh_add(&fh->fh); out: if (fh && result != 0) { kfree(fh); @@ -280,6 +282,8 @@ static int fops_release(struct file *file) saa7146_video_uops.release(dev,file); } + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); module_put(dev->ext->module); file->private_data = NULL; kfree(fh); @@ -322,12 +326,13 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) struct saa7146_fh *fh = file->private_data; struct videobuf_buffer *buf = NULL; struct videobuf_queue *q; + unsigned int res = v4l2_ctrl_poll(file, wait); DEB_EE("file:%p, poll:%p\n", file, wait); if (vdev->vfl_type == VFL_TYPE_VBI) { if( 0 == fh->vbi_q.streaming ) - return videobuf_poll_stream(file, &fh->vbi_q, wait); + return res | videobuf_poll_stream(file, &fh->vbi_q, wait); q = &fh->vbi_q; } else { DEB_D("using video queue\n"); @@ -339,17 +344,17 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) if (!buf) { DEB_D("buf == NULL!\n"); - return POLLERR; + return res | POLLERR; } poll_wait(file, &buf->done, wait); if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { DEB_D("poll succeeded!\n"); - return POLLIN|POLLRDNORM; + return res | POLLIN | POLLRDNORM; } DEB_D("nothing to poll for, buf->state:%d\n", buf->state); - return 0; + return res; } static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) @@ -583,6 +588,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, vfd->lock = &dev->v4l2_lock; vfd->v4l2_dev = &dev->v4l2_dev; vfd->tvnorms = 0; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); for (i = 0; i < dev->ext_vv_data->num_stds; i++) vfd->tvnorms |= dev->ext_vv_data->stds[i].id; strlcpy(vfd->name, name, sizeof(vfd->name)); diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 850799051bef..4ca9a2561513 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include static int max_memory = 32; @@ -1021,6 +1023,8 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /*********************************************************************************/ diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 2cc32c514173..2bbdf3046be6 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -84,6 +85,8 @@ struct saa7146_overlay { /* per open data */ struct saa7146_fh { + /* Must be the first field! */ + struct v4l2_fh fh; struct saa7146_dev *dev; /* video capture */ -- cgit v1.2.3 From ab49ae0f201f1e7e07250d011fffde8ed2530175 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 12:57:57 -0300 Subject: [media] saa7146: fix querycap, vbi/video separation and g/s_register The querycap ioctl returned an incorrect version number and incorrect capabilities (mixing up vbi and video caps). The reason for that was that video nodes could do vbi activities: that should be separated between the vbi and video nodes. There were also a few minor problems with dbg_g/s_register that have been resolved. The mxb/saa7146 driver now passes the v4l2_compliance tests. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 8 +++++-- drivers/media/common/saa7146_video.c | 35 ++++++++++++++++++++++++++---- drivers/media/dvb/ttpci/av7110_v4l.c | 24 ++++++++++----------- drivers/media/dvb/ttpci/budget-av.c | 6 +++--- drivers/media/video/hexium_gemini.c | 12 +++++------ drivers/media/video/hexium_orion.c | 6 +++--- drivers/media/video/mxb.c | 41 ++++++++++++++++++++++++------------ include/media/saa7146.h | 2 -- include/media/saa7146_vv.h | 4 +++- 9 files changed, 92 insertions(+), 46 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index dfb396568ab6..428a543ec2ce 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -478,7 +478,8 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) v4l2_ctrl_handler_free(hdl); return -ENOMEM; } - ext_vv->ops = saa7146_video_ioctl_ops; + ext_vv->vid_ops = saa7146_video_ioctl_ops; + ext_vv->vbi_ops = saa7146_vbi_ioctl_ops; ext_vv->core_ops = &saa7146_video_ioctl_ops; DEB_EE("dev:%p\n", dev); @@ -579,7 +580,10 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, return -ENOMEM; vfd->fops = &video_fops; - vfd->ioctl_ops = &dev->ext_vv_data->ops; + if (type == VFL_TYPE_GRABBER) + vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; + else + vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; vfd->release = video_device_release; /* Locking in file operations other than ioctl should be done by the driver, not the V4L2 core. diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 4ca9a2561513..9d193208b892 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -446,18 +446,24 @@ static int video_end(struct saa7146_fh *fh, struct file *file) static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; strcpy((char *)cap->driver, "saa7146 v4l2"); strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci)); - cap->version = SAA7146_VERSION_CODE; cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; cap->device_caps |= dev->ext_vv_data->capabilities; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps &= + ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); + else + cap->device_caps &= + ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY); cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -990,10 +996,14 @@ static int vidioc_g_chip_ident(struct file *file, void *__fh, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - if (chip->match.type == V4L2_CHIP_MATCH_HOST && !chip->match.addr) { - chip->ident = V4L2_IDENT_SAA7146; + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) + chip->ident = V4L2_IDENT_SAA7146; return 0; } + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; return v4l2_device_call_until_err(&dev->v4l2_dev, 0, core, g_chip_ident, chip); } @@ -1008,7 +1018,6 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_g_chip_ident = vidioc_g_chip_ident, .vidioc_overlay = vidioc_overlay, @@ -1027,6 +1036,24 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; +const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_g_chip_ident = vidioc_g_chip_ident, + + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + /*********************************************************************************/ /* buffer handling functions */ diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index ee8ee1d481fa..f1dc6e76a6a1 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -802,18 +802,18 @@ int av7110_init_v4l(struct av7110 *av7110) ERR("cannot init capture device. skipping\n"); return -ENODEV; } - vv_data->ops.vidioc_enum_input = vidioc_enum_input; - vv_data->ops.vidioc_g_input = vidioc_g_input; - vv_data->ops.vidioc_s_input = vidioc_s_input; - vv_data->ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data->ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data->ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data->ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data->ops.vidioc_g_audio = vidioc_g_audio; - vv_data->ops.vidioc_s_audio = vidioc_s_audio; - vv_data->ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; - vv_data->ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; - vv_data->ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; + vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data->vid_ops.vidioc_g_input = vidioc_g_input; + vv_data->vid_ops.vidioc_s_input = vidioc_s_input; + vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; + vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; + vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; + vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; + vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { ERR("cannot register capture device. skipping\n"); diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 8b32e282bf5d..12ddb53c58dc 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -1483,9 +1483,9 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio ERR("cannot init vv subsystem\n"); return err; } - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { /* fixme: proper cleanup here */ diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index a62322d5c0d8..22650322d0a9 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -399,12 +399,12 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d hexium->cur_input = 0; saa7146_vv_init(dev, &vv_data); - vv_data.ops.vidioc_queryctrl = vidioc_queryctrl; - vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl; - vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl; - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_queryctrl = vidioc_queryctrl; + vv_data.vid_ops.vidioc_g_ctrl = vidioc_g_ctrl; + vv_data.vid_ops.vidioc_s_ctrl = vidioc_s_ctrl; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER); if (ret < 0) { pr_err("cannot register capture v4l2 device. skipping.\n"); diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 23debc967d94..e549339f32d6 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -371,9 +371,9 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d DEB_EE("\n"); saa7146_vv_init(dev, &vv_data); - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) { pr_err("cannot register capture v4l2 device. skipping.\n"); return -1; diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index db0c5ddec87f..d2d261291158 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -662,13 +662,28 @@ static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_regist { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - return call_all(dev, core, g_register, reg); + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (v4l2_chip_match_host(®->match)) { + reg->val = saa7146_read(dev, reg->reg); + reg->size = 4; + return 0; + } + call_all(dev, core, g_register, reg); + return 0; } static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (v4l2_chip_match_host(®->match)) { + saa7146_write(dev, reg->reg, reg->val); + reg->size = 4; + return 0; + } return call_all(dev, core, s_register, reg); } #endif @@ -689,19 +704,19 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data } mxb = (struct mxb *)dev->ext_priv; - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; - vv_data.ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data.ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data.ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data.ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data.ops.vidioc_enumaudio = vidioc_enumaudio; - vv_data.ops.vidioc_g_audio = vidioc_g_audio; - vv_data.ops.vidioc_s_audio = vidioc_s_audio; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio; + vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio; + vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio; #ifdef CONFIG_VIDEO_ADV_DEBUG - vv_data.ops.vidioc_g_register = vidioc_g_register; - vv_data.ops.vidioc_s_register = vidioc_s_register; + vv_data.vid_ops.vidioc_g_register = vidioc_g_register; + vv_data.vid_ops.vidioc_s_register = vidioc_s_register; #endif if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { ERR("cannot register capture v4l2 device. skipping.\n"); diff --git a/include/media/saa7146.h b/include/media/saa7146.h index c791940c579b..773e527deabe 100644 --- a/include/media/saa7146.h +++ b/include/media/saa7146.h @@ -18,8 +18,6 @@ #include /* for vmalloc() */ #include /* for vmalloc_to_page() */ -#define SAA7146_VERSION_CODE 0x000600 /* 0.6.0 */ - #define saa7146_write(sxy,adr,dat) writel((dat),(sxy->mem+(adr))) #define saa7146_read(sxy,adr) readl(sxy->mem+(adr)) diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 2bbdf3046be6..944ecdf3530f 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -161,7 +161,8 @@ struct saa7146_ext_vv int (*std_callback)(struct saa7146_dev*, struct saa7146_standard *); /* the extension can override this */ - struct v4l2_ioctl_ops ops; + struct v4l2_ioctl_ops vid_ops; + struct v4l2_ioctl_ops vbi_ops; /* pointer to the saa7146 core ops */ const struct v4l2_ioctl_ops *core_ops; @@ -200,6 +201,7 @@ void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data); /* from saa7146_video.c */ extern const struct v4l2_ioctl_ops saa7146_video_ioctl_ops; +extern const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops; extern struct saa7146_use_ops saa7146_video_uops; int saa7146_start_preview(struct saa7146_fh *fh); int saa7146_stop_preview(struct saa7146_fh *fh); -- cgit v1.2.3 From 313fce750a073b041526f6e595f755f6233f8e73 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 13:13:49 -0300 Subject: [media] fixes and add querystd support to mxb Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ttpci/av7110_v4l.c | 10 ++++++++++ drivers/media/video/mxb.c | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index f1dc6e76a6a1..3b7a624b5e9f 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -811,6 +811,16 @@ int av7110_init_v4l(struct av7110 *av7110) vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; + + vv_data->vbi_ops.vidioc_enum_input = vidioc_enum_input; + vv_data->vbi_ops.vidioc_g_input = vidioc_g_input; + vv_data->vbi_ops.vidioc_s_input = vidioc_s_input; + vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data->vbi_ops.vidioc_g_audio = vidioc_g_audio; + vv_data->vbi_ops.vidioc_s_audio = vidioc_s_audio; vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index d2d261291158..b520a45cb3f3 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -575,6 +575,13 @@ static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) return call_all(dev, tuner, s_tuner, t); } +static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + return call_all(dev, video, querystd, norm); +} + static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; @@ -707,6 +714,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; vv_data.vid_ops.vidioc_g_input = vidioc_g_input; vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_querystd = vidioc_querystd; vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; -- cgit v1.2.3 From 332e799cea32b17c969a1d67e4ed1586d878286f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 14:49:28 -0300 Subject: [media] hexium-gemini: remove B&W control, fix input table The B&W control never worked, and you get the same effect by setting saturation to the lowest value. So it has been removed. Also fixed some incorrect entries in the input table. This driver now passes v4l2-compliance. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/hexium_gemini.c | 123 +++--------------------------------- 1 file changed, 10 insertions(+), 113 deletions(-) diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index 22650322d0a9..366434f5647e 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -40,15 +40,15 @@ static int hexium_num; #define HEXIUM_INPUTS 9 static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { - { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; #define HEXIUM_AUDIOS 0 @@ -59,11 +59,6 @@ struct hexium_data u8 byte; }; -#define HEXIUM_CONTROLS 1 -static struct v4l2_queryctrl hexium_controls[] = { - { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 }, -}; - #define HEXIUM_GEMINI_V_1_0 1 #define HEXIUM_GEMINI_DUAL_V_1_0 2 @@ -76,7 +71,6 @@ struct hexium int cur_input; /* current input */ v4l2_std_id cur_std; /* current standard */ - int cur_bw; /* current black/white status */ }; /* Samsung KS0127B decoder default registers */ @@ -119,18 +113,10 @@ static struct hexium_data hexium_pal[] = { { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } }; -static struct hexium_data hexium_pal_bw[] = { - { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } -}; - static struct hexium_data hexium_ntsc[] = { { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } }; -static struct hexium_data hexium_ntsc_bw[] = { - { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } -}; - static struct hexium_data hexium_secam[] = { { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } }; @@ -264,93 +250,6 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) return 0; } -/* the saa7146 provides some controls (brightness, contrast, saturation) - which gets registered *after* this function. because of this we have - to return with a value != 0 even if the function succeeded.. */ -static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - int i; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == qc->id) { - *qc = hexium_controls[i]; - DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id); - return 0; - } - } - return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc); -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - int i; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_PRIVATE_BASE) { - vc->value = hexium->cur_bw; - DEB_D("VIDIOC_G_CTRL BW:%d\n", vc->value); - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - int i = 0; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_PRIVATE_BASE) - hexium->cur_bw = vc->value; - - DEB_D("VIDIOC_S_CTRL BW:%d\n", hexium->cur_bw); - - if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { - hexium_set_standard(hexium, hexium_pal); - return 0; - } - if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { - hexium_set_standard(hexium, hexium_ntsc); - return 0; - } - if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) { - hexium_set_standard(hexium, hexium_secam); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { - hexium_set_standard(hexium, hexium_pal_bw); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { - hexium_set_standard(hexium, hexium_ntsc_bw); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) - /* fixme: is there no bw secam mode? */ - return -EINVAL; - - return -EINVAL; -} - - static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ @@ -399,9 +298,7 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d hexium->cur_input = 0; saa7146_vv_init(dev, &vv_data); - vv_data.vid_ops.vidioc_queryctrl = vidioc_queryctrl; - vv_data.vid_ops.vidioc_g_ctrl = vidioc_g_ctrl; - vv_data.vid_ops.vidioc_s_ctrl = vidioc_s_ctrl; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; vv_data.vid_ops.vidioc_g_input = vidioc_g_input; vv_data.vid_ops.vidioc_s_input = vidioc_s_input; -- cgit v1.2.3 From fa0fcf46f3b5353b531994edcd73e6d442bc3211 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 15:10:01 -0300 Subject: [media] hexium-orion: fix incorrect input table Fix the standard and audioset values. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/hexium_orion.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index e549339f32d6..a1eb26d11070 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -41,15 +41,15 @@ static int hexium_num; #define HEXIUM_INPUTS 9 static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { - { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; #define HEXIUM_AUDIOS 0 -- cgit v1.2.3 From 3d51dca2a951330ec60fac705316cf3d82988e29 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 2 May 2012 03:15:11 -0300 Subject: [media] vivi: add more pixelformats This is very useful for testing whether userspace can handle the various formats correctly. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/vivi.c | 165 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 135 insertions(+), 30 deletions(-) diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 6f2e354a242d..5e457452a6e1 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -94,6 +94,16 @@ static struct vivi_fmt formats[] = { .fourcc = V4L2_PIX_FMT_UYVY, .depth = 16, }, + { + .name = "4:2:2, packed, YVYU", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + }, + { + .name = "4:2:2, packed, VYUY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + }, { .name = "RGB565 (LE)", .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ @@ -114,6 +124,26 @@ static struct vivi_fmt formats[] = { .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ .depth = 16, }, + { + .name = "RGB24 (LE)", + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .depth = 24, + }, + { + .name = "RGB24 (BE)", + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .depth = 24, + }, + { + .name = "RGB32 (LE)", + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .depth = 32, + }, + { + .name = "RGB32 (BE)", + .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */ + .depth = 32, + }, }; static struct vivi_fmt *get_format(struct v4l2_format *f) @@ -204,8 +234,9 @@ struct vivi_dev { enum v4l2_field field; unsigned int field_count; - u8 bars[9][3]; - u8 line[MAX_WIDTH * 4]; + u8 bars[9][3]; + u8 line[MAX_WIDTH * 8]; + unsigned int pixelsize; }; /* ------------------------------------------------------------------ @@ -284,6 +315,8 @@ static void precalculate_bars(struct vivi_dev *dev) switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: is_yuv = 1; break; case V4L2_PIX_FMT_RGB565: @@ -298,6 +331,11 @@ static void precalculate_bars(struct vivi_dev *dev) g >>= 3; b >>= 3; break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + break; } if (is_yuv) { @@ -317,7 +355,8 @@ static void precalculate_bars(struct vivi_dev *dev) #define TSTAMP_INPUT_X 10 #define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X) -static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) +/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */ +static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) { u8 r_y, g_u, b_v; int color; @@ -327,46 +366,56 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) g_u = dev->bars[colorpos][1]; /* G or precalculated U */ b_v = dev->bars[colorpos][2]; /* B or precalculated V */ - for (color = 0; color < 4; color++) { + for (color = 0; color < dev->pixelsize; color++) { p = buf + color; switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: switch (color) { case 0: - case 2: *p = r_y; break; case 1: - *p = g_u; - break; - case 3: - *p = b_v; + *p = odd ? b_v : g_u; break; } break; case V4L2_PIX_FMT_UYVY: switch (color) { + case 0: + *p = odd ? b_v : g_u; + break; case 1: - case 3: *p = r_y; break; + } + break; + case V4L2_PIX_FMT_YVYU: + switch (color) { + case 0: + *p = r_y; + break; + case 1: + *p = odd ? g_u : b_v; + break; + } + break; + case V4L2_PIX_FMT_VYUY: + switch (color) { case 0: - *p = g_u; + *p = odd ? g_u : b_v; break; - case 2: - *p = b_v; + case 1: + *p = r_y; break; } break; case V4L2_PIX_FMT_RGB565: switch (color) { case 0: - case 2: *p = (g_u << 5) | b_v; break; case 1: - case 3: *p = (r_y << 3) | (g_u >> 3); break; } @@ -374,11 +423,9 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) case V4L2_PIX_FMT_RGB565X: switch (color) { case 0: - case 2: *p = (r_y << 3) | (g_u >> 3); break; case 1: - case 3: *p = (g_u << 5) | b_v; break; } @@ -386,11 +433,9 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) case V4L2_PIX_FMT_RGB555: switch (color) { case 0: - case 2: *p = (g_u << 5) | b_v; break; case 1: - case 3: *p = (r_y << 2) | (g_u >> 3); break; } @@ -398,15 +443,71 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) case V4L2_PIX_FMT_RGB555X: switch (color) { case 0: - case 2: *p = (r_y << 2) | (g_u >> 3); break; case 1: - case 3: *p = (g_u << 5) | b_v; break; } break; + case V4L2_PIX_FMT_RGB24: + switch (color) { + case 0: + *p = r_y; + break; + case 1: + *p = g_u; + break; + case 2: + *p = b_v; + break; + } + break; + case V4L2_PIX_FMT_BGR24: + switch (color) { + case 0: + *p = b_v; + break; + case 1: + *p = g_u; + break; + case 2: + *p = r_y; + break; + } + break; + case V4L2_PIX_FMT_RGB32: + switch (color) { + case 0: + *p = 0; + break; + case 1: + *p = r_y; + break; + case 2: + *p = g_u; + break; + case 3: + *p = b_v; + break; + } + break; + case V4L2_PIX_FMT_BGR32: + switch (color) { + case 0: + *p = b_v; + break; + case 1: + *p = g_u; + break; + case 2: + *p = r_y; + break; + case 3: + *p = 0; + break; + } + break; } } } @@ -415,10 +516,10 @@ static void precalculate_line(struct vivi_dev *dev) { int w; - for (w = 0; w < dev->width * 2; w += 2) { - int colorpos = (w / (dev->width / 8) % 8); + for (w = 0; w < dev->width * 2; w++) { + int colorpos = w / (dev->width / 8) % 8; - gen_twopix(dev, dev->line + w * 2, colorpos); + gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1); } } @@ -434,7 +535,7 @@ static void gen_text(struct vivi_dev *dev, char *basep, /* Print stream time */ for (line = y; line < y + 16; line++) { int j = 0; - char *pos = basep + line * dev->width * 2 + x * 2; + char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize; char *s; for (s = text; *s; s++) { @@ -444,9 +545,9 @@ static void gen_text(struct vivi_dev *dev, char *basep, for (i = 0; i < 7; i++, j++) { /* Draw white font on black background */ if (chr & (1 << (7 - i))) - gen_twopix(dev, pos + j * 2, WHITE); + gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1); else - gen_twopix(dev, pos + j * 2, TEXT_BLACK); + gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1); } } } @@ -467,7 +568,9 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) return; for (h = 0; h < hmax; h++) - memcpy(vbuf + h * wmax * 2, dev->line + (dev->mv_count % wmax) * 2, wmax * 2); + memcpy(vbuf + h * wmax * dev->pixelsize, + dev->line + (dev->mv_count % wmax) * dev->pixelsize, + wmax * dev->pixelsize); /* Updates stream time */ @@ -662,7 +765,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, struct vivi_dev *dev = vb2_get_drv_priv(vq); unsigned long size; - size = dev->width * dev->height * 2; + size = dev->width * dev->height * dev->pixelsize; if (0 == *nbuffers) *nbuffers = 32; @@ -726,7 +829,7 @@ static int buffer_prepare(struct vb2_buffer *vb) dev->height < 32 || dev->height > MAX_HEIGHT) return -EINVAL; - size = dev->width * dev->height * 2; + size = dev->width * dev->height * dev->pixelsize; if (vb2_plane_size(vb, 0) < size) { dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n", __func__, vb2_plane_size(vb, 0), size); @@ -920,6 +1023,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, } dev->fmt = get_format(f); + dev->pixelsize = dev->fmt->depth / 8; dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; dev->field = f->fmt.pix.field; @@ -1266,6 +1370,7 @@ static int __init vivi_create_instance(int inst) dev->fmt = &formats[0]; dev->width = 640; dev->height = 480; + dev->pixelsize = dev->fmt->depth / 8; hdl = &dev->ctrl_handler; v4l2_ctrl_handler_init(hdl, 11); dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, -- cgit v1.2.3 From 7088f4df4c489811f6a5ad9a883b7dd259adfb6a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 2 May 2012 03:33:52 -0300 Subject: [media] vivi: add the alpha component control Useful to set the alpha component for the pixel formats with an alpha channel. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/vivi.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 5e457452a6e1..0960d7f0d394 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -200,6 +200,7 @@ struct vivi_dev { struct v4l2_ctrl *gain; }; struct v4l2_ctrl *volume; + struct v4l2_ctrl *alpha; struct v4l2_ctrl *button; struct v4l2_ctrl *boolean; struct v4l2_ctrl *int32; @@ -237,6 +238,7 @@ struct vivi_dev { u8 bars[9][3]; u8 line[MAX_WIDTH * 8]; unsigned int pixelsize; + u8 alpha_component; }; /* ------------------------------------------------------------------ @@ -359,6 +361,7 @@ static void precalculate_bars(struct vivi_dev *dev) static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) { u8 r_y, g_u, b_v; + u8 alpha = dev->alpha_component; int color; u8 *p; @@ -436,14 +439,14 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) *p = (g_u << 5) | b_v; break; case 1: - *p = (r_y << 2) | (g_u >> 3); + *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); break; } break; case V4L2_PIX_FMT_RGB555X: switch (color) { case 0: - *p = (r_y << 2) | (g_u >> 3); + *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); break; case 1: *p = (g_u << 5) | b_v; @@ -479,7 +482,7 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) case V4L2_PIX_FMT_RGB32: switch (color) { case 0: - *p = 0; + *p = alpha; break; case 1: *p = r_y; @@ -504,7 +507,7 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) *p = r_y; break; case 3: - *p = 0; + *p = alpha; break; } break; @@ -595,8 +598,9 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->saturation->cur.val, dev->hue->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); - snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d ", - dev->autogain->cur.val, gain, dev->volume->cur.val); + snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d, alpha 0x%02x ", + dev->autogain->cur.val, gain, dev->volume->cur.val, + dev->alpha->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ", dev->int32->cur.val, @@ -1125,8 +1129,15 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl) { struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler); - if (ctrl == dev->button) - dev->button_pressed = 30; + switch (ctrl->id) { + case V4L2_CID_ALPHA_COMPONENT: + dev->alpha_component = ctrl->val; + break; + default: + if (ctrl == dev->button) + dev->button_pressed = 30; + break; + } return 0; } @@ -1387,6 +1398,8 @@ static int __init vivi_create_instance(int inst) V4L2_CID_AUTOGAIN, 0, 1, 1, 1); dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, V4L2_CID_GAIN, 0, 255, 1, 100); + dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL); dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL); dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL); -- cgit v1.2.3 From a299e407b9ef356bf14fbb49793dc026877440df Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 May 2012 13:31:27 -0300 Subject: [media] av7110: fix v4l2_compliance test issues Besides the usual inconsistencies in input enumeration there was also a kernel crash if you tried to poll on a vbi node. The checks for sliced vbi output vs vbi capture were not complete enough. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 4 +++ drivers/media/common/saa7146_video.c | 4 +-- drivers/media/dvb/ttpci/av7110_v4l.c | 48 ++++++++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 428a543ec2ce..7d42c11c8684 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -309,6 +309,8 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma) case VFL_TYPE_VBI: { DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n", file, vma); + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return -ENODEV; q = &fh->vbi_q; break; } @@ -331,6 +333,8 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) DEB_EE("file:%p, poll:%p\n", file, wait); if (vdev->vfl_type == VFL_TYPE_VBI) { + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return res | POLLOUT | POLLWRNORM; if( 0 == fh->vbi_q.streaming ) return res | videobuf_poll_stream(file, &fh->vbi_q, wait); q = &fh->vbi_q; diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 9d193208b892..6d14785d4747 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -458,13 +458,13 @@ static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability * V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; cap->device_caps |= dev->ext_vv_data->capabilities; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; if (vdev->vfl_type == VFL_TYPE_GRABBER) cap->device_caps &= ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); else cap->device_caps &= - ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY); - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO); return 0; } diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index 3b7a624b5e9f..1b2d15140a1d 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -107,7 +107,7 @@ static struct v4l2_input inputs[4] = { .index = 1, .name = "Television", .type = V4L2_INPUT_TYPE_TUNER, - .audioset = 2, + .audioset = 1, .tuner = 0, .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, .status = 0, @@ -494,7 +494,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) dprintk(2, "VIDIOC_S_INPUT: %d\n", input); if (!av7110->analog_tuner_flags) - return 0; + return input ? -EINVAL : 0; if (input >= 4) return -EINVAL; @@ -503,19 +503,38 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) return av7110_dvb_c_switch(fh); } +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); + if (a->index != 0) + return -EINVAL; + *a = msp3400_v4l2_audio; + return 0; +} + static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) { + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); if (a->index != 0) return -EINVAL; - memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio)); + if (av7110->current_input >= 2) + return -EINVAL; + *a = msp3400_v4l2_audio; return 0; } static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) { + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index); - return 0; + if (av7110->current_input >= 2) + return -EINVAL; + return a->index ? -EINVAL : 0; } static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, @@ -809,29 +828,32 @@ int av7110_init_v4l(struct av7110 *av7110) vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner; vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency; vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio; vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; + vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL; - vv_data->vbi_ops.vidioc_enum_input = vidioc_enum_input; - vv_data->vbi_ops.vidioc_g_input = vidioc_g_input; - vv_data->vbi_ops.vidioc_s_input = vidioc_s_input; vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner; vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner; vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency; vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data->vbi_ops.vidioc_g_audio = vidioc_g_audio; - vv_data->vbi_ops.vidioc_s_audio = vidioc_s_audio; + vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL; vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; + if (FW_VERSION(av7110->arm_app) < 0x2623) + vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; + if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { ERR("cannot register capture device. skipping\n"); saa7146_vv_release(dev); return -ENODEV; } - if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) - ERR("cannot register vbi v4l2 device. skipping\n"); + if (FW_VERSION(av7110->arm_app) >= 0x2623) { + if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) + ERR("cannot register vbi v4l2 device. skipping\n"); + } return 0; } @@ -915,7 +937,7 @@ static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) static struct saa7146_ext_vv av7110_vv_data_st = { .inputs = 1, .audios = 1, - .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT, + .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, .flags = 0, .stds = &standard[0], @@ -930,7 +952,7 @@ static struct saa7146_ext_vv av7110_vv_data_st = { static struct saa7146_ext_vv av7110_vv_data_c = { .inputs = 1, .audios = 1, - .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, .flags = SAA7146_USE_PORT_B_FOR_VBI, .stds = &standard[0], -- cgit v1.2.3 From 74f22c48640cf39df1f9cbf4fac07f5fcd365a48 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 May 2012 12:54:27 -0300 Subject: [media] v4l2-framework.txt: update the core lock documentation Thanks to Laurent Pinchart for pointing out that this information was missing. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 0dace876b978..c24a9393dbb9 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -590,8 +590,8 @@ You should also set these fields: future!), then set this to your v4l2_ioctl_ops struct. - lock: leave to NULL if you want to do all the locking in the driver. - Otherwise you give it a pointer to a struct mutex_lock and before any - of the v4l2_file_operations is called this lock will be taken by the + Otherwise you give it a pointer to a struct mutex_lock and before the + unlocked_ioctl file operation is called this lock will be taken by the core and released afterwards. See the next section for more details. - prio: keeps track of the priorities. Used to implement VIDIOC_G/S_PRIORITY. @@ -652,8 +652,8 @@ v4l2_file_operations and locking You can set a pointer to a mutex_lock in struct video_device. Usually this will be either a top-level mutex or a mutex per device node. By default this -lock will be used for each file operation and ioctl, but you can disable -locking for selected ioctls by calling: +lock will be used for unlocked_ioctl, but you can disable locking for +selected ioctls by calling: void v4l2_dont_use_lock(struct video_device *vdev, unsigned int cmd); @@ -674,7 +674,7 @@ of a USB webcam might take a long time), then you might be better off with doing your own locking if you want to allow the user to do other things with the device while waiting for the high-latency command to finish. -If a lock is specified then all file operations will be serialized on that +If a lock is specified then all ioctl commands will be serialized on that lock. If you use videobuf then you must pass the same lock to the videobuf queue initialize function: if videobuf has to wait for a frame to arrive, then it will temporarily unlock the lock and relock it afterwards. If your driver -- cgit v1.2.3 From 47bd4bc1a2624939c9f4ba154a2c18abe9d6c614 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 May 2012 11:28:44 -0300 Subject: [media] v4l2-dev.h: add comment not to use V4L2_FL_LOCK_ALL_FOPS in new drivers This flag is for legacy drivers only and will go away in the future. A note regarding commit 5126f2590bee412e3053de851cb07f531e4be36a (v4l2-dev: add flag to have the core lock all file operations): That commit message suggests that by not taking the core lock for fops other than unlocked_ioctl all problems relating to AB-BA locking and mm->mmap_sem are solved. This is not the case. More work needs to be done by moving the core lock further down into video_ioctl2. It should only be taken after the copy_from/to_user calls are done. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-dev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index b604a7a50940..70d91c99728b 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -40,7 +40,7 @@ struct v4l2_ctrl_handler; /* Use the prio field of v4l2_fh for core priority checking */ #define V4L2_FL_USE_FH_PRIO (2) /* If ioctl core locking is in use, then apply that also to all - file operations. */ + file operations. Don't use this flag in new drivers! */ #define V4L2_FL_LOCK_ALL_FOPS (3) /* Priority helper functions */ -- cgit v1.2.3 From 152a3a7320d1582009db85d8be365ce430d079af Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 May 2012 11:32:48 -0300 Subject: [media] v4l2-dev: rename two functions Rename the function v4l2_dont_use_lock to v4l2_disable_ioctl_locking, and rename v4l2_dont_use_cmd to v4l2_disable_ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 6 +++--- drivers/media/video/gspca/gspca.c | 6 +++--- drivers/media/video/pwc/pwc-if.c | 6 +++--- drivers/media/video/v4l2-dev.c | 2 +- include/media/v4l2-dev.h | 10 +++++----- sound/i2c/other/tea575x-tuner.c | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index c24a9393dbb9..1f5905270050 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -623,7 +623,7 @@ In some cases you want to tell the core that a function you had specified in your v4l2_ioctl_ops should be ignored. You can mark such ioctls by calling this function before video_device_register is called: -void v4l2_dont_use_cmd(struct video_device *vdev, unsigned int cmd); +void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd); This tends to be needed if based on external factors (e.g. which card is being used) you want to turns off certain features in v4l2_ioctl_ops without @@ -655,9 +655,9 @@ will be either a top-level mutex or a mutex per device node. By default this lock will be used for unlocked_ioctl, but you can disable locking for selected ioctls by calling: - void v4l2_dont_use_lock(struct video_device *vdev, unsigned int cmd); + void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd); -E.g.: v4l2_dont_use_lock(vdev, VIDIOC_DQBUF); +E.g.: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF); You have to call this before you register the video_device. diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 2b393b2cf62d..137166d73945 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -2285,9 +2285,9 @@ int gspca_dev_probe2(struct usb_interface *intf, * usb_lock is taken for a long time, e.g. when changing a control * value, and a new frame is ready to be dequeued. */ - v4l2_dont_use_lock(&gspca_dev->vdev, VIDIOC_DQBUF); - v4l2_dont_use_lock(&gspca_dev->vdev, VIDIOC_QBUF); - v4l2_dont_use_lock(&gspca_dev->vdev, VIDIOC_QUERYBUF); + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF); + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF); + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF); /* init video stuff */ ret = video_register_device(&gspca_dev->vdev, diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 998e809765a7..ec4e2ef54e65 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -1195,9 +1195,9 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id * v4l2_lock is taken for a long time, e.g. when changing a control * value, and a new frame is ready to be dequeued. */ - v4l2_dont_use_lock(&pdev->vdev, VIDIOC_DQBUF); - v4l2_dont_use_lock(&pdev->vdev, VIDIOC_QBUF); - v4l2_dont_use_lock(&pdev->vdev, VIDIOC_QUERYBUF); + v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_DQBUF); + v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QBUF); + v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QUERYBUF); rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); if (rc < 0) { diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 2c4feffa4939..5ccbd4629f9c 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -329,7 +329,7 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (vdev->lock) { /* always lock unless the cmd is marked as "don't use lock" */ locked = !v4l2_is_known_ioctl(cmd) || - !test_bit(_IOC_NR(cmd), vdev->dont_use_lock); + !test_bit(_IOC_NR(cmd), vdev->disable_locking); if (locked && mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 70d91c99728b..a056e6ee1b68 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -132,7 +132,7 @@ struct video_device DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); /* serialization lock */ - DECLARE_BITMAP(dont_use_lock, BASE_VIDIOC_PRIVATE); + DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE); struct mutex *lock; }; @@ -182,17 +182,17 @@ void video_device_release_empty(struct video_device *vdev); bool v4l2_is_known_ioctl(unsigned int cmd); /* mark that this command shouldn't use core locking */ -static inline void v4l2_dont_use_lock(struct video_device *vdev, unsigned int cmd) +static inline void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd) { if (_IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) - set_bit(_IOC_NR(cmd), vdev->dont_use_lock); + set_bit(_IOC_NR(cmd), vdev->disable_locking); } -/* Mark that this command isn't implemented, must be called before +/* Mark that this command isn't implemented. This must be called before video_device_register. See also the comments in determine_valid_ioctls(). This function allows drivers to provide just one v4l2_ioctl_ops struct, but disable ioctls based on the specific card that is actually found. */ -static inline void v4l2_dont_use_cmd(struct video_device *vdev, unsigned int cmd) +static inline void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd) { if (_IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) set_bit(_IOC_NR(cmd), vdev->valid_ioctls); diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 6e9ca7bd0f11..582aace20ea3 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -377,7 +377,7 @@ int snd_tea575x_init(struct snd_tea575x *tea) set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags); /* disable hw_freq_seek if we can't use it */ if (tea->cannot_read_data) - v4l2_dont_use_cmd(&tea->vd, VIDIOC_S_HW_FREQ_SEEK); + v4l2_disable_ioctl(&tea->vd, VIDIOC_S_HW_FREQ_SEEK); v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); -- cgit v1.2.3 From 60332f033d4c8655e0afcea932e16d44646e82bf Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sat, 28 Apr 2012 09:57:01 -0300 Subject: [media] em28xx: Make card_setup() and pre_card_setup() static This cleans namespace a bit by making em28xx_card_setup() em28xx_pre_card_setup() static functions. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 6 ++++-- drivers/media/video/em28xx/em28xx.h | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 0ac117c23c47..5dd6806c75ab 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -69,6 +69,8 @@ struct em28xx_hash_table { unsigned int tuner; }; +static void em28xx_pre_card_setup(struct em28xx *dev); + /* * Reset sequences for analog/digital modes */ @@ -2361,7 +2363,7 @@ static int em28xx_hint_sensor(struct em28xx *dev) /* Since em28xx_pre_card_setup() requires a proper dev->model, * this won't work for boards with generic PCI IDs */ -void em28xx_pre_card_setup(struct em28xx *dev) +static void em28xx_pre_card_setup(struct em28xx *dev) { /* Set the initial XCLK and I2C clock values based on the board definition */ @@ -2661,7 +2663,7 @@ static int em28xx_hint_board(struct em28xx *dev) return -1; } -void em28xx_card_setup(struct em28xx *dev) +static void em28xx_card_setup(struct em28xx *dev) { /* * If the device can be a webcam, seek for a sensor. diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 9a2bd5c40163..9486eacc6e0f 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -698,8 +698,6 @@ void em28xx_release_analog_resources(struct em28xx *dev); /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device *udev, int model); -extern void em28xx_pre_card_setup(struct em28xx *dev); -extern void em28xx_card_setup(struct em28xx *dev); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; -- cgit v1.2.3 From cb04af205e4b1f0aaa9010726faf95da67f10152 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sat, 28 Apr 2012 09:57:02 -0300 Subject: [media] em28xx: Remove unused list_head struct for queued buffers The list_head struct usage was fully removed by commit d7aa80207babe694b316a48200b096cf0336ecb3. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 2 -- drivers/media/video/em28xx/em28xx.h | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 5dd6806c75ab..fcc713f8098d 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -3080,9 +3080,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); INIT_LIST_HEAD(&dev->vbiq.active); - INIT_LIST_HEAD(&dev->vbiq.queued); if (dev->board.has_msp34xx) { /* Send a reset to other chips via gpio */ diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 9486eacc6e0f..009a95a4cb02 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -255,7 +255,6 @@ struct em28xx_buffer { struct em28xx_dmaqueue { struct list_head active; - struct list_head queued; wait_queue_head_t wq; -- cgit v1.2.3 From 959a181681d7aff659c591fcbf7e5ed424181bb5 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Sat, 28 Apr 2012 11:09:48 -0300 Subject: [media] em28xx: Remove unused field from em28xx_buffer struct Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 009a95a4cb02..700feb25535e 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -250,7 +250,6 @@ struct em28xx_buffer { struct list_head frame; int top_field; - int receiving; }; struct em28xx_dmaqueue { -- cgit v1.2.3 From 3477e2ab0e52fe55999130eee833af9a29e2789a Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Sat, 28 Apr 2012 11:09:49 -0300 Subject: [media] em28xx: Remove unused enum em28xx_io_method Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 700feb25535e..a3f77f6f5d95 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -261,13 +261,6 @@ struct em28xx_dmaqueue { int pos; }; -/* io methods */ -enum em28xx_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - /* inputs */ #define MAX_EM28XX_INPUT 4 @@ -562,7 +555,6 @@ struct em28xx { /* states */ enum em28xx_dev_state state; - enum em28xx_io_method io; /* vbi related state tracking */ int capture_type; -- cgit v1.2.3 From a39fbb1d747aa88e410d0a25af92cd9e179ba0f4 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 30 Apr 2012 18:06:27 -0300 Subject: [media] via-camera: specify XO-1.5 camera clock speed For the ov7670 camera to return images at the requested frame rate, it needs to make calculations based on the clock speed, which is a completely external factor (depends on the wiring of the system). On the XO-1.5, which is the only known via-camera user, the camera is clocked at 90MHz. Pass this information to the ov7670 driver, to fix an issue where a framerate of 3x the requested amount was being provided. Signed-off-by: Daniel Drake Acked-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/via-camera.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c index 20f7237b8242..308e150a39bc 100644 --- a/drivers/media/video/via-camera.c +++ b/drivers/media/video/via-camera.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1347,11 +1348,21 @@ static __devinit bool viacam_serial_is_enabled(void) return false; } +static struct ov7670_config sensor_cfg = { + /* The XO-1.5 (only known user) clocks the camera at 90MHz. */ + .clock_speed = 90, +}; + static __devinit int viacam_probe(struct platform_device *pdev) { int ret; struct i2c_adapter *sensor_adapter; struct viafb_dev *viadev = pdev->dev.platform_data; + struct i2c_board_info ov7670_info = { + .type = "ov7670", + .addr = 0x42 >> 1, + .platform_data = &sensor_cfg, + }; /* * Note that there are actually two capture channels on @@ -1433,8 +1444,8 @@ static __devinit int viacam_probe(struct platform_device *pdev) * is OLPC-specific. 0x42 assumption is ov7670-specific. */ sensor_adapter = viafb_find_i2c_adapter(VIA_PORT_31); - cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, sensor_adapter, - "ov7670", 0x42 >> 1, NULL); + cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, sensor_adapter, + &ov7670_info, NULL); if (cam->sensor == NULL) { dev_err(&pdev->dev, "Unable to find the sensor!\n"); ret = -ENODEV; -- cgit v1.2.3 From b18900080387808b7fb5b5374192e305fb216257 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 2 May 2012 02:15:25 -0300 Subject: [media] gspca: passing wrong length parameter to reg_w() This looks like a cut an paste error. This is a two byte array but we use 8 as a length parameter. Signed-off-by: Dan Carpenter Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/conex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index ea17b5d94ea4..f39fee0fd10f 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -306,7 +306,7 @@ static void cx_sensor(struct gspca_dev*gspca_dev) reg_w(gspca_dev, 0x0020, reg20, 8); reg_w(gspca_dev, 0x0028, reg28, 8); - reg_w(gspca_dev, 0x0010, reg10, 8); + reg_w(gspca_dev, 0x0010, reg10, 2); reg_w_val(gspca_dev, 0x0092, 0x03); switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { @@ -326,7 +326,7 @@ static void cx_sensor(struct gspca_dev*gspca_dev) } reg_w(gspca_dev, 0x007b, reg7b, 6); reg_w_val(gspca_dev, 0x00f8, 0x00); - reg_w(gspca_dev, 0x0010, reg10, 8); + reg_w(gspca_dev, 0x0010, reg10, 2); reg_w_val(gspca_dev, 0x0098, 0x41); for (i = 0; i < 11; i++) { if (i == 3 || i == 5 || i == 8) -- cgit v1.2.3 From 5cbd28df305ab29b7e43bd8e901e7824f7ed9417 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 3 May 2012 06:54:51 -0300 Subject: [media] Convert I2C drivers to dev_pm_ops The legacy I2C PM functions have been deprecated and warning on boot for over a year, convert the drivers still using them to dev_pm_ops. Signed-off-by: Mark Brown Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/msp3400-driver.c | 15 +++++++++++---- drivers/media/video/tuner-core.c | 15 +++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 82ce50721de3..aeb22be7dcbd 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -597,19 +597,23 @@ static int msp_log_status(struct v4l2_subdev *sd) return 0; } -static int msp_suspend(struct i2c_client *client, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int msp_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); v4l_dbg(1, msp_debug, client, "suspend\n"); msp_reset(client); return 0; } -static int msp_resume(struct i2c_client *client) +static int msp_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); v4l_dbg(1, msp_debug, client, "resume\n"); msp_wake_thread(client); return 0; } +#endif /* ----------------------------------------------------------------------- */ @@ -863,6 +867,10 @@ static int msp_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct dev_pm_ops msp3400_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume) +}; + static const struct i2c_device_id msp_id[] = { { "msp3400", 0 }, { } @@ -873,11 +881,10 @@ static struct i2c_driver msp_driver = { .driver = { .owner = THIS_MODULE, .name = "msp3400", + .pm = &msp3400_pm_ops, }, .probe = msp_probe, .remove = msp_remove, - .suspend = msp_suspend, - .resume = msp_resume, .id_table = msp_id, }; diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index a5c6397ad591..3e050e12153b 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -1241,8 +1241,10 @@ static int tuner_log_status(struct v4l2_subdev *sd) return 0; } -static int tuner_suspend(struct i2c_client *c, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int tuner_suspend(struct device *dev) { + struct i2c_client *c = to_i2c_client(dev); struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; @@ -1254,8 +1256,9 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state) return 0; } -static int tuner_resume(struct i2c_client *c) +static int tuner_resume(struct device *dev) { + struct i2c_client *c = to_i2c_client(dev); struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("resume\n"); @@ -1266,6 +1269,7 @@ static int tuner_resume(struct i2c_client *c) return 0; } +#endif static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) { @@ -1310,6 +1314,10 @@ static const struct v4l2_subdev_ops tuner_ops = { * I2C structs and module init functions */ +static const struct dev_pm_ops tuner_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tuner_suspend, tuner_resume) +}; + static const struct i2c_device_id tuner_id[] = { { "tuner", }, /* autodetect */ { } @@ -1320,12 +1328,11 @@ static struct i2c_driver tuner_driver = { .driver = { .owner = THIS_MODULE, .name = "tuner", + .pm = &tuner_pm_ops, }, .probe = tuner_probe, .remove = tuner_remove, .command = tuner_command, - .suspend = tuner_suspend, - .resume = tuner_resume, .id_table = tuner_id, }; -- cgit v1.2.3 From d0f8dfc6e74df5cdefb65ae27d52d848d3973cc8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 3 May 2012 18:22:27 -0300 Subject: [media] video/omap24xxcam: use __iomem annotations MMIO registers are __iomem tokens in virtual address space, not integers. Signed-off-by: Arnd Bergmann Signed-off-by: Mathieu Poirier Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap24xxcam-dma.c | 20 ++++++++++---------- drivers/media/video/omap24xxcam.c | 3 +-- drivers/media/video/omap24xxcam.h | 14 +++++++------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/media/video/omap24xxcam-dma.c b/drivers/media/video/omap24xxcam-dma.c index 3ea38a8def8e..b5ae170de4a5 100644 --- a/drivers/media/video/omap24xxcam-dma.c +++ b/drivers/media/video/omap24xxcam-dma.c @@ -38,7 +38,7 @@ */ /* Ack all interrupt on CSR and IRQSTATUS_L0 */ -static void omap24xxcam_dmahw_ack_all(unsigned long base) +static void omap24xxcam_dmahw_ack_all(void __iomem *base) { u32 csr; int i; @@ -52,7 +52,7 @@ static void omap24xxcam_dmahw_ack_all(unsigned long base) } /* Ack dmach on CSR and IRQSTATUS_L0 */ -static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach) +static u32 omap24xxcam_dmahw_ack_ch(void __iomem *base, int dmach) { u32 csr; @@ -65,12 +65,12 @@ static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach) return csr; } -static int omap24xxcam_dmahw_running(unsigned long base, int dmach) +static int omap24xxcam_dmahw_running(void __iomem *base, int dmach) { return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE; } -static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach, +static void omap24xxcam_dmahw_transfer_setup(void __iomem *base, int dmach, dma_addr_t start, u32 len) { omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), @@ -112,7 +112,7 @@ static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach, | CAMDMA_CICR_DROP_IE); } -static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach) +static void omap24xxcam_dmahw_transfer_start(void __iomem *base, int dmach) { omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), CAMDMA_CCR_SEL_SRC_DST_SYNC @@ -124,7 +124,7 @@ static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach) | CAMDMA_CCR_SYNCHRO_CAMERA); } -static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach, +static void omap24xxcam_dmahw_transfer_chain(void __iomem *base, int dmach, int free_dmach) { int prev_dmach, ch; @@ -160,7 +160,7 @@ static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach, * controller may not be idle after this routine completes, because * the completion routines might start new transfers. */ -static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach) +static void omap24xxcam_dmahw_abort_ch(void __iomem *base, int dmach) { /* mask all interrupts from this channel */ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0); @@ -171,7 +171,7 @@ static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach) omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE); } -static void omap24xxcam_dmahw_init(unsigned long base) +static void omap24xxcam_dmahw_init(void __iomem *base) { omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG, CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY @@ -362,7 +362,7 @@ void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma) } static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma, - unsigned long base) + void __iomem *base) { int ch; @@ -577,7 +577,7 @@ void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma) } void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, - unsigned long base, + void __iomem *base, void (*reset_callback)(unsigned long data), unsigned long reset_callback_data) { diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index 7d3864144368..e5015b0d5508 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -1776,8 +1776,7 @@ static int __devinit omap24xxcam_probe(struct platform_device *pdev) cam->mmio_size = resource_size(mem); /* map the region */ - cam->mmio_base = (unsigned long) - ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); + cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); if (!cam->mmio_base) { dev_err(cam->dev, "cannot map camera register I/O region\n"); goto err; diff --git a/drivers/media/video/omap24xxcam.h b/drivers/media/video/omap24xxcam.h index 2ce67f5a48d5..d59727afe894 100644 --- a/drivers/media/video/omap24xxcam.h +++ b/drivers/media/video/omap24xxcam.h @@ -429,7 +429,7 @@ struct sgdma_state { struct omap24xxcam_dma { spinlock_t lock; /* Lock for the whole structure. */ - unsigned long base; /* base address for dma controller */ + void __iomem *base; /* base address for dma controller */ /* While dma_stop!=0, an attempt to start a new DMA transfer will * fail. @@ -491,7 +491,7 @@ struct omap24xxcam_device { /*** hardware resources ***/ unsigned int irq; - unsigned long mmio_base; + void __iomem *mmio_base; unsigned long mmio_base_phys; unsigned long mmio_size; @@ -544,22 +544,22 @@ struct omap24xxcam_fh { * */ -static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset) +static inline u32 omap24xxcam_reg_in(u32 __iomem *base, u32 offset) { return readl(base + offset); } -static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset, +static inline u32 omap24xxcam_reg_out(u32 __iomem *base, u32 offset, u32 val) { writel(val, base + offset); return val; } -static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset, +static inline u32 omap24xxcam_reg_merge(u32 __iomem *base, u32 offset, u32 val, u32 mask) { - u32 addr = base + offset; + u32 __iomem *addr = base + offset; u32 new_val = (readl(addr) & ~mask) | (val & mask); writel(new_val, addr); @@ -585,7 +585,7 @@ int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, int len, sgdma_callback_t callback, void *arg); void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma); void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, - unsigned long base, + void __iomem *base, void (*reset_callback)(unsigned long data), unsigned long reset_callback_data); void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma); -- cgit v1.2.3 From 60ab5e12879bd15416c05e6b0460689163581164 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 3 May 2012 18:22:26 -0300 Subject: [media] dvb/drxd: stub out drxd_attach when not built This avoids getting drivers/media/video/em28xx/em28xx-dvb.c:721: \ undefined reference to `drxd_attach' Signed-off-by: Arnd Bergmann Signed-off-by: Mathieu Poirier Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/drxd.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/media/dvb/frontends/drxd.h b/drivers/media/dvb/frontends/drxd.h index 34398738f9bc..216c8c3702f8 100644 --- a/drivers/media/dvb/frontends/drxd.h +++ b/drivers/media/dvb/frontends/drxd.h @@ -51,9 +51,23 @@ struct drxd_config { s16(*osc_deviation) (void *priv, s16 dev, int flag); }; +#if defined(CONFIG_DVB_DRXD) || \ + (defined(CONFIG_DVB_DRXD_MODULE) && defined(MODULE)) extern struct dvb_frontend *drxd_attach(const struct drxd_config *config, void *priv, struct i2c_adapter *i2c, struct device *dev); +#else +static inline +struct dvb_frontend *drxd_attach(const struct drxd_config *config, + void *priv, struct i2c_adapter *i2c, + struct device *dev) +{ + printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", + __func__); + return NULL; +} +#endif + extern int drxd_config_i2c(struct dvb_frontend *, int); #endif -- cgit v1.2.3 From b66d2086c08dac16e6e4cec7642e9e10c4466336 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 3 May 2012 18:22:24 -0300 Subject: [media] media/rc: IR_SONY_DECODER depends on BITREVERSE The IR sony decoder is making use of 'bitrev8' that, in turn, requires BITREVERSE. Signed-off-by: Arnd Bergmann Signed-off-by: Mathieu Poirier Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index a3fbb21350e9..f97eeb870455 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -69,6 +69,7 @@ config IR_JVC_DECODER config IR_SONY_DECODER tristate "Enable IR raw decoder for the Sony protocol" depends on RC_CORE + select BITREVERSE default y ---help--- -- cgit v1.2.3 From 14f2226022be6b169faeee3f0f6ca848170fdb3d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 3 May 2012 18:22:25 -0300 Subject: [media] media/video: add I2C dependencies Davinci VIDEO_VPFE_CAPTURE depends on I2C, so reflect that in Kconfig to avoid build failures in random configurations. Signed-off-by: Arnd Bergmann Signed-off-by: Mathieu Poirier Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 60a456ebdc7c..9337b5605c90 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -40,6 +40,7 @@ config VIDEO_VPSS_SYSTEM config VIDEO_VPFE_CAPTURE tristate "VPFE Video Capture Driver" depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3) + depends on I2C select VIDEOBUF_DMA_CONTIG help Support for DMx/AMx VPFE based frame grabber. This is the -- cgit v1.2.3 From cd624c7b7ce3c3cfa09845dcefbe856bf9f9c4c6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 3 May 2012 18:22:22 -0300 Subject: [media] drivers/media: add missing __devexit_p() annotations Drivers that refer to a __devexit function in an operations structure need to annotate that pointer with __devexit_p so replace it with a NULL pointer when the section gets discarded. Signed-off-by: Arnd Bergmann Signed-off-by: Mathieu Poirier Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ddbridge/ddbridge-core.c | 2 +- drivers/media/radio/radio-timb.c | 2 +- drivers/media/radio/saa7706h.c | 2 +- drivers/media/radio/tef6862.c | 2 +- drivers/media/rc/imon.c | 2 +- drivers/media/rc/mceusb.c | 2 +- drivers/media/rc/redrat3.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c index 115777ec7536..131b938e9e81 100644 --- a/drivers/media/dvb/ddbridge/ddbridge-core.c +++ b/drivers/media/dvb/ddbridge/ddbridge-core.c @@ -1695,7 +1695,7 @@ static struct pci_driver ddb_pci_driver = { .name = "DDBridge", .id_table = ddb_id_tbl, .probe = ddb_probe, - .remove = ddb_remove, + .remove = __devexit_p(ddb_remove), }; static __init int module_init_ddbridge(void) diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index 5d9a90ac3a1c..7052adc0c0b0 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -223,7 +223,7 @@ static struct platform_driver timbradio_platform_driver = { .owner = THIS_MODULE, }, .probe = timbradio_probe, - .remove = timbradio_remove, + .remove = __devexit_p(timbradio_remove), }; module_platform_driver(timbradio_platform_driver); diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 9474706350f8..bb953ef75f61 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -430,7 +430,7 @@ static struct i2c_driver saa7706h_driver = { .name = DRIVER_NAME, }, .probe = saa7706h_probe, - .remove = saa7706h_remove, + .remove = __devexit_p(saa7706h_remove), .id_table = saa7706h_id, }; diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 6418c4c9faf1..06d47e5cce9f 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -211,7 +211,7 @@ static struct i2c_driver tef6862_driver = { .name = DRIVER_NAME, }, .probe = tef6862_probe, - .remove = tef6862_remove, + .remove = __devexit_p(tef6862_remove), .id_table = tef6862_id, }; diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 7f26fdf2e54e..5dd0386604f0 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -255,7 +255,7 @@ static struct usb_device_id imon_usb_id_table[] = { static struct usb_driver imon_driver = { .name = MOD_NAME, .probe = imon_probe, - .disconnect = imon_disconnect, + .disconnect = __devexit_p(imon_disconnect), .suspend = imon_suspend, .resume = imon_resume, .id_table = imon_usb_id_table, diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 3e29cc55fdd1..84e06d3aa696 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1442,7 +1442,7 @@ static int mceusb_dev_resume(struct usb_interface *intf) static struct usb_driver mceusb_dev_driver = { .name = DRIVER_NAME, .probe = mceusb_dev_probe, - .disconnect = mceusb_dev_disconnect, + .disconnect = __devexit_p(mceusb_dev_disconnect), .suspend = mceusb_dev_suspend, .resume = mceusb_dev_resume, .reset_resume = mceusb_dev_resume, diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index ad95c67a4dba..2878b0ed9741 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -1277,7 +1277,7 @@ static int redrat3_dev_resume(struct usb_interface *intf) static struct usb_driver redrat3_dev_driver = { .name = DRIVER_NAME, .probe = redrat3_dev_probe, - .disconnect = redrat3_dev_disconnect, + .disconnect = __devexit_p(redrat3_dev_disconnect), .suspend = redrat3_dev_suspend, .resume = redrat3_dev_resume, .reset_resume = redrat3_dev_resume, -- cgit v1.2.3 From d2b30e9068577e15e432974ddf0862315ec2069c Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Sat, 5 May 2012 12:17:33 -0300 Subject: [media] em28xx: Remove unused wait_queue's Nobody ever waits on any of these wait_queue's, so this patch removes them completely. Tested by compilation only. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 7 ------- drivers/media/video/em28xx/em28xx-video.c | 1 - drivers/media/video/em28xx/em28xx.h | 1 - 3 files changed, 9 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index fcc713f8098d..20a7e24de6fb 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -2945,9 +2945,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, dev->udev = udev; mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); - init_waitqueue_head(&dev->open); - init_waitqueue_head(&dev->wait_frame); - init_waitqueue_head(&dev->wait_stream); dev->em28xx_write_regs = em28xx_write_regs; dev->em28xx_read_reg = em28xx_read_reg; @@ -3385,8 +3382,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) resources */ mutex_lock(&dev->lock); - wake_up_interruptible_all(&dev->open); - v4l2_device_disconnect(&dev->v4l2_dev); if (dev->users) { @@ -3398,8 +3393,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) dev->state |= DEV_MISCONFIGURED; em28xx_uninit_isoc(dev, dev->mode); dev->state |= DEV_DISCONNECTED; - wake_up_interruptible(&dev->wait_frame); - wake_up_interruptible(&dev->wait_stream); } else { dev->state |= DEV_DISCONNECTED; em28xx_release_resources(dev); diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 308a1dd08cfb..1d4068052ef0 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -2284,7 +2284,6 @@ static int em28xx_v4l2_close(struct file *filp) videobuf_mmap_free(&fh->vb_vbiq); kfree(fh); dev->users--; - wake_up_interruptible_nr(&dev->open, 1); return 0; } diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index a3f77f6f5d95..8757523e6863 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -570,7 +570,6 @@ struct em28xx { struct mutex ctrl_urb_lock; /* protects urb_buf */ /* spinlock_t queue_lock; */ struct list_head inqueue, outqueue; - wait_queue_head_t open, wait_frame, wait_stream; struct video_device *vbi_dev; struct video_device *radio_dev; -- cgit v1.2.3 From 0550b29464f1d43a690198a6a8e984ae788ba9fa Mon Sep 17 00:00:00 2001 From: joseph daniel Date: Sun, 6 May 2012 01:33:51 -0300 Subject: [media] staging/media/as102: removed else statements The else statement is actually not required, as we can assign AS10X_CMD_ERROR to the error variable directly. Signed-off-by: joseph daniel Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/as102/as10x_cmd.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/as102/as10x_cmd.c b/drivers/staging/media/as102/as10x_cmd.c index 262bb94ad27e..a73df10982d0 100644 --- a/drivers/staging/media/as102/as10x_cmd.c +++ b/drivers/staging/media/as102/as10x_cmd.c @@ -31,7 +31,7 @@ */ int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -54,8 +54,6 @@ int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap) (uint8_t *) prsp, sizeof(prsp->body.turn_on.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -77,7 +75,7 @@ out: */ int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -99,8 +97,6 @@ int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap) sizeof(pcmd->body.turn_off.req) + HEADER_SIZE, (uint8_t *) prsp, sizeof(prsp->body.turn_off.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -124,7 +120,7 @@ out: int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, struct as10x_tune_args *ptune) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *preq, *prsp; ENTER(); @@ -159,8 +155,6 @@ int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, (uint8_t *) prsp, sizeof(prsp->body.set_tune.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -184,7 +178,7 @@ out: int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, struct as10x_tune_status *pstatus) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *preq, *prsp; ENTER(); @@ -208,8 +202,6 @@ int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, sizeof(preq->body.get_tune_status.req) + HEADER_SIZE, (uint8_t *) prsp, sizeof(prsp->body.get_tune_status.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -241,7 +233,7 @@ out: */ int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -266,8 +258,6 @@ int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps) (uint8_t *) prsp, sizeof(prsp->body.get_tps.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -305,7 +295,7 @@ out: int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, struct as10x_demod_stats *pdemod_stats) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -330,8 +320,6 @@ int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, (uint8_t *) prsp, sizeof(prsp->body.get_demod_stats.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -370,7 +358,7 @@ out: int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, uint8_t *is_ready) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -395,8 +383,6 @@ int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, (uint8_t *) prsp, sizeof(prsp->body.get_impulse_rsp.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) -- cgit v1.2.3 From 9ab494b010e1b4c4693afa297ffbcf0f09bb9063 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Mon, 7 May 2012 11:22:52 -0300 Subject: [media] it913x.: Fix a misuse of || On Mon, 2012-05-07 at 07:45 -0300, Mauro Carvalho Chehab wrote: > Malcolm, > > Em 04-04-2012 20:00, Joe Perches escreveu: > > Likely these should be && not || > > > > drivers/scsi/FlashPoint.c: if(bit_cnt != 0 || bit_cnt != 8) > > > drivers/media/dvb/dvb-usb/it913x.c: if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT) > > drivers/media/dvb/dvb-usb/it913x.c: if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT) > > Could you please take a look on the above? Hmm... yes, thanks, also a bug. Just check for -EBUSY && -ETIMEDOUT Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/it913x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c index 482d249ca7f3..6244fe9d1a3a 100644 --- a/drivers/media/dvb/dvb-usb/it913x.c +++ b/drivers/media/dvb/dvb-usb/it913x.c @@ -81,7 +81,7 @@ static int it913x_bulk_write(struct usb_device *dev, for (i = 0; i < IT913X_RETRY; i++) { ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), snd, len , &actual_l, IT913X_SND_TIMEOUT); - if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT) + if (ret != -EBUSY && ret != -ETIMEDOUT) break; } @@ -99,7 +99,7 @@ static int it913x_bulk_read(struct usb_device *dev, for (i = 0; i < IT913X_RETRY; i++) { ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe), rev, len , &actual_l, IT913X_RCV_TIMEOUT); - if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT) + if (ret != -EBUSY && ret != -ETIMEDOUT) break; } -- cgit v1.2.3 From b7dc4cd17506284a47eeb9160207e43c7d5486fe Mon Sep 17 00:00:00 2001 From: Il Han Date: Mon, 7 May 2012 12:04:42 -0300 Subject: [media] lmedm04: Initialize a variable before its usage The variable ret is used uninitialized. It should be initialized before used. Initialize it. Signed-off-by: Il Han Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/lmedm04.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index 003fb9bb7117..25d1031460f8 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -373,7 +373,7 @@ static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) struct lme2510_state *st = adap->dev->priv; static u8 clear_pid_reg[] = LME_ALL_PIDS; static u8 rbuf[1]; - int ret; + int ret = 0; deb_info(1, "PID Clearing Filter"); -- cgit v1.2.3 From 6e6d76cdc541e28bf4f609141d76c488c6c0d263 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 7 May 2012 16:53:20 -0300 Subject: [media] v4l2-event: fix regression with initial event handling If the V4L2_EVENT_SUB_FL_SEND_INITIAL was set, then the application expects to receive an initial event of the initial value of the control. However, commit c53c2549333b340e2662dc64ec81323476b69a97 that added the new v4l2_subscribed_event_ops introduced a regression: while the code still queued that initial event the __v4l2_event_queue_fh() function was modified to ignore such requests if sev->elems was 0 (meaning that the event subscription wasn't finished yet). And sev->elems was only set to a non-zero value after the add operation returned. This patch fixes this by passing the elems value to the add function. Then the add function can set it before queuing the initial event. Signed-off-by: Hans Verkuil Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 5 ++++- drivers/media/video/v4l2-ctrls.c | 5 ++++- drivers/media/video/v4l2-event.c | 2 +- include/media/v4l2-event.h | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 28363b72ff8a..f3bd66c500b6 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1250,7 +1250,7 @@ static void uvc_ctrl_send_events(struct uvc_fh *handle, } } -static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev) +static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); struct uvc_control_mapping *mapping; @@ -1278,6 +1278,9 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev) uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val, changes); + /* Mark the queue as active, allowing this initial + event to be accepted. */ + sev->elems = elems; v4l2_event_queue_fh(sev->fh, &ev); } diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index a5fbace4c059..9abd9abd4502 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -2559,7 +2559,7 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); -static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev) +static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); @@ -2576,6 +2576,9 @@ static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev) if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)) changes |= V4L2_EVENT_CTRL_CH_VALUE; fill_event(&ev, ctrl, changes); + /* Mark the queue as active, allowing this initial + event to be accepted. */ + sev->elems = elems; v4l2_event_queue_fh(sev->fh, &ev); } v4l2_ctrl_unlock(ctrl); diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index 60b4e2e9c874..ef2a33c94045 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c @@ -239,7 +239,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, } if (sev->ops && sev->ops->add) { - int ret = sev->ops->add(sev); + int ret = sev->ops->add(sev, elems); if (ret) { sev->ops = NULL; v4l2_event_unsubscribe(fh, sub); diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index 88fa9a1e0df3..2885a810a128 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -85,7 +85,7 @@ struct v4l2_kevent { * @merge: Optional callback that can merge event 'old' into event 'new'. */ struct v4l2_subscribed_event_ops { - int (*add)(struct v4l2_subscribed_event *sev); + int (*add)(struct v4l2_subscribed_event *sev, unsigned elems); void (*del)(struct v4l2_subscribed_event *sev); void (*replace)(struct v4l2_event *old, const struct v4l2_event *new); void (*merge)(const struct v4l2_event *old, struct v4l2_event *new); -- cgit v1.2.3 From cbde6a2dbaf704defb95958af335fddd770edc4c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 8 May 2012 12:56:15 -0300 Subject: [media] V4L: marvell-ccic: (cosmetic) remove redundant variable assignment The "ret = 0" assignment in mcam_vidioc_s_fmt_vid_cap() is redundant, because at that location "ret" is anyway guaranteed to be == 0. Signed-off-by: Guennadi Liakhovetski Acked-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/marvell-ccic/mcam-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c index 996ac34d9a89..ce2b7b4788d6 100644 --- a/drivers/media/video/marvell-ccic/mcam-core.c +++ b/drivers/media/video/marvell-ccic/mcam-core.c @@ -1356,7 +1356,6 @@ static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, goto out; } mcam_set_config_needed(cam, 1); - ret = 0; out: mutex_unlock(&cam->s_mutex); return ret; -- cgit v1.2.3 From ad3537b56742848743aa11d42ccc1d336682bd5b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 8 May 2012 13:00:51 -0300 Subject: [media] V4L: soc-camera: (cosmetic) use a more explicit name for a host handler Use "enum_framesizes" instead of "enum_fsizes" to more precisely follow the name of the respective ioctl(). Signed-off-by: Guennadi Liakhovetski Reviewed-by: Sergio Aguirre Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 14 +++++++------- include/media/soc_camera.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index c3ab55883c2d..d86b15084628 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -257,13 +257,13 @@ static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) return v4l2_subdev_call(sd, core, g_std, a); } -static int soc_camera_enum_fsizes(struct file *file, void *fh, +static int soc_camera_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - return ici->ops->enum_fsizes(icd, fsize); + return ici->ops->enum_framesizes(icd, fsize); } static int soc_camera_reqbufs(struct file *file, void *priv, @@ -1244,8 +1244,8 @@ static int default_s_parm(struct soc_camera_device *icd, return v4l2_subdev_call(sd, video, s_parm, parm); } -static int default_enum_fsizes(struct soc_camera_device *icd, - struct v4l2_frmsizeenum *fsize) +static int default_enum_framesizes(struct soc_camera_device *icd, + struct v4l2_frmsizeenum *fsize) { int ret; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); @@ -1298,8 +1298,8 @@ int soc_camera_host_register(struct soc_camera_host *ici) ici->ops->set_parm = default_s_parm; if (!ici->ops->get_parm) ici->ops->get_parm = default_g_parm; - if (!ici->ops->enum_fsizes) - ici->ops->enum_fsizes = default_enum_fsizes; + if (!ici->ops->enum_framesizes) + ici->ops->enum_framesizes = default_enum_framesizes; mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { @@ -1390,7 +1390,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_s_input = soc_camera_s_input, .vidioc_s_std = soc_camera_s_std, .vidioc_g_std = soc_camera_g_std, - .vidioc_enum_framesizes = soc_camera_enum_fsizes, + .vidioc_enum_framesizes = soc_camera_enum_framesizes, .vidioc_reqbufs = soc_camera_reqbufs, .vidioc_querybuf = soc_camera_querybuf, .vidioc_qbuf = soc_camera_qbuf, diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index cad374bdcf4b..a87062c393b5 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -98,7 +98,7 @@ struct soc_camera_host_ops { int (*set_bus_param)(struct soc_camera_device *); int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *); int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *); - int (*enum_fsizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *); + int (*enum_framesizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *); unsigned int (*poll)(struct file *, poll_table *); }; -- cgit v1.2.3 From ab019fd41d993911ba32db0204bb1233682705ee Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 24 Feb 2012 11:24:14 -0300 Subject: [media] staging: easycap: Split device struct alloc and retrieval code When the device is probed a driver struct is either allocated or retrieved. This operation is logically splitted in several functions. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/easycap/easycap_main.c | 384 +++++++++++++++------------ 1 file changed, 216 insertions(+), 168 deletions(-) diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index d0fe34afc2e5..c4198be6344c 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -2842,6 +2842,209 @@ static void easycap_complete(struct urb *purb) return; } +static struct easycap *alloc_easycap(u8 bInterfaceNumber) +{ + struct easycap *peasycap; + int i; + + peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL); + if (!peasycap) { + SAY("ERROR: Could not allocate peasycap\n"); + return NULL; + } + + if (mutex_lock_interruptible(&mutex_dongle)) { + SAY("ERROR: cannot lock mutex_dongle\n"); + kfree(peasycap); + return NULL; + } + + /* Find a free dongle in easycapdc60_dongle array */ + for (i = 0; i < DONGLE_MANY; i++) { + + if ((!easycapdc60_dongle[i].peasycap) && + (!mutex_is_locked(&easycapdc60_dongle[i].mutex_video)) && + (!mutex_is_locked(&easycapdc60_dongle[i].mutex_audio))) { + + easycapdc60_dongle[i].peasycap = peasycap; + peasycap->isdongle = i; + JOM(8, "intf[%i]: peasycap-->easycap" + "_dongle[%i].peasycap\n", + bInterfaceNumber, i); + break; + } + } + + mutex_unlock(&mutex_dongle); + + if (i >= DONGLE_MANY) { + SAM("ERROR: too many dongles\n"); + kfree(peasycap); + return NULL; + } + + return peasycap; +} + +/* + * FIXME: Identify the appropriate pointer peasycap for interfaces + * 1 and 2. The address of peasycap->pusb_device is reluctantly used + * for this purpose. + */ +static struct easycap *get_easycap(struct usb_device *usbdev, + u8 bInterfaceNumber) +{ + int i; + struct easycap *peasycap; + + for (i = 0; i < DONGLE_MANY; i++) { + if (easycapdc60_dongle[i].peasycap->pusb_device == usbdev) { + peasycap = easycapdc60_dongle[i].peasycap; + JOT(8, "intf[%i]: dongle[%i].peasycap\n", + bInterfaceNumber, i); + break; + } + } + if (i >= DONGLE_MANY) { + SAY("ERROR: peasycap is unknown when probing interface %i\n", + bInterfaceNumber); + return NULL; + } + if (!peasycap) { + SAY("ERROR: peasycap is NULL when probing interface %i\n", + bInterfaceNumber); + return NULL; + } + + return peasycap; +} + +static void init_easycap(struct easycap *peasycap, + struct usb_device *usbdev, + struct usb_interface *intf, + u8 bInterfaceNumber) +{ + /* Save usb_device and usb_interface */ + peasycap->pusb_device = usbdev; + peasycap->pusb_interface = intf; + + peasycap->minor = -1; + kref_init(&peasycap->kref); + JOM(8, "intf[%i]: after kref_init(..._video) " + "%i=peasycap->kref.refcount.counter\n", + bInterfaceNumber, peasycap->kref.refcount.counter); + + /* module params */ + peasycap->gain = (s8)clamp(easycap_gain, 0, 31); + + init_waitqueue_head(&peasycap->wq_video); + init_waitqueue_head(&peasycap->wq_audio); + init_waitqueue_head(&peasycap->wq_trigger); + + peasycap->allocation_video_struct = sizeof(struct easycap); + + peasycap->microphone = false; + + peasycap->video_interface = -1; + peasycap->video_altsetting_on = -1; + peasycap->video_altsetting_off = -1; + peasycap->video_endpointnumber = -1; + peasycap->video_isoc_maxframesize = -1; + peasycap->video_isoc_buffer_size = -1; + + peasycap->audio_interface = -1; + peasycap->audio_altsetting_on = -1; + peasycap->audio_altsetting_off = -1; + peasycap->audio_endpointnumber = -1; + peasycap->audio_isoc_maxframesize = -1; + peasycap->audio_isoc_buffer_size = -1; + + peasycap->frame_buffer_many = FRAME_BUFFER_MANY; +} + +static int populate_inputset(struct easycap *peasycap) +{ + struct inputset *inputset; + struct easycap_format *peasycap_format; + struct v4l2_pix_format *pix; + int m, i, k, mask, fmtidx; + s32 value; + + inputset = peasycap->inputset; + + /* FIXME: peasycap->ntsc is not yet initialized */ + fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN; + + m = 0; + mask = 0; + for (i = 0; easycap_standard[i].mask != 0xffff; i++) { + if (fmtidx == easycap_standard[i].v4l2_standard.index) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].standard_offset = i; + mask = easycap_standard[i].mask; + } + } + + if (m != 1) { + SAM("ERROR: inputset->standard_offset unpopulated, %i=m\n", m); + return -ENOENT; + } + + peasycap_format = &easycap_format[0]; + m = 0; + for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) { + pix = &peasycap_format->v4l2_format.fmt.pix; + if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) + && pix->field == V4L2_FIELD_NONE + && pix->pixelformat == V4L2_PIX_FMT_UYVY + && pix->width == 640 && pix->height == 480) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].format_offset = i; + break; + } + peasycap_format++; + } + if (m != 1) { + SAM("ERROR: inputset[]->format_offset unpopulated\n"); + return -ENOENT; + } + + m = 0; + for (i = 0; easycap_control[i].id != 0xffffffff; i++) { + value = easycap_control[i].default_value; + if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].brightness = value; + } else if (V4L2_CID_CONTRAST == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].contrast = value; + } else if (V4L2_CID_SATURATION == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].saturation = value; + } else if (V4L2_CID_HUE == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].hue = value; + } + } + + if (m != 4) { + SAM("ERROR: inputset[]->brightness underpopulated\n"); + return -ENOENT; + } + + for (k = 0; k < INPUT_MANY; k++) + inputset[k].input = k; + JOM(4, "populated inputset[]\n"); + + return 0; +} + static const struct v4l2_file_operations v4l2_fops = { .owner = THIS_MODULE, .open = easycap_open_noinode, @@ -2863,7 +3066,6 @@ static int easycap_usb_probe(struct usb_interface *intf, struct usb_interface_descriptor *interface; struct urb *purb; struct easycap *peasycap; - int ndong; struct data_urb *pdata_urb; int i, j, k, m, rc; u8 bInterfaceNumber; @@ -2874,11 +3076,6 @@ static int easycap_usb_probe(struct usb_interface *intf, int okepn[8]; int okmps[8]; int maxpacketsize; - u16 mask; - s32 value; - struct easycap_format *peasycap_format; - int fmtidx; - struct inputset *inputset; usbdev = interface_to_usbdev(intf); @@ -2916,76 +3113,16 @@ static int easycap_usb_probe(struct usb_interface *intf, * interfaces 1 and 2 are probed. */ if (0 == bInterfaceNumber) { - peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL); - if (!peasycap) { - SAY("ERROR: Could not allocate peasycap\n"); - return -ENOMEM; - } - - /* Perform urgent initializations */ - peasycap->minor = -1; - kref_init(&peasycap->kref); - JOM(8, "intf[%i]: after kref_init(..._video) " - "%i=peasycap->kref.refcount.counter\n", - bInterfaceNumber, peasycap->kref.refcount.counter); - - /* module params */ - peasycap->gain = (s8)clamp(easycap_gain, 0, 31); - - init_waitqueue_head(&peasycap->wq_video); - init_waitqueue_head(&peasycap->wq_audio); - init_waitqueue_head(&peasycap->wq_trigger); - - if (mutex_lock_interruptible(&mutex_dongle)) { - SAY("ERROR: cannot down mutex_dongle\n"); - return -ERESTARTSYS; - } - - for (ndong = 0; ndong < DONGLE_MANY; ndong++) { - if ((!easycapdc60_dongle[ndong].peasycap) && - (!mutex_is_locked(&easycapdc60_dongle - [ndong].mutex_video)) && - (!mutex_is_locked(&easycapdc60_dongle - [ndong].mutex_audio))) { - easycapdc60_dongle[ndong].peasycap = peasycap; - peasycap->isdongle = ndong; - JOM(8, "intf[%i]: peasycap-->easycap" - "_dongle[%i].peasycap\n", - bInterfaceNumber, ndong); - break; - } - } - - if (DONGLE_MANY <= ndong) { - SAM("ERROR: too many dongles\n"); - mutex_unlock(&mutex_dongle); + /* + * Alloc structure and save it in a free slot in + * easycapdc60_dongle array + */ + peasycap = alloc_easycap(bInterfaceNumber); + if (!peasycap) return -ENOMEM; - } - mutex_unlock(&mutex_dongle); - peasycap->allocation_video_struct = sizeof(struct easycap); - - /* and further initialize the structure */ - peasycap->pusb_device = usbdev; - peasycap->pusb_interface = intf; - - peasycap->microphone = false; - - peasycap->video_interface = -1; - peasycap->video_altsetting_on = -1; - peasycap->video_altsetting_off = -1; - peasycap->video_endpointnumber = -1; - peasycap->video_isoc_maxframesize = -1; - peasycap->video_isoc_buffer_size = -1; - - peasycap->audio_interface = -1; - peasycap->audio_altsetting_on = -1; - peasycap->audio_altsetting_off = -1; - peasycap->audio_endpointnumber = -1; - peasycap->audio_isoc_maxframesize = -1; - peasycap->audio_isoc_buffer_size = -1; - - peasycap->frame_buffer_many = FRAME_BUFFER_MANY; + /* Perform basic struct initialization */ + init_easycap(peasycap, usbdev, intf, bInterfaceNumber); /* Dynamically fill in the available formats */ rc = easycap_video_fillin_formats(); @@ -2996,103 +3133,14 @@ static int easycap_usb_probe(struct usb_interface *intf, JOM(4, "%i formats available\n", rc); /* Populate easycap.inputset[] */ - inputset = peasycap->inputset; - fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN; - m = 0; - mask = 0; - for (i = 0; 0xFFFF != easycap_standard[i].mask; i++) { - if (fmtidx == easycap_standard[i].v4l2_standard.index) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].standard_offset = i; - - mask = easycap_standard[i].mask; - } - } - if (1 != m) { - SAM("ERROR: " - "inputset->standard_offset unpopulated, %i=m\n", m); - return -ENOENT; - } - - peasycap_format = &easycap_format[0]; - m = 0; - for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) { - struct v4l2_pix_format *pix = - &peasycap_format->v4l2_format.fmt.pix; - if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) && - pix->field == V4L2_FIELD_NONE && - pix->pixelformat == V4L2_PIX_FMT_UYVY && - pix->width == 640 && pix->height == 480) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].format_offset = i; - break; - } - peasycap_format++; - } - if (1 != m) { - SAM("ERROR: inputset[]->format_offset unpopulated\n"); - return -ENOENT; - } - - m = 0; - for (i = 0; 0xFFFFFFFF != easycap_control[i].id; i++) { - value = easycap_control[i].default_value; - if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].brightness = value; - } else if (V4L2_CID_CONTRAST == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].contrast = value; - } else if (V4L2_CID_SATURATION == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].saturation = value; - } else if (V4L2_CID_HUE == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].hue = value; - } - } - - if (4 != m) { - SAM("ERROR: inputset[]->brightness underpopulated\n"); - return -ENOENT; - } - for (k = 0; k < INPUT_MANY; k++) - inputset[k].input = k; - JOM(4, "populated inputset[]\n"); + rc = populate_inputset(peasycap); + if (rc < 0) + return rc; JOM(4, "finished initialization\n"); } else { - - /* - * FIXME: Identify the appropriate pointer - * peasycap for interfaces 1 and 2. - * The address of peasycap->pusb_device - * is reluctantly used for this purpose. - */ - for (ndong = 0; ndong < DONGLE_MANY; ndong++) { - if (usbdev == easycapdc60_dongle[ndong].peasycap-> - pusb_device) { - peasycap = easycapdc60_dongle[ndong].peasycap; - JOT(8, "intf[%i]: dongle[%i].peasycap\n", - bInterfaceNumber, ndong); - break; - } - } - if (DONGLE_MANY <= ndong) { - SAY("ERROR: peasycap is unknown when probing interface %i\n", - bInterfaceNumber); + peasycap = get_easycap(usbdev, bInterfaceNumber); + if (!peasycap) return -ENODEV; - } - if (!peasycap) { - SAY("ERROR: peasycap is NULL when probing interface %i\n", - bInterfaceNumber); - return -ENODEV; - } } if ((USB_CLASS_VIDEO == bInterfaceClass) || -- cgit v1.2.3 From 20c50af28aa9301b1f5bf0ba571068f663f7b0ac Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 24 Feb 2012 11:24:15 -0300 Subject: [media] staging: easycap: Split buffer and video urb allocation When the device is probed, this driver allocates frame buffers, field buffers, isoc buffers and urbs. This patch just split this into separate functions, which helps clearing the currently gigantic probe function. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/easycap/easycap_main.c | 375 +++++++++++++++------------ 1 file changed, 211 insertions(+), 164 deletions(-) diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index c4198be6344c..6d7cdef58800 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -3045,6 +3045,203 @@ static int populate_inputset(struct easycap *peasycap) return 0; } +static int alloc_framebuffers(struct easycap *peasycap) +{ + int i, j; + void *pbuf; + + JOM(4, "allocating %i frame buffers of size %li\n", + FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE); + JOM(4, ".... each scattered over %li pages\n", + FRAME_BUFFER_SIZE/PAGE_SIZE); + + for (i = 0; i < FRAME_BUFFER_MANY; i++) { + for (j = 0; j < FRAME_BUFFER_SIZE/PAGE_SIZE; j++) { + if (peasycap->frame_buffer[i][j].pgo) + SAM("attempting to reallocate framebuffers\n"); + else { + pbuf = (void *)__get_free_page(GFP_KERNEL); + if (!pbuf) { + SAM("ERROR: Could not allocate " + "framebuffer %i page %i\n", i, j); + return -ENOMEM; + } + peasycap->allocation_video_page += 1; + peasycap->frame_buffer[i][j].pgo = pbuf; + } + peasycap->frame_buffer[i][j].pto = + peasycap->frame_buffer[i][j].pgo; + } + } + + peasycap->frame_fill = 0; + peasycap->frame_read = 0; + JOM(4, "allocation of frame buffers done: %i pages\n", i*j); + + return 0; +} + +static int alloc_fieldbuffers(struct easycap *peasycap) +{ + int i, j; + void *pbuf; + + JOM(4, "allocating %i field buffers of size %li\n", + FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE); + JOM(4, ".... each scattered over %li pages\n", + FIELD_BUFFER_SIZE/PAGE_SIZE); + + for (i = 0; i < FIELD_BUFFER_MANY; i++) { + for (j = 0; j < FIELD_BUFFER_SIZE/PAGE_SIZE; j++) { + if (peasycap->field_buffer[i][j].pgo) { + SAM("ERROR: attempting to reallocate " + "fieldbuffers\n"); + } else { + pbuf = (void *) __get_free_page(GFP_KERNEL); + if (!pbuf) { + SAM("ERROR: Could not allocate " + "fieldbuffer %i page %i\n", i, j); + return -ENOMEM; + } + peasycap->allocation_video_page += 1; + peasycap->field_buffer[i][j].pgo = pbuf; + } + peasycap->field_buffer[i][j].pto = + peasycap->field_buffer[i][j].pgo; + } + /* TODO: Hardcoded 0x0200 meaning? */ + peasycap->field_buffer[i][0].kount = 0x0200; + } + peasycap->field_fill = 0; + peasycap->field_page = 0; + peasycap->field_read = 0; + JOM(4, "allocation of field buffers done: %i pages\n", i*j); + + return 0; +} + +static int alloc_isocbuffers(struct easycap *peasycap) +{ + int i; + void *pbuf; + + JOM(4, "allocating %i isoc video buffers of size %i\n", + VIDEO_ISOC_BUFFER_MANY, + peasycap->video_isoc_buffer_size); + JOM(4, ".... each occupying contiguous memory pages\n"); + + for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++) { + pbuf = (void *)__get_free_pages(GFP_KERNEL, + VIDEO_ISOC_ORDER); + if (!pbuf) { + SAM("ERROR: Could not allocate isoc " + "video buffer %i\n", i); + return -ENOMEM; + } + peasycap->allocation_video_page += BIT(VIDEO_ISOC_ORDER); + + peasycap->video_isoc_buffer[i].pgo = pbuf; + peasycap->video_isoc_buffer[i].pto = + pbuf + peasycap->video_isoc_buffer_size; + peasycap->video_isoc_buffer[i].kount = i; + } + JOM(4, "allocation of isoc video buffers done: %i pages\n", + i * (0x01 << VIDEO_ISOC_ORDER)); + return 0; +} + +static int create_video_urbs(struct easycap *peasycap) +{ + struct urb *purb; + struct data_urb *pdata_urb; + int i, j; + + JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY); + JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n", + peasycap->video_isoc_framesperdesc); + JOM(4, "using %i=peasycap->video_isoc_maxframesize\n", + peasycap->video_isoc_maxframesize); + JOM(4, "using %i=peasycap->video_isoc_buffer_sizen", + peasycap->video_isoc_buffer_size); + + for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++) { + purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc, + GFP_KERNEL); + if (!purb) { + SAM("ERROR: usb_alloc_urb returned NULL for buffer " + "%i\n", i); + return -ENOMEM; + } + + peasycap->allocation_video_urb += 1; + pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); + if (!pdata_urb) { + SAM("ERROR: Could not allocate struct data_urb.\n"); + return -ENOMEM; + } + + peasycap->allocation_video_struct += + sizeof(struct data_urb); + + pdata_urb->purb = purb; + pdata_urb->isbuf = i; + pdata_urb->length = 0; + list_add_tail(&(pdata_urb->list_head), + peasycap->purb_video_head); + + if (!i) { + JOM(4, "initializing video urbs thus:\n"); + JOM(4, " purb->interval = 1;\n"); + JOM(4, " purb->dev = peasycap->pusb_device;\n"); + JOM(4, " purb->pipe = usb_rcvisocpipe" + "(peasycap->pusb_device,%i);\n", + peasycap->video_endpointnumber); + JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); + JOM(4, " purb->transfer_buffer = peasycap->" + "video_isoc_buffer[.].pgo;\n"); + JOM(4, " purb->transfer_buffer_length = %i;\n", + peasycap->video_isoc_buffer_size); + JOM(4, " purb->complete = easycap_complete;\n"); + JOM(4, " purb->context = peasycap;\n"); + JOM(4, " purb->start_frame = 0;\n"); + JOM(4, " purb->number_of_packets = %i;\n", + peasycap->video_isoc_framesperdesc); + JOM(4, " for (j = 0; j < %i; j++)\n", + peasycap->video_isoc_framesperdesc); + JOM(4, " {\n"); + JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", + peasycap->video_isoc_maxframesize); + JOM(4, " purb->iso_frame_desc[j].length = %i;\n", + peasycap->video_isoc_maxframesize); + JOM(4, " }\n"); + } + + purb->interval = 1; + purb->dev = peasycap->pusb_device; + purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, + peasycap->video_endpointnumber); + + purb->transfer_flags = URB_ISO_ASAP; + purb->transfer_buffer = peasycap->video_isoc_buffer[i].pgo; + purb->transfer_buffer_length = + peasycap->video_isoc_buffer_size; + + purb->complete = easycap_complete; + purb->context = peasycap; + purb->start_frame = 0; + purb->number_of_packets = peasycap->video_isoc_framesperdesc; + + for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { + purb->iso_frame_desc[j].offset = + j * peasycap->video_isoc_maxframesize; + purb->iso_frame_desc[j].length = + peasycap->video_isoc_maxframesize; + } + } + JOM(4, "allocation of %i struct urb done.\n", i); + return 0; +} + static const struct v4l2_file_operations v4l2_fops = { .owner = THIS_MODULE, .open = easycap_open_noinode, @@ -3067,7 +3264,7 @@ static int easycap_usb_probe(struct usb_interface *intf, struct urb *purb; struct easycap *peasycap; struct data_urb *pdata_urb; - int i, j, k, m, rc; + int i, j, k, rc; u8 bInterfaceNumber; u8 bInterfaceClass; u8 bInterfaceSubClass; @@ -3416,173 +3613,23 @@ static int easycap_usb_probe(struct usb_interface *intf, */ INIT_LIST_HEAD(&(peasycap->urb_video_head)); peasycap->purb_video_head = &(peasycap->urb_video_head); - JOM(4, "allocating %i frame buffers of size %li\n", - FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE); - JOM(4, ".... each scattered over %li pages\n", - FRAME_BUFFER_SIZE/PAGE_SIZE); - - for (k = 0; k < FRAME_BUFFER_MANY; k++) { - for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->frame_buffer[k][m].pgo) - SAM("attempting to reallocate frame " - " buffers\n"); - else { - pbuf = (void *)__get_free_page(GFP_KERNEL); - if (!pbuf) { - SAM("ERROR: Could not allocate frame " - "buffer %i page %i\n", k, m); - return -ENOMEM; - } - - peasycap->allocation_video_page += 1; - peasycap->frame_buffer[k][m].pgo = pbuf; - } - peasycap->frame_buffer[k][m].pto = - peasycap->frame_buffer[k][m].pgo; - } - } - peasycap->frame_fill = 0; - peasycap->frame_read = 0; - JOM(4, "allocation of frame buffers done: %i pages\n", k * - m); - JOM(4, "allocating %i field buffers of size %li\n", - FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE); - JOM(4, ".... each scattered over %li pages\n", - FIELD_BUFFER_SIZE/PAGE_SIZE); - - for (k = 0; k < FIELD_BUFFER_MANY; k++) { - for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->field_buffer[k][m].pgo) { - SAM("ERROR: attempting to reallocate " - "field buffers\n"); - } else { - pbuf = (void *) __get_free_page(GFP_KERNEL); - if (!pbuf) { - SAM("ERROR: Could not allocate field" - " buffer %i page %i\n", k, m); - return -ENOMEM; - } - - peasycap->allocation_video_page += 1; - peasycap->field_buffer[k][m].pgo = pbuf; - } - peasycap->field_buffer[k][m].pto = - peasycap->field_buffer[k][m].pgo; - } - peasycap->field_buffer[k][0].kount = 0x0200; - } - peasycap->field_fill = 0; - peasycap->field_page = 0; - peasycap->field_read = 0; - JOM(4, "allocation of field buffers done: %i pages\n", k * - m); - JOM(4, "allocating %i isoc video buffers of size %i\n", - VIDEO_ISOC_BUFFER_MANY, - peasycap->video_isoc_buffer_size); - JOM(4, ".... each occupying contiguous memory pages\n"); - - for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { - pbuf = (void *)__get_free_pages(GFP_KERNEL, - VIDEO_ISOC_ORDER); - if (!pbuf) { - SAM("ERROR: Could not allocate isoc video buffer " - "%i\n", k); - return -ENOMEM; - } - peasycap->allocation_video_page += - BIT(VIDEO_ISOC_ORDER); - - peasycap->video_isoc_buffer[k].pgo = pbuf; - peasycap->video_isoc_buffer[k].pto = - pbuf + peasycap->video_isoc_buffer_size; - peasycap->video_isoc_buffer[k].kount = k; - } - JOM(4, "allocation of isoc video buffers done: %i pages\n", - k * (0x01 << VIDEO_ISOC_ORDER)); - - /* Allocate and initialize multiple struct usb */ - JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY); - JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n", - peasycap->video_isoc_framesperdesc); - JOM(4, "using %i=peasycap->video_isoc_maxframesize\n", - peasycap->video_isoc_maxframesize); - JOM(4, "using %i=peasycap->video_isoc_buffer_sizen", - peasycap->video_isoc_buffer_size); - - for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { - purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc, - GFP_KERNEL); - if (!purb) { - SAM("ERROR: usb_alloc_urb returned NULL for buffer " - "%i\n", k); - return -ENOMEM; - } - - peasycap->allocation_video_urb += 1; - pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); - if (!pdata_urb) { - SAM("ERROR: Could not allocate struct data_urb.\n"); - return -ENOMEM; - } - - peasycap->allocation_video_struct += - sizeof(struct data_urb); + rc = alloc_framebuffers(peasycap); + if (rc < 0) + return rc; - pdata_urb->purb = purb; - pdata_urb->isbuf = k; - pdata_urb->length = 0; - list_add_tail(&(pdata_urb->list_head), - peasycap->purb_video_head); + rc = alloc_fieldbuffers(peasycap); + if (rc < 0) + return rc; - /* Initialize allocated urbs */ - if (!k) { - JOM(4, "initializing video urbs thus:\n"); - JOM(4, " purb->interval = 1;\n"); - JOM(4, " purb->dev = peasycap->pusb_device;\n"); - JOM(4, " purb->pipe = usb_rcvisocpipe" - "(peasycap->pusb_device,%i);\n", - peasycap->video_endpointnumber); - JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); - JOM(4, " purb->transfer_buffer = peasycap->" - "video_isoc_buffer[.].pgo;\n"); - JOM(4, " purb->transfer_buffer_length = %i;\n", - peasycap->video_isoc_buffer_size); - JOM(4, " purb->complete = easycap_complete;\n"); - JOM(4, " purb->context = peasycap;\n"); - JOM(4, " purb->start_frame = 0;\n"); - JOM(4, " purb->number_of_packets = %i;\n", - peasycap->video_isoc_framesperdesc); - JOM(4, " for (j = 0; j < %i; j++)\n", - peasycap->video_isoc_framesperdesc); - JOM(4, " {\n"); - JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", - peasycap->video_isoc_maxframesize); - JOM(4, " purb->iso_frame_desc[j].length = %i;\n", - peasycap->video_isoc_maxframesize); - JOM(4, " }\n"); - } + rc = alloc_isocbuffers(peasycap); + if (rc < 0) + return rc; - purb->interval = 1; - purb->dev = peasycap->pusb_device; - purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, - peasycap->video_endpointnumber); - purb->transfer_flags = URB_ISO_ASAP; - purb->transfer_buffer = peasycap->video_isoc_buffer[k].pgo; - purb->transfer_buffer_length = - peasycap->video_isoc_buffer_size; - purb->complete = easycap_complete; - purb->context = peasycap; - purb->start_frame = 0; - purb->number_of_packets = peasycap->video_isoc_framesperdesc; - for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { - purb->iso_frame_desc[j].offset = j * - peasycap->video_isoc_maxframesize; - purb->iso_frame_desc[j].length = - peasycap->video_isoc_maxframesize; - } - } - JOM(4, "allocation of %i struct urb done.\n", k); + /* Allocate and initialize video urbs */ + rc = create_video_urbs(peasycap); + if (rc < 0) + return rc; /* Save pointer peasycap in this interface */ usb_set_intfdata(intf, peasycap); -- cgit v1.2.3 From f9482d01985b03633e39e1c772bd7d365ab82dc6 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 24 Feb 2012 11:24:16 -0300 Subject: [media] staging: easycap: Push bInterfaceNumber saving to config_easycap() Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/easycap/easycap_main.c | 72 ++++++++++++++++------------ 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index 6d7cdef58800..9d6dc09656be 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -3242,6 +3242,44 @@ static int create_video_urbs(struct easycap *peasycap) return 0; } +static void config_easycap(struct easycap *peasycap, + u8 bInterfaceNumber, + u8 bInterfaceClass, + u8 bInterfaceSubClass) +{ + if ((USB_CLASS_VIDEO == bInterfaceClass) || + (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) { + if (-1 == peasycap->video_interface) { + peasycap->video_interface = bInterfaceNumber; + JOM(4, "setting peasycap->video_interface=%i\n", + peasycap->video_interface); + } else { + if (peasycap->video_interface != bInterfaceNumber) { + SAM("ERROR: attempting to reset " + "peasycap->video_interface\n"); + SAM("...... continuing with " + "%i=peasycap->video_interface\n", + peasycap->video_interface); + } + } + } else if ((USB_CLASS_AUDIO == bInterfaceClass) && + (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) { + if (-1 == peasycap->audio_interface) { + peasycap->audio_interface = bInterfaceNumber; + JOM(4, "setting peasycap->audio_interface=%i\n", + peasycap->audio_interface); + } else { + if (peasycap->audio_interface != bInterfaceNumber) { + SAM("ERROR: attempting to reset " + "peasycap->audio_interface\n"); + SAM("...... continuing with " + "%i=peasycap->audio_interface\n", + peasycap->audio_interface); + } + } + } +} + static const struct v4l2_file_operations v4l2_fops = { .owner = THIS_MODULE, .open = easycap_open_noinode, @@ -3340,37 +3378,9 @@ static int easycap_usb_probe(struct usb_interface *intf, return -ENODEV; } - if ((USB_CLASS_VIDEO == bInterfaceClass) || - (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) { - if (-1 == peasycap->video_interface) { - peasycap->video_interface = bInterfaceNumber; - JOM(4, "setting peasycap->video_interface=%i\n", - peasycap->video_interface); - } else { - if (peasycap->video_interface != bInterfaceNumber) { - SAM("ERROR: attempting to reset " - "peasycap->video_interface\n"); - SAM("...... continuing with " - "%i=peasycap->video_interface\n", - peasycap->video_interface); - } - } - } else if ((USB_CLASS_AUDIO == bInterfaceClass) && - (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) { - if (-1 == peasycap->audio_interface) { - peasycap->audio_interface = bInterfaceNumber; - JOM(4, "setting peasycap->audio_interface=%i\n", - peasycap->audio_interface); - } else { - if (peasycap->audio_interface != bInterfaceNumber) { - SAM("ERROR: attempting to reset " - "peasycap->audio_interface\n"); - SAM("...... continuing with " - "%i=peasycap->audio_interface\n", - peasycap->audio_interface); - } - } - } + config_easycap(peasycap, bInterfaceNumber, + bInterfaceClass, + bInterfaceSubClass); /* * Investigate all altsettings. This is done in detail -- cgit v1.2.3 From 751869e6ef489e33c66a254fa004c92e76ef1a43 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 24 Feb 2012 11:24:17 -0300 Subject: [media] staging: easycap: Initialize 'ntsc' parameter before usage This parameter is now initialized at init_easycap(), this way we assure it won't be used uninitialized. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/easycap/easycap_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index 9d6dc09656be..480164d8698c 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -2960,6 +2960,10 @@ static void init_easycap(struct easycap *peasycap, peasycap->audio_isoc_buffer_size = -1; peasycap->frame_buffer_many = FRAME_BUFFER_MANY; + + peasycap->ntsc = easycap_ntsc; + JOM(8, "defaulting initially to %s\n", + easycap_ntsc ? "NTSC" : "PAL"); } static int populate_inputset(struct easycap *peasycap) @@ -2972,7 +2976,6 @@ static int populate_inputset(struct easycap *peasycap) inputset = peasycap->inputset; - /* FIXME: peasycap->ntsc is not yet initialized */ fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN; m = 0; @@ -3650,9 +3653,6 @@ static int easycap_usb_probe(struct usb_interface *intf, * because some udev rules triggers easycap_open() * immediately after registration, causing a clash. */ - peasycap->ntsc = easycap_ntsc; - JOM(8, "defaulting initially to %s\n", - easycap_ntsc ? "NTSC" : "PAL"); rc = reset(peasycap); if (rc) { SAM("ERROR: reset() rc = %i\n", rc); -- cgit v1.2.3 From 217d55f2ba0511a18f7427c4a0c10ea21c2eb7d0 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 24 Feb 2012 11:24:18 -0300 Subject: [media] staging: easycap: Push video registration to easycap_register_video() Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/easycap/easycap_main.c | 58 ++++++++++++++++------------ 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index 480164d8698c..68af1a2297be 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -3291,6 +3291,37 @@ static const struct v4l2_file_operations v4l2_fops = { .mmap = easycap_mmap, }; +static int easycap_register_video(struct easycap *peasycap) +{ + /* + * FIXME: This is believed to be harmless, + * but may well be unnecessary or wrong. + */ + peasycap->video_device.v4l2_dev = NULL; + + strcpy(&peasycap->video_device.name[0], "easycapdc60"); + peasycap->video_device.fops = &v4l2_fops; + peasycap->video_device.minor = -1; + peasycap->video_device.release = (void *)(&videodev_release); + + video_set_drvdata(&(peasycap->video_device), (void *)peasycap); + + if (0 != (video_register_device(&(peasycap->video_device), + VFL_TYPE_GRABBER, -1))) { + err("Not able to register with videodev"); + videodev_release(&(peasycap->video_device)); + return -ENODEV; + } + + peasycap->registered_video++; + + SAM("registered with videodev: %i=minor\n", + peasycap->video_device.minor); + peasycap->minor = peasycap->video_device.minor; + + return 0; +} + /* * When the device is plugged, this function is called three times, * one for each interface. @@ -3667,32 +3698,9 @@ static int easycap_usb_probe(struct usb_interface *intf, JOM(4, "registered device instance: %s\n", peasycap->v4l2_device.name); - /* - * FIXME: This is believed to be harmless, - * but may well be unnecessary or wrong. - */ - peasycap->video_device.v4l2_dev = NULL; - - - strcpy(&peasycap->video_device.name[0], "easycapdc60"); - peasycap->video_device.fops = &v4l2_fops; - peasycap->video_device.minor = -1; - peasycap->video_device.release = (void *)(&videodev_release); - - video_set_drvdata(&(peasycap->video_device), (void *)peasycap); - - if (0 != (video_register_device(&(peasycap->video_device), - VFL_TYPE_GRABBER, -1))) { - err("Not able to register with videodev"); - videodev_release(&(peasycap->video_device)); + rc = easycap_register_video(peasycap); + if (rc < 0) return -ENODEV; - } - - peasycap->registered_video++; - SAM("registered with videodev: %i=minor\n", - peasycap->video_device.minor); - peasycap->minor = peasycap->video_device.minor; - break; } /* 1: Audio control */ -- cgit v1.2.3 From de6ffc5e5a4ce0c4f078eaa2189bc906e8963d11 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 24 Feb 2012 11:24:19 -0300 Subject: [media] staging: easycap: Split audio buffer and urb allocation When the device is probed, this driver allocates audio buffers, and audio urbs. This patch just split this into separate functions, which helps clearing the currently gigantic probe function. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/easycap/easycap_main.c | 229 +++++++++++++++------------ 1 file changed, 124 insertions(+), 105 deletions(-) diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index 68af1a2297be..6e1734d8d276 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -3245,6 +3245,123 @@ static int create_video_urbs(struct easycap *peasycap) return 0; } +static int alloc_audio_buffers(struct easycap *peasycap) +{ + void *pbuf; + int k; + + JOM(4, "allocating %i isoc audio buffers of size %i\n", + AUDIO_ISOC_BUFFER_MANY, + peasycap->audio_isoc_buffer_size); + JOM(4, ".... each occupying contiguous memory pages\n"); + + for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { + pbuf = (void *)__get_free_pages(GFP_KERNEL, AUDIO_ISOC_ORDER); + if (!pbuf) { + SAM("ERROR: Could not allocate isoc audio buffer %i\n", + k); + return -ENOMEM; + } + peasycap->allocation_audio_page += BIT(AUDIO_ISOC_ORDER); + + peasycap->audio_isoc_buffer[k].pgo = pbuf; + peasycap->audio_isoc_buffer[k].pto = + pbuf + peasycap->audio_isoc_buffer_size; + peasycap->audio_isoc_buffer[k].kount = k; + } + + JOM(4, "allocation of isoc audio buffers done.\n"); + return 0; +} + +static int create_audio_urbs(struct easycap *peasycap) +{ + struct urb *purb; + struct data_urb *pdata_urb; + int k, j; + + JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY); + JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n", + peasycap->audio_isoc_framesperdesc); + JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n", + peasycap->audio_isoc_maxframesize); + JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n", + peasycap->audio_isoc_buffer_size); + + for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { + purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc, + GFP_KERNEL); + if (!purb) { + SAM("ERROR: usb_alloc_urb returned NULL for buffer " + "%i\n", k); + return -ENOMEM; + } + peasycap->allocation_audio_urb += 1 ; + pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); + if (!pdata_urb) { + usb_free_urb(purb); + SAM("ERROR: Could not allocate struct data_urb.\n"); + return -ENOMEM; + } + peasycap->allocation_audio_struct += + sizeof(struct data_urb); + + pdata_urb->purb = purb; + pdata_urb->isbuf = k; + pdata_urb->length = 0; + list_add_tail(&(pdata_urb->list_head), + peasycap->purb_audio_head); + + if (!k) { + JOM(4, "initializing audio urbs thus:\n"); + JOM(4, " purb->interval = 1;\n"); + JOM(4, " purb->dev = peasycap->pusb_device;\n"); + JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->" + "pusb_device,%i);\n", + peasycap->audio_endpointnumber); + JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); + JOM(4, " purb->transfer_buffer = " + "peasycap->audio_isoc_buffer[.].pgo;\n"); + JOM(4, " purb->transfer_buffer_length = %i;\n", + peasycap->audio_isoc_buffer_size); + JOM(4, " purb->complete = easycap_alsa_complete;\n"); + JOM(4, " purb->context = peasycap;\n"); + JOM(4, " purb->start_frame = 0;\n"); + JOM(4, " purb->number_of_packets = %i;\n", + peasycap->audio_isoc_framesperdesc); + JOM(4, " for (j = 0; j < %i; j++)\n", + peasycap->audio_isoc_framesperdesc); + JOM(4, " {\n"); + JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", + peasycap->audio_isoc_maxframesize); + JOM(4, " purb->iso_frame_desc[j].length = %i;\n", + peasycap->audio_isoc_maxframesize); + JOM(4, " }\n"); + } + + purb->interval = 1; + purb->dev = peasycap->pusb_device; + purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, + peasycap->audio_endpointnumber); + purb->transfer_flags = URB_ISO_ASAP; + purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo; + purb->transfer_buffer_length = + peasycap->audio_isoc_buffer_size; + purb->complete = easycap_alsa_complete; + purb->context = peasycap; + purb->start_frame = 0; + purb->number_of_packets = peasycap->audio_isoc_framesperdesc; + for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { + purb->iso_frame_desc[j].offset = + j * peasycap->audio_isoc_maxframesize; + purb->iso_frame_desc[j].length = + peasycap->audio_isoc_maxframesize; + } + } + JOM(4, "allocation of %i struct urb done.\n", k); + return 0; +} + static void config_easycap(struct easycap *peasycap, u8 bInterfaceNumber, u8 bInterfaceClass, @@ -3333,14 +3450,11 @@ static int easycap_usb_probe(struct usb_interface *intf, struct usb_host_interface *alt; struct usb_endpoint_descriptor *ep; struct usb_interface_descriptor *interface; - struct urb *purb; struct easycap *peasycap; - struct data_urb *pdata_urb; - int i, j, k, rc; + int i, j, rc; u8 bInterfaceNumber; u8 bInterfaceClass; u8 bInterfaceSubClass; - void *pbuf; int okalt[8], isokalt; int okepn[8]; int okmps[8]; @@ -3823,109 +3937,14 @@ static int easycap_usb_probe(struct usb_interface *intf, INIT_LIST_HEAD(&(peasycap->urb_audio_head)); peasycap->purb_audio_head = &(peasycap->urb_audio_head); - JOM(4, "allocating %i isoc audio buffers of size %i\n", - AUDIO_ISOC_BUFFER_MANY, - peasycap->audio_isoc_buffer_size); - JOM(4, ".... each occupying contiguous memory pages\n"); - - for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { - pbuf = (void *)__get_free_pages(GFP_KERNEL, - AUDIO_ISOC_ORDER); - if (!pbuf) { - SAM("ERROR: Could not allocate isoc audio buffer " - "%i\n", k); - return -ENOMEM; - } - peasycap->allocation_audio_page += - BIT(AUDIO_ISOC_ORDER); - - peasycap->audio_isoc_buffer[k].pgo = pbuf; - peasycap->audio_isoc_buffer[k].pto = pbuf + - peasycap->audio_isoc_buffer_size; - peasycap->audio_isoc_buffer[k].kount = k; - } - JOM(4, "allocation of isoc audio buffers done.\n"); + alloc_audio_buffers(peasycap); + if (rc < 0) + return rc; /* Allocate and initialize urbs */ - JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY); - JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n", - peasycap->audio_isoc_framesperdesc); - JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n", - peasycap->audio_isoc_maxframesize); - JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n", - peasycap->audio_isoc_buffer_size); - - for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { - purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc, - GFP_KERNEL); - if (!purb) { - SAM("ERROR: usb_alloc_urb returned NULL for buffer " - "%i\n", k); - return -ENOMEM; - } - peasycap->allocation_audio_urb += 1 ; - pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); - if (!pdata_urb) { - usb_free_urb(purb); - SAM("ERROR: Could not allocate struct data_urb.\n"); - return -ENOMEM; - } - peasycap->allocation_audio_struct += - sizeof(struct data_urb); - - pdata_urb->purb = purb; - pdata_urb->isbuf = k; - pdata_urb->length = 0; - list_add_tail(&(pdata_urb->list_head), - peasycap->purb_audio_head); - - if (!k) { - JOM(4, "initializing audio urbs thus:\n"); - JOM(4, " purb->interval = 1;\n"); - JOM(4, " purb->dev = peasycap->pusb_device;\n"); - JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->" - "pusb_device,%i);\n", - peasycap->audio_endpointnumber); - JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); - JOM(4, " purb->transfer_buffer = " - "peasycap->audio_isoc_buffer[.].pgo;\n"); - JOM(4, " purb->transfer_buffer_length = %i;\n", - peasycap->audio_isoc_buffer_size); - JOM(4, " purb->complete = easycap_alsa_complete;\n"); - JOM(4, " purb->context = peasycap;\n"); - JOM(4, " purb->start_frame = 0;\n"); - JOM(4, " purb->number_of_packets = %i;\n", - peasycap->audio_isoc_framesperdesc); - JOM(4, " for (j = 0; j < %i; j++)\n", - peasycap->audio_isoc_framesperdesc); - JOM(4, " {\n"); - JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", - peasycap->audio_isoc_maxframesize); - JOM(4, " purb->iso_frame_desc[j].length = %i;\n", - peasycap->audio_isoc_maxframesize); - JOM(4, " }\n"); - } - - purb->interval = 1; - purb->dev = peasycap->pusb_device; - purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, - peasycap->audio_endpointnumber); - purb->transfer_flags = URB_ISO_ASAP; - purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo; - purb->transfer_buffer_length = - peasycap->audio_isoc_buffer_size; - purb->complete = easycap_alsa_complete; - purb->context = peasycap; - purb->start_frame = 0; - purb->number_of_packets = peasycap->audio_isoc_framesperdesc; - for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { - purb->iso_frame_desc[j].offset = j * - peasycap->audio_isoc_maxframesize; - purb->iso_frame_desc[j].length = - peasycap->audio_isoc_maxframesize; - } - } - JOM(4, "allocation of %i struct urb done.\n", k); + rc = create_audio_urbs(peasycap); + if (rc < 0) + return rc; /* Save pointer peasycap in this interface */ usb_set_intfdata(intf, peasycap); -- cgit v1.2.3 From cf32b65d0589cdbe9bc4cc7081f13ebbae11308c Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 24 Feb 2012 11:24:20 -0300 Subject: [media] staging: easycap: Clean comment style in easycap_usb_disconnect() Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/easycap/easycap_main.c | 64 +++++++++++----------------- 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index 6e1734d8d276..de53ed825f2c 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -3973,15 +3973,13 @@ static int easycap_usb_probe(struct usb_interface *intf, SAM("ends successfully for interface %i\n", bInterfaceNumber); return 0; } -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ + /* - * WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY - * UNPLUGGED. HENCE peasycap->pusb_device IS NO LONGER VALID. - * - * THIS FUNCTION AFFECTS ALSA. BEWARE. + * When this function is called the device has already been + * physically unplugged. + * Hence, peasycap->pusb_device is no longer valid. + * This function affects alsa. */ -/*---------------------------------------------------------------------------*/ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) { struct usb_host_interface *pusb_host_interface; @@ -4006,6 +4004,7 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) minor = pusb_interface->minor; JOT(4, "intf[%i]: minor=%i\n", bInterfaceNumber, minor); + /* There is nothing to do for Interface Number 1 */ if (1 == bInterfaceNumber) return; @@ -4014,11 +4013,8 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) SAY("ERROR: peasycap is NULL\n"); return; } -/*---------------------------------------------------------------------------*/ -/* - * IF THE WAIT QUEUES ARE NOT CLEARED A DEADLOCK IS POSSIBLE. BEWARE. -*/ -/*---------------------------------------------------------------------------*/ + + /* If the waitqueues are not cleared a deadlock is possible */ peasycap->video_eof = 1; peasycap->audio_eof = 1; wake_up_interruptible(&(peasycap->wq_video)); @@ -4034,15 +4030,14 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) default: break; } -/*--------------------------------------------------------------------------*/ -/* - * DEREGISTER - * - * THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO - * IOCTL ARE ALL UNLOCKED. IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN - * AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING. BEWARE. - */ -/*--------------------------------------------------------------------------*/ + + /* + * Deregister + * This procedure will block until easycap_poll(), + * video and audio ioctl are all unlocked. + * If this is not done an oops can occur when an easycap + * is unplugged while the urbs are running. + */ kd = easycap_isdongle(peasycap); switch (bInterfaceNumber) { case 0: { @@ -4059,7 +4054,6 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) } else { SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd); } -/*---------------------------------------------------------------------------*/ if (!peasycap->v4l2_device.name[0]) { SAM("ERROR: peasycap->v4l2_device.name is empty\n"); if (0 <= kd && DONGLE_MANY > kd) @@ -4075,7 +4069,6 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) JOM(4, "intf[%i]: video_unregister_device() minor=%i\n", bInterfaceNumber, minor); peasycap->registered_video--; -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ if (0 <= kd && DONGLE_MANY > kd) { mutex_unlock(&easycapdc60_dongle[kd].mutex_video); @@ -4111,12 +4104,12 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) default: break; } -/*---------------------------------------------------------------------------*/ -/* - * CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap - * (ALSO WHEN ALSA HAS BEEN IN USE) - */ -/*---------------------------------------------------------------------------*/ + + /* + * If no remaining references to peasycap, + * call easycap_delete. + * (Also when alsa has been in use) + */ if (!peasycap->kref.refcount.counter) { SAM("ERROR: peasycap->kref.refcount.counter is zero " "so cannot call kref_put()\n"); @@ -4151,17 +4144,11 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) mutex_unlock(&easycapdc60_dongle[kd].mutex_video); JOT(4, "unlocked dongle[%i].mutex_video\n", kd); } -/*---------------------------------------------------------------------------*/ JOM(4, "ends\n"); return; } -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * PARAMETERS APPLICABLE TO ENTIRE DRIVER, I.E. BOTH VIDEO AND AUDIO - */ -/*---------------------------------------------------------------------------*/ +/* Devices supported by this driver */ static struct usb_device_id easycap_usb_device_id_table[] = { {USB_DEVICE(USB_EASYCAP_VENDOR_ID, USB_EASYCAP_PRODUCT_ID)}, { } @@ -4196,14 +4183,11 @@ static int __init easycap_module_init(void) return rc; } -/*****************************************************************************/ + static void __exit easycap_module_exit(void) { usb_deregister(&easycap_usb_driver); } -/*****************************************************************************/ module_init(easycap_module_init); module_exit(easycap_module_exit); - -/*****************************************************************************/ -- cgit v1.2.3 From df8c4b72322ade883766a9b9311d42ae8a83cf66 Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 24 Feb 2012 11:24:21 -0300 Subject: [media] staging: easycap: Clean comment style in easycap_delete() Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/easycap/easycap_main.c | 43 +++++++++++----------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index de53ed825f2c..76a2c5b33a91 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -700,17 +700,13 @@ static int videodev_release(struct video_device *pvideo_device) JOM(4, "ending successfully\n"); return 0; } -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*****************************************************************************/ -/*--------------------------------------------------------------------------*/ + /* - * THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS - * PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect(). - * - * BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO - * peasycap->pusb_device IS NO LONGER VALID. + * This function is called from within easycap_usb_disconnect() and is + * protected by semaphores set and cleared by easycap_usb_disconnect(). + * By this stage the device has already been physically unplugged, + * so peasycap->pusb_device is no longer valid. */ -/*---------------------------------------------------------------------------*/ static void easycap_delete(struct kref *pkref) { struct easycap *peasycap; @@ -731,11 +727,8 @@ static void easycap_delete(struct kref *pkref) return; } kd = easycap_isdongle(peasycap); -/*---------------------------------------------------------------------------*/ -/* - * FREE VIDEO. - */ -/*---------------------------------------------------------------------------*/ + + /* Free video urbs */ if (peasycap->purb_video_head) { m = 0; list_for_each(plist_head, peasycap->purb_video_head) { @@ -750,7 +743,6 @@ static void easycap_delete(struct kref *pkref) } JOM(4, "%i video urbs freed\n", m); -/*---------------------------------------------------------------------------*/ JOM(4, "freeing video data_urb structures.\n"); m = 0; list_for_each_safe(plist_head, plist_next, @@ -768,7 +760,8 @@ static void easycap_delete(struct kref *pkref) JOM(4, "setting peasycap->purb_video_head=NULL\n"); peasycap->purb_video_head = NULL; } -/*---------------------------------------------------------------------------*/ + + /* Free video isoc buffers */ JOM(4, "freeing video isoc buffers.\n"); m = 0; for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { @@ -784,7 +777,7 @@ static void easycap_delete(struct kref *pkref) } JOM(4, "isoc video buffers freed: %i pages\n", m * (0x01 << VIDEO_ISOC_ORDER)); -/*---------------------------------------------------------------------------*/ + /* Free video field buffers */ JOM(4, "freeing video field buffers.\n"); gone = 0; for (k = 0; k < FIELD_BUFFER_MANY; k++) { @@ -799,7 +792,8 @@ static void easycap_delete(struct kref *pkref) } } JOM(4, "video field buffers freed: %i pages\n", gone); -/*---------------------------------------------------------------------------*/ + + /* Free video frame buffers */ JOM(4, "freeing video frame buffers.\n"); gone = 0; for (k = 0; k < FRAME_BUFFER_MANY; k++) { @@ -814,11 +808,8 @@ static void easycap_delete(struct kref *pkref) } } JOM(4, "video frame buffers freed: %i pages\n", gone); -/*---------------------------------------------------------------------------*/ -/* - * FREE AUDIO. - */ -/*---------------------------------------------------------------------------*/ + + /* Free audio urbs */ if (peasycap->purb_audio_head) { JOM(4, "freeing audio urbs\n"); m = 0; @@ -833,7 +824,6 @@ static void easycap_delete(struct kref *pkref) } } JOM(4, "%i audio urbs freed\n", m); -/*---------------------------------------------------------------------------*/ JOM(4, "freeing audio data_urb structures.\n"); m = 0; list_for_each_safe(plist_head, plist_next, @@ -851,7 +841,8 @@ static void easycap_delete(struct kref *pkref) JOM(4, "setting peasycap->purb_audio_head=NULL\n"); peasycap->purb_audio_head = NULL; } -/*---------------------------------------------------------------------------*/ + + /* Free audio isoc buffers */ JOM(4, "freeing audio isoc buffers.\n"); m = 0; for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { @@ -867,7 +858,6 @@ static void easycap_delete(struct kref *pkref) } JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n", m * (0x01 << AUDIO_ISOC_ORDER)); -/*---------------------------------------------------------------------------*/ JOM(4, "freeing easycap structure.\n"); allocation_video_urb = peasycap->allocation_video_urb; allocation_video_page = peasycap->allocation_video_page; @@ -895,7 +885,6 @@ static void easycap_delete(struct kref *pkref) kfree(peasycap); -/*---------------------------------------------------------------------------*/ SAY("%8i=video urbs after all deletions\n", allocation_video_urb); SAY("%8i=video pages after all deletions\n", allocation_video_page); SAY("%8i=video structs after all deletions\n", allocation_video_struct); -- cgit v1.2.3 From 92a0144225fa14e044bdbdaa23e1049ca2047a5c Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Fri, 24 Feb 2012 11:24:22 -0300 Subject: [media] staging: easycap: Split easycap_delete() into several pieces The patch splits easycap_delete(), which is in charge of buffer deallocation, into smaller functions each deallocating a specific kind of buffer. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/easycap/easycap_main.c | 445 +++++++++++++++------------ 1 file changed, 249 insertions(+), 196 deletions(-) diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index 76a2c5b33a91..aed953751a90 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -701,202 +701,6 @@ static int videodev_release(struct video_device *pvideo_device) return 0; } -/* - * This function is called from within easycap_usb_disconnect() and is - * protected by semaphores set and cleared by easycap_usb_disconnect(). - * By this stage the device has already been physically unplugged, - * so peasycap->pusb_device is no longer valid. - */ -static void easycap_delete(struct kref *pkref) -{ - struct easycap *peasycap; - struct data_urb *pdata_urb; - struct list_head *plist_head, *plist_next; - int k, m, gone, kd; - int allocation_video_urb; - int allocation_video_page; - int allocation_video_struct; - int allocation_audio_urb; - int allocation_audio_page; - int allocation_audio_struct; - int registered_video, registered_audio; - - peasycap = container_of(pkref, struct easycap, kref); - if (!peasycap) { - SAM("ERROR: peasycap is NULL: cannot perform deletions\n"); - return; - } - kd = easycap_isdongle(peasycap); - - /* Free video urbs */ - if (peasycap->purb_video_head) { - m = 0; - list_for_each(plist_head, peasycap->purb_video_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb && pdata_urb->purb) { - usb_free_urb(pdata_urb->purb); - pdata_urb->purb = NULL; - peasycap->allocation_video_urb--; - m++; - } - } - - JOM(4, "%i video urbs freed\n", m); - JOM(4, "freeing video data_urb structures.\n"); - m = 0; - list_for_each_safe(plist_head, plist_next, - peasycap->purb_video_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb) { - peasycap->allocation_video_struct -= - sizeof(struct data_urb); - kfree(pdata_urb); - m++; - } - } - JOM(4, "%i video data_urb structures freed\n", m); - JOM(4, "setting peasycap->purb_video_head=NULL\n"); - peasycap->purb_video_head = NULL; - } - - /* Free video isoc buffers */ - JOM(4, "freeing video isoc buffers.\n"); - m = 0; - for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { - if (peasycap->video_isoc_buffer[k].pgo) { - free_pages((unsigned long) - peasycap->video_isoc_buffer[k].pgo, - VIDEO_ISOC_ORDER); - peasycap->video_isoc_buffer[k].pgo = NULL; - peasycap->allocation_video_page -= - BIT(VIDEO_ISOC_ORDER); - m++; - } - } - JOM(4, "isoc video buffers freed: %i pages\n", - m * (0x01 << VIDEO_ISOC_ORDER)); - /* Free video field buffers */ - JOM(4, "freeing video field buffers.\n"); - gone = 0; - for (k = 0; k < FIELD_BUFFER_MANY; k++) { - for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->field_buffer[k][m].pgo) { - free_page((unsigned long) - peasycap->field_buffer[k][m].pgo); - peasycap->field_buffer[k][m].pgo = NULL; - peasycap->allocation_video_page -= 1; - gone++; - } - } - } - JOM(4, "video field buffers freed: %i pages\n", gone); - - /* Free video frame buffers */ - JOM(4, "freeing video frame buffers.\n"); - gone = 0; - for (k = 0; k < FRAME_BUFFER_MANY; k++) { - for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->frame_buffer[k][m].pgo) { - free_page((unsigned long) - peasycap->frame_buffer[k][m].pgo); - peasycap->frame_buffer[k][m].pgo = NULL; - peasycap->allocation_video_page -= 1; - gone++; - } - } - } - JOM(4, "video frame buffers freed: %i pages\n", gone); - - /* Free audio urbs */ - if (peasycap->purb_audio_head) { - JOM(4, "freeing audio urbs\n"); - m = 0; - list_for_each(plist_head, (peasycap->purb_audio_head)) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb && pdata_urb->purb) { - usb_free_urb(pdata_urb->purb); - pdata_urb->purb = NULL; - peasycap->allocation_audio_urb--; - m++; - } - } - JOM(4, "%i audio urbs freed\n", m); - JOM(4, "freeing audio data_urb structures.\n"); - m = 0; - list_for_each_safe(plist_head, plist_next, - peasycap->purb_audio_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb) { - peasycap->allocation_audio_struct -= - sizeof(struct data_urb); - kfree(pdata_urb); - m++; - } - } - JOM(4, "%i audio data_urb structures freed\n", m); - JOM(4, "setting peasycap->purb_audio_head=NULL\n"); - peasycap->purb_audio_head = NULL; - } - - /* Free audio isoc buffers */ - JOM(4, "freeing audio isoc buffers.\n"); - m = 0; - for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { - if (peasycap->audio_isoc_buffer[k].pgo) { - free_pages((unsigned long) - (peasycap->audio_isoc_buffer[k].pgo), - AUDIO_ISOC_ORDER); - peasycap->audio_isoc_buffer[k].pgo = NULL; - peasycap->allocation_audio_page -= - BIT(AUDIO_ISOC_ORDER); - m++; - } - } - JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n", - m * (0x01 << AUDIO_ISOC_ORDER)); - JOM(4, "freeing easycap structure.\n"); - allocation_video_urb = peasycap->allocation_video_urb; - allocation_video_page = peasycap->allocation_video_page; - allocation_video_struct = peasycap->allocation_video_struct; - registered_video = peasycap->registered_video; - allocation_audio_urb = peasycap->allocation_audio_urb; - allocation_audio_page = peasycap->allocation_audio_page; - allocation_audio_struct = peasycap->allocation_audio_struct; - registered_audio = peasycap->registered_audio; - - if (0 <= kd && DONGLE_MANY > kd) { - if (mutex_lock_interruptible(&mutex_dongle)) { - SAY("ERROR: cannot down mutex_dongle\n"); - } else { - JOM(4, "locked mutex_dongle\n"); - easycapdc60_dongle[kd].peasycap = NULL; - mutex_unlock(&mutex_dongle); - JOM(4, "unlocked mutex_dongle\n"); - JOT(4, " null-->dongle[%i].peasycap\n", kd); - allocation_video_struct -= sizeof(struct easycap); - } - } else { - SAY("ERROR: cannot purge dongle[].peasycap"); - } - - kfree(peasycap); - - SAY("%8i=video urbs after all deletions\n", allocation_video_urb); - SAY("%8i=video pages after all deletions\n", allocation_video_page); - SAY("%8i=video structs after all deletions\n", allocation_video_struct); - SAY("%8i=video devices after all deletions\n", registered_video); - SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb); - SAY("%8i=audio pages after all deletions\n", allocation_audio_page); - SAY("%8i=audio structs after all deletions\n", allocation_audio_struct); - SAY("%8i=audio devices after all deletions\n", registered_audio); - - JOT(4, "ending.\n"); - return; -} /*****************************************************************************/ static unsigned int easycap_poll(struct file *file, poll_table *wait) { @@ -2875,6 +2679,56 @@ static struct easycap *alloc_easycap(u8 bInterfaceNumber) return peasycap; } +static void free_easycap(struct easycap *peasycap) +{ + int allocation_video_urb; + int allocation_video_page; + int allocation_video_struct; + int allocation_audio_urb; + int allocation_audio_page; + int allocation_audio_struct; + int registered_video, registered_audio; + int kd; + + JOM(4, "freeing easycap structure.\n"); + allocation_video_urb = peasycap->allocation_video_urb; + allocation_video_page = peasycap->allocation_video_page; + allocation_video_struct = peasycap->allocation_video_struct; + registered_video = peasycap->registered_video; + allocation_audio_urb = peasycap->allocation_audio_urb; + allocation_audio_page = peasycap->allocation_audio_page; + allocation_audio_struct = peasycap->allocation_audio_struct; + registered_audio = peasycap->registered_audio; + + kd = easycap_isdongle(peasycap); + if (0 <= kd && DONGLE_MANY > kd) { + if (mutex_lock_interruptible(&mutex_dongle)) { + SAY("ERROR: cannot down mutex_dongle\n"); + } else { + JOM(4, "locked mutex_dongle\n"); + easycapdc60_dongle[kd].peasycap = NULL; + mutex_unlock(&mutex_dongle); + JOM(4, "unlocked mutex_dongle\n"); + JOT(4, " null-->dongle[%i].peasycap\n", kd); + allocation_video_struct -= sizeof(struct easycap); + } + } else { + SAY("ERROR: cannot purge dongle[].peasycap"); + } + + /* Free device structure */ + kfree(peasycap); + + SAY("%8i=video urbs after all deletions\n", allocation_video_urb); + SAY("%8i=video pages after all deletions\n", allocation_video_page); + SAY("%8i=video structs after all deletions\n", allocation_video_struct); + SAY("%8i=video devices after all deletions\n", registered_video); + SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb); + SAY("%8i=audio pages after all deletions\n", allocation_audio_page); + SAY("%8i=audio structs after all deletions\n", allocation_audio_struct); + SAY("%8i=audio devices after all deletions\n", registered_audio); +} + /* * FIXME: Identify the appropriate pointer peasycap for interfaces * 1 and 2. The address of peasycap->pusb_device is reluctantly used @@ -3073,6 +2927,26 @@ static int alloc_framebuffers(struct easycap *peasycap) return 0; } +static void free_framebuffers(struct easycap *peasycap) +{ + int k, m, gone; + + JOM(4, "freeing video frame buffers.\n"); + gone = 0; + for (k = 0; k < FRAME_BUFFER_MANY; k++) { + for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { + if (peasycap->frame_buffer[k][m].pgo) { + free_page((unsigned long) + peasycap->frame_buffer[k][m].pgo); + peasycap->frame_buffer[k][m].pgo = NULL; + peasycap->allocation_video_page -= 1; + gone++; + } + } + } + JOM(4, "video frame buffers freed: %i pages\n", gone); +} + static int alloc_fieldbuffers(struct easycap *peasycap) { int i, j; @@ -3112,6 +2986,26 @@ static int alloc_fieldbuffers(struct easycap *peasycap) return 0; } +static void free_fieldbuffers(struct easycap *peasycap) +{ + int k, m, gone; + + JOM(4, "freeing video field buffers.\n"); + gone = 0; + for (k = 0; k < FIELD_BUFFER_MANY; k++) { + for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { + if (peasycap->field_buffer[k][m].pgo) { + free_page((unsigned long) + peasycap->field_buffer[k][m].pgo); + peasycap->field_buffer[k][m].pgo = NULL; + peasycap->allocation_video_page -= 1; + gone++; + } + } + } + JOM(4, "video field buffers freed: %i pages\n", gone); +} + static int alloc_isocbuffers(struct easycap *peasycap) { int i; @@ -3142,6 +3036,27 @@ static int alloc_isocbuffers(struct easycap *peasycap) return 0; } +static void free_isocbuffers(struct easycap *peasycap) +{ + int k, m; + + JOM(4, "freeing video isoc buffers.\n"); + m = 0; + for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { + if (peasycap->video_isoc_buffer[k].pgo) { + free_pages((unsigned long) + peasycap->video_isoc_buffer[k].pgo, + VIDEO_ISOC_ORDER); + peasycap->video_isoc_buffer[k].pgo = NULL; + peasycap->allocation_video_page -= + BIT(VIDEO_ISOC_ORDER); + m++; + } + } + JOM(4, "isoc video buffers freed: %i pages\n", + m * (0x01 << VIDEO_ISOC_ORDER)); +} + static int create_video_urbs(struct easycap *peasycap) { struct urb *purb; @@ -3234,6 +3149,45 @@ static int create_video_urbs(struct easycap *peasycap) return 0; } +static void free_video_urbs(struct easycap *peasycap) +{ + struct list_head *plist_head, *plist_next; + struct data_urb *pdata_urb; + int m; + + if (peasycap->purb_video_head) { + m = 0; + list_for_each(plist_head, peasycap->purb_video_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) { + usb_free_urb(pdata_urb->purb); + pdata_urb->purb = NULL; + peasycap->allocation_video_urb--; + m++; + } + } + + JOM(4, "%i video urbs freed\n", m); + JOM(4, "freeing video data_urb structures.\n"); + m = 0; + list_for_each_safe(plist_head, plist_next, + peasycap->purb_video_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb) { + peasycap->allocation_video_struct -= + sizeof(struct data_urb); + kfree(pdata_urb); + m++; + } + } + JOM(4, "%i video data_urb structures freed\n", m); + JOM(4, "setting peasycap->purb_video_head=NULL\n"); + peasycap->purb_video_head = NULL; + } +} + static int alloc_audio_buffers(struct easycap *peasycap) { void *pbuf; @@ -3263,6 +3217,27 @@ static int alloc_audio_buffers(struct easycap *peasycap) return 0; } +static void free_audio_buffers(struct easycap *peasycap) +{ + int k, m; + + JOM(4, "freeing audio isoc buffers.\n"); + m = 0; + for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { + if (peasycap->audio_isoc_buffer[k].pgo) { + free_pages((unsigned long) + (peasycap->audio_isoc_buffer[k].pgo), + AUDIO_ISOC_ORDER); + peasycap->audio_isoc_buffer[k].pgo = NULL; + peasycap->allocation_audio_page -= + BIT(AUDIO_ISOC_ORDER); + m++; + } + } + JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n", + m * (0x01 << AUDIO_ISOC_ORDER)); +} + static int create_audio_urbs(struct easycap *peasycap) { struct urb *purb; @@ -3351,6 +3326,45 @@ static int create_audio_urbs(struct easycap *peasycap) return 0; } +static void free_audio_urbs(struct easycap *peasycap) +{ + struct list_head *plist_head, *plist_next; + struct data_urb *pdata_urb; + int m; + + if (peasycap->purb_audio_head) { + JOM(4, "freeing audio urbs\n"); + m = 0; + list_for_each(plist_head, (peasycap->purb_audio_head)) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) { + usb_free_urb(pdata_urb->purb); + pdata_urb->purb = NULL; + peasycap->allocation_audio_urb--; + m++; + } + } + JOM(4, "%i audio urbs freed\n", m); + JOM(4, "freeing audio data_urb structures.\n"); + m = 0; + list_for_each_safe(plist_head, plist_next, + peasycap->purb_audio_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb) { + peasycap->allocation_audio_struct -= + sizeof(struct data_urb); + kfree(pdata_urb); + m++; + } + } + JOM(4, "%i audio data_urb structures freed\n", m); + JOM(4, "setting peasycap->purb_audio_head=NULL\n"); + peasycap->purb_audio_head = NULL; + } +} + static void config_easycap(struct easycap *peasycap, u8 bInterfaceNumber, u8 bInterfaceClass, @@ -3389,6 +3403,45 @@ static void config_easycap(struct easycap *peasycap, } } +/* + * This function is called from within easycap_usb_disconnect() and is + * protected by semaphores set and cleared by easycap_usb_disconnect(). + * By this stage the device has already been physically unplugged, + * so peasycap->pusb_device is no longer valid. + */ +static void easycap_delete(struct kref *pkref) +{ + struct easycap *peasycap; + + peasycap = container_of(pkref, struct easycap, kref); + if (!peasycap) { + SAM("ERROR: peasycap is NULL: cannot perform deletions\n"); + return; + } + + /* Free video urbs */ + free_video_urbs(peasycap); + + /* Free video isoc buffers */ + free_isocbuffers(peasycap); + + /* Free video field buffers */ + free_fieldbuffers(peasycap); + + /* Free video frame buffers */ + free_framebuffers(peasycap); + + /* Free audio urbs */ + free_audio_urbs(peasycap); + + /* Free audio isoc buffers */ + free_audio_buffers(peasycap); + + free_easycap(peasycap); + + JOT(4, "ending.\n"); +} + static const struct v4l2_file_operations v4l2_fops = { .owner = THIS_MODULE, .open = easycap_open_noinode, -- cgit v1.2.3 From 626f95a3e1ee1a227bf447d60f98166b87ea7f8b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 15 May 2012 09:13:28 -0300 Subject: [media] saa7134: remove unused log_err() macro As reported by Masanari, this macro is using "KERN_ERR" instead of "KERN_ERROR". That would lead into a compilation breakage, if this macro were used somewhere inside the driver. Instead of fixing the macro, as originally proposed, let's just remove the dead code. Reported-by: Masanari Iida Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7164/saa7164.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h index 742b34103b5d..8d120e3baf70 100644 --- a/drivers/media/video/saa7164/saa7164.h +++ b/drivers/media/video/saa7164/saa7164.h @@ -611,11 +611,6 @@ extern unsigned int saa_debug; printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\ } while (0) -#define log_err(fmt, arg...)\ - do { \ - printk(KERN_ERROR "%s: " fmt, dev->name, ## arg);\ - } while (0) - #define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2)) #define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2)) -- cgit v1.2.3 From 38f0fe23dc3d3de08d2e187c42569356ba6b72bc Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Thu, 15 Mar 2012 13:33:48 -0300 Subject: [media] em28xx-dvb: enable LNA for cxd2820r in DVB-T mode Enable the LNA amplifier also for DVB-T (like for DVB-T2 and DVB-C); this greatly improves reception of weak signals without affecting the reception of the strong ones. Experimental data (collected with the mipsel STB) on the weakest frequencies available in my area: LNA OFF: MUX level BER picture RAI mux 4 72% 32000 corrupted TIMB 2 75% 14 OK TVA Vicenza 68% 32000 corrupted RAI mux 2 78% 14 OK LNA ON: MUX level BER picture RAI mux 4 73% 1500 OK TIMB 2 76% 0 OK TVA Vicenza 69% 0 OK RAI mux 2 79% 0 OK Moreover, with LNA enabled, the PCTV 290e was able to pick up 2 new frequencies matching the integrated tuner of my Panasonic G20 TV, which is really good. Signed-off-by: Gianluca Gennari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-dvb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 2d73ee2abf7e..ea3810fa5a15 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -542,7 +542,8 @@ static struct cxd2820r_config em28xx_cxd2820r_config = { .i2c_address = (0xd8 >> 1), .ts_mode = CXD2820R_TS_SERIAL, - /* enable LNA for DVB-T2 and DVB-C */ + /* enable LNA for DVB-T, DVB-T2 and DVB-C */ + .gpio_dvbt[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, }; -- cgit v1.2.3 From b115f400946dba529cfa9a769c2327ae6dbe3694 Mon Sep 17 00:00:00 2001 From: Gianluca Gennari Date: Thu, 15 Mar 2012 13:33:47 -0300 Subject: [media] cxd2820r: tweak search algorithm behavior MPIS based STBs running 3.x kernels and the Enigma2 OS are not able to tune DVB-T channels with the PCTV 290e using the current cxd2820r driver. DVB-T2 channels instead work properly. This patch fixes the problem by changing the condition to break out from the wait lock loop in the "search" function of the cxd2820r demodulator from FE_HAS_SIGNAL to FE_HAS_LOCK. As a consequence, the "search" function of the demodulator driver now returns DVBFE_ALGO_SEARCH_SUCCESS only if the frequency lock is successfully acquired. This behavior seems consistent with other demodulator drivers (e.g. stv090x, hd29l2, stv0900, stb0899, mb86a16). This patch has been successfully tested with DVB-T and DVB-T2 signals, on both PC and the mipsel STB running Enigma2. No apparent side effect has been observed on PC applications like Kaffeine. DVB-C is not available in my country so it's not tested. Signed-off-by: Gianluca Gennari Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/cxd2820r_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c index 5c7c2aaf9bf5..3bba37d74f57 100644 --- a/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/drivers/media/dvb/frontends/cxd2820r_core.c @@ -526,12 +526,12 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) if (ret) goto error; - if (status & FE_HAS_SIGNAL) + if (status & FE_HAS_LOCK) break; } /* check if we have a valid signal */ - if (status) { + if (status & FE_HAS_LOCK) { priv->last_tune_failed = 0; return DVBFE_ALGO_SEARCH_SUCCESS; } else { -- cgit v1.2.3 From 26a11eb176bfd66442dd47f8aac6b8c4252bd6da Mon Sep 17 00:00:00 2001 From: Santosh Nayak Date: Mon, 19 Mar 2012 07:27:37 -0300 Subject: [media] dib0700: Return -EINTR and unlock mutex if locking attempts fails In 'dib0700_i2c_xfer_new()' and 'dib0700_i2c_xfer_legacy()' we are taking two locks: 1. i2c_mutex 2. usb_mutex If attempt to take 'usb_mutex' lock fails then the previously taken lock 'i2c_mutex' should be unlocked and -EINTR should be returned so that caller can take appropriate action. If locking attempt was interrupted by a signal then we should return -EINTR. At present we are returning '0' for such scenarios which is wrong. Replace -EAGAIN by -EINTR as a return type for the the scenario where locking attempt was interrupted by signal. Signed-off-by: Santosh Nayak Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_core.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 02290c60f72f..7e9e00fae04e 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -32,7 +32,7 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), @@ -118,7 +118,7 @@ int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_ if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_GPIO; @@ -139,7 +139,7 @@ static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets) if (st->fw_version >= 0x10201) { if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_USB_XFER_LEN; @@ -178,7 +178,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, /* Ensure nobody else hits the i2c bus while we're sending our sequence of messages, (such as the remote control thread) */ if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; + return -EINTR; for (i = 0; i < num; i++) { if (i == 0) { @@ -228,7 +228,8 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, /* Write request */ if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + mutex_unlock(&d->i2c_mutex); + return -EINTR; } st->buf[0] = REQUEST_NEW_I2C_WRITE; st->buf[1] = msg[i].addr << 1; @@ -271,10 +272,11 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, int i,len; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; + return -EINTR; if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + mutex_unlock(&d->i2c_mutex); + return -EINTR; } for (i = 0; i < num; i++) { @@ -369,7 +371,7 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_CLOCK; @@ -401,7 +403,7 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz) if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_I2C_PARAM; @@ -561,7 +563,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_ENABLE_VIDEO; @@ -611,7 +613,7 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type) if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_RC; -- cgit v1.2.3 From 662f9602bdea183456c49260e3bd2edfad4be471 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 19 Mar 2012 11:48:18 -0300 Subject: [media] get_dvb_firmware: add dvb-demod-drxk-pctv.fw Firmware for the PCTV QuatroStick nano (520e). Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index d1d4a179a382..fbb241174486 100755 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -28,7 +28,8 @@ use IO::Handle; "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718", "af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395", "lme2510c_s7395_old", "drxk", "drxk_terratec_h5", - "drxk_hauppauge_hvr930c", "tda10071", "it9135", "it9137"); + "drxk_hauppauge_hvr930c", "tda10071", "it9135", "it9137", + "drxk_pctv"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -730,6 +731,23 @@ sub tda10071 { "$fwfile"; } +sub drxk_pctv { + my $sourcefile = "PCTV_460e_reference.zip"; + my $url = "ftp://ftp.pctvsystems.com/TV/driver/PCTV%2070e%2080e%20100e%20320e%20330e%20800e/"; + my $hash = "4403de903bf2593464c8d74bbc200a57"; + my $fwfile = "dvb-demod-drxk-pctv.fw"; + my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1); + + checkstandard(); + + wgetfile($sourcefile, $url . $sourcefile); + verify($sourcefile, $hash); + unzip($sourcefile, $tmpdir); + extract("$tmpdir/PCTV\ 70e\ 80e\ 100e\ 320e\ 330e\ 800e/32\ bit/emOEM.sys", 0x72b80, 42692, $fwfile); + + "$fwfile"; +} + # --------------------------------------------------------------- # Utilities -- cgit v1.2.3 From 943a903459db81a07c3af703605d4eb6fb64b523 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 19 Mar 2012 16:27:47 -0300 Subject: [media] rtl28xxu: dynamic USB ID support DVB USB core refuses to load driver when current USB ID does not match IDs on driver table. Due to that dynamic IDs does not work. Replace reference design ID by dynamic ID in .probe() in order to get it working. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/rtl28xxu.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c index 8f4736a10fc8..4e69e9db849e 100644 --- a/drivers/media/dvb/dvb-usb/rtl28xxu.c +++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c @@ -909,6 +909,8 @@ static int rtl28xxu_probe(struct usb_interface *intf, int ret, i; int properties_count = ARRAY_SIZE(rtl28xxu_properties); struct dvb_usb_device *d; + struct usb_device *udev; + bool found; deb_info("%s: interface=%d\n", __func__, intf->cur_altsetting->desc.bInterfaceNumber); @@ -916,6 +918,29 @@ static int rtl28xxu_probe(struct usb_interface *intf, if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return 0; + /* Dynamic USB ID support. Replaces first device ID with current one .*/ + udev = interface_to_usbdev(intf); + + for (i = 0, found = false; i < ARRAY_SIZE(rtl28xxu_table) - 1; i++) { + if (rtl28xxu_table[i].idVendor == + le16_to_cpu(udev->descriptor.idVendor) && + rtl28xxu_table[i].idProduct == + le16_to_cpu(udev->descriptor.idProduct)) { + found = true; + break; + } + } + + if (!found) { + deb_info("%s: using dynamic ID %04x:%04x\n", __func__, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + rtl28xxu_properties[0].devices[0].warm_ids[0]->idVendor = + le16_to_cpu(udev->descriptor.idVendor); + rtl28xxu_properties[0].devices[0].warm_ids[0]->idProduct = + le16_to_cpu(udev->descriptor.idProduct); + } + for (i = 0; i < properties_count; i++) { ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i], THIS_MODULE, &d, adapter_nr); -- cgit v1.2.3 From 1eaef48b42aae965950c884e3f03143cb44da71b Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Mon, 19 Mar 2012 16:48:34 -0300 Subject: [media] dib9000: get rid of Dib*Lock macros The patch replaces Dib*Lock macros with direct calls to mutex functions as soon as they just make the driver code harder to review (per request of Mauro). Signed-off-by: Alexey Khoroshilov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/dib9000.c | 128 +++++++++++++++------------------- 1 file changed, 58 insertions(+), 70 deletions(-) diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c index 0661da919b50..6201c59a78dd 100644 --- a/drivers/media/dvb/frontends/dib9000.c +++ b/drivers/media/dvb/frontends/dib9000.c @@ -31,13 +31,6 @@ struct i2c_device { u8 *i2c_write_buffer; }; -/* lock */ -#define DIB_LOCK struct mutex -#define DibAcquireLock(lock) mutex_lock_interruptible(lock) -#define DibReleaseLock(lock) mutex_unlock(lock) -#define DibInitLock(lock) mutex_init(lock) -#define DibFreeLock(lock) - struct dib9000_pid_ctrl { #define DIB9000_PID_FILTER_CTRL 0 #define DIB9000_PID_FILTER 1 @@ -82,11 +75,11 @@ struct dib9000_state { } fe_mm[18]; u8 memcmd; - DIB_LOCK mbx_if_lock; /* to protect read/write operations */ - DIB_LOCK mbx_lock; /* to protect the whole mailbox handling */ + struct mutex mbx_if_lock; /* to protect read/write operations */ + struct mutex mbx_lock; /* to protect the whole mailbox handling */ - DIB_LOCK mem_lock; /* to protect the memory accesses */ - DIB_LOCK mem_mbx_lock; /* to protect the memory-based mailbox */ + struct mutex mem_lock; /* to protect the memory accesses */ + struct mutex mem_mbx_lock; /* to protect the memory-based mailbox */ #define MBX_MAX_WORDS (256 - 200 - 2) #define DIB9000_MSG_CACHE_SIZE 2 @@ -108,7 +101,7 @@ struct dib9000_state { struct i2c_msg msg[2]; u8 i2c_write_buffer[255]; u8 i2c_read_buffer[255]; - DIB_LOCK demod_lock; + struct mutex demod_lock; u8 get_frontend_internal; struct dib9000_pid_ctrl pid_ctrl[10]; s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */ @@ -446,13 +439,13 @@ static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u1 if (!state->platform.risc.fw_is_running) return -EIO; - if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } dib9000_risc_mem_setup(state, cmd | 0x80); dib9000_risc_mem_read_chunks(state, b, len); - DibReleaseLock(&state->platform.risc.mem_lock); + mutex_unlock(&state->platform.risc.mem_lock); return 0; } @@ -462,13 +455,13 @@ static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 if (!state->platform.risc.fw_is_running) return -EIO; - if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } dib9000_risc_mem_setup(state, cmd); dib9000_risc_mem_write_chunks(state, b, m->size); - DibReleaseLock(&state->platform.risc.mem_lock); + mutex_unlock(&state->platform.risc.mem_lock); return 0; } @@ -537,7 +530,7 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, if (!state->platform.risc.fw_is_running) return -EINVAL; - if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -584,7 +577,7 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr); out: - DibReleaseLock(&state->platform.risc.mbx_if_lock); + mutex_unlock(&state->platform.risc.mbx_if_lock); return ret; } @@ -602,7 +595,7 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, if (!state->platform.risc.fw_is_running) return 0; - if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) { dprintk("could not get the lock"); return 0; } @@ -643,7 +636,7 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, /* Update register nb_mes_in_TX */ dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr); - DibReleaseLock(&state->platform.risc.mbx_if_lock); + mutex_unlock(&state->platform.risc.mbx_if_lock); return size + 1; } @@ -712,7 +705,7 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) if (!state->platform.risc.fw_is_running) return -1; - if (DibAcquireLock(&state->platform.risc.mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mbx_lock) < 0) { dprintk("could not get the lock"); return -1; } @@ -723,7 +716,7 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ /* if (tmp) */ /* dprintk( "cleared IRQ: %x", tmp); */ - DibReleaseLock(&state->platform.risc.mbx_lock); + mutex_unlock(&state->platform.risc.mbx_lock); return ret; } @@ -1192,7 +1185,7 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe) struct dibDVBTChannel *ch; int ret = 0; - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -1322,7 +1315,7 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe) } error: - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); return ret; } @@ -1677,7 +1670,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2 p[12] = 0; } - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); return 0; } @@ -1691,7 +1684,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2 /* do the transaction */ if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); return 0; } @@ -1699,7 +1692,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2 if ((num > 1) && (msg[1].flags & I2C_M_RD)) dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1].buf, msg[1].len); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); return num; } @@ -1788,7 +1781,7 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) return 0; } - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -1798,7 +1791,7 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) dprintk("PID filter enabled %d", onoff); ret = dib9000_write_word(state, 294 + 1, val); - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -1823,14 +1816,14 @@ int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) return 0; } - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); ret = dib9000_write_word(state, 300 + 1 + id, onoff ? (1 << 13) | pid : 0); - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } EXPORT_SYMBOL(dib9000_fw_pid_filter); @@ -1850,11 +1843,6 @@ static void dib9000_release(struct dvb_frontend *demod) for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) dvb_frontend_detach(st->fe[index_frontend]); - DibFreeLock(&state->platform.risc.mbx_if_lock); - DibFreeLock(&state->platform.risc.mbx_lock); - DibFreeLock(&state->platform.risc.mem_lock); - DibFreeLock(&state->platform.risc.mem_mbx_lock); - DibFreeLock(&state->demod_lock); dibx000_exit_i2c_master(&st->i2c_master); i2c_del_adapter(&st->tuner_adap); @@ -1874,7 +1862,7 @@ static int dib9000_sleep(struct dvb_frontend *fe) u8 index_frontend; int ret = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -1886,7 +1874,7 @@ static int dib9000_sleep(struct dvb_frontend *fe) ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0); error: - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -1904,7 +1892,7 @@ static int dib9000_get_frontend(struct dvb_frontend *fe) int ret = 0; if (state->get_frontend_internal == 0) { - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -1963,7 +1951,7 @@ static int dib9000_get_frontend(struct dvb_frontend *fe) return_value: if (state->get_frontend_internal == 0) - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -2011,7 +1999,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe) } state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */ - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return 0; } @@ -2080,7 +2068,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe) /* check the tune result */ if (exit_condition == 1) { /* tune failed */ dprintk("tune failed"); - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); /* tune failed; put all the pid filtering cmd to junk */ state->pid_ctrl_index = -1; return 0; @@ -2136,7 +2124,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe) /* turn off the diversity for the last frontend */ dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0); - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); if (state->pid_ctrl_index >= 0) { u8 index_pid_filter_cmd; u8 pid_ctrl_index = state->pid_ctrl_index; @@ -2174,7 +2162,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) u8 index_frontend; u16 lock = 0, lock_slave = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -2196,7 +2184,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) if ((lock & 0x0008) || (lock_slave & 0x0008)) *stat |= FE_HAS_LOCK; - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return 0; } @@ -2207,30 +2195,30 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber) u16 *c; int ret = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); ret = -EINTR; goto error; } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); ret = -EIO; goto error; } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, state->i2c_read_buffer, 16 * 2); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); c = (u16 *)state->i2c_read_buffer; *ber = c[10] << 16 | c[11]; error: - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -2242,7 +2230,7 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) u16 val; int ret = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -2255,18 +2243,18 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) *strength += val; } - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); ret = -EINTR; goto error; } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); ret = -EIO; goto error; } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); val = 65535 - c[4]; if (val > 65535 - *strength) @@ -2275,7 +2263,7 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) *strength += val; error: - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -2286,16 +2274,16 @@ static u32 dib9000_get_snr(struct dvb_frontend *fe) u32 n, s, exp; u16 val; - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); return 0; } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); return 0; } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); val = c[7]; n = (val >> 4) & 0xff; @@ -2325,7 +2313,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) u8 index_frontend; u32 snr_master; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -2339,7 +2327,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) } else *snr = 0; - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return 0; } @@ -2350,27 +2338,27 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) u16 *c = (u16 *)state->i2c_read_buffer; int ret = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); ret = -EINTR; goto error; } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); ret = -EIO; goto error; } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); *unc = c[12]; error: - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -2513,11 +2501,11 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES; st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS; - DibInitLock(&st->platform.risc.mbx_if_lock); - DibInitLock(&st->platform.risc.mbx_lock); - DibInitLock(&st->platform.risc.mem_lock); - DibInitLock(&st->platform.risc.mem_mbx_lock); - DibInitLock(&st->demod_lock); + mutex_init(&st->platform.risc.mbx_if_lock); + mutex_init(&st->platform.risc.mbx_lock); + mutex_init(&st->platform.risc.mem_lock); + mutex_init(&st->platform.risc.mem_mbx_lock); + mutex_init(&st->demod_lock); st->get_frontend_internal = 0; st->pid_ctrl_index = -2; -- cgit v1.2.3 From 002eaf90a24451e423221d7a5826cdfdea1a0451 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Mon, 26 Mar 2012 09:17:46 -0300 Subject: [media] media: tvp5150: Fix mbus format According to p.14 fig 3-3 of the datasheet (SLES098A) this decoder transmits data in UYVY format. Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tvp5150.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 476a6a0bd108..b7867427e5c4 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -822,7 +822,7 @@ static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, if (index) return -EINVAL; - *code = V4L2_MBUS_FMT_YUYV8_2X8; + *code = V4L2_MBUS_FMT_UYVY8_2X8; return 0; } @@ -839,7 +839,7 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, f->width = decoder->rect.width; f->height = decoder->rect.height; - f->code = V4L2_MBUS_FMT_YUYV8_2X8; + f->code = V4L2_MBUS_FMT_UYVY8_2X8; f->field = V4L2_FIELD_SEQ_TB; f->colorspace = V4L2_COLORSPACE_SMPTE170M; -- cgit v1.2.3 From 5ff203b5d82edd581b1220aedbf55492f12cf416 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Mon, 26 Mar 2012 09:17:47 -0300 Subject: [media] i.MX27: visstrim_m10: Remove use of MX2_CAMERA_SWAP16 Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-imx/mach-imx27_visstrim_m10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c index f7b074f496f0..84578175b4a6 100644 --- a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c +++ b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c @@ -152,7 +152,7 @@ static struct soc_camera_link iclink_tvp5150 = { static struct mx2_camera_platform_data visstrim_camera = { .flags = MX2_CAMERA_CCIR | MX2_CAMERA_CCIR_INTERLACE | - MX2_CAMERA_SWAP16 | MX2_CAMERA_PCLK_SAMPLE_RISING, + MX2_CAMERA_PCLK_SAMPLE_RISING, .clk = 100000, }; -- cgit v1.2.3 From d509835e32bd761a2b7b446034a273da568e5573 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Mon, 26 Mar 2012 09:17:48 -0300 Subject: [media] media: mx2_camera: Fix mbus format handling Remove MX2_CAMERA_SWAP16 and MX2_CAMERA_PACK_DIR_MSB flags so that the driver can negotiate with the attached sensor whether the mbus format needs convertion from UYUV to YUYV or not. Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- arch/arm/plat-mxc/include/mach/mx2_cam.h | 2 -- drivers/media/video/mx2_camera.c | 52 +++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/arch/arm/plat-mxc/include/mach/mx2_cam.h b/arch/arm/plat-mxc/include/mach/mx2_cam.h index 3c080a32dbf5..7ded6f1f74bc 100644 --- a/arch/arm/plat-mxc/include/mach/mx2_cam.h +++ b/arch/arm/plat-mxc/include/mach/mx2_cam.h @@ -23,7 +23,6 @@ #ifndef __MACH_MX2_CAM_H_ #define __MACH_MX2_CAM_H_ -#define MX2_CAMERA_SWAP16 (1 << 0) #define MX2_CAMERA_EXT_VSYNC (1 << 1) #define MX2_CAMERA_CCIR (1 << 2) #define MX2_CAMERA_CCIR_INTERLACE (1 << 3) @@ -31,7 +30,6 @@ #define MX2_CAMERA_GATED_CLOCK (1 << 5) #define MX2_CAMERA_INV_DATA (1 << 6) #define MX2_CAMERA_PCLK_SAMPLE_RISING (1 << 7) -#define MX2_CAMERA_PACK_DIR_MSB (1 << 8) /** * struct mx2_camera_platform_data - optional platform data for mx2_camera diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 18afaeeadb7b..7c3c0e8eda42 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -344,6 +344,19 @@ static struct mx2_fmt_cfg mx27_emma_prp_table[] = { PRP_INTR_CH2OVF, } }, + { + .in_fmt = V4L2_MBUS_FMT_UYVY8_2X8, + .out_fmt = V4L2_PIX_FMT_YUV420, + .cfg = { + .channel = 2, + .in_fmt = PRP_CNTL_DATA_IN_YUV422, + .out_fmt = PRP_CNTL_CH2_OUT_YUV420, + .src_pixel = 0x22000888, /* YUV422 (YUYV) */ + .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR | + PRP_INTR_CH2FC | PRP_INTR_LBOVF | + PRP_INTR_CH2OVF, + } + }, }; static struct mx2_fmt_cfg *mx27_emma_prp_get_format( @@ -980,6 +993,7 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + const struct soc_camera_format_xlate *xlate; unsigned long common_flags; int ret; int bytesperline; @@ -1024,14 +1038,31 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) return ret; } + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(icd->parent, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + if (xlate->code == V4L2_MBUS_FMT_YUYV8_2X8) { + csicr1 |= CSICR1_PACK_DIR; + csicr1 &= ~CSICR1_SWAP16_EN; + dev_dbg(icd->parent, "already yuyv format, don't convert\n"); + } else if (xlate->code == V4L2_MBUS_FMT_UYVY8_2X8) { + csicr1 &= ~CSICR1_PACK_DIR; + csicr1 |= CSICR1_SWAP16_EN; + dev_dbg(icd->parent, "convert uyvy mbus format into yuyv\n"); + } else { + dev_warn(icd->parent, "mbus format not supported\n"); + return -EINVAL; + } + if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) csicr1 |= CSICR1_REDGE; if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_SOF_POL; if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_HSYNC_POL; - if (pcdev->platform_flags & MX2_CAMERA_SWAP16) - csicr1 |= CSICR1_SWAP16_EN; if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC) csicr1 |= CSICR1_EXT_VSYNC; if (pcdev->platform_flags & MX2_CAMERA_CCIR) @@ -1042,8 +1073,6 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) csicr1 |= CSICR1_GCLK_MODE; if (pcdev->platform_flags & MX2_CAMERA_INV_DATA) csicr1 |= CSICR1_INV_DATA; - if (pcdev->platform_flags & MX2_CAMERA_PACK_DIR_MSB) - csicr1 |= CSICR1_PACK_DIR; pcdev->csicr1 = csicr1; @@ -1118,7 +1147,8 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, return 0; } - if (code == V4L2_MBUS_FMT_YUYV8_2X8) { + if (code == V4L2_MBUS_FMT_YUYV8_2X8 || + code == V4L2_MBUS_FMT_UYVY8_2X8) { formats++; if (xlate) { /* @@ -1134,6 +1164,18 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, } } + if (code == V4L2_MBUS_FMT_UYVY8_2X8) { + formats++; + if (xlate) { + xlate->host_fmt = + soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_2X8); + xlate->code = code; + dev_dbg(dev, "Providing host format %s for sensor code %d\n", + xlate->host_fmt->name, code); + xlate++; + } + } + /* Generic pass-trough */ formats++; if (xlate) { -- cgit v1.2.3 From f9b26cd8e2cb8979694c48ea642d30784e7aceb3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 15 May 2012 10:36:09 -0300 Subject: [media] smiapp: fix compilation breakage The usage of ../*.h breaks out-of-tree compilation and likely breaks compilation when O= argument is used. Instead of doing that, just add the include path via Makefile. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/Makefile | 2 ++ drivers/media/video/smiapp/smiapp.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/smiapp/Makefile b/drivers/media/video/smiapp/Makefile index 5a207eecd357..36b0cfa2c541 100644 --- a/drivers/media/video/smiapp/Makefile +++ b/drivers/media/video/smiapp/Makefile @@ -1,3 +1,5 @@ smiapp-objs += smiapp-core.o smiapp-regs.o \ smiapp-quirk.o smiapp-limits.o obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o + +ccflags-y += -Idrivers/media/video diff --git a/drivers/media/video/smiapp/smiapp.h b/drivers/media/video/smiapp/smiapp.h index 805d8c8a3c18..35b9216e48cd 100644 --- a/drivers/media/video/smiapp/smiapp.h +++ b/drivers/media/video/smiapp/smiapp.h @@ -30,7 +30,7 @@ #include #include -#include "../smiapp-pll.h" +#include "smiapp-pll.h" #include "smiapp-reg.h" #include "smiapp-regs.h" #include "smiapp-quirk.h" -- cgit v1.2.3 From bc050e6702725c6629cc0f1db95c49e63f1f32e8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 8 May 2012 06:04:24 -0300 Subject: [media] af9015: various small changes and clean-ups Clean-up dvb_usb_device_properties and fix errors reported by checkpatch.pl. Some other very minor changes. Functionality remains untouched. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 495 ++++++++++++++++--------------------- 1 file changed, 212 insertions(+), 283 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 7e70ea50ef26..677fed79b01e 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -244,8 +244,7 @@ static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], u8 uninitialized_var(mbox), addr_len; struct req_t req; -/* TODO: implement bus lock - +/* The bus lock is needed because there is two tuners both using same I2C-address. Due to that the only way to select correct tuner is use demodulator I2C-gate. @@ -789,7 +788,7 @@ static void af9015_set_remote_config(struct usb_device *udev, /* try to load remote based USB ID */ if (!props->rc.core.rc_codes) props->rc.core.rc_codes = af9015_rc_setup_match( - (vid << 16) + pid, af9015_rc_setup_usbids); + (vid << 16) | pid, af9015_rc_setup_usbids); /* try to load remote based USB iManufacturer string */ if (!props->rc.core.rc_codes && vid == USB_VID_AFATECH) { @@ -1220,8 +1219,8 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) } /* attach demodulator */ - adap->fe_adap[0].fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id], - &adap->dev->i2c_adap); + adap->fe_adap[0].fe = dvb_attach(af9013_attach, + &af9015_af9013_config[adap->id], &adap->dev->i2c_adap); /* * AF9015 firmware does not like if it gets interrupted by I2C adapter @@ -1324,14 +1323,15 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap) switch (af9015_af9013_config[adap->id].tuner) { case AF9013_TUNER_MT2060: case AF9013_TUNER_MT2060_2: - ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, - &af9015_mt2060_config, + ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9015_mt2060_config, af9015_config.mt2060_if1[adap->id]) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_QT1010: case AF9013_TUNER_QT1010A: - ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, + ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9015_qt1010_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_TDA18271: @@ -1434,69 +1434,85 @@ enum af9015_usb_table_entry { }; static struct usb_device_id af9015_usb_table[] = { - [AFATECH_9015] = - {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)}, - [AFATECH_9016] = - {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)}, - [WINFAST_DTV_GOLD] = - {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)}, - [PINNACLE_PCTV_71E] = - {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)}, - [KWORLD_PLUSTV_399U] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)}, - [TINYTWIN] = {USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN)}, - [AZUREWAVE_TU700] = - {USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700)}, - [TERRATEC_AF9015] = {USB_DEVICE(USB_VID_TERRATEC, + [AFATECH_9015] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)}, + [AFATECH_9016] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)}, + [WINFAST_DTV_GOLD] = { + USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)}, + [PINNACLE_PCTV_71E] = { + USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)}, + [KWORLD_PLUSTV_399U] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)}, + [TINYTWIN] = { + USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN)}, + [AZUREWAVE_TU700] = { + USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700)}, + [TERRATEC_AF9015] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)}, - [KWORLD_PLUSTV_PC160] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)}, - [AVERTV_VOLAR_X] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)}, - [XTENSIONS_380U] = - {USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)}, - [MSI_DIGIVOX_DUO] = - {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, - [AVERTV_VOLAR_X_REV2] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, - [TELESTAR_STARSTICK_2] = - {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)}, - [AVERMEDIA_A309_USB] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)}, - [MSI_DIGIVOX_MINI_III] = - {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)}, - [KWORLD_E396] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)}, - [KWORLD_E39B] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)}, - [KWORLD_E395] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3)}, - [TREKSTOR_DVBT] = {USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT)}, - [AVERTV_A850] = {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850)}, - [AVERTV_A805] = {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805)}, - [CONCEPTRONIC_CTVDIGRCU] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)}, - [KWORLD_MC810] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)}, - [GENIUS_TVGO_DVB_T03] = - {USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)}, - [KWORLD_399U_2] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)}, - [KWORLD_PC160_T] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)}, - [SVEON_STV20] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)}, - [TINYTWIN_2] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)}, - [WINFAST_DTV2000DS] = - {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)}, - [KWORLD_UB383_T] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)}, - [KWORLD_E39A] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)}, - [AVERMEDIA_A815M] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)}, - [CINERGY_T_STICK_RC] = {USB_DEVICE(USB_VID_TERRATEC, + [KWORLD_PLUSTV_PC160] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)}, + [AVERTV_VOLAR_X] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)}, + [XTENSIONS_380U] = { + USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)}, + [MSI_DIGIVOX_DUO] = { + USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, + [AVERTV_VOLAR_X_REV2] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, + [TELESTAR_STARSTICK_2] = { + USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)}, + [AVERMEDIA_A309_USB] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)}, + [MSI_DIGIVOX_MINI_III] = { + USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)}, + [KWORLD_E396] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)}, + [KWORLD_E39B] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)}, + [KWORLD_E395] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3)}, + [TREKSTOR_DVBT] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT)}, + [AVERTV_A850] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850)}, + [AVERTV_A805] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805)}, + [CONCEPTRONIC_CTVDIGRCU] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)}, + [KWORLD_MC810] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)}, + [GENIUS_TVGO_DVB_T03] = { + USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)}, + [KWORLD_399U_2] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)}, + [KWORLD_PC160_T] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)}, + [SVEON_STV20] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)}, + [TINYTWIN_2] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)}, + [WINFAST_DTV2000DS] = { + USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)}, + [KWORLD_UB383_T] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)}, + [KWORLD_E39A] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)}, + [AVERMEDIA_A815M] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)}, + [CINERGY_T_STICK_RC] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_RC)}, - [CINERGY_T_DUAL_RC] = {USB_DEVICE(USB_VID_TERRATEC, + [CINERGY_T_DUAL_RC] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC)}, - [AVERTV_A850T] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)}, - [TINYTWIN_3] = {USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3)}, - [SVEON_STV22] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22)}, + [AVERTV_A850T] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)}, + [TINYTWIN_3] = { + USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3)}, + [SVEON_STV22] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22)}, { } }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1516,43 +1532,44 @@ static struct dvb_usb_device_properties af9015_properties[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, - .fe = {{ - .caps = DVB_USB_ADAP_HAS_PID_FILTER | - DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, - - .pid_filter_count = 32, - .pid_filter = af9015_pid_filter, - .pid_filter_ctrl = af9015_pid_filter_ctrl, - - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x84, + .num_frontends = 1, + .fe = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, + + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + } }, - }}, }, { - .num_frontends = 1, - .fe = {{ - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x85, - .u = { - .bulk = { - .buffersize = - TS_USB20_FRAME_SIZE, - } + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + .u = { + .bulk = { + .buffersize = TS_USB20_FRAME_SIZE, + } + } + }, } }, - }}, } }, @@ -1575,102 +1592,67 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = { &af9015_usb_table[AFATECH_9015], &af9015_usb_table[AFATECH_9016], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Leadtek WinFast DTV Dongle Gold", .cold_ids = { &af9015_usb_table[WINFAST_DTV_GOLD], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Pinnacle PCTV 71e", .cold_ids = { &af9015_usb_table[PINNACLE_PCTV_71E], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld PlusTV Dual DVB-T Stick " \ "(DVB-T 399U)", .cold_ids = { &af9015_usb_table[KWORLD_PLUSTV_399U], &af9015_usb_table[KWORLD_399U_2], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "DigitalNow TinyTwin DVB-T Receiver", .cold_ids = { &af9015_usb_table[TINYTWIN], &af9015_usb_table[TINYTWIN_2], &af9015_usb_table[TINYTWIN_3], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TwinHan AzureWave AD-TU700(704J)", .cold_ids = { &af9015_usb_table[AZUREWAVE_TU700], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TerraTec Cinergy T USB XE", .cold_ids = { &af9015_usb_table[TERRATEC_AF9015], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld PlusTV Dual DVB-T PCI " \ "(DVB-T PC160-2T)", .cold_ids = { &af9015_usb_table[KWORLD_PLUSTV_PC160], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AVerMedia AVerTV DVB-T Volar X", .cold_ids = { &af9015_usb_table[AVERTV_VOLAR_X], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TerraTec Cinergy T Stick RC", .cold_ids = { &af9015_usb_table[CINERGY_T_STICK_RC], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TerraTec Cinergy T Stick Dual RC", .cold_ids = { &af9015_usb_table[CINERGY_T_DUAL_RC], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AverMedia AVerTV Red HD+ (A850T)", .cold_ids = { &af9015_usb_table[AVERTV_A850T], - NULL }, - .warm_ids = {NULL}, }, } }, { @@ -1686,43 +1668,44 @@ static struct dvb_usb_device_properties af9015_properties[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, - .fe = {{ - .caps = DVB_USB_ADAP_HAS_PID_FILTER | - DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, - - .pid_filter_count = 32, - .pid_filter = af9015_pid_filter, - .pid_filter_ctrl = af9015_pid_filter_ctrl, - - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x84, + .num_frontends = 1, + .fe = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, + + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + } }, - }}, }, { - .num_frontends = 1, - .fe = {{ - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x85, - .u = { - .bulk = { - .buffersize = - TS_USB20_FRAME_SIZE, - } + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + .u = { + .bulk = { + .buffersize = TS_USB20_FRAME_SIZE, + } + } + }, } }, - }}, } }, @@ -1744,51 +1727,33 @@ static struct dvb_usb_device_properties af9015_properties[] = { .name = "Xtensions XD-380", .cold_ids = { &af9015_usb_table[XTENSIONS_380U], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "MSI DIGIVOX Duo", .cold_ids = { &af9015_usb_table[MSI_DIGIVOX_DUO], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Fujitsu-Siemens Slim Mobile USB DVB-T", .cold_ids = { &af9015_usb_table[AVERTV_VOLAR_X_REV2], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Telestar Starstick 2", .cold_ids = { &af9015_usb_table[TELESTAR_STARSTICK_2], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AVerMedia A309", .cold_ids = { &af9015_usb_table[AVERMEDIA_A309_USB], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "MSI Digi VOX mini III", .cold_ids = { &af9015_usb_table[MSI_DIGIVOX_MINI_III], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld USB DVB-T TV Stick II " \ "(VS-DVB-T 395U)", .cold_ids = { @@ -1796,34 +1761,23 @@ static struct dvb_usb_device_properties af9015_properties[] = { &af9015_usb_table[KWORLD_E39B], &af9015_usb_table[KWORLD_E395], &af9015_usb_table[KWORLD_E39A], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TrekStor DVB-T USB Stick", .cold_ids = { &af9015_usb_table[TREKSTOR_DVBT], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AverMedia AVerTV Volar Black HD " \ "(A850)", .cold_ids = { &af9015_usb_table[AVERTV_A850], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Sveon STV22 Dual USB DVB-T Tuner HDTV", .cold_ids = { &af9015_usb_table[SVEON_STV22], - NULL }, - .warm_ids = {NULL}, }, } }, { @@ -1839,43 +1793,44 @@ static struct dvb_usb_device_properties af9015_properties[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, - .fe = {{ - .caps = DVB_USB_ADAP_HAS_PID_FILTER | - DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, - - .pid_filter_count = 32, - .pid_filter = af9015_pid_filter, - .pid_filter_ctrl = af9015_pid_filter_ctrl, - - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x84, + .num_frontends = 1, + .fe = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, + + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + } }, - }}, }, { - .num_frontends = 1, - .fe = {{ - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x85, - .u = { - .bulk = { - .buffersize = - TS_USB20_FRAME_SIZE, - } + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + .u = { + .bulk = { + .buffersize = TS_USB20_FRAME_SIZE, + } + } + }, } }, - }}, } }, @@ -1897,76 +1852,50 @@ static struct dvb_usb_device_properties af9015_properties[] = { .name = "AverMedia AVerTV Volar GPS 805 (A805)", .cold_ids = { &af9015_usb_table[AVERTV_A805], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Conceptronic USB2.0 DVB-T CTVDIGRCU " \ "V3.0", .cold_ids = { &af9015_usb_table[CONCEPTRONIC_CTVDIGRCU], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld Digial MC-810", .cold_ids = { &af9015_usb_table[KWORLD_MC810], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Genius TVGo DVB-T03", .cold_ids = { &af9015_usb_table[GENIUS_TVGO_DVB_T03], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld PlusTV DVB-T PCI Pro Card " \ "(DVB-T PC160-T)", .cold_ids = { &af9015_usb_table[KWORLD_PC160_T], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Sveon STV20 Tuner USB DVB-T HDTV", .cold_ids = { &af9015_usb_table[SVEON_STV20], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Leadtek WinFast DTV2000DS", .cold_ids = { &af9015_usb_table[WINFAST_DTV2000DS], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld USB DVB-T Stick Mobile " \ "(UB383-T)", .cold_ids = { &af9015_usb_table[KWORLD_UB383_T], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AverMedia AVerTV Volar M (A815Mac)", .cold_ids = { &af9015_usb_table[AVERMEDIA_A815M], - NULL }, - .warm_ids = {NULL}, }, } }, @@ -2019,5 +1948,5 @@ static struct usb_driver af9015_usb_driver = { module_usb_driver(af9015_usb_driver); MODULE_AUTHOR("Antti Palosaari "); -MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T"); +MODULE_DESCRIPTION("Afatech AF9015 driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 579e92ffac65c717c9c8a50feb755a035a51bb3f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 10 Apr 2012 16:31:31 -0300 Subject: [media] V4L: JPEG class documentation corrections This patch fixes following compilation warning: Error: no ID for constraint linkend: v4l2-jpeg-chroma-subsampling. and adds missing JPEG control class at the Table A.58. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 2 +- Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 132b0cc29832..89941329290e 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3978,7 +3978,7 @@ interface and may change in the future. V4L2_CID_JPEG_CHROMA_SUBSAMPLING menu - + The chroma subsampling factors describe how each component of an input image is sampled, in respect to maximum sample rate in each spatial dimension. See , diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index 0a4b90fcf2da..e3d5afcdafbb 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -284,6 +284,13 @@ These controls are described in . + + V4L2_CTRL_CLASS_JPEG + 0x9d0000 + The class containing JPEG compression controls. +These controls are described in . + -- cgit v1.2.3 From 272ed119d7cb60d0a817786f555eccb2d87c0c69 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Tue, 10 Apr 2012 20:55:40 -0300 Subject: [media] cx231xx: replace open-coded ARRAY_SIZE with macro Signed-off-by: Jim Cromie Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx231xx/cx231xx-avcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index 6e4bca251862..b085a3c6dc04 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -1595,8 +1595,8 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, } cx231xx_info("Enter IF=%zd\n", - sizeof(Dif_set_array)/sizeof(struct dif_settings)); - for (i = 0; i < sizeof(Dif_set_array)/sizeof(struct dif_settings); i++) { + ARRAY_SIZE(Dif_set_array)); + for (i = 0; i < ARRAY_SIZE(Dif_set_array); i++) { if (Dif_set_array[i].if_freq == if_freq) { vid_blk_write_word(dev, Dif_set_array[i].register_address, Dif_set_array[i].value); -- cgit v1.2.3 From b89225a1c803d50271f86be15ecfccc0b0215007 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 15 May 2012 11:02:48 -0300 Subject: [media] smiapp: Remove smiapp-debug.h in favour of dynamic debug Remove smiapp-debug.h and let people use CONFIG_DYNAMIC_DEBUG instead. The option only affected to when debug information was being printed. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp-pll.c | 2 -- drivers/media/video/smiapp/Kconfig | 7 ------- drivers/media/video/smiapp/smiapp-core.c | 2 -- drivers/media/video/smiapp/smiapp-debug.h | 32 ------------------------------- drivers/media/video/smiapp/smiapp-quirk.c | 2 -- drivers/media/video/smiapp/smiapp-regs.c | 2 -- 6 files changed, 47 deletions(-) delete mode 100644 drivers/media/video/smiapp/smiapp-debug.h diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c index 501da413dfad..a416e27a4282 100644 --- a/drivers/media/video/smiapp-pll.c +++ b/drivers/media/video/smiapp-pll.c @@ -22,8 +22,6 @@ * */ -#include "smiapp/smiapp-debug.h" - #include #include #include diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/video/smiapp/Kconfig index 9504c436a5ca..f7b35ff443bf 100644 --- a/drivers/media/video/smiapp/Kconfig +++ b/drivers/media/video/smiapp/Kconfig @@ -4,10 +4,3 @@ config VIDEO_SMIAPP select VIDEO_SMIAPP_PLL ---help--- This is a generic driver for SMIA++/SMIA camera modules. - -config VIDEO_SMIAPP_DEBUG - bool "Enable debugging for the generic SMIA++/SMIA driver" - depends on VIDEO_SMIAPP - ---help--- - Enable debugging output in the generic SMIA++/SMIA driver. If you - are developing the driver you might want to enable this. diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 3991c452acb2..a8a1db9563b1 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -26,8 +26,6 @@ * */ -#include "smiapp-debug.h" - #include #include #include diff --git a/drivers/media/video/smiapp/smiapp-debug.h b/drivers/media/video/smiapp/smiapp-debug.h deleted file mode 100644 index 627809eed1d9..000000000000 --- a/drivers/media/video/smiapp/smiapp-debug.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * drivers/media/video/smiapp/smiapp-debug.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef SMIAPP_DEBUG_H -#define SMIAPP_DEBUG_H - -#ifdef CONFIG_VIDEO_SMIAPP_DEBUG -#define DEBUG -#endif - -#endif diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c index dae85a12f7ec..fb018de4bfa8 100644 --- a/drivers/media/video/smiapp/smiapp-quirk.c +++ b/drivers/media/video/smiapp/smiapp-quirk.c @@ -22,8 +22,6 @@ * */ -#include "smiapp-debug.h" - #include #include "smiapp.h" diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c index 4851ff710779..a5055f18091f 100644 --- a/drivers/media/video/smiapp/smiapp-regs.c +++ b/drivers/media/video/smiapp/smiapp-regs.c @@ -22,8 +22,6 @@ * */ -#include "smiapp-debug.h" - #include #include -- cgit v1.2.3 From 9ce3ce4d4a8dc80b2a2265650be54a86e854561e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 27 Apr 2012 04:16:46 -0300 Subject: [media] V4L: mem2mem: fix alignment in mem2mem-testdev Fix a trivial alignment calculation issue. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mem2mem_testdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index ee3efbd83bdb..d2dec585e61b 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -40,7 +40,7 @@ MODULE_VERSION("0.1.1"); #define MIN_H 32 #define MAX_W 640 #define MAX_H 480 -#define DIM_ALIGN_MASK 0x08 /* 8-alignment for dimensions */ +#define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */ /* Flags that indicate a format can be used for capture/output */ #define MEM2MEM_CAPTURE (1 << 0) -- cgit v1.2.3 From 584943aa0692fea9ec3356c7d4677dd7a225cdef Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:03:20 -0300 Subject: [media] mx2_camera: Fix sizeimage computation in try_fmt() The try_fmt() handler restricts the image width based on the hardware limits and updates the bytesperline value, but doesn't update sizeimage. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mx2_camera.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 7c3c0e8eda42..36d17f3483eb 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -1416,6 +1416,7 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->bytesperline = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); BUG_ON(pix->bytesperline < 0); + pix->sizeimage = pix->height * pix->bytesperline; } } -- cgit v1.2.3 From 2b61d46e2c44568886bc099f8085aefc7107e372 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:03:21 -0300 Subject: [media] soc_camera: Use soc_camera_device::sizeimage to compute buffer sizes Instead of computing the buffer size manually in the videobuf queue setup and buffer prepare callbacks, use the previously negotiated soc_camera_device::sizeimage value. Signed-off-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/atmel-isi.c | 17 +++-------------- drivers/media/video/mx1_camera.c | 14 ++------------ drivers/media/video/mx2_camera.c | 14 ++------------ drivers/media/video/mx3_camera.c | 20 +++++++++----------- drivers/media/video/omap1_camera.c | 14 ++------------ drivers/media/video/pxa_camera.c | 14 ++------------ drivers/media/video/sh_mobile_ceu_camera.c | 25 +++++++++---------------- 7 files changed, 29 insertions(+), 89 deletions(-) diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c index ec3f6a06f9c3..d58491b897ca 100644 --- a/drivers/media/video/atmel-isi.c +++ b/drivers/media/video/atmel-isi.c @@ -260,7 +260,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; unsigned long size; - int ret, bytes_per_line; + int ret; /* Reset ISI */ ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); @@ -271,13 +271,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, /* Disable all interrupts */ isi_writel(isi, ISI_INTDIS, ~0UL); - bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; - - size = bytes_per_line * icd->user_height; + size = icd->sizeimage; if (!*nbuffers || *nbuffers > MAX_BUFFER_NUM) *nbuffers = MAX_BUFFER_NUM; @@ -316,13 +310,8 @@ static int buffer_prepare(struct vb2_buffer *vb) struct atmel_isi *isi = ici->priv; unsigned long size; struct isi_dma_desc *desc; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; - size = bytes_per_line * icd->user_height; + size = icd->sizeimage; if (vb2_plane_size(vb, 0) < size) { dev_err(icd->parent, "%s data will not fit into plane (%lu < %lu)\n", diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 055d11ddb038..4296a8350298 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -126,13 +126,8 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytes_per_line < 0) - return bytes_per_line; - - *size = bytes_per_line * icd->user_height; + *size = icd->sizeimage; if (!*count) *count = 32; @@ -171,11 +166,6 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq, struct soc_camera_device *icd = vq->priv_data; struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); int ret; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -202,7 +192,7 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq, vb->state = VIDEOBUF_NEEDS_INIT; } - vb->size = bytes_per_line * vb->height; + vb->size = icd->sizeimage; if (0 != vb->baddr && vb->bsize < vb->size) { ret = -EINVAL; goto out; diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 36d17f3483eb..368049dbe105 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -538,8 +538,6 @@ static int mx2_videobuf_setup(struct vb2_queue *vq, struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]); @@ -547,12 +545,9 @@ static int mx2_videobuf_setup(struct vb2_queue *vq, if (fmt != NULL) return -ENOTTY; - if (bytes_per_line < 0) - return bytes_per_line; - alloc_ctxs[0] = pcdev->alloc_ctx; - sizes[0] = bytes_per_line * icd->user_height; + sizes[0] = icd->sizeimage; if (0 == *count) *count = 32; @@ -568,16 +563,11 @@ static int mx2_videobuf_setup(struct vb2_queue *vq, static int mx2_videobuf_prepare(struct vb2_buffer *vb) { struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); int ret = 0; dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - if (bytes_per_line < 0) - return bytes_per_line; - #ifdef DEBUG /* * This can be useful if you want to see if we actually fill @@ -587,7 +577,7 @@ static int mx2_videobuf_prepare(struct vb2_buffer *vb) 0xaa, vb2_get_plane_payload(vb, 0)); #endif - vb2_set_plane_payload(vb, 0, bytes_per_line * icd->user_height); + vb2_set_plane_payload(vb, 0, icd->sizeimage); if (vb2_plane_vaddr(vb, 0) && vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { ret = -EINVAL; diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 93c35ef5f0ad..b3016adb0f4f 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -199,8 +199,6 @@ static int mx3_videobuf_setup(struct vb2_queue *vq, struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; - int bytes_per_line; - unsigned int height; if (!mx3_cam->idmac_channel[0]) return -EINVAL; @@ -208,21 +206,21 @@ static int mx3_videobuf_setup(struct vb2_queue *vq, if (fmt) { const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd, fmt->fmt.pix.pixelformat); + int bytes_per_line; + if (!xlate) return -EINVAL; + bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width, xlate->host_fmt); - height = fmt->fmt.pix.height; + if (bytes_per_line < 0) + return bytes_per_line; + + sizes[0] = bytes_per_line * fmt->fmt.pix.height; } else { /* Called from VIDIOC_REQBUFS or in compatibility mode */ - bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - height = icd->user_height; + sizes[0] = icd->sizeimage; } - if (bytes_per_line < 0) - return bytes_per_line; - - sizes[0] = bytes_per_line * height; alloc_ctxs[0] = mx3_cam->alloc_ctx; @@ -274,7 +272,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) BUG_ON(bytes_per_line <= 0); - new_size = bytes_per_line * icd->user_height; + new_size = icd->sizeimage; if (vb2_plane_size(vb, 0) < new_size) { dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n", diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c index c20f5ecd6790..addab76048c3 100644 --- a/drivers/media/video/omap1_camera.c +++ b/drivers/media/video/omap1_camera.c @@ -206,15 +206,10 @@ static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; - if (bytes_per_line < 0) - return bytes_per_line; - - *size = bytes_per_line * icd->user_height; + *size = icd->sizeimage; if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode)) *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode); @@ -256,15 +251,10 @@ static int omap1_videobuf_prepare(struct videobuf_queue *vq, { struct soc_camera_device *icd = vq->priv_data; struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb); - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; int ret; - if (bytes_per_line < 0) - return bytes_per_line; - WARN_ON(!list_empty(&vb->queue)); BUG_ON(NULL == icd->current_fmt); @@ -281,7 +271,7 @@ static int omap1_videobuf_prepare(struct videobuf_queue *vq, vb->state = VIDEOBUF_NEEDS_INIT; } - vb->size = bytes_per_line * vb->height; + vb->size = icd->sizeimage; if (vb->baddr && vb->bsize < vb->size) { ret = -EINVAL; diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 5a413f4427e0..6130abe31c43 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -241,15 +241,10 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); - *size = bytes_per_line * icd->user_height; + *size = icd->sizeimage; if (0 == *count) *count = 32; @@ -435,11 +430,6 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); int ret; int size_y, size_u = 0, size_v = 0; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -474,7 +464,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, vb->state = VIDEOBUF_NEEDS_INIT; } - vb->size = bytes_per_line * vb->height; + vb->size = icd->sizeimage; if (0 != vb->baddr && vb->bsize < vb->size) { ret = -EINVAL; goto out; diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 424dfacd263a..8c6dc242ede2 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -210,27 +210,25 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - int bytes_per_line; - unsigned int height; if (fmt) { const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd, fmt->fmt.pix.pixelformat); + int bytes_per_line; + if (!xlate) return -EINVAL; + bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width, xlate->host_fmt); - height = fmt->fmt.pix.height; + if (bytes_per_line < 0) + return bytes_per_line; + + sizes[0] = bytes_per_line * fmt->fmt.pix.height; } else { /* Called from VIDIOC_REQBUFS or in compatibility mode */ - bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - height = icd->user_height; + sizes[0] = icd->sizeimage; } - if (bytes_per_line < 0) - return bytes_per_line; - - sizes[0] = bytes_per_line * height; alloc_ctxs[0] = pcdev->alloc_ctx; @@ -377,13 +375,8 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) struct sh_mobile_ceu_dev *pcdev = ici->priv; struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); unsigned long size; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - goto error; - size = icd->user_height * bytes_per_line; + size = icd->sizeimage; if (vb2_plane_size(vb, 0) < size) { dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", -- cgit v1.2.3 From 1c0f95eec6d6c4cedfbead8eade7d879534da651 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:03:22 -0300 Subject: [media] soc_camera: Use soc_camera_device::bytesperline to compute line sizes Instead of computing the line sizes, use the previously negotiated soc_camera_device::bytesperline value. Signed-off-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mx3_camera.c | 7 ++----- drivers/media/video/sh_mobile_ceu_camera.c | 4 +--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index b3016adb0f4f..6c87d966a554 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -265,13 +265,10 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; struct idmac_video_param *video = &ichan->params.video; const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt); unsigned long flags; dma_cookie_t cookie; size_t new_size; - BUG_ON(bytes_per_line <= 0); - new_size = icd->sizeimage; if (vb2_plane_size(vb, 0) < new_size) { @@ -312,9 +309,9 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) * horizontal parameters in this case are expressed in bytes, * not in pixels. */ - video->out_width = bytes_per_line; + video->out_width = icd->bytesperline; video->out_height = icd->user_height; - video->out_stride = bytes_per_line; + video->out_stride = icd->bytesperline; } else { /* * For IPU known formats the pixel unit will be managed diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 8c6dc242ede2..3d28c9c7a2c1 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -337,9 +337,7 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) if (planar) phys_addr_bottom = phys_addr_top + icd->user_width; else - phys_addr_bottom = phys_addr_top + - soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); + phys_addr_bottom = phys_addr_top + icd->bytesperline; ceu_write(pcdev, bottom1, phys_addr_bottom); } -- cgit v1.2.3 From ad3b81faa1db60b2052f5f5a6ddae712f51b2dff Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:03:23 -0300 Subject: [media] soc-camera: Add plane layout information to struct soc_mbus_pixelfmt To compute the value of the v4l2_pix_format::bytesperline field, we need information about planes layout for planar formats. The new enum soc_mbus_layout conveys that information. Signed-off-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/atmel-isi.c | 1 + drivers/media/video/mx3_camera.c | 2 ++ drivers/media/video/omap1_camera.c | 8 ++++++++ drivers/media/video/pxa_camera.c | 1 + drivers/media/video/sh_mobile_ceu_camera.c | 4 ++++ drivers/media/video/soc_mediabus.c | 33 ++++++++++++++++++++++++++++++ include/media/soc_mediabus.h | 19 +++++++++++++++++ 7 files changed, 68 insertions(+) diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c index d58491b897ca..6274a91c25c7 100644 --- a/drivers/media/video/atmel-isi.c +++ b/drivers/media/video/atmel-isi.c @@ -627,6 +627,7 @@ static const struct soc_mbus_pixelfmt isi_camera_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }; diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 6c87d966a554..2bdda6ca13c0 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -637,12 +637,14 @@ static const struct soc_mbus_pixelfmt mx3_camera_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, { .fourcc = V4L2_PIX_FMT_GREY, .name = "Monochrome 8 bit", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }; diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c index addab76048c3..c7e41145041f 100644 --- a/drivers/media/video/omap1_camera.c +++ b/drivers/media/video/omap1_camera.c @@ -989,6 +989,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_VYUY8_2X8, @@ -998,6 +999,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YUYV8_2X8, @@ -1007,6 +1009,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YVYU8_2X8, @@ -1016,6 +1019,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, @@ -1025,6 +1029,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, @@ -1034,6 +1039,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB565_2X8_BE, @@ -1043,6 +1049,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB565_2X8_LE, @@ -1052,6 +1059,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, }; diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 6130abe31c43..9c21e01f2c24 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -1234,6 +1234,7 @@ static const struct soc_mbus_pixelfmt pxa_camera_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_U_V, }, }; diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 3d28c9c7a2c1..87b07bcab323 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -955,24 +955,28 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C, }, { .fourcc = V4L2_PIX_FMT_NV21, .name = "NV21", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C, }, { .fourcc = V4L2_PIX_FMT_NV16, .name = "NV16", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, }, { .fourcc = V4L2_PIX_FMT_NV61, .name = "NV61", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, }, }; diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index cf7f2194ded4..44dba6c0f790 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -24,6 +24,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YVYU8_2X8, @@ -33,6 +34,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_UYVY8_2X8, @@ -42,6 +44,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_VYUY8_2X8, @@ -51,6 +54,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, @@ -60,6 +64,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, @@ -69,6 +74,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB565_2X8_LE, @@ -78,6 +84,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB565_2X8_BE, @@ -87,6 +94,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR8_1X8, @@ -96,6 +104,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_1X10, @@ -105,6 +114,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_Y8_1X8, @@ -114,6 +124,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_Y10_1X10, @@ -123,6 +134,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, @@ -132,6 +144,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, @@ -141,6 +154,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADLO, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, @@ -150,6 +164,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, @@ -159,6 +174,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADLO, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_JPEG_1X8, @@ -168,6 +184,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_VARIABLE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, @@ -177,6 +194,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YUYV8_1_5X8, @@ -186,6 +204,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YVYU8_1_5X8, @@ -195,6 +214,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_UYVY8_1X16, @@ -204,6 +224,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 16, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_VYUY8_1X16, @@ -213,6 +234,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 16, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YUYV8_1X16, @@ -222,6 +244,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 16, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YVYU8_1X16, @@ -231,6 +254,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 16, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGRBG8_1X8, @@ -240,6 +264,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, @@ -249,6 +274,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGBRG10_1X10, @@ -258,6 +284,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGRBG10_1X10, @@ -267,6 +294,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SRGGB10_1X10, @@ -276,6 +304,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR12_1X12, @@ -285,6 +314,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 12, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGBRG12_1X12, @@ -294,6 +324,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 12, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGRBG12_1X12, @@ -303,6 +334,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 12, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SRGGB12_1X12, @@ -312,6 +344,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 12, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, }; diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h index 73f1e7eb60f3..e18eed4e5678 100644 --- a/include/media/soc_mediabus.h +++ b/include/media/soc_mediabus.h @@ -46,6 +46,24 @@ enum soc_mbus_order { SOC_MBUS_ORDER_BE, }; +/** + * enum soc_mbus_layout - planes layout in memory + * @SOC_MBUS_LAYOUT_PACKED: color components packed + * @SOC_MBUS_LAYOUT_PLANAR_2Y_U_V: YUV components stored in 3 planes (4:2:2) + * @SOC_MBUS_LAYOUT_PLANAR_2Y_C: YUV components stored in a luma and a + * chroma plane (C plane is half the size + * of Y plane) + * @SOC_MBUS_LAYOUT_PLANAR_Y_C: YUV components stored in a luma and a + * chroma plane (C plane is the same size + * as Y plane) + */ +enum soc_mbus_layout { + SOC_MBUS_LAYOUT_PACKED = 0, + SOC_MBUS_LAYOUT_PLANAR_2Y_U_V, + SOC_MBUS_LAYOUT_PLANAR_2Y_C, + SOC_MBUS_LAYOUT_PLANAR_Y_C, +}; + /** * struct soc_mbus_pixelfmt - Data format on the media bus * @name: Name of the format @@ -60,6 +78,7 @@ struct soc_mbus_pixelfmt { u32 fourcc; enum soc_mbus_packing packing; enum soc_mbus_order order; + enum soc_mbus_layout layout; u8 bits_per_sample; }; -- cgit v1.2.3 From 4e0e620ce713893b2883522a16a71b5589e8e3db Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:03:24 -0300 Subject: [media] soc-camera: Fix bytes per line computation for planar formats The V4L2 specification defines bytesperline for planar formats as the number of bytes per line for the largest plane. Modify soc_mbus_bytes_per_line() accordingly. Signed-off-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_mediabus.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index 44dba6c0f790..a7073142f9f1 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -378,6 +378,9 @@ EXPORT_SYMBOL(soc_mbus_samples_per_pixel); s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) { + if (mf->layout != SOC_MBUS_LAYOUT_PACKED) + return width * mf->bits_per_sample / 8; + switch (mf->packing) { case SOC_MBUS_PACKING_NONE: return width * mf->bits_per_sample / 8; -- cgit v1.2.3 From 8929c96378a162263c8e3e547975e283dfd17e7f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:03:25 -0300 Subject: [media] soc-camera: Add soc_mbus_image_size The function returns the minimum size of an image for a given number of bytes per line (as per the V4L2 specification), width and format. Signed-off-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_mediabus.c | 18 ++++++++++++++++++ include/media/soc_mediabus.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index a7073142f9f1..89dce097a827 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -397,6 +397,24 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) } EXPORT_SYMBOL(soc_mbus_bytes_per_line); +s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, + u32 bytes_per_line, u32 height) +{ + if (mf->layout == SOC_MBUS_LAYOUT_PACKED) + return bytes_per_line * height; + + switch (mf->packing) { + case SOC_MBUS_PACKING_2X8_PADHI: + case SOC_MBUS_PACKING_2X8_PADLO: + return bytes_per_line * height * 2; + case SOC_MBUS_PACKING_1_5X8: + return bytes_per_line * height * 3 / 2; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL(soc_mbus_image_size); + const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( enum v4l2_mbus_pixelcode code, const struct soc_mbus_lookup *lookup, diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h index e18eed4e5678..0dc6f4625b92 100644 --- a/include/media/soc_mediabus.h +++ b/include/media/soc_mediabus.h @@ -99,6 +99,8 @@ const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( enum v4l2_mbus_pixelcode code); s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf); +s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, + u32 bytes_per_line, u32 height); int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, unsigned int *numerator, unsigned int *denominator); unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, -- cgit v1.2.3 From bed8d8033037431be3968cd604f32ad8b7260600 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:03:26 -0300 Subject: [media] soc-camera: Honor user-requested bytesperline and sizeimage Compute the bytesperline and sizeimage values when trying/setting formats or when allocating buffers by taking the user-requested values into account. Signed-off-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mx3_camera.c | 20 ++++++++++++++------ drivers/media/video/sh_mobile_ceu_camera.c | 20 ++++++++++++++------ drivers/media/video/soc_camera.c | 29 +++++++++++++++-------------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 2bdda6ca13c0..02d54a057b60 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -206,17 +206,25 @@ static int mx3_videobuf_setup(struct vb2_queue *vq, if (fmt) { const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd, fmt->fmt.pix.pixelformat); - int bytes_per_line; + unsigned int bytes_per_line; + int ret; if (!xlate) return -EINVAL; - bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width, - xlate->host_fmt); - if (bytes_per_line < 0) - return bytes_per_line; + ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width, + xlate->host_fmt); + if (ret < 0) + return ret; + + bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line, + fmt->fmt.pix.height); + if (ret < 0) + return ret; - sizes[0] = bytes_per_line * fmt->fmt.pix.height; + sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret); } else { /* Called from VIDIOC_REQBUFS or in compatibility mode */ sizes[0] = icd->sizeimage; diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 87b07bcab323..79bec15cc4a3 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -214,17 +214,25 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, if (fmt) { const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd, fmt->fmt.pix.pixelformat); - int bytes_per_line; + unsigned int bytes_per_line; + int ret; if (!xlate) return -EINVAL; - bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width, - xlate->host_fmt); - if (bytes_per_line < 0) - return bytes_per_line; + ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width, + xlate->host_fmt); + if (ret < 0) + return ret; + + bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line, + fmt->fmt.pix.height); + if (ret < 0) + return ret; - sizes[0] = bytes_per_line * fmt->fmt.pix.height; + sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret); } else { /* Called from VIDIOC_REQBUFS or in compatibility mode */ sizes[0] = icd->sizeimage; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index d86b15084628..5e3274e55756 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -164,6 +164,7 @@ static int soc_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; int ret; @@ -177,22 +178,22 @@ static int soc_camera_try_fmt(struct soc_camera_device *icd, if (ret < 0) return ret; - if (!pix->sizeimage) { - if (!pix->bytesperline) { - const struct soc_camera_format_xlate *xlate; + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) + return -EINVAL; - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) - return -EINVAL; + ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); + if (ret < 0) + return ret; - ret = soc_mbus_bytes_per_line(pix->width, - xlate->host_fmt); - if (ret > 0) - pix->bytesperline = ret; - } - if (pix->bytesperline) - pix->sizeimage = pix->bytesperline * pix->height; - } + pix->bytesperline = max_t(u32, pix->bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, + pix->height); + if (ret < 0) + return ret; + + pix->sizeimage = max_t(u32, pix->sizeimage, ret); return 0; } -- cgit v1.2.3 From b0a461ff1b476067c24131ea0b2a40571592f761 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:03:27 -0300 Subject: [media] mx2_camera: Use soc_mbus_image_size() instead of manual computation Use the new soc_mbus_image_size() function to compute the image size. Signed-off-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mx2_camera.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 368049dbe105..b63168781cfe 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -1395,7 +1395,8 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, xlate->host_fmt); if (pix->bytesperline < 0) return pix->bytesperline; - pix->sizeimage = pix->height * pix->bytesperline; + pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, + pix->bytesperline, pix->height); /* Check against the CSIRXCNT limit */ if (pix->sizeimage > 4 * 0x3ffff) { /* Adjust geometry, preserve aspect ratio */ @@ -1406,7 +1407,8 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->bytesperline = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); BUG_ON(pix->bytesperline < 0); - pix->sizeimage = pix->height * pix->bytesperline; + pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, + pix->bytesperline, pix->height); } } -- cgit v1.2.3 From 914f05c8118e17d65c4626ae3ed2edcf79f00031 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:03:28 -0300 Subject: [media] soc-camera: Support user-configurable line stride Add a capabilities field to the soc_camera_host structure to flag hosts that support user-configurable line strides. soc_camera_try_fmt() then passes the user-provided bytesperline and sizeimage format fields to such hosts, and expects the host to check (and fix if needed) the values. Signed-off-by: Laurent Pinchart [g.liakhovetski@gmx.de: fix a typo in mx2_camera.c] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mx2_camera.c | 2 ++ drivers/media/video/soc_camera.c | 6 ++++-- include/media/soc_camera.h | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index b63168781cfe..ecd83faf9038 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -1787,6 +1787,8 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->soc_host.priv = pcdev; pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; + if (cpu_is_mx25()) + pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE; pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(pcdev->alloc_ctx)) { diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 5e3274e55756..cfac53544ba6 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -171,8 +171,10 @@ static int soc_camera_try_fmt(struct soc_camera_device *icd, dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n", pixfmtstr(pix->pixelformat), pix->width, pix->height); - pix->bytesperline = 0; - pix->sizeimage = 0; + if (!(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) { + pix->bytesperline = 0; + pix->sizeimage = 0; + } ret = ici->ops->try_fmt(icd, f); if (ret < 0) diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index a87062c393b5..d865dcf9879f 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -56,11 +56,15 @@ struct soc_camera_device { }; }; +/* Host supports programmable stride */ +#define SOCAM_HOST_CAP_STRIDE (1 << 0) + struct soc_camera_host { struct v4l2_device v4l2_dev; struct list_head list; struct mutex host_lock; /* Protect during probing */ unsigned char nr; /* Host number */ + u32 capabilities; void *priv; const char *drv_name; struct soc_camera_host_ops *ops; -- cgit v1.2.3 From fc13baff743cf9fa49035974471c17378bfe6146 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Mar 2012 08:16:05 -0300 Subject: [media] sh_mobile_ceu_camera: Support user-configurable line stride In image mode, the CEU allows configurable line strides up to 8188 pixels. Signed-off-by: Laurent Pinchart [g.liakhovetski@gmx.de: unify sh_mobile_ceu_set_rect() in data-fetch mode] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/sh_mobile_ceu_camera.c | 33 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 79bec15cc4a3..2ffeb21dfe33 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -342,19 +342,15 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) ceu_write(pcdev, top1, phys_addr_top); if (V4L2_FIELD_NONE != pcdev->field) { - if (planar) - phys_addr_bottom = phys_addr_top + icd->user_width; - else - phys_addr_bottom = phys_addr_top + icd->bytesperline; + phys_addr_bottom = phys_addr_top + icd->bytesperline; ceu_write(pcdev, bottom1, phys_addr_bottom); } if (planar) { - phys_addr_top += icd->user_width * - icd->user_height; + phys_addr_top += icd->bytesperline * icd->user_height; ceu_write(pcdev, top2, phys_addr_top); if (V4L2_FIELD_NONE != pcdev->field) { - phys_addr_bottom = phys_addr_top + icd->user_width; + phys_addr_bottom = phys_addr_top + icd->bytesperline; ceu_write(pcdev, bottom2, phys_addr_bottom); } } @@ -681,10 +677,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) in_width *= 2; left_offset *= 2; } - cdwdr_width = width; } else { - int bytes_per_line = soc_mbus_bytes_per_line(width, - icd->current_fmt->host_fmt); unsigned int w_factor; switch (icd->current_fmt->host_fmt->packing) { @@ -697,13 +690,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) in_width = cam->width * w_factor; left_offset *= w_factor; - - if (bytes_per_line < 0) - cdwdr_width = width; - else - cdwdr_width = bytes_per_line; } + cdwdr_width = icd->bytesperline; + height = icd->user_height; in_height = cam->height; if (V4L2_FIELD_NONE != pcdev->field) { @@ -1848,6 +1838,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, return 0; } +#define CEU_CHDW_MAX 8188U /* Maximum line stride */ + static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -1926,10 +1918,20 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, pix->width = width; if (mf.height > height) pix->height = height; + + pix->bytesperline = max(pix->bytesperline, pix->width); + pix->bytesperline = min(pix->bytesperline, CEU_CHDW_MAX); + pix->bytesperline &= ~3; + break; + + default: + /* Configurable stride isn't supported in pass-through mode. */ + pix->bytesperline = 0; } pix->width &= ~3; pix->height &= ~3; + pix->sizeimage = 0; dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n", __func__, ret, pix->pixelformat, pix->width, pix->height); @@ -2148,6 +2150,7 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.nr = pdev->id; pcdev->ici.drv_name = dev_name(&pdev->dev); pcdev->ici.ops = &sh_mobile_ceu_host_ops; + pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE; pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(pcdev->alloc_ctx)) { -- cgit v1.2.3 From 67e86524b907ed2189f4875cdbe7a5249c71ebb2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 8 May 2012 11:46:44 -0300 Subject: [media] V4L: mx2-camera: avoid overflowing 32-bits In mx2_camera_try_fmt(), when applying i.MX25 restrictions to frame sizes, the height is checked to be <= 0xffff. But later an integer multiplication height * 4 * 0x3ffff is performed, which will overflow even for bounded height values. This patch switches to using 64-bit multiplication and division to avoid overflowing. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mx2_camera.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index ecd83faf9038..ded26b7286fa 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1400,8 +1401,8 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, /* Check against the CSIRXCNT limit */ if (pix->sizeimage > 4 * 0x3ffff) { /* Adjust geometry, preserve aspect ratio */ - unsigned int new_height = int_sqrt(4 * 0x3ffff * - pix->height / pix->bytesperline); + unsigned int new_height = int_sqrt(div_u64(0x3ffffULL * + 4 * pix->height, pix->bytesperline)); pix->width = new_height * pix->width / pix->height; pix->height = new_height; pix->bytesperline = soc_mbus_bytes_per_line(pix->width, -- cgit v1.2.3 From 9633c0867fb5fc1eef8bd3c4e7f413034d2c5367 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 8 May 2012 17:41:33 -0300 Subject: [media] V4L: soc-camera: switch to using the existing .enum_framesizes() The recently introduced .enum_mbus_fsizes() v4l2-subdev video operation is a duplicate of the .enum_framesizes() operation, introduced earlier. Switch soc-camera over to using the original one. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index cfac53544ba6..0421bf9453b4 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -1262,7 +1262,7 @@ static int default_enum_framesizes(struct soc_camera_device *icd, /* map xlate-code to pixel_format, sensor only handle xlate-code*/ fsize_mbus.pixel_format = xlate->code; - ret = v4l2_subdev_call(sd, video, enum_mbus_fsizes, &fsize_mbus); + ret = v4l2_subdev_call(sd, video, enum_framesizes, &fsize_mbus); if (ret < 0) return ret; -- cgit v1.2.3 From fec0f72f5f7bceb75234417999a149abe24da347 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 9 May 2012 18:12:28 -0300 Subject: [media] V4L: sh_mobile_ceu_camera: don't fail TRY_FMT VIDIOC_TRY_FMT shouldn't fail if the user requests an unsupported pixel format. Instead the driver should replace it with a supported one. Fix the sh_mobile_ceu_camera driver accordingly. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/sh_mobile_ceu_camera.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 2ffeb21dfe33..61f7f91de931 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -1858,8 +1858,12 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); - return -EINVAL; + xlate = icd->current_fmt; + dev_dbg(icd->parent, "Format %x not found, keeping %x\n", + pixfmt, xlate->host_fmt->fourcc); + pixfmt = xlate->host_fmt->fourcc; + pix->pixelformat = pixfmt; + pix->colorspace = icd->colorspace; } /* FIXME: calculate using depth and bus width */ -- cgit v1.2.3 From 2564f67bc8d56e5c7fc2970f80f41f2d38db3e21 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 8 May 2012 00:00:07 -0300 Subject: [media] V4L2: sh_mobile_ceu: manage lower 8bit bus CAMCR::DTIF feild controls camera bus as upper8bit/16bit/lower8bit. This patch manages unmanaged lower 8bit bus Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/sh_mobile_ceu_camera.c | 8 +++++--- include/media/sh_mobile_ceu.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 61f7f91de931..0baaf94db7e0 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -870,11 +870,13 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; - value |= pcdev->is_16bit ? 1 << 12 : 0; - /* CSI2 mode */ - if (pcdev->pdata->csi2) + if (pcdev->pdata->csi2) /* CSI2 mode */ value |= 3 << 12; + else if (pcdev->is_16bit) + value |= 1 << 12; + else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT) + value |= 2 << 12; ceu_write(pcdev, CAMCR, value); diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h index a90a765f18da..6fdb6adf6b2b 100644 --- a/include/media/sh_mobile_ceu.h +++ b/include/media/sh_mobile_ceu.h @@ -5,6 +5,7 @@ #define SH_CEU_FLAG_USE_16BIT_BUS (1 << 1) /* use 16bit bus width */ #define SH_CEU_FLAG_HSYNC_LOW (1 << 2) /* default High if possible */ #define SH_CEU_FLAG_VSYNC_LOW (1 << 3) /* default High if possible */ +#define SH_CEU_FLAG_LOWER_8BIT (1 << 4) /* default upper 8bit */ struct device; struct resource; -- cgit v1.2.3 From 61282daf505f3c8def09332ca337ac257b792029 Mon Sep 17 00:00:00 2001 From: Masahiro Nakai Date: Tue, 8 May 2012 01:22:31 -0300 Subject: [media] V4L2: mt9t112: fixup JPEG initialization workaround It has been indicated on Atmark Techno Web page http://armadillo.atmark-techno.com/faq/a800eva-dont-work-camera Signed-off-by: Masahiro Nakai Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9t112.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index 8d1445f12708..e1ae46a7ee96 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -453,6 +453,7 @@ static int mt9t112_init_pll(const struct i2c_client *client) * I2C Master Clock Divider */ mt9t112_reg_write(ret, client, 0x0014, 0x3046); + mt9t112_reg_write(ret, client, 0x0016, 0x0400); /* JPEG initialization workaround */ mt9t112_reg_write(ret, client, 0x0022, 0x0190); mt9t112_reg_write(ret, client, 0x3B84, 0x0212); -- cgit v1.2.3 From dedb8cb1d64a118ac21eaabd941ac0b9acdd82fb Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Sat, 5 May 2012 16:13:22 -0300 Subject: [media] em28xx: Fix memory leak on driver defered resource release When the device is physically unplugged but there are still open file handles, resource release is defered until last opened handle is closed. This patch fixes a missing em28xx_fh struct release. Tested by compilation only. Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 1d4068052ef0..50f5f4fc2148 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -2260,6 +2260,7 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_release_resources(dev); kfree(dev->alt_max_pkt_size); kfree(dev); + kfree(fh); return 0; } -- cgit v1.2.3 From 06bba75d2a3e0bf558421b7548a6248ed5c7bfec Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 24 Apr 2012 09:25:18 -0300 Subject: [media] videodev2.h: add enum/query/cap dv_timings ioctls These new ioctls make it possible for the dv_timings API to replace the dv_preset API eventually. Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 173 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 149 insertions(+), 24 deletions(-) diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index dc3e3ea28f99..13d84ed9d3a8 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -991,29 +991,56 @@ struct v4l2_dv_enum_preset { * D V B T T I M I N G S */ -/* BT.656/BT.1120 timing data */ +/** struct v4l2_bt_timings - BT.656/BT.1120 timing data + * @width: total width of the active video in pixels + * @height: total height of the active video in lines + * @interlaced: Interlaced or progressive + * @polarities: Positive or negative polarities + * @pixelclock: Pixel clock in HZ. Ex. 74.25MHz->74250000 + * @hfrontporch:Horizontal front porch in pixels + * @hsync: Horizontal Sync length in pixels + * @hbackporch: Horizontal back porch in pixels + * @vfrontporch:Vertical front porch in lines + * @vsync: Vertical Sync length in lines + * @vbackporch: Vertical back porch in lines + * @il_vfrontporch:Vertical front porch for the even field + * (aka field 2) of interlaced field formats + * @il_vsync: Vertical Sync length for the even field + * (aka field 2) of interlaced field formats + * @il_vbackporch:Vertical back porch for the even field + * (aka field 2) of interlaced field formats + * @standards: Standards the timing belongs to + * @flags: Flags + * @reserved: Reserved fields, must be zeroed. + * + * A note regarding vertical interlaced timings: height refers to the total + * height of the active video frame (= two fields). The blanking timings refer + * to the blanking of each field. So the height of the total frame is + * calculated as follows: + * + * tot_height = height + vfrontporch + vsync + vbackporch + + * il_vfrontporch + il_vsync + il_vbackporch + * + * The active height of each field is height / 2. + */ struct v4l2_bt_timings { - __u32 width; /* width in pixels */ - __u32 height; /* height in lines */ - __u32 interlaced; /* Interlaced or progressive */ - __u32 polarities; /* Positive or negative polarity */ - __u64 pixelclock; /* Pixel clock in HZ. Ex. 74.25MHz->74250000 */ - __u32 hfrontporch; /* Horizpontal front porch in pixels */ - __u32 hsync; /* Horizontal Sync length in pixels */ - __u32 hbackporch; /* Horizontal back porch in pixels */ - __u32 vfrontporch; /* Vertical front porch in pixels */ - __u32 vsync; /* Vertical Sync length in lines */ - __u32 vbackporch; /* Vertical back porch in lines */ - __u32 il_vfrontporch; /* Vertical front porch for bottom field of - * interlaced field formats - */ - __u32 il_vsync; /* Vertical sync length for bottom field of - * interlaced field formats - */ - __u32 il_vbackporch; /* Vertical back porch for bottom field of - * interlaced field formats - */ - __u32 reserved[16]; + __u32 width; + __u32 height; + __u32 interlaced; + __u32 polarities; + __u64 pixelclock; + __u32 hfrontporch; + __u32 hsync; + __u32 hbackporch; + __u32 vfrontporch; + __u32 vsync; + __u32 vbackporch; + __u32 il_vfrontporch; + __u32 il_vsync; + __u32 il_vbackporch; + __u32 standards; + __u32 flags; + __u32 reserved[14]; } __attribute__ ((packed)); /* Interlaced or progressive format */ @@ -1024,8 +1051,42 @@ struct v4l2_bt_timings { #define V4L2_DV_VSYNC_POS_POL 0x00000001 #define V4L2_DV_HSYNC_POS_POL 0x00000002 - -/* DV timings */ +/* Timings standards */ +#define V4L2_DV_BT_STD_CEA861 (1 << 0) /* CEA-861 Digital TV Profile */ +#define V4L2_DV_BT_STD_DMT (1 << 1) /* VESA Discrete Monitor Timings */ +#define V4L2_DV_BT_STD_CVT (1 << 2) /* VESA Coordinated Video Timings */ +#define V4L2_DV_BT_STD_GTF (1 << 3) /* VESA Generalized Timings Formula */ + +/* Flags */ + +/* CVT/GTF specific: timing uses reduced blanking (CVT) or the 'Secondary + GTF' curve (GTF). In both cases the horizontal and/or vertical blanking + intervals are reduced, allowing a higher resolution over the same + bandwidth. This is a read-only flag. */ +#define V4L2_DV_FL_REDUCED_BLANKING (1 << 0) +/* CEA-861 specific: set for CEA-861 formats with a framerate of a multiple + of six. These formats can be optionally played at 1 / 1.001 speed. + This is a read-only flag. */ +#define V4L2_DV_FL_CAN_REDUCE_FPS (1 << 1) +/* CEA-861 specific: only valid for video transmitters, the flag is cleared + by receivers. + If the framerate of the format is a multiple of six, then the pixelclock + used to set up the transmitter is divided by 1.001 to make it compatible + with 60 Hz based standards such as NTSC and PAL-M that use a framerate of + 29.97 Hz. Otherwise this flag is cleared. If the transmitter can't generate + such frequencies, then the flag will also be cleared. */ +#define V4L2_DV_FL_REDUCED_FPS (1 << 2) +/* Specific to interlaced formats: if set, then field 1 is really one half-line + longer and field 2 is really one half-line shorter, so each field has + exactly the same number of half-lines. Whether half-lines can be detected + or used depends on the hardware. */ +#define V4L2_DV_FL_HALF_LINE (1 << 0) + + +/** struct v4l2_dv_timings - DV timings + * @type: the type of the timings + * @bt: BT656/1120 timings + */ struct v4l2_dv_timings { __u32 type; union { @@ -1037,6 +1098,64 @@ struct v4l2_dv_timings { /* Values for the type field */ #define V4L2_DV_BT_656_1120 0 /* BT.656/1120 timing type */ + +/** struct v4l2_enum_dv_timings - DV timings enumeration + * @index: enumeration index + * @reserved: must be zeroed + * @timings: the timings for the given index + */ +struct v4l2_enum_dv_timings { + __u32 index; + __u32 reserved[3]; + struct v4l2_dv_timings timings; +}; + +/** struct v4l2_bt_timings_cap - BT.656/BT.1120 timing capabilities + * @min_width: width in pixels + * @max_width: width in pixels + * @min_height: height in lines + * @max_height: height in lines + * @min_pixelclock: Pixel clock in HZ. Ex. 74.25MHz->74250000 + * @max_pixelclock: Pixel clock in HZ. Ex. 74.25MHz->74250000 + * @standards: Supported standards + * @capabilities: Supported capabilities + * @reserved: Must be zeroed + */ +struct v4l2_bt_timings_cap { + __u32 min_width; + __u32 max_width; + __u32 min_height; + __u32 max_height; + __u64 min_pixelclock; + __u64 max_pixelclock; + __u32 standards; + __u32 capabilities; + __u32 reserved[16]; +} __attribute__ ((packed)); + +/* Supports interlaced formats */ +#define V4L2_DV_BT_CAP_INTERLACED (1 << 0) +/* Supports progressive formats */ +#define V4L2_DV_BT_CAP_PROGRESSIVE (1 << 1) +/* Supports CVT/GTF reduced blanking */ +#define V4L2_DV_BT_CAP_REDUCED_BLANKING (1 << 2) +/* Supports custom formats */ +#define V4L2_DV_BT_CAP_CUSTOM (1 << 3) + +/** struct v4l2_dv_timings_cap - DV timings capabilities + * @type: the type of the timings (same as in struct v4l2_dv_timings) + * @bt: the BT656/1120 timings capabilities + */ +struct v4l2_dv_timings_cap { + __u32 type; + __u32 reserved[3]; + union { + struct v4l2_bt_timings_cap bt; + __u32 raw_data[32]; + }; +}; + + /* * V I D E O I N P U T S */ @@ -2513,6 +2632,12 @@ struct v4l2_create_buffers { #define VIDIOC_DECODER_CMD _IOWR('V', 96, struct v4l2_decoder_cmd) #define VIDIOC_TRY_DECODER_CMD _IOWR('V', 97, struct v4l2_decoder_cmd) +/* Experimental, these three ioctls may change over the next couple of kernel + versions. */ +#define VIDIOC_ENUM_DV_TIMINGS _IOWR('V', 96, struct v4l2_enum_dv_timings) +#define VIDIOC_QUERY_DV_TIMINGS _IOR('V', 97, struct v4l2_dv_timings) +#define VIDIOC_DV_TIMINGS_CAP _IOWR('V', 98, struct v4l2_dv_timings_cap) + /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ -- cgit v1.2.3 From 7dcc606b2a980222d9816d092a5c20b7c98cbd1a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 15 May 2012 08:04:28 -0300 Subject: [media] V4L2 spec: document the new V4L2 DV timings ioctls Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/biblio.xml | 18 ++ Documentation/DocBook/media/v4l/common.xml | 32 ++-- Documentation/DocBook/media/v4l/compat.xml | 13 ++ Documentation/DocBook/media/v4l/v4l2.xml | 15 +- .../DocBook/media/v4l/vidioc-create-bufs.xml | 6 + .../DocBook/media/v4l/vidioc-dv-timings-cap.xml | 211 +++++++++++++++++++++ .../DocBook/media/v4l/vidioc-enum-dv-presets.xml | 4 + .../DocBook/media/v4l/vidioc-enum-dv-timings.xml | 119 ++++++++++++ .../DocBook/media/v4l/vidioc-enuminput.xml | 2 +- .../DocBook/media/v4l/vidioc-enumoutput.xml | 2 +- .../DocBook/media/v4l/vidioc-g-dv-timings.xml | 130 +++++++++++-- .../DocBook/media/v4l/vidioc-prepare-buf.xml | 6 + .../DocBook/media/v4l/vidioc-query-dv-timings.xml | 104 ++++++++++ 13 files changed, 633 insertions(+), 29 deletions(-) create mode 100644 Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml create mode 100644 Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml create mode 100644 Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml diff --git a/Documentation/DocBook/media/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml index 66a0ef251c79..7c49facecd25 100644 --- a/Documentation/DocBook/media/v4l/biblio.xml +++ b/Documentation/DocBook/media/v4l/biblio.xml @@ -208,4 +208,22 @@ in the frequency range from 87,5 to 108,0 MHz recommended exposure index + + CEA-861-E + + Consumer Electronics Association +(http://www.ce.org) + + A DTV Profile for Uncompressed High Speed Digital Interfaces + + + + VESA DMT + + Video Electronics Standards Association +(http://www.vesa.org) + + VESA and Industry Standards and Guidelines for Computer Display Monitor Timing (DMT) + + diff --git a/Documentation/DocBook/media/v4l/common.xml b/Documentation/DocBook/media/v4l/common.xml index c79278acfb0e..81b7cf384a39 100644 --- a/Documentation/DocBook/media/v4l/common.xml +++ b/Documentation/DocBook/media/v4l/common.xml @@ -724,41 +724,47 @@ if (-1 == ioctl (fd, &VIDIOC-S-STD;, &std_id)) { } +
Digital Video (DV) Timings - The video standards discussed so far has been dealing with Analog TV and the + The video standards discussed so far have been dealing with Analog TV and the corresponding video timings. Today there are many more different hardware interfaces such as High Definition TV interfaces (HDMI), VGA, DVI connectors etc., that carry video signals and there is a need to extend the API to select the video timings for these interfaces. Since it is not possible to extend the &v4l2-std-id; due to -the limited bits available, a new set of IOCTLs is added to set/get video timings at +the limited bits available, a new set of IOCTLs was added to set/get video timings at the input and output: + + DV Timings: This will allow applications to define detailed +video timings for the interface. This includes parameters such as width, height, +polarities, frontporch, backporch etc. The linux/v4l2-dv-timings.h +header can be used to get the timings of the formats in the and + standards. + + DV Presets: Digital Video (DV) presets. These are IDs representing a video timing at the input/output. Presets are pre-defined timings implemented by the hardware according to video standards. A __u32 data type is used to represent a preset unlike the bit mask that is used in &v4l2-std-id; allowing future extensions to support as many different presets as needed. - - - Custom DV Timings: This will allow applications to define more detailed -custom video timings for the interface. This includes parameters such as width, height, -polarities, frontporch, backporch etc. - + To enumerate and query the attributes of the DV timings supported by a device, + applications use the &VIDIOC-ENUM-DV-TIMINGS; and &VIDIOC-DV-TIMINGS-CAP; ioctls. + To set DV timings for the device, applications use the +&VIDIOC-S-DV-TIMINGS; ioctl and to get current DV timings they use the +&VIDIOC-G-DV-TIMINGS; ioctl. To detect the DV timings as seen by the video receiver applications +use the &VIDIOC-QUERY-DV-TIMINGS; ioctl. To enumerate and query the attributes of DV presets supported by a device, applications use the &VIDIOC-ENUM-DV-PRESETS; ioctl. To get the current DV preset, applications use the &VIDIOC-G-DV-PRESET; ioctl and to set a preset they use the -&VIDIOC-S-DV-PRESET; ioctl. - To set custom DV timings for the device, applications use the -&VIDIOC-S-DV-TIMINGS; ioctl and to get current custom DV timings they use the -&VIDIOC-G-DV-TIMINGS; ioctl. +&VIDIOC-S-DV-PRESET; ioctl. To detect the preset as seen by the video receiver applications +use the &VIDIOC-QUERY-DV-PRESET; ioctl. Applications can make use of the and flags to decide what ioctls are available to set the video timings for the device. -
&sub-controls; diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index dc61b013b8a8..cd19d21085db 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2407,6 +2407,11 @@ details. Added JPEG compression control class. + + Extended the DV Timings API: + &VIDIOC-ENUM-DV-TIMINGS;, &VIDIOC-QUERY-DV-TIMINGS; and + &VIDIOC-DV-TIMINGS-CAP;. + @@ -2548,6 +2553,10 @@ and may change in the future. &VIDIOC-ENCODER-CMD; and &VIDIOC-TRY-ENCODER-CMD; +ioctls. + + + &VIDIOC-DECODER-CMD; and &VIDIOC-TRY-DECODER-CMD; ioctls. @@ -2557,6 +2566,10 @@ ioctls. &VIDIOC-DBG-G-CHIP-IDENT; ioctl. + + &VIDIOC-ENUM-DV-TIMINGS;, &VIDIOC-QUERY-DV-TIMINGS; and + &VIDIOC-DV-TIMINGS-CAP; ioctls. + Flash API. diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index e6fbbc6c17e1..015c561754b7 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -28,8 +28,8 @@ documentation. Hans Verkuil Designed and documented the VIDIOC_LOG_STATUS ioctl, -the extended control ioctls and major parts of the sliced VBI -API. +the extended control ioctls, major parts of the sliced VBI API, the +MPEG encoder and decoder APIs and the DV Timings API.
hverkuil@xs4all.nl @@ -123,6 +123,7 @@ Remote Controller chapter. 2009 2010 2011 + 2012 Bill Dirks, Michael H. Schimek, Hans Verkuil, Martin Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab, Pawel Osciak @@ -153,6 +154,11 @@ applications. --> V4L2_CID_AUTO_FOCUS_STOP, V4L2_CID_AUTO_FOCUS_STATUS and V4L2_CID_AUTO_FOCUS_RANGE. + 2012-05-01 + hv + Added VIDIOC_ENUM_DV_TIMINGS, VIDIOC_QUERY_DV_TIMINGS and + VIDIOC_DV_TIMINGS_CAP. + @@ -461,7 +467,7 @@ and discussions on the V4L mailing list. Video for Linux Two API Specification - Revision 3.3 + Revision 3.5 &sub-common; @@ -519,10 +525,12 @@ and discussions on the V4L mailing list. &sub-dbg-g-register; &sub-decoder-cmd; &sub-dqevent; + &sub-dv-timings-cap; &sub-encoder-cmd; &sub-enumaudio; &sub-enumaudioout; &sub-enum-dv-presets; + &sub-enum-dv-timings; &sub-enum-fmt; &sub-enum-framesizes; &sub-enum-frameintervals; @@ -557,6 +565,7 @@ and discussions on the V4L mailing list. &sub-querycap; &sub-queryctrl; &sub-query-dv-preset; + &sub-query-dv-timings; &sub-querystd; &sub-prepare-buf; &sub-reqbufs; diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml index 184cdfc13081..765549ff8a71 100644 --- a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml @@ -48,6 +48,12 @@ Description + + Experimental + This is an experimental + interface and may change in the future. + + This ioctl is used to create buffers for memory mapped or user pointer I/O. It can be used as an alternative or in addition to the diff --git a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml new file mode 100644 index 000000000000..6673ce582050 --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml @@ -0,0 +1,211 @@ + + + ioctl VIDIOC_DV_TIMINGS_CAP + &manvol; + + + + VIDIOC_DV_TIMINGS_CAP + The capabilities of the Digital Video receiver/transmitter + + + + + + int ioctl + int fd + int request + struct v4l2_dv_timings_cap *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_DV_TIMINGS_CAP + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + To query the available timings, applications initialize the +index field and zero the reserved array of &v4l2-dv-timings-cap; +and call the VIDIOC_DV_TIMINGS_CAP ioctl with a pointer to this +structure. Drivers fill the rest of the structure or return an +&EINVAL; when the index is out of bounds. To enumerate all supported DV timings, +applications shall begin at index zero, incrementing by one until the +driver returns EINVAL. Note that drivers may enumerate a +different set of DV timings after switching the video input or +output. + + + struct <structname>v4l2_bt_timings_cap</structname> + + &cs-str; + + + __u32 + min_width + Minimum width of the active video in pixels. + + + __u32 + max_width + Maximum width of the active video in pixels. + + + __u32 + min_height + Minimum height of the active video in lines. + + + __u32 + max_height + Maximum height of the active video in lines. + + + __u64 + min_pixelclock + Minimum pixelclock frequency in Hz. + + + __u64 + max_pixelclock + Maximum pixelclock frequency in Hz. + + + __u32 + standards + The video standard(s) supported by the hardware. + See for a list of standards. + + + __u32 + capabilities + Several flags giving more information about the capabilities. + See for a description of the flags. + + + + __u32 + reserved[16] + + + + +
+ + + struct <structname>v4l2_dv_timings_cap</structname> + + &cs-str; + + + __u32 + type + Type of DV timings as listed in . + + + __u32 + reserved[3] + Reserved for future extensions. Drivers must set the array to zero. + + + union + + + + + + &v4l2-bt-timings-cap; + bt + BT.656/1120 timings capabilities of the hardware. + + + + __u32 + raw_data[32] + + + + +
+ + + DV BT Timing capabilities + + &cs-str; + + + Flag + Description + + + + + + + V4L2_DV_BT_CAP_INTERLACED + Interlaced formats are supported. + + + + V4L2_DV_BT_CAP_PROGRESSIVE + Progressive formats are supported. + + + + V4L2_DV_BT_CAP_REDUCED_BLANKING + CVT/GTF specific: the timings can make use of reduced blanking (CVT) +or the 'Secondary GTF' curve (GTF). + + + + V4L2_DV_BT_CAP_CUSTOM + Can support non-standard timings, i.e. timings not belonging to the +standards set in the standards field. + + + + +
+
+ + + &return-value; + +
+ + diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml index 0be17c232d3a..509f0012d2a6 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml @@ -48,6 +48,10 @@ Description + This ioctl is deprecated. + New drivers and applications should use &VIDIOC-ENUM-DV-TIMINGS; instead. + + To query the attributes of a DV preset, applications initialize the index field and zero the reserved array of &v4l2-dv-enum-preset; and call the VIDIOC_ENUM_DV_PRESETS ioctl with a pointer to this diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml new file mode 100644 index 000000000000..24c3bf4fd29a --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml @@ -0,0 +1,119 @@ + + + ioctl VIDIOC_ENUM_DV_TIMINGS + &manvol; + + + + VIDIOC_ENUM_DV_TIMINGS + Enumerate supported Digital Video timings + + + + + + int ioctl + int fd + int request + struct v4l2_enum_dv_timings *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_ENUM_DV_TIMINGS + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + While some DV receivers or transmitters support a wide range of timings, others +support only a limited number of timings. With this ioctl applications can enumerate a list +of known supported timings. Call &VIDIOC-DV-TIMINGS-CAP; to check if it also supports other +standards or even custom timings that are not in this list. + + To query the available timings, applications initialize the +index field and zero the reserved array of &v4l2-enum-dv-timings; +and call the VIDIOC_ENUM_DV_TIMINGS ioctl with a pointer to this +structure. Drivers fill the rest of the structure or return an +&EINVAL; when the index is out of bounds. To enumerate all supported DV timings, +applications shall begin at index zero, incrementing by one until the +driver returns EINVAL. Note that drivers may enumerate a +different set of DV timings after switching the video input or +output. + + + struct <structname>v4l2_enum_dv_timings</structname> + + &cs-str; + + + __u32 + index + Number of the DV timings, set by the +application. + + + __u32 + reserved[3] + Reserved for future extensions. Drivers must set the array to zero. + + + &v4l2-dv-timings; + timings + The timings. + + + +
+
+ + + &return-value; + + + + EINVAL + + The &v4l2-enum-dv-timings; index +is out of bounds. + + + + +
+ + diff --git a/Documentation/DocBook/media/v4l/vidioc-enuminput.xml b/Documentation/DocBook/media/v4l/vidioc-enuminput.xml index 9b8efcd6e947..46d5a044a537 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enuminput.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enuminput.xml @@ -285,7 +285,7 @@ input/output interface to linux-media@vger.kernel.org on 19 Oct 2009. V4L2_IN_CAP_CUSTOM_TIMINGS 0x00000002 - This input supports setting custom video timings by using VIDIOC_S_DV_TIMINGS. + This input supports setting video timings by using VIDIOC_S_DV_TIMINGS. V4L2_IN_CAP_STD diff --git a/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml b/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml index a64d5ef103fa..428020000ef0 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml @@ -170,7 +170,7 @@ input/output interface to linux-media@vger.kernel.org on 19 Oct 2009. V4L2_OUT_CAP_CUSTOM_TIMINGS 0x00000002 - This output supports setting custom video timings by using VIDIOC_S_DV_TIMINGS. + This output supports setting video timings by using VIDIOC_S_DV_TIMINGS. V4L2_OUT_CAP_STD diff --git a/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml index 4a8648ae9a63..eda1a2991bbe 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml @@ -7,7 +7,7 @@ VIDIOC_G_DV_TIMINGS VIDIOC_S_DV_TIMINGS - Get or set custom DV timings for input or output + Get or set DV timings for input or output @@ -48,12 +48,15 @@ Description - To set custom DV timings for the input or output, applications use the -VIDIOC_S_DV_TIMINGS ioctl and to get the current custom timings, + To set DV timings for the input or output, applications use the +VIDIOC_S_DV_TIMINGS ioctl and to get the current timings, applications use the VIDIOC_G_DV_TIMINGS ioctl. The detailed timing information is filled in using the structure &v4l2-dv-timings;. These ioctls take a pointer to the &v4l2-dv-timings; structure as argument. If the ioctl is not supported or the timing values are not correct, the driver returns &EINVAL;. +The linux/v4l2-dv-timings.h header can be used to get the +timings of the formats in the and +standards. @@ -83,12 +86,13 @@ or the timing values are not correct, the driver returns &EINVAL;.
__u32 width - Width of the active video in pixels + Width of the active video in pixels. __u32 height - Height of the active video in lines + Height of the active video frame in lines. So for interlaced formats the + height of the active video in each field is height/2. __u32 @@ -125,32 +129,52 @@ bit 0 (V4L2_DV_VSYNC_POS_POL) is for vertical sync polarity and bit 1 (V4L2_DV_H __u32 vfrontporch - Vertical front porch in lines + Vertical front porch in lines. For interlaced formats this refers to the + odd field (aka field 1). __u32 vsync - Vertical sync length in lines + Vertical sync length in lines. For interlaced formats this refers to the + odd field (aka field 1). __u32 vbackporch - Vertical back porch in lines + Vertical back porch in lines. For interlaced formats this refers to the + odd field (aka field 1). __u32 il_vfrontporch - Vertical front porch in lines for bottom field of interlaced field formats + Vertical front porch in lines for the even field (aka field 2) of + interlaced field formats. __u32 il_vsync - Vertical sync length in lines for bottom field of interlaced field formats + Vertical sync length in lines for the even field (aka field 2) of + interlaced field formats. __u32 il_vbackporch - Vertical back porch in lines for bottom field of interlaced field formats + Vertical back porch in lines for the even field (aka field 2) of + interlaced field formats. + + + __u32 + standards + The video standard(s) this format belongs to. This will be filled in by + the driver. Applications must set this to 0. See + for a list of standards. + + + __u32 + flags + Several flags giving more information about the format. + See for a description of the flags. + @@ -211,6 +235,90 @@ bit 0 (V4L2_DV_VSYNC_POS_POL) is for vertical sync polarity and bit 1 (V4L2_DV_H + + DV BT Timing standards + + &cs-str; + + + Timing standard + Description + + + + + + + V4L2_DV_BT_STD_CEA861 + The timings follow the CEA-861 Digital TV Profile standard + + + V4L2_DV_BT_STD_DMT + The timings follow the VESA Discrete Monitor Timings standard + + + V4L2_DV_BT_STD_CVT + The timings follow the VESA Coordinated Video Timings standard + + + V4L2_DV_BT_STD_GTF + The timings follow the VESA Generalized Timings Formula standard + + + +
+ + DV BT Timing flags + + &cs-str; + + + Flag + Description + + + + + + + V4L2_DV_FL_REDUCED_BLANKING + CVT/GTF specific: the timings use reduced blanking (CVT) or the 'Secondary +GTF' curve (GTF). In both cases the horizontal and/or vertical blanking +intervals are reduced, allowing a higher resolution over the same +bandwidth. This is a read-only flag, applications must not set this. + + + + V4L2_DV_FL_CAN_REDUCE_FPS + CEA-861 specific: set for CEA-861 formats with a framerate that is a multiple +of six. These formats can be optionally played at 1 / 1.001 speed to +be compatible with 60 Hz based standards such as NTSC and PAL-M that use a framerate of +29.97 frames per second. If the transmitter can't generate such frequencies, then the +flag will also be cleared. This is a read-only flag, applications must not set this. + + + + V4L2_DV_FL_REDUCED_FPS + CEA-861 specific: only valid for video transmitters, the flag is cleared +by receivers. It is also only valid for formats with the V4L2_DV_FL_CAN_REDUCE_FPS flag +set, for other formats the flag will be cleared by the driver. + +If the application sets this flag, then the pixelclock used to set up the transmitter is +divided by 1.001 to make it compatible with NTSC framerates. If the transmitter +can't generate such frequencies, then the flag will also be cleared. + + + + V4L2_DV_FL_HALF_LINE + Specific to interlaced formats: if set, then field 1 (aka the odd field) +is really one half-line longer and field 2 (aka the even field) is really one half-line +shorter, so each field has exactly the same number of half-lines. Whether half-lines can be +detected or used depends on the hardware. + + + + +
&return-value; diff --git a/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml b/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml index 7bde698760e4..fa7ad7e33228 100644 --- a/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml @@ -48,6 +48,12 @@ Description + + Experimental + This is an experimental + interface and may change in the future. + + Applications can optionally call the VIDIOC_PREPARE_BUF ioctl to pass ownership of the buffer to the driver before actually enqueuing it, using the diff --git a/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml new file mode 100644 index 000000000000..44935a0ffcf0 --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml @@ -0,0 +1,104 @@ + + + ioctl VIDIOC_QUERY_DV_TIMINGS + &manvol; + + + + VIDIOC_QUERY_DV_TIMINGS + Sense the DV preset received by the current +input + + + + + + int ioctl + int fd + int request + struct v4l2_dv_timings *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_QUERY_DV_TIMINGS + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + The hardware may be able to detect the current DV timings +automatically, similar to sensing the video standard. To do so, applications +call VIDIOC_QUERY_DV_TIMINGS with a pointer to a +&v4l2-dv-timings;. Once the hardware detects the timings, it will fill in the +timings structure. + +If the timings could not be detected because there was no signal, then +ENOLINK is returned. If a signal was detected, but +it was unstable and the receiver could not lock to the signal, then +ENOLCK is returned. If the receiver could lock to the signal, +but the format is unsupported (e.g. because the pixelclock is out of range +of the hardware capabilities), then the driver fills in whatever timings it +could find and returns ERANGE. In that case the application +can call &VIDIOC-DV-TIMINGS-CAP; to compare the found timings with the hardware's +capabilities in order to give more precise feedback to the user. + + + + + &return-value; + + + + ENOLINK + + No timings could be detected because no signal was found. + + + + + ENOLCK + + The signal was unstable and the hardware could not lock on to it. + + + + + ERANGE + + Timings were found, but they are out of range of the hardware +capabilities. + + + + + + -- cgit v1.2.3 From 5d7758eed2307f7b9934c6b64fbdbfaab52e436d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 15 May 2012 08:06:44 -0300 Subject: [media] v4l2 framework: add support for the new dv_timings ioctls Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-compat-ioctl32.c | 3 + drivers/media/video/v4l2-ioctl.c | 126 +++++++++++++++++++++--------- include/media/v4l2-ioctl.h | 6 ++ include/media/v4l2-subdev.h | 6 ++ 4 files changed, 104 insertions(+), 37 deletions(-) diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 89ae433877e6..5327ad3a6390 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -1023,6 +1023,9 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_UNSUBSCRIBE_EVENT: case VIDIOC_CREATE_BUFS32: case VIDIOC_PREPARE_BUF32: + case VIDIOC_ENUM_DV_TIMINGS: + case VIDIOC_QUERY_DV_TIMINGS: + case VIDIOC_DV_TIMINGS_CAP: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 623d280ce095..91be4e871f43 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -281,6 +281,9 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, 0), IOCTL_INFO(VIDIOC_CREATE_BUFS, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_PREPARE_BUF, 0), + IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, 0), + IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, 0), + IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, 0), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -368,6 +371,34 @@ static inline void dbgrect(struct video_device *vfd, char *s, r->width, r->height); }; +static void dbgtimings(struct video_device *vfd, + const struct v4l2_dv_timings *p) +{ + switch (p->type) { + case V4L2_DV_BT_656_1120: + dbgarg2("bt-656/1120:interlaced=%d," + " pixelclock=%lld," + " width=%d, height=%d, polarities=%x," + " hfrontporch=%d, hsync=%d," + " hbackporch=%d, vfrontporch=%d," + " vsync=%d, vbackporch=%d," + " il_vfrontporch=%d, il_vsync=%d," + " il_vbackporch=%d, standards=%x, flags=%x\n", + p->bt.interlaced, p->bt.pixelclock, + p->bt.width, p->bt.height, + p->bt.polarities, p->bt.hfrontporch, + p->bt.hsync, p->bt.hbackporch, + p->bt.vfrontporch, p->bt.vsync, + p->bt.vbackporch, p->bt.il_vfrontporch, + p->bt.il_vsync, p->bt.il_vbackporch, + p->bt.standards, p->bt.flags); + break; + default: + dbgarg2("Unknown type %d!\n", p->type); + break; + } +} + static inline void v4l_print_pix_fmt(struct video_device *vfd, struct v4l2_pix_format *fmt) { @@ -1916,25 +1947,13 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_timings *p = arg; + dbgtimings(vfd, p); switch (p->type) { case V4L2_DV_BT_656_1120: - dbgarg2("bt-656/1120:interlaced=%d, pixelclock=%lld," - " width=%d, height=%d, polarities=%x," - " hfrontporch=%d, hsync=%d, hbackporch=%d," - " vfrontporch=%d, vsync=%d, vbackporch=%d," - " il_vfrontporch=%d, il_vsync=%d," - " il_vbackporch=%d\n", - p->bt.interlaced, p->bt.pixelclock, - p->bt.width, p->bt.height, p->bt.polarities, - p->bt.hfrontporch, p->bt.hsync, - p->bt.hbackporch, p->bt.vfrontporch, - p->bt.vsync, p->bt.vbackporch, - p->bt.il_vfrontporch, p->bt.il_vsync, - p->bt.il_vbackporch); ret = ops->vidioc_s_dv_timings(file, fh, p); break; default: - dbgarg2("Unknown type %d!\n", p->type); + ret = -EINVAL; break; } break; @@ -1944,29 +1963,60 @@ static long __video_do_ioctl(struct file *file, struct v4l2_dv_timings *p = arg; ret = ops->vidioc_g_dv_timings(file, fh, p); + if (!ret) + dbgtimings(vfd, p); + break; + } + case VIDIOC_ENUM_DV_TIMINGS: + { + struct v4l2_enum_dv_timings *p = arg; + + if (!ops->vidioc_enum_dv_timings) + break; + + ret = ops->vidioc_enum_dv_timings(file, fh, p); if (!ret) { - switch (p->type) { - case V4L2_DV_BT_656_1120: - dbgarg2("bt-656/1120:interlaced=%d," - " pixelclock=%lld," - " width=%d, height=%d, polarities=%x," - " hfrontporch=%d, hsync=%d," - " hbackporch=%d, vfrontporch=%d," - " vsync=%d, vbackporch=%d," - " il_vfrontporch=%d, il_vsync=%d," - " il_vbackporch=%d\n", - p->bt.interlaced, p->bt.pixelclock, - p->bt.width, p->bt.height, - p->bt.polarities, p->bt.hfrontporch, - p->bt.hsync, p->bt.hbackporch, - p->bt.vfrontporch, p->bt.vsync, - p->bt.vbackporch, p->bt.il_vfrontporch, - p->bt.il_vsync, p->bt.il_vbackporch); - break; - default: - dbgarg2("Unknown type %d!\n", p->type); - break; - } + dbgarg(cmd, "index=%d: ", p->index); + dbgtimings(vfd, &p->timings); + } + break; + } + case VIDIOC_QUERY_DV_TIMINGS: + { + struct v4l2_dv_timings *p = arg; + + if (!ops->vidioc_query_dv_timings) + break; + + ret = ops->vidioc_query_dv_timings(file, fh, p); + if (!ret) + dbgtimings(vfd, p); + break; + } + case VIDIOC_DV_TIMINGS_CAP: + { + struct v4l2_dv_timings_cap *p = arg; + + if (!ops->vidioc_dv_timings_cap) + break; + + ret = ops->vidioc_dv_timings_cap(file, fh, p); + if (ret) + break; + switch (p->type) { + case V4L2_DV_BT_656_1120: + dbgarg(cmd, + "type=%d, width=%u-%u, height=%u-%u, " + "pixelclock=%llu-%llu, standards=%x, capabilities=%x ", + p->type, + p->bt.min_width, p->bt.max_width, + p->bt.min_height, p->bt.max_height, + p->bt.min_pixelclock, p->bt.max_pixelclock, + p->bt.standards, p->bt.capabilities); + break; + default: + dbgarg(cmd, "unknown type "); + break; } break; } @@ -2215,7 +2265,9 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, err = -EFAULT; goto out_array_args; } - if (err < 0) + /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid + results that must be returned. */ + if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS) goto out; out_array_args: diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 3cb939cd03f9..d8b76f7392f8 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -271,6 +271,12 @@ struct v4l2_ioctl_ops { struct v4l2_dv_timings *timings); int (*vidioc_g_dv_timings) (struct file *file, void *fh, struct v4l2_dv_timings *timings); + int (*vidioc_query_dv_timings) (struct file *file, void *fh, + struct v4l2_dv_timings *timings); + int (*vidioc_enum_dv_timings) (struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings); + int (*vidioc_dv_timings_cap) (struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap); int (*vidioc_subscribe_event) (struct v4l2_fh *fh, struct v4l2_event_subscription *sub); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 1c2318b15bd2..c35a3545e273 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -307,6 +307,12 @@ struct v4l2_subdev_video_ops { struct v4l2_dv_timings *timings); int (*g_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings); + int (*enum_dv_timings)(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings); + int (*query_dv_timings)(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings); + int (*dv_timings_cap)(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap); int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code); int (*enum_mbus_fsizes)(struct v4l2_subdev *sd, -- cgit v1.2.3 From f00dc30422d442c6cfbbab3c6e93fe6cb6681621 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 9 May 2012 03:37:07 -0300 Subject: [media] v4l2-dv-timings.h: definitions for CEA-861 and VESA DMT timings This header contains the timings for the common CEA-861 and all VESA DMT formats for use with the V4L2 dv_timings API. Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- include/linux/Kbuild | 1 + include/linux/v4l2-dv-timings.h | 816 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 817 insertions(+) create mode 100644 include/linux/v4l2-dv-timings.h diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 3c9b616c834a..d38b3a8fb380 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -382,6 +382,7 @@ header-y += usbdevice_fs.h header-y += utime.h header-y += utsname.h header-y += uvcvideo.h +header-y += v4l2-dv-timings.h header-y += v4l2-mediabus.h header-y += v4l2-subdev.h header-y += veth.h diff --git a/include/linux/v4l2-dv-timings.h b/include/linux/v4l2-dv-timings.h new file mode 100644 index 000000000000..9ef8172e5ed0 --- /dev/null +++ b/include/linux/v4l2-dv-timings.h @@ -0,0 +1,816 @@ +/* + * V4L2 DV timings header. + * + * Copyright (C) 2012 Hans Verkuil + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef _V4L2_DV_TIMINGS_H +#define _V4L2_DV_TIMINGS_H + +#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6)) +/* Sadly gcc versions older than 4.6 have a bug in how they initialize + anonymous unions where they require additional curly brackets. + This violates the C1x standard. This workaround adds the curly brackets + if needed. */ +#define V4L2_INIT_BT_TIMINGS(_width, args...) \ + { .bt = { _width , ## args } } +#else +#define V4L2_INIT_BT_TIMINGS(_width, args...) \ + .bt = { _width , ## args } +#endif + +/* CEA-861-E timings (i.e. standard HDTV timings) */ + +#define V4L2_DV_BT_CEA_640X480P59_94 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 480, 0, 0, \ + 25175000, 16, 96, 48, 10, 2, 33, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_720X480P59_94 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(720, 480, 0, 0, \ + 27000000, 16, 62, 60, 9, 6, 30, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_720X576P50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(720, 576, 0, 0, \ + 27000000, 12, 64, 68, 5, 5, 39, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1280X720P24 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 59400000, 1760, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1280X720P25 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 2420, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1280X720P30 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 1760, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1280X720P50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 440, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1280X720P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 110, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P24 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P25 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 528, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P30 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 88, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1920X1080I50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 528, 44, 148, 2, 5, 15, 2, 5, 16, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_HALF_LINE) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 148500000, 528, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1920X1080I60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 88, 44, 148, 2, 5, 15, 2, 5, 16, \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_HALF_LINE) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 148500000, 88, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + + +/* VESA Discrete Monitor Timings as per version 1.0, revision 12 */ + +#define V4L2_DV_BT_DMT_640X350P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 350, 0, V4L2_DV_HSYNC_POS_POL, \ + 31500000, 32, 64, 96, 32, 3, 60, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_640X400P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 400, 0, V4L2_DV_VSYNC_POS_POL, \ + 31500000, 32, 64, 96, 1, 3, 41, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_720X400P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(720, 400, 0, V4L2_DV_VSYNC_POS_POL, \ + 35500000, 36, 72, 108, 1, 3, 42, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +/* VGA resolutions */ +#define V4L2_DV_BT_DMT_640X480P60 V4L2_DV_BT_CEA_640X480P59_94 + +#define V4L2_DV_BT_DMT_640X480P72 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 480, 0, 0, \ + 31500000, 24, 40, 128, 9, 3, 28, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_640X480P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 480, 0, 0, \ + 31500000, 16, 64, 120, 1, 3, 16, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_640X480P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 480, 0, 0, \ + 36000000, 56, 56, 80, 1, 3, 25, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +/* SVGA resolutions */ +#define V4L2_DV_BT_DMT_800X600P56 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 36000000, 24, 72, 128, 1, 2, 22, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 40000000, 40, 128, 88, 1, 4, 23, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P72 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 50000000, 56, 120, 64, 37, 6, 23, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 49500000, 16, 80, 160, 1, 3, 21, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 56250000, 32, 64, 152, 1, 3, 27, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, V4L2_DV_HSYNC_POS_POL, \ + 73250000, 48, 32, 80, 3, 4, 29, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_848X480P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(848, 480, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 33750000, 16, 112, 112, 6, 8, 23, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768I43 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 1, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 44900000, 8, 176, 56, 0, 4, 20, 0, 4, 21, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +/* XGA resolutions */ +#define V4L2_DV_BT_DMT_1024X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, 0, \ + 65000000, 24, 136, 160, 3, 6, 29, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768P70 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, 0, \ + 75000000, 24, 136, 144, 3, 6, 29, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 78750000, 16, 96, 176, 1, 3, 28, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 94500000, 48, 96, 208, 1, 3, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, V4L2_DV_HSYNC_POS_POL, \ + 115500000, 48, 32, 80, 3, 4, 38, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* XGA+ resolution */ +#define V4L2_DV_BT_DMT_1152X864P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1152, 864, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 108000000, 64, 128, 256, 1, 3, 32, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X720P60 V4L2_DV_BT_CEA_1280X720P60 + +/* WXGA resolutions */ +#define V4L2_DV_BT_DMT_1280X768P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_HSYNC_POS_POL, \ + 68250000, 48, 32, 80, 3, 7, 12, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1280X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_VSYNC_POS_POL, \ + 79500000, 64, 128, 192, 3, 7, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X768P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_VSYNC_POS_POL, \ + 102250000, 80, 128, 208, 3, 7, 27, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X768P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_VSYNC_POS_POL, \ + 117500000, 80, 136, 216, 3, 7, 31, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X768P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_HSYNC_POS_POL, \ + 140250000, 48, 32, 80, 3, 7, 35, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1280X800P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_HSYNC_POS_POL, \ + 71000000, 48, 32, 80, 3, 6, 14, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1280X800P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_VSYNC_POS_POL, \ + 83500000, 72, 128, 200, 3, 6, 22, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X800P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_VSYNC_POS_POL, \ + 106500000, 80, 128, 208, 3, 6, 29, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X800P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_VSYNC_POS_POL, \ + 122500000, 80, 136, 216, 3, 6, 34, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X800P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_HSYNC_POS_POL, \ + 146250000, 48, 32, 80, 3, 6, 38, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1280X960P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 960, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 108000000, 96, 112, 312, 1, 3, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X960P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 960, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 148500000, 64, 160, 224, 1, 3, 47, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X960P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 960, 0, V4L2_DV_HSYNC_POS_POL, \ + 175500000, 48, 32, 80, 3, 4, 50, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* SXGA resolutions */ +#define V4L2_DV_BT_DMT_1280X1024P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 1024, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 108000000, 48, 112, 248, 1, 3, 38, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X1024P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 1024, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 135000000, 16, 144, 248, 1, 3, 38, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X1024P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 1024, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 157500000, 64, 160, 224, 1, 3, 44, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X1024P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 1024, 0, V4L2_DV_HSYNC_POS_POL, \ + 187250000, 48, 32, 80, 3, 7, 50, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1360X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1360, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 85500000, 64, 112, 256, 3, 6, 18, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1360X768P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1360, 768, 0, V4L2_DV_HSYNC_POS_POL, \ + 148250000, 48, 32, 80, 3, 5, 37, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1366X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1366, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 85500000, 70, 143, 213, 3, 3, 24, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1366X768P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1366, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 72000000, 14, 56, 64, 1, 3, 28, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* SXGA+ resolutions */ +#define V4L2_DV_BT_DMT_1400X1050P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_HSYNC_POS_POL, \ + 101000000, 48, 32, 80, 3, 4, 23, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1400X1050P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 121750000, 88, 144, 232, 3, 4, 32, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1400X1050P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 156000000, 104, 144, 248, 3, 4, 42, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1400X1050P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 179500000, 104, 152, 256, 3, 4, 48, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1400X1050P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_HSYNC_POS_POL, \ + 208000000, 48, 32, 80, 3, 4, 55, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* WXGA+ resolutions */ +#define V4L2_DV_BT_DMT_1440X900P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_HSYNC_POS_POL, \ + 88750000, 48, 32, 80, 3, 6, 17, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1440X900P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_VSYNC_POS_POL, \ + 106500000, 80, 152, 232, 3, 6, 25, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1440X900P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_VSYNC_POS_POL, \ + 136750000, 96, 152, 248, 3, 6, 33, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1440X900P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_VSYNC_POS_POL, \ + 157000000, 104, 152, 256, 3, 6, 39, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1440X900P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_HSYNC_POS_POL, \ + 182750000, 48, 32, 80, 3, 6, 44, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1600X900P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 900, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 108000000, 24, 80, 96, 1, 3, 96, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* UXGA resolutions */ +#define V4L2_DV_BT_DMT_1600X1200P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 162000000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P65 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 175500000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P70 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 189000000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 202500000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 229500000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, V4L2_DV_HSYNC_POS_POL, \ + 268250000, 48, 32, 80, 3, 4, 64, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* WSXGA+ resolutions */ +#define V4L2_DV_BT_DMT_1680X1050P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_HSYNC_POS_POL, \ + 119000000, 48, 32, 80, 3, 6, 21, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1680X1050P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 146250000, 104, 176, 280, 3, 6, 30, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1680X1050P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 187000000, 120, 176, 296, 3, 6, 40, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1680X1050P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 214750000, 128, 176, 304, 3, 6, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1680X1050P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_HSYNC_POS_POL, \ + 245500000, 48, 32, 80, 3, 6, 53, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1792X1344P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1792, 1344, 0, V4L2_DV_VSYNC_POS_POL, \ + 204750000, 128, 200, 328, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1792X1344P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1792, 1344, 0, V4L2_DV_VSYNC_POS_POL, \ + 261000000, 96, 216, 352, 1, 3, 69, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1792X1344P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1792, 1344, 0, V4L2_DV_HSYNC_POS_POL, \ + 333250000, 48, 32, 80, 3, 4, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1856X1392P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1856, 1392, 0, V4L2_DV_VSYNC_POS_POL, \ + 218250000, 96, 224, 352, 1, 3, 43, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1856X1392P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1856, 1392, 0, V4L2_DV_VSYNC_POS_POL, \ + 288000000, 128, 224, 352, 1, 3, 104, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1856X1392P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1856, 1392, 0, V4L2_DV_HSYNC_POS_POL, \ + 356500000, 48, 32, 80, 3, 4, 75, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1920X1080P60 V4L2_DV_BT_CEA_1920X1080P60 + +/* WUXGA resolutions */ +#define V4L2_DV_BT_DMT_1920X1200P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_HSYNC_POS_POL, \ + 154000000, 48, 32, 80, 3, 6, 26, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1920X1200P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_VSYNC_POS_POL, \ + 193250000, 136, 200, 336, 3, 6, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1200P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_VSYNC_POS_POL, \ + 245250000, 136, 208, 344, 3, 6, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1200P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_VSYNC_POS_POL, \ + 281250000, 144, 208, 352, 3, 6, 53, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1200P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_HSYNC_POS_POL, \ + 317000000, 48, 32, 80, 3, 6, 62, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1920X1440P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1440, 0, V4L2_DV_VSYNC_POS_POL, \ + 234000000, 128, 208, 344, 1, 3, 56, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1440P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1440, 0, V4L2_DV_VSYNC_POS_POL, \ + 297000000, 144, 224, 352, 1, 3, 56, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1440P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1440, 0, V4L2_DV_HSYNC_POS_POL, \ + 380500000, 48, 32, 80, 3, 4, 78, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_2048X1152P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2048, 1152, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 162000000, 26, 80, 96, 1, 3, 44, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* WQXGA resolutions */ +#define V4L2_DV_BT_DMT_2560X1600P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_HSYNC_POS_POL, \ + 268500000, 48, 32, 80, 3, 6, 37, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_2560X1600P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_VSYNC_POS_POL, \ + 348500000, 192, 280, 472, 3, 6, 49, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_2560X1600P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_VSYNC_POS_POL, \ + 443250000, 208, 280, 488, 3, 6, 63, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_2560X1600P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_VSYNC_POS_POL, \ + 505250000, 208, 280, 488, 3, 6, 73, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_2560X1600P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_HSYNC_POS_POL, \ + 552750000, 48, 32, 80, 3, 6, 85, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1366X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1366, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 85500000, 70, 143, 213, 3, 3, 24, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#endif -- cgit v1.2.3 From eb8305b159f34abf9ac1349d61b4cf47578b13ab Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 15 May 2012 08:07:24 -0300 Subject: [media] tvp7002: add support for the new dv timings API Signed-off-by: Hans Verkuil Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tvp7002.c | 102 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 13 deletions(-) diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index 408b65e782e5..fb6a5b57eb83 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -328,6 +329,7 @@ static const struct i2c_reg_value tvp7002_parms_720P50[] = { /* Preset definition for handling device operation */ struct tvp7002_preset_definition { u32 preset; + struct v4l2_dv_timings timings; const struct i2c_reg_value *p_settings; enum v4l2_colorspace color_space; enum v4l2_field scanmode; @@ -341,6 +343,7 @@ struct tvp7002_preset_definition { static const struct tvp7002_preset_definition tvp7002_presets[] = { { V4L2_DV_720P60, + V4L2_DV_BT_CEA_1280X720P60, tvp7002_parms_720P60, V4L2_COLORSPACE_REC709, V4L2_FIELD_NONE, @@ -351,6 +354,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_1080I60, + V4L2_DV_BT_CEA_1920X1080I60, tvp7002_parms_1080I60, V4L2_COLORSPACE_REC709, V4L2_FIELD_INTERLACED, @@ -361,6 +365,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_1080I50, + V4L2_DV_BT_CEA_1920X1080I50, tvp7002_parms_1080I50, V4L2_COLORSPACE_REC709, V4L2_FIELD_INTERLACED, @@ -371,6 +376,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_720P50, + V4L2_DV_BT_CEA_1280X720P50, tvp7002_parms_720P50, V4L2_COLORSPACE_REC709, V4L2_FIELD_NONE, @@ -381,6 +387,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_1080P60, + V4L2_DV_BT_CEA_1920X1080P60, tvp7002_parms_1080P60, V4L2_COLORSPACE_REC709, V4L2_FIELD_NONE, @@ -391,6 +398,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_480P59_94, + V4L2_DV_BT_CEA_720X480P59_94, tvp7002_parms_480P, V4L2_COLORSPACE_SMPTE170M, V4L2_FIELD_NONE, @@ -401,6 +409,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_576P50, + V4L2_DV_BT_CEA_720X576P50, tvp7002_parms_576P, V4L2_COLORSPACE_SMPTE170M, V4L2_FIELD_NONE, @@ -605,6 +614,35 @@ static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, return -EINVAL; } +static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *dv_timings) +{ + struct tvp7002 *device = to_tvp7002(sd); + const struct v4l2_bt_timings *bt = &dv_timings->bt; + int i; + + if (dv_timings->type != V4L2_DV_BT_656_1120) + return -EINVAL; + for (i = 0; i < NUM_PRESETS; i++) { + const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt; + + if (!memcmp(bt, t, &bt->standards - &bt->width)) { + device->current_preset = &tvp7002_presets[i]; + return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); + } + } + return -EINVAL; +} + +static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *dv_timings) +{ + struct tvp7002 *device = to_tvp7002(sd); + + *dv_timings = device->current_preset->timings; + return 0; +} + /* * tvp7002_s_ctrl() - Set a control * @ctrl: ptr to v4l2_ctrl struct @@ -666,8 +704,7 @@ static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f * Returns the current DV preset by TVP7002. If no active input is * detected, returns -EINVAL */ -static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *qpreset) +static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) { const struct tvp7002_preset_definition *presets = tvp7002_presets; u8 progressive; @@ -678,10 +715,9 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, u8 lpf_msb; u8 cpl_lsb; u8 cpl_msb; - int index; - /* Return invalid preset if no active input is detected */ - qpreset->preset = V4L2_DV_INVALID; + /* Return invalid index if no active input is detected */ + *index = NUM_PRESETS; /* Read standards from device registers */ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); @@ -702,8 +738,8 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; /* Do checking of video modes */ - for (index = 0; index < NUM_PRESETS; index++, presets++) - if (lpfr == presets->lines_per_frame && + for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++) + if (lpfr == presets->lines_per_frame && progressive == presets->progressive) { if (presets->cpl_min == 0xffff) break; @@ -711,17 +747,42 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, break; } - if (index == NUM_PRESETS) { + if (*index == NUM_PRESETS) { v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", lpfr, cpln); - return 0; + return -ENOLINK; } - /* Set values in found preset */ - qpreset->preset = presets->preset; - /* Update lines per frame and clocks per line info */ - v4l2_dbg(1, debug, sd, "detected preset: %d\n", presets->preset); + v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index); + return 0; +} + +static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *qpreset) +{ + int index; + int err = tvp7002_query_dv(sd, &index); + + if (err || index == NUM_PRESETS) { + qpreset->preset = V4L2_DV_INVALID; + if (err == -ENOLINK) + err = 0; + return err; + } + qpreset->preset = tvp7002_presets[index].preset; + return 0; +} + +static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + int index; + int err = tvp7002_query_dv(sd, &index); + + if (err) + return err; + *timings = tvp7002_presets[index].timings; return 0; } @@ -891,6 +952,17 @@ static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); } +static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + /* Check requested format index is within range */ + if (timings->index >= NUM_PRESETS) + return -EINVAL; + + timings->timings = tvp7002_presets[timings->index].timings; + return 0; +} + static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { .s_ctrl = tvp7002_s_ctrl, }; @@ -917,6 +989,10 @@ static const struct v4l2_subdev_video_ops tvp7002_video_ops = { .enum_dv_presets = tvp7002_enum_dv_presets, .s_dv_preset = tvp7002_s_dv_preset, .query_dv_preset = tvp7002_query_dv_preset, + .g_dv_timings = tvp7002_g_dv_timings, + .s_dv_timings = tvp7002_s_dv_timings, + .enum_dv_timings = tvp7002_enum_dv_timings, + .query_dv_timings = tvp7002_query_dv_timings, .s_stream = tvp7002_s_stream, .g_mbus_fmt = tvp7002_mbus_fmt, .try_mbus_fmt = tvp7002_mbus_fmt, -- cgit v1.2.3 From f2b9e8acaa5555afac53f3d5490fa89506df37a2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 24 Apr 2012 10:30:15 -0300 Subject: [media] Feature removal: remove invalid DV presets Formats V4L2_DV_1080I25, V4L2_DV_1080I30 and V4L2_DV_1080I29_97 do not exist, so these presets are bogus. Remove them in 3.6. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index e4b57756b9f5..09701afc031a 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -542,6 +542,15 @@ Who: Sasikantha Babu ---------------------------- +What: remove bogus DV presets V4L2_DV_1080I29_97, V4L2_DV_1080I30 and + V4L2_DV_1080I25 +When: 3.6 +Why: These HDTV formats do not exist and were added by a confused mind + (that was me, to be precise...) +Who: Hans Verkuil + +---------------------------- + What: V4L2_CID_HCENTER, V4L2_CID_VCENTER V4L2 controls When: 3.7 Why: The V4L2_CID_VCENTER, V4L2_CID_HCENTER controls have been deprecated -- cgit v1.2.3 From 75916fd279323fd1b1636625a127838962677dfd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 May 2012 05:29:07 -0300 Subject: [media] V4L2: Mark the DV Preset API as deprecated The DV Preset API will be phased out in favor of the more flexible DV Timings API. Mark the preset API accordingly in the header and documentation. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/common.xml | 6 ++++-- Documentation/DocBook/media/v4l/compat.xml | 4 ++++ Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml | 6 ++++++ Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml | 4 ++++ include/linux/videodev2.h | 6 ++++++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Documentation/DocBook/media/v4l/common.xml b/Documentation/DocBook/media/v4l/common.xml index 81b7cf384a39..4101aeb56540 100644 --- a/Documentation/DocBook/media/v4l/common.xml +++ b/Documentation/DocBook/media/v4l/common.xml @@ -744,11 +744,13 @@ header can be used to get the timings of the formats in the deprecated). + These are IDs representing a video timing at the input/output. Presets are pre-defined timings implemented by the hardware according to video standards. A __u32 data type is used to represent a preset unlike the bit mask that is used in &v4l2-std-id; allowing future extensions -to support as many different presets as needed. +to support as many different presets as needed. This API is deprecated in favor of the DV Timings +API.
To enumerate and query the attributes of the DV timings supported by a device, diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index cd19d21085db..ea42ef824948 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2602,6 +2602,10 @@ interfaces and should not be implemented in new drivers. VIDIOC_S_MPEGCOMP ioctls. Use Extended Controls, . + + &VIDIOC-G-DV-PRESET;, &VIDIOC-S-DV-PRESET;, &VIDIOC-ENUM-DV-PRESETS; and + &VIDIOC-QUERY-DV-PRESET; ioctls. Use the DV Timings API (). + VIDIOC_SUBDEV_G_CROP and VIDIOC_SUBDEV_S_CROP ioctls. Use diff --git a/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml index 7940c1149393..61be9fa3803a 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml @@ -48,6 +48,12 @@ Description + + These ioctls are deprecated. + New drivers and applications should use &VIDIOC-G-DV-TIMINGS; and &VIDIOC-S-DV-TIMINGS; + instead. + + To query and select the current DV preset, applications use the VIDIOC_G_DV_PRESET and VIDIOC_S_DV_PRESET ioctls which take a pointer to a &v4l2-dv-preset; type as argument. diff --git a/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml index 23b17f604211..1bc8aeb3ff1f 100644 --- a/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml +++ b/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml @@ -49,6 +49,10 @@ input Description + This ioctl is deprecated. + New drivers and applications should use &VIDIOC-QUERY-DV-TIMINGS; instead. + + The hardware may be able to detect the current DV preset automatically, similar to sensing the video standard. To do so, applications call VIDIOC_QUERY_DV_PRESET with a pointer to a diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 13d84ed9d3a8..370d11106c11 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -944,6 +944,9 @@ struct v4l2_standard { __u32 reserved[4]; }; +/* The DV Preset API is deprecated in favor of the DV Timings API. + New drivers shouldn't use this anymore! */ + /* * V I D E O T I M I N G S D V P R E S E T */ @@ -2608,6 +2611,9 @@ struct v4l2_create_buffers { #endif #define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek) + +/* These four DV Preset ioctls are deprecated in favor of the DV Timings + ioctls. */ #define VIDIOC_ENUM_DV_PRESETS _IOWR('V', 83, struct v4l2_dv_enum_preset) #define VIDIOC_S_DV_PRESET _IOWR('V', 84, struct v4l2_dv_preset) #define VIDIOC_G_DV_PRESET _IOWR('V', 85, struct v4l2_dv_preset) -- cgit v1.2.3 From 5fa1a89d37efa35b80d51a70b89aaa8cf54ab180 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 8 May 2012 15:12:41 -0300 Subject: [media] bw-qcam: update to latest frameworks Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bw-qcam.c | 132 ++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 70 deletions(-) diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index f09df9dffaae..2520219f01ba 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -77,6 +77,9 @@ OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include +#include /* One from column A... */ #define QC_NOTSET 0 @@ -103,6 +106,7 @@ OTHER DEALINGS IN THE SOFTWARE. struct qcam { struct v4l2_device v4l2_dev; struct video_device vdev; + struct v4l2_ctrl_handler hdl; struct pardevice *pdev; struct parport *pport; struct mutex lock; @@ -646,7 +650,8 @@ static int qcam_querycap(struct file *file, void *priv, strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "B&W Quickcam", sizeof(vcap->card)); strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -674,72 +679,6 @@ static int qcam_s_input(struct file *file, void *fh, unsigned int inp) return (inp > 0) ? -EINVAL : 0; } -static int qcam_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 180); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192); - case V4L2_CID_GAMMA: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 105); - } - return -EINVAL; -} - -static int qcam_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct qcam *qcam = video_drvdata(file); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = qcam->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = qcam->contrast; - break; - case V4L2_CID_GAMMA: - ctrl->value = qcam->whitebal; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int qcam_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct qcam *qcam = video_drvdata(file); - int ret = 0; - - mutex_lock(&qcam->lock); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - qcam->brightness = ctrl->value; - break; - case V4L2_CID_CONTRAST: - qcam->contrast = ctrl->value; - break; - case V4L2_CID_GAMMA: - qcam->whitebal = ctrl->value; - break; - default: - ret = -EINVAL; - break; - } - if (ret == 0) { - qc_setscanmode(qcam); - qcam->status |= QC_PARAM_CHANGE; - } - mutex_unlock(&qcam->lock); - return ret; -} - static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct qcam *qcam = video_drvdata(file); @@ -856,8 +795,40 @@ static ssize_t qcam_read(struct file *file, char __user *buf, return len; } +static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct qcam *qcam = + container_of(ctrl->handler, struct qcam, hdl); + int ret = 0; + + mutex_lock(&qcam->lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + qcam->brightness = ctrl->val; + break; + case V4L2_CID_CONTRAST: + qcam->contrast = ctrl->val; + break; + case V4L2_CID_GAMMA: + qcam->whitebal = ctrl->val; + break; + default: + ret = -EINVAL; + break; + } + if (ret == 0) { + qc_setscanmode(qcam); + qcam->status |= QC_PARAM_CHANGE; + } + mutex_unlock(&qcam->lock); + return ret; +} + static const struct v4l2_file_operations qcam_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, .read = qcam_read, }; @@ -867,13 +838,17 @@ static const struct v4l2_ioctl_ops qcam_ioctl_ops = { .vidioc_g_input = qcam_g_input, .vidioc_s_input = qcam_s_input, .vidioc_enum_input = qcam_enum_input, - .vidioc_queryctrl = qcam_queryctrl, - .vidioc_g_ctrl = qcam_g_ctrl, - .vidioc_s_ctrl = qcam_s_ctrl, .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_ctrl_ops qcam_ctrl_ops = { + .s_ctrl = qcam_s_ctrl, }; /* Initialize the QuickCam driver control structure. This is where @@ -897,19 +872,35 @@ static struct qcam *qcam_init(struct parport *port) return NULL; } + v4l2_ctrl_handler_init(&qcam->hdl, 3); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 180); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 192); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_GAMMA, 0, 255, 1, 105); + if (qcam->hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + v4l2_ctrl_handler_free(&qcam->hdl); + kfree(qcam); + return NULL; + } qcam->pport = port; qcam->pdev = parport_register_device(port, "bw-qcam", NULL, NULL, NULL, 0, NULL); if (qcam->pdev == NULL) { v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); + v4l2_ctrl_handler_free(&qcam->hdl); kfree(qcam); return NULL; } strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name)); qcam->vdev.v4l2_dev = v4l2_dev; + qcam->vdev.ctrl_handler = &qcam->hdl; qcam->vdev.fops = &qcam_fops; qcam->vdev.ioctl_ops = &qcam_ioctl_ops; + set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); qcam->vdev.release = video_device_release_empty; video_set_drvdata(&qcam->vdev, qcam); @@ -1003,6 +994,7 @@ static int init_bwqcam(struct parport *port) static void close_bwqcam(struct qcam *qcam) { video_unregister_device(&qcam->vdev); + v4l2_ctrl_handler_free(&qcam->hdl); parport_unregister_device(qcam->pdev); kfree(qcam); } -- cgit v1.2.3 From 34caed8b3a1ec07a5b8fde42b7c4784aa2fb9440 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 8 May 2012 15:20:21 -0300 Subject: [media] c-qcam: convert to the latest frameworks Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/c-qcam.c | 137 ++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 72 deletions(-) diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index 73c65c2bf178..ec51e1f12e82 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -40,10 +40,14 @@ #include #include #include +#include +#include +#include struct qcam { struct v4l2_device v4l2_dev; struct video_device vdev; + struct v4l2_ctrl_handler hdl; struct pardevice *pdev; struct parport *pport; int width, height; @@ -515,7 +519,8 @@ static int qcam_querycap(struct file *file, void *priv, strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card)); strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -543,73 +548,6 @@ static int qcam_s_input(struct file *file, void *fh, unsigned int inp) return (inp > 0) ? -EINVAL : 0; } -static int qcam_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 240); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192); - case V4L2_CID_GAMMA: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - } - return -EINVAL; -} - -static int qcam_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct qcam *qcam = video_drvdata(file); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = qcam->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = qcam->contrast; - break; - case V4L2_CID_GAMMA: - ctrl->value = qcam->whitebal; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int qcam_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct qcam *qcam = video_drvdata(file); - int ret = 0; - - mutex_lock(&qcam->lock); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - qcam->brightness = ctrl->value; - break; - case V4L2_CID_CONTRAST: - qcam->contrast = ctrl->value; - break; - case V4L2_CID_GAMMA: - qcam->whitebal = ctrl->value; - break; - default: - ret = -EINVAL; - break; - } - if (ret == 0) { - parport_claim_or_block(qcam->pdev); - qc_setup(qcam); - parport_release(qcam->pdev); - } - mutex_unlock(&qcam->lock); - return ret; -} - static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct qcam *qcam = video_drvdata(file); @@ -713,8 +651,41 @@ static ssize_t qcam_read(struct file *file, char __user *buf, return len; } +static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct qcam *qcam = + container_of(ctrl->handler, struct qcam, hdl); + int ret = 0; + + mutex_lock(&qcam->lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + qcam->brightness = ctrl->val; + break; + case V4L2_CID_CONTRAST: + qcam->contrast = ctrl->val; + break; + case V4L2_CID_GAMMA: + qcam->whitebal = ctrl->val; + break; + default: + ret = -EINVAL; + break; + } + if (ret == 0) { + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + } + mutex_unlock(&qcam->lock); + return ret; +} + static const struct v4l2_file_operations qcam_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, .read = qcam_read, }; @@ -724,13 +695,17 @@ static const struct v4l2_ioctl_ops qcam_ioctl_ops = { .vidioc_g_input = qcam_g_input, .vidioc_s_input = qcam_s_input, .vidioc_enum_input = qcam_enum_input, - .vidioc_queryctrl = qcam_queryctrl, - .vidioc_g_ctrl = qcam_g_ctrl, - .vidioc_s_ctrl = qcam_s_ctrl, - .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_ctrl_ops qcam_ctrl_ops = { + .s_ctrl = qcam_s_ctrl, }; /* Initialize the QuickCam driver control structure. */ @@ -753,6 +728,20 @@ static struct qcam *qcam_init(struct parport *port) return NULL; } + v4l2_ctrl_handler_init(&qcam->hdl, 3); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 240); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 192); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_GAMMA, 0, 255, 1, 128); + if (qcam->hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + v4l2_ctrl_handler_free(&qcam->hdl); + kfree(qcam); + return NULL; + } + qcam->pport = port; qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL, NULL, 0, NULL); @@ -761,6 +750,7 @@ static struct qcam *qcam_init(struct parport *port) if (qcam->pdev == NULL) { v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); + v4l2_ctrl_handler_free(&qcam->hdl); kfree(qcam); return NULL; } @@ -770,6 +760,8 @@ static struct qcam *qcam_init(struct parport *port) qcam->vdev.fops = &qcam_fops; qcam->vdev.ioctl_ops = &qcam_ioctl_ops; qcam->vdev.release = video_device_release_empty; + qcam->vdev.ctrl_handler = &qcam->hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); video_set_drvdata(&qcam->vdev, qcam); mutex_init(&qcam->lock); @@ -844,6 +836,7 @@ static int init_cqcam(struct parport *port) static void close_cqcam(struct qcam *qcam) { video_unregister_device(&qcam->vdev); + v4l2_ctrl_handler_free(&qcam->hdl); parport_unregister_device(qcam->pdev); kfree(qcam); } -- cgit v1.2.3 From c551af62e506889e4c5611433cb72b5fe8157566 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 8 May 2012 16:50:33 -0300 Subject: [media] arv: use latest frameworks Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/arv.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c index b6ed44aebe30..e346d32d08ce 100644 --- a/drivers/media/video/arv.c +++ b/drivers/media/video/arv.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -403,7 +404,8 @@ static int ar_querycap(struct file *file, void *priv, strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card)); strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -709,6 +711,8 @@ static int ar_initialize(struct ar *ar) static const struct v4l2_file_operations ar_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, .read = ar_read, .unlocked_ioctl = video_ioctl2, }; @@ -769,6 +773,7 @@ static int __init ar_init(void) ar->vdev.fops = &ar_fops; ar->vdev.ioctl_ops = &ar_ioctl_ops; ar->vdev.release = video_device_release_empty; + set_bit(V4L2_FL_USE_FH_PRIO, &ar->vdev.flags); video_set_drvdata(&ar->vdev, ar); if (vga) { -- cgit v1.2.3 From 9d5934446aded013d55fcbf191d754667fa9d261 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 8 May 2012 17:00:43 -0300 Subject: [media] w9966: convert to the latest frameworks Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/w9966.c | 94 ++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 53 deletions(-) diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index 7fd7ac567e1a..db2a6003a1c3 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -62,6 +62,9 @@ #include #include #include +#include +#include +#include #include /*#define DEBUG*/ /* Undef me for production */ @@ -104,6 +107,7 @@ struct w9966 { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; unsigned char dev_state; unsigned char i2c_state; unsigned short ppmode; @@ -567,7 +571,8 @@ static int cam_querycap(struct file *file, void *priv, strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card)); strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -595,67 +600,25 @@ static int cam_s_input(struct file *file, void *fh, unsigned int inp) return (inp > 0) ? -EINVAL : 0; } -static int cam_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int cam_s_ctrl(struct v4l2_ctrl *ctrl) { - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - } - return -EINVAL; -} - -static int cam_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct w9966 *cam = video_drvdata(file); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = cam->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = cam->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = cam->color; - break; - case V4L2_CID_HUE: - ctrl->value = cam->hue; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int cam_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct w9966 *cam = video_drvdata(file); + struct w9966 *cam = + container_of(ctrl->handler, struct w9966, hdl); int ret = 0; mutex_lock(&cam->lock); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - cam->brightness = ctrl->value; + cam->brightness = ctrl->val; break; case V4L2_CID_CONTRAST: - cam->contrast = ctrl->value; + cam->contrast = ctrl->val; break; case V4L2_CID_SATURATION: - cam->color = ctrl->value; + cam->color = ctrl->val; break; case V4L2_CID_HUE: - cam->hue = ctrl->value; + cam->hue = ctrl->val; break; default: ret = -EINVAL; @@ -813,6 +776,9 @@ out: static const struct v4l2_file_operations w9966_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, .read = w9966_v4l_read, }; @@ -822,13 +788,17 @@ static const struct v4l2_ioctl_ops w9966_ioctl_ops = { .vidioc_g_input = cam_g_input, .vidioc_s_input = cam_s_input, .vidioc_enum_input = cam_enum_input, - .vidioc_queryctrl = cam_queryctrl, - .vidioc_g_ctrl = cam_g_ctrl, - .vidioc_s_ctrl = cam_s_ctrl, .vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_ctrl_ops cam_ctrl_ops = { + .s_ctrl = cam_s_ctrl, }; @@ -849,6 +819,20 @@ static int w9966_init(struct w9966 *cam, struct parport *port) v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); return -1; } + + v4l2_ctrl_handler_init(&cam->hdl, 4); + v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, + V4L2_CID_CONTRAST, -64, 64, 1, 64); + v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, + V4L2_CID_SATURATION, -64, 64, 1, 64); + v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + if (cam->hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + return -1; + } cam->pport = port; cam->brightness = 128; cam->contrast = 64; @@ -898,6 +882,8 @@ static int w9966_init(struct w9966 *cam, struct parport *port) cam->vdev.fops = &w9966_fops; cam->vdev.ioctl_ops = &w9966_ioctl_ops; cam->vdev.release = video_device_release_empty; + cam->vdev.ctrl_handler = &cam->hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); video_set_drvdata(&cam->vdev, cam); mutex_init(&cam->lock); @@ -923,6 +909,8 @@ static void w9966_term(struct w9966 *cam) w9966_set_state(cam, W9966_STATE_VDEV, 0); } + v4l2_ctrl_handler_free(&cam->hdl); + /* Terminate from IEEE1284 mode and release pdev block */ if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { w9966_pdev_claim(cam); -- cgit v1.2.3 From c1bf9c654a752b68be1b4408b33443a17515a671 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 21 Oct 2011 04:56:12 -0300 Subject: [media] v4l: s5p-tv: fix plane size calculation Fix plane size calculation. Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-tv/mixer_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index c0eadd75c9ac..cc1e9b9b53ba 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -853,7 +853,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, *nplanes = fmt->num_subframes; for (i = 0; i < fmt->num_subframes; ++i) { alloc_ctxs[i] = layer->mdev->alloc_ctx; - sizes[i] = PAGE_ALIGN(planes[i].sizeimage); + sizes[i] = planes[i].sizeimage; mxr_dbg(mdev, "size[%d] = %08lx\n", i, sizes[i]); } -- cgit v1.2.3 From c31e3c4b1e70ff6bdf0cf415be15c399c4742def Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Fri, 9 Mar 2012 07:07:28 -0300 Subject: [media] v4l: s5p-tv: mixer: fix compilation warning This patch fixes compilation warning in debug message. The warning is caused by incorrect 'unsigned' to 'unsigned long' conversion in dev_dbg. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-tv/mixer_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index cc1e9b9b53ba..fa7feb53ffd7 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -854,7 +854,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, for (i = 0; i < fmt->num_subframes; ++i) { alloc_ctxs[i] = layer->mdev->alloc_ctx; sizes[i] = planes[i].sizeimage; - mxr_dbg(mdev, "size[%d] = %08lx\n", i, sizes[i]); + mxr_dbg(mdev, "size[%d] = %08x\n", i, sizes[i]); } if (*nbuffers == 0) -- cgit v1.2.3 From 2470ea3f7f14283efa2f427884efc6634e39f243 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Fri, 3 Feb 2012 14:00:11 -0300 Subject: [media] v4l: s5p-tv: hdmiphy: add support for per-platform variants Adds selection of HDMIPHY configuration tables basing on both preset and platform variant. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-tv/hdmiphy_drv.c | 225 ++++++++++++++++++++++++++----- 1 file changed, 188 insertions(+), 37 deletions(-) diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c index 0afef77747e5..f67b38631801 100644 --- a/drivers/media/video/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c @@ -26,53 +26,188 @@ MODULE_DESCRIPTION("Samsung HDMI Physical interface driver"); MODULE_LICENSE("GPL"); struct hdmiphy_conf { - u32 preset; + unsigned long pixclk; const u8 *data; }; -static const u8 hdmiphy_conf27[32] = { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, - 0x6B, 0x10, 0x02, 0x51, 0xDf, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xe3, 0x26, 0x00, 0x00, 0x00, 0x00, +struct hdmiphy_ctx { + struct v4l2_subdev sd; + const struct hdmiphy_conf *conf_tab; }; -static const u8 hdmiphy_conf74_175[32] = { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B, - 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00, +static const struct hdmiphy_conf hdmiphy_conf_s5pv210[] = { + { .pixclk = 27000000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, + 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, } + }, + { .pixclk = 27027000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, + 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, } + }, + { .pixclk = 74176000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, + 0x6D, 0x10, 0x01, 0x52, 0xEF, 0xF3, 0x54, 0xB9, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, } + }, + { .pixclk = 74250000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, + 0x6A, 0x10, 0x01, 0x52, 0xFF, 0xF1, 0x54, 0xBA, + 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, } + }, + { /* end marker */ } }; -static const u8 hdmiphy_conf74_25[32] = { - 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40, - 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xe0, - 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00, +static const struct hdmiphy_conf hdmiphy_conf_exynos4210[] = { + { .pixclk = 27000000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, + 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, } + }, + { .pixclk = 27027000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, + 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, } + }, + { .pixclk = 74176000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, + 0x6D, 0x10, 0x01, 0x51, 0xEF, 0xF3, 0x54, 0xB9, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, } + }, + { .pixclk = 74250000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, + 0x6A, 0x10, 0x01, 0x51, 0xFF, 0xF1, 0x54, 0xBA, + 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, } + }, + { .pixclk = 148352000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, + 0x6D, 0x18, 0x00, 0x51, 0xEF, 0xF3, 0x54, 0xB9, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x11, 0x40, 0xA5, 0x26, 0x02, 0x00, 0x00, 0x00, } + }, + { .pixclk = 148500000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, + 0x6A, 0x18, 0x00, 0x51, 0xFF, 0xF1, 0x54, 0xBA, + 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x11, 0x40, 0xA4, 0x26, 0x02, 0x00, 0x00, 0x00, } + }, + { /* end marker */ } }; -static const u8 hdmiphy_conf148_5[32] = { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40, - 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00, +static const struct hdmiphy_conf hdmiphy_conf_exynos4212[] = { + { .pixclk = 27000000, .data = (u8 [32]) { + 0x01, 0x11, 0x2D, 0x75, 0x00, 0x01, 0x00, 0x08, + 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71, + 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 27027000, .data = (u8 [32]) { + 0x01, 0x91, 0x2D, 0x72, 0x00, 0x64, 0x12, 0x08, + 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71, + 0x54, 0xE2, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 74176000, .data = (u8 [32]) { + 0x01, 0x91, 0x3E, 0x35, 0x00, 0x5B, 0xDE, 0x08, + 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52, + 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 74250000, .data = (u8 [32]) { + 0x01, 0x91, 0x3E, 0x35, 0x00, 0x40, 0xF0, 0x08, + 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52, + 0x54, 0xA4, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 148500000, .data = (u8 [32]) { + 0x01, 0x91, 0x3E, 0x15, 0x00, 0x40, 0xF0, 0x08, + 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0xA4, + 0x54, 0x4A, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, } + }, + { /* end marker */ } }; -static const struct hdmiphy_conf hdmiphy_conf[] = { - { V4L2_DV_480P59_94, hdmiphy_conf27 }, - { V4L2_DV_1080P30, hdmiphy_conf74_175 }, - { V4L2_DV_720P59_94, hdmiphy_conf74_175 }, - { V4L2_DV_720P60, hdmiphy_conf74_25 }, - { V4L2_DV_1080P50, hdmiphy_conf148_5 }, - { V4L2_DV_1080P60, hdmiphy_conf148_5 }, +static const struct hdmiphy_conf hdmiphy_conf_exynos4412[] = { + { .pixclk = 27000000, .data = (u8 [32]) { + 0x01, 0x11, 0x2D, 0x75, 0x40, 0x01, 0x00, 0x08, + 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 27027000, .data = (u8 [32]) { + 0x01, 0x91, 0x2D, 0x72, 0x40, 0x64, 0x12, 0x08, + 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 74176000, .data = (u8 [32]) { + 0x01, 0x91, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0x08, + 0x81, 0x20, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 74250000, .data = (u8 [32]) { + 0x01, 0x91, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08, + 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 148500000, .data = (u8 [32]) { + 0x01, 0x91, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08, + 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0x4B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, } + }, + { /* end marker */ } }; -const u8 *hdmiphy_preset2conf(u32 preset) +static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd) +{ + return container_of(sd, struct hdmiphy_ctx, sd); +} + +static unsigned long hdmiphy_preset_to_pixclk(u32 preset) +{ + static const unsigned long pixclk[] = { + [V4L2_DV_480P59_94] = 27000000, + [V4L2_DV_576P50] = 27000000, + [V4L2_DV_720P59_94] = 74176000, + [V4L2_DV_720P50] = 74250000, + [V4L2_DV_720P60] = 74250000, + [V4L2_DV_1080P24] = 74250000, + [V4L2_DV_1080P30] = 74250000, + [V4L2_DV_1080I50] = 74250000, + [V4L2_DV_1080I60] = 74250000, + [V4L2_DV_1080P50] = 148500000, + [V4L2_DV_1080P60] = 148500000, + }; + if (preset < ARRAY_SIZE(pixclk)) + return pixclk[preset]; + else + return 0; +} + +static const u8 *hdmiphy_find_conf(u32 preset, const struct hdmiphy_conf *conf) { - int i; - for (i = 0; i < ARRAY_SIZE(hdmiphy_conf); ++i) - if (hdmiphy_conf[i].preset == preset) - return hdmiphy_conf[i].data; + unsigned long pixclk; + + pixclk = hdmiphy_preset_to_pixclk(preset); + if (!pixclk) + return NULL; + + for (; conf->pixclk; ++conf) + if (conf->pixclk == pixclk) + return conf->data; return NULL; } @@ -88,11 +223,12 @@ static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd, const u8 *data; u8 buffer[32]; int ret; + struct hdmiphy_ctx *ctx = sd_to_ctx(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset); - data = hdmiphy_preset2conf(preset->preset); + data = hdmiphy_find_conf(preset->preset, ctx->conf_tab); if (!data) { dev_err(dev, "format not supported\n"); return -EINVAL; @@ -146,21 +282,36 @@ static const struct v4l2_subdev_ops hdmiphy_ops = { static int __devinit hdmiphy_probe(struct i2c_client *client, const struct i2c_device_id *id) { - static struct v4l2_subdev sd; + struct hdmiphy_ctx *ctx; + + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data; + v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops); - v4l2_i2c_subdev_init(&sd, client, &hdmiphy_ops); dev_info(&client->dev, "probe successful\n"); return 0; } static int __devexit hdmiphy_remove(struct i2c_client *client) { + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hdmiphy_ctx *ctx = sd_to_ctx(sd); + + kfree(ctx); dev_info(&client->dev, "remove successful\n"); + return 0; } static const struct i2c_device_id hdmiphy_id[] = { - { "hdmiphy", 0 }, + { "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 }, + { "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 }, + { "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 }, + { "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 }, + { "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 }, { }, }; MODULE_DEVICE_TABLE(i2c, hdmiphy_id); -- cgit v1.2.3 From 3f468accf2ae2951dacebc04f186a08f495ce92a Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Fri, 3 Feb 2012 14:02:17 -0300 Subject: [media] v4l: s5p-tv: hdmi: parametrize DV timings This patch fixes timings configuration in HDMI register. It adds support for numerous new presets including interlaced ones. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-tv/hdmi_drv.c | 460 +++++++++++++++------------------ drivers/media/video/s5p-tv/regs-hdmi.h | 1 + 2 files changed, 207 insertions(+), 254 deletions(-) diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c index 4865d25a0e57..eefb903313c3 100644 --- a/drivers/media/video/s5p-tv/hdmi_drv.c +++ b/drivers/media/video/s5p-tv/hdmi_drv.c @@ -42,7 +42,23 @@ MODULE_DESCRIPTION("Samsung HDMI"); MODULE_LICENSE("GPL"); /* default preset configured on probe */ -#define HDMI_DEFAULT_PRESET V4L2_DV_1080P60 +#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94 + +struct hdmi_pulse { + u32 beg; + u32 end; +}; + +struct hdmi_timings { + struct hdmi_pulse hact; + u32 hsyn_pol; /* 0 - high, 1 - low */ + struct hdmi_pulse hsyn; + u32 interlaced; + struct hdmi_pulse vact[2]; + u32 vsyn_pol; /* 0 - high, 1 - low */ + u32 vsyn_off; + struct hdmi_pulse vsyn[2]; +}; struct hdmi_resources { struct clk *hdmi; @@ -70,64 +86,13 @@ struct hdmi_device { /** subdev of MHL interface */ struct v4l2_subdev *mhl_sd; /** configuration of current graphic mode */ - const struct hdmi_preset_conf *cur_conf; + const struct hdmi_timings *cur_conf; /** current preset */ u32 cur_preset; /** other resources */ struct hdmi_resources res; }; -struct hdmi_tg_regs { - u8 cmd; - u8 h_fsz_l; - u8 h_fsz_h; - u8 hact_st_l; - u8 hact_st_h; - u8 hact_sz_l; - u8 hact_sz_h; - u8 v_fsz_l; - u8 v_fsz_h; - u8 vsync_l; - u8 vsync_h; - u8 vsync2_l; - u8 vsync2_h; - u8 vact_st_l; - u8 vact_st_h; - u8 vact_sz_l; - u8 vact_sz_h; - u8 field_chg_l; - u8 field_chg_h; - u8 vact_st2_l; - u8 vact_st2_h; - u8 vsync_top_hdmi_l; - u8 vsync_top_hdmi_h; - u8 vsync_bot_hdmi_l; - u8 vsync_bot_hdmi_h; - u8 field_top_hdmi_l; - u8 field_top_hdmi_h; - u8 field_bot_hdmi_l; - u8 field_bot_hdmi_h; -}; - -struct hdmi_core_regs { - u8 h_blank[2]; - u8 v_blank[3]; - u8 h_v_line[3]; - u8 vsync_pol[1]; - u8 int_pro_mode[1]; - u8 v_blank_f[3]; - u8 h_sync_gen[3]; - u8 v_sync_gen1[3]; - u8 v_sync_gen2[3]; - u8 v_sync_gen3[3]; -}; - -struct hdmi_preset_conf { - struct hdmi_core_regs core; - struct hdmi_tg_regs tg; - struct v4l2_mbus_framefmt mbus_fmt; -}; - static struct platform_device_id hdmi_driver_types[] = { { .name = "s5pv210-hdmi", @@ -165,6 +130,21 @@ void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value) writeb(value, hdev->regs + reg_id); } +static inline +void hdmi_writebn(struct hdmi_device *hdev, u32 reg_id, int n, u32 value) +{ + switch (n) { + default: + writeb(value >> 24, hdev->regs + reg_id + 12); + case 3: + writeb(value >> 16, hdev->regs + reg_id + 8); + case 2: + writeb(value >> 8, hdev->regs + reg_id + 4); + case 1: + writeb(value >> 0, hdev->regs + reg_id + 0); + } +} + static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id) { return readl(hdev->regs + reg_id); @@ -211,72 +191,63 @@ static void hdmi_reg_init(struct hdmi_device *hdev) } static void hdmi_timing_apply(struct hdmi_device *hdev, - const struct hdmi_preset_conf *conf) + const struct hdmi_timings *t) { - const struct hdmi_core_regs *core = &conf->core; - const struct hdmi_tg_regs *tg = &conf->tg; - /* setting core registers */ - hdmi_writeb(hdev, HDMI_H_BLANK_0, core->h_blank[0]); - hdmi_writeb(hdev, HDMI_H_BLANK_1, core->h_blank[1]); - hdmi_writeb(hdev, HDMI_V_BLANK_0, core->v_blank[0]); - hdmi_writeb(hdev, HDMI_V_BLANK_1, core->v_blank[1]); - hdmi_writeb(hdev, HDMI_V_BLANK_2, core->v_blank[2]); - hdmi_writeb(hdev, HDMI_H_V_LINE_0, core->h_v_line[0]); - hdmi_writeb(hdev, HDMI_H_V_LINE_1, core->h_v_line[1]); - hdmi_writeb(hdev, HDMI_H_V_LINE_2, core->h_v_line[2]); - hdmi_writeb(hdev, HDMI_VSYNC_POL, core->vsync_pol[0]); - hdmi_writeb(hdev, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); - hdmi_writeb(hdev, HDMI_V_BLANK_F_0, core->v_blank_f[0]); - hdmi_writeb(hdev, HDMI_V_BLANK_F_1, core->v_blank_f[1]); - hdmi_writeb(hdev, HDMI_V_BLANK_F_2, core->v_blank_f[2]); - hdmi_writeb(hdev, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]); - hdmi_writeb(hdev, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]); - hdmi_writeb(hdev, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); + hdmi_writebn(hdev, HDMI_H_BLANK_0, 2, t->hact.beg); + hdmi_writebn(hdev, HDMI_H_SYNC_GEN_0, 3, + (t->hsyn_pol << 20) | (t->hsyn.end << 10) | t->hsyn.beg); + hdmi_writeb(hdev, HDMI_VSYNC_POL, t->vsyn_pol); + hdmi_writebn(hdev, HDMI_V_BLANK_0, 3, + (t->vact[0].beg << 11) | t->vact[0].end); + hdmi_writebn(hdev, HDMI_V_SYNC_GEN_1_0, 3, + (t->vsyn[0].beg << 12) | t->vsyn[0].end); + if (t->interlaced) { + u32 vsyn_trans = t->hsyn.beg + t->vsyn_off; + + hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 1); + hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3, + (t->hact.end << 12) | t->vact[1].end); + hdmi_writebn(hdev, HDMI_V_BLANK_F_0, 3, + (t->vact[1].end << 11) | t->vact[1].beg); + hdmi_writebn(hdev, HDMI_V_SYNC_GEN_2_0, 3, + (t->vsyn[1].beg << 12) | t->vsyn[1].end); + hdmi_writebn(hdev, HDMI_V_SYNC_GEN_3_0, 3, + (vsyn_trans << 12) | vsyn_trans); + } else { + hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 0); + hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3, + (t->hact.end << 12) | t->vact[0].end); + } + /* Timing generator registers */ - hdmi_writeb(hdev, HDMI_TG_H_FSZ_L, tg->h_fsz_l); - hdmi_writeb(hdev, HDMI_TG_H_FSZ_H, tg->h_fsz_h); - hdmi_writeb(hdev, HDMI_TG_HACT_ST_L, tg->hact_st_l); - hdmi_writeb(hdev, HDMI_TG_HACT_ST_H, tg->hact_st_h); - hdmi_writeb(hdev, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); - hdmi_writeb(hdev, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); - hdmi_writeb(hdev, HDMI_TG_V_FSZ_L, tg->v_fsz_l); - hdmi_writeb(hdev, HDMI_TG_V_FSZ_H, tg->v_fsz_h); - hdmi_writeb(hdev, HDMI_TG_VSYNC_L, tg->vsync_l); - hdmi_writeb(hdev, HDMI_TG_VSYNC_H, tg->vsync_h); - hdmi_writeb(hdev, HDMI_TG_VSYNC2_L, tg->vsync2_l); - hdmi_writeb(hdev, HDMI_TG_VSYNC2_H, tg->vsync2_h); - hdmi_writeb(hdev, HDMI_TG_VACT_ST_L, tg->vact_st_l); - hdmi_writeb(hdev, HDMI_TG_VACT_ST_H, tg->vact_st_h); - hdmi_writeb(hdev, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); - hdmi_writeb(hdev, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); - hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); - hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); - hdmi_writeb(hdev, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); - hdmi_writeb(hdev, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); - hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); - hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); - hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); - hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); - hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); - hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); - hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); - hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); + hdmi_writebn(hdev, HDMI_TG_H_FSZ_L, 2, t->hact.end); + hdmi_writebn(hdev, HDMI_TG_HACT_ST_L, 2, t->hact.beg); + hdmi_writebn(hdev, HDMI_TG_HACT_SZ_L, 2, t->hact.end - t->hact.beg); + hdmi_writebn(hdev, HDMI_TG_VSYNC_L, 2, t->vsyn[0].beg); + hdmi_writebn(hdev, HDMI_TG_VACT_ST_L, 2, t->vact[0].beg); + hdmi_writebn(hdev, HDMI_TG_VACT_SZ_L, 2, + t->vact[0].end - t->vact[0].beg); + hdmi_writebn(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, 2, t->vsyn[0].beg); + hdmi_writebn(hdev, HDMI_TG_FIELD_TOP_HDMI_L, 2, t->vsyn[0].beg); + if (t->interlaced) { + hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_FIELD_EN); + hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[1].end); + hdmi_writebn(hdev, HDMI_TG_VSYNC2_L, 2, t->vsyn[1].beg); + hdmi_writebn(hdev, HDMI_TG_FIELD_CHG_L, 2, t->vact[0].end); + hdmi_writebn(hdev, HDMI_TG_VACT_ST2_L, 2, t->vact[1].beg); + hdmi_writebn(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, 2, t->vsyn[1].beg); + hdmi_writebn(hdev, HDMI_TG_FIELD_BOT_HDMI_L, 2, t->vsyn[1].beg); + } else { + hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_FIELD_EN); + hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[0].end); + } } static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) { struct device *dev = hdmi_dev->dev; - const struct hdmi_preset_conf *conf = hdmi_dev->cur_conf; + const struct hdmi_timings *conf = hdmi_dev->cur_conf; struct v4l2_dv_preset preset; int ret; @@ -398,156 +369,126 @@ static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix) #undef DUMPREG } -static const struct hdmi_preset_conf hdmi_conf_480p = { - .core = { - .h_blank = {0x8a, 0x00}, - .v_blank = {0x0d, 0x6a, 0x01}, - .h_v_line = {0x0d, 0xa2, 0x35}, - .vsync_pol = {0x01}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, - .h_sync_gen = {0x0e, 0x30, 0x11}, - .v_sync_gen1 = {0x0f, 0x90, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x5a, 0x03, /* h_fsz */ - 0x8a, 0x00, 0xd0, 0x02, /* hact */ - 0x0d, 0x02, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0xe0, 0x01, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, - .mbus_fmt = { - .width = 720, - .height = 480, - .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, +static const struct hdmi_timings hdmi_timings_480p = { + .hact = { .beg = 138, .end = 858 }, + .hsyn_pol = 1, + .hsyn = { .beg = 16, .end = 16 + 62 }, + .interlaced = 0, + .vact[0] = { .beg = 42 + 3, .end = 522 + 3 }, + .vsyn_pol = 1, + .vsyn[0] = { .beg = 6 + 3, .end = 12 + 3}, +}; + +static const struct hdmi_timings hdmi_timings_576p50 = { + .hact = { .beg = 144, .end = 864 }, + .hsyn_pol = 1, + .hsyn = { .beg = 12, .end = 12 + 64 }, + .interlaced = 0, + .vact[0] = { .beg = 44 + 5, .end = 620 + 5 }, + .vsyn_pol = 1, + .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, +}; + +static const struct hdmi_timings hdmi_timings_720p60 = { + .hact = { .beg = 370, .end = 1650 }, + .hsyn_pol = 0, + .hsyn = { .beg = 110, .end = 110 + 40 }, + .interlaced = 0, + .vact[0] = { .beg = 25 + 5, .end = 745 + 5 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, +}; + +static const struct hdmi_timings hdmi_timings_720p50 = { + .hact = { .beg = 700, .end = 1980 }, + .hsyn_pol = 0, + .hsyn = { .beg = 440, .end = 440 + 40 }, + .interlaced = 0, + .vact[0] = { .beg = 25 + 5, .end = 745 + 5 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, +}; + +static const struct hdmi_timings hdmi_timings_1080p24 = { + .hact = { .beg = 830, .end = 2750 }, + .hsyn_pol = 0, + .hsyn = { .beg = 638, .end = 638 + 44 }, + .interlaced = 0, + .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, +}; + +static const struct hdmi_timings hdmi_timings_1080p60 = { + .hact = { .beg = 280, .end = 2200 }, + .hsyn_pol = 0, + .hsyn = { .beg = 88, .end = 88 + 44 }, + .interlaced = 0, + .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, }; -static const struct hdmi_preset_conf hdmi_conf_720p60 = { - .core = { - .h_blank = {0x72, 0x01}, - .v_blank = {0xee, 0xf2, 0x00}, - .h_v_line = {0xee, 0x22, 0x67}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ - .h_sync_gen = {0x6c, 0x50, 0x02}, - .v_sync_gen1 = {0x0a, 0x50, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x72, 0x06, /* h_fsz */ - 0x72, 0x01, 0x00, 0x05, /* hact */ - 0xee, 0x02, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x1e, 0x00, 0xd0, 0x02, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, - .mbus_fmt = { - .width = 1280, - .height = 720, - .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, +static const struct hdmi_timings hdmi_timings_1080i60 = { + .hact = { .beg = 280, .end = 2200 }, + .hsyn_pol = 0, + .hsyn = { .beg = 88, .end = 88 + 44 }, + .interlaced = 1, + .vact[0] = { .beg = 20 + 2, .end = 560 + 2 }, + .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 }, + .vsyn_pol = 0, + .vsyn_off = 1100, + .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2}, + .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2}, }; -static const struct hdmi_preset_conf hdmi_conf_1080p50 = { - .core = { - .h_blank = {0xd0, 0x02}, - .v_blank = {0x65, 0x6c, 0x01}, - .h_v_line = {0x65, 0x04, 0xa5}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ - .h_sync_gen = {0x0e, 0xea, 0x08}, - .v_sync_gen1 = {0x09, 0x40, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x98, 0x08, /* h_fsz */ - 0x18, 0x01, 0x80, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0x38, 0x04, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, - .mbus_fmt = { - .width = 1920, - .height = 1080, - .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, +static const struct hdmi_timings hdmi_timings_1080i50 = { + .hact = { .beg = 720, .end = 2640 }, + .hsyn_pol = 0, + .hsyn = { .beg = 528, .end = 528 + 44 }, + .interlaced = 1, + .vact[0] = { .beg = 20 + 2, .end = 560 + 2 }, + .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 }, + .vsyn_pol = 0, + .vsyn_off = 1320, + .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2}, + .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2}, }; -static const struct hdmi_preset_conf hdmi_conf_1080p60 = { - .core = { - .h_blank = {0x18, 0x01}, - .v_blank = {0x65, 0x6c, 0x01}, - .h_v_line = {0x65, 0x84, 0x89}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ - .h_sync_gen = {0x56, 0x08, 0x02}, - .v_sync_gen1 = {0x09, 0x40, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x98, 0x08, /* h_fsz */ - 0x18, 0x01, 0x80, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0x38, 0x04, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, - .mbus_fmt = { - .width = 1920, - .height = 1080, - .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, +static const struct hdmi_timings hdmi_timings_1080p50 = { + .hact = { .beg = 720, .end = 2640 }, + .hsyn_pol = 0, + .hsyn = { .beg = 528, .end = 528 + 44 }, + .interlaced = 0, + .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, }; static const struct { u32 preset; - const struct hdmi_preset_conf *conf; -} hdmi_conf[] = { - { V4L2_DV_480P59_94, &hdmi_conf_480p }, - { V4L2_DV_720P59_94, &hdmi_conf_720p60 }, - { V4L2_DV_1080P50, &hdmi_conf_1080p50 }, - { V4L2_DV_1080P30, &hdmi_conf_1080p60 }, - { V4L2_DV_1080P60, &hdmi_conf_1080p60 }, + const struct hdmi_timings *timings; +} hdmi_timings[] = { + { V4L2_DV_480P59_94, &hdmi_timings_480p }, + { V4L2_DV_576P50, &hdmi_timings_576p50 }, + { V4L2_DV_720P50, &hdmi_timings_720p50 }, + { V4L2_DV_720P59_94, &hdmi_timings_720p60 }, + { V4L2_DV_720P60, &hdmi_timings_720p60 }, + { V4L2_DV_1080P24, &hdmi_timings_1080p24 }, + { V4L2_DV_1080P30, &hdmi_timings_1080p60 }, + { V4L2_DV_1080P50, &hdmi_timings_1080p50 }, + { V4L2_DV_1080I50, &hdmi_timings_1080i50 }, + { V4L2_DV_1080I60, &hdmi_timings_1080i60 }, + { V4L2_DV_1080P60, &hdmi_timings_1080p60 }, }; -static const struct hdmi_preset_conf *hdmi_preset2conf(u32 preset) +static const struct hdmi_timings *hdmi_preset2timings(u32 preset) { int i; - for (i = 0; i < ARRAY_SIZE(hdmi_conf); ++i) - if (hdmi_conf[i].preset == preset) - return hdmi_conf[i].conf; + for (i = 0; i < ARRAY_SIZE(hdmi_timings); ++i) + if (hdmi_timings[i].preset == preset) + return hdmi_timings[i].timings; return NULL; } @@ -671,9 +612,9 @@ static int hdmi_s_dv_preset(struct v4l2_subdev *sd, { struct hdmi_device *hdev = sd_to_hdmi_dev(sd); struct device *dev = hdev->dev; - const struct hdmi_preset_conf *conf; + const struct hdmi_timings *conf; - conf = hdmi_preset2conf(preset->preset); + conf = hdmi_preset2timings(preset->preset); if (conf == NULL) { dev_err(dev, "preset (%u) not supported\n", preset->preset); return -EINVAL; @@ -695,21 +636,32 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) { struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - struct device *dev = hdev->dev; + const struct hdmi_timings *t = hdev->cur_conf; - dev_dbg(dev, "%s\n", __func__); + dev_dbg(hdev->dev, "%s\n", __func__); if (!hdev->cur_conf) return -EINVAL; - *fmt = hdev->cur_conf->mbus_fmt; + memset(fmt, 0, sizeof *fmt); + fmt->width = t->hact.end - t->hact.beg; + fmt->height = t->vact[0].end - t->vact[0].beg; + fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */ + fmt->colorspace = V4L2_COLORSPACE_SRGB; + if (t->interlaced) { + fmt->field = V4L2_FIELD_INTERLACED; + fmt->height *= 2; + } else { + fmt->field = V4L2_FIELD_NONE; + } return 0; } static int hdmi_enum_dv_presets(struct v4l2_subdev *sd, struct v4l2_dv_enum_preset *preset) { - if (preset->index >= ARRAY_SIZE(hdmi_conf)) + if (preset->index >= ARRAY_SIZE(hdmi_timings)) return -EINVAL; - return v4l_fill_dv_preset_info(hdmi_conf[preset->index].preset, preset); + return v4l_fill_dv_preset_info(hdmi_timings[preset->index].preset, + preset); } static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { @@ -993,7 +945,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev) strlcpy(sd->name, "s5p-hdmi", sizeof sd->name); hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; /* FIXME: missing fail preset is not supported */ - hdmi_dev->cur_conf = hdmi_preset2conf(hdmi_dev->cur_preset); + hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset); /* storing subdev for call that have only access to struct device */ dev_set_drvdata(dev, sd); diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/video/s5p-tv/regs-hdmi.h index 33247d13e4c0..a889d1f57f28 100644 --- a/drivers/media/video/s5p-tv/regs-hdmi.h +++ b/drivers/media/video/s5p-tv/regs-hdmi.h @@ -140,6 +140,7 @@ #define HDMI_MODE_MASK (3 << 0) /* HDMI_TG_CMD */ +#define HDMI_TG_FIELD_EN (1 << 1) #define HDMI_TG_EN (1 << 0) #endif /* SAMSUNG_REGS_HDMI_H */ -- cgit v1.2.3 From 2fd07a4a9e0d34fc53443601781ae58b2e8f1649 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Fri, 9 Mar 2012 08:05:24 -0300 Subject: [media] v4l: s5p-tv: hdmi: fix mode synchronization The mode setup was applied on HDMI hardware only on resume event. This caused problem if HDMI was not suspended between mode switches. This patch fixes this problem by setting a dirty flag on a mode change event. If flag is set, then new mode is applied on the next stream-on event. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-tv/hdmi_drv.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c index eefb903313c3..20cb6eef2979 100644 --- a/drivers/media/video/s5p-tv/hdmi_drv.c +++ b/drivers/media/video/s5p-tv/hdmi_drv.c @@ -87,6 +87,8 @@ struct hdmi_device { struct v4l2_subdev *mhl_sd; /** configuration of current graphic mode */ const struct hdmi_timings *cur_conf; + /** flag indicating that timings are dirty */ + int cur_conf_dirty; /** current preset */ u32 cur_preset; /** other resources */ @@ -253,6 +255,10 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) dev_dbg(dev, "%s\n", __func__); + /* skip if conf is already synchronized with HW */ + if (!hdmi_dev->cur_conf_dirty) + return 0; + /* reset hdmiphy */ hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); mdelay(10); @@ -278,6 +284,8 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) /* setting core registers */ hdmi_timing_apply(hdmi_dev, conf); + hdmi_dev->cur_conf_dirty = 0; + return 0; } @@ -500,6 +508,10 @@ static int hdmi_streamon(struct hdmi_device *hdev) dev_dbg(dev, "%s\n", __func__); + ret = hdmi_conf_apply(hdev); + if (ret) + return ret; + ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1); if (ret) return ret; @@ -620,6 +632,7 @@ static int hdmi_s_dv_preset(struct v4l2_subdev *sd, return -EINVAL; } hdev->cur_conf = conf; + hdev->cur_conf_dirty = 1; hdev->cur_preset = preset->preset; return 0; } @@ -689,6 +702,8 @@ static int hdmi_runtime_suspend(struct device *dev) dev_dbg(dev, "%s\n", __func__); v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0); hdmi_resource_poweroff(&hdev->res); + /* flag that device context is lost */ + hdev->cur_conf_dirty = 1; return 0; } @@ -702,10 +717,6 @@ static int hdmi_runtime_resume(struct device *dev) hdmi_resource_poweron(&hdev->res); - ret = hdmi_conf_apply(hdev); - if (ret) - goto fail; - /* starting MHL */ ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1); if (hdev->mhl_sd && ret) @@ -946,6 +957,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev) hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; /* FIXME: missing fail preset is not supported */ hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset); + hdmi_dev->cur_conf_dirty = 1; /* storing subdev for call that have only access to struct device */ dev_set_drvdata(dev, sd); -- cgit v1.2.3 From ef6a6ddc4d084774bac14df8b8867a42739cf142 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Fri, 9 Mar 2012 10:49:58 -0300 Subject: [media] v4l: s5p-tv: mixer: fix handling of interlaced modes The next frame was fetched by Mixer at every VSYNC event. This caused tearing when Mixer's output in interlaced mode. This patch fixes this bug by fetching new frame every second VSYNC when working in interlaced mode. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-tv/mixer.h | 1 + drivers/media/video/s5p-tv/mixer_reg.c | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h index 1597078c4a50..fa3e30f27447 100644 --- a/drivers/media/video/s5p-tv/mixer.h +++ b/drivers/media/video/s5p-tv/mixer.h @@ -226,6 +226,7 @@ struct mxr_resources { /* event flags used */ enum mxr_devide_flags { MXR_EVENT_VSYNC = 0, + MXR_EVENT_TOP = 1, }; /** drivers instance */ diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/video/s5p-tv/mixer_reg.c index 4800a3cbb297..3b1670a045f4 100644 --- a/drivers/media/video/s5p-tv/mixer_reg.c +++ b/drivers/media/video/s5p-tv/mixer_reg.c @@ -296,21 +296,25 @@ irqreturn_t mxr_irq_handler(int irq, void *dev_data) /* wake up process waiting for VSYNC */ if (val & MXR_INT_STATUS_VSYNC) { set_bit(MXR_EVENT_VSYNC, &mdev->event_flags); + /* toggle TOP field event if working in interlaced mode */ + if (~mxr_read(mdev, MXR_CFG) & MXR_CFG_SCAN_PROGRASSIVE) + change_bit(MXR_EVENT_TOP, &mdev->event_flags); wake_up(&mdev->event_queue); - } - - /* clear interrupts */ - if (~val & MXR_INT_EN_VSYNC) { /* vsync interrupt use different bit for read and clear */ - val &= ~MXR_INT_EN_VSYNC; + val &= ~MXR_INT_STATUS_VSYNC; val |= MXR_INT_CLEAR_VSYNC; } + + /* clear interrupts */ mxr_write(mdev, MXR_INT_STATUS, val); spin_unlock(&mdev->reg_slock); /* leave on non-vsync event */ if (~val & MXR_INT_CLEAR_VSYNC) return IRQ_HANDLED; + /* skip layer update on bottom field */ + if (!test_bit(MXR_EVENT_TOP, &mdev->event_flags)) + return IRQ_HANDLED; for (i = 0; i < MXR_MAX_LAYERS; ++i) mxr_irq_layer_handle(mdev->layer[i]); return IRQ_HANDLED; @@ -333,6 +337,7 @@ void mxr_reg_streamon(struct mxr_device *mdev) /* start MIXER */ mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_REG_RUN); + set_bit(MXR_EVENT_TOP, &mdev->event_flags); spin_unlock_irqrestore(&mdev->reg_slock, flags); } -- cgit v1.2.3 From e6364a987413b9e5413b48e9d31ecdd2b02bb28b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 12 Mar 2012 03:13:34 -0300 Subject: [media] v4l: s5p-tv: Fix section mismatch warning in mixer_video.c The function __devinit mxr_probe() references a function __devexit mxr_release_video(). Since mxr_release_video() is referenced outside the exit section, the following compilation warning is generated which is fixed here: WARNING: drivers/media/video/s5p-tv/s5p-mixer.o(.devinit.text+0x340): Section mismatch in reference from the function mxr_probe() to the function devexit.text:mxr_release_video() Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-tv/mixer.h | 2 +- drivers/media/video/s5p-tv/mixer_video.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h index fa3e30f27447..ddb422e23550 100644 --- a/drivers/media/video/s5p-tv/mixer.h +++ b/drivers/media/video/s5p-tv/mixer.h @@ -294,7 +294,7 @@ int __devinit mxr_acquire_video(struct mxr_device *mdev, struct mxr_output_conf *output_cont, int output_count); /** releasing common video resources */ -void __devexit mxr_release_video(struct mxr_device *mdev); +void mxr_release_video(struct mxr_device *mdev); struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx); struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx); diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index fa7feb53ffd7..33fde2a763ec 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -140,7 +140,7 @@ fail: return ret; } -void __devexit mxr_release_video(struct mxr_device *mdev) +void mxr_release_video(struct mxr_device *mdev) { int i; -- cgit v1.2.3 From 1259762facdcf31ea362997d9682ccc3d93d7346 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 10 May 2012 03:32:00 -0300 Subject: [media] s5p-mfc: Fix NULL pointer warnings Fixes the following type of warnings detected by sparse: warning: Using plain integer as NULL pointer. Signed-off-by: Sachin Kamat Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-mfc/s5p_mfc.c | 10 +++++----- drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c | 16 ++++++++-------- drivers/media/video/s5p-mfc/s5p_mfc_opr.c | 26 +++++++++++++------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index 76008549b3f1..9b7fe3375877 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -373,7 +373,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx, /* If no context is available then all necessary * processing has been done. */ - if (ctx == 0) + if (ctx == NULL) return; dev = ctx->dev; @@ -429,7 +429,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx, struct s5p_mfc_dev *dev; unsigned int guard_width, guard_height; - if (ctx == 0) + if (ctx == NULL) return; dev = ctx->dev; if (ctx->c_ops->post_seq_start) { @@ -496,7 +496,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, struct s5p_mfc_dev *dev; unsigned long flags; - if (ctx == 0) + if (ctx == NULL) return; dev = ctx->dev; s5p_mfc_clear_int_flags(dev); @@ -772,7 +772,7 @@ err_queue_init: err_init_hw: s5p_mfc_release_firmware(dev); err_alloc_fw: - dev->ctx[ctx->num] = 0; + dev->ctx[ctx->num] = NULL; del_timer_sync(&dev->watchdog_timer); s5p_mfc_clock_off(); err_pwr_enable: @@ -849,7 +849,7 @@ static int s5p_mfc_release(struct file *file) } mfc_debug(2, "Shutting down clock\n"); s5p_mfc_clock_off(); - dev->ctx[ctx->num] = 0; + dev->ctx[ctx->num] = NULL; s5p_mfc_dec_ctrls_delete(ctx); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c index f2481a85e0a2..08a5cfeaa59e 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c @@ -52,7 +52,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc( dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size); if (IS_ERR(s5p_mfc_bitproc_buf)) { - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; mfc_err("Allocating bitprocessor buffer failed\n"); release_firmware(fw_blob); return -ENOMEM; @@ -63,7 +63,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) mfc_err("The base memory for bank 1 is not aligned to 128KB\n"); vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; release_firmware(fw_blob); return -EIO; } @@ -72,7 +72,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) mfc_err("Bitprocessor memory remap failed\n"); vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; release_firmware(fw_blob); return -EIO; } @@ -82,7 +82,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) if (IS_ERR(b_base)) { vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; mfc_err("Allocating bank2 base failed\n"); release_firmware(fw_blob); return -ENOMEM; @@ -94,7 +94,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) mfc_err("The base memory for bank 2 is not aligned to 128KB\n"); vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; release_firmware(fw_blob); return -EIO; } @@ -126,7 +126,7 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) release_firmware(fw_blob); return -ENOMEM; } - if (s5p_mfc_bitproc_buf == 0 || s5p_mfc_bitproc_phys == 0) { + if (s5p_mfc_bitproc_buf == NULL || s5p_mfc_bitproc_phys == 0) { mfc_err("MFC firmware is not allocated or was not mapped correctly\n"); release_firmware(fw_blob); return -EINVAL; @@ -146,9 +146,9 @@ int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev) if (!s5p_mfc_bitproc_buf) return -EINVAL; vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_virt = 0; + s5p_mfc_bitproc_virt = NULL; s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c index e08b21c50ebf..a8028297fc70 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c @@ -43,7 +43,7 @@ int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx) ctx->desc_buf = vb2_dma_contig_memops.alloc( dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], DESC_BUF_SIZE); if (IS_ERR_VALUE((int)ctx->desc_buf)) { - ctx->desc_buf = 0; + ctx->desc_buf = NULL; mfc_err("Allocating DESC buffer failed\n"); return -ENOMEM; } @@ -54,7 +54,7 @@ int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx) if (desc_virt == NULL) { vb2_dma_contig_memops.put(ctx->desc_buf); ctx->desc_phys = 0; - ctx->desc_buf = 0; + ctx->desc_buf = NULL; mfc_err("Remapping DESC buffer failed\n"); return -ENOMEM; } @@ -69,7 +69,7 @@ void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx) if (ctx->desc_phys) { vb2_dma_contig_memops.put(ctx->desc_buf); ctx->desc_phys = 0; - ctx->desc_buf = 0; + ctx->desc_buf = NULL; } } @@ -186,7 +186,7 @@ int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx) ctx->bank1_buf = vb2_dma_contig_memops.alloc( dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size); if (IS_ERR(ctx->bank1_buf)) { - ctx->bank1_buf = 0; + ctx->bank1_buf = NULL; printk(KERN_ERR "Buf alloc for decoding failed (port A)\n"); return -ENOMEM; @@ -200,7 +200,7 @@ int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx) ctx->bank2_buf = vb2_dma_contig_memops.alloc( dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size); if (IS_ERR(ctx->bank2_buf)) { - ctx->bank2_buf = 0; + ctx->bank2_buf = NULL; mfc_err("Buf alloc for decoding failed (port B)\n"); return -ENOMEM; } @@ -216,13 +216,13 @@ void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx) { if (ctx->bank1_buf) { vb2_dma_contig_memops.put(ctx->bank1_buf); - ctx->bank1_buf = 0; + ctx->bank1_buf = NULL; ctx->bank1_phys = 0; ctx->bank1_size = 0; } if (ctx->bank2_buf) { vb2_dma_contig_memops.put(ctx->bank2_buf); - ctx->bank2_buf = 0; + ctx->bank2_buf = NULL; ctx->bank2_phys = 0; ctx->bank2_size = 0; } @@ -244,7 +244,7 @@ int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx) if (IS_ERR(ctx->ctx_buf)) { mfc_err("Allocating context buffer failed\n"); ctx->ctx_phys = 0; - ctx->ctx_buf = 0; + ctx->ctx_buf = NULL; return -ENOMEM; } ctx->ctx_phys = s5p_mfc_mem_cookie( @@ -256,7 +256,7 @@ int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx) mfc_err("Remapping instance buffer failed\n"); vb2_dma_contig_memops.put(ctx->ctx_buf); ctx->ctx_phys = 0; - ctx->ctx_buf = 0; + ctx->ctx_buf = NULL; return -ENOMEM; } /* Zero content of the allocated memory */ @@ -265,7 +265,7 @@ int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx) if (s5p_mfc_init_shm(ctx) < 0) { vb2_dma_contig_memops.put(ctx->ctx_buf); ctx->ctx_phys = 0; - ctx->ctx_buf = 0; + ctx->ctx_buf = NULL; return -ENOMEM; } return 0; @@ -277,12 +277,12 @@ void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx) if (ctx->ctx_buf) { vb2_dma_contig_memops.put(ctx->ctx_buf); ctx->ctx_phys = 0; - ctx->ctx_buf = 0; + ctx->ctx_buf = NULL; } if (ctx->shm_alloc) { vb2_dma_contig_memops.put(ctx->shm_alloc); - ctx->shm_alloc = 0; - ctx->shm = 0; + ctx->shm_alloc = NULL; + ctx->shm = NULL; } } -- cgit v1.2.3 From a13bba4f35eee67552b0fcfb0790d9a0f5f5fd07 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 10 May 2012 03:32:01 -0300 Subject: [media] s5p-mfc: Add missing static storage class to silence warnings Fixes the following sparse warnings: drivers/media/video/s5p-mfc/s5p_mfc.c:73:6 warning: symbol 's5p_mfc_watchdog' was not declared. Should it be static? drivers/media/video/s5p-mfc/s5p_mfc_opr.c:299:6: warning: symbol 's5p_mfc_set_shared_buffer' was not declared. Should it be static? Signed-off-by: Sachin Kamat Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-mfc/s5p_mfc.c | 2 +- drivers/media/video/s5p-mfc/s5p_mfc_opr.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index 9b7fe3375877..6fc22256847b 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -70,7 +70,7 @@ static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason, wake_up(&dev->queue); } -void s5p_mfc_watchdog(unsigned long arg) +static void s5p_mfc_watchdog(unsigned long arg) { struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg; diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c index a8028297fc70..e6217cbfa4a3 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c @@ -296,7 +296,7 @@ void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx) } /* Set registers for shared buffer */ -void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; mfc_write(dev, ctx->shm_ofs, S5P_FIMV_SI_CH0_HOST_WR_ADR); -- cgit v1.2.3 From 2356877cb12969cac1572f195d541a2b49d7d78e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 10 May 2012 03:35:47 -0300 Subject: [media] s5p-g2d: Fix NULL pointer warnings in g2d.c file Fixes the following warnings detected by sparse: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-g2d/g2d.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index 02605cecfd65..059d93210670 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -546,11 +546,11 @@ static void job_abort(void *prv) struct g2d_dev *dev = ctx->dev; int ret; - if (dev->curr == 0) /* No job currently running */ + if (dev->curr == NULL) /* No job currently running */ return; ret = wait_event_timeout(dev->irq_queue, - dev->curr == 0, + dev->curr == NULL, msecs_to_jiffies(G2D_TIMEOUT)); } @@ -599,19 +599,19 @@ static irqreturn_t g2d_isr(int irq, void *prv) g2d_clear_int(dev); clk_disable(dev->gate); - BUG_ON(ctx == 0); + BUG_ON(ctx == NULL); src = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - BUG_ON(src == 0); - BUG_ON(dst == 0); + BUG_ON(src == NULL); + BUG_ON(dst == NULL); v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); - dev->curr = 0; + dev->curr = NULL; wake_up(&dev->irq_queue); return IRQ_HANDLED; } -- cgit v1.2.3 From ec76afe8970b5110fd6ef1797606f089aec2f134 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 10 May 2012 03:35:48 -0300 Subject: [media] s5p-g2d: Add missing static storage class in g2d.c file Fixes the following sparse warnings: drivers/media/video/s5p-g2d/g2d.c:68:18: warning: symbol 'def_frame' was not declared. Should it be static? drivers/media/video/s5p-g2d/g2d.c:80:16: warning: symbol 'find_fmt' was not declared. Should it be static? drivers/media/video/s5p-g2d/g2d.c:205:5: warning: symbol 'g2d_setup_ctrls' was not declared. Should it be static? Signed-off-by: Sachin Kamat Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-g2d/g2d.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index 059d93210670..2270b7c1425b 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -65,7 +65,7 @@ static struct g2d_fmt formats[] = { }; #define NUM_FORMATS ARRAY_SIZE(formats) -struct g2d_frame def_frame = { +static struct g2d_frame def_frame = { .width = DEFAULT_WIDTH, .height = DEFAULT_HEIGHT, .c_width = DEFAULT_WIDTH, @@ -77,7 +77,7 @@ struct g2d_frame def_frame = { .bottom = DEFAULT_HEIGHT, }; -struct g2d_fmt *find_fmt(struct v4l2_format *f) +static struct g2d_fmt *find_fmt(struct v4l2_format *f) { unsigned int i; for (i = 0; i < NUM_FORMATS; i++) { @@ -202,7 +202,7 @@ static const struct v4l2_ctrl_ops g2d_ctrl_ops = { .s_ctrl = g2d_s_ctrl, }; -int g2d_setup_ctrls(struct g2d_ctx *ctx) +static int g2d_setup_ctrls(struct g2d_ctx *ctx) { struct g2d_dev *dev = ctx->dev; -- cgit v1.2.3 From 9f3bd320fc5a08f7892424978ad8e542357759a2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 10 May 2012 03:38:40 -0300 Subject: [media] s5p-jpeg: Make s5p_jpeg_g_selection function static Makes the function s5p_jpeg_g_selection static (detected by sparse). Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-jpeg/jpeg-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index ecf7b0b04c78..2d7dd8ac73f3 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -813,7 +813,7 @@ static int s5p_jpeg_streamoff(struct file *file, void *priv, return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); } -int s5p_jpeg_g_selection(struct file *file, void *priv, +static int s5p_jpeg_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); -- cgit v1.2.3 From 3e9095d54c3712ffda70dc84f9273d4b5d24c0fd Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 11 May 2012 04:38:14 -0300 Subject: [media] s5p-mfc: Add missing static storage class in s5p_mfc_enc.c file Fixes the following sparse warnings: drivers/media/video/s5p-mfc/s5p_mfc_enc.c:1439:5: warning: symbol 'vidioc_s_parm' was not declared. Should it be static? drivers/media/video/s5p-mfc/s5p_mfc_enc.c:1455:5: warning: symbol 'vidioc_g_parm' was not declared. Should it be static? Signed-off-by: Sachin Kamat Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-mfc/s5p_mfc_enc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index dff9dc798795..acedb2004be3 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -1436,7 +1436,8 @@ static const struct v4l2_ctrl_ops s5p_mfc_enc_ctrl_ops = { .s_ctrl = s5p_mfc_enc_s_ctrl, }; -int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a) +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) { struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); @@ -1452,7 +1453,8 @@ int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a) return 0; } -int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *a) +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) { struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); -- cgit v1.2.3 From 32fced05ad9ef4736e4a89b2361cccf56227529b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 14 May 2012 06:31:24 -0300 Subject: [media] s5p-g2d: Use devm_* functions in g2d.c file devm_* functions are device managed functions and make error handling and cleanup simpler. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-g2d/g2d.c | 47 +++++++++------------------------------ drivers/media/video/s5p-g2d/g2d.h | 1 - 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index 2270b7c1425b..7c98ee7377ee 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -674,42 +674,27 @@ static int g2d_probe(struct platform_device *pdev) struct resource *res; int ret = 0; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; + spin_lock_init(&dev->ctrl_lock); mutex_init(&dev->mutex); atomic_set(&dev->num_inst, 0); init_waitqueue_head(&dev->irq_queue); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to find registers\n"); - ret = -ENOENT; - goto free_dev; - } - dev->res_regs = request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev)); - - if (!dev->res_regs) { - dev_err(&pdev->dev, "failed to obtain register region\n"); - ret = -ENOENT; - goto free_dev; - } - - dev->regs = ioremap(res->start, resource_size(res)); - if (!dev->regs) { - dev_err(&pdev->dev, "failed to map registers\n"); - ret = -ENOENT; - goto rel_res_regs; + dev->regs = devm_request_and_ioremap(&pdev->dev, res); + if (dev->regs == NULL) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; } dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); if (IS_ERR_OR_NULL(dev->clk)) { dev_err(&pdev->dev, "failed to get g2d clock\n"); - ret = -ENXIO; - goto unmap_regs; + return -ENXIO; } ret = clk_prepare(dev->clk); @@ -740,7 +725,8 @@ static int g2d_probe(struct platform_device *pdev) dev->irq = res->start; - ret = request_irq(dev->irq, g2d_isr, 0, pdev->name, dev); + ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr, + 0, pdev->name, dev); if (ret) { dev_err(&pdev->dev, "failed to install IRQ\n"); goto put_clk_gate; @@ -749,7 +735,7 @@ static int g2d_probe(struct platform_device *pdev) dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(dev->alloc_ctx)) { ret = PTR_ERR(dev->alloc_ctx); - goto rel_irq; + goto unprep_clk_gate; } ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); @@ -797,8 +783,6 @@ unreg_v4l2_dev: v4l2_device_unregister(&dev->v4l2_dev); alloc_ctx_cleanup: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); -rel_irq: - free_irq(dev->irq, dev); unprep_clk_gate: clk_unprepare(dev->gate); put_clk_gate: @@ -807,12 +791,7 @@ unprep_clk: clk_unprepare(dev->clk); put_clk: clk_put(dev->clk); -unmap_regs: - iounmap(dev->regs); -rel_res_regs: - release_resource(dev->res_regs); -free_dev: - kfree(dev); + return ret; } @@ -825,14 +804,10 @@ static int g2d_remove(struct platform_device *pdev) video_unregister_device(dev->vfd); v4l2_device_unregister(&dev->v4l2_dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); - free_irq(dev->irq, dev); clk_unprepare(dev->gate); clk_put(dev->gate); clk_unprepare(dev->clk); clk_put(dev->clk); - iounmap(dev->regs); - release_resource(dev->res_regs); - kfree(dev); return 0; } diff --git a/drivers/media/video/s5p-g2d/g2d.h b/drivers/media/video/s5p-g2d/g2d.h index 1b82065aeaef..6b765b0216c5 100644 --- a/drivers/media/video/s5p-g2d/g2d.h +++ b/drivers/media/video/s5p-g2d/g2d.h @@ -23,7 +23,6 @@ struct g2d_dev { spinlock_t ctrl_lock; atomic_t num_inst; struct vb2_alloc_ctx *alloc_ctx; - struct resource *res_regs; void __iomem *regs; struct clk *clk; struct clk *gate; -- cgit v1.2.3 From 5b58b95405aafbaf7362564dbcf6231836f1433c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 14 May 2012 06:33:34 -0300 Subject: [media] s5p-jpeg: Use devm_* functions in jpeg-core.c file devm_* functions are used to replace kzalloc, request_mem_region, ioremap and request_irq functions in probe call. With the usage of devm_* functions explicit freeing and unmapping is not required. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-jpeg/jpeg-core.c | 58 ++++++-------------------------- drivers/media/video/s5p-jpeg/jpeg-core.h | 2 -- 2 files changed, 10 insertions(+), 50 deletions(-) diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index 2d7dd8ac73f3..28b5225d94f5 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -1290,7 +1290,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) int ret; /* JPEG IP abstraction struct */ - jpeg = kzalloc(sizeof(struct s5p_jpeg), GFP_KERNEL); + jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL); if (!jpeg) return -ENOMEM; @@ -1300,43 +1300,25 @@ static int s5p_jpeg_probe(struct platform_device *pdev) /* memory-mapped registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "cannot find IO resource\n"); - ret = -ENOENT; - goto jpeg_alloc_rollback; - } - - jpeg->ioarea = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!jpeg->ioarea) { - dev_err(&pdev->dev, "cannot request IO\n"); - ret = -ENXIO; - goto jpeg_alloc_rollback; - } - jpeg->regs = ioremap(res->start, resource_size(res)); - if (!jpeg->regs) { - dev_err(&pdev->dev, "cannot map IO\n"); - ret = -ENXIO; - goto mem_region_rollback; + jpeg->regs = devm_request_and_ioremap(&pdev->dev, res); + if (jpeg->regs == NULL) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; } - dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", - jpeg->regs, jpeg->ioarea, res); - /* interrupt service routine registration */ jpeg->irq = ret = platform_get_irq(pdev, 0); if (ret < 0) { dev_err(&pdev->dev, "cannot find IRQ\n"); - goto ioremap_rollback; + return ret; } - ret = request_irq(jpeg->irq, s5p_jpeg_irq, 0, - dev_name(&pdev->dev), jpeg); - + ret = devm_request_irq(&pdev->dev, jpeg->irq, s5p_jpeg_irq, 0, + dev_name(&pdev->dev), jpeg); if (ret) { dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq); - goto ioremap_rollback; + return ret; } /* clocks */ @@ -1344,7 +1326,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) if (IS_ERR(jpeg->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); ret = PTR_ERR(jpeg->clk); - goto request_irq_rollback; + return ret; } dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk); clk_enable(jpeg->clk); @@ -1464,18 +1446,6 @@ clk_get_rollback: clk_disable(jpeg->clk); clk_put(jpeg->clk); -request_irq_rollback: - free_irq(jpeg->irq, jpeg); - -ioremap_rollback: - iounmap(jpeg->regs); - -mem_region_rollback: - release_resource(jpeg->ioarea); - release_mem_region(jpeg->ioarea->start, resource_size(jpeg->ioarea)); - -jpeg_alloc_rollback: - kfree(jpeg); return ret; } @@ -1496,14 +1466,6 @@ static int s5p_jpeg_remove(struct platform_device *pdev) clk_disable(jpeg->clk); clk_put(jpeg->clk); - free_irq(jpeg->irq, jpeg); - - iounmap(jpeg->regs); - - release_resource(jpeg->ioarea); - release_mem_region(jpeg->ioarea->start, resource_size(jpeg->ioarea)); - kfree(jpeg); - return 0; } diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.h b/drivers/media/video/s5p-jpeg/jpeg-core.h index 38d7367f7a6d..9d0cd2b76f61 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.h +++ b/drivers/media/video/s5p-jpeg/jpeg-core.h @@ -54,7 +54,6 @@ * @vfd_encoder: video device node for encoder mem2mem mode * @vfd_decoder: video device node for decoder mem2mem mode * @m2m_dev: v4l2 mem2mem device data - * @ioarea: JPEG IP memory region * @regs: JPEG IP registers mapping * @irq: JPEG IP irq * @clk: JPEG IP clock @@ -70,7 +69,6 @@ struct s5p_jpeg { struct video_device *vfd_decoder; struct v4l2_m2m_dev *m2m_dev; - struct resource *ioarea; void __iomem *regs; unsigned int irq; struct clk *clk; -- cgit v1.2.3 From d310f478e288ee7b2fa638339680460430e73e03 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 14 May 2012 08:22:27 -0300 Subject: [media] s5p-mfc: Use devm_* functions in s5p_mfc.c file devm_* functions are device managed functions and make error handling and cleanup simpler. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-mfc/s5p_mfc.c | 63 +++++++--------------------- drivers/media/video/s5p-mfc/s5p_mfc_common.h | 2 - 2 files changed, 14 insertions(+), 51 deletions(-) diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index 6fc22256847b..9bb68e7b5ae8 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -948,7 +948,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) int ret; pr_debug("%s++\n", __func__); - dev = kzalloc(sizeof *dev, GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof *dev, GFP_KERNEL); if (!dev) { dev_err(&pdev->dev, "Not enough memory for MFC device\n"); return -ENOMEM; @@ -959,49 +959,35 @@ static int s5p_mfc_probe(struct platform_device *pdev) dev->plat_dev = pdev; if (!dev->plat_dev) { dev_err(&pdev->dev, "No platform data specified\n"); - ret = -ENODEV; - goto err_dev; + return -ENODEV; } ret = s5p_mfc_init_pm(dev); if (ret < 0) { dev_err(&pdev->dev, "failed to get mfc clock source\n"); - goto err_clk; + return ret; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get memory region resource\n"); - ret = -ENOENT; - goto err_res; - } - dev->mfc_mem = request_mem_region(res->start, resource_size(res), - pdev->name); - if (dev->mfc_mem == NULL) { - dev_err(&pdev->dev, "failed to get memory region\n"); - ret = -ENOENT; - goto err_mem_reg; - } - dev->regs_base = ioremap(dev->mfc_mem->start, resource_size(dev->mfc_mem)); + dev->regs_base = devm_request_and_ioremap(&pdev->dev, res); if (dev->regs_base == NULL) { - dev_err(&pdev->dev, "failed to ioremap address region\n"); - ret = -ENOENT; - goto err_ioremap; + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { dev_err(&pdev->dev, "failed to get irq resource\n"); ret = -ENOENT; - goto err_get_res; + goto err_res; } dev->irq = res->start; - ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name, - dev); + ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq, + IRQF_DISABLED, pdev->name, dev); if (ret) { dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret); - goto err_req_irq; + goto err_res; } dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l", @@ -1009,20 +995,20 @@ static int s5p_mfc_probe(struct platform_device *pdev) if (!dev->mem_dev_l) { mfc_err("Mem child (L) device get failed\n"); ret = -ENODEV; - goto err_find_child; + goto err_res; } dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r", match_child); if (!dev->mem_dev_r) { mfc_err("Mem child (R) device get failed\n"); ret = -ENODEV; - goto err_find_child; + goto err_res; } dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) { ret = PTR_ERR(dev->alloc_ctx[0]); - goto err_mem_init_ctx_0; + goto err_res; } dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r); if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) { @@ -1116,22 +1102,9 @@ err_v4l2_dev_reg: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); err_mem_init_ctx_1: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); -err_mem_init_ctx_0: -err_find_child: - free_irq(dev->irq, dev); -err_req_irq: -err_get_res: - iounmap(dev->regs_base); - dev->regs_base = NULL; -err_ioremap: - release_resource(dev->mfc_mem); - kfree(dev->mfc_mem); -err_mem_reg: err_res: s5p_mfc_final_pm(dev); -err_clk: -err_dev: - kfree(dev); + pr_debug("%s-- with error\n", __func__); return ret; @@ -1154,15 +1127,7 @@ static int __devexit s5p_mfc_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); - free_irq(dev->irq, dev); - iounmap(dev->regs_base); - if (dev->mfc_mem) { - release_resource(dev->mfc_mem); - kfree(dev->mfc_mem); - dev->mfc_mem = NULL; - } s5p_mfc_final_pm(dev); - kfree(dev); return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h b/drivers/media/video/s5p-mfc/s5p_mfc_common.h index 91146fa622e4..bd5706a6bad1 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h @@ -185,7 +185,6 @@ struct s5p_mfc_pm { * @mem_dev_r: child device of the right memory bank (1) * @regs_base: base address of the MFC hw registers * @irq: irq resource - * @mfc_mem: MFC registers memory resource * @dec_ctrl_handler: control framework handler for decoding * @enc_ctrl_handler: control framework handler for encoding * @pm: power management control @@ -221,7 +220,6 @@ struct s5p_mfc_dev { struct device *mem_dev_r; void __iomem *regs_base; int irq; - struct resource *mfc_mem; struct v4l2_ctrl_handler dec_ctrl_handler; struct v4l2_ctrl_handler enc_ctrl_handler; struct s5p_mfc_pm pm; -- cgit v1.2.3 From 41df5bf088a10e54c0613ec4d7350b74d5ab8252 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 30 Apr 2012 12:10:58 -0300 Subject: [media] s5p-fimc: Avoid crash with null platform_data In commit "s5p-fimc: Handle sub-device interdependencies using deferred.." there was a check added for pdata->num_clients without first checking pdata against NULL. This causes a crash when platform_data is not set, which is a valid use case. Fix this regression by skipping the MIPI-CSIS subdev registration also when pdata is null. Reported-by: HeungJun Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-mdevice.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 75296a625a9d..f97ac02b8677 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -371,6 +371,8 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd) * Check if there is any sensor on the MIPI-CSI2 bus and * if not skip the s5p-csis module loading. */ + if (pdata == NULL) + return 0; for (i = 0; i < pdata->num_clients; i++) { if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) { ret = 1; -- cgit v1.2.3 From 97d974226575227ebafdf3ab009f0212d8a7e223 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 8 May 2012 15:51:24 -0300 Subject: [media] s5p-fimc: Move m2m node driver into separate file Virtually no functional changes, just code reordering. This let us to clearly separate all logical functions available in the driver: fimc capture, mem-to-mem, and later fimc-lite capture, ISP features, etc. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/Makefile | 2 +- drivers/media/video/s5p-fimc/fimc-capture.c | 68 ++- drivers/media/video/s5p-fimc/fimc-core.c | 859 +--------------------------- drivers/media/video/s5p-fimc/fimc-core.h | 10 +- drivers/media/video/s5p-fimc/fimc-m2m.c | 812 ++++++++++++++++++++++++++ 5 files changed, 894 insertions(+), 857 deletions(-) create mode 100644 drivers/media/video/s5p-fimc/fimc-m2m.c diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile index 33dec7f890e7..1da3c6ae7fa8 100644 --- a/drivers/media/video/s5p-fimc/Makefile +++ b/drivers/media/video/s5p-fimc/Makefile @@ -1,4 +1,4 @@ -s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o fimc-mdevice.o +s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o s5p-csis-objs := mipi-csis.o obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index be5e4e237297..0051d8161c6c 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -139,7 +139,7 @@ static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend) * spinlock held. It updates the camera pixel crop, rotation and * image flip in H/W. */ -int fimc_capture_config_update(struct fimc_ctx *ctx) +static int fimc_capture_config_update(struct fimc_ctx *ctx) { struct fimc_dev *fimc = ctx->fimc_dev; int ret; @@ -166,6 +166,70 @@ int fimc_capture_config_update(struct fimc_ctx *ctx) return ret; } +void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) +{ + struct fimc_vid_cap *cap = &fimc->vid_cap; + struct fimc_vid_buffer *v_buf; + struct timeval *tv; + struct timespec ts; + + if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { + wake_up(&fimc->irq_queue); + goto done; + } + + if (!list_empty(&cap->active_buf_q) && + test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) { + ktime_get_real_ts(&ts); + + v_buf = fimc_active_queue_pop(cap); + + tv = &v_buf->vb.v4l2_buf.timestamp; + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; + v_buf->vb.v4l2_buf.sequence = cap->frame_count++; + + vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); + } + + if (!list_empty(&cap->pending_buf_q)) { + + v_buf = fimc_pending_queue_pop(cap); + fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); + v_buf->index = cap->buf_index; + + /* Move the buffer to the capture active queue */ + fimc_active_queue_add(cap, v_buf); + + dbg("next frame: %d, done frame: %d", + fimc_hw_get_frame_index(fimc), v_buf->index); + + if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) + cap->buf_index = 0; + } + + if (cap->active_buf_cnt == 0) { + if (deq_buf) + clear_bit(ST_CAPT_RUN, &fimc->state); + + if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) + cap->buf_index = 0; + } else { + set_bit(ST_CAPT_RUN, &fimc->state); + } + + fimc_capture_config_update(cap->ctx); +done: + if (cap->active_buf_cnt == 1) { + fimc_deactivate_capture(fimc); + clear_bit(ST_CAPT_STREAM, &fimc->state); + } + + dbg("frame: %d, active_buf_cnt: %d", + fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); +} + + static int start_streaming(struct vb2_queue *q, unsigned int count) { struct fimc_ctx *ctx = q->drv_priv; @@ -1245,7 +1309,7 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, struct fimc_vid_buffer, list); vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg)); } - fimc_capture_irq_handler(fimc, true); + fimc_capture_irq_handler(fimc, 1); fimc_deactivate_capture(fimc); spin_unlock_irqrestore(&fimc->slock, irq_flags); } diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index f6b9060a0c0f..749db4deefcb 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1,5 +1,5 @@ /* - * Samsung S5P/EXYNOS4 SoC series camera interface (video postprocessor) driver + * Samsung S5P/EXYNOS4 SoC series FIMC (CAMIF) driver * * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. * Contact: Sylwester Nawrocki, @@ -187,12 +187,12 @@ static struct fimc_fmt fimc_formats[] = { }, }; -static unsigned int get_m2m_fmt_flags(unsigned int stream_type) +struct fimc_fmt * fimc_get_format(unsigned int index) { - if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return FMT_FLAGS_M2M_IN; - else - return FMT_FLAGS_M2M_OUT; + if (index >= ARRAY_SIZE(fimc_formats)) + return NULL; + + return &fimc_formats[index]; } int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, @@ -293,126 +293,9 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) return 0; } -static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) -{ - struct vb2_buffer *src_vb, *dst_vb; - - if (!ctx || !ctx->m2m_ctx) - return; - - src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - - if (src_vb && dst_vb) { - v4l2_m2m_buf_done(src_vb, vb_state); - v4l2_m2m_buf_done(dst_vb, vb_state); - v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, - ctx->m2m_ctx); - } -} - -/* Complete the transaction which has been scheduled for execution. */ -static int fimc_m2m_shutdown(struct fimc_ctx *ctx) -{ - struct fimc_dev *fimc = ctx->fimc_dev; - int ret; - - if (!fimc_m2m_pending(fimc)) - return 0; - - fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); - - ret = wait_event_timeout(fimc->irq_queue, - !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), - FIMC_SHUTDOWN_TIMEOUT); - - return ret == 0 ? -ETIMEDOUT : ret; -} - -static int start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct fimc_ctx *ctx = q->drv_priv; - int ret; - - ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev); - return ret > 0 ? 0 : ret; -} - -static int stop_streaming(struct vb2_queue *q) -{ - struct fimc_ctx *ctx = q->drv_priv; - int ret; - - ret = fimc_m2m_shutdown(ctx); - if (ret == -ETIMEDOUT) - fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); - - pm_runtime_put(&ctx->fimc_dev->pdev->dev); - return 0; -} - -void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final) -{ - struct fimc_vid_cap *cap = &fimc->vid_cap; - struct fimc_vid_buffer *v_buf; - struct timeval *tv; - struct timespec ts; - - if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { - wake_up(&fimc->irq_queue); - return; - } - - if (!list_empty(&cap->active_buf_q) && - test_bit(ST_CAPT_RUN, &fimc->state) && final) { - ktime_get_real_ts(&ts); - - v_buf = fimc_active_queue_pop(cap); - - tv = &v_buf->vb.v4l2_buf.timestamp; - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; - v_buf->vb.v4l2_buf.sequence = cap->frame_count++; - - vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); - } - - if (!list_empty(&cap->pending_buf_q)) { - - v_buf = fimc_pending_queue_pop(cap); - fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); - v_buf->index = cap->buf_index; - - /* Move the buffer to the capture active queue */ - fimc_active_queue_add(cap, v_buf); - - dbg("next frame: %d, done frame: %d", - fimc_hw_get_frame_index(fimc), v_buf->index); - - if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) - cap->buf_index = 0; - } - - if (cap->active_buf_cnt == 0) { - if (final) - clear_bit(ST_CAPT_RUN, &fimc->state); - - if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) - cap->buf_index = 0; - } else { - set_bit(ST_CAPT_RUN, &fimc->state); - } - - fimc_capture_config_update(cap->ctx); - - dbg("frame: %d, active_buf_cnt: %d", - fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); -} - static irqreturn_t fimc_irq_handler(int irq, void *priv) { struct fimc_dev *fimc = priv; - struct fimc_vid_cap *cap = &fimc->vid_cap; struct fimc_ctx *ctx; fimc_hw_clear_irq(fimc); @@ -437,12 +320,9 @@ static irqreturn_t fimc_irq_handler(int irq, void *priv) return IRQ_HANDLED; } } else if (test_bit(ST_CAPT_PEND, &fimc->state)) { - fimc_capture_irq_handler(fimc, - !test_bit(ST_CAPT_JPEG, &fimc->state)); - if (cap->active_buf_cnt == 1) { - fimc_deactivate_capture(fimc); - clear_bit(ST_CAPT_STREAM, &fimc->state); - } + int last_buf = test_bit(ST_CAPT_JPEG, &fimc->state) && + fimc->vid_cap.reqbufs_count == 1; + fimc_capture_irq_handler(fimc, !last_buf); } out: spin_unlock(&fimc->slock); @@ -582,154 +462,6 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); } -static void fimc_dma_run(void *priv) -{ - struct vb2_buffer *vb = NULL; - struct fimc_ctx *ctx = priv; - struct fimc_frame *sf, *df; - struct fimc_dev *fimc; - unsigned long flags; - u32 ret; - - if (WARN(!ctx, "null hardware context\n")) - return; - - fimc = ctx->fimc_dev; - spin_lock_irqsave(&fimc->slock, flags); - set_bit(ST_M2M_PEND, &fimc->state); - sf = &ctx->s_frame; - df = &ctx->d_frame; - - if (ctx->state & FIMC_PARAMS) { - /* Prepare the DMA offsets for scaler */ - fimc_prepare_dma_offset(ctx, sf); - fimc_prepare_dma_offset(ctx, df); - } - - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); - if (ret) - goto dma_unlock; - - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); - if (ret) - goto dma_unlock; - - /* Reconfigure hardware if the context has changed. */ - if (fimc->m2m.ctx != ctx) { - ctx->state |= FIMC_PARAMS; - fimc->m2m.ctx = ctx; - } - - if (ctx->state & FIMC_PARAMS) { - fimc_set_yuv_order(ctx); - fimc_hw_set_input_path(ctx); - fimc_hw_set_in_dma(ctx); - ret = fimc_set_scaler_info(ctx); - if (ret) - goto dma_unlock; - fimc_hw_set_prescaler(ctx); - fimc_hw_set_mainscaler(ctx); - fimc_hw_set_target_format(ctx); - fimc_hw_set_rotation(ctx); - fimc_hw_set_effect(ctx, false); - fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) - fimc_hw_set_rgb_alpha(ctx); - fimc_hw_set_output_path(ctx); - } - fimc_hw_set_input_addr(fimc, &sf->paddr); - fimc_hw_set_output_addr(fimc, &df->paddr, -1); - - fimc_activate_capture(ctx); - - ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | - FIMC_SRC_FMT | FIMC_DST_FMT); - fimc_hw_activate_input_dma(fimc, true); -dma_unlock: - spin_unlock_irqrestore(&fimc->slock, flags); -} - -static void fimc_job_abort(void *priv) -{ - fimc_m2m_shutdown(priv); -} - -static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, - unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - struct fimc_frame *f; - int i; - - f = ctx_get_frame(ctx, vq->type); - if (IS_ERR(f)) - return PTR_ERR(f); - /* - * Return number of non-contigous planes (plane buffers) - * depending on the configured color format. - */ - if (!f->fmt) - return -EINVAL; - - *num_planes = f->fmt->memplanes; - for (i = 0; i < f->fmt->memplanes; i++) { - sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8; - allocators[i] = ctx->fimc_dev->alloc_ctx; - } - return 0; -} - -static int fimc_buf_prepare(struct vb2_buffer *vb) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct fimc_frame *frame; - int i; - - frame = ctx_get_frame(ctx, vb->vb2_queue->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); - - for (i = 0; i < frame->fmt->memplanes; i++) - vb2_set_plane_payload(vb, i, frame->payload[i]); - - return 0; -} - -static void fimc_buf_queue(struct vb2_buffer *vb) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); - - if (ctx->m2m_ctx) - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); -} - -static void fimc_lock(struct vb2_queue *vq) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_lock(&ctx->fimc_dev->lock); -} - -static void fimc_unlock(struct vb2_queue *vq) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_unlock(&ctx->fimc_dev->lock); -} - -static struct vb2_ops fimc_qops = { - .queue_setup = fimc_queue_setup, - .buf_prepare = fimc_buf_prepare, - .buf_queue = fimc_buf_queue, - .wait_prepare = fimc_unlock, - .wait_finish = fimc_lock, - .stop_streaming = stop_streaming, - .start_streaming = start_streaming, -}; - /* * V4L2 controls handling */ @@ -877,39 +609,6 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) v4l2_ctrl_unlock(ctrl); } -/* - * V4L2 ioctl handlers - */ -static int fimc_m2m_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_dev *fimc = ctx->fimc_dev; - - strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); - strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); - cap->bus_info[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; - - return 0; -} - -static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct fimc_fmt *fmt; - - fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type), - f->index); - if (!fmt) - return -EINVAL; - - strncpy(f->description, fmt->name, sizeof(f->description) - 1); - f->pixelformat = fmt->fourcc; - return 0; -} - int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; @@ -988,18 +687,6 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, } } -static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_frame *frame = ctx_get_frame(ctx, f->type); - - if (IS_ERR(frame)) - return PTR_ERR(frame); - - return fimc_fill_format(frame, f); -} - /** * fimc_find_format - lookup fimc color format by fourcc or media bus format * @pixelformat: fourcc to match, ignored if null @@ -1032,534 +719,6 @@ struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, return def_fmt; } -static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) -{ - struct fimc_dev *fimc = ctx->fimc_dev; - struct samsung_fimc_variant *variant = fimc->variant; - struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct fimc_fmt *fmt; - u32 max_w, mod_x, mod_y; - - if (!IS_M2M(f->type)) - return -EINVAL; - - dbg("w: %d, h: %d", pix->width, pix->height); - - fmt = fimc_find_format(&pix->pixelformat, NULL, - get_m2m_fmt_flags(f->type), 0); - if (WARN(fmt == NULL, "Pixel format lookup failed")) - return -EINVAL; - - if (pix->field == V4L2_FIELD_ANY) - pix->field = V4L2_FIELD_NONE; - else if (pix->field != V4L2_FIELD_NONE) - return -EINVAL; - - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - max_w = variant->pix_limit->scaler_dis_w; - mod_x = ffs(variant->min_inp_pixsize) - 1; - } else { - max_w = variant->pix_limit->out_rot_dis_w; - mod_x = ffs(variant->min_out_pixsize) - 1; - } - - if (tiled_fmt(fmt)) { - mod_x = 6; /* 64 x 32 pixels tile */ - mod_y = 5; - } else { - if (variant->min_vsize_align == 1) - mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; - else - mod_y = ffs(variant->min_vsize_align) - 1; - } - - v4l_bound_align_image(&pix->width, 16, max_w, mod_x, - &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); - - fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); - return 0; -} - -static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return fimc_try_fmt_mplane(ctx, f); -} - -static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_dev *fimc = ctx->fimc_dev; - struct vb2_queue *vq; - struct fimc_frame *frame; - struct v4l2_pix_format_mplane *pix; - int i, ret = 0; - - ret = fimc_try_fmt_mplane(ctx, f); - if (ret) - return ret; - - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - - if (vb2_is_busy(vq)) { - v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type); - return -EBUSY; - } - - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - frame = &ctx->s_frame; - else - frame = &ctx->d_frame; - - pix = &f->fmt.pix_mp; - frame->fmt = fimc_find_format(&pix->pixelformat, NULL, - get_m2m_fmt_flags(f->type), 0); - if (!frame->fmt) - return -EINVAL; - - /* Update RGB Alpha control state and value range */ - fimc_alpha_ctrl_update(ctx); - - for (i = 0; i < frame->fmt->colplanes; i++) { - frame->payload[i] = - (pix->width * pix->height * frame->fmt->depth[i]) / 8; - } - - fimc_fill_frame(frame, f); - - ctx->scaler.enabled = 1; - - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); - else - fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); - - dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); - - return 0; -} - -static int fimc_m2m_reqbufs(struct file *file, void *fh, - struct v4l2_requestbuffers *reqbufs) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int fimc_m2m_querybuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_qbuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_dqbuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_streamon(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - /* The source and target color format need to be set */ - if (V4L2_TYPE_IS_OUTPUT(type)) { - if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx)) - return -EINVAL; - } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) { - return -EINVAL; - } - - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int fimc_m2m_streamoff(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} - -static int fimc_m2m_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *cr) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_frame *frame; - - frame = ctx_get_frame(ctx, cr->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); - - cr->bounds.left = 0; - cr->bounds.top = 0; - cr->bounds.width = frame->o_width; - cr->bounds.height = frame->o_height; - cr->defrect = cr->bounds; - - return 0; -} - -static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_frame *frame; - - frame = ctx_get_frame(ctx, cr->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); - - cr->c.left = frame->offs_h; - cr->c.top = frame->offs_v; - cr->c.width = frame->width; - cr->c.height = frame->height; - - return 0; -} - -static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) -{ - struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_frame *f; - u32 min_size, halign, depth = 0; - int i; - - if (cr->c.top < 0 || cr->c.left < 0) { - v4l2_err(fimc->m2m.vfd, - "doesn't support negative values for top & left\n"); - return -EINVAL; - } - if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - f = &ctx->d_frame; - else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - f = &ctx->s_frame; - else - return -EINVAL; - - min_size = (f == &ctx->s_frame) ? - fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; - - /* Get pixel alignment constraints. */ - if (fimc->variant->min_vsize_align == 1) - halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; - else - halign = ffs(fimc->variant->min_vsize_align) - 1; - - for (i = 0; i < f->fmt->colplanes; i++) - depth += f->fmt->depth[i]; - - v4l_bound_align_image(&cr->c.width, min_size, f->o_width, - ffs(min_size) - 1, - &cr->c.height, min_size, f->o_height, - halign, 64/(ALIGN(depth, 8))); - - /* adjust left/top if cropping rectangle is out of bounds */ - if (cr->c.left + cr->c.width > f->o_width) - cr->c.left = f->o_width - cr->c.width; - if (cr->c.top + cr->c.height > f->o_height) - cr->c.top = f->o_height - cr->c.height; - - cr->c.left = round_down(cr->c.left, min_size); - cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); - - dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", - cr->c.left, cr->c.top, cr->c.width, cr->c.height, - f->f_width, f->f_height); - - return 0; -} - -static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_frame *f; - int ret; - - ret = fimc_m2m_try_crop(ctx, cr); - if (ret) - return ret; - - f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? - &ctx->s_frame : &ctx->d_frame; - - /* Check to see if scaling ratio is within supported range */ - if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { - if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ret = fimc_check_scaler_ratio(ctx, cr->c.width, - cr->c.height, ctx->d_frame.width, - ctx->d_frame.height, ctx->rotation); - } else { - ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, - ctx->s_frame.height, cr->c.width, - cr->c.height, ctx->rotation); - } - if (ret) { - v4l2_err(fimc->m2m.vfd, "Out of scaler range\n"); - return -EINVAL; - } - } - - f->offs_h = cr->c.left; - f->offs_v = cr->c.top; - f->width = cr->c.width; - f->height = cr->c.height; - - fimc_ctx_state_set(FIMC_PARAMS, ctx); - - return 0; -} - -static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { - .vidioc_querycap = fimc_m2m_querycap, - - .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, - .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, - - .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, - .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, - - .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, - .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, - - .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, - .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, - - .vidioc_reqbufs = fimc_m2m_reqbufs, - .vidioc_querybuf = fimc_m2m_querybuf, - - .vidioc_qbuf = fimc_m2m_qbuf, - .vidioc_dqbuf = fimc_m2m_dqbuf, - - .vidioc_streamon = fimc_m2m_streamon, - .vidioc_streamoff = fimc_m2m_streamoff, - - .vidioc_g_crop = fimc_m2m_g_crop, - .vidioc_s_crop = fimc_m2m_s_crop, - .vidioc_cropcap = fimc_m2m_cropcap - -}; - -static int queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - struct fimc_ctx *ctx = priv; - int ret; - - memset(src_vq, 0, sizeof(*src_vq)); - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; - src_vq->drv_priv = ctx; - src_vq->ops = &fimc_qops; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - - ret = vb2_queue_init(src_vq); - if (ret) - return ret; - - memset(dst_vq, 0, sizeof(*dst_vq)); - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; - dst_vq->drv_priv = ctx; - dst_vq->ops = &fimc_qops; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - - return vb2_queue_init(dst_vq); -} - -static int fimc_m2m_open(struct file *file) -{ - struct fimc_dev *fimc = video_drvdata(file); - struct fimc_ctx *ctx; - int ret; - - dbg("pid: %d, state: 0x%lx, refcnt: %d", - task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); - - /* - * Return if the corresponding video capture node - * is already opened. - */ - if (fimc->vid_cap.refcnt > 0) - return -EBUSY; - - ctx = kzalloc(sizeof *ctx, GFP_KERNEL); - if (!ctx) - return -ENOMEM; - v4l2_fh_init(&ctx->fh, fimc->m2m.vfd); - ctx->fimc_dev = fimc; - - /* Default color format */ - ctx->s_frame.fmt = &fimc_formats[0]; - ctx->d_frame.fmt = &fimc_formats[0]; - - ret = fimc_ctrls_create(ctx); - if (ret) - goto error_fh; - - /* Use separate control handler per file handle */ - ctx->fh.ctrl_handler = &ctx->ctrl_handler; - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - - /* Setup the device context for memory-to-memory mode */ - ctx->state = FIMC_CTX_M2M; - ctx->flags = 0; - ctx->in_path = FIMC_DMA; - ctx->out_path = FIMC_DMA; - - ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - ret = PTR_ERR(ctx->m2m_ctx); - goto error_c; - } - - if (fimc->m2m.refcnt++ == 0) - set_bit(ST_M2M_RUN, &fimc->state); - return 0; - -error_c: - fimc_ctrls_delete(ctx); -error_fh: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - kfree(ctx); - return ret; -} - -static int fimc_m2m_release(struct file *file) -{ - struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - struct fimc_dev *fimc = ctx->fimc_dev; - - dbg("pid: %d, state: 0x%lx, refcnt= %d", - task_pid_nr(current), fimc->state, fimc->m2m.refcnt); - - v4l2_m2m_ctx_release(ctx->m2m_ctx); - fimc_ctrls_delete(ctx); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - - if (--fimc->m2m.refcnt <= 0) - clear_bit(ST_M2M_RUN, &fimc->state); - kfree(ctx); - return 0; -} - -static unsigned int fimc_m2m_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - - return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); -} - - -static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - - return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); -} - -static const struct v4l2_file_operations fimc_m2m_fops = { - .owner = THIS_MODULE, - .open = fimc_m2m_open, - .release = fimc_m2m_release, - .poll = fimc_m2m_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = fimc_m2m_mmap, -}; - -static struct v4l2_m2m_ops m2m_ops = { - .device_run = fimc_dma_run, - .job_abort = fimc_job_abort, -}; - -int fimc_register_m2m_device(struct fimc_dev *fimc, - struct v4l2_device *v4l2_dev) -{ - struct video_device *vfd; - struct platform_device *pdev; - int ret = 0; - - if (!fimc) - return -ENODEV; - - pdev = fimc->pdev; - fimc->v4l2_dev = v4l2_dev; - - vfd = video_device_alloc(); - if (!vfd) { - v4l2_err(v4l2_dev, "Failed to allocate video device\n"); - return -ENOMEM; - } - - vfd->fops = &fimc_m2m_fops; - vfd->ioctl_ops = &fimc_m2m_ioctl_ops; - vfd->v4l2_dev = v4l2_dev; - vfd->minor = -1; - vfd->release = video_device_release; - vfd->lock = &fimc->lock; - /* Locking in file operations other than ioctl should be done - by the driver, not the V4L2 core. - This driver needs auditing so that this flag can be removed. */ - set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); - - snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev)); - video_set_drvdata(vfd, fimc); - - fimc->m2m.vfd = vfd; - fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); - if (IS_ERR(fimc->m2m.m2m_dev)) { - v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); - ret = PTR_ERR(fimc->m2m.m2m_dev); - goto err_init; - } - - ret = media_entity_init(&vfd->entity, 0, NULL, 0); - if (!ret) - return 0; - - v4l2_m2m_release(fimc->m2m.m2m_dev); -err_init: - video_device_release(fimc->m2m.vfd); - return ret; -} - -void fimc_unregister_m2m_device(struct fimc_dev *fimc) -{ - if (!fimc) - return; - - if (fimc->m2m.m2m_dev) - v4l2_m2m_release(fimc->m2m.m2m_dev); - if (fimc->m2m.vfd) { - media_entity_cleanup(&fimc->m2m.vfd->entity); - /* Can also be called if video device wasn't registered */ - video_unregister_device(fimc->m2m.vfd); - } -} - static void fimc_clk_put(struct fimc_dev *fimc) { int i; diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 7afabb0e1907..288631a86e53 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -712,6 +712,7 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, struct v4l2_pix_format_mplane *pix); struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, unsigned int mask, int index); +struct fimc_fmt *fimc_get_format(unsigned int index); int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, int dw, int dh, int rotation); @@ -722,7 +723,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f); void fimc_set_yuv_order(struct fimc_ctx *ctx); void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f); -void fimc_capture_irq_handler(struct fimc_dev *fimc, bool done); +void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf); int fimc_register_m2m_device(struct fimc_dev *fimc, struct v4l2_device *v4l2_dev); @@ -730,19 +731,20 @@ void fimc_unregister_m2m_device(struct fimc_dev *fimc); int fimc_register_driver(void); void fimc_unregister_driver(void); +/* -----------------------------------------------------*/ +/* fimc-m2m.c */ +void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state); + /* -----------------------------------------------------*/ /* fimc-capture.c */ int fimc_register_capture_device(struct fimc_dev *fimc, struct v4l2_device *v4l2_dev); void fimc_unregister_capture_device(struct fimc_dev *fimc); int fimc_capture_ctrls_create(struct fimc_dev *fimc); -int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, - struct fimc_vid_buffer *fimc_vb); void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg); int fimc_capture_suspend(struct fimc_dev *fimc); int fimc_capture_resume(struct fimc_dev *fimc); -int fimc_capture_config_update(struct fimc_ctx *ctx); /* Locking: the caller holds fimc->slock */ static inline void fimc_activate_capture(struct fimc_ctx *ctx) diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c new file mode 100644 index 000000000000..9935cc4bd0cb --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -0,0 +1,812 @@ +/* + * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 2 of the License, + * or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-core.h" +#include "fimc-mdevice.h" + + +static unsigned int get_m2m_fmt_flags(unsigned int stream_type) +{ + if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return FMT_FLAGS_M2M_IN; + else + return FMT_FLAGS_M2M_OUT; +} + +void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) +{ + struct vb2_buffer *src_vb, *dst_vb; + + if (!ctx || !ctx->m2m_ctx) + return; + + src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + + if (src_vb && dst_vb) { + v4l2_m2m_buf_done(src_vb, vb_state); + v4l2_m2m_buf_done(dst_vb, vb_state); + v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, + ctx->m2m_ctx); + } +} + +/* Complete the transaction which has been scheduled for execution. */ +static int fimc_m2m_shutdown(struct fimc_ctx *ctx) +{ + struct fimc_dev *fimc = ctx->fimc_dev; + int ret; + + if (!fimc_m2m_pending(fimc)) + return 0; + + fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); + + ret = wait_event_timeout(fimc->irq_queue, + !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), + FIMC_SHUTDOWN_TIMEOUT); + + return ret == 0 ? -ETIMEDOUT : ret; +} + +static int start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct fimc_ctx *ctx = q->drv_priv; + int ret; + + ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev); + return ret > 0 ? 0 : ret; +} + +static int stop_streaming(struct vb2_queue *q) +{ + struct fimc_ctx *ctx = q->drv_priv; + int ret; + + ret = fimc_m2m_shutdown(ctx); + if (ret == -ETIMEDOUT) + fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); + + pm_runtime_put(&ctx->fimc_dev->pdev->dev); + return 0; +} + +static void fimc_device_run(void *priv) +{ + struct vb2_buffer *vb = NULL; + struct fimc_ctx *ctx = priv; + struct fimc_frame *sf, *df; + struct fimc_dev *fimc; + unsigned long flags; + u32 ret; + + if (WARN(!ctx, "Null context\n")) + return; + + fimc = ctx->fimc_dev; + spin_lock_irqsave(&fimc->slock, flags); + + set_bit(ST_M2M_PEND, &fimc->state); + sf = &ctx->s_frame; + df = &ctx->d_frame; + + if (ctx->state & FIMC_PARAMS) { + /* Prepare the DMA offsets for scaler */ + fimc_prepare_dma_offset(ctx, sf); + fimc_prepare_dma_offset(ctx, df); + } + + vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); + if (ret) + goto dma_unlock; + + vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); + if (ret) + goto dma_unlock; + + /* Reconfigure hardware if the context has changed. */ + if (fimc->m2m.ctx != ctx) { + ctx->state |= FIMC_PARAMS; + fimc->m2m.ctx = ctx; + } + + if (ctx->state & FIMC_PARAMS) { + fimc_set_yuv_order(ctx); + fimc_hw_set_input_path(ctx); + fimc_hw_set_in_dma(ctx); + ret = fimc_set_scaler_info(ctx); + if (ret) + goto dma_unlock; + fimc_hw_set_prescaler(ctx); + fimc_hw_set_mainscaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_hw_set_effect(ctx, false); + fimc_hw_set_out_dma(ctx); + if (fimc->variant->has_alpha) + fimc_hw_set_rgb_alpha(ctx); + fimc_hw_set_output_path(ctx); + } + fimc_hw_set_input_addr(fimc, &sf->paddr); + fimc_hw_set_output_addr(fimc, &df->paddr, -1); + + fimc_activate_capture(ctx); + ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | + FIMC_SRC_FMT | FIMC_DST_FMT); + fimc_hw_activate_input_dma(fimc, true); + +dma_unlock: + spin_unlock_irqrestore(&fimc->slock, flags); +} + +static void fimc_job_abort(void *priv) +{ + fimc_m2m_shutdown(priv); +} + +static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *allocators[]) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + struct fimc_frame *f; + int i; + + f = ctx_get_frame(ctx, vq->type); + if (IS_ERR(f)) + return PTR_ERR(f); + /* + * Return number of non-contigous planes (plane buffers) + * depending on the configured color format. + */ + if (!f->fmt) + return -EINVAL; + + *num_planes = f->fmt->memplanes; + for (i = 0; i < f->fmt->memplanes; i++) { + sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8; + allocators[i] = ctx->fimc_dev->alloc_ctx; + } + return 0; +} + +static int fimc_buf_prepare(struct vb2_buffer *vb) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct fimc_frame *frame; + int i; + + frame = ctx_get_frame(ctx, vb->vb2_queue->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + for (i = 0; i < frame->fmt->memplanes; i++) + vb2_set_plane_payload(vb, i, frame->payload[i]); + + return 0; +} + +static void fimc_buf_queue(struct vb2_buffer *vb) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); + + if (ctx->m2m_ctx) + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); +} + +static void fimc_lock(struct vb2_queue *vq) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + mutex_lock(&ctx->fimc_dev->lock); +} + +static void fimc_unlock(struct vb2_queue *vq) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + mutex_unlock(&ctx->fimc_dev->lock); +} + +static struct vb2_ops fimc_qops = { + .queue_setup = fimc_queue_setup, + .buf_prepare = fimc_buf_prepare, + .buf_queue = fimc_buf_queue, + .wait_prepare = fimc_unlock, + .wait_finish = fimc_lock, + .stop_streaming = stop_streaming, + .start_streaming = start_streaming, +}; + +/* + * V4L2 ioctl handlers + */ +static int fimc_m2m_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_dev *fimc = ctx->fimc_dev; + + strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); + strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->capabilities = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; + + return 0; +} + +static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct fimc_fmt *fmt; + + fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type), + f->index); + if (!fmt) + return -EINVAL; + + strncpy(f->description, fmt->name, sizeof(f->description) - 1); + f->pixelformat = fmt->fourcc; + return 0; +} + +static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_frame *frame = ctx_get_frame(ctx, f->type); + + if (IS_ERR(frame)) + return PTR_ERR(frame); + + return fimc_fill_format(frame, f); +} + +static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) +{ + struct fimc_dev *fimc = ctx->fimc_dev; + struct samsung_fimc_variant *variant = fimc->variant; + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct fimc_fmt *fmt; + u32 max_w, mod_x, mod_y; + + if (!IS_M2M(f->type)) + return -EINVAL; + + dbg("w: %d, h: %d", pix->width, pix->height); + + fmt = fimc_find_format(&pix->pixelformat, NULL, + get_m2m_fmt_flags(f->type), 0); + if (WARN(fmt == NULL, "Pixel format lookup failed")) + return -EINVAL; + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + max_w = variant->pix_limit->scaler_dis_w; + mod_x = ffs(variant->min_inp_pixsize) - 1; + } else { + max_w = variant->pix_limit->out_rot_dis_w; + mod_x = ffs(variant->min_out_pixsize) - 1; + } + + if (tiled_fmt(fmt)) { + mod_x = 6; /* 64 x 32 pixels tile */ + mod_y = 5; + } else { + if (variant->min_vsize_align == 1) + mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; + else + mod_y = ffs(variant->min_vsize_align) - 1; + } + + v4l_bound_align_image(&pix->width, 16, max_w, mod_x, + &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); + + fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); + return 0; +} + +static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return fimc_try_fmt_mplane(ctx, f); +} + +static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_dev *fimc = ctx->fimc_dev; + struct vb2_queue *vq; + struct fimc_frame *frame; + struct v4l2_pix_format_mplane *pix; + int i, ret = 0; + + ret = fimc_try_fmt_mplane(ctx, f); + if (ret) + return ret; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + + if (vb2_is_busy(vq)) { + v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type); + return -EBUSY; + } + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + frame = &ctx->s_frame; + else + frame = &ctx->d_frame; + + pix = &f->fmt.pix_mp; + frame->fmt = fimc_find_format(&pix->pixelformat, NULL, + get_m2m_fmt_flags(f->type), 0); + if (!frame->fmt) + return -EINVAL; + + /* Update RGB Alpha control state and value range */ + fimc_alpha_ctrl_update(ctx); + + for (i = 0; i < frame->fmt->colplanes; i++) { + frame->payload[i] = + (pix->width * pix->height * frame->fmt->depth[i]) / 8; + } + + fimc_fill_frame(frame, f); + + ctx->scaler.enabled = 1; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); + else + fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); + + dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); + + return 0; +} + +static int fimc_m2m_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *reqbufs) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); +} + +static int fimc_m2m_querybuf(struct file *file, void *fh, + struct v4l2_buffer *buf) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); +} + +static int fimc_m2m_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buf) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int fimc_m2m_dqbuf(struct file *file, void *fh, + struct v4l2_buffer *buf) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +static int fimc_m2m_streamon(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + /* The source and target color format need to be set */ + if (V4L2_TYPE_IS_OUTPUT(type)) { + if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx)) + return -EINVAL; + } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) { + return -EINVAL; + } + + return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); +} + +static int fimc_m2m_streamoff(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); +} + +static int fimc_m2m_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cr) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_frame *frame; + + frame = ctx_get_frame(ctx, cr->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + cr->bounds.left = 0; + cr->bounds.top = 0; + cr->bounds.width = frame->o_width; + cr->bounds.height = frame->o_height; + cr->defrect = cr->bounds; + + return 0; +} + +static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_frame *frame; + + frame = ctx_get_frame(ctx, cr->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + cr->c.left = frame->offs_h; + cr->c.top = frame->offs_v; + cr->c.width = frame->width; + cr->c.height = frame->height; + + return 0; +} + +static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) +{ + struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_frame *f; + u32 min_size, halign, depth = 0; + int i; + + if (cr->c.top < 0 || cr->c.left < 0) { + v4l2_err(fimc->m2m.vfd, + "doesn't support negative values for top & left\n"); + return -EINVAL; + } + if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + f = &ctx->d_frame; + else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + f = &ctx->s_frame; + else + return -EINVAL; + + min_size = (f == &ctx->s_frame) ? + fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; + + /* Get pixel alignment constraints. */ + if (fimc->variant->min_vsize_align == 1) + halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; + else + halign = ffs(fimc->variant->min_vsize_align) - 1; + + for (i = 0; i < f->fmt->colplanes; i++) + depth += f->fmt->depth[i]; + + v4l_bound_align_image(&cr->c.width, min_size, f->o_width, + ffs(min_size) - 1, + &cr->c.height, min_size, f->o_height, + halign, 64/(ALIGN(depth, 8))); + + /* adjust left/top if cropping rectangle is out of bounds */ + if (cr->c.left + cr->c.width > f->o_width) + cr->c.left = f->o_width - cr->c.width; + if (cr->c.top + cr->c.height > f->o_height) + cr->c.top = f->o_height - cr->c.height; + + cr->c.left = round_down(cr->c.left, min_size); + cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); + + dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", + cr->c.left, cr->c.top, cr->c.width, cr->c.height, + f->f_width, f->f_height); + + return 0; +} + +static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_frame *f; + int ret; + + ret = fimc_m2m_try_crop(ctx, cr); + if (ret) + return ret; + + f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? + &ctx->s_frame : &ctx->d_frame; + + /* Check to see if scaling ratio is within supported range */ + if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { + if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = fimc_check_scaler_ratio(ctx, cr->c.width, + cr->c.height, ctx->d_frame.width, + ctx->d_frame.height, ctx->rotation); + } else { + ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, + ctx->s_frame.height, cr->c.width, + cr->c.height, ctx->rotation); + } + if (ret) { + v4l2_err(fimc->m2m.vfd, "Out of scaler range\n"); + return -EINVAL; + } + } + + f->offs_h = cr->c.left; + f->offs_v = cr->c.top; + f->width = cr->c.width; + f->height = cr->c.height; + + fimc_ctx_state_set(FIMC_PARAMS, ctx); + + return 0; +} + +static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { + .vidioc_querycap = fimc_m2m_querycap, + .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, + .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, + .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, + .vidioc_reqbufs = fimc_m2m_reqbufs, + .vidioc_querybuf = fimc_m2m_querybuf, + .vidioc_qbuf = fimc_m2m_qbuf, + .vidioc_dqbuf = fimc_m2m_dqbuf, + .vidioc_streamon = fimc_m2m_streamon, + .vidioc_streamoff = fimc_m2m_streamoff, + .vidioc_g_crop = fimc_m2m_g_crop, + .vidioc_s_crop = fimc_m2m_s_crop, + .vidioc_cropcap = fimc_m2m_cropcap + +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct fimc_ctx *ctx = priv; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = ctx; + src_vq->ops = &fimc_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = ctx; + dst_vq->ops = &fimc_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + + return vb2_queue_init(dst_vq); +} + +static int fimc_m2m_open(struct file *file) +{ + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_ctx *ctx; + int ret; + + dbg("pid: %d, state: 0x%lx, refcnt: %d", + task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); + + /* + * Return if the corresponding video capture node + * is already opened. + */ + if (fimc->vid_cap.refcnt > 0) + return -EBUSY; + + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + v4l2_fh_init(&ctx->fh, fimc->m2m.vfd); + ctx->fimc_dev = fimc; + + /* Default color format */ + ctx->s_frame.fmt = fimc_get_format(0); + ctx->d_frame.fmt = fimc_get_format(0); + + ret = fimc_ctrls_create(ctx); + if (ret) + goto error_fh; + + /* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + /* Setup the device context for memory-to-memory mode */ + ctx->state = FIMC_CTX_M2M; + ctx->flags = 0; + ctx->in_path = FIMC_DMA; + ctx->out_path = FIMC_DMA; + + ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->m2m_ctx)) { + ret = PTR_ERR(ctx->m2m_ctx); + goto error_c; + } + + if (fimc->m2m.refcnt++ == 0) + set_bit(ST_M2M_RUN, &fimc->state); + return 0; + +error_c: + fimc_ctrls_delete(ctx); +error_fh: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + return ret; +} + +static int fimc_m2m_release(struct file *file) +{ + struct fimc_ctx *ctx = fh_to_ctx(file->private_data); + struct fimc_dev *fimc = ctx->fimc_dev; + + dbg("pid: %d, state: 0x%lx, refcnt= %d", + task_pid_nr(current), fimc->state, fimc->m2m.refcnt); + + v4l2_m2m_ctx_release(ctx->m2m_ctx); + fimc_ctrls_delete(ctx); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + + if (--fimc->m2m.refcnt <= 0) + clear_bit(ST_M2M_RUN, &fimc->state); + kfree(ctx); + return 0; +} + +static unsigned int fimc_m2m_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct fimc_ctx *ctx = fh_to_ctx(file->private_data); + + return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); +} + + +static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct fimc_ctx *ctx = fh_to_ctx(file->private_data); + + return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); +} + +static const struct v4l2_file_operations fimc_m2m_fops = { + .owner = THIS_MODULE, + .open = fimc_m2m_open, + .release = fimc_m2m_release, + .poll = fimc_m2m_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_m2m_mmap, +}; + +static struct v4l2_m2m_ops m2m_ops = { + .device_run = fimc_device_run, + .job_abort = fimc_job_abort, +}; + +int fimc_register_m2m_device(struct fimc_dev *fimc, + struct v4l2_device *v4l2_dev) +{ + struct video_device *vfd; + struct platform_device *pdev; + int ret = 0; + + if (!fimc) + return -ENODEV; + + pdev = fimc->pdev; + fimc->v4l2_dev = v4l2_dev; + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(v4l2_dev, "Failed to allocate video device\n"); + return -ENOMEM; + } + + vfd->fops = &fimc_m2m_fops; + vfd->ioctl_ops = &fimc_m2m_ioctl_ops; + vfd->v4l2_dev = v4l2_dev; + vfd->minor = -1; + vfd->release = video_device_release; + vfd->lock = &fimc->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); + + snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev)); + video_set_drvdata(vfd, fimc); + + fimc->m2m.vfd = vfd; + fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(fimc->m2m.m2m_dev)) { + v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); + ret = PTR_ERR(fimc->m2m.m2m_dev); + goto err_init; + } + + ret = media_entity_init(&vfd->entity, 0, NULL, 0); + if (!ret) + return 0; + + v4l2_m2m_release(fimc->m2m.m2m_dev); +err_init: + video_device_release(fimc->m2m.vfd); + return ret; +} + +void fimc_unregister_m2m_device(struct fimc_dev *fimc) +{ + if (!fimc) + return; + + if (fimc->m2m.m2m_dev) + v4l2_m2m_release(fimc->m2m.m2m_dev); + if (fimc->m2m.vfd) { + media_entity_cleanup(&fimc->m2m.vfd->entity); + /* Can also be called if video device wasn't registered */ + video_unregister_device(fimc->m2m.vfd); + } +} -- cgit v1.2.3 From 693f5c40825e91632478624bf0366e6ebf862a25 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 20 Apr 2012 18:57:25 -0300 Subject: [media] s5p-fimc: Use v4l2_subdev internal ops to register video nodes In order to be able to select only FIMC-LITE support, which is added with subsequent patches, the regular FIMC support is now contained only in fimc-core.c, fimc-m2m.c and fimc-capture.c files. The graph and pipeline management is now solely handled in fimc-mdevice.[ch]. This means the FIMC driver can now be excluded with Kconfig option, leaving only FIMC-LITE and allowing this driver to be reused in SoCs that have only FIMC-LITE and no regular FIMC IP. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 142 +++++++++++++++------------- drivers/media/video/s5p-fimc/fimc-core.c | 13 ++- drivers/media/video/s5p-fimc/fimc-core.h | 7 +- drivers/media/video/s5p-fimc/fimc-m2m.c | 17 +++- drivers/media/video/s5p-fimc/fimc-mdevice.c | 89 +++++++---------- drivers/media/video/s5p-fimc/fimc-mdevice.h | 1 + 6 files changed, 138 insertions(+), 131 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 0051d8161c6c..b45da2780213 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -993,7 +993,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; /* Don't call FIMC subdev operation to avoid nested locking */ - if (sd == fimc->vid_cap.subdev) { + if (sd == &fimc->vid_cap.subdev) { struct fimc_frame *ff = &vid_cap->ctx->s_frame; sink_fmt.format.width = ff->f_width; sink_fmt.format.height = ff->f_height; @@ -1489,53 +1489,6 @@ static struct v4l2_subdev_ops fimc_subdev_ops = { .pad = &fimc_subdev_pad_ops, }; -static int fimc_create_capture_subdev(struct fimc_dev *fimc, - struct v4l2_device *v4l2_dev) -{ - struct v4l2_subdev *sd; - int ret; - - sd = kzalloc(sizeof(*sd), GFP_KERNEL); - if (!sd) - return -ENOMEM; - - v4l2_subdev_init(sd, &fimc_subdev_ops); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id); - - fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, - fimc->vid_cap.sd_pads, 0); - if (ret) - goto me_err; - ret = v4l2_device_register_subdev(v4l2_dev, sd); - if (ret) - goto sd_err; - - fimc->vid_cap.subdev = sd; - v4l2_set_subdevdata(sd, fimc); - sd->entity.ops = &fimc_sd_media_ops; - return 0; -sd_err: - media_entity_cleanup(&sd->entity); -me_err: - kfree(sd); - return ret; -} - -static void fimc_destroy_capture_subdev(struct fimc_dev *fimc) -{ - struct v4l2_subdev *sd = fimc->vid_cap.subdev; - - if (!sd) - return; - media_entity_cleanup(&sd->entity); - v4l2_device_unregister_subdev(sd); - kfree(sd); - fimc->vid_cap.subdev = NULL; -} - /* Set default format at the sensor and host interface */ static int fimc_capture_set_default_format(struct fimc_dev *fimc) { @@ -1554,7 +1507,7 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc) } /* fimc->lock must be already initialized */ -int fimc_register_capture_device(struct fimc_dev *fimc, +static int fimc_register_capture_device(struct fimc_dev *fimc, struct v4l2_device *v4l2_dev) { struct video_device *vfd; @@ -1572,7 +1525,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc, ctx->out_path = FIMC_DMA; ctx->state = FIMC_CTX_CAP; ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); - ctx->d_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); + ctx->d_frame.fmt = ctx->s_frame.fmt; vfd = video_device_alloc(); if (!vfd) { @@ -1580,8 +1533,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc, goto err_vd_alloc; } - snprintf(vfd->name, sizeof(vfd->name), "%s.capture", - dev_name(&fimc->pdev->dev)); + snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.capture", fimc->id); vfd->fops = &fimc_capture_fops; vfd->ioctl_ops = &fimc_capture_ioctl_ops; @@ -1616,18 +1568,22 @@ int fimc_register_capture_device(struct fimc_dev *fimc, vb2_queue_init(q); - fimc->vid_cap.vd_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vfd->entity, 1, &fimc->vid_cap.vd_pad, 0); + vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); if (ret) goto err_ent; - ret = fimc_create_capture_subdev(fimc, v4l2_dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); if (ret) - goto err_sd_reg; + goto err_vd; + + v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", + vfd->name, video_device_node_name(vfd)); vfd->ctrl_handler = &ctx->ctrl_handler; return 0; -err_sd_reg: +err_vd: media_entity_cleanup(&vfd->entity); err_ent: video_device_release(vfd); @@ -1636,17 +1592,73 @@ err_vd_alloc: return ret; } -void fimc_unregister_capture_device(struct fimc_dev *fimc) +static int fimc_capture_subdev_registered(struct v4l2_subdev *sd) { - struct video_device *vfd = fimc->vid_cap.vfd; + struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + int ret; - if (vfd) { - media_entity_cleanup(&vfd->entity); - /* Can also be called if video device was - not registered */ - video_unregister_device(vfd); + ret = fimc_register_m2m_device(fimc, sd->v4l2_dev); + if (ret) + return ret; + + ret = fimc_register_capture_device(fimc, sd->v4l2_dev); + if (ret) + fimc_unregister_m2m_device(fimc); + + return ret; +} + +static void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd) +{ + struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + + if (fimc == NULL) + return; + + fimc_unregister_m2m_device(fimc); + + if (fimc->vid_cap.vfd) { + media_entity_cleanup(&fimc->vid_cap.vfd->entity); + video_unregister_device(fimc->vid_cap.vfd); + fimc->vid_cap.vfd = NULL; } - fimc_destroy_capture_subdev(fimc); + kfree(fimc->vid_cap.ctx); fimc->vid_cap.ctx = NULL; } + +static const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = { + .registered = fimc_capture_subdev_registered, + .unregistered = fimc_capture_subdev_unregistered, +}; + +int fimc_initialize_capture_subdev(struct fimc_dev *fimc) +{ + struct v4l2_subdev *sd = &fimc->vid_cap.subdev; + int ret; + + v4l2_subdev_init(sd, &fimc_subdev_ops); + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id); + + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->vid_cap.sd_pads, 0); + if (ret) + return ret; + + sd->entity.ops = &fimc_sd_media_ops; + sd->internal_ops = &fimc_capture_sd_internal_ops; + v4l2_set_subdevdata(sd, fimc); + return 0; +} + +void fimc_unregister_capture_subdev(struct fimc_dev *fimc) +{ + struct v4l2_subdev *sd = &fimc->vid_cap.subdev; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_set_subdevdata(sd, NULL); +} diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 749db4deefcb..add24cd373a5 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -842,8 +842,6 @@ static int fimc_probe(struct platform_device *pdev) clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); clk_enable(fimc->clock[CLK_BUS]); - platform_set_drvdata(pdev, fimc); - ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, 0, pdev->name, fimc); if (ret) { @@ -851,10 +849,15 @@ static int fimc_probe(struct platform_device *pdev) goto err_clk; } + ret = fimc_initialize_capture_subdev(fimc); + if (ret) + goto err_clk; + + platform_set_drvdata(pdev, fimc); pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) - goto err_clk; + goto err_sd; /* Initialize contiguous memory allocator */ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(fimc->alloc_ctx)) { @@ -866,9 +869,10 @@ static int fimc_probe(struct platform_device *pdev) pm_runtime_put(&pdev->dev); return 0; - err_pm: pm_runtime_put(&pdev->dev); +err_sd: + fimc_unregister_capture_subdev(fimc); err_clk: fimc_clk_put(fimc); return ret; @@ -953,6 +957,7 @@ static int __devexit fimc_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); + fimc_unregister_capture_subdev(fimc); vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); clk_disable(fimc->clock[CLK_BUS]); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 288631a86e53..ef7c6a23ca2a 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -331,7 +331,7 @@ struct fimc_vid_cap { struct fimc_ctx *ctx; struct vb2_alloc_ctx *alloc_ctx; struct video_device *vfd; - struct v4l2_subdev *subdev; + struct v4l2_subdev subdev; struct media_pad vd_pad; struct v4l2_mbus_framefmt mf; struct media_pad sd_pads[FIMC_SD_PADS_NUM]; @@ -737,9 +737,8 @@ void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state); /* -----------------------------------------------------*/ /* fimc-capture.c */ -int fimc_register_capture_device(struct fimc_dev *fimc, - struct v4l2_device *v4l2_dev); -void fimc_unregister_capture_device(struct fimc_dev *fimc); +int fimc_initialize_capture_subdev(struct fimc_dev *fimc); +void fimc_unregister_capture_subdev(struct fimc_dev *fimc); int fimc_capture_ctrls_create(struct fimc_dev *fimc); void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg); diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c index 9935cc4bd0cb..92b6059b25bf 100644 --- a/drivers/media/video/s5p-fimc/fimc-m2m.c +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -776,7 +776,7 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, This driver needs auditing so that this flag can be removed. */ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); - snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev)); + snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id); video_set_drvdata(vfd, fimc); fimc->m2m.vfd = vfd; @@ -788,9 +788,20 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, } ret = media_entity_init(&vfd->entity, 0, NULL, 0); - if (!ret) - return 0; + if (ret) + goto err_me; + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + if (ret) + goto err_vd; + + v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", + vfd->name, video_device_node_name(vfd)); + return 0; +err_vd: + media_entity_cleanup(&vfd->entity); +err_me: v4l2_m2m_release(fimc->m2m.m2m_dev); err_init: video_device_release(fimc->m2m.vfd); diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index f97ac02b8677..c319842c762d 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -304,8 +304,9 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) static int fimc_register_callback(struct device *dev, void *p) { struct fimc_dev *fimc = dev_get_drvdata(dev); + struct v4l2_subdev *sd = &fimc->vid_cap.subdev; struct fimc_md *fmd = p; - int ret; + int ret = 0; if (!fimc || !fimc->pdev) return 0; @@ -313,12 +314,14 @@ static int fimc_register_callback(struct device *dev, void *p) return 0; fmd->fimc[fimc->pdev->id] = fimc; - ret = fimc_register_m2m_device(fimc, &fmd->v4l2_dev); - if (ret) - return ret; - ret = fimc_register_capture_device(fimc, &fmd->v4l2_dev); - if (!ret) - fimc->vid_cap.user_subdev_api = fmd->user_subdev_api; + sd->grp_id = FIMC_GROUP_ID; + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (ret) { + v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n", + fimc->id, ret); + } + return ret; } @@ -401,8 +404,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_DEVS; i++) { if (fmd->fimc[i] == NULL) continue; - fimc_unregister_m2m_device(fmd->fimc[i]); - fimc_unregister_capture_device(fmd->fimc[i]); + v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev); fmd->fimc[i] = NULL; } for (i = 0; i < CSIS_MAX_ENTITIES; i++) { @@ -420,35 +422,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) } } -static int fimc_md_register_video_nodes(struct fimc_md *fmd) -{ - struct video_device *vdev; - int i, ret = 0; - - for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) { - if (!fmd->fimc[i]) - continue; - - vdev = fmd->fimc[i]->m2m.vfd; - if (vdev) { - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - if (ret) - break; - v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n", - vdev->name, video_device_node_name(vdev)); - } - - vdev = fmd->fimc[i]->vid_cap.vfd; - if (vdev == NULL) - continue; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n", - vdev->name, video_device_node_name(vdev)); - } - - return ret; -} - /** * __fimc_md_create_fimc_links - create links to all FIMC entities * @fmd: fimc media device @@ -479,7 +452,7 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd, continue; flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; - sink = &fmd->fimc[i]->vid_cap.subdev->entity; + sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_entity_create_link(source, pad, sink, FIMC_SD_PAD_SINK, flags); if (ret) @@ -588,7 +561,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) continue; - source = &fmd->fimc[i]->vid_cap.subdev->entity; + source = &fmd->fimc[i]->vid_cap.subdev.entity; sink = &fmd->fimc[i]->vid_cap.vfd->entity; ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, sink, 0, flags); @@ -817,42 +790,48 @@ static int fimc_md_probe(struct platform_device *pdev) ret = media_device_register(&fmd->media_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); - goto err2; + goto err_md; } ret = fimc_md_get_clocks(fmd); if (ret) - goto err3; + goto err_clk; fmd->user_subdev_api = false; + + /* Protect the media graph while we're registering entities */ + mutex_lock(&fmd->media_dev.graph_mutex); + ret = fimc_md_register_platform_entities(fmd); if (ret) - goto err3; + goto err_unlock; if (pdev->dev.platform_data) { ret = fimc_md_register_sensor_entities(fmd); if (ret) - goto err3; + goto err_unlock; } ret = fimc_md_create_links(fmd); if (ret) - goto err3; + goto err_unlock; ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); if (ret) - goto err3; - ret = fimc_md_register_video_nodes(fmd); - if (ret) - goto err3; + goto err_unlock; ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); - if (!ret) { - platform_set_drvdata(pdev, fmd); - return 0; - } -err3: + if (ret) + goto err_unlock; + + platform_set_drvdata(pdev, fmd); + mutex_unlock(&fmd->media_dev.graph_mutex); + return 0; + +err_unlock: + mutex_unlock(&fmd->media_dev.graph_mutex); +err_clk: media_device_unregister(&fmd->media_dev); fimc_md_put_clocks(fmd); fimc_md_unregister_entities(fmd); -err2: +err_md: v4l2_device_unregister(&fmd->v4l2_dev); return ret; } diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h index da3780823e7d..4f3b69c682cb 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h @@ -24,6 +24,7 @@ #define SENSOR_GROUP_ID (1 << 8) #define CSIS_GROUP_ID (1 << 9) #define WRITEBACK_GROUP_ID (1 << 10) +#define FIMC_GROUP_ID (1 << 11) #define FIMC_MAX_SENSORS 8 #define FIMC_MAX_CAMCLKS 2 -- cgit v1.2.3 From c83a1ff063eb7cd8eb2025c08194f6bcb49334f1 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 2 May 2012 06:14:49 -0300 Subject: [media] s5p-fimc: Refactor the register interface functions Simplify the register API and use FIMC_REG_ prefix for all register definitions for consistency with FIMC-LITE. The unused image effect defines are removed. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 1 + drivers/media/video/s5p-fimc/fimc-core.c | 21 +- drivers/media/video/s5p-fimc/fimc-core.h | 118 ------ drivers/media/video/s5p-fimc/fimc-m2m.c | 1 + drivers/media/video/s5p-fimc/fimc-reg.c | 554 +++++++++++++++------------- drivers/media/video/s5p-fimc/fimc-reg.h | 326 ++++++++++++++++ drivers/media/video/s5p-fimc/regs-fimc.h | 301 --------------- 7 files changed, 641 insertions(+), 681 deletions(-) create mode 100644 drivers/media/video/s5p-fimc/fimc-reg.h delete mode 100644 drivers/media/video/s5p-fimc/regs-fimc.h diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index b45da2780213..52a5fb469b45 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -29,6 +29,7 @@ #include "fimc-mdevice.h" #include "fimc-core.h" +#include "fimc-reg.h" static int fimc_init_capture(struct fimc_dev *fimc) { diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index add24cd373a5..afd69e3d44c2 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -28,6 +28,7 @@ #include #include "fimc-core.h" +#include "fimc-reg.h" #include "fimc-mdevice.h" static char *fimc_clocks[MAX_FIMC_CLOCKS] = { @@ -388,40 +389,40 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, void fimc_set_yuv_order(struct fimc_ctx *ctx) { /* The one only mode supported in SoC. */ - ctx->in_order_2p = S5P_FIMC_LSB_CRCB; - ctx->out_order_2p = S5P_FIMC_LSB_CRCB; + ctx->in_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB; + ctx->out_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB; /* Set order for 1 plane input formats. */ switch (ctx->s_frame.fmt->color) { case S5P_FIMC_YCRYCB422: - ctx->in_order_1p = S5P_MSCTRL_ORDER422_CBYCRY; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY; break; case S5P_FIMC_CBYCRY422: - ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCRYCB; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB; break; case S5P_FIMC_CRYCBY422: - ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCBYCR; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR; break; case S5P_FIMC_YCBYCR422: default: - ctx->in_order_1p = S5P_MSCTRL_ORDER422_CRYCBY; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY; break; } dbg("ctx->in_order_1p= %d", ctx->in_order_1p); switch (ctx->d_frame.fmt->color) { case S5P_FIMC_YCRYCB422: - ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CBYCRY; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY; break; case S5P_FIMC_CBYCRY422: - ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCRYCB; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB; break; case S5P_FIMC_CRYCBY422: - ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCBYCR; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR; break; case S5P_FIMC_YCBYCR422: default: - ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CRYCBY; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY; break; } dbg("ctx->out_order_1p= %d", ctx->out_order_1p); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index ef7c6a23ca2a..34fbba424692 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -26,8 +26,6 @@ #include #include -#include "regs-fimc.h" - #define err(fmt, args...) \ printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args) @@ -106,17 +104,6 @@ enum fimc_color_fmt { #define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \ __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) -/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */ -#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB - -/* The embedded image effect selection */ -#define S5P_FIMC_EFFECT_ORIGINAL S5P_CIIMGEFF_FIN_BYPASS -#define S5P_FIMC_EFFECT_ARBITRARY S5P_CIIMGEFF_FIN_ARBITRARY -#define S5P_FIMC_EFFECT_NEGATIVE S5P_CIIMGEFF_FIN_NEGATIVE -#define S5P_FIMC_EFFECT_ARTFREEZE S5P_CIIMGEFF_FIN_ARTFREEZE -#define S5P_FIMC_EFFECT_EMBOSSING S5P_CIIMGEFF_FIN_EMBOSSING -#define S5P_FIMC_EFFECT_SIKHOUETTE S5P_CIIMGEFF_FIN_SILHOUETTE - /* The hardware context state. */ #define FIMC_PARAMS (1 << 0) #define FIMC_SRC_FMT (1 << 3) @@ -588,54 +575,6 @@ static inline int fimc_get_alpha_mask(struct fimc_fmt *fmt) }; } -static inline void fimc_hw_clear_irq(struct fimc_dev *dev) -{ - u32 cfg = readl(dev->regs + S5P_CIGCTRL); - cfg |= S5P_CIGCTRL_IRQ_CLR; - writel(cfg, dev->regs + S5P_CIGCTRL); -} - -static inline void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on) -{ - u32 cfg = readl(dev->regs + S5P_CISCCTRL); - if (on) - cfg |= S5P_CISCCTRL_SCALERSTART; - else - cfg &= ~S5P_CISCCTRL_SCALERSTART; - writel(cfg, dev->regs + S5P_CISCCTRL); -} - -static inline void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on) -{ - u32 cfg = readl(dev->regs + S5P_MSCTRL); - if (on) - cfg |= S5P_MSCTRL_ENVID; - else - cfg &= ~S5P_MSCTRL_ENVID; - writel(cfg, dev->regs + S5P_MSCTRL); -} - -static inline void fimc_hw_dis_capture(struct fimc_dev *dev) -{ - u32 cfg = readl(dev->regs + S5P_CIIMGCPT); - cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC); - writel(cfg, dev->regs + S5P_CIIMGCPT); -} - -/** - * fimc_hw_set_dma_seq - configure output DMA buffer sequence - * @mask: each bit corresponds to one of 32 output buffer registers set - * 1 to include buffer in the sequence, 0 to disable - * - * This function mask output DMA ring buffers, i.e. it allows to configure - * which of the output buffer address registers will be used by the DMA - * engine. - */ -static inline void fimc_hw_set_dma_seq(struct fimc_dev *dev, u32 mask) -{ - writel(mask, dev->regs + S5P_CIFCNTSEQ); -} - static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, enum v4l2_buf_type type) { @@ -657,48 +596,6 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, return frame; } -/* Return an index to the buffer actually being written. */ -static inline u32 fimc_hw_get_frame_index(struct fimc_dev *dev) -{ - u32 reg; - - if (dev->variant->has_cistatus2) { - reg = readl(dev->regs + S5P_CISTATUS2) & 0x3F; - return reg > 0 ? --reg : reg; - } else { - reg = readl(dev->regs + S5P_CISTATUS); - return (reg & S5P_CISTATUS_FRAMECNT_MASK) >> - S5P_CISTATUS_FRAMECNT_SHIFT; - } -} - -/* -----------------------------------------------------*/ -/* fimc-reg.c */ -void fimc_hw_reset(struct fimc_dev *fimc); -void fimc_hw_set_rotation(struct fimc_ctx *ctx); -void fimc_hw_set_target_format(struct fimc_ctx *ctx); -void fimc_hw_set_out_dma(struct fimc_ctx *ctx); -void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); -void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); -void fimc_hw_set_prescaler(struct fimc_ctx *ctx); -void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); -void fimc_hw_en_capture(struct fimc_ctx *ctx); -void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active); -void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); -void fimc_hw_set_in_dma(struct fimc_ctx *ctx); -void fimc_hw_set_input_path(struct fimc_ctx *ctx); -void fimc_hw_set_output_path(struct fimc_ctx *ctx); -void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); -void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, - int index); -int fimc_hw_set_camera_source(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); -int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); -int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); -int fimc_hw_set_camera_type(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); - /* -----------------------------------------------------*/ /* fimc-core.c */ int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, @@ -745,21 +642,6 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, int fimc_capture_suspend(struct fimc_dev *fimc); int fimc_capture_resume(struct fimc_dev *fimc); -/* Locking: the caller holds fimc->slock */ -static inline void fimc_activate_capture(struct fimc_ctx *ctx) -{ - fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled); - fimc_hw_en_capture(ctx); -} - -static inline void fimc_deactivate_capture(struct fimc_dev *fimc) -{ - fimc_hw_en_lastirq(fimc, true); - fimc_hw_dis_capture(fimc); - fimc_hw_enable_scaler(fimc, false); - fimc_hw_en_lastirq(fimc, false); -} - /* * Buffer list manipulation functions. Must be called with fimc.slock held. */ diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c index 92b6059b25bf..70edc75e6fce 100644 --- a/drivers/media/video/s5p-fimc/fimc-m2m.c +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -28,6 +28,7 @@ #include #include "fimc-core.h" +#include "fimc-reg.h" #include "fimc-mdevice.h" diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index ff11f10fea0b..31a8b99ee71a 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -12,9 +12,9 @@ #include #include -#include #include +#include "fimc-reg.h" #include "fimc-core.h" @@ -22,19 +22,19 @@ void fimc_hw_reset(struct fimc_dev *dev) { u32 cfg; - cfg = readl(dev->regs + S5P_CISRCFMT); - cfg |= S5P_CISRCFMT_ITU601_8BIT; - writel(cfg, dev->regs + S5P_CISRCFMT); + cfg = readl(dev->regs + FIMC_REG_CISRCFMT); + cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; + writel(cfg, dev->regs + FIMC_REG_CISRCFMT); /* Software reset. */ - cfg = readl(dev->regs + S5P_CIGCTRL); - cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL); - writel(cfg, dev->regs + S5P_CIGCTRL); + cfg = readl(dev->regs + FIMC_REG_CIGCTRL); + cfg |= (FIMC_REG_CIGCTRL_SWRST | FIMC_REG_CIGCTRL_IRQ_LEVEL); + writel(cfg, dev->regs + FIMC_REG_CIGCTRL); udelay(10); - cfg = readl(dev->regs + S5P_CIGCTRL); - cfg &= ~S5P_CIGCTRL_SWRST; - writel(cfg, dev->regs + S5P_CIGCTRL); + cfg = readl(dev->regs + FIMC_REG_CIGCTRL); + cfg &= ~FIMC_REG_CIGCTRL_SWRST; + writel(cfg, dev->regs + FIMC_REG_CIGCTRL); if (dev->variant->out_buf_count > 4) fimc_hw_set_dma_seq(dev, 0xF); @@ -42,32 +42,32 @@ void fimc_hw_reset(struct fimc_dev *dev) static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx) { - u32 flip = S5P_MSCTRL_FLIP_NORMAL; + u32 flip = FIMC_REG_MSCTRL_FLIP_NORMAL; if (ctx->hflip) - flip = S5P_MSCTRL_FLIP_X_MIRROR; + flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; if (ctx->vflip) - flip = S5P_MSCTRL_FLIP_Y_MIRROR; + flip = FIMC_REG_MSCTRL_FLIP_Y_MIRROR; if (ctx->rotation <= 90) return flip; - return (flip ^ S5P_MSCTRL_FLIP_180) & S5P_MSCTRL_FLIP_180; + return (flip ^ FIMC_REG_MSCTRL_FLIP_180) & FIMC_REG_MSCTRL_FLIP_180; } static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx) { - u32 flip = S5P_CITRGFMT_FLIP_NORMAL; + u32 flip = FIMC_REG_CITRGFMT_FLIP_NORMAL; if (ctx->hflip) - flip |= S5P_CITRGFMT_FLIP_X_MIRROR; + flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; if (ctx->vflip) - flip |= S5P_CITRGFMT_FLIP_Y_MIRROR; + flip |= FIMC_REG_CITRGFMT_FLIP_Y_MIRROR; if (ctx->rotation <= 90) return flip; - return (flip ^ S5P_CITRGFMT_FLIP_180) & S5P_CITRGFMT_FLIP_180; + return (flip ^ FIMC_REG_CITRGFMT_FLIP_180) & FIMC_REG_CITRGFMT_FLIP_180; } void fimc_hw_set_rotation(struct fimc_ctx *ctx) @@ -75,9 +75,9 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx) u32 cfg, flip; struct fimc_dev *dev = ctx->fimc_dev; - cfg = readl(dev->regs + S5P_CITRGFMT); - cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90 | - S5P_CITRGFMT_FLIP_180); + cfg = readl(dev->regs + FIMC_REG_CITRGFMT); + cfg &= ~(FIMC_REG_CITRGFMT_INROT90 | FIMC_REG_CITRGFMT_OUTROT90 | + FIMC_REG_CITRGFMT_FLIP_180); /* * The input and output rotator cannot work simultaneously. @@ -86,20 +86,20 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx) */ if (ctx->rotation == 90 || ctx->rotation == 270) { if (ctx->out_path == FIMC_LCDFIFO) - cfg |= S5P_CITRGFMT_INROT90; + cfg |= FIMC_REG_CITRGFMT_INROT90; else - cfg |= S5P_CITRGFMT_OUTROT90; + cfg |= FIMC_REG_CITRGFMT_OUTROT90; } if (ctx->out_path == FIMC_DMA) { cfg |= fimc_hw_get_target_flip(ctx); - writel(cfg, dev->regs + S5P_CITRGFMT); + writel(cfg, dev->regs + FIMC_REG_CITRGFMT); } else { /* LCD FIFO path */ - flip = readl(dev->regs + S5P_MSCTRL); - flip &= ~S5P_MSCTRL_FLIP_MASK; + flip = readl(dev->regs + FIMC_REG_MSCTRL); + flip &= ~FIMC_REG_MSCTRL_FLIP_MASK; flip |= fimc_hw_get_in_flip(ctx); - writel(flip, dev->regs + S5P_MSCTRL); + writel(flip, dev->regs + FIMC_REG_MSCTRL); } } @@ -110,43 +110,40 @@ void fimc_hw_set_target_format(struct fimc_ctx *ctx) struct fimc_frame *frame = &ctx->d_frame; dbg("w= %d, h= %d color: %d", frame->width, - frame->height, frame->fmt->color); + frame->height, frame->fmt->color); - cfg = readl(dev->regs + S5P_CITRGFMT); - cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK | - S5P_CITRGFMT_VSIZE_MASK); + cfg = readl(dev->regs + FIMC_REG_CITRGFMT); + cfg &= ~(FIMC_REG_CITRGFMT_FMT_MASK | FIMC_REG_CITRGFMT_HSIZE_MASK | + FIMC_REG_CITRGFMT_VSIZE_MASK); switch (frame->fmt->color) { case S5P_FIMC_RGB444...S5P_FIMC_RGB888: - cfg |= S5P_CITRGFMT_RGB; + cfg |= FIMC_REG_CITRGFMT_RGB; break; case S5P_FIMC_YCBCR420: - cfg |= S5P_CITRGFMT_YCBCR420; + cfg |= FIMC_REG_CITRGFMT_YCBCR420; break; case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422: if (frame->fmt->colplanes == 1) - cfg |= S5P_CITRGFMT_YCBCR422_1P; + cfg |= FIMC_REG_CITRGFMT_YCBCR422_1P; else - cfg |= S5P_CITRGFMT_YCBCR422; + cfg |= FIMC_REG_CITRGFMT_YCBCR422; break; default: break; } - if (ctx->rotation == 90 || ctx->rotation == 270) { - cfg |= S5P_CITRGFMT_HSIZE(frame->height); - cfg |= S5P_CITRGFMT_VSIZE(frame->width); - } else { - - cfg |= S5P_CITRGFMT_HSIZE(frame->width); - cfg |= S5P_CITRGFMT_VSIZE(frame->height); - } + if (ctx->rotation == 90 || ctx->rotation == 270) + cfg |= (frame->height << 16) | frame->width; + else + cfg |= (frame->width << 16) | frame->height; - writel(cfg, dev->regs + S5P_CITRGFMT); + writel(cfg, dev->regs + FIMC_REG_CITRGFMT); - cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK; + cfg = readl(dev->regs + FIMC_REG_CITAREA); + cfg &= ~FIMC_REG_CITAREA_MASK; cfg |= (frame->width * frame->height); - writel(cfg, dev->regs + S5P_CITAREA); + writel(cfg, dev->regs + FIMC_REG_CITAREA); } static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx) @@ -155,87 +152,82 @@ static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx) struct fimc_frame *frame = &ctx->d_frame; u32 cfg; - cfg = S5P_ORIG_SIZE_HOR(frame->f_width); - cfg |= S5P_ORIG_SIZE_VER(frame->f_height); - writel(cfg, dev->regs + S5P_ORGOSIZE); + cfg = (frame->f_height << 16) | frame->f_width; + writel(cfg, dev->regs + FIMC_REG_ORGOSIZE); /* Select color space conversion equation (HD/SD size).*/ - cfg = readl(dev->regs + S5P_CIGCTRL); + cfg = readl(dev->regs + FIMC_REG_CIGCTRL); if (frame->f_width >= 1280) /* HD */ - cfg |= S5P_CIGCTRL_CSC_ITU601_709; + cfg |= FIMC_REG_CIGCTRL_CSC_ITU601_709; else /* SD */ - cfg &= ~S5P_CIGCTRL_CSC_ITU601_709; - writel(cfg, dev->regs + S5P_CIGCTRL); + cfg &= ~FIMC_REG_CIGCTRL_CSC_ITU601_709; + writel(cfg, dev->regs + FIMC_REG_CIGCTRL); } void fimc_hw_set_out_dma(struct fimc_ctx *ctx) { - u32 cfg; struct fimc_dev *dev = ctx->fimc_dev; struct fimc_frame *frame = &ctx->d_frame; struct fimc_dma_offset *offset = &frame->dma_offset; struct fimc_fmt *fmt = frame->fmt; + u32 cfg; /* Set the input dma offsets. */ - cfg = 0; - cfg |= S5P_CIO_OFFS_HOR(offset->y_h); - cfg |= S5P_CIO_OFFS_VER(offset->y_v); - writel(cfg, dev->regs + S5P_CIOYOFF); + cfg = (offset->y_v << 16) | offset->y_h; + writel(cfg, dev->regs + FIMC_REG_CIOYOFF); - cfg = 0; - cfg |= S5P_CIO_OFFS_HOR(offset->cb_h); - cfg |= S5P_CIO_OFFS_VER(offset->cb_v); - writel(cfg, dev->regs + S5P_CIOCBOFF); + cfg = (offset->cb_v << 16) | offset->cb_h; + writel(cfg, dev->regs + FIMC_REG_CIOCBOFF); - cfg = 0; - cfg |= S5P_CIO_OFFS_HOR(offset->cr_h); - cfg |= S5P_CIO_OFFS_VER(offset->cr_v); - writel(cfg, dev->regs + S5P_CIOCROFF); + cfg = (offset->cr_v << 16) | offset->cr_h; + writel(cfg, dev->regs + FIMC_REG_CIOCROFF); fimc_hw_set_out_dma_size(ctx); /* Configure chroma components order. */ - cfg = readl(dev->regs + S5P_CIOCTRL); + cfg = readl(dev->regs + FIMC_REG_CIOCTRL); - cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK | - S5P_CIOCTRL_YCBCR_PLANE_MASK | S5P_CIOCTRL_RGB16FMT_MASK); + cfg &= ~(FIMC_REG_CIOCTRL_ORDER2P_MASK | + FIMC_REG_CIOCTRL_ORDER422_MASK | + FIMC_REG_CIOCTRL_YCBCR_PLANE_MASK | + FIMC_REG_CIOCTRL_RGB16FMT_MASK); if (fmt->colplanes == 1) cfg |= ctx->out_order_1p; else if (fmt->colplanes == 2) - cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE; + cfg |= ctx->out_order_2p | FIMC_REG_CIOCTRL_YCBCR_2PLANE; else if (fmt->colplanes == 3) - cfg |= S5P_CIOCTRL_YCBCR_3PLANE; + cfg |= FIMC_REG_CIOCTRL_YCBCR_3PLANE; if (fmt->color == S5P_FIMC_RGB565) - cfg |= S5P_CIOCTRL_RGB565; + cfg |= FIMC_REG_CIOCTRL_RGB565; else if (fmt->color == S5P_FIMC_RGB555) - cfg |= S5P_CIOCTRL_ARGB1555; + cfg |= FIMC_REG_CIOCTRL_ARGB1555; else if (fmt->color == S5P_FIMC_RGB444) - cfg |= S5P_CIOCTRL_ARGB4444; + cfg |= FIMC_REG_CIOCTRL_ARGB4444; - writel(cfg, dev->regs + S5P_CIOCTRL); + writel(cfg, dev->regs + FIMC_REG_CIOCTRL); } static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable) { - u32 cfg = readl(dev->regs + S5P_ORGISIZE); + u32 cfg = readl(dev->regs + FIMC_REG_ORGISIZE); if (enable) - cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN; + cfg |= FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; else - cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN; - writel(cfg, dev->regs + S5P_ORGISIZE); + cfg &= ~FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; + writel(cfg, dev->regs + FIMC_REG_ORGISIZE); } void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable) { - u32 cfg = readl(dev->regs + S5P_CIOCTRL); + u32 cfg = readl(dev->regs + FIMC_REG_CIOCTRL); if (enable) - cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE; + cfg |= FIMC_REG_CIOCTRL_LASTIRQ_ENABLE; else - cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE; - writel(cfg, dev->regs + S5P_CIOCTRL); + cfg &= ~FIMC_REG_CIOCTRL_LASTIRQ_ENABLE; + writel(cfg, dev->regs + FIMC_REG_CIOCTRL); } void fimc_hw_set_prescaler(struct fimc_ctx *ctx) @@ -245,15 +237,13 @@ void fimc_hw_set_prescaler(struct fimc_ctx *ctx) u32 cfg, shfactor; shfactor = 10 - (sc->hfactor + sc->vfactor); + cfg = shfactor << 28; - cfg = S5P_CISCPRERATIO_SHFACTOR(shfactor); - cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio); - cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio); - writel(cfg, dev->regs + S5P_CISCPRERATIO); + cfg |= (sc->pre_hratio << 16) | sc->pre_vratio; + writel(cfg, dev->regs + FIMC_REG_CISCPRERATIO); - cfg = S5P_CISCPREDST_WIDTH(sc->pre_dst_width); - cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height); - writel(cfg, dev->regs + S5P_CISCPREDST); + cfg = (sc->pre_dst_width << 16) | sc->pre_dst_height; + writel(cfg, dev->regs + FIMC_REG_CISCPREDST); } static void fimc_hw_set_scaler(struct fimc_ctx *ctx) @@ -263,39 +253,40 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) struct fimc_frame *src_frame = &ctx->s_frame; struct fimc_frame *dst_frame = &ctx->d_frame; - u32 cfg = readl(dev->regs + S5P_CISCCTRL); + u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); - cfg &= ~(S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE | - S5P_CISCCTRL_SCALEUP_H | S5P_CISCCTRL_SCALEUP_V | - S5P_CISCCTRL_SCALERBYPASS | S5P_CISCCTRL_ONE2ONE | - S5P_CISCCTRL_INRGB_FMT_MASK | S5P_CISCCTRL_OUTRGB_FMT_MASK | - S5P_CISCCTRL_INTERLACE | S5P_CISCCTRL_RGB_EXT); + cfg &= ~(FIMC_REG_CISCCTRL_CSCR2Y_WIDE | FIMC_REG_CISCCTRL_CSCY2R_WIDE | + FIMC_REG_CISCCTRL_SCALEUP_H | FIMC_REG_CISCCTRL_SCALEUP_V | + FIMC_REG_CISCCTRL_SCALERBYPASS | FIMC_REG_CISCCTRL_ONE2ONE | + FIMC_REG_CISCCTRL_INRGB_FMT_MASK | FIMC_REG_CISCCTRL_OUTRGB_FMT_MASK | + FIMC_REG_CISCCTRL_INTERLACE | FIMC_REG_CISCCTRL_RGB_EXT); if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW)) - cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE); + cfg |= (FIMC_REG_CISCCTRL_CSCR2Y_WIDE | + FIMC_REG_CISCCTRL_CSCY2R_WIDE); if (!sc->enabled) - cfg |= S5P_CISCCTRL_SCALERBYPASS; + cfg |= FIMC_REG_CISCCTRL_SCALERBYPASS; if (sc->scaleup_h) - cfg |= S5P_CISCCTRL_SCALEUP_H; + cfg |= FIMC_REG_CISCCTRL_SCALEUP_H; if (sc->scaleup_v) - cfg |= S5P_CISCCTRL_SCALEUP_V; + cfg |= FIMC_REG_CISCCTRL_SCALEUP_V; if (sc->copy_mode) - cfg |= S5P_CISCCTRL_ONE2ONE; + cfg |= FIMC_REG_CISCCTRL_ONE2ONE; if (ctx->in_path == FIMC_DMA) { switch (src_frame->fmt->color) { case S5P_FIMC_RGB565: - cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565; + cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB565; break; case S5P_FIMC_RGB666: - cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666; + cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB666; break; case S5P_FIMC_RGB888: - cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888; + cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB888; break; } } @@ -304,19 +295,19 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) u32 color = dst_frame->fmt->color; if (color >= S5P_FIMC_RGB444 && color <= S5P_FIMC_RGB565) - cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565; + cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB565; else if (color == S5P_FIMC_RGB666) - cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666; + cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB666; else if (color == S5P_FIMC_RGB888) - cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888; + cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888; } else { - cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888; + cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888; if (ctx->flags & FIMC_SCAN_MODE_INTERLACED) - cfg |= S5P_CISCCTRL_INTERLACE; + cfg |= FIMC_REG_CISCCTRL_INTERLACE; } - writel(cfg, dev->regs + S5P_CISCCTRL); + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); } void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) @@ -327,29 +318,30 @@ void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) u32 cfg; dbg("main_hratio= 0x%X main_vratio= 0x%X", - sc->main_hratio, sc->main_vratio); + sc->main_hratio, sc->main_vratio); fimc_hw_set_scaler(ctx); - cfg = readl(dev->regs + S5P_CISCCTRL); - cfg &= ~(S5P_CISCCTRL_MHRATIO_MASK | S5P_CISCCTRL_MVRATIO_MASK); + cfg = readl(dev->regs + FIMC_REG_CISCCTRL); + cfg &= ~(FIMC_REG_CISCCTRL_MHRATIO_MASK | + FIMC_REG_CISCCTRL_MVRATIO_MASK); if (variant->has_mainscaler_ext) { - cfg |= S5P_CISCCTRL_MHRATIO_EXT(sc->main_hratio); - cfg |= S5P_CISCCTRL_MVRATIO_EXT(sc->main_vratio); - writel(cfg, dev->regs + S5P_CISCCTRL); + cfg |= FIMC_REG_CISCCTRL_MHRATIO_EXT(sc->main_hratio); + cfg |= FIMC_REG_CISCCTRL_MVRATIO_EXT(sc->main_vratio); + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); - cfg = readl(dev->regs + S5P_CIEXTEN); + cfg = readl(dev->regs + FIMC_REG_CIEXTEN); - cfg &= ~(S5P_CIEXTEN_MVRATIO_EXT_MASK | - S5P_CIEXTEN_MHRATIO_EXT_MASK); - cfg |= S5P_CIEXTEN_MHRATIO_EXT(sc->main_hratio); - cfg |= S5P_CIEXTEN_MVRATIO_EXT(sc->main_vratio); - writel(cfg, dev->regs + S5P_CIEXTEN); + cfg &= ~(FIMC_REG_CIEXTEN_MVRATIO_EXT_MASK | + FIMC_REG_CIEXTEN_MHRATIO_EXT_MASK); + cfg |= FIMC_REG_CIEXTEN_MHRATIO_EXT(sc->main_hratio); + cfg |= FIMC_REG_CIEXTEN_MVRATIO_EXT(sc->main_vratio); + writel(cfg, dev->regs + FIMC_REG_CIEXTEN); } else { - cfg |= S5P_CISCCTRL_MHRATIO(sc->main_hratio); - cfg |= S5P_CISCCTRL_MVRATIO(sc->main_vratio); - writel(cfg, dev->regs + S5P_CISCCTRL); + cfg |= FIMC_REG_CISCCTRL_MHRATIO(sc->main_hratio); + cfg |= FIMC_REG_CISCCTRL_MVRATIO(sc->main_vratio); + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); } } @@ -357,22 +349,24 @@ void fimc_hw_en_capture(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - u32 cfg = readl(dev->regs + S5P_CIIMGCPT); + u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); if (ctx->out_path == FIMC_DMA) { /* one shot mode */ - cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE | S5P_CIIMGCPT_IMGCPTEN; + cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | + FIMC_REG_CIIMGCPT_IMGCPTEN; } else { /* Continuous frame capture mode (freerun). */ - cfg &= ~(S5P_CIIMGCPT_CPT_FREN_ENABLE | - S5P_CIIMGCPT_CPT_FRMOD_CNT); - cfg |= S5P_CIIMGCPT_IMGCPTEN; + cfg &= ~(FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | + FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT); + cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; } if (ctx->scaler.enabled) - cfg |= S5P_CIIMGCPT_IMGCPTEN_SC; + cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; - writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT); + cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; + writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); } void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active) @@ -382,15 +376,14 @@ void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active) u32 cfg = 0; if (active) { - cfg |= S5P_CIIMGEFF_IE_SC_AFTER | S5P_CIIMGEFF_IE_ENABLE; + cfg |= FIMC_REG_CIIMGEFF_IE_SC_AFTER | + FIMC_REG_CIIMGEFF_IE_ENABLE; cfg |= effect->type; - if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) { - cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb); - cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr); - } + if (effect->type == FIMC_REG_CIIMGEFF_FIN_ARBITRARY) + cfg |= (effect->pat_cb << 13) | effect->pat_cr; } - writel(cfg, dev->regs + S5P_CIIMGEFF); + writel(cfg, dev->regs + FIMC_REG_CIIMGEFF); } void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx) @@ -402,10 +395,10 @@ void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx) if (!(frame->fmt->flags & FMT_HAS_ALPHA)) return; - cfg = readl(dev->regs + S5P_CIOCTRL); - cfg &= ~S5P_CIOCTRL_ALPHA_OUT_MASK; + cfg = readl(dev->regs + FIMC_REG_CIOCTRL); + cfg &= ~FIMC_REG_CIOCTRL_ALPHA_OUT_MASK; cfg |= (frame->alpha << 4); - writel(cfg, dev->regs + S5P_CIOCTRL); + writel(cfg, dev->regs + FIMC_REG_CIOCTRL); } static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx) @@ -416,15 +409,13 @@ static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx) u32 cfg_r = 0; if (FIMC_LCDFIFO == ctx->out_path) - cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN; + cfg_r |= FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; - cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width); - cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height); - cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width); - cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height); + cfg_o |= (frame->f_height << 16) | frame->f_width; + cfg_r |= (frame->height << 16) | frame->width; - writel(cfg_o, dev->regs + S5P_ORGISIZE); - writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE); + writel(cfg_o, dev->regs + FIMC_REG_ORGISIZE); + writel(cfg_r, dev->regs + FIMC_REG_CIREAL_ISIZE); } void fimc_hw_set_in_dma(struct fimc_ctx *ctx) @@ -435,17 +426,14 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) u32 cfg; /* Set the pixel offsets. */ - cfg = S5P_CIO_OFFS_HOR(offset->y_h); - cfg |= S5P_CIO_OFFS_VER(offset->y_v); - writel(cfg, dev->regs + S5P_CIIYOFF); + cfg = (offset->y_v << 16) | offset->y_h; + writel(cfg, dev->regs + FIMC_REG_CIIYOFF); - cfg = S5P_CIO_OFFS_HOR(offset->cb_h); - cfg |= S5P_CIO_OFFS_VER(offset->cb_v); - writel(cfg, dev->regs + S5P_CIICBOFF); + cfg = (offset->cb_v << 16) | offset->cb_h; + writel(cfg, dev->regs + FIMC_REG_CIICBOFF); - cfg = S5P_CIO_OFFS_HOR(offset->cr_h); - cfg |= S5P_CIO_OFFS_VER(offset->cr_v); - writel(cfg, dev->regs + S5P_CIICROFF); + cfg = (offset->cr_v << 16) | offset->cr_h; + writel(cfg, dev->regs + FIMC_REG_CIICROFF); /* Input original and real size. */ fimc_hw_set_in_dma_size(ctx); @@ -454,61 +442,61 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO); /* Set the input DMA to process single frame only. */ - cfg = readl(dev->regs + S5P_MSCTRL); - cfg &= ~(S5P_MSCTRL_INFORMAT_MASK - | S5P_MSCTRL_IN_BURST_COUNT_MASK - | S5P_MSCTRL_INPUT_MASK - | S5P_MSCTRL_C_INT_IN_MASK - | S5P_MSCTRL_2P_IN_ORDER_MASK); + cfg = readl(dev->regs + FIMC_REG_MSCTRL); + cfg &= ~(FIMC_REG_MSCTRL_INFORMAT_MASK + | FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK + | FIMC_REG_MSCTRL_INPUT_MASK + | FIMC_REG_MSCTRL_C_INT_IN_MASK + | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK); - cfg |= (S5P_MSCTRL_IN_BURST_COUNT(4) - | S5P_MSCTRL_INPUT_MEMORY - | S5P_MSCTRL_FIFO_CTRL_FULL); + cfg |= (FIMC_REG_MSCTRL_IN_BURST_COUNT(4) + | FIMC_REG_MSCTRL_INPUT_MEMORY + | FIMC_REG_MSCTRL_FIFO_CTRL_FULL); switch (frame->fmt->color) { case S5P_FIMC_RGB565...S5P_FIMC_RGB888: - cfg |= S5P_MSCTRL_INFORMAT_RGB; + cfg |= FIMC_REG_MSCTRL_INFORMAT_RGB; break; case S5P_FIMC_YCBCR420: - cfg |= S5P_MSCTRL_INFORMAT_YCBCR420; + cfg |= FIMC_REG_MSCTRL_INFORMAT_YCBCR420; if (frame->fmt->colplanes == 2) - cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE; + cfg |= ctx->in_order_2p | FIMC_REG_MSCTRL_C_INT_IN_2PLANE; else - cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; + cfg |= FIMC_REG_MSCTRL_C_INT_IN_3PLANE; break; case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422: if (frame->fmt->colplanes == 1) { cfg |= ctx->in_order_1p - | S5P_MSCTRL_INFORMAT_YCBCR422_1P; + | FIMC_REG_MSCTRL_INFORMAT_YCBCR422_1P; } else { - cfg |= S5P_MSCTRL_INFORMAT_YCBCR422; + cfg |= FIMC_REG_MSCTRL_INFORMAT_YCBCR422; if (frame->fmt->colplanes == 2) cfg |= ctx->in_order_2p - | S5P_MSCTRL_C_INT_IN_2PLANE; + | FIMC_REG_MSCTRL_C_INT_IN_2PLANE; else - cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; + cfg |= FIMC_REG_MSCTRL_C_INT_IN_3PLANE; } break; default: break; } - writel(cfg, dev->regs + S5P_MSCTRL); + writel(cfg, dev->regs + FIMC_REG_MSCTRL); /* Input/output DMA linear/tiled mode. */ - cfg = readl(dev->regs + S5P_CIDMAPARAM); - cfg &= ~S5P_CIDMAPARAM_TILE_MASK; + cfg = readl(dev->regs + FIMC_REG_CIDMAPARAM); + cfg &= ~FIMC_REG_CIDMAPARAM_TILE_MASK; if (tiled_fmt(ctx->s_frame.fmt)) - cfg |= S5P_CIDMAPARAM_R_64X32; + cfg |= FIMC_REG_CIDMAPARAM_R_64X32; if (tiled_fmt(ctx->d_frame.fmt)) - cfg |= S5P_CIDMAPARAM_W_64X32; + cfg |= FIMC_REG_CIDMAPARAM_W_64X32; - writel(cfg, dev->regs + S5P_CIDMAPARAM); + writel(cfg, dev->regs + FIMC_REG_CIDMAPARAM); } @@ -516,40 +504,40 @@ void fimc_hw_set_input_path(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - u32 cfg = readl(dev->regs + S5P_MSCTRL); - cfg &= ~S5P_MSCTRL_INPUT_MASK; + u32 cfg = readl(dev->regs + FIMC_REG_MSCTRL); + cfg &= ~FIMC_REG_MSCTRL_INPUT_MASK; if (ctx->in_path == FIMC_DMA) - cfg |= S5P_MSCTRL_INPUT_MEMORY; + cfg |= FIMC_REG_MSCTRL_INPUT_MEMORY; else - cfg |= S5P_MSCTRL_INPUT_EXTCAM; + cfg |= FIMC_REG_MSCTRL_INPUT_EXTCAM; - writel(cfg, dev->regs + S5P_MSCTRL); + writel(cfg, dev->regs + FIMC_REG_MSCTRL); } void fimc_hw_set_output_path(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - u32 cfg = readl(dev->regs + S5P_CISCCTRL); - cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO; + u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); + cfg &= ~FIMC_REG_CISCCTRL_LCDPATHEN_FIFO; if (ctx->out_path == FIMC_LCDFIFO) - cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO; - writel(cfg, dev->regs + S5P_CISCCTRL); + cfg |= FIMC_REG_CISCCTRL_LCDPATHEN_FIFO; + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); } void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr) { - u32 cfg = readl(dev->regs + S5P_CIREAL_ISIZE); - cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS; - writel(cfg, dev->regs + S5P_CIREAL_ISIZE); + u32 cfg = readl(dev->regs + FIMC_REG_CIREAL_ISIZE); + cfg |= FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS; + writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE); - writel(paddr->y, dev->regs + S5P_CIIYSA(0)); - writel(paddr->cb, dev->regs + S5P_CIICBSA(0)); - writel(paddr->cr, dev->regs + S5P_CIICRSA(0)); + writel(paddr->y, dev->regs + FIMC_REG_CIIYSA(0)); + writel(paddr->cb, dev->regs + FIMC_REG_CIICBSA(0)); + writel(paddr->cr, dev->regs + FIMC_REG_CIICRSA(0)); - cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS; - writel(cfg, dev->regs + S5P_CIREAL_ISIZE); + cfg &= ~FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS; + writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE); } void fimc_hw_set_output_addr(struct fimc_dev *dev, @@ -557,9 +545,9 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev, { int i = (index == -1) ? 0 : index; do { - writel(paddr->y, dev->regs + S5P_CIOYSA(i)); - writel(paddr->cb, dev->regs + S5P_CIOCBSA(i)); - writel(paddr->cr, dev->regs + S5P_CIOCRSA(i)); + writel(paddr->y, dev->regs + FIMC_REG_CIOYSA(i)); + writel(paddr->cb, dev->regs + FIMC_REG_CIOCBSA(i)); + writel(paddr->cr, dev->regs + FIMC_REG_CIOCRSA(i)); dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", i, paddr->y, paddr->cb, paddr->cr); } while (index == -1 && ++i < FIMC_MAX_OUT_BUFS); @@ -568,32 +556,45 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev, int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, struct s5p_fimc_isp_info *cam) { - u32 cfg = readl(fimc->regs + S5P_CIGCTRL); + u32 cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); - cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC | - S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC | - S5P_CIGCTRL_INVPOLFIELD); + cfg &= ~(FIMC_REG_CIGCTRL_INVPOLPCLK | FIMC_REG_CIGCTRL_INVPOLVSYNC | + FIMC_REG_CIGCTRL_INVPOLHREF | FIMC_REG_CIGCTRL_INVPOLHSYNC | + FIMC_REG_CIGCTRL_INVPOLFIELD); if (cam->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - cfg |= S5P_CIGCTRL_INVPOLPCLK; + cfg |= FIMC_REG_CIGCTRL_INVPOLPCLK; if (cam->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - cfg |= S5P_CIGCTRL_INVPOLVSYNC; + cfg |= FIMC_REG_CIGCTRL_INVPOLVSYNC; if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - cfg |= S5P_CIGCTRL_INVPOLHREF; + cfg |= FIMC_REG_CIGCTRL_INVPOLHREF; if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - cfg |= S5P_CIGCTRL_INVPOLHSYNC; + cfg |= FIMC_REG_CIGCTRL_INVPOLHSYNC; if (cam->flags & V4L2_MBUS_FIELD_EVEN_LOW) - cfg |= S5P_CIGCTRL_INVPOLFIELD; + cfg |= FIMC_REG_CIGCTRL_INVPOLFIELD; - writel(cfg, fimc->regs + S5P_CIGCTRL); + writel(cfg, fimc->regs + FIMC_REG_CIGCTRL); return 0; } +struct mbus_pixfmt_desc { + u32 pixelcode; + u32 cisrcfmt; + u16 bus_width; +}; + +static const struct mbus_pixfmt_desc pix_desc[] = { + { V4L2_MBUS_FMT_YUYV8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCBYCR, 8 }, + { V4L2_MBUS_FMT_YVYU8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCRYCB, 8 }, + { V4L2_MBUS_FMT_VYUY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CRYCBY, 8 }, + { V4L2_MBUS_FMT_UYVY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CBYCRY, 8 }, +}; + int fimc_hw_set_camera_source(struct fimc_dev *fimc, struct s5p_fimc_isp_info *cam) { @@ -602,18 +603,6 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, u32 bus_width; int i; - static const struct { - u32 pixelcode; - u32 cisrcfmt; - u16 bus_width; - } pix_desc[] = { - { V4L2_MBUS_FMT_YUYV8_2X8, S5P_CISRCFMT_ORDER422_YCBYCR, 8 }, - { V4L2_MBUS_FMT_YVYU8_2X8, S5P_CISRCFMT_ORDER422_YCRYCB, 8 }, - { V4L2_MBUS_FMT_VYUY8_2X8, S5P_CISRCFMT_ORDER422_CRYCBY, 8 }, - { V4L2_MBUS_FMT_UYVY8_2X8, S5P_CISRCFMT_ORDER422_CBYCRY, 8 }, - /* TODO: Add pixel codes for 16-bit bus width */ - }; - if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) { @@ -632,41 +621,37 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, if (cam->bus_type == FIMC_ITU_601) { if (bus_width == 8) - cfg |= S5P_CISRCFMT_ITU601_8BIT; + cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; else if (bus_width == 16) - cfg |= S5P_CISRCFMT_ITU601_16BIT; + cfg |= FIMC_REG_CISRCFMT_ITU601_16BIT; } /* else defaults to ITU-R BT.656 8-bit */ } else if (cam->bus_type == FIMC_MIPI_CSI2) { if (fimc_fmt_is_jpeg(f->fmt->color)) - cfg |= S5P_CISRCFMT_ITU601_8BIT; + cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; } - cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height); - writel(cfg, fimc->regs + S5P_CISRCFMT); + cfg |= (f->o_width << 16) | f->o_height; + writel(cfg, fimc->regs + FIMC_REG_CISRCFMT); return 0; } - -int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) +void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) { u32 hoff2, voff2; - u32 cfg = readl(fimc->regs + S5P_CIWDOFST); + u32 cfg = readl(fimc->regs + FIMC_REG_CIWDOFST); - cfg &= ~(S5P_CIWDOFST_HOROFF_MASK | S5P_CIWDOFST_VEROFF_MASK); - cfg |= S5P_CIWDOFST_OFF_EN | - S5P_CIWDOFST_HOROFF(f->offs_h) | - S5P_CIWDOFST_VEROFF(f->offs_v); + cfg &= ~(FIMC_REG_CIWDOFST_HOROFF_MASK | FIMC_REG_CIWDOFST_VEROFF_MASK); + cfg |= FIMC_REG_CIWDOFST_OFF_EN | + (f->offs_h << 16) | f->offs_v; - writel(cfg, fimc->regs + S5P_CIWDOFST); + writel(cfg, fimc->regs + FIMC_REG_CIWDOFST); /* See CIWDOFSTn register description in the datasheet for details. */ hoff2 = f->o_width - f->width - f->offs_h; voff2 = f->o_height - f->height - f->offs_v; - cfg = S5P_CIWDOFST2_HOROFF(hoff2) | S5P_CIWDOFST2_VEROFF(voff2); - - writel(cfg, fimc->regs + S5P_CIWDOFST2); - return 0; + cfg = (hoff2 << 16) | voff2; + writel(cfg, fimc->regs + FIMC_REG_CIWDOFST2); } int fimc_hw_set_camera_type(struct fimc_dev *fimc, @@ -676,27 +661,27 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, struct fimc_vid_cap *vid_cap = &fimc->vid_cap; u32 csis_data_alignment = 32; - cfg = readl(fimc->regs + S5P_CIGCTRL); + cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); /* Select ITU B interface, disable Writeback path and test pattern. */ - cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A | - S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB | - S5P_CIGCTRL_SELCAM_MIPI_A | S5P_CIGCTRL_CAM_JPEG); + cfg &= ~(FIMC_REG_CIGCTRL_TESTPAT_MASK | FIMC_REG_CIGCTRL_SELCAM_ITU_A | + FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB | + FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG); if (cam->bus_type == FIMC_MIPI_CSI2) { - cfg |= S5P_CIGCTRL_SELCAM_MIPI; + cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI; if (cam->mux_id == 0) - cfg |= S5P_CIGCTRL_SELCAM_MIPI_A; + cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI_A; /* TODO: add remaining supported formats. */ switch (vid_cap->mf.code) { case V4L2_MBUS_FMT_VYUY8_2X8: - tmp = S5P_CSIIMGFMT_YCBCR422_8BIT; + tmp = FIMC_REG_CSIIMGFMT_YCBCR422_8BIT; break; case V4L2_MBUS_FMT_JPEG_1X8: - tmp = S5P_CSIIMGFMT_USER(1); - cfg |= S5P_CIGCTRL_CAM_JPEG; + tmp = FIMC_REG_CSIIMGFMT_USER(1); + cfg |= FIMC_REG_CIGCTRL_CAM_JPEG; break; default: v4l2_err(fimc->vid_cap.vfd, @@ -706,19 +691,84 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, } tmp |= (csis_data_alignment == 32) << 8; - writel(tmp, fimc->regs + S5P_CSIIMGFMT); + writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT); } else if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ - cfg |= S5P_CIGCTRL_SELCAM_ITU_A; + cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A; } else if (cam->bus_type == FIMC_LCD_WB) { - cfg |= S5P_CIGCTRL_CAMIF_SELWB; + cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; } else { err("invalid camera bus type selected\n"); return -EINVAL; } - writel(cfg, fimc->regs + S5P_CIGCTRL); + writel(cfg, fimc->regs + FIMC_REG_CIGCTRL); return 0; } + +void fimc_hw_clear_irq(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CIGCTRL); + cfg |= FIMC_REG_CIGCTRL_IRQ_CLR; + writel(cfg, dev->regs + FIMC_REG_CIGCTRL); +} + +void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); + if (on) + cfg |= FIMC_REG_CISCCTRL_SCALERSTART; + else + cfg &= ~FIMC_REG_CISCCTRL_SCALERSTART; + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); +} + +void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on) +{ + u32 cfg = readl(dev->regs + FIMC_REG_MSCTRL); + if (on) + cfg |= FIMC_REG_MSCTRL_ENVID; + else + cfg &= ~FIMC_REG_MSCTRL_ENVID; + writel(cfg, dev->regs + FIMC_REG_MSCTRL); +} + +void fimc_hw_dis_capture(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | FIMC_REG_CIIMGCPT_IMGCPTEN_SC); + writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); +} + +/* Return an index to the buffer actually being written. */ +u32 fimc_hw_get_frame_index(struct fimc_dev *dev) +{ + u32 reg; + + if (dev->variant->has_cistatus2) { + reg = readl(dev->regs + FIMC_REG_CISTATUS2) & 0x3F; + return reg > 0 ? --reg : reg; + } + + reg = readl(dev->regs + FIMC_REG_CISTATUS); + + return (reg & FIMC_REG_CISTATUS_FRAMECNT_MASK) >> + FIMC_REG_CISTATUS_FRAMECNT_SHIFT; +} + +/* Locking: the caller holds fimc->slock */ +void fimc_activate_capture(struct fimc_ctx *ctx) +{ + fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled); + fimc_hw_en_capture(ctx); +} + +void fimc_deactivate_capture(struct fimc_dev *fimc) +{ + fimc_hw_en_lastirq(fimc, true); + fimc_hw_dis_capture(fimc); + fimc_hw_enable_scaler(fimc, false); + fimc_hw_en_lastirq(fimc, false); +} diff --git a/drivers/media/video/s5p-fimc/fimc-reg.h b/drivers/media/video/s5p-fimc/fimc-reg.h new file mode 100644 index 000000000000..1472880b94ff --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-reg.h @@ -0,0 +1,326 @@ +/* + * Samsung camera host interface (FIMC) registers definition + * + * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_REG_H_ +#define FIMC_REG_H_ + +#include "fimc-core.h" + +/* Input source format */ +#define FIMC_REG_CISRCFMT 0x00 +#define FIMC_REG_CISRCFMT_ITU601_8BIT (1 << 31) +#define FIMC_REG_CISRCFMT_ITU601_16BIT (1 << 29) +#define FIMC_REG_CISRCFMT_ORDER422_YCBYCR (0 << 14) +#define FIMC_REG_CISRCFMT_ORDER422_YCRYCB (1 << 14) +#define FIMC_REG_CISRCFMT_ORDER422_CBYCRY (2 << 14) +#define FIMC_REG_CISRCFMT_ORDER422_CRYCBY (3 << 14) + +/* Window offset */ +#define FIMC_REG_CIWDOFST 0x04 +#define FIMC_REG_CIWDOFST_OFF_EN (1 << 31) +#define FIMC_REG_CIWDOFST_CLROVFIY (1 << 30) +#define FIMC_REG_CIWDOFST_CLROVRLB (1 << 29) +#define FIMC_REG_CIWDOFST_HOROFF_MASK (0x7ff << 16) +#define FIMC_REG_CIWDOFST_CLROVFICB (1 << 15) +#define FIMC_REG_CIWDOFST_CLROVFICR (1 << 14) +#define FIMC_REG_CIWDOFST_VEROFF_MASK (0xfff << 0) + +/* Global control */ +#define FIMC_REG_CIGCTRL 0x08 +#define FIMC_REG_CIGCTRL_SWRST (1 << 31) +#define FIMC_REG_CIGCTRL_CAMRST_A (1 << 30) +#define FIMC_REG_CIGCTRL_SELCAM_ITU_A (1 << 29) +#define FIMC_REG_CIGCTRL_TESTPAT_NORMAL (0 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_HOR_INC (2 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_VER_INC (3 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_MASK (3 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_SHIFT 27 +#define FIMC_REG_CIGCTRL_INVPOLPCLK (1 << 26) +#define FIMC_REG_CIGCTRL_INVPOLVSYNC (1 << 25) +#define FIMC_REG_CIGCTRL_INVPOLHREF (1 << 24) +#define FIMC_REG_CIGCTRL_IRQ_OVFEN (1 << 22) +#define FIMC_REG_CIGCTRL_HREF_MASK (1 << 21) +#define FIMC_REG_CIGCTRL_IRQ_LEVEL (1 << 20) +#define FIMC_REG_CIGCTRL_IRQ_CLR (1 << 19) +#define FIMC_REG_CIGCTRL_IRQ_ENABLE (1 << 16) +#define FIMC_REG_CIGCTRL_SHDW_DISABLE (1 << 12) +#define FIMC_REG_CIGCTRL_CAM_JPEG (1 << 8) +#define FIMC_REG_CIGCTRL_SELCAM_MIPI_A (1 << 7) +#define FIMC_REG_CIGCTRL_CAMIF_SELWB (1 << 6) +/* 0 - ITU601; 1 - ITU709 */ +#define FIMC_REG_CIGCTRL_CSC_ITU601_709 (1 << 5) +#define FIMC_REG_CIGCTRL_INVPOLHSYNC (1 << 4) +#define FIMC_REG_CIGCTRL_SELCAM_MIPI (1 << 3) +#define FIMC_REG_CIGCTRL_INVPOLFIELD (1 << 1) +#define FIMC_REG_CIGCTRL_INTERLACE (1 << 0) + +/* Window offset 2 */ +#define FIMC_REG_CIWDOFST2 0x14 +#define FIMC_REG_CIWDOFST2_HOROFF_MASK (0xfff << 16) +#define FIMC_REG_CIWDOFST2_VEROFF_MASK (0xfff << 0) + +/* Output DMA Y/Cb/Cr plane start addresses */ +#define FIMC_REG_CIOYSA(n) (0x18 + (n) * 4) +#define FIMC_REG_CIOCBSA(n) (0x28 + (n) * 4) +#define FIMC_REG_CIOCRSA(n) (0x38 + (n) * 4) + +/* Target image format */ +#define FIMC_REG_CITRGFMT 0x48 +#define FIMC_REG_CITRGFMT_INROT90 (1 << 31) +#define FIMC_REG_CITRGFMT_YCBCR420 (0 << 29) +#define FIMC_REG_CITRGFMT_YCBCR422 (1 << 29) +#define FIMC_REG_CITRGFMT_YCBCR422_1P (2 << 29) +#define FIMC_REG_CITRGFMT_RGB (3 << 29) +#define FIMC_REG_CITRGFMT_FMT_MASK (3 << 29) +#define FIMC_REG_CITRGFMT_HSIZE_MASK (0xfff << 16) +#define FIMC_REG_CITRGFMT_FLIP_SHIFT 14 +#define FIMC_REG_CITRGFMT_FLIP_NORMAL (0 << 14) +#define FIMC_REG_CITRGFMT_FLIP_X_MIRROR (1 << 14) +#define FIMC_REG_CITRGFMT_FLIP_Y_MIRROR (2 << 14) +#define FIMC_REG_CITRGFMT_FLIP_180 (3 << 14) +#define FIMC_REG_CITRGFMT_FLIP_MASK (3 << 14) +#define FIMC_REG_CITRGFMT_OUTROT90 (1 << 13) +#define FIMC_REG_CITRGFMT_VSIZE_MASK (0xfff << 0) + +/* Output DMA control */ +#define FIMC_REG_CIOCTRL 0x4c +#define FIMC_REG_CIOCTRL_ORDER422_MASK (3 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (0 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (1 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (2 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (3 << 0) +#define FIMC_REG_CIOCTRL_LASTIRQ_ENABLE (1 << 2) +#define FIMC_REG_CIOCTRL_YCBCR_3PLANE (0 << 3) +#define FIMC_REG_CIOCTRL_YCBCR_2PLANE (1 << 3) +#define FIMC_REG_CIOCTRL_YCBCR_PLANE_MASK (1 << 3) +#define FIMC_REG_CIOCTRL_ALPHA_OUT_MASK (0xff << 4) +#define FIMC_REG_CIOCTRL_RGB16FMT_MASK (3 << 16) +#define FIMC_REG_CIOCTRL_RGB565 (0 << 16) +#define FIMC_REG_CIOCTRL_ARGB1555 (1 << 16) +#define FIMC_REG_CIOCTRL_ARGB4444 (2 << 16) +#define FIMC_REG_CIOCTRL_ORDER2P_SHIFT 24 +#define FIMC_REG_CIOCTRL_ORDER2P_MASK (3 << 24) +#define FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24) + +/* Pre-scaler control 1 */ +#define FIMC_REG_CISCPRERATIO 0x50 + +#define FIMC_REG_CISCPREDST 0x54 + +/* Main scaler control */ +#define FIMC_REG_CISCCTRL 0x58 +#define FIMC_REG_CISCCTRL_SCALERBYPASS (1 << 31) +#define FIMC_REG_CISCCTRL_SCALEUP_H (1 << 30) +#define FIMC_REG_CISCCTRL_SCALEUP_V (1 << 29) +#define FIMC_REG_CISCCTRL_CSCR2Y_WIDE (1 << 28) +#define FIMC_REG_CISCCTRL_CSCY2R_WIDE (1 << 27) +#define FIMC_REG_CISCCTRL_LCDPATHEN_FIFO (1 << 26) +#define FIMC_REG_CISCCTRL_INTERLACE (1 << 25) +#define FIMC_REG_CISCCTRL_SCALERSTART (1 << 15) +#define FIMC_REG_CISCCTRL_INRGB_FMT_RGB565 (0 << 13) +#define FIMC_REG_CISCCTRL_INRGB_FMT_RGB666 (1 << 13) +#define FIMC_REG_CISCCTRL_INRGB_FMT_RGB888 (2 << 13) +#define FIMC_REG_CISCCTRL_INRGB_FMT_MASK (3 << 13) +#define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) +#define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) +#define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) +#define FIMC_REG_CISCCTRL_OUTRGB_FMT_MASK (3 << 11) +#define FIMC_REG_CISCCTRL_RGB_EXT (1 << 10) +#define FIMC_REG_CISCCTRL_ONE2ONE (1 << 9) +#define FIMC_REG_CISCCTRL_MHRATIO(x) ((x) << 16) +#define FIMC_REG_CISCCTRL_MVRATIO(x) ((x) << 0) +#define FIMC_REG_CISCCTRL_MHRATIO_MASK (0x1ff << 16) +#define FIMC_REG_CISCCTRL_MVRATIO_MASK (0x1ff << 0) +#define FIMC_REG_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16) +#define FIMC_REG_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0) + +/* Target area */ +#define FIMC_REG_CITAREA 0x5c +#define FIMC_REG_CITAREA_MASK 0x0fffffff + +/* General status */ +#define FIMC_REG_CISTATUS 0x64 +#define FIMC_REG_CISTATUS_OVFIY (1 << 31) +#define FIMC_REG_CISTATUS_OVFICB (1 << 30) +#define FIMC_REG_CISTATUS_OVFICR (1 << 29) +#define FIMC_REG_CISTATUS_VSYNC (1 << 28) +#define FIMC_REG_CISTATUS_FRAMECNT_MASK (3 << 26) +#define FIMC_REG_CISTATUS_FRAMECNT_SHIFT 26 +#define FIMC_REG_CISTATUS_WINOFF_EN (1 << 25) +#define FIMC_REG_CISTATUS_IMGCPT_EN (1 << 22) +#define FIMC_REG_CISTATUS_IMGCPT_SCEN (1 << 21) +#define FIMC_REG_CISTATUS_VSYNC_A (1 << 20) +#define FIMC_REG_CISTATUS_VSYNC_B (1 << 19) +#define FIMC_REG_CISTATUS_OVRLB (1 << 18) +#define FIMC_REG_CISTATUS_FRAME_END (1 << 17) +#define FIMC_REG_CISTATUS_LASTCAPT_END (1 << 16) +#define FIMC_REG_CISTATUS_VVALID_A (1 << 15) +#define FIMC_REG_CISTATUS_VVALID_B (1 << 14) + +/* Indexes to the last and the currently processed buffer. */ +#define FIMC_REG_CISTATUS2 0x68 + +/* Image capture control */ +#define FIMC_REG_CIIMGCPT 0xc0 +#define FIMC_REG_CIIMGCPT_IMGCPTEN (1 << 31) +#define FIMC_REG_CIIMGCPT_IMGCPTEN_SC (1 << 30) +#define FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE (1 << 25) +#define FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT (1 << 18) + +/* Frame capture sequence */ +#define FIMC_REG_CICPTSEQ 0xc4 + +/* Image effect */ +#define FIMC_REG_CIIMGEFF 0xd0 +#define FIMC_REG_CIIMGEFF_IE_ENABLE (1 << 30) +#define FIMC_REG_CIIMGEFF_IE_SC_BEFORE (0 << 29) +#define FIMC_REG_CIIMGEFF_IE_SC_AFTER (1 << 29) +#define FIMC_REG_CIIMGEFF_FIN_BYPASS (0 << 26) +#define FIMC_REG_CIIMGEFF_FIN_ARBITRARY (1 << 26) +#define FIMC_REG_CIIMGEFF_FIN_NEGATIVE (2 << 26) +#define FIMC_REG_CIIMGEFF_FIN_ARTFREEZE (3 << 26) +#define FIMC_REG_CIIMGEFF_FIN_EMBOSSING (4 << 26) +#define FIMC_REG_CIIMGEFF_FIN_SILHOUETTE (5 << 26) +#define FIMC_REG_CIIMGEFF_FIN_MASK (7 << 26) +#define FIMC_REG_CIIMGEFF_PAT_CBCR_MASK ((0xff << 13) | 0xff) + +/* Input DMA Y/Cb/Cr plane start address 0/1 */ +#define FIMC_REG_CIIYSA(n) (0xd4 + (n) * 0x70) +#define FIMC_REG_CIICBSA(n) (0xd8 + (n) * 0x70) +#define FIMC_REG_CIICRSA(n) (0xdc + (n) * 0x70) + +/* Real input DMA image size */ +#define FIMC_REG_CIREAL_ISIZE 0xf8 +#define FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31) +#define FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30) + +/* Input DMA control */ +#define FIMC_REG_MSCTRL 0xfc +#define FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK (0xf << 24) +#define FIMC_REG_MSCTRL_2P_IN_ORDER_MASK (3 << 16) +#define FIMC_REG_MSCTRL_2P_IN_ORDER_SHIFT 16 +#define FIMC_REG_MSCTRL_C_INT_IN_3PLANE (0 << 15) +#define FIMC_REG_MSCTRL_C_INT_IN_2PLANE (1 << 15) +#define FIMC_REG_MSCTRL_C_INT_IN_MASK (1 << 15) +#define FIMC_REG_MSCTRL_FLIP_SHIFT 13 +#define FIMC_REG_MSCTRL_FLIP_MASK (3 << 13) +#define FIMC_REG_MSCTRL_FLIP_NORMAL (0 << 13) +#define FIMC_REG_MSCTRL_FLIP_X_MIRROR (1 << 13) +#define FIMC_REG_MSCTRL_FLIP_Y_MIRROR (2 << 13) +#define FIMC_REG_MSCTRL_FLIP_180 (3 << 13) +#define FIMC_REG_MSCTRL_FIFO_CTRL_FULL (1 << 12) +#define FIMC_REG_MSCTRL_ORDER422_SHIFT 4 +#define FIMC_REG_MSCTRL_ORDER422_YCBYCR (0 << 4) +#define FIMC_REG_MSCTRL_ORDER422_CBYCRY (1 << 4) +#define FIMC_REG_MSCTRL_ORDER422_YCRYCB (2 << 4) +#define FIMC_REG_MSCTRL_ORDER422_CRYCBY (3 << 4) +#define FIMC_REG_MSCTRL_ORDER422_MASK (3 << 4) +#define FIMC_REG_MSCTRL_INPUT_EXTCAM (0 << 3) +#define FIMC_REG_MSCTRL_INPUT_MEMORY (1 << 3) +#define FIMC_REG_MSCTRL_INPUT_MASK (1 << 3) +#define FIMC_REG_MSCTRL_INFORMAT_YCBCR420 (0 << 1) +#define FIMC_REG_MSCTRL_INFORMAT_YCBCR422 (1 << 1) +#define FIMC_REG_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1) +#define FIMC_REG_MSCTRL_INFORMAT_RGB (3 << 1) +#define FIMC_REG_MSCTRL_INFORMAT_MASK (3 << 1) +#define FIMC_REG_MSCTRL_ENVID (1 << 0) +#define FIMC_REG_MSCTRL_IN_BURST_COUNT(x) ((x) << 24) + +/* Output DMA Y/Cb/Cr offset */ +#define FIMC_REG_CIOYOFF 0x168 +#define FIMC_REG_CIOCBOFF 0x16c +#define FIMC_REG_CIOCROFF 0x170 + +/* Input DMA Y/Cb/Cr offset */ +#define FIMC_REG_CIIYOFF 0x174 +#define FIMC_REG_CIICBOFF 0x178 +#define FIMC_REG_CIICROFF 0x17c + +/* Input DMA original image size */ +#define FIMC_REG_ORGISIZE 0x180 + +/* Output DMA original image size */ +#define FIMC_REG_ORGOSIZE 0x184 + +/* Real output DMA image size (extension register) */ +#define FIMC_REG_CIEXTEN 0x188 +#define FIMC_REG_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10) +#define FIMC_REG_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f) +#define FIMC_REG_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10) +#define FIMC_REG_CIEXTEN_MVRATIO_EXT_MASK 0x3f + +#define FIMC_REG_CIDMAPARAM 0x18c +#define FIMC_REG_CIDMAPARAM_R_LINEAR (0 << 29) +#define FIMC_REG_CIDMAPARAM_R_64X32 (3 << 29) +#define FIMC_REG_CIDMAPARAM_W_LINEAR (0 << 13) +#define FIMC_REG_CIDMAPARAM_W_64X32 (3 << 13) +#define FIMC_REG_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13)) + +/* MIPI CSI image format */ +#define FIMC_REG_CSIIMGFMT 0x194 +#define FIMC_REG_CSIIMGFMT_YCBCR422_8BIT 0x1e +#define FIMC_REG_CSIIMGFMT_RAW8 0x2a +#define FIMC_REG_CSIIMGFMT_RAW10 0x2b +#define FIMC_REG_CSIIMGFMT_RAW12 0x2c +/* User defined formats. x = 0...16. */ +#define FIMC_REG_CSIIMGFMT_USER(x) (0x30 + x - 1) + +/* Output frame buffer sequence mask */ +#define FIMC_REG_CIFCNTSEQ 0x1fc + +/* + * Function declarations + */ +void fimc_hw_reset(struct fimc_dev *fimc); +void fimc_hw_set_rotation(struct fimc_ctx *ctx); +void fimc_hw_set_target_format(struct fimc_ctx *ctx); +void fimc_hw_set_out_dma(struct fimc_ctx *ctx); +void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); +void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); +void fimc_hw_set_prescaler(struct fimc_ctx *ctx); +void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); +void fimc_hw_en_capture(struct fimc_ctx *ctx); +void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active); +void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); +void fimc_hw_set_in_dma(struct fimc_ctx *ctx); +void fimc_hw_set_input_path(struct fimc_ctx *ctx); +void fimc_hw_set_output_path(struct fimc_ctx *ctx); +void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); +void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, + int index); +int fimc_hw_set_camera_source(struct fimc_dev *fimc, + struct s5p_fimc_isp_info *cam); +void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); +int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, + struct s5p_fimc_isp_info *cam); +int fimc_hw_set_camera_type(struct fimc_dev *fimc, + struct s5p_fimc_isp_info *cam); +void fimc_hw_clear_irq(struct fimc_dev *dev); +void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on); +void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); +void fimc_hw_dis_capture(struct fimc_dev *dev); +u32 fimc_hw_get_frame_index(struct fimc_dev *dev); +void fimc_activate_capture(struct fimc_ctx *ctx); +void fimc_deactivate_capture(struct fimc_dev *fimc); + +/** + * fimc_hw_set_dma_seq - configure output DMA buffer sequence + * @mask: bitmask for the DMA output buffer registers, set to 0 to skip buffer + * This function masks output DMA ring buffers, it allows to select which of + * the 32 available output buffer address registers will be used by the DMA + * engine. + */ +static inline void fimc_hw_set_dma_seq(struct fimc_dev *dev, u32 mask) +{ + writel(mask, dev->regs + FIMC_REG_CIFCNTSEQ); +} + +#endif /* FIMC_REG_H_ */ diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h deleted file mode 100644 index c7a5bc51d571..000000000000 --- a/drivers/media/video/s5p-fimc/regs-fimc.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Register definition file for Samsung Camera Interface (FIMC) driver - * - * Copyright (c) 2010 Samsung Electronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef REGS_FIMC_H_ -#define REGS_FIMC_H_ - -/* Input source format */ -#define S5P_CISRCFMT 0x00 -#define S5P_CISRCFMT_ITU601_8BIT (1 << 31) -#define S5P_CISRCFMT_ITU601_16BIT (1 << 29) -#define S5P_CISRCFMT_ORDER422_YCBYCR (0 << 14) -#define S5P_CISRCFMT_ORDER422_YCRYCB (1 << 14) -#define S5P_CISRCFMT_ORDER422_CBYCRY (2 << 14) -#define S5P_CISRCFMT_ORDER422_CRYCBY (3 << 14) -#define S5P_CISRCFMT_HSIZE(x) ((x) << 16) -#define S5P_CISRCFMT_VSIZE(x) ((x) << 0) - -/* Window offset */ -#define S5P_CIWDOFST 0x04 -#define S5P_CIWDOFST_OFF_EN (1 << 31) -#define S5P_CIWDOFST_CLROVFIY (1 << 30) -#define S5P_CIWDOFST_CLROVRLB (1 << 29) -#define S5P_CIWDOFST_HOROFF_MASK (0x7ff << 16) -#define S5P_CIWDOFST_CLROVFICB (1 << 15) -#define S5P_CIWDOFST_CLROVFICR (1 << 14) -#define S5P_CIWDOFST_HOROFF(x) ((x) << 16) -#define S5P_CIWDOFST_VEROFF(x) ((x) << 0) -#define S5P_CIWDOFST_VEROFF_MASK (0xfff << 0) - -/* Global control */ -#define S5P_CIGCTRL 0x08 -#define S5P_CIGCTRL_SWRST (1 << 31) -#define S5P_CIGCTRL_CAMRST_A (1 << 30) -#define S5P_CIGCTRL_SELCAM_ITU_A (1 << 29) -#define S5P_CIGCTRL_TESTPAT_NORMAL (0 << 27) -#define S5P_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27) -#define S5P_CIGCTRL_TESTPAT_HOR_INC (2 << 27) -#define S5P_CIGCTRL_TESTPAT_VER_INC (3 << 27) -#define S5P_CIGCTRL_TESTPAT_MASK (3 << 27) -#define S5P_CIGCTRL_TESTPAT_SHIFT (27) -#define S5P_CIGCTRL_INVPOLPCLK (1 << 26) -#define S5P_CIGCTRL_INVPOLVSYNC (1 << 25) -#define S5P_CIGCTRL_INVPOLHREF (1 << 24) -#define S5P_CIGCTRL_IRQ_OVFEN (1 << 22) -#define S5P_CIGCTRL_HREF_MASK (1 << 21) -#define S5P_CIGCTRL_IRQ_LEVEL (1 << 20) -#define S5P_CIGCTRL_IRQ_CLR (1 << 19) -#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16) -#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12) -#define S5P_CIGCTRL_CAM_JPEG (1 << 8) -#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7) -#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6) -/* 0 - ITU601; 1 - ITU709 */ -#define S5P_CIGCTRL_CSC_ITU601_709 (1 << 5) -#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4) -#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3) -#define S5P_CIGCTRL_INVPOLFIELD (1 << 1) -#define S5P_CIGCTRL_INTERLACE (1 << 0) - -/* Window offset 2 */ -#define S5P_CIWDOFST2 0x14 -#define S5P_CIWDOFST2_HOROFF_MASK (0xfff << 16) -#define S5P_CIWDOFST2_VEROFF_MASK (0xfff << 0) -#define S5P_CIWDOFST2_HOROFF(x) ((x) << 16) -#define S5P_CIWDOFST2_VEROFF(x) ((x) << 0) - -/* Output DMA Y/Cb/Cr plane start addresses */ -#define S5P_CIOYSA(n) (0x18 + (n) * 4) -#define S5P_CIOCBSA(n) (0x28 + (n) * 4) -#define S5P_CIOCRSA(n) (0x38 + (n) * 4) - -/* Target image format */ -#define S5P_CITRGFMT 0x48 -#define S5P_CITRGFMT_INROT90 (1 << 31) -#define S5P_CITRGFMT_YCBCR420 (0 << 29) -#define S5P_CITRGFMT_YCBCR422 (1 << 29) -#define S5P_CITRGFMT_YCBCR422_1P (2 << 29) -#define S5P_CITRGFMT_RGB (3 << 29) -#define S5P_CITRGFMT_FMT_MASK (3 << 29) -#define S5P_CITRGFMT_HSIZE_MASK (0xfff << 16) -#define S5P_CITRGFMT_FLIP_SHIFT (14) -#define S5P_CITRGFMT_FLIP_NORMAL (0 << 14) -#define S5P_CITRGFMT_FLIP_X_MIRROR (1 << 14) -#define S5P_CITRGFMT_FLIP_Y_MIRROR (2 << 14) -#define S5P_CITRGFMT_FLIP_180 (3 << 14) -#define S5P_CITRGFMT_FLIP_MASK (3 << 14) -#define S5P_CITRGFMT_OUTROT90 (1 << 13) -#define S5P_CITRGFMT_VSIZE_MASK (0xfff << 0) -#define S5P_CITRGFMT_HSIZE(x) ((x) << 16) -#define S5P_CITRGFMT_VSIZE(x) ((x) << 0) - -/* Output DMA control */ -#define S5P_CIOCTRL 0x4c -#define S5P_CIOCTRL_ORDER422_MASK (3 << 0) -#define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0) -#define S5P_CIOCTRL_ORDER422_CBYCRY (1 << 0) -#define S5P_CIOCTRL_ORDER422_YCRYCB (2 << 0) -#define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0) -#define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2) -#define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3) -#define S5P_CIOCTRL_YCBCR_2PLANE (1 << 3) -#define S5P_CIOCTRL_YCBCR_PLANE_MASK (1 << 3) -#define S5P_CIOCTRL_ALPHA_OUT_MASK (0xff << 4) -#define S5P_CIOCTRL_RGB16FMT_MASK (3 << 16) -#define S5P_CIOCTRL_RGB565 (0 << 16) -#define S5P_CIOCTRL_ARGB1555 (1 << 16) -#define S5P_CIOCTRL_ARGB4444 (2 << 16) -#define S5P_CIOCTRL_ORDER2P_SHIFT (24) -#define S5P_CIOCTRL_ORDER2P_MASK (3 << 24) -#define S5P_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24) - -/* Pre-scaler control 1 */ -#define S5P_CISCPRERATIO 0x50 -#define S5P_CISCPRERATIO_SHFACTOR(x) ((x) << 28) -#define S5P_CISCPRERATIO_HOR(x) ((x) << 16) -#define S5P_CISCPRERATIO_VER(x) ((x) << 0) - -#define S5P_CISCPREDST 0x54 -#define S5P_CISCPREDST_WIDTH(x) ((x) << 16) -#define S5P_CISCPREDST_HEIGHT(x) ((x) << 0) - -/* Main scaler control */ -#define S5P_CISCCTRL 0x58 -#define S5P_CISCCTRL_SCALERBYPASS (1 << 31) -#define S5P_CISCCTRL_SCALEUP_H (1 << 30) -#define S5P_CISCCTRL_SCALEUP_V (1 << 29) -#define S5P_CISCCTRL_CSCR2Y_WIDE (1 << 28) -#define S5P_CISCCTRL_CSCY2R_WIDE (1 << 27) -#define S5P_CISCCTRL_LCDPATHEN_FIFO (1 << 26) -#define S5P_CISCCTRL_INTERLACE (1 << 25) -#define S5P_CISCCTRL_SCALERSTART (1 << 15) -#define S5P_CISCCTRL_INRGB_FMT_RGB565 (0 << 13) -#define S5P_CISCCTRL_INRGB_FMT_RGB666 (1 << 13) -#define S5P_CISCCTRL_INRGB_FMT_RGB888 (2 << 13) -#define S5P_CISCCTRL_INRGB_FMT_MASK (3 << 13) -#define S5P_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) -#define S5P_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) -#define S5P_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) -#define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11) -#define S5P_CISCCTRL_RGB_EXT (1 << 10) -#define S5P_CISCCTRL_ONE2ONE (1 << 9) -#define S5P_CISCCTRL_MHRATIO(x) ((x) << 16) -#define S5P_CISCCTRL_MVRATIO(x) ((x) << 0) -#define S5P_CISCCTRL_MHRATIO_MASK (0x1ff << 16) -#define S5P_CISCCTRL_MVRATIO_MASK (0x1ff << 0) -#define S5P_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16) -#define S5P_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0) - -/* Target area */ -#define S5P_CITAREA 0x5c -#define S5P_CITAREA_MASK 0x0fffffff - -/* General status */ -#define S5P_CISTATUS 0x64 -#define S5P_CISTATUS_OVFIY (1 << 31) -#define S5P_CISTATUS_OVFICB (1 << 30) -#define S5P_CISTATUS_OVFICR (1 << 29) -#define S5P_CISTATUS_VSYNC (1 << 28) -#define S5P_CISTATUS_FRAMECNT_MASK (3 << 26) -#define S5P_CISTATUS_FRAMECNT_SHIFT 26 -#define S5P_CISTATUS_WINOFF_EN (1 << 25) -#define S5P_CISTATUS_IMGCPT_EN (1 << 22) -#define S5P_CISTATUS_IMGCPT_SCEN (1 << 21) -#define S5P_CISTATUS_VSYNC_A (1 << 20) -#define S5P_CISTATUS_VSYNC_B (1 << 19) -#define S5P_CISTATUS_OVRLB (1 << 18) -#define S5P_CISTATUS_FRAME_END (1 << 17) -#define S5P_CISTATUS_LASTCAPT_END (1 << 16) -#define S5P_CISTATUS_VVALID_A (1 << 15) -#define S5P_CISTATUS_VVALID_B (1 << 14) - -/* Indexes to the last and the currently processed buffer. */ -#define S5P_CISTATUS2 0x68 - -/* Image capture control */ -#define S5P_CIIMGCPT 0xc0 -#define S5P_CIIMGCPT_IMGCPTEN (1 << 31) -#define S5P_CIIMGCPT_IMGCPTEN_SC (1 << 30) -#define S5P_CIIMGCPT_CPT_FREN_ENABLE (1 << 25) -#define S5P_CIIMGCPT_CPT_FRMOD_CNT (1 << 18) - -/* Frame capture sequence */ -#define S5P_CICPTSEQ 0xc4 - -/* Image effect */ -#define S5P_CIIMGEFF 0xd0 -#define S5P_CIIMGEFF_IE_ENABLE (1 << 30) -#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29) -#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29) -#define S5P_CIIMGEFF_FIN_BYPASS (0 << 26) -#define S5P_CIIMGEFF_FIN_ARBITRARY (1 << 26) -#define S5P_CIIMGEFF_FIN_NEGATIVE (2 << 26) -#define S5P_CIIMGEFF_FIN_ARTFREEZE (3 << 26) -#define S5P_CIIMGEFF_FIN_EMBOSSING (4 << 26) -#define S5P_CIIMGEFF_FIN_SILHOUETTE (5 << 26) -#define S5P_CIIMGEFF_FIN_MASK (7 << 26) -#define S5P_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0)) -#define S5P_CIIMGEFF_PAT_CB(x) ((x) << 13) -#define S5P_CIIMGEFF_PAT_CR(x) ((x) << 0) - -/* Input DMA Y/Cb/Cr plane start address 0/1 */ -#define S5P_CIIYSA(n) (0xd4 + (n) * 0x70) -#define S5P_CIICBSA(n) (0xd8 + (n) * 0x70) -#define S5P_CIICRSA(n) (0xdc + (n) * 0x70) - -/* Real input DMA image size */ -#define S5P_CIREAL_ISIZE 0xf8 -#define S5P_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31) -#define S5P_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30) -#define S5P_CIREAL_ISIZE_HEIGHT(x) ((x) << 16) -#define S5P_CIREAL_ISIZE_WIDTH(x) ((x) << 0) - - -/* Input DMA control */ -#define S5P_MSCTRL 0xfc -#define S5P_MSCTRL_IN_BURST_COUNT_MASK (0xF << 24) -#define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16) -#define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16 -#define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15) -#define S5P_MSCTRL_C_INT_IN_2PLANE (1 << 15) -#define S5P_MSCTRL_C_INT_IN_MASK (1 << 15) -#define S5P_MSCTRL_FLIP_SHIFT 13 -#define S5P_MSCTRL_FLIP_MASK (3 << 13) -#define S5P_MSCTRL_FLIP_NORMAL (0 << 13) -#define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13) -#define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13) -#define S5P_MSCTRL_FLIP_180 (3 << 13) -#define S5P_MSCTRL_FIFO_CTRL_FULL (1 << 12) -#define S5P_MSCTRL_ORDER422_SHIFT 4 -#define S5P_MSCTRL_ORDER422_YCBYCR (0 << 4) -#define S5P_MSCTRL_ORDER422_CBYCRY (1 << 4) -#define S5P_MSCTRL_ORDER422_YCRYCB (2 << 4) -#define S5P_MSCTRL_ORDER422_CRYCBY (3 << 4) -#define S5P_MSCTRL_ORDER422_MASK (3 << 4) -#define S5P_MSCTRL_INPUT_EXTCAM (0 << 3) -#define S5P_MSCTRL_INPUT_MEMORY (1 << 3) -#define S5P_MSCTRL_INPUT_MASK (1 << 3) -#define S5P_MSCTRL_INFORMAT_YCBCR420 (0 << 1) -#define S5P_MSCTRL_INFORMAT_YCBCR422 (1 << 1) -#define S5P_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1) -#define S5P_MSCTRL_INFORMAT_RGB (3 << 1) -#define S5P_MSCTRL_INFORMAT_MASK (3 << 1) -#define S5P_MSCTRL_ENVID (1 << 0) -#define S5P_MSCTRL_IN_BURST_COUNT(x) ((x) << 24) - -/* Output DMA Y/Cb/Cr offset */ -#define S5P_CIOYOFF 0x168 -#define S5P_CIOCBOFF 0x16c -#define S5P_CIOCROFF 0x170 - -/* Input DMA Y/Cb/Cr offset */ -#define S5P_CIIYOFF 0x174 -#define S5P_CIICBOFF 0x178 -#define S5P_CIICROFF 0x17c - -#define S5P_CIO_OFFS_VER(x) ((x) << 16) -#define S5P_CIO_OFFS_HOR(x) ((x) << 0) - -/* Input DMA original image size */ -#define S5P_ORGISIZE 0x180 - -/* Output DMA original image size */ -#define S5P_ORGOSIZE 0x184 - -#define S5P_ORIG_SIZE_VER(x) ((x) << 16) -#define S5P_ORIG_SIZE_HOR(x) ((x) << 0) - -/* Real output DMA image size (extension register) */ -#define S5P_CIEXTEN 0x188 -#define S5P_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10) -#define S5P_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f) -#define S5P_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10) -#define S5P_CIEXTEN_MVRATIO_EXT_MASK 0x3f - -#define S5P_CIDMAPARAM 0x18c -#define S5P_CIDMAPARAM_R_LINEAR (0 << 29) -#define S5P_CIDMAPARAM_R_64X32 (3 << 29) -#define S5P_CIDMAPARAM_W_LINEAR (0 << 13) -#define S5P_CIDMAPARAM_W_64X32 (3 << 13) -#define S5P_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13)) - -/* MIPI CSI image format */ -#define S5P_CSIIMGFMT 0x194 -#define S5P_CSIIMGFMT_YCBCR422_8BIT 0x1e -#define S5P_CSIIMGFMT_RAW8 0x2a -#define S5P_CSIIMGFMT_RAW10 0x2b -#define S5P_CSIIMGFMT_RAW12 0x2c -/* User defined formats. x = 0...16. */ -#define S5P_CSIIMGFMT_USER(x) (0x30 + x - 1) - -/* Output frame buffer sequence mask */ -#define S5P_CIFCNTSEQ 0x1FC - -#endif /* REGS_FIMC_H_ */ -- cgit v1.2.3 From 2b511edb986fc11b8fa2b5e124c885a99aded257 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 20 Apr 2012 19:43:14 -0300 Subject: [media] s5p-fimc: Add FIMC-LITE register definitions Add register definitions and register API for FIMC-LITE devices. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-lite-reg.c | 300 +++++++++++++++++++++++++++ drivers/media/video/s5p-fimc/fimc-lite-reg.h | 150 ++++++++++++++ drivers/media/video/s5p-fimc/fimc-lite.h | 213 +++++++++++++++++++ 3 files changed, 663 insertions(+) create mode 100644 drivers/media/video/s5p-fimc/fimc-lite-reg.c create mode 100644 drivers/media/video/s5p-fimc/fimc-lite-reg.h create mode 100644 drivers/media/video/s5p-fimc/fimc-lite.h diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.c b/drivers/media/video/s5p-fimc/fimc-lite-reg.c new file mode 100644 index 000000000000..419adfb7cdf9 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-lite-reg.c @@ -0,0 +1,300 @@ +/* + * Register interface file for EXYNOS FIMC-LITE (camera interface) driver + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include + +#include "fimc-lite-reg.h" +#include "fimc-lite.h" +#include "fimc-core.h" + +#define FLITE_RESET_TIMEOUT 50 /* in ms */ + +void flite_hw_reset(struct fimc_lite *dev) +{ + unsigned long end = jiffies + msecs_to_jiffies(FLITE_RESET_TIMEOUT); + u32 cfg; + + cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + cfg |= FLITE_REG_CIGCTRL_SWRST_REQ; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + + while (time_is_after_jiffies(end)) { + cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + if (cfg & FLITE_REG_CIGCTRL_SWRST_RDY) + break; + usleep_range(1000, 5000); + } + + cfg |= FLITE_REG_CIGCTRL_SWRST; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); +} + +void flite_hw_clear_pending_irq(struct fimc_lite *dev) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS); + cfg &= ~FLITE_REG_CISTATUS_IRQ_CAM; + writel(cfg, dev->regs + FLITE_REG_CISTATUS); +} + +u32 flite_hw_get_interrupt_source(struct fimc_lite *dev) +{ + u32 intsrc = readl(dev->regs + FLITE_REG_CISTATUS); + return intsrc & FLITE_REG_CISTATUS_IRQ_MASK; +} + +void flite_hw_clear_last_capture_end(struct fimc_lite *dev) +{ + + u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS2); + cfg &= ~FLITE_REG_CISTATUS2_LASTCAPEND; + writel(cfg, dev->regs + FLITE_REG_CISTATUS2); +} + +void flite_hw_set_interrupt_mask(struct fimc_lite *dev) +{ + u32 cfg, intsrc; + + /* Select interrupts to be enabled for each output mode */ + if (dev->out_path == FIMC_IO_DMA) { + intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | + FLITE_REG_CIGCTRL_IRQ_LASTEN | + FLITE_REG_CIGCTRL_IRQ_STARTEN; + } else { + /* An output to the FIMC-IS */ + intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | + FLITE_REG_CIGCTRL_IRQ_LASTEN; + } + + cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + cfg |= FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK; + cfg &= ~intsrc; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); +} + +void flite_hw_capture_start(struct fimc_lite *dev) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT); + cfg |= FLITE_REG_CIIMGCPT_IMGCPTEN; + writel(cfg, dev->regs + FLITE_REG_CIIMGCPT); +} + +void flite_hw_capture_stop(struct fimc_lite *dev) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT); + cfg &= ~FLITE_REG_CIIMGCPT_IMGCPTEN; + writel(cfg, dev->regs + FLITE_REG_CIIMGCPT); +} + +/* + * Test pattern (color bars) enable/disable. External sensor + * pixel clock must be active for the test pattern to work. + */ +void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + if (on) + cfg |= FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR; + else + cfg &= ~FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); +} + +static const u32 src_pixfmt_map[8][3] = { + { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR, + FLITE_REG_CIGCTRL_YUV422_1P }, + { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB, + FLITE_REG_CIGCTRL_YUV422_1P }, + { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY, + FLITE_REG_CIGCTRL_YUV422_1P }, + { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY, + FLITE_REG_CIGCTRL_YUV422_1P }, + { V4L2_PIX_FMT_SGRBG8, 0, FLITE_REG_CIGCTRL_RAW8 }, + { V4L2_PIX_FMT_SGRBG10, 0, FLITE_REG_CIGCTRL_RAW10 }, + { V4L2_PIX_FMT_SGRBG12, 0, FLITE_REG_CIGCTRL_RAW12 }, + { V4L2_MBUS_FMT_JPEG_1X8, 0, FLITE_REG_CIGCTRL_USER(1) }, +}; + +/* Set camera input pixel format and resolution */ +void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f) +{ + enum v4l2_mbus_pixelcode pixelcode = dev->fmt->mbus_code; + unsigned int i = ARRAY_SIZE(src_pixfmt_map); + u32 cfg; + + while (i-- >= 0) { + if (src_pixfmt_map[i][0] == pixelcode) + break; + } + + if (i == 0 && src_pixfmt_map[i][0] != pixelcode) { + v4l2_err(dev->vfd, + "Unsupported pixel code, falling back to %#08x\n", + src_pixfmt_map[i][0]); + } + + cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + cfg &= ~FLITE_REG_CIGCTRL_FMT_MASK; + cfg |= src_pixfmt_map[i][2]; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + + cfg = readl(dev->regs + FLITE_REG_CISRCSIZE); + cfg &= ~(FLITE_REG_CISRCSIZE_ORDER422_MASK | + FLITE_REG_CISRCSIZE_SIZE_CAM_MASK); + cfg |= (f->f_width << 16) | f->f_height; + cfg |= src_pixfmt_map[i][1]; + writel(cfg, dev->regs + FLITE_REG_CISRCSIZE); +} + +/* Set the camera host input window offsets (cropping) */ +void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f) +{ + u32 hoff2, voff2; + u32 cfg; + + cfg = readl(dev->regs + FLITE_REG_CIWDOFST); + cfg &= ~FLITE_REG_CIWDOFST_OFST_MASK; + cfg |= (f->rect.left << 16) | f->rect.top; + cfg |= FLITE_REG_CIWDOFST_WINOFSEN; + writel(cfg, dev->regs + FLITE_REG_CIWDOFST); + + hoff2 = f->f_width - f->rect.width - f->rect.left; + voff2 = f->f_height - f->rect.height - f->rect.top; + + cfg = (hoff2 << 16) | voff2; + writel(cfg, dev->regs + FLITE_REG_CIWDOFST2); +} + +/* Select camera port (A, B) */ +static void flite_hw_set_camera_port(struct fimc_lite *dev, int id) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIGENERAL); + if (id == 0) + cfg &= ~FLITE_REG_CIGENERAL_CAM_B; + else + cfg |= FLITE_REG_CIGENERAL_CAM_B; + writel(cfg, dev->regs + FLITE_REG_CIGENERAL); +} + +/* Select serial or parallel bus, camera port (A,B) and set signals polarity */ +void flite_hw_set_camera_bus(struct fimc_lite *dev, + struct s5p_fimc_isp_info *s_info) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + unsigned int flags = s_info->flags; + + if (s_info->bus_type != FIMC_MIPI_CSI2) { + cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI | + FLITE_REG_CIGCTRL_INVPOLPCLK | + FLITE_REG_CIGCTRL_INVPOLVSYNC | + FLITE_REG_CIGCTRL_INVPOLHREF); + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + cfg |= FLITE_REG_CIGCTRL_INVPOLPCLK; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + cfg |= FLITE_REG_CIGCTRL_INVPOLVSYNC; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + cfg |= FLITE_REG_CIGCTRL_INVPOLHREF; + } else { + cfg |= FLITE_REG_CIGCTRL_SELCAM_MIPI; + } + + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + + flite_hw_set_camera_port(dev, s_info->mux_id); +} + +void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) +{ + static const u32 pixcode[4][2] = { + { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR }, + { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CIODMAFMT_YCRYCB }, + { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CIODMAFMT_CBYCRY }, + { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY }, + }; + u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT); + unsigned int i = ARRAY_SIZE(pixcode); + + while (i-- >= 0) + if (pixcode[i][0] == dev->fmt->mbus_code) + break; + cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK; + writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT); +} + +void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f) +{ + u32 cfg; + + /* Maximum output pixel size */ + cfg = readl(dev->regs + FLITE_REG_CIOCAN); + cfg &= ~FLITE_REG_CIOCAN_MASK; + cfg = (f->f_height << 16) | f->f_width; + writel(cfg, dev->regs + FLITE_REG_CIOCAN); + + /* DMA offsets */ + cfg = readl(dev->regs + FLITE_REG_CIOOFF); + cfg &= ~FLITE_REG_CIOOFF_MASK; + cfg |= (f->rect.top << 16) | f->rect.left; + writel(cfg, dev->regs + FLITE_REG_CIOOFF); +} + +/* Enable/disable output DMA, set output pixel size and offsets (composition) */ +void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, + bool enable) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + + if (!enable) { + cfg |= FLITE_REG_CIGCTRL_ODMA_DISABLE; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + return; + } + + cfg &= ~FLITE_REG_CIGCTRL_ODMA_DISABLE; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + + flite_hw_set_out_order(dev, f); + flite_hw_set_dma_window(dev, f); +} + +void flite_hw_dump_regs(struct fimc_lite *dev, const char *label) +{ + struct { + u32 offset; + const char * const name; + } registers[] = { + { 0x00, "CISRCSIZE" }, + { 0x04, "CIGCTRL" }, + { 0x08, "CIIMGCPT" }, + { 0x0c, "CICPTSEQ" }, + { 0x10, "CIWDOFST" }, + { 0x14, "CIWDOFST2" }, + { 0x18, "CIODMAFMT" }, + { 0x20, "CIOCAN" }, + { 0x24, "CIOOFF" }, + { 0x30, "CIOSA" }, + { 0x40, "CISTATUS" }, + { 0x44, "CISTATUS2" }, + { 0xf0, "CITHOLD" }, + { 0xfc, "CIGENERAL" }, + }; + u32 i; + + pr_info("--- %s ---\n", label); + for (i = 0; i < ARRAY_SIZE(registers); i++) { + u32 cfg = readl(dev->regs + registers[i].offset); + pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg); + } +} diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.h b/drivers/media/video/s5p-fimc/fimc-lite-reg.h new file mode 100644 index 000000000000..adb9e9e6f3c2 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-lite-reg.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_LITE_REG_H_ +#define FIMC_LITE_REG_H_ + +#include "fimc-lite.h" + +/* Camera Source size */ +#define FLITE_REG_CISRCSIZE 0x00 +#define FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR (0 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB (1 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY (2 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY (3 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_MASK (0x3 << 14) +#define FLITE_REG_CISRCSIZE_SIZE_CAM_MASK (0x3fff << 16 | 0x3fff) + +/* Global control */ +#define FLITE_REG_CIGCTRL 0x04 +#define FLITE_REG_CIGCTRL_YUV422_1P (0x1e << 24) +#define FLITE_REG_CIGCTRL_RAW8 (0x2a << 24) +#define FLITE_REG_CIGCTRL_RAW10 (0x2b << 24) +#define FLITE_REG_CIGCTRL_RAW12 (0x2c << 24) +#define FLITE_REG_CIGCTRL_RAW14 (0x2d << 24) +/* User defined formats. x = 0...15 */ +#define FLITE_REG_CIGCTRL_USER(x) ((0x30 + x - 1) << 24) +#define FLITE_REG_CIGCTRL_FMT_MASK (0x3f << 24) +#define FLITE_REG_CIGCTRL_SHADOWMASK_DISABLE (1 << 21) +#define FLITE_REG_CIGCTRL_ODMA_DISABLE (1 << 20) +#define FLITE_REG_CIGCTRL_SWRST_REQ (1 << 19) +#define FLITE_REG_CIGCTRL_SWRST_RDY (1 << 18) +#define FLITE_REG_CIGCTRL_SWRST (1 << 17) +#define FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR (1 << 15) +#define FLITE_REG_CIGCTRL_INVPOLPCLK (1 << 14) +#define FLITE_REG_CIGCTRL_INVPOLVSYNC (1 << 13) +#define FLITE_REG_CIGCTRL_INVPOLHREF (1 << 12) +/* Interrupts mask bits (1 disables an interrupt) */ +#define FLITE_REG_CIGCTRL_IRQ_LASTEN (1 << 8) +#define FLITE_REG_CIGCTRL_IRQ_ENDEN (1 << 7) +#define FLITE_REG_CIGCTRL_IRQ_STARTEN (1 << 6) +#define FLITE_REG_CIGCTRL_IRQ_OVFEN (1 << 5) +#define FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK (0xf << 5) +#define FLITE_REG_CIGCTRL_SELCAM_MIPI (1 << 3) + +/* Image Capture Enable */ +#define FLITE_REG_CIIMGCPT 0x08 +#define FLITE_REG_CIIMGCPT_IMGCPTEN (1 << 31) +#define FLITE_REG_CIIMGCPT_CPT_FREN (1 << 25) +#define FLITE_REG_CIIMGCPT_CPT_MOD_FRCNT (1 << 18) +#define FLITE_REG_CIIMGCPT_CPT_MOD_FREN (0 << 18) + +/* Capture Sequence */ +#define FLITE_REG_CICPTSEQ 0x0c + +/* Camera Window Offset */ +#define FLITE_REG_CIWDOFST 0x10 +#define FLITE_REG_CIWDOFST_WINOFSEN (1 << 31) +#define FLITE_REG_CIWDOFST_CLROVIY (1 << 31) +#define FLITE_REG_CIWDOFST_CLROVFICB (1 << 15) +#define FLITE_REG_CIWDOFST_CLROVFICR (1 << 14) +#define FLITE_REG_CIWDOFST_OFST_MASK ((0x1fff << 16) | 0x1fff) + +/* Camera Window Offset2 */ +#define FLITE_REG_CIWDOFST2 0x14 + +/* Camera Output DMA Format */ +#define FLITE_REG_CIODMAFMT 0x18 +#define FLITE_REG_CIODMAFMT_RAW_CON (1 << 15) +#define FLITE_REG_CIODMAFMT_PACK12 (1 << 14) +#define FLITE_REG_CIODMAFMT_CRYCBY (0 << 4) +#define FLITE_REG_CIODMAFMT_CBYCRY (1 << 4) +#define FLITE_REG_CIODMAFMT_YCRYCB (2 << 4) +#define FLITE_REG_CIODMAFMT_YCBYCR (3 << 4) +#define FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK (0x3 << 4) + +/* Camera Output Canvas */ +#define FLITE_REG_CIOCAN 0x20 +#define FLITE_REG_CIOCAN_MASK ((0x3fff << 16) | 0x3fff) + +/* Camera Output DMA Offset */ +#define FLITE_REG_CIOOFF 0x24 +#define FLITE_REG_CIOOFF_MASK ((0x3fff << 16) | 0x3fff) + +/* Camera Output DMA Start Address */ +#define FLITE_REG_CIOSA 0x30 + +/* Camera Status */ +#define FLITE_REG_CISTATUS 0x40 +#define FLITE_REG_CISTATUS_MIPI_VVALID (1 << 22) +#define FLITE_REG_CISTATUS_MIPI_HVALID (1 << 21) +#define FLITE_REG_CISTATUS_MIPI_DVALID (1 << 20) +#define FLITE_REG_CISTATUS_ITU_VSYNC (1 << 14) +#define FLITE_REG_CISTATUS_ITU_HREFF (1 << 13) +#define FLITE_REG_CISTATUS_OVFIY (1 << 10) +#define FLITE_REG_CISTATUS_OVFICB (1 << 9) +#define FLITE_REG_CISTATUS_OVFICR (1 << 8) +#define FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW (1 << 7) +#define FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND (1 << 6) +#define FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART (1 << 5) +#define FLITE_REG_CISTATUS_IRQ_SRC_FRMEND (1 << 4) +#define FLITE_REG_CISTATUS_IRQ_CAM (1 << 0) +#define FLITE_REG_CISTATUS_IRQ_MASK (0xf << 4) + +/* Camera Status2 */ +#define FLITE_REG_CISTATUS2 0x44 +#define FLITE_REG_CISTATUS2_LASTCAPEND (1 << 1) +#define FLITE_REG_CISTATUS2_FRMEND (1 << 0) + +/* Qos Threshold */ +#define FLITE_REG_CITHOLD 0xf0 +#define FLITE_REG_CITHOLD_W_QOS_EN (1 << 30) + +/* Camera General Purpose */ +#define FLITE_REG_CIGENERAL 0xfc +/* b0: 1 - camera B, 0 - camera A */ +#define FLITE_REG_CIGENERAL_CAM_B (1 << 0) + +/* ---------------------------------------------------------------------------- + * Function declarations + */ +void flite_hw_reset(struct fimc_lite *dev); +void flite_hw_clear_pending_irq(struct fimc_lite *dev); +u32 flite_hw_get_interrupt_source(struct fimc_lite *dev); +void flite_hw_clear_last_capture_end(struct fimc_lite *dev); +void flite_hw_set_interrupt_mask(struct fimc_lite *dev); +void flite_hw_capture_start(struct fimc_lite *dev); +void flite_hw_capture_stop(struct fimc_lite *dev); +void flite_hw_set_camera_bus(struct fimc_lite *dev, + struct s5p_fimc_isp_info *s_info); +void flite_hw_set_camera_polarity(struct fimc_lite *dev, + struct s5p_fimc_isp_info *cam); +void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f); +void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f); + +void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, + bool enable); +void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f); +void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on); +void flite_hw_dump_regs(struct fimc_lite *dev, const char *label); + +static inline void flite_hw_set_output_addr(struct fimc_lite *dev, u32 paddr) +{ + writel(paddr, dev->regs + FLITE_REG_CIOSA); +} +#endif /* FIMC_LITE_REG_H */ diff --git a/drivers/media/video/s5p-fimc/fimc-lite.h b/drivers/media/video/s5p-fimc/fimc-lite.h new file mode 100644 index 000000000000..44424eee81d8 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-lite.h @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_LITE_H_ +#define FIMC_LITE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fimc-core.h" + +#define FIMC_LITE_DRV_NAME "exynos-fimc-lite" +#define FLITE_CLK_NAME "flite" +#define FIMC_LITE_MAX_DEVS 2 +#define FLITE_REQ_BUFS_MIN 2 + +/* Bit index definitions for struct fimc_lite::state */ +enum { + ST_FLITE_LPM, + ST_FLITE_PENDING, + ST_FLITE_RUN, + ST_FLITE_STREAM, + ST_FLITE_SUSPENDED, + ST_FLITE_OFF, + ST_FLITE_IN_USE, + ST_FLITE_CONFIG, + ST_SENSOR_STREAM, +}; + +#define FLITE_SD_PAD_SINK 0 +#define FLITE_SD_PAD_SOURCE 1 +#define FLITE_SD_PADS_NUM 2 + +struct flite_variant { + unsigned short max_width; + unsigned short max_height; + unsigned short out_width_align; + unsigned short win_hor_offs_align; + unsigned short out_hor_offs_align; +}; + +struct flite_drvdata { + struct flite_variant *variant[FIMC_LITE_MAX_DEVS]; +}; + +#define fimc_lite_get_drvdata(_pdev) \ + ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data) + +struct fimc_lite_events { + unsigned int data_overflow; +}; + +#define FLITE_MAX_PLANES 1 + +/** + * struct flite_frame - source/target frame properties + * @f_width: full pixel width + * @f_height: full pixel height + * @rect: crop/composition rectangle + */ +struct flite_frame { + u16 f_width; + u16 f_height; + struct v4l2_rect rect; +}; + +/** + * struct flite_buffer - video buffer structure + * @vb: vb2 buffer + * @list: list head for the buffers queue + * @paddr: precalculated physical address + */ +struct flite_buffer { + struct vb2_buffer vb; + struct list_head list; + dma_addr_t paddr; +}; + +/** + * struct fimc_lite - fimc lite structure + * @pdev: pointer to FIMC-LITE platform device + * @variant: variant information for this IP + * @v4l2_dev: pointer to top the level v4l2_device + * @vfd: video device node + * @fh: v4l2 file handle + * @alloc_ctx: videobuf2 memory allocator context + * @subdev: FIMC-LITE subdev + * @vd_pad: media (sink) pad for the capture video node + * @subdev_pads: the subdev media pads + * @ctrl_handler: v4l2 control handler + * @test_pattern: test pattern controls + * @index: FIMC-LITE platform device index + * @pipeline: video capture pipeline data structure + * @slock: spinlock protecting this data structure and the hw registers + * @lock: mutex serializing video device and the subdev operations + * @clock: FIMC-LITE gate clock + * @regs: memory mapped io registers + * @irq_queue: interrupt handler waitqueue + * @fmt: pointer to color format description structure + * @payload: image size in bytes (w x h x bpp) + * @inp_frame: camera input frame structure + * @out_frame: DMA output frame structure + * @out_path: output data path (DMA or FIFO) + * @source_subdev_grp_id: source subdev group id + * @state: driver state flags + * @pending_buf_q: pending buffers queue head + * @active_buf_q: the queue head of buffers scheduled in hardware + * @vb_queue: vb2 buffers queue + * @active_buf_count: number of video buffers scheduled in hardware + * @frame_count: the captured frames counter + * @reqbufs_count: the number of buffers requested with REQBUFS ioctl + * @ref_count: driver's private reference counter + */ +struct fimc_lite { + struct platform_device *pdev; + struct flite_variant *variant; + struct v4l2_device *v4l2_dev; + struct video_device *vfd; + struct v4l2_fh fh; + struct vb2_alloc_ctx *alloc_ctx; + struct v4l2_subdev subdev; + struct media_pad vd_pad; + struct media_pad subdev_pads[FLITE_SD_PADS_NUM]; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *test_pattern; + u32 index; + struct fimc_pipeline pipeline; + + struct mutex lock; + spinlock_t slock; + + struct clk *clock; + void __iomem *regs; + wait_queue_head_t irq_queue; + + const struct fimc_fmt *fmt; + unsigned long payload[FLITE_MAX_PLANES]; + struct flite_frame inp_frame; + struct flite_frame out_frame; + enum fimc_datapath out_path; + unsigned int source_subdev_grp_id; + + unsigned long state; + struct list_head pending_buf_q; + struct list_head active_buf_q; + struct vb2_queue vb_queue; + unsigned int frame_count; + unsigned int reqbufs_count; + int ref_count; + + struct fimc_lite_events events; +}; + +static inline bool fimc_lite_active(struct fimc_lite *fimc) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&fimc->slock, flags); + ret = fimc->state & (1 << ST_FLITE_RUN) || + fimc->state & (1 << ST_FLITE_PENDING); + spin_unlock_irqrestore(&fimc->slock, flags); + return ret; +} + +static inline void fimc_lite_active_queue_add(struct fimc_lite *dev, + struct flite_buffer *buf) +{ + list_add_tail(&buf->list, &dev->active_buf_q); +} + +static inline struct flite_buffer *fimc_lite_active_queue_pop( + struct fimc_lite *dev) +{ + struct flite_buffer *buf = list_entry(dev->active_buf_q.next, + struct flite_buffer, list); + list_del(&buf->list); + return buf; +} + +static inline void fimc_lite_pending_queue_add(struct fimc_lite *dev, + struct flite_buffer *buf) +{ + list_add_tail(&buf->list, &dev->pending_buf_q); +} + +static inline struct flite_buffer *fimc_lite_pending_queue_pop( + struct fimc_lite *dev) +{ + struct flite_buffer *buf = list_entry(dev->pending_buf_q.next, + struct flite_buffer, list); + list_del(&buf->list); + return buf; +} + +#endif /* FIMC_LITE_H_ */ -- cgit v1.2.3 From 0f735f5236643cbbeb833fa0946bd52c20d00966 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 27 Apr 2012 09:33:10 -0300 Subject: [media] s5p-fimc: Rework the video pipeline control functions There is getting more entities to manage within single video pipeline in newer SoCs. To simplify code put subdevs' pointer into an array rather than adding new member in struct fimc_pipeline for each subdev. This allows to easier handle subdev operations in proper order. Additionally walk graph in one direction only in fimc_pipeline_prepare() function to make sure we properly gather only media entities that below to single data pipeline. This avoids wrong initialization in case where, for example there are multiple active links from s5p-mipi-csis subdev output pad. struct fimc_pipeline declaration is moved to the driver's public header to allow other drivers to reuse the fimc-lite driver added in subsequent patches. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 32 +++--- drivers/media/video/s5p-fimc/fimc-core.h | 5 - drivers/media/video/s5p-fimc/fimc-mdevice.c | 164 +++++++++++++++++----------- drivers/media/video/s5p-fimc/fimc-mdevice.h | 10 +- include/media/s5p_fimc.h | 16 +++ 5 files changed, 139 insertions(+), 88 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 52a5fb469b45..7c884bb7104f 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -34,16 +34,17 @@ static int fimc_init_capture(struct fimc_dev *fimc) { struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_pipeline *p = &fimc->pipeline; struct fimc_sensor_info *sensor; unsigned long flags; int ret = 0; - if (fimc->pipeline.sensor == NULL || ctx == NULL) + if (p->subdevs[IDX_SENSOR] == NULL || ctx == NULL) return -ENXIO; if (ctx->s_frame.fmt == NULL) return -EINVAL; - sensor = v4l2_get_subdev_hostdata(fimc->pipeline.sensor); + sensor = v4l2_get_subdev_hostdata(p->subdevs[IDX_SENSOR]); spin_lock_irqsave(&fimc->slock, flags); fimc_prepare_dma_offset(ctx, &ctx->d_frame); @@ -109,7 +110,7 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) spin_unlock_irqrestore(&fimc->slock, flags); if (streaming) - return fimc_pipeline_s_stream(fimc, 0); + return fimc_pipeline_s_stream(&fimc->pipeline, 0); else return 0; } @@ -254,7 +255,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) fimc_activate_capture(ctx); if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) - fimc_pipeline_s_stream(fimc, 1); + fimc_pipeline_s_stream(&fimc->pipeline, 1); } return 0; @@ -281,7 +282,7 @@ int fimc_capture_suspend(struct fimc_dev *fimc) int ret = fimc_stop_capture(fimc, suspend); if (ret) return ret; - return fimc_pipeline_shutdown(fimc); + return fimc_pipeline_shutdown(&fimc->pipeline); } static void buffer_queue(struct vb2_buffer *vb); @@ -297,7 +298,7 @@ int fimc_capture_resume(struct fimc_dev *fimc) INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); vid_cap->buf_index = 0; - fimc_pipeline_initialize(fimc, &fimc->vid_cap.vfd->entity, + fimc_pipeline_initialize(&fimc->pipeline, &vid_cap->vfd->entity, false); fimc_init_capture(fimc); @@ -414,7 +415,7 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&fimc->slock, flags); if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) - fimc_pipeline_s_stream(fimc, 1); + fimc_pipeline_s_stream(&fimc->pipeline, 1); return; } spin_unlock_irqrestore(&fimc->slock, flags); @@ -464,7 +465,7 @@ int fimc_capture_ctrls_create(struct fimc_dev *fimc) return ret; return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrl_handler, - fimc->pipeline.sensor->ctrl_handler); + fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler); } static int fimc_capture_set_default_format(struct fimc_dev *fimc); @@ -487,7 +488,7 @@ static int fimc_capture_open(struct file *file) pm_runtime_get_sync(&fimc->pdev->dev); if (++fimc->vid_cap.refcnt == 1) { - ret = fimc_pipeline_initialize(fimc, + ret = fimc_pipeline_initialize(&fimc->pipeline, &fimc->vid_cap.vfd->entity, true); if (ret < 0) { dev_err(&fimc->pdev->dev, @@ -515,7 +516,7 @@ static int fimc_capture_close(struct file *file) if (--fimc->vid_cap.refcnt == 0) { clear_bit(ST_CAPT_BUSY, &fimc->state); fimc_stop_capture(fimc, false); - fimc_pipeline_shutdown(fimc); + fimc_pipeline_shutdown(&fimc->pipeline); clear_bit(ST_CAPT_SUSPENDED, &fimc->state); } @@ -736,8 +737,8 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, bool set) { struct fimc_dev *fimc = ctx->fimc_dev; - struct v4l2_subdev *sd = fimc->pipeline.sensor; - struct v4l2_subdev *csis = fimc->pipeline.csis; + struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; + struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS]; struct v4l2_subdev_format sfmt; struct v4l2_mbus_framefmt *mf = &sfmt.format; struct fimc_fmt *ffmt = NULL; @@ -945,7 +946,7 @@ static int fimc_cap_enum_input(struct file *file, void *priv, struct v4l2_input *i) { struct fimc_dev *fimc = video_drvdata(file); - struct v4l2_subdev *sd = fimc->pipeline.sensor; + struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; if (i->index != 0) return -EINVAL; @@ -1037,7 +1038,8 @@ static int fimc_cap_streamon(struct file *file, void *priv, if (fimc_capture_active(fimc)) return -EBUSY; - media_entity_pipeline_start(&p->sensor->entity, p->pipe); + media_entity_pipeline_start(&p->subdevs[IDX_SENSOR]->entity, + p->m_pipeline); if (fimc->vid_cap.user_subdev_api) { ret = fimc_pipeline_validate(fimc); @@ -1051,7 +1053,7 @@ static int fimc_cap_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_dev *fimc = video_drvdata(file); - struct v4l2_subdev *sd = fimc->pipeline.sensor; + struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; int ret; ret = vb2_streamoff(&fimc->vid_cap.vbq, type); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 34fbba424692..8b073979cee8 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -400,11 +400,6 @@ struct samsung_fimc_driverdata { int num_entities; }; -struct fimc_pipeline { - struct media_pipeline *pipe; - struct v4l2_subdev *sensor; - struct v4l2_subdev *csis; -}; struct fimc_ctx; diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index c319842c762d..212474130dfb 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -25,6 +25,7 @@ #include #include "fimc-core.h" +#include "fimc-lite.h" #include "fimc-mdevice.h" #include "mipi-csis.h" @@ -37,22 +38,43 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, * * Caller holds the graph mutex. */ -void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me) +void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me) { - struct media_entity_graph graph; + struct media_pad *pad = &me->pads[0]; struct v4l2_subdev *sd; + int i; - media_entity_graph_walk_start(&graph, me); + for (i = 0; i < IDX_MAX; i++) + p->subdevs[i] = NULL; - while ((me = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV) - continue; - sd = media_entity_to_v4l2_subdev(me); + while (1) { + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + /* source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; - if (sd->grp_id == SENSOR_GROUP_ID) - fimc->pipeline.sensor = sd; - else if (sd->grp_id == CSIS_GROUP_ID) - fimc->pipeline.csis = sd; + sd = media_entity_to_v4l2_subdev(pad->entity); + + switch (sd->grp_id) { + case SENSOR_GROUP_ID: + p->subdevs[IDX_SENSOR] = sd; + break; + case CSIS_GROUP_ID: + p->subdevs[IDX_CSIS] = sd; + break; + case FIMC_GROUP_ID: + /* No need to control FIMC subdev through subdev ops */ + break; + default: + pr_warn("%s: Unknown subdev grp_id: %#x\n", + __func__, sd->grp_id); + } + /* sink pad */ + pad = &sd->entity.pads[0]; } } @@ -85,30 +107,27 @@ static int __subdev_set_power(struct v4l2_subdev *sd, int on) /** * fimc_pipeline_s_power - change power state of all pipeline subdevs * @fimc: fimc device terminating the pipeline - * @state: 1 to enable power or 0 for power down + * @state: true to power on, false to power off * - * Need to be called with the graph mutex held. + * Needs to be called with the graph mutex held. */ -int fimc_pipeline_s_power(struct fimc_dev *fimc, int state) +int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state) { - int ret = 0; + unsigned int i; + int ret; - if (fimc->pipeline.sensor == NULL) + if (p->subdevs[IDX_SENSOR] == NULL) return -ENXIO; - if (state) { - ret = __subdev_set_power(fimc->pipeline.csis, 1); - if (ret && ret != -ENXIO) + for (i = 0; i < IDX_MAX; i++) { + unsigned int idx = state ? (IDX_MAX - 1) - i : i; + + ret = __subdev_set_power(p->subdevs[idx], state); + if (ret < 0 && ret != -ENXIO) return ret; - return __subdev_set_power(fimc->pipeline.sensor, 1); } - ret = __subdev_set_power(fimc->pipeline.sensor, 0); - if (ret) - return ret; - ret = __subdev_set_power(fimc->pipeline.csis, 0); - - return ret == -ENXIO ? 0 : ret; + return 0; } /** @@ -119,32 +138,36 @@ int fimc_pipeline_s_power(struct fimc_dev *fimc, int state) * * This function must be called with the graph mutex held. */ -static int __fimc_pipeline_initialize(struct fimc_dev *fimc, +static int __fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me, bool prep) { int ret; if (prep) - fimc_pipeline_prepare(fimc, me); - if (fimc->pipeline.sensor == NULL) + fimc_pipeline_prepare(p, me); + + if (p->subdevs[IDX_SENSOR] == NULL) return -EINVAL; - ret = fimc_md_set_camclk(fimc->pipeline.sensor, true); + + ret = fimc_md_set_camclk(p->subdevs[IDX_SENSOR], true); if (ret) return ret; - return fimc_pipeline_s_power(fimc, 1); + + return fimc_pipeline_s_power(p, 1); } -int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, +int fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me, bool prep) { int ret; mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_initialize(fimc, me, prep); + ret = __fimc_pipeline_initialize(p, me, prep); mutex_unlock(&me->parent->graph_mutex); return ret; } +EXPORT_SYMBOL_GPL(fimc_pipeline_initialize); /** * __fimc_pipeline_shutdown - disable the sensor clock and pipeline power @@ -154,52 +177,55 @@ int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, * sensor clock. * Called with the graph mutex held. */ -int __fimc_pipeline_shutdown(struct fimc_dev *fimc) +int __fimc_pipeline_shutdown(struct fimc_pipeline *p) { int ret = 0; - if (fimc->pipeline.sensor) { - ret = fimc_pipeline_s_power(fimc, 0); - fimc_md_set_camclk(fimc->pipeline.sensor, false); + if (p->subdevs[IDX_SENSOR]) { + ret = fimc_pipeline_s_power(p, 0); + fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false); } return ret == -ENXIO ? 0 : ret; } -int fimc_pipeline_shutdown(struct fimc_dev *fimc) +int fimc_pipeline_shutdown(struct fimc_pipeline *p) { - struct media_entity *me = &fimc->vid_cap.vfd->entity; + struct media_entity *me = &p->subdevs[IDX_SENSOR]->entity; int ret; mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_shutdown(fimc); + ret = __fimc_pipeline_shutdown(p); mutex_unlock(&me->parent->graph_mutex); return ret; } +EXPORT_SYMBOL_GPL(fimc_pipeline_shutdown); /** * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs - * @fimc: fimc device terminating the pipeline + * @pipeline: video pipeline structure * @on: passed as the s_stream call argument */ -int fimc_pipeline_s_stream(struct fimc_dev *fimc, int on) +int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) { - struct fimc_pipeline *p = &fimc->pipeline; - int ret = 0; + int i, ret; - if (p->sensor == NULL) + if (p->subdevs[IDX_SENSOR] == NULL) return -ENODEV; - if ((on && p->csis) || !on) - ret = v4l2_subdev_call(on ? p->csis : p->sensor, - video, s_stream, on); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - if ((!on && p->csis) || on) - ret = v4l2_subdev_call(on ? p->sensor : p->csis, - video, s_stream, on); - return ret == -ENOIOCTLCMD ? 0 : ret; + for (i = 0; i < IDX_MAX; i++) { + unsigned int idx = on ? (IDX_MAX - 1) - i : i; + + ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on); + + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + } + + return 0; + } +EXPORT_SYMBOL_GPL(fimc_pipeline_s_stream); /* * Sensor subdevice helper functions @@ -677,6 +703,7 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) static int fimc_md_link_notify(struct media_pad *source, struct media_pad *sink, u32 flags) { + struct fimc_pipeline *pipeline; struct v4l2_subdev *sd; struct fimc_dev *fimc; int ret = 0; @@ -685,16 +712,26 @@ static int fimc_md_link_notify(struct media_pad *source, return 0; sd = media_entity_to_v4l2_subdev(sink->entity); - fimc = v4l2_get_subdevdata(sd); - if (!(flags & MEDIA_LNK_FL_ENABLED)) { - ret = __fimc_pipeline_shutdown(fimc); - fimc->pipeline.sensor = NULL; - fimc->pipeline.csis = NULL; + switch (sd->grp_id) { + case FIMC_GROUP_ID: + fimc = v4l2_get_subdevdata(sd); + pipeline = &fimc->pipeline; + break; + default: + return 0; + } - mutex_lock(&fimc->lock); - fimc_ctrls_delete(fimc->vid_cap.ctx); - mutex_unlock(&fimc->lock); + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ret = __fimc_pipeline_shutdown(pipeline); + pipeline->subdevs[IDX_SENSOR] = NULL; + pipeline->subdevs[IDX_CSIS] = NULL; + + if (fimc) { + mutex_lock(&fimc->lock); + fimc_ctrls_delete(fimc->vid_cap.ctx); + mutex_unlock(&fimc->lock); + } return ret; } /* @@ -704,7 +741,8 @@ static int fimc_md_link_notify(struct media_pad *source, */ mutex_lock(&fimc->lock); if (fimc->vid_cap.refcnt > 0) { - ret = __fimc_pipeline_initialize(fimc, source->entity, true); + ret = __fimc_pipeline_initialize(pipeline, + source->entity, true); if (!ret) ret = fimc_capture_ctrls_create(fimc); } diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h index 4f3b69c682cb..c5ac3e64b0d9 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h @@ -109,11 +109,11 @@ static inline void fimc_md_graph_unlock(struct fimc_dev *fimc) } int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); -void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me); -int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, +void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me); +int fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me, bool resume); -int fimc_pipeline_shutdown(struct fimc_dev *fimc); -int fimc_pipeline_s_power(struct fimc_dev *fimc, int state); -int fimc_pipeline_s_stream(struct fimc_dev *fimc, int state); +int fimc_pipeline_shutdown(struct fimc_pipeline *p); +int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state); +int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool state); #endif diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index 688fb3f1dc35..8587aaf73646 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -64,4 +64,20 @@ struct s5p_platform_fimc { */ #define S5P_FIMC_TX_END_NOTIFY _IO('e', 0) +enum fimc_subdev_index { + IDX_SENSOR, + IDX_CSIS, + IDX_FLITE, + IDX_FIMC, + IDX_MAX, +}; + +struct media_pipeline; +struct v4l2_subdev; + +struct fimc_pipeline { + struct v4l2_subdev *subdevs[IDX_MAX]; + struct media_pipeline *m_pipeline; +}; + #endif /* S5P_FIMC_H_ */ -- cgit v1.2.3 From 3d112d9aced3d4ad959e159a1662503452792295 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 26 Apr 2012 06:26:29 -0300 Subject: [media] s5p-fimc: Prefix format enumerations with FIMC_FMT_ Prefix the pixel format enumerations with FIMC_FMT_ to make it more clear, especially when used in new IP drivers, like fimc-lite, etc. Also add IO_ prefix in the input/output enumeration. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 4 +-- drivers/media/video/s5p-fimc/fimc-core.c | 56 ++++++++++++++--------------- drivers/media/video/s5p-fimc/fimc-core.h | 45 ++++++++++++----------- drivers/media/video/s5p-fimc/fimc-m2m.c | 4 +-- drivers/media/video/s5p-fimc/fimc-reg.c | 48 ++++++++++++------------- 5 files changed, 81 insertions(+), 76 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 7c884bb7104f..b36809d2f9fc 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1524,8 +1524,8 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, return -ENOMEM; ctx->fimc_dev = fimc; - ctx->in_path = FIMC_CAMERA; - ctx->out_path = FIMC_DMA; + ctx->in_path = FIMC_IO_CAMERA; + ctx->out_path = FIMC_IO_DMA; ctx->state = FIMC_CTX_CAP; ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); ctx->d_frame.fmt = ctx->s_frame.fmt; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index afd69e3d44c2..e0fe9748ec57 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -40,7 +40,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565, .depth = { 16 }, - .color = S5P_FIMC_RGB565, + .color = FIMC_FMT_RGB565, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M, @@ -48,7 +48,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "BGR666", .fourcc = V4L2_PIX_FMT_BGR666, .depth = { 32 }, - .color = S5P_FIMC_RGB666, + .color = FIMC_FMT_RGB666, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M, @@ -56,7 +56,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "ARGB8888, 32 bpp", .fourcc = V4L2_PIX_FMT_RGB32, .depth = { 32 }, - .color = S5P_FIMC_RGB888, + .color = FIMC_FMT_RGB888, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M | FMT_HAS_ALPHA, @@ -64,7 +64,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "ARGB1555", .fourcc = V4L2_PIX_FMT_RGB555, .depth = { 16 }, - .color = S5P_FIMC_RGB555, + .color = FIMC_FMT_RGB555, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA, @@ -72,7 +72,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "ARGB4444", .fourcc = V4L2_PIX_FMT_RGB444, .depth = { 16 }, - .color = S5P_FIMC_RGB444, + .color = FIMC_FMT_RGB444, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA, @@ -80,7 +80,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 packed, YCbYCr", .fourcc = V4L2_PIX_FMT_YUYV, .depth = { 16 }, - .color = S5P_FIMC_YCBYCR422, + .color = FIMC_FMT_YCBYCR422, .memplanes = 1, .colplanes = 1, .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, @@ -89,7 +89,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 packed, CbYCrY", .fourcc = V4L2_PIX_FMT_UYVY, .depth = { 16 }, - .color = S5P_FIMC_CBYCRY422, + .color = FIMC_FMT_CBYCRY422, .memplanes = 1, .colplanes = 1, .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, @@ -98,7 +98,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 packed, CrYCbY", .fourcc = V4L2_PIX_FMT_VYUY, .depth = { 16 }, - .color = S5P_FIMC_CRYCBY422, + .color = FIMC_FMT_CRYCBY422, .memplanes = 1, .colplanes = 1, .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, @@ -107,7 +107,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 packed, YCrYCb", .fourcc = V4L2_PIX_FMT_YVYU, .depth = { 16 }, - .color = S5P_FIMC_YCRYCB422, + .color = FIMC_FMT_YCRYCB422, .memplanes = 1, .colplanes = 1, .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, @@ -116,7 +116,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 planar, Y/Cb/Cr", .fourcc = V4L2_PIX_FMT_YUV422P, .depth = { 12 }, - .color = S5P_FIMC_YCBYCR422, + .color = FIMC_FMT_YCBYCR422, .memplanes = 1, .colplanes = 3, .flags = FMT_FLAGS_M2M, @@ -124,7 +124,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 planar, Y/CbCr", .fourcc = V4L2_PIX_FMT_NV16, .depth = { 16 }, - .color = S5P_FIMC_YCBYCR422, + .color = FIMC_FMT_YCBYCR422, .memplanes = 1, .colplanes = 2, .flags = FMT_FLAGS_M2M, @@ -132,7 +132,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 planar, Y/CrCb", .fourcc = V4L2_PIX_FMT_NV61, .depth = { 16 }, - .color = S5P_FIMC_YCRYCB422, + .color = FIMC_FMT_YCRYCB422, .memplanes = 1, .colplanes = 2, .flags = FMT_FLAGS_M2M, @@ -140,7 +140,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:0 planar, YCbCr", .fourcc = V4L2_PIX_FMT_YUV420, .depth = { 12 }, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .memplanes = 1, .colplanes = 3, .flags = FMT_FLAGS_M2M, @@ -148,14 +148,14 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:0 planar, Y/CbCr", .fourcc = V4L2_PIX_FMT_NV12, .depth = { 12 }, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .memplanes = 1, .colplanes = 2, .flags = FMT_FLAGS_M2M, }, { .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr", .fourcc = V4L2_PIX_FMT_NV12M, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .depth = { 8, 4 }, .memplanes = 2, .colplanes = 2, @@ -163,7 +163,7 @@ static struct fimc_fmt fimc_formats[] = { }, { .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr", .fourcc = V4L2_PIX_FMT_YUV420M, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .depth = { 8, 2, 2 }, .memplanes = 3, .colplanes = 3, @@ -171,7 +171,7 @@ static struct fimc_fmt fimc_formats[] = { }, { .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled", .fourcc = V4L2_PIX_FMT_NV12MT, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .depth = { 8, 4 }, .memplanes = 2, .colplanes = 2, @@ -179,7 +179,7 @@ static struct fimc_fmt fimc_formats[] = { }, { .name = "JPEG encoded data", .fourcc = V4L2_PIX_FMT_JPEG, - .color = S5P_FIMC_JPEG, + .color = FIMC_FMT_JPEG, .depth = { 8 }, .memplanes = 1, .colplanes = 1, @@ -361,7 +361,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, case 3: paddr->cb = (u32)(paddr->y + pix_size); /* decompose Y into Y/Cb/Cr */ - if (S5P_FIMC_YCBCR420 == frame->fmt->color) + if (FIMC_FMT_YCBCR420 == frame->fmt->color) paddr->cr = (u32)(paddr->cb + (pix_size >> 2)); else /* 422 */ @@ -394,16 +394,16 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) /* Set order for 1 plane input formats. */ switch (ctx->s_frame.fmt->color) { - case S5P_FIMC_YCRYCB422: + case FIMC_FMT_YCRYCB422: ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY; break; - case S5P_FIMC_CBYCRY422: + case FIMC_FMT_CBYCRY422: ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB; break; - case S5P_FIMC_CRYCBY422: + case FIMC_FMT_CRYCBY422: ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR; break; - case S5P_FIMC_YCBYCR422: + case FIMC_FMT_YCBYCR422: default: ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY; break; @@ -411,16 +411,16 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) dbg("ctx->in_order_1p= %d", ctx->in_order_1p); switch (ctx->d_frame.fmt->color) { - case S5P_FIMC_YCRYCB422: + case FIMC_FMT_YCRYCB422: ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY; break; - case S5P_FIMC_CBYCRY422: + case FIMC_FMT_CBYCRY422: ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB; break; - case S5P_FIMC_CRYCBY422: + case FIMC_FMT_CRYCBY422: ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR; break; - case S5P_FIMC_YCBYCR422: + case FIMC_FMT_YCBYCR422: default: ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY; break; @@ -453,7 +453,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->dma_offset.cb_h >>= 1; f->dma_offset.cr_h >>= 1; } - if (f->fmt->color == S5P_FIMC_YCBCR420) { + if (f->fmt->color == FIMC_FMT_YCBCR420) { f->dma_offset.cb_v >>= 1; f->dma_offset.cr_v >>= 1; } diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 8b073979cee8..fbb651cc6a96 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -76,26 +76,31 @@ enum fimc_dev_flags { #define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state) enum fimc_datapath { - FIMC_CAMERA, - FIMC_DMA, - FIMC_LCDFIFO, - FIMC_WRITEBACK + FIMC_IO_NONE, + FIMC_IO_CAMERA, + FIMC_IO_DMA, + FIMC_IO_LCDFIFO, + FIMC_IO_WRITEBACK, + FIMC_IO_ISP, }; enum fimc_color_fmt { - S5P_FIMC_RGB444 = 0x10, - S5P_FIMC_RGB555, - S5P_FIMC_RGB565, - S5P_FIMC_RGB666, - S5P_FIMC_RGB888, - S5P_FIMC_RGB30_LOCAL, - S5P_FIMC_YCBCR420 = 0x20, - S5P_FIMC_YCBYCR422, - S5P_FIMC_YCRYCB422, - S5P_FIMC_CBYCRY422, - S5P_FIMC_CRYCBY422, - S5P_FIMC_YCBCR444_LOCAL, - S5P_FIMC_JPEG = 0x40, + FIMC_FMT_RGB444 = 0x10, + FIMC_FMT_RGB555, + FIMC_FMT_RGB565, + FIMC_FMT_RGB666, + FIMC_FMT_RGB888, + FIMC_FMT_RGB30_LOCAL, + FIMC_FMT_YCBCR420 = 0x20, + FIMC_FMT_YCBYCR422, + FIMC_FMT_YCRYCB422, + FIMC_FMT_CBYCRY422, + FIMC_FMT_CRYCBY422, + FIMC_FMT_YCBCR444_LOCAL, + FIMC_FMT_JPEG = 0x40, + FIMC_FMT_RAW8 = 0x80, + FIMC_FMT_RAW10, + FIMC_FMT_RAW12, }; #define fimc_fmt_is_rgb(x) (!!((x) & 0x10)) @@ -563,9 +568,9 @@ static inline int tiled_fmt(struct fimc_fmt *fmt) static inline int fimc_get_alpha_mask(struct fimc_fmt *fmt) { switch (fmt->color) { - case S5P_FIMC_RGB444: return 0x0f; - case S5P_FIMC_RGB555: return 0x01; - case S5P_FIMC_RGB888: return 0xff; + case FIMC_FMT_RGB444: return 0x0f; + case FIMC_FMT_RGB555: return 0x01; + case FIMC_FMT_RGB888: return 0xff; default: return 0; }; } diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c index 70edc75e6fce..60bbab157c24 100644 --- a/drivers/media/video/s5p-fimc/fimc-m2m.c +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -676,8 +676,8 @@ static int fimc_m2m_open(struct file *file) /* Setup the device context for memory-to-memory mode */ ctx->state = FIMC_CTX_M2M; ctx->flags = 0; - ctx->in_path = FIMC_DMA; - ctx->out_path = FIMC_DMA; + ctx->in_path = FIMC_IO_DMA; + ctx->out_path = FIMC_IO_DMA; ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 31a8b99ee71a..5b1adde5a57a 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -85,13 +85,13 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx) * in direct fifo output mode. */ if (ctx->rotation == 90 || ctx->rotation == 270) { - if (ctx->out_path == FIMC_LCDFIFO) + if (ctx->out_path == FIMC_IO_LCDFIFO) cfg |= FIMC_REG_CITRGFMT_INROT90; else cfg |= FIMC_REG_CITRGFMT_OUTROT90; } - if (ctx->out_path == FIMC_DMA) { + if (ctx->out_path == FIMC_IO_DMA) { cfg |= fimc_hw_get_target_flip(ctx); writel(cfg, dev->regs + FIMC_REG_CITRGFMT); } else { @@ -117,13 +117,13 @@ void fimc_hw_set_target_format(struct fimc_ctx *ctx) FIMC_REG_CITRGFMT_VSIZE_MASK); switch (frame->fmt->color) { - case S5P_FIMC_RGB444...S5P_FIMC_RGB888: + case FIMC_FMT_RGB444...FIMC_FMT_RGB888: cfg |= FIMC_REG_CITRGFMT_RGB; break; - case S5P_FIMC_YCBCR420: + case FIMC_FMT_YCBCR420: cfg |= FIMC_REG_CITRGFMT_YCBCR420; break; - case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422: + case FIMC_FMT_YCBYCR422...FIMC_FMT_CRYCBY422: if (frame->fmt->colplanes == 1) cfg |= FIMC_REG_CITRGFMT_YCBCR422_1P; else @@ -200,11 +200,11 @@ void fimc_hw_set_out_dma(struct fimc_ctx *ctx) else if (fmt->colplanes == 3) cfg |= FIMC_REG_CIOCTRL_YCBCR_3PLANE; - if (fmt->color == S5P_FIMC_RGB565) + if (fmt->color == FIMC_FMT_RGB565) cfg |= FIMC_REG_CIOCTRL_RGB565; - else if (fmt->color == S5P_FIMC_RGB555) + else if (fmt->color == FIMC_FMT_RGB555) cfg |= FIMC_REG_CIOCTRL_ARGB1555; - else if (fmt->color == S5P_FIMC_RGB444) + else if (fmt->color == FIMC_FMT_RGB444) cfg |= FIMC_REG_CIOCTRL_ARGB4444; writel(cfg, dev->regs + FIMC_REG_CIOCTRL); @@ -277,28 +277,28 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) if (sc->copy_mode) cfg |= FIMC_REG_CISCCTRL_ONE2ONE; - if (ctx->in_path == FIMC_DMA) { + if (ctx->in_path == FIMC_IO_DMA) { switch (src_frame->fmt->color) { - case S5P_FIMC_RGB565: + case FIMC_FMT_RGB565: cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB565; break; - case S5P_FIMC_RGB666: + case FIMC_FMT_RGB666: cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB666; break; - case S5P_FIMC_RGB888: + case FIMC_FMT_RGB888: cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB888; break; } } - if (ctx->out_path == FIMC_DMA) { + if (ctx->out_path == FIMC_IO_DMA) { u32 color = dst_frame->fmt->color; - if (color >= S5P_FIMC_RGB444 && color <= S5P_FIMC_RGB565) + if (color >= FIMC_FMT_RGB444 && color <= FIMC_FMT_RGB565) cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB565; - else if (color == S5P_FIMC_RGB666) + else if (color == FIMC_FMT_RGB666) cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB666; - else if (color == S5P_FIMC_RGB888) + else if (color == FIMC_FMT_RGB888) cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888; } else { cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888; @@ -351,7 +351,7 @@ void fimc_hw_en_capture(struct fimc_ctx *ctx) u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - if (ctx->out_path == FIMC_DMA) { + if (ctx->out_path == FIMC_IO_DMA) { /* one shot mode */ cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | FIMC_REG_CIIMGCPT_IMGCPTEN; @@ -408,7 +408,7 @@ static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx) u32 cfg_o = 0; u32 cfg_r = 0; - if (FIMC_LCDFIFO == ctx->out_path) + if (FIMC_IO_LCDFIFO == ctx->out_path) cfg_r |= FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; cfg_o |= (frame->f_height << 16) | frame->f_width; @@ -439,7 +439,7 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) fimc_hw_set_in_dma_size(ctx); /* Use DMA autoload only in FIFO mode. */ - fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO); + fimc_hw_en_autoload(dev, ctx->out_path == FIMC_IO_LCDFIFO); /* Set the input DMA to process single frame only. */ cfg = readl(dev->regs + FIMC_REG_MSCTRL); @@ -454,10 +454,10 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) | FIMC_REG_MSCTRL_FIFO_CTRL_FULL); switch (frame->fmt->color) { - case S5P_FIMC_RGB565...S5P_FIMC_RGB888: + case FIMC_FMT_RGB565...FIMC_FMT_RGB888: cfg |= FIMC_REG_MSCTRL_INFORMAT_RGB; break; - case S5P_FIMC_YCBCR420: + case FIMC_FMT_YCBCR420: cfg |= FIMC_REG_MSCTRL_INFORMAT_YCBCR420; if (frame->fmt->colplanes == 2) @@ -466,7 +466,7 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) cfg |= FIMC_REG_MSCTRL_C_INT_IN_3PLANE; break; - case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422: + case FIMC_FMT_YCBYCR422...FIMC_FMT_CRYCBY422: if (frame->fmt->colplanes == 1) { cfg |= ctx->in_order_1p | FIMC_REG_MSCTRL_INFORMAT_YCBCR422_1P; @@ -507,7 +507,7 @@ void fimc_hw_set_input_path(struct fimc_ctx *ctx) u32 cfg = readl(dev->regs + FIMC_REG_MSCTRL); cfg &= ~FIMC_REG_MSCTRL_INPUT_MASK; - if (ctx->in_path == FIMC_DMA) + if (ctx->in_path == FIMC_IO_DMA) cfg |= FIMC_REG_MSCTRL_INPUT_MEMORY; else cfg |= FIMC_REG_MSCTRL_INPUT_EXTCAM; @@ -521,7 +521,7 @@ void fimc_hw_set_output_path(struct fimc_ctx *ctx) u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); cfg &= ~FIMC_REG_CISCCTRL_LCDPATHEN_FIFO; - if (ctx->out_path == FIMC_LCDFIFO) + if (ctx->out_path == FIMC_IO_LCDFIFO) cfg |= FIMC_REG_CISCCTRL_LCDPATHEN_FIFO; writel(cfg, dev->regs + FIMC_REG_CISCCTRL); } -- cgit v1.2.3 From bb7c276ec48b27d62a6d3f98e047a4b3d6a29b5c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 27 Apr 2012 09:33:23 -0300 Subject: [media] s5p-fimc: Minor cleanups Tidy up the variant and driver data handling. Remove the 'samsung_' prefix from some data structures since it doesn't really carry any useful information and makes the names unnecessarily long. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 33 +++++++++++++---------- drivers/media/video/s5p-fimc/fimc-core.c | 41 +++++++++++++---------------- drivers/media/video/s5p-fimc/fimc-core.h | 26 +++++++++--------- drivers/media/video/s5p-fimc/fimc-m2m.c | 2 +- drivers/media/video/s5p-fimc/fimc-reg.c | 2 +- 5 files changed, 53 insertions(+), 51 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index b36809d2f9fc..15b74083a066 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -31,7 +31,7 @@ #include "fimc-core.h" #include "fimc-reg.h" -static int fimc_init_capture(struct fimc_dev *fimc) +static int fimc_capture_hw_init(struct fimc_dev *fimc) { struct fimc_ctx *ctx = fimc->vid_cap.ctx; struct fimc_pipeline *p = &fimc->pipeline; @@ -73,6 +73,14 @@ static int fimc_init_capture(struct fimc_dev *fimc) return ret; } +/* + * Reinitialize the driver so it is ready to start the streaming again. + * Set fimc->state to indicate stream off and the hardware shut down state. + * If not suspending (@suspend is false), return any buffers to videobuf2. + * Otherwise put any owned buffers onto the pending buffers queue, so they + * can be re-spun when the device is being resumed. Also perform FIMC + * software reset and disable streaming on the whole pipeline if required. + */ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) { struct fimc_vid_cap *cap = &fimc->vid_cap; @@ -146,9 +154,6 @@ static int fimc_capture_config_update(struct fimc_ctx *ctx) struct fimc_dev *fimc = ctx->fimc_dev; int ret; - if (!test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) - return 0; - fimc_hw_set_camera_offset(fimc, &ctx->s_frame); ret = fimc_set_scaler_info(ctx); @@ -220,7 +225,8 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) set_bit(ST_CAPT_RUN, &fimc->state); } - fimc_capture_config_update(cap->ctx); + if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) + fimc_capture_config_update(cap->ctx); done: if (cap->active_buf_cnt == 1) { fimc_deactivate_capture(fimc); @@ -242,9 +248,11 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) vid_cap->frame_count = 0; - ret = fimc_init_capture(fimc); - if (ret) - goto error; + ret = fimc_capture_hw_init(fimc); + if (ret) { + fimc_capture_state_cleanup(fimc, false); + return ret; + } set_bit(ST_CAPT_PEND, &fimc->state); @@ -259,9 +267,6 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) } return 0; -error: - fimc_capture_state_cleanup(fimc, false); - return ret; } static int stop_streaming(struct vb2_queue *q) @@ -300,7 +305,7 @@ int fimc_capture_resume(struct fimc_dev *fimc) vid_cap->buf_index = 0; fimc_pipeline_initialize(&fimc->pipeline, &vid_cap->vfd->entity, false); - fimc_init_capture(fimc); + fimc_capture_hw_init(fimc); clear_bit(ST_CAPT_SUSPENDED, &fimc->state); @@ -563,7 +568,7 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, { bool rotation = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct samsung_fimc_variant *var = fimc->variant; + struct fimc_variant *var = fimc->variant; struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *dst = &ctx->d_frame; u32 depth, min_w, max_w, min_h, align_h = 3; @@ -629,7 +634,7 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, { bool rotate = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct samsung_fimc_variant *var = fimc->variant; + struct fimc_variant *var = fimc->variant; struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *sink = &ctx->s_frame; u32 max_w, max_h, min_w = 0, min_h = 0, min_sz; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index e0fe9748ec57..917eef412566 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -188,7 +188,7 @@ static struct fimc_fmt fimc_formats[] = { }, }; -struct fimc_fmt * fimc_get_format(unsigned int index) +struct fimc_fmt *fimc_get_format(unsigned int index) { if (index >= ARRAY_SIZE(fimc_formats)) return NULL; @@ -231,7 +231,7 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) int fimc_set_scaler_info(struct fimc_ctx *ctx) { - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + struct fimc_variant *variant = ctx->fimc_dev->variant; struct device *dev = &ctx->fimc_dev->pdev->dev; struct fimc_scaler *sc = &ctx->scaler; struct fimc_frame *s_frame = &ctx->s_frame; @@ -430,7 +430,7 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + struct fimc_variant *variant = ctx->fimc_dev->variant; u32 i, depth = 0; for (i = 0; i < f->fmt->colplanes; i++) @@ -472,7 +472,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) { struct fimc_dev *fimc = ctx->fimc_dev; - struct samsung_fimc_variant *variant = fimc->variant; + struct fimc_variant *variant = fimc->variant; unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT; int ret = 0; @@ -532,7 +532,7 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = { int fimc_ctrls_create(struct fimc_ctx *ctx) { - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); if (ctx->ctrls_rdy) @@ -794,15 +794,12 @@ static int fimc_m2m_resume(struct fimc_dev *fimc) static int fimc_probe(struct platform_device *pdev) { + struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); + struct s5p_platform_fimc *pdata; struct fimc_dev *fimc; struct resource *res; - struct samsung_fimc_driverdata *drv_data; - struct s5p_platform_fimc *pdata; int ret = 0; - drv_data = (struct samsung_fimc_driverdata *) - platform_get_device_id(pdev)->driver_data; - if (pdev->id >= drv_data->num_entities) { dev_err(&pdev->dev, "Invalid platform device id: %d\n", pdev->id); @@ -1004,7 +1001,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = { }, }; -static struct samsung_fimc_variant fimc0_variant_s5p = { +static struct fimc_variant fimc0_variant_s5p = { .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1016,17 +1013,17 @@ static struct samsung_fimc_variant fimc0_variant_s5p = { .pix_limit = &s5p_pix_limit[0], }; -static struct samsung_fimc_variant fimc2_variant_s5p = { +static struct fimc_variant fimc2_variant_s5p = { .has_cam_if = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, .out_buf_count = 4, - .pix_limit = &s5p_pix_limit[1], + .pix_limit = &s5p_pix_limit[1], }; -static struct samsung_fimc_variant fimc0_variant_s5pv210 = { +static struct fimc_variant fimc0_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1039,7 +1036,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[1], }; -static struct samsung_fimc_variant fimc1_variant_s5pv210 = { +static struct fimc_variant fimc1_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1053,7 +1050,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct samsung_fimc_variant fimc2_variant_s5pv210 = { +static struct fimc_variant fimc2_variant_s5pv210 = { .has_cam_if = 1, .pix_hoff = 1, .min_inp_pixsize = 16, @@ -1064,7 +1061,7 @@ static struct samsung_fimc_variant fimc2_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct samsung_fimc_variant fimc0_variant_exynos4 = { +static struct fimc_variant fimc0_variant_exynos4 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1080,7 +1077,7 @@ static struct samsung_fimc_variant fimc0_variant_exynos4 = { .pix_limit = &s5p_pix_limit[1], }; -static struct samsung_fimc_variant fimc3_variant_exynos4 = { +static struct fimc_variant fimc3_variant_exynos4 = { .pix_hoff = 1, .has_cam_if = 1, .has_cistatus2 = 1, @@ -1095,7 +1092,7 @@ static struct samsung_fimc_variant fimc3_variant_exynos4 = { }; /* S5PC100 */ -static struct samsung_fimc_driverdata fimc_drvdata_s5p = { +static struct fimc_drvdata fimc_drvdata_s5p = { .variant = { [0] = &fimc0_variant_s5p, [1] = &fimc0_variant_s5p, @@ -1106,7 +1103,7 @@ static struct samsung_fimc_driverdata fimc_drvdata_s5p = { }; /* S5PV210, S5PC110 */ -static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = { +static struct fimc_drvdata fimc_drvdata_s5pv210 = { .variant = { [0] = &fimc0_variant_s5pv210, [1] = &fimc1_variant_s5pv210, @@ -1116,8 +1113,8 @@ static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = { .lclk_frequency = 166000000UL, }; -/* S5PV310, S5PC210 */ -static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = { +/* EXYNOS4210, S5PV310, S5PC210 */ +static struct fimc_drvdata fimc_drvdata_exynos4 = { .variant = { [0] = &fimc0_variant_exynos4, [1] = &fimc0_variant_exynos4, diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index fbb651cc6a96..97a25d2fac58 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -360,8 +360,7 @@ struct fimc_pix_limit { }; /** - * struct samsung_fimc_variant - camera interface variant information - * + * struct fimc_variant - FIMC device variant information * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes * @has_inp_rot: set if has input rotator * @has_out_rot: set if has output rotator @@ -376,7 +375,7 @@ struct fimc_pix_limit { * @min_vsize_align: minimum vertical pixel size alignment * @out_buf_count: the number of buffers in output DMA sequence */ -struct samsung_fimc_variant { +struct fimc_variant { unsigned int pix_hoff:1; unsigned int has_inp_rot:1; unsigned int has_out_rot:1; @@ -393,18 +392,19 @@ struct samsung_fimc_variant { }; /** - * struct samsung_fimc_driverdata - per device type driver data for init time. - * - * @variant: the variant information for this driver. - * @dev_cnt: number of fimc sub-devices available in SoC - * @lclk_frequency: fimc bus clock frequency + * struct fimc_drvdata - per device type driver data + * @variant: variant information for this device + * @num_entities: number of fimc instances available in a SoC + * @lclk_frequency: local bus clock frequency */ -struct samsung_fimc_driverdata { - struct samsung_fimc_variant *variant[FIMC_MAX_DEVS]; - unsigned long lclk_frequency; - int num_entities; +struct fimc_drvdata { + struct fimc_variant *variant[FIMC_MAX_DEVS]; + int num_entities; + unsigned long lclk_frequency; }; +#define fimc_get_drvdata(_pdev) \ + ((struct fimc_drvdata *) platform_get_device_id(_pdev)->driver_data) struct fimc_ctx; @@ -431,7 +431,7 @@ struct fimc_dev { struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; - struct samsung_fimc_variant *variant; + struct fimc_variant *variant; u16 id; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c index 60bbab157c24..3de22b0db66f 100644 --- a/drivers/media/video/s5p-fimc/fimc-m2m.c +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -295,7 +295,7 @@ static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) { struct fimc_dev *fimc = ctx->fimc_dev; - struct samsung_fimc_variant *variant = fimc->variant; + struct fimc_variant *variant = fimc->variant; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct fimc_fmt *fmt; u32 max_w, mod_x, mod_y; diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 5b1adde5a57a..382981788c43 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -313,7 +313,7 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - struct samsung_fimc_variant *variant = dev->variant; + struct fimc_variant *variant = dev->variant; struct fimc_scaler *sc = &ctx->scaler; u32 cfg; -- cgit v1.2.3 From 5af86c2691b9e0fc1f993cdcd4aa27eb684c1c17 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 27 Apr 2012 08:35:44 -0300 Subject: [media] s5p-fimc: Make sure an interrupt is properly requested Use dev_name() for requesting an interrupt so we don't get an interrupt requested with same name for multiple device instances. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 917eef412566..2e319c71de02 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -841,7 +841,7 @@ static int fimc_probe(struct platform_device *pdev) clk_enable(fimc->clock[CLK_BUS]); ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, - 0, pdev->name, fimc); + 0, dev_name(&pdev->dev), fimc); if (ret) { dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); goto err_clk; -- cgit v1.2.3 From 4af813108b880e96a4b8b01e162f950a4aaa2475 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 27 Apr 2012 05:29:05 -0300 Subject: [media] s5p-fimc: Add support for Exynos4x12 FIMC-LITE This patch adds driver for FIMC-LITE camera host interface. This new IP differs from the regular FIMC IP in that it doesn't have input DMA, scaler and color space conversion support. So it just plain camera host interface for MIPI-CSI2 and ITU-R interfaces. For the serial bus support it interworks with MIPI-CSIS and the exisiting s5p-csis driver. The FIMC-LITE and MIPI-CSIS drivers can also be reused in the Exynos5 SoC series. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 24 +- drivers/media/video/s5p-fimc/Kconfig | 48 + drivers/media/video/s5p-fimc/Makefile | 4 +- drivers/media/video/s5p-fimc/fimc-core.h | 1 + drivers/media/video/s5p-fimc/fimc-lite.c | 1576 +++++++++++++++++++++++++++ drivers/media/video/s5p-fimc/fimc-mdevice.c | 152 ++- drivers/media/video/s5p-fimc/fimc-mdevice.h | 5 +- 7 files changed, 1766 insertions(+), 44 deletions(-) create mode 100644 drivers/media/video/s5p-fimc/Kconfig create mode 100644 drivers/media/video/s5p-fimc/fimc-lite.c diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 3dc0ea7ba927..268b36db1411 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -1130,19 +1130,6 @@ config VIDEO_MX2 This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor Interface -config VIDEO_SAMSUNG_S5P_FIMC - tristate "Samsung S5P and EXYNOS4 camera interface driver (EXPERIMENTAL)" - depends on VIDEO_V4L2 && I2C && PLAT_S5P && PM_RUNTIME && \ - VIDEO_V4L2_SUBDEV_API && EXPERIMENTAL - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - ---help--- - This is a v4l2 driver for Samsung S5P and EXYNOS4 camera - host interface and video postprocessor. - - To compile this driver as a module, choose M here: the - module will be called s5p-fimc. - config VIDEO_ATMEL_ISI tristate "ATMEL Image Sensor Interface (ISI) support" depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91 @@ -1151,16 +1138,7 @@ config VIDEO_ATMEL_ISI This module makes the ATMEL Image Sensor Interface available as a v4l2 device. -config VIDEO_S5P_MIPI_CSIS - tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver" - depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P - depends on VIDEO_V4L2_SUBDEV_API && REGULATOR - ---help--- - This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver. - - To compile this driver as a module, choose M here: the - module will be called s5p-csis. - +source "drivers/media/video/s5p-fimc/Kconfig" source "drivers/media/video/s5p-tv/Kconfig" endif # V4L_PLATFORM_DRIVERS diff --git a/drivers/media/video/s5p-fimc/Kconfig b/drivers/media/video/s5p-fimc/Kconfig new file mode 100644 index 000000000000..a564f7eeb064 --- /dev/null +++ b/drivers/media/video/s5p-fimc/Kconfig @@ -0,0 +1,48 @@ + +config VIDEO_SAMSUNG_S5P_FIMC + bool "Samsung S5P/EXYNOS SoC camera interface driver (experimental)" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PLAT_S5P && PM_RUNTIME + depends on EXPERIMENTAL + help + Say Y here to enable camera host interface devices for + Samsung S5P and EXYNOS SoC series. + +if VIDEO_SAMSUNG_S5P_FIMC + +config VIDEO_S5P_FIMC + tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver" + depends on I2C + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host + interface and video postprocessor (FIMC and FIMC-LITE) devices. + + To compile this driver as a module, choose M here: the + module will be called s5p-fimc. + +config VIDEO_S5P_MIPI_CSIS + tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver" + depends on REGULATOR + help + This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2 + receiver (MIPI-CSIS) devices. + + To compile this driver as a module, choose M here: the + module will be called s5p-csis. + +if ARCH_EXYNOS + +config VIDEO_EXYNOS_FIMC_LITE + tristate "EXYNOS FIMC-LITE camera interface driver" + depends on I2C + select VIDEOBUF2_DMA_CONTIG + help + This is a V4L2 driver for Samsung EXYNOS4/5 SoC FIMC-LITE camera + host interface. + + To compile this driver as a module, choose M here: the + module will be called exynos-fimc-lite. +endif + +endif # VIDEO_SAMSUNG_S5P_FIMC diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile index 1da3c6ae7fa8..46485143e1ca 100644 --- a/drivers/media/video/s5p-fimc/Makefile +++ b/drivers/media/video/s5p-fimc/Makefile @@ -1,5 +1,7 @@ s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o +exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o s5p-csis-objs := mipi-csis.o obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc.o +obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o +obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 97a25d2fac58..c67d485ecce6 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c new file mode 100644 index 000000000000..400d701aef04 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -0,0 +1,1576 @@ +/* + * Samsung EXYNOS FIMC-LITE (camera host interface) driver +* + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fimc-mdevice.h" +#include "fimc-core.h" +#include "fimc-lite-reg.h" + +static int debug; +module_param(debug, int, 0644); + +static const struct fimc_fmt fimc_lite_formats[] = { + { + .name = "YUV 4:2:2 packed, YCbYCr", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = { 16 }, + .color = FIMC_FMT_YCBYCR422, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + }, { + .name = "YUV 4:2:2 packed, CbYCrY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = { 16 }, + .color = FIMC_FMT_CBYCRY422, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + }, { + .name = "YUV 4:2:2 packed, CrYCbY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = { 16 }, + .color = FIMC_FMT_CRYCBY422, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, + }, { + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = { 16 }, + .color = FIMC_FMT_YCRYCB422, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, + }, { + .name = "RAW8 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG8, + .depth = { 8 }, + .color = FIMC_FMT_RAW8, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8, + }, { + .name = "RAW10 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG10, + .depth = { 10 }, + .color = FIMC_FMT_RAW10, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, + }, { + .name = "RAW12 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG12, + .depth = { 12 }, + .color = FIMC_FMT_RAW12, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, + }, +}; + +/** + * fimc_lite_find_format - lookup fimc color format by fourcc or media bus code + * @pixelformat: fourcc to match, ignored if null + * @mbus_code: media bus code to match, ignored if null + * @index: index to the fimc_lite_formats array, ignored if negative + */ +static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, + const u32 *mbus_code, int index) +{ + const struct fimc_fmt *fmt, *def_fmt = NULL; + unsigned int i; + int id = 0; + + if (index >= (int)ARRAY_SIZE(fimc_lite_formats)) + return NULL; + + for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) { + fmt = &fimc_lite_formats[i]; + if (pixelformat && fmt->fourcc == *pixelformat) + return fmt; + if (mbus_code && fmt->mbus_code == *mbus_code) + return fmt; + if (index == id) + def_fmt = fmt; + id++; + } + return def_fmt; +} + +static int fimc_lite_hw_init(struct fimc_lite *fimc) +{ + struct fimc_pipeline *pipeline = &fimc->pipeline; + struct fimc_sensor_info *sensor; + unsigned long flags; + + if (pipeline->subdevs[IDX_SENSOR] == NULL) + return -ENXIO; + + if (fimc->fmt == NULL) + return -EINVAL; + + sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]); + spin_lock_irqsave(&fimc->slock, flags); + + flite_hw_set_camera_bus(fimc, sensor->pdata); + flite_hw_set_source_format(fimc, &fimc->inp_frame); + flite_hw_set_window_offset(fimc, &fimc->inp_frame); + flite_hw_set_output_dma(fimc, &fimc->out_frame, true); + flite_hw_set_interrupt_mask(fimc); + flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); + + if (debug > 0) + flite_hw_dump_regs(fimc, __func__); + + spin_unlock_irqrestore(&fimc->slock, flags); + return 0; +} + +/* + * Reinitialize the driver so it is ready to start the streaming again. + * Set fimc->state to indicate stream off and the hardware shut down state. + * If not suspending (@suspend is false), return any buffers to videobuf2. + * Otherwise put any owned buffers onto the pending buffers queue, so they + * can be re-spun when the device is being resumed. Also perform FIMC + * software reset and disable streaming on the whole pipeline if required. + */ +static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend) +{ + struct flite_buffer *buf; + unsigned long flags; + bool streaming; + + spin_lock_irqsave(&fimc->slock, flags); + streaming = fimc->state & (1 << ST_SENSOR_STREAM); + + fimc->state &= ~(1 << ST_FLITE_RUN | 1 << ST_FLITE_OFF | + 1 << ST_FLITE_STREAM | 1 << ST_SENSOR_STREAM); + if (suspend) + fimc->state |= (1 << ST_FLITE_SUSPENDED); + else + fimc->state &= ~(1 << ST_FLITE_PENDING | + 1 << ST_FLITE_SUSPENDED); + + /* Release unused buffers */ + while (!suspend && !list_empty(&fimc->pending_buf_q)) { + buf = fimc_lite_pending_queue_pop(fimc); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + /* If suspending put unused buffers onto pending queue */ + while (!list_empty(&fimc->active_buf_q)) { + buf = fimc_lite_active_queue_pop(fimc); + if (suspend) + fimc_lite_pending_queue_add(fimc, buf); + else + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + + spin_unlock_irqrestore(&fimc->slock, flags); + + flite_hw_reset(fimc); + + if (!streaming) + return 0; + + return fimc_pipeline_s_stream(&fimc->pipeline, 0); +} + +static int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend) +{ + unsigned long flags; + + if (!fimc_lite_active(fimc)) + return 0; + + spin_lock_irqsave(&fimc->slock, flags); + set_bit(ST_FLITE_OFF, &fimc->state); + flite_hw_capture_stop(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + wait_event_timeout(fimc->irq_queue, + !test_bit(ST_FLITE_OFF, &fimc->state), + (2*HZ/10)); /* 200 ms */ + + return fimc_lite_reinit(fimc, suspend); +} + +/* Must be called with fimc.slock spinlock held. */ +static void fimc_lite_config_update(struct fimc_lite *fimc) +{ + flite_hw_set_window_offset(fimc, &fimc->inp_frame); + flite_hw_set_dma_window(fimc, &fimc->out_frame); + flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); + clear_bit(ST_FLITE_CONFIG, &fimc->state); +} + +static irqreturn_t flite_irq_handler(int irq, void *priv) +{ + struct fimc_lite *fimc = priv; + struct flite_buffer *vbuf; + unsigned long flags; + struct timeval *tv; + struct timespec ts; + u32 intsrc; + + spin_lock_irqsave(&fimc->slock, flags); + + intsrc = flite_hw_get_interrupt_source(fimc); + flite_hw_clear_pending_irq(fimc); + + if (test_and_clear_bit(ST_FLITE_OFF, &fimc->state)) { + wake_up(&fimc->irq_queue); + goto done; + } + + if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW) { + clear_bit(ST_FLITE_RUN, &fimc->state); + fimc->events.data_overflow++; + } + + if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND) { + flite_hw_clear_last_capture_end(fimc); + clear_bit(ST_FLITE_STREAM, &fimc->state); + wake_up(&fimc->irq_queue); + } + + if (fimc->out_path != FIMC_IO_DMA) + goto done; + + if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) && + test_bit(ST_FLITE_RUN, &fimc->state) && + !list_empty(&fimc->active_buf_q) && + !list_empty(&fimc->pending_buf_q)) { + vbuf = fimc_lite_active_queue_pop(fimc); + ktime_get_ts(&ts); + tv = &vbuf->vb.v4l2_buf.timestamp; + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; + vbuf->vb.v4l2_buf.sequence = fimc->frame_count++; + vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE); + + vbuf = fimc_lite_pending_queue_pop(fimc); + flite_hw_set_output_addr(fimc, vbuf->paddr); + fimc_lite_active_queue_add(fimc, vbuf); + } + + if (test_bit(ST_FLITE_CONFIG, &fimc->state)) + fimc_lite_config_update(fimc); + + if (list_empty(&fimc->pending_buf_q)) { + flite_hw_capture_stop(fimc); + clear_bit(ST_FLITE_STREAM, &fimc->state); + } +done: + set_bit(ST_FLITE_RUN, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + return IRQ_HANDLED; +} + +static int start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct fimc_lite *fimc = q->drv_priv; + int ret; + + fimc->frame_count = 0; + + ret = fimc_lite_hw_init(fimc); + if (ret) { + fimc_lite_reinit(fimc, false); + return ret; + } + + set_bit(ST_FLITE_PENDING, &fimc->state); + + if (!list_empty(&fimc->active_buf_q) && + !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) { + flite_hw_capture_start(fimc); + + if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) + fimc_pipeline_s_stream(&fimc->pipeline, 1); + } + if (debug > 0) + flite_hw_dump_regs(fimc, __func__); + + return 0; +} + +static int stop_streaming(struct vb2_queue *q) +{ + struct fimc_lite *fimc = q->drv_priv; + + if (!fimc_lite_active(fimc)) + return -EINVAL; + + return fimc_lite_stop_capture(fimc, false); +} + +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *allocators[]) +{ + const struct v4l2_pix_format_mplane *pixm = NULL; + struct fimc_lite *fimc = vq->drv_priv; + struct flite_frame *frame = &fimc->out_frame; + const struct fimc_fmt *fmt = fimc->fmt; + unsigned long wh; + int i; + + if (pfmt) { + pixm = &pfmt->fmt.pix_mp; + fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, -1); + wh = pixm->width * pixm->height; + } else { + wh = frame->f_width * frame->f_height; + } + + if (fmt == NULL) + return -EINVAL; + + *num_planes = fmt->memplanes; + + for (i = 0; i < fmt->memplanes; i++) { + unsigned int size = (wh * fmt->depth[i]) / 8; + if (pixm) + sizes[i] = max(size, pixm->plane_fmt[i].sizeimage); + else + sizes[i] = size; + allocators[i] = fimc->alloc_ctx; + } + + return 0; +} + +static int buffer_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct fimc_lite *fimc = vq->drv_priv; + int i; + + if (fimc->fmt == NULL) + return -EINVAL; + + for (i = 0; i < fimc->fmt->memplanes; i++) { + unsigned long size = fimc->payload[i]; + + if (vb2_plane_size(vb, i) < size) { + v4l2_err(fimc->vfd, + "User buffer too small (%ld < %ld)\n", + vb2_plane_size(vb, i), size); + return -EINVAL; + } + vb2_set_plane_payload(vb, i, size); + } + + return 0; +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + struct flite_buffer *buf + = container_of(vb, struct flite_buffer, vb); + struct fimc_lite *fimc = vb2_get_drv_priv(vb->vb2_queue); + unsigned long flags; + + spin_lock_irqsave(&fimc->slock, flags); + buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0); + + if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) && + !test_bit(ST_FLITE_STREAM, &fimc->state) && + list_empty(&fimc->active_buf_q)) { + flite_hw_set_output_addr(fimc, buf->paddr); + fimc_lite_active_queue_add(fimc, buf); + } else { + fimc_lite_pending_queue_add(fimc, buf); + } + + if (vb2_is_streaming(&fimc->vb_queue) && + !list_empty(&fimc->pending_buf_q) && + !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) { + flite_hw_capture_start(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) + fimc_pipeline_s_stream(&fimc->pipeline, 1); + return; + } + spin_unlock_irqrestore(&fimc->slock, flags); +} + +static void fimc_lock(struct vb2_queue *vq) +{ + struct fimc_lite *fimc = vb2_get_drv_priv(vq); + mutex_lock(&fimc->lock); +} + +static void fimc_unlock(struct vb2_queue *vq) +{ + struct fimc_lite *fimc = vb2_get_drv_priv(vq); + mutex_unlock(&fimc->lock); +} + +static const struct vb2_ops fimc_lite_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .wait_prepare = fimc_unlock, + .wait_finish = fimc_lock, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, +}; + +static void fimc_lite_clear_event_counters(struct fimc_lite *fimc) +{ + unsigned long flags; + + spin_lock_irqsave(&fimc->slock, flags); + memset(&fimc->events, 0, sizeof(fimc->events)); + spin_unlock_irqrestore(&fimc->slock, flags); +} + +static int fimc_lite_open(struct file *file) +{ + struct fimc_lite *fimc = video_drvdata(file); + int ret = v4l2_fh_open(file); + + if (ret) + return ret; + + set_bit(ST_FLITE_IN_USE, &fimc->state); + pm_runtime_get_sync(&fimc->pdev->dev); + + if (++fimc->ref_count != 1 || fimc->out_path != FIMC_IO_DMA) + return ret; + + ret = fimc_pipeline_initialize(&fimc->pipeline, &fimc->vfd->entity, + true); + if (ret < 0) { + v4l2_err(fimc->vfd, "Video pipeline initialization failed\n"); + pm_runtime_put_sync(&fimc->pdev->dev); + fimc->ref_count--; + v4l2_fh_release(file); + clear_bit(ST_FLITE_IN_USE, &fimc->state); + } + + fimc_lite_clear_event_counters(fimc); + return ret; +} + +static int fimc_lite_close(struct file *file) +{ + struct fimc_lite *fimc = video_drvdata(file); + + if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) { + clear_bit(ST_FLITE_IN_USE, &fimc->state); + fimc_lite_stop_capture(fimc, false); + fimc_pipeline_shutdown(&fimc->pipeline); + clear_bit(ST_FLITE_SUSPENDED, &fimc->state); + } + + pm_runtime_put(&fimc->pdev->dev); + + if (fimc->ref_count == 0) + vb2_queue_release(&fimc->vb_queue); + + return v4l2_fh_release(file); +} + +static unsigned int fimc_lite_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct fimc_lite *fimc = video_drvdata(file); + return vb2_poll(&fimc->vb_queue, file, wait); +} + +static int fimc_lite_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct fimc_lite *fimc = video_drvdata(file); + return vb2_mmap(&fimc->vb_queue, vma); +} + +static const struct v4l2_file_operations fimc_lite_fops = { + .owner = THIS_MODULE, + .open = fimc_lite_open, + .release = fimc_lite_close, + .poll = fimc_lite_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_lite_mmap, +}; + +/* + * Format and crop negotiation helpers + */ + +static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc, + u32 *width, u32 *height, + u32 *code, u32 *fourcc, int pad) +{ + struct flite_variant *variant = fimc->variant; + const struct fimc_fmt *fmt; + + fmt = fimc_lite_find_format(fourcc, code, 0); + if (WARN_ON(!fmt)) + return NULL; + + if (code) + *code = fmt->mbus_code; + if (fourcc) + *fourcc = fmt->fourcc; + + if (pad == FLITE_SD_PAD_SINK) { + v4l_bound_align_image(width, 8, variant->max_width, + ffs(variant->out_width_align) - 1, + height, 0, variant->max_height, 0, 0); + } else { + v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width, + ffs(variant->out_width_align) - 1, + height, 0, fimc->inp_frame.rect.height, + 0, 0); + } + + v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n", + code ? *code : 0, *width, *height); + + return fmt; +} + +static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r) +{ + struct flite_frame *frame = &fimc->inp_frame; + + v4l_bound_align_image(&r->width, 0, frame->f_width, 0, + &r->height, 0, frame->f_height, 0, 0); + + /* Adjust left/top if cropping rectangle got out of bounds */ + r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); + r->left = round_down(r->left, fimc->variant->win_hor_offs_align); + r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height); + + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d", + r->left, r->top, r->width, r->height, + frame->f_width, frame->f_height); +} + +static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) +{ + struct flite_frame *frame = &fimc->out_frame; + struct v4l2_rect *crop_rect = &fimc->inp_frame.rect; + + /* Scaling is not supported so we enforce compose rectangle size + same as size of the sink crop rectangle. */ + r->width = crop_rect->width; + r->height = crop_rect->height; + + /* Adjust left/top if the composing rectangle got out of bounds */ + r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); + r->left = round_down(r->left, fimc->variant->out_hor_offs_align); + r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height); + + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d", + r->left, r->top, r->width, r->height, + frame->f_width, frame->f_height); +} + +/* + * Video node ioctl operations + */ +static int fimc_vidioc_querycap_capture(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver)); + cap->bus_info[0] = 0; + cap->card[0] = 0; + cap->capabilities = V4L2_CAP_STREAMING; + return 0; +} + +static int fimc_lite_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct fimc_fmt *fmt; + + if (f->index >= ARRAY_SIZE(fimc_lite_formats)) + return -EINVAL; + + fmt = &fimc_lite_formats[f->index]; + strlcpy(f->description, fmt->name, sizeof(f->description)); + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int fimc_lite_g_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0]; + struct flite_frame *frame = &fimc->out_frame; + const struct fimc_fmt *fmt = fimc->fmt; + + plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8; + plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height; + + pixm->num_planes = fmt->memplanes; + pixm->pixelformat = fmt->fourcc; + pixm->width = frame->f_width; + pixm->height = frame->f_height; + pixm->field = V4L2_FIELD_NONE; + pixm->colorspace = V4L2_COLORSPACE_JPEG; + return 0; +} + +static int fimc_lite_try_fmt(struct fimc_lite *fimc, + struct v4l2_pix_format_mplane *pixm, + const struct fimc_fmt **ffmt) +{ + struct flite_variant *variant = fimc->variant; + u32 bpl = pixm->plane_fmt[0].bytesperline; + const struct fimc_fmt *fmt; + + fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0); + if (WARN_ON(fmt == NULL)) + return -EINVAL; + if (ffmt) + *ffmt = fmt; + v4l_bound_align_image(&pixm->width, 8, variant->max_width, + ffs(variant->out_width_align) - 1, + &pixm->height, 0, variant->max_height, 0, 0); + + if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width)) + pixm->plane_fmt[0].bytesperline = (pixm->width * + fmt->depth[0]) / 8; + + if (pixm->plane_fmt[0].sizeimage == 0) + pixm->plane_fmt[0].sizeimage = (pixm->width * pixm->height * + fmt->depth[0]) / 8; + pixm->num_planes = fmt->memplanes; + pixm->pixelformat = fmt->fourcc; + pixm->colorspace = V4L2_COLORSPACE_JPEG; + pixm->field = V4L2_FIELD_NONE; + return 0; +} + +static int fimc_lite_try_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL); +} + +static int fimc_lite_s_fmt_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; + struct fimc_lite *fimc = video_drvdata(file); + struct flite_frame *frame = &fimc->out_frame; + const struct fimc_fmt *fmt = NULL; + int ret; + + if (vb2_is_busy(&fimc->vb_queue)) + return -EBUSY; + + ret = fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, &fmt); + if (ret < 0) + return ret; + + fimc->fmt = fmt; + fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8, + pixm->plane_fmt[0].sizeimage); + frame->f_width = pixm->width; + frame->f_height = pixm->height; + + return 0; +} + +static int fimc_pipeline_validate(struct fimc_lite *fimc) +{ + struct v4l2_subdev *sd = &fimc->subdev; + struct v4l2_subdev_format sink_fmt, src_fmt; + struct media_pad *pad; + int ret; + + while (1) { + /* Retrieve format at the sink pad */ + pad = &sd->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + /* Don't call FIMC subdev operation to avoid nested locking */ + if (sd == &fimc->subdev) { + struct flite_frame *ff = &fimc->out_frame; + sink_fmt.format.width = ff->f_width; + sink_fmt.format.height = ff->f_height; + sink_fmt.format.code = fimc->fmt->mbus_code; + } else { + sink_fmt.pad = pad->index; + sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, + &sink_fmt); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + } + /* Retrieve format at the source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + src_fmt.pad = pad->index; + src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + if (src_fmt.format.width != sink_fmt.format.width || + src_fmt.format.height != sink_fmt.format.height || + src_fmt.format.code != sink_fmt.format.code) + return -EPIPE; + } + return 0; +} + +static int fimc_lite_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR]; + struct fimc_pipeline *p = &fimc->pipeline; + int ret; + + if (fimc_lite_active(fimc)) + return -EBUSY; + + media_entity_pipeline_start(&sensor->entity, p->m_pipeline); + + ret = fimc_pipeline_validate(fimc); + if (ret) { + media_entity_pipeline_stop(&sensor->entity); + return ret; + } + + return vb2_streamon(&fimc->vb_queue, type); +} + +static int fimc_lite_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; + int ret; + + ret = vb2_streamoff(&fimc->vb_queue, type); + if (ret == 0) + media_entity_pipeline_stop(&sd->entity); + return ret; +} + +static int fimc_lite_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct fimc_lite *fimc = video_drvdata(file); + int ret; + + reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count); + ret = vb2_reqbufs(&fimc->vb_queue, reqbufs); + if (!ret < 0) + fimc->reqbufs_count = reqbufs->count; + + return ret; +} + +static int fimc_lite_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_querybuf(&fimc->vb_queue, buf); +} + +static int fimc_lite_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_qbuf(&fimc->vb_queue, buf); +} + +static int fimc_lite_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_dqbuf(&fimc->vb_queue, buf, file->f_flags & O_NONBLOCK); +} + +static int fimc_lite_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *create) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_create_bufs(&fimc->vb_queue, create); +} + +static int fimc_lite_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_prepare_buf(&fimc->vb_queue, b); +} + +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +{ + if (a->left < b->left || a->top < b->top) + return 0; + if (a->left + a->width > b->left + b->width) + return 0; + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int fimc_lite_g_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct flite_frame *f = &fimc->out_frame; + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = f->f_width; + sel->r.height = f->f_height; + return 0; + + case V4L2_SEL_TGT_COMPOSE_ACTIVE: + sel->r = f->rect; + return 0; + } + + return -EINVAL; +} + +static int fimc_lite_s_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct flite_frame *f = &fimc->out_frame; + struct v4l2_rect rect = sel->r; + unsigned long flags; + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || + sel->target != V4L2_SEL_TGT_COMPOSE_ACTIVE) + return -EINVAL; + + fimc_lite_try_compose(fimc, &rect); + + if ((sel->flags & V4L2_SEL_FLAG_LE) && + !enclosed_rectangle(&rect, &sel->r)) + return -ERANGE; + + if ((sel->flags & V4L2_SEL_FLAG_GE) && + !enclosed_rectangle(&sel->r, &rect)) + return -ERANGE; + + sel->r = rect; + spin_lock_irqsave(&fimc->slock, flags); + f->rect = rect; + set_bit(ST_FLITE_CONFIG, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + + return 0; +} + +static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { + .vidioc_querycap = fimc_vidioc_querycap_capture, + .vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_lite_g_fmt_mplane, + .vidioc_g_selection = fimc_lite_g_selection, + .vidioc_s_selection = fimc_lite_s_selection, + .vidioc_reqbufs = fimc_lite_reqbufs, + .vidioc_querybuf = fimc_lite_querybuf, + .vidioc_prepare_buf = fimc_lite_prepare_buf, + .vidioc_create_bufs = fimc_lite_create_bufs, + .vidioc_qbuf = fimc_lite_qbuf, + .vidioc_dqbuf = fimc_lite_dqbuf, + .vidioc_streamon = fimc_lite_streamon, + .vidioc_streamoff = fimc_lite_streamoff, +}; + +/* Capture subdev media entity operations */ +static int fimc_lite_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + unsigned int remote_ent_type = media_entity_type(remote->entity); + + if (WARN_ON(fimc == NULL)) + return 0; + + v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x", + __func__, local->entity->name, remote->entity->name, + flags, fimc->source_subdev_grp_id); + + switch (local->index) { + case FIMC_SD_PAD_SINK: + if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) + return -EINVAL; + + if (flags & MEDIA_LNK_FL_ENABLED) { + if (fimc->source_subdev_grp_id != 0) + return -EBUSY; + fimc->source_subdev_grp_id = sd->grp_id; + return 0; + } + + fimc->source_subdev_grp_id = 0; + break; + + case FIMC_SD_PAD_SOURCE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + fimc->out_path = FIMC_IO_NONE; + return 0; + } + if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) + fimc->out_path = FIMC_IO_ISP; + else + fimc->out_path = FIMC_IO_DMA; + break; + + default: + v4l2_err(sd, "Invalid pad index\n"); + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations fimc_lite_subdev_media_ops = { + .link_setup = fimc_lite_link_setup, +}; + +static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct fimc_fmt *fmt; + + fmt = fimc_lite_find_format(NULL, NULL, code->index); + if (!fmt) + return -EINVAL; + code->code = fmt->mbus_code; + return 0; +} + +static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct flite_frame *f = &fimc->out_frame; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *mf; + return 0; + } + mf->colorspace = V4L2_COLORSPACE_JPEG; + + mutex_lock(&fimc->lock); + mf->code = fimc->fmt->mbus_code; + + if (fmt->pad == FLITE_SD_PAD_SINK) { + /* full camera input frame size */ + mf->width = f->f_width; + mf->height = f->f_height; + } else { + /* crop size */ + mf->width = f->rect.width; + mf->height = f->rect.height; + } + mutex_unlock(&fimc->lock); + return 0; +} + +static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct flite_frame *sink = &fimc->inp_frame; + const struct fimc_fmt *ffmt; + + v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d", + fmt->pad, mf->code, mf->width, mf->height); + + mf->colorspace = V4L2_COLORSPACE_JPEG; + mutex_lock(&fimc->lock); + + if ((fimc->out_path == FIMC_IO_ISP && sd->entity.stream_count > 0) || + (fimc->out_path == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) { + mutex_unlock(&fimc->lock); + return -EBUSY; + } + + ffmt = fimc_lite_try_format(fimc, &mf->width, &mf->height, + &mf->code, NULL, fmt->pad); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + mutex_unlock(&fimc->lock); + return 0; + } + + if (fmt->pad == FLITE_SD_PAD_SINK) { + sink->f_width = mf->width; + sink->f_height = mf->height; + fimc->fmt = ffmt; + /* Set sink crop rectangle */ + sink->rect.width = mf->width; + sink->rect.height = mf->height; + sink->rect.left = 0; + sink->rect.top = 0; + /* Reset source crop rectangle */ + fimc->out_frame.rect = sink->rect; + } else { + /* Allow changing format only on sink pad */ + mf->code = fimc->fmt->mbus_code; + mf->width = sink->rect.width; + mf->height = sink->rect.height; + } + + mutex_unlock(&fimc->lock); + return 0; +} + +static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct flite_frame *f = &fimc->inp_frame; + + if ((sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL && + sel->target != V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS) || + sel->pad != FLITE_SD_PAD_SINK) + return -EINVAL; + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); + return 0; + } + + mutex_lock(&fimc->lock); + if (sel->target == V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL) { + sel->r = f->rect; + } else { + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = f->f_width; + sel->r.height = f->f_height; + } + mutex_unlock(&fimc->lock); + + v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d", + __func__, f->rect.left, f->rect.top, f->rect.width, + f->rect.height, f->f_width, f->f_height); + + return 0; +} + +static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct flite_frame *f = &fimc->inp_frame; + int ret = 0; + + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != FLITE_SD_PAD_SINK) + return -EINVAL; + + mutex_lock(&fimc->lock); + fimc_lite_try_crop(fimc, &sel->r); + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r; + } else { + unsigned long flags; + spin_lock_irqsave(&fimc->slock, flags); + f->rect = sel->r; + /* Same crop rectangle on the source pad */ + fimc->out_frame.rect = sel->r; + set_bit(ST_FLITE_CONFIG, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + } + mutex_unlock(&fimc->lock); + + v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d", + __func__, f->rect.left, f->rect.top, f->rect.width, + f->rect.height, f->f_width, f->f_height); + + return ret; +} + +static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + + if (fimc->out_path == FIMC_IO_DMA) + return -ENOIOCTLCMD; + + /* TODO: */ + + return 0; +} + +static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + + if (fimc->out_path == FIMC_IO_DMA) + return -ENOIOCTLCMD; + + /* TODO: */ + + return 0; +} + +static int fimc_lite_log_status(struct v4l2_subdev *sd) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + + flite_hw_dump_regs(fimc, __func__); + return 0; +} + +static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct vb2_queue *q = &fimc->vb_queue; + struct video_device *vfd; + int ret; + + fimc->fmt = &fimc_lite_formats[0]; + fimc->out_path = FIMC_IO_DMA; + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(sd->v4l2_dev, "Failed to allocate video device\n"); + return -ENOMEM; + } + + snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture", + fimc->index); + + vfd->fops = &fimc_lite_fops; + vfd->ioctl_ops = &fimc_lite_ioctl_ops; + vfd->v4l2_dev = sd->v4l2_dev; + vfd->minor = -1; + vfd->release = video_device_release; + vfd->lock = &fimc->lock; + fimc->vfd = vfd; + fimc->ref_count = 0; + fimc->reqbufs_count = 0; + + INIT_LIST_HEAD(&fimc->pending_buf_q); + INIT_LIST_HEAD(&fimc->active_buf_q); + + memset(q, 0, sizeof(*q)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->ops = &fimc_lite_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct flite_buffer); + q->drv_priv = fimc; + + vb2_queue_init(q); + + fimc->vd_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0); + if (ret) + goto err; + + video_set_drvdata(vfd, fimc); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + if (ret) + goto err_vd; + + v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n", + vfd->name, video_device_node_name(vfd)); + return 0; + + err_vd: + media_entity_cleanup(&vfd->entity); + err: + video_device_release(vfd); + return ret; +} + +static void fimc_lite_subdev_unregistered(struct v4l2_subdev *sd) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + + if (fimc == NULL) + return; + + if (fimc->vfd) { + video_unregister_device(fimc->vfd); + media_entity_cleanup(&fimc->vfd->entity); + fimc->vfd = NULL; + } +} + +static const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = { + .registered = fimc_lite_subdev_registered, + .unregistered = fimc_lite_subdev_unregistered, +}; + +static const struct v4l2_subdev_pad_ops fimc_lite_subdev_pad_ops = { + .enum_mbus_code = fimc_lite_subdev_enum_mbus_code, + .get_selection = fimc_lite_subdev_get_selection, + .set_selection = fimc_lite_subdev_set_selection, + .get_fmt = fimc_lite_subdev_get_fmt, + .set_fmt = fimc_lite_subdev_set_fmt, +}; + +static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = { + .s_stream = fimc_lite_subdev_s_stream, +}; + +static const struct v4l2_subdev_core_ops fimc_lite_core_ops = { + .s_power = fimc_lite_subdev_s_power, + .log_status = fimc_lite_log_status, +}; + +static struct v4l2_subdev_ops fimc_lite_subdev_ops = { + .core = &fimc_lite_core_ops, + .video = &fimc_lite_subdev_video_ops, + .pad = &fimc_lite_subdev_pad_ops, +}; + +static int fimc_lite_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct fimc_lite *fimc = container_of(ctrl->handler, struct fimc_lite, + ctrl_handler); + set_bit(ST_FLITE_CONFIG, &fimc->state); + return 0; +} + +static const struct v4l2_ctrl_ops fimc_lite_ctrl_ops = { + .s_ctrl = fimc_lite_s_ctrl, +}; + +static const struct v4l2_ctrl_config fimc_lite_ctrl = { + .ops = &fimc_lite_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER | 0x1001, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Test Pattern 640x480", +}; + +static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) +{ + struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler; + struct v4l2_subdev *sd = &fimc->subdev; + int ret; + + v4l2_subdev_init(sd, &fimc_lite_subdev_ops); + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); + + fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->subdev_pads, 0); + if (ret) + return ret; + + v4l2_ctrl_handler_init(handler, 1); + fimc->test_pattern = v4l2_ctrl_new_custom(handler, &fimc_lite_ctrl, + NULL); + if (handler->error) { + media_entity_cleanup(&sd->entity); + return handler->error; + } + + sd->ctrl_handler = handler; + sd->internal_ops = &fimc_lite_subdev_internal_ops; + sd->entity.ops = &fimc_lite_subdev_media_ops; + v4l2_set_subdevdata(sd, fimc); + + return 0; +} + +static void fimc_lite_unregister_capture_subdev(struct fimc_lite *fimc) +{ + struct v4l2_subdev *sd = &fimc->subdev; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&fimc->ctrl_handler); + v4l2_set_subdevdata(sd, NULL); +} + +static void fimc_lite_clk_put(struct fimc_lite *fimc) +{ + if (IS_ERR_OR_NULL(fimc->clock)) + return; + + clk_unprepare(fimc->clock); + clk_put(fimc->clock); + fimc->clock = NULL; +} + +static int fimc_lite_clk_get(struct fimc_lite *fimc) +{ + int ret; + + fimc->clock = clk_get(&fimc->pdev->dev, FLITE_CLK_NAME); + if (IS_ERR(fimc->clock)) + return PTR_ERR(fimc->clock); + + ret = clk_prepare(fimc->clock); + if (ret < 0) { + clk_put(fimc->clock); + fimc->clock = NULL; + } + return ret; +} + +static int __devinit fimc_lite_probe(struct platform_device *pdev) +{ + struct flite_drvdata *drv_data = fimc_lite_get_drvdata(pdev); + struct fimc_lite *fimc; + struct resource *res; + int ret; + + fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); + if (!fimc) + return -ENOMEM; + + fimc->index = pdev->id; + fimc->variant = drv_data->variant[fimc->index]; + fimc->pdev = pdev; + + init_waitqueue_head(&fimc->irq_queue); + spin_lock_init(&fimc->slock); + mutex_init(&fimc->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fimc->regs = devm_request_and_ioremap(&pdev->dev, res); + if (fimc->regs == NULL) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + return -ENXIO; + } + + ret = fimc_lite_clk_get(fimc); + if (ret) + return ret; + + ret = devm_request_irq(&pdev->dev, res->start, flite_irq_handler, + 0, dev_name(&pdev->dev), fimc); + if (ret) { + dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret); + goto err_clk; + } + + /* The video node will be created within the subdev's registered() op */ + ret = fimc_lite_create_capture_subdev(fimc); + if (ret) + goto err_clk; + + platform_set_drvdata(pdev, fimc); + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_sd; + + fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(fimc->alloc_ctx)) { + ret = PTR_ERR(fimc->alloc_ctx); + goto err_pm; + } + pm_runtime_put(&pdev->dev); + + dev_dbg(&pdev->dev, "FIMC-LITE.%d registered successfully\n", + fimc->index); + return 0; +err_pm: + pm_runtime_put(&pdev->dev); +err_sd: + fimc_lite_unregister_capture_subdev(fimc); +err_clk: + fimc_lite_clk_put(fimc); + return ret; +} + +static int fimc_lite_runtime_resume(struct device *dev) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + + clk_enable(fimc->clock); + return 0; +} + +static int fimc_lite_runtime_suspend(struct device *dev) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + + clk_disable(fimc->clock); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fimc_lite_resume(struct device *dev) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + struct flite_buffer *buf; + unsigned long flags; + int i; + + spin_lock_irqsave(&fimc->slock, flags); + if (!test_and_clear_bit(ST_LPM, &fimc->state) || + !test_bit(ST_FLITE_IN_USE, &fimc->state)) { + spin_unlock_irqrestore(&fimc->slock, flags); + return 0; + } + flite_hw_reset(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + if (!test_and_clear_bit(ST_FLITE_SUSPENDED, &fimc->state)) + return 0; + + INIT_LIST_HEAD(&fimc->active_buf_q); + fimc_pipeline_initialize(&fimc->pipeline, &fimc->vfd->entity, false); + fimc_lite_hw_init(fimc); + clear_bit(ST_FLITE_SUSPENDED, &fimc->state); + + for (i = 0; i < fimc->reqbufs_count; i++) { + if (list_empty(&fimc->pending_buf_q)) + break; + buf = fimc_lite_pending_queue_pop(fimc); + buffer_queue(&buf->vb); + } + return 0; +} + +static int fimc_lite_suspend(struct device *dev) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + bool suspend = test_bit(ST_FLITE_IN_USE, &fimc->state); + int ret; + + if (test_and_set_bit(ST_LPM, &fimc->state)) + return 0; + + ret = fimc_lite_stop_capture(fimc, suspend); + if (ret) + return ret; + + return fimc_pipeline_shutdown(&fimc->pipeline); +} +#endif /* CONFIG_PM_SLEEP */ + +static int __devexit fimc_lite_remove(struct platform_device *pdev) +{ + struct fimc_lite *fimc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + fimc_lite_unregister_capture_subdev(fimc); + vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + fimc_lite_clk_put(fimc); + + dev_info(dev, "Driver unloaded\n"); + return 0; +} + +static struct flite_variant fimc_lite0_variant_exynos4 = { + .max_width = 8192, + .max_height = 8192, + .out_width_align = 8, + .win_hor_offs_align = 2, + .out_hor_offs_align = 8, +}; + +/* EXYNOS4212, EXYNOS4412 */ +static struct flite_drvdata fimc_lite_drvdata_exynos4 = { + .variant = { + [0] = &fimc_lite0_variant_exynos4, + [1] = &fimc_lite0_variant_exynos4, + }, +}; + +static struct platform_device_id fimc_lite_driver_ids[] = { + { + .name = "exynos-fimc-lite", + .driver_data = (unsigned long)&fimc_lite_drvdata_exynos4, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids); + +static const struct dev_pm_ops fimc_lite_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) + SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, + NULL) +}; + +static struct platform_driver fimc_lite_driver = { + .probe = fimc_lite_probe, + .remove = __devexit_p(fimc_lite_remove), + .id_table = fimc_lite_driver_ids, + .driver = { + .name = FIMC_LITE_DRV_NAME, + .owner = THIS_MODULE, + .pm = &fimc_lite_pm_ops, + } +}; +module_platform_driver(fimc_lite_driver); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME); diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 212474130dfb..6753c45631b8 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -66,6 +66,9 @@ void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me) case CSIS_GROUP_ID: p->subdevs[IDX_CSIS] = sd; break; + case FLITE_GROUP_ID: + p->subdevs[IDX_FLITE] = sd; + break; case FIMC_GROUP_ID: /* No need to control FIMC subdev through subdev ops */ break; @@ -336,6 +339,7 @@ static int fimc_register_callback(struct device *dev, void *p) if (!fimc || !fimc->pdev) return 0; + if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS) return 0; @@ -351,6 +355,31 @@ static int fimc_register_callback(struct device *dev, void *p) return ret; } +static int fimc_lite_register_callback(struct device *dev, void *p) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + struct v4l2_subdev *sd = &fimc->subdev; + struct fimc_md *fmd = p; + int ret; + + if (fimc == NULL) + return 0; + + if (fimc->index >= FIMC_LITE_MAX_DEVS) + return 0; + + fmd->fimc_lite[fimc->index] = fimc; + sd->grp_id = FLITE_GROUP_ID; + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (ret) { + v4l2_err(&fmd->v4l2_dev, + "Failed to register FIMC-LITE.%d (%d)\n", + fimc->index, ret); + } + return ret; +} + static int csis_register_callback(struct device *dev, void *p) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -396,6 +425,15 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd) fimc_register_callback); if (ret) return ret; + + driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type); + if (driver && try_module_get(driver->owner)) { + ret = driver_for_each_device(driver, NULL, fmd, + fimc_lite_register_callback); + if (ret) + return ret; + module_put(driver->owner); + } /* * Check if there is any sensor on the MIPI-CSI2 bus and * if not skip the s5p-csis module loading. @@ -433,6 +471,12 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev); fmd->fimc[i] = NULL; } + for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { + if (fmd->fimc_lite[i] == NULL) + continue; + v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev); + fmd->fimc_lite[i] = NULL; + } for (i = 0; i < CSIS_MAX_ENTITIES; i++) { if (fmd->csis[i].sd == NULL) continue; @@ -456,28 +500,28 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) * @pad: the source entity pad index * @fimc_id: index of the fimc device for which link should be enabled */ -static int __fimc_md_create_fimc_links(struct fimc_md *fmd, - struct media_entity *source, - struct v4l2_subdev *sensor, - int pad, int fimc_id) +static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, + struct media_entity *source, + struct v4l2_subdev *sensor, + int pad, int fimc_id) { struct fimc_sensor_info *s_info; struct media_entity *sink; - unsigned int flags; + unsigned int flags = 0; int ret, i; for (i = 0; i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) - break; + continue; /* * Some FIMC variants are not fitted with camera capture * interface. Skip creating a link from sensor for those. */ - if (sensor->grp_id == SENSOR_GROUP_ID && - !fmd->fimc[i]->variant->has_cam_if) + if (!fmd->fimc[i]->variant->has_cam_if) continue; flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; + sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_entity_create_link(source, pad, sink, FIMC_SD_PAD_SINK, flags); @@ -493,7 +537,7 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd, v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", source->name, flags ? '=' : '-', sink->name); - if (flags == 0) + if (flags == 0 || sensor == NULL) continue; s_info = v4l2_get_subdev_hostdata(sensor); if (!WARN_ON(s_info == NULL)) { @@ -503,9 +547,55 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd, spin_unlock_irqrestore(&fmd->slock, irq_flags); } } + + for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { + if (!fmd->fimc_lite[i]) + continue; + + flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; + + sink = &fmd->fimc_lite[i]->subdev.entity; + ret = media_entity_create_link(source, pad, sink, + FLITE_SD_PAD_SINK, flags); + if (ret) + return ret; + + /* Notify FIMC-LITE subdev entity */ + ret = media_entity_call(sink, link_setup, &sink->pads[0], + &source->pads[pad], flags); + if (ret) + break; + + v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", + source->name, flags ? '=' : '-', sink->name); + } return 0; } +/* Create links from FIMC-LITE source pads to other entities */ +static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) +{ + struct media_entity *source, *sink; + unsigned int flags = MEDIA_LNK_FL_ENABLED; + int i, ret; + + for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { + struct fimc_lite *fimc = fmd->fimc_lite[i]; + if (fimc == NULL) + continue; + source = &fimc->subdev.entity; + sink = &fimc->vfd->entity; + /* FIMC-LITE's subdev and video node */ + ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + sink, 0, flags); + if (ret) + break; + /* TODO: create links to other entities */ + } + + return ret; +} + /** * fimc_md_create_links - create default links between registered entities * @@ -562,8 +652,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]", sensor->entity.name, csis->entity.name); - source = &csis->entity; - pad = CSIS_PAD_SOURCE; + source = NULL; break; case FIMC_ITU_601...FIMC_ITU_656: @@ -579,9 +668,21 @@ static int fimc_md_create_links(struct fimc_md *fmd) if (source == NULL) continue; - ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad, - fimc_id++); + ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor, + pad, fimc_id++); + } + + fimc_id = 0; + for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) { + if (fmd->csis[i].sd == NULL) + continue; + source = &fmd->csis[i].sd->entity; + pad = CSIS_PAD_SOURCE; + + ret = __fimc_md_create_fimc_sink_links(fmd, source, NULL, + pad, fimc_id++); } + /* Create immutable links between each FIMC's subdev and video node */ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; for (i = 0; i < FIMC_MAX_DEVS; i++) { @@ -595,7 +696,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) break; } - return ret; + return __fimc_md_create_flite_source_links(fmd); } /* @@ -703,9 +804,10 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) static int fimc_md_link_notify(struct media_pad *source, struct media_pad *sink, u32 flags) { + struct fimc_lite *fimc_lite = NULL; + struct fimc_dev *fimc = NULL; struct fimc_pipeline *pipeline; struct v4l2_subdev *sd; - struct fimc_dev *fimc; int ret = 0; if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) @@ -714,6 +816,10 @@ static int fimc_md_link_notify(struct media_pad *source, sd = media_entity_to_v4l2_subdev(sink->entity); switch (sd->grp_id) { + case FLITE_GROUP_ID: + fimc_lite = v4l2_get_subdevdata(sd); + pipeline = &fimc_lite->pipeline; + break; case FIMC_GROUP_ID: fimc = v4l2_get_subdevdata(sd); pipeline = &fimc->pipeline; @@ -739,15 +845,23 @@ static int fimc_md_link_notify(struct media_pad *source, * pipeline is already in use, i.e. its video node is opened. * Recreate the controls destroyed during the link deactivation. */ - mutex_lock(&fimc->lock); - if (fimc->vid_cap.refcnt > 0) { + if (fimc) { + mutex_lock(&fimc->lock); + if (fimc->vid_cap.refcnt > 0) { ret = __fimc_pipeline_initialize(pipeline, source->entity, true); if (!ret) ret = fimc_capture_ctrls_create(fimc); + } + mutex_unlock(&fimc->lock); + } else { + mutex_lock(&fimc_lite->lock); + if (fimc_lite->ref_count > 0) { + ret = __fimc_pipeline_initialize(pipeline, + source->entity, true); + } + mutex_unlock(&fimc_lite->lock); } - mutex_unlock(&fimc->lock); - return ret ? -EPIPE : ret; } diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h index c5ac3e64b0d9..3524c19ae801 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h @@ -18,13 +18,15 @@ #include #include "fimc-core.h" +#include "fimc-lite.h" #include "mipi-csis.h" -/* Group IDs of sensor, MIPI CSIS and the writeback subdevs. */ +/* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */ #define SENSOR_GROUP_ID (1 << 8) #define CSIS_GROUP_ID (1 << 9) #define WRITEBACK_GROUP_ID (1 << 10) #define FIMC_GROUP_ID (1 << 11) +#define FLITE_GROUP_ID (1 << 12) #define FIMC_MAX_SENSORS 8 #define FIMC_MAX_CAMCLKS 2 @@ -74,6 +76,7 @@ struct fimc_md { struct fimc_sensor_info sensor[FIMC_MAX_SENSORS]; int num_sensors; struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS]; + struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS]; struct fimc_dev *fimc[FIMC_MAX_DEVS]; struct media_device media_dev; struct v4l2_device v4l2_dev; -- cgit v1.2.3 From 0c9204d3427015a22fa90b865b6317fed337810b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 25 Apr 2012 06:55:42 -0300 Subject: [media] s5p-fimc: Update copyright notices Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 4 ++-- drivers/media/video/s5p-fimc/fimc-core.c | 4 ++-- drivers/media/video/s5p-fimc/fimc-core.h | 2 +- drivers/media/video/s5p-fimc/fimc-mdevice.h | 2 +- drivers/media/video/s5p-fimc/fimc-reg.c | 5 ++--- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 15b74083a066..6d079ac7089e 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1,8 +1,8 @@ /* * Samsung S5P/EXYNOS4 SoC series camera interface (camera capture) driver * - * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. - * Author: Sylwester Nawrocki, + * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 2e319c71de02..bad9ad018baa 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1,8 +1,8 @@ /* * Samsung S5P/EXYNOS4 SoC series FIMC (CAMIF) driver * - * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. - * Contact: Sylwester Nawrocki, + * Copyright (C) 2010-2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index c67d485ecce6..3ca8c2682e0e 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. + * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h index 3524c19ae801..3b8a3492a176 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 382981788c43..78c95d7ddde7 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -1,9 +1,8 @@ /* * Register interface file for Samsung Camera Interface (FIMC) driver * - * Copyright (c) 2010 Samsung Electronics - * - * Sylwester Nawrocki, s.nawrocki@samsung.com + * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as -- cgit v1.2.3 From 9448ab7dec30489d5318f786d0faee08354ef3d5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 2 Apr 2012 06:41:22 -0300 Subject: [media] s5p-fimc: Add color effect control Add support for V4L2_CID_COLORFX control at the mem-to-mem and capture video nodes. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 11 +-- drivers/media/video/s5p-fimc/fimc-core.c | 127 ++++++++++++++++++++++------ drivers/media/video/s5p-fimc/fimc-core.h | 38 ++++++--- drivers/media/video/s5p-fimc/fimc-m2m.c | 4 +- drivers/media/video/s5p-fimc/fimc-reg.c | 4 +- drivers/media/video/s5p-fimc/fimc-reg.h | 2 +- 6 files changed, 136 insertions(+), 50 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 6d079ac7089e..12415e7383d4 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -62,7 +62,7 @@ static int fimc_capture_hw_init(struct fimc_dev *fimc) fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); - fimc_hw_set_effect(ctx, false); + fimc_hw_set_effect(ctx); fimc_hw_set_output_path(ctx); fimc_hw_set_out_dma(ctx); if (fimc->variant->has_alpha) @@ -164,6 +164,7 @@ static int fimc_capture_config_update(struct fimc_ctx *ctx) fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); + fimc_hw_set_effect(ctx); fimc_prepare_dma_offset(ctx, &ctx->d_frame); fimc_hw_set_out_dma(ctx); if (fimc->variant->has_alpha) @@ -462,14 +463,14 @@ int fimc_capture_ctrls_create(struct fimc_dev *fimc) if (WARN_ON(vid_cap->ctx == NULL)) return -ENXIO; - if (vid_cap->ctx->ctrls_rdy) + if (vid_cap->ctx->ctrls.ready) return 0; ret = fimc_ctrls_create(vid_cap->ctx); - if (ret || vid_cap->user_subdev_api) + if (ret || vid_cap->user_subdev_api || !vid_cap->ctx->ctrls.ready) return ret; - return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrl_handler, + return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler, fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler); } @@ -1588,7 +1589,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", vfd->name, video_device_node_name(vfd)); - vfd->ctrl_handler = &ctx->ctrl_handler; + vfd->ctrl_handler = &ctx->ctrls.handler; return 0; err_vd: diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index bad9ad018baa..fedcd561ba27 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -463,11 +463,53 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); } +int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx) +{ + struct fimc_effect *effect = &ctx->effect; + + switch (colorfx) { + case V4L2_COLORFX_NONE: + effect->type = FIMC_REG_CIIMGEFF_FIN_BYPASS; + break; + case V4L2_COLORFX_BW: + effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY; + effect->pat_cb = 128; + effect->pat_cr = 128; + break; + case V4L2_COLORFX_SEPIA: + effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY; + effect->pat_cb = 115; + effect->pat_cr = 145; + break; + case V4L2_COLORFX_NEGATIVE: + effect->type = FIMC_REG_CIIMGEFF_FIN_NEGATIVE; + break; + case V4L2_COLORFX_EMBOSS: + effect->type = FIMC_REG_CIIMGEFF_FIN_EMBOSSING; + break; + case V4L2_COLORFX_ART_FREEZE: + effect->type = FIMC_REG_CIIMGEFF_FIN_ARTFREEZE; + break; + case V4L2_COLORFX_SILHOUETTE: + effect->type = FIMC_REG_CIIMGEFF_FIN_SILHOUETTE; + break; + case V4L2_COLORFX_SET_CBCR: + effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY; + effect->pat_cb = ctx->ctrls.colorfx_cbcr->val >> 8; + effect->pat_cr = ctx->ctrls.colorfx_cbcr->val & 0xff; + break; + default: + return -EINVAL; + } + + return 0; +} + /* * V4L2 controls handling */ #define ctrl_to_ctx(__ctrl) \ - container_of((__ctrl)->handler, struct fimc_ctx, ctrl_handler) + container_of((__ctrl)->handler, struct fimc_ctx, ctrls.handler) static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) { @@ -507,7 +549,14 @@ static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) case V4L2_CID_ALPHA_COMPONENT: ctx->d_frame.alpha = ctrl->val; break; + + case V4L2_CID_COLORFX: + ret = fimc_set_color_effect(ctx, ctrl->val); + if (ret) + return ret; + break; } + ctx->state |= FIMC_PARAMS; set_bit(ST_CAPT_APPLY_CFG, &fimc->state); return 0; @@ -534,69 +583,91 @@ int fimc_ctrls_create(struct fimc_ctx *ctx) { struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); + struct fimc_ctrls *ctrls = &ctx->ctrls; + struct v4l2_ctrl_handler *handler = &ctrls->handler; - if (ctx->ctrls_rdy) + if (ctx->ctrls.ready) return 0; - v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4); - ctx->ctrl_rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, + v4l2_ctrl_handler_init(handler, 6); + + ctrls->rotate = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0); - ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, + ctrls->hflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, + ctrls->vflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + if (variant->has_alpha) - ctx->ctrl_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler, - &fimc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, - 0, max_alpha, 1, 0); + ctrls->alpha = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, + 0, max_alpha, 1, 0); else - ctx->ctrl_alpha = NULL; + ctrls->alpha = NULL; + + ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, &fimc_ctrl_ops, + V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR, + ~0x983f, V4L2_COLORFX_NONE); + + ctrls->colorfx_cbcr = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, + V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0); - ctx->ctrls_rdy = ctx->ctrl_handler.error == 0; + ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS; - return ctx->ctrl_handler.error; + if (!handler->error) { + v4l2_ctrl_cluster(3, &ctrls->colorfx); + ctrls->ready = true; + } + + return handler->error; } void fimc_ctrls_delete(struct fimc_ctx *ctx) { - if (ctx->ctrls_rdy) { - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - ctx->ctrls_rdy = false; - ctx->ctrl_alpha = NULL; + struct fimc_ctrls *ctrls = &ctx->ctrls; + + if (ctrls->ready) { + v4l2_ctrl_handler_free(&ctrls->handler); + ctrls->ready = false; + ctrls->alpha = NULL; } } void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active) { unsigned int has_alpha = ctx->d_frame.fmt->flags & FMT_HAS_ALPHA; + struct fimc_ctrls *ctrls = &ctx->ctrls; - if (!ctx->ctrls_rdy) + if (!ctrls->ready) return; - mutex_lock(&ctx->ctrl_handler.lock); - v4l2_ctrl_activate(ctx->ctrl_rotate, active); - v4l2_ctrl_activate(ctx->ctrl_hflip, active); - v4l2_ctrl_activate(ctx->ctrl_vflip, active); - if (ctx->ctrl_alpha) - v4l2_ctrl_activate(ctx->ctrl_alpha, active && has_alpha); + mutex_lock(&ctrls->handler.lock); + v4l2_ctrl_activate(ctrls->rotate, active); + v4l2_ctrl_activate(ctrls->hflip, active); + v4l2_ctrl_activate(ctrls->vflip, active); + v4l2_ctrl_activate(ctrls->colorfx, active); + if (ctrls->alpha) + v4l2_ctrl_activate(ctrls->alpha, active && has_alpha); if (active) { - ctx->rotation = ctx->ctrl_rotate->val; - ctx->hflip = ctx->ctrl_hflip->val; - ctx->vflip = ctx->ctrl_vflip->val; + fimc_set_color_effect(ctx, ctrls->colorfx->cur.val); + ctx->rotation = ctrls->rotate->val; + ctx->hflip = ctrls->hflip->val; + ctx->vflip = ctrls->vflip->val; } else { + ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS; ctx->rotation = 0; ctx->hflip = 0; ctx->vflip = 0; } - mutex_unlock(&ctx->ctrl_handler.lock); + mutex_unlock(&ctrls->handler.lock); } /* Update maximum value of the alpha color control */ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) { struct fimc_dev *fimc = ctx->fimc_dev; - struct v4l2_ctrl *ctrl = ctx->ctrl_alpha; + struct v4l2_ctrl *ctrl = ctx->ctrls.alpha; if (ctrl == NULL || !fimc->variant->has_alpha) return; diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 3ca8c2682e0e..794c496d6811 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -445,6 +445,30 @@ struct fimc_dev { struct fimc_pipeline pipeline; }; +/** + * struct fimc_ctrls - v4l2 controls structure + * @handler: the control handler + * @colorfx: image effect control + * @colorfx_cbcr: Cb/Cr coefficients control + * @rotate: image rotation control + * @hflip: horizontal flip control + * @vflip: vertical flip control + * @alpha: RGB alpha control + * @ready: true if @handler is initialized + */ +struct fimc_ctrls { + struct v4l2_ctrl_handler handler; + struct { + struct v4l2_ctrl *colorfx; + struct v4l2_ctrl *colorfx_cbcr; + }; + struct v4l2_ctrl *rotate; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *alpha; + bool ready; +}; + /** * fimc_ctx - the device context data * @s_frame: source frame properties @@ -465,12 +489,7 @@ struct fimc_dev { * @fimc_dev: the FIMC device this context applies to * @m2m_ctx: memory-to-memory device context * @fh: v4l2 file handle - * @ctrl_handler: v4l2 controls handler - * @ctrl_rotate image rotation control - * @ctrl_hflip horizontal flip control - * @ctrl_vflip vertical flip control - * @ctrl_alpha RGB alpha control - * @ctrls_rdy: true if the control handler is initialized + * @ctrls: v4l2 controls structure */ struct fimc_ctx { struct fimc_frame s_frame; @@ -491,12 +510,7 @@ struct fimc_ctx { struct fimc_dev *fimc_dev; struct v4l2_m2m_ctx *m2m_ctx; struct v4l2_fh fh; - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_ctrl *ctrl_rotate; - struct v4l2_ctrl *ctrl_hflip; - struct v4l2_ctrl *ctrl_vflip; - struct v4l2_ctrl *ctrl_alpha; - bool ctrls_rdy; + struct fimc_ctrls ctrls; }; #define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh) diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c index 3de22b0db66f..4c58e0570962 100644 --- a/drivers/media/video/s5p-fimc/fimc-m2m.c +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -150,7 +150,7 @@ static void fimc_device_run(void *priv) fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); - fimc_hw_set_effect(ctx, false); + fimc_hw_set_effect(ctx); fimc_hw_set_out_dma(ctx); if (fimc->variant->has_alpha) fimc_hw_set_rgb_alpha(ctx); @@ -669,7 +669,7 @@ static int fimc_m2m_open(struct file *file) goto error_fh; /* Use separate control handler per file handle */ - ctx->fh.ctrl_handler = &ctx->ctrl_handler; + ctx->fh.ctrl_handler = &ctx->ctrls.handler; file->private_data = &ctx->fh; v4l2_fh_add(&ctx->fh); diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 78c95d7ddde7..1fc4ce8446f5 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -368,13 +368,13 @@ void fimc_hw_en_capture(struct fimc_ctx *ctx) writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); } -void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active) +void fimc_hw_set_effect(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; struct fimc_effect *effect = &ctx->effect; u32 cfg = 0; - if (active) { + if (effect->type != FIMC_REG_CIIMGEFF_FIN_BYPASS) { cfg |= FIMC_REG_CIIMGEFF_IE_SC_AFTER | FIMC_REG_CIIMGEFF_IE_ENABLE; cfg |= effect->type; diff --git a/drivers/media/video/s5p-fimc/fimc-reg.h b/drivers/media/video/s5p-fimc/fimc-reg.h index 1472880b94ff..579ac8ac03de 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.h +++ b/drivers/media/video/s5p-fimc/fimc-reg.h @@ -288,7 +288,7 @@ void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); void fimc_hw_set_prescaler(struct fimc_ctx *ctx); void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); void fimc_hw_en_capture(struct fimc_ctx *ctx); -void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active); +void fimc_hw_set_effect(struct fimc_ctx *ctx); void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); void fimc_hw_set_in_dma(struct fimc_ctx *ctx); void fimc_hw_set_input_path(struct fimc_ctx *ctx); -- cgit v1.2.3 From fed07f848888267d5991b9446e103f35aaeb58d8 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 29 Apr 2012 17:46:03 -0300 Subject: [media] s5p-fimc: Use selection API in place of crop operations Replace deprecated crop operations with the selection API. Original crop ioctls are supported through a compatibility layer at the v4l2 core. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kuyngmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 176 +++++++++++++++++----------- drivers/media/video/s5p-fimc/fimc-core.h | 2 +- 2 files changed, 109 insertions(+), 69 deletions(-) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 12415e7383d4..354574591908 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -611,8 +611,13 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, } /* Apply the scaler and the output DMA constraints */ max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w; - min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize; - min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize; + if (ctx->state & FIMC_COMPOSE) { + min_w = dst->offs_h + dst->width; + min_h = dst->offs_v + dst->height; + } else { + min_w = var->min_out_pixsize; + min_h = var->min_out_pixsize; + } if (var->min_vsize_align == 1 && !rotation) align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1; @@ -630,8 +635,9 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, return ffmt; } -static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, - int pad) +static void fimc_capture_try_selection(struct fimc_ctx *ctx, + struct v4l2_rect *r, + int target) { bool rotate = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; @@ -649,7 +655,7 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, r->left = r->top = 0; return; } - if (pad == FIMC_SD_PAD_SOURCE) { + if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { if (ctx->rotation != 90 && ctx->rotation != 270) align_h = 1; max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3)); @@ -663,8 +669,7 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, max_sc_h = max_sc_v = 1; } /* - * For the crop rectangle at source pad the following constraints - * must be met: + * For the compose rectangle the following constraints must be met: * - it must fit in the sink pad format rectangle (f_width/f_height); * - maximum downscaling ratio is 64; * - maximum crop size depends if the rotator is used or not; @@ -676,7 +681,8 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, rotate ? pl->out_rot_en_w : pl->out_rot_dis_w, rotate ? sink->f_height : sink->f_width); max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height); - if (pad == FIMC_SD_PAD_SOURCE) { + + if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { min_w = min_t(u32, max_w, sink->f_width / max_sc_h); min_h = min_t(u32, max_h, sink->f_height / max_sc_v); if (rotate) { @@ -687,13 +693,13 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1, &r->height, min_h, max_h, align_h, align_sz); - /* Adjust left/top if cropping rectangle is out of bounds */ + /* Adjust left/top if crop/compose rectangle is out of bounds */ r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width); r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height); r->left = round_down(r->left, var->hor_offs_align); - dbg("pad%d: (%d,%d)/%dx%d, sink fmt: %dx%d", - pad, r->left, r->top, r->width, r->height, + dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d", + target, r->left, r->top, r->width, r->height, sink->f_width, sink->f_height); } @@ -925,7 +931,7 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) set_frame_bounds(ff, pix->width, pix->height); /* Reset the composition rectangle if not yet configured */ - if (!(ctx->state & FIMC_DST_CROP)) + if (!(ctx->state & FIMC_COMPOSE)) set_frame_crop(ff, 0, 0, pix->width, pix->height); fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color)); @@ -1175,29 +1181,18 @@ static int fimc_cap_s_selection(struct file *file, void *fh, struct v4l2_rect rect = s->r; struct fimc_frame *f; unsigned long flags; - unsigned int pad; if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; - switch (s->target) { - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + if (s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE) f = &ctx->d_frame; - pad = FIMC_SD_PAD_SOURCE; - break; - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_ACTIVE: + else if (s->target == V4L2_SEL_TGT_CROP_ACTIVE) f = &ctx->s_frame; - pad = FIMC_SD_PAD_SINK; - break; - default: + else return -EINVAL; - } - fimc_capture_try_crop(ctx, &rect, pad); + fimc_capture_try_selection(ctx, &rect, s->target); if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&rect, &s->r)) @@ -1409,77 +1404,122 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, ff->fmt = ffmt; /* Reset the crop rectangle if required. */ - if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_DST_CROP))) + if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE))) set_frame_crop(ff, 0, 0, mf->width, mf->height); if (fmt->pad == FIMC_SD_PAD_SINK) - ctx->state &= ~FIMC_DST_CROP; + ctx->state &= ~FIMC_COMPOSE; mutex_unlock(&fimc->lock); return 0; } -static int fimc_subdev_get_crop(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int fimc_subdev_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); struct fimc_ctx *ctx = fimc->vid_cap.ctx; - struct v4l2_rect *r = &crop->rect; - struct fimc_frame *ff; + struct fimc_frame *f = &ctx->s_frame; + struct v4l2_rect *r = &sel->r; + struct v4l2_rect *try_sel; + + if (sel->pad != FIMC_SD_PAD_SINK) + return -EINVAL; + + mutex_lock(&fimc->lock); - if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { - crop->rect = *v4l2_subdev_get_try_crop(fh, crop->pad); + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + f = &ctx->d_frame; + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + r->width = f->o_width; + r->height = f->o_height; + r->left = 0; + r->top = 0; + mutex_unlock(&fimc->lock); return 0; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); + f = &ctx->d_frame; + break; + default: + mutex_unlock(&fimc->lock); + return -EINVAL; } - ff = crop->pad == FIMC_SD_PAD_SINK ? - &ctx->s_frame : &ctx->d_frame; - mutex_lock(&fimc->lock); - r->left = ff->offs_h; - r->top = ff->offs_v; - r->width = ff->width; - r->height = ff->height; - mutex_unlock(&fimc->lock); + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + sel->r = *try_sel; + } else { + r->left = f->offs_h; + r->top = f->offs_v; + r->width = f->width; + r->height = f->height; + } - dbg("ff:%p, pad%d: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d", - ff, crop->pad, r->left, r->top, r->width, r->height, - ff->f_width, ff->f_height); + dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d", + sel->pad, r->left, r->top, r->width, r->height, + f->f_width, f->f_height); + mutex_unlock(&fimc->lock); return 0; } -static int fimc_subdev_set_crop(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int fimc_subdev_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); struct fimc_ctx *ctx = fimc->vid_cap.ctx; - struct v4l2_rect *r = &crop->rect; - struct fimc_frame *ff; + struct fimc_frame *f = &ctx->s_frame; + struct v4l2_rect *r = &sel->r; + struct v4l2_rect *try_sel; unsigned long flags; - dbg("(%d,%d)/%dx%d", r->left, r->top, r->width, r->height); - - ff = crop->pad == FIMC_SD_PAD_SOURCE ? - &ctx->d_frame : &ctx->s_frame; + if (sel->pad != FIMC_SD_PAD_SINK) + return -EINVAL; mutex_lock(&fimc->lock); - fimc_capture_try_crop(ctx, r, crop->pad); + fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP_ACTIVE); - if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + f = &ctx->d_frame; + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + r->width = f->o_width; + r->height = f->o_height; + r->left = 0; + r->top = 0; mutex_unlock(&fimc->lock); - *v4l2_subdev_get_try_crop(fh, crop->pad) = *r; return 0; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); + f = &ctx->d_frame; + break; + default: + mutex_unlock(&fimc->lock); + return -EINVAL; } - spin_lock_irqsave(&fimc->slock, flags); - set_frame_crop(ff, r->left, r->top, r->width, r->height); - if (crop->pad == FIMC_SD_PAD_SOURCE) - ctx->state |= FIMC_DST_CROP; - set_bit(ST_CAPT_APPLY_CFG, &fimc->state); - spin_unlock_irqrestore(&fimc->slock, flags); + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + *try_sel = sel->r; + } else { + spin_lock_irqsave(&fimc->slock, flags); + set_frame_crop(f, r->left, r->top, r->width, r->height); + set_bit(ST_CAPT_APPLY_CFG, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + if (sel->target == V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL) + ctx->state |= FIMC_COMPOSE; + } - dbg("pad%d: (%d,%d)/%dx%d", crop->pad, r->left, r->top, + dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top, r->width, r->height); mutex_unlock(&fimc->lock); @@ -1488,10 +1528,10 @@ static int fimc_subdev_set_crop(struct v4l2_subdev *sd, static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = { .enum_mbus_code = fimc_subdev_enum_mbus_code, + .get_selection = fimc_subdev_get_selection, + .set_selection = fimc_subdev_set_selection, .get_fmt = fimc_subdev_get_fmt, .set_fmt = fimc_subdev_set_fmt, - .get_crop = fimc_subdev_get_crop, - .set_crop = fimc_subdev_set_crop, }; static struct v4l2_subdev_ops fimc_subdev_ops = { diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 794c496d6811..95b27ae5cf27 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -114,7 +114,7 @@ enum fimc_color_fmt { #define FIMC_PARAMS (1 << 0) #define FIMC_SRC_FMT (1 << 3) #define FIMC_DST_FMT (1 << 4) -#define FIMC_DST_CROP (1 << 5) +#define FIMC_COMPOSE (1 << 5) #define FIMC_CTX_M2M (1 << 16) #define FIMC_CTX_CAP (1 << 17) #define FIMC_CTX_SHUT (1 << 18) -- cgit v1.2.3 From 4ad34da0300d7196be25ef79ef3f054756cdc739 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 18 May 2012 08:40:42 -0300 Subject: [media] gspca: the field 'frozen' is under CONFIG_PM The gspca_dev field 'frozen' is added only if CONFIG_PM is set. So add the relevant #ifdef's to various subdrivers that use it. The m32r daily build caught this mistake. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/finepix.c | 20 +++++++++++++++----- drivers/media/video/gspca/jl2005bcd.c | 6 +++++- drivers/media/video/gspca/sq905.c | 6 +++++- drivers/media/video/gspca/sq905c.c | 6 +++++- drivers/media/video/gspca/vicam.c | 6 +++++- drivers/media/video/gspca/zc3xx.c | 7 +++++-- 6 files changed, 40 insertions(+), 11 deletions(-) diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index d0befe981098..6e26c93b4656 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -94,7 +94,11 @@ static void dostream(struct work_struct *work) /* loop reading a frame */ again: - while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif /* request a frame */ mutex_lock(&gspca_dev->usb_lock); @@ -102,8 +106,11 @@ again: mutex_unlock(&gspca_dev->usb_lock); if (ret < 0) break; - if (gspca_dev->frozen || !gspca_dev->dev || - !gspca_dev->streaming) +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif + if (!gspca_dev->dev || !gspca_dev->streaming) break; /* the frame comes in parts */ @@ -118,8 +125,11 @@ again: * error. Just restart. */ goto again; } - if (gspca_dev->frozen || !gspca_dev->dev || - !gspca_dev->streaming) +#ifdef CONFIG_PM + if (gspca_dev->frozen) + goto out; +#endif + if (!gspca_dev->dev || !gspca_dev->streaming) goto out; if (len < FPIX_MAX_TRANSFER || (data[len - 2] == 0xff && diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c index e1fc2561e4bc..9c591c7c6f54 100644 --- a/drivers/media/video/gspca/jl2005bcd.c +++ b/drivers/media/video/gspca/jl2005bcd.c @@ -335,7 +335,11 @@ static void jl2005c_dostream(struct work_struct *work) goto quit_stream; } - while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif /* Check if this is a new frame. If so, start the frame first */ if (!header_read) { mutex_lock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index a144ce759b66..04f54654a026 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -232,7 +232,11 @@ static void sq905_dostream(struct work_struct *work) frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + FRAME_HEADER_LEN; - while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif /* request some data and then read it until we have * a complete frame. */ bytes_left = frame_sz; diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index 720c187f6ec7..f34ddb0570c8 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -150,7 +150,11 @@ static void sq905c_dostream(struct work_struct *work) goto quit_stream; } - while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif /* Request the header, which tells the size to download */ ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c index 432d6cd99cd6..15a30f7a4b2a 100644 --- a/drivers/media/video/gspca/vicam.c +++ b/drivers/media/video/gspca/vicam.c @@ -225,7 +225,11 @@ static void vicam_dostream(struct work_struct *work) goto exit; } - while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif ret = vicam_read_frame(gspca_dev, buffer, frame_sz); if (ret < 0) break; diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 0d504a7c512c..f0bacee33ef9 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -5946,8 +5946,11 @@ static void transfer_update(struct work_struct *work) msleep(100); mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->frozen || !gspca_dev->dev || - !gspca_dev->streaming) +#ifdef CONFIG_PM + if (gspca_dev->frozen) + goto err; +#endif + if (!gspca_dev->dev || !gspca_dev->streaming) goto err; /* Bit 0 of register 11 indicates FIFO overflow */ -- cgit v1.2.3 From ca689488ee6f850000b8d12f0f41e810bff28a7c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 29 Jan 2012 15:44:58 -0300 Subject: [media] linux-dvb v5 API support for ATSC-MH Add the following properties for controlling an ATSC-MH frontend: DTV_ATSCMH_FIC_VER DTV_ATSCMH_PARADE_ID DTV_ATSCMH_NOG DTV_ATSCMH_TNOG DTV_ATSCMH_SGN DTV_ATSCMH_PRC DTV_ATSCMH_RS_FRAME_MODE DTV_ATSCMH_RS_FRAME_ENSEMBLE DTV_ATSCMH_RS_CODE_MODE_PRI DTV_ATSCMH_RS_CODE_MODE_SEC DTV_ATSCMH_SCCC_BLOCK_MODE DTV_ATSCMH_SCCC_CODE_MODE_A DTV_ATSCMH_SCCC_CODE_MODE_B DTV_ATSCMH_SCCC_CODE_MODE_C DTV_ATSCMH_SCCC_CODE_MODE_D DTV_ATSCMH_FIC_ERR DTV_ATSCMH_CRC_ERR DTV_ATSCMH_RS_ERR Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 92 ++++++++++++++++++++++++++++++- drivers/media/dvb/dvb-core/dvb_frontend.h | 22 ++++++++ include/linux/dvb/frontend.h | 54 +++++++++++++++++- 3 files changed, 166 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index cb888d835a89..cd23f303162a 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -182,13 +182,13 @@ static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system) case SYS_DMBTH: return DVBV3_OFDM; case SYS_ATSC: + case SYS_ATSCMH: case SYS_DVBC_ANNEX_B: return DVBV3_ATSC; case SYS_UNDEFINED: case SYS_ISDBC: case SYS_DVBH: case SYS_DAB: - case SYS_ATSCMH: default: /* * Doesn't know how to emulate those types and/or @@ -1030,6 +1030,28 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = { _DTV_CMD(DTV_HIERARCHY, 0, 0), _DTV_CMD(DTV_ENUM_DELSYS, 0, 0), + + _DTV_CMD(DTV_ATSCMH_PARADE_ID, 1, 0), + _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 1, 0), + + _DTV_CMD(DTV_ATSCMH_FIC_VER, 0, 0), + _DTV_CMD(DTV_ATSCMH_PARADE_ID, 0, 0), + _DTV_CMD(DTV_ATSCMH_NOG, 0, 0), + _DTV_CMD(DTV_ATSCMH_TNOG, 0, 0), + _DTV_CMD(DTV_ATSCMH_SGN, 0, 0), + _DTV_CMD(DTV_ATSCMH_PRC, 0, 0), + _DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE, 0, 0), + _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 0, 0), + _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI, 0, 0), + _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_A, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0), + _DTV_CMD(DTV_ATSCMH_FIC_ERR, 0, 0), + _DTV_CMD(DTV_ATSCMH_CRC_ERR, 0, 0), + _DTV_CMD(DTV_ATSCMH_RS_ERR, 0, 0), }; static void dtv_property_dump(struct dtv_property *tvp) @@ -1121,6 +1143,8 @@ static int dtv_property_cache_sync(struct dvb_frontend *fe, case DVBV3_ATSC: dprintk("%s() Preparing ATSC req\n", __func__); c->modulation = p->u.vsb.modulation; + if (c->delivery_system == SYS_ATSCMH) + break; if ((c->modulation == VSB_8) || (c->modulation == VSB_16)) c->delivery_system = SYS_ATSC; else @@ -1367,6 +1391,63 @@ static int dtv_property_process_get(struct dvb_frontend *fe, case DTV_DVBT2_PLP_ID: tvp->u.data = c->dvbt2_plp_id; break; + + /* ATSC-MH */ + case DTV_ATSCMH_FIC_VER: + tvp->u.data = fe->dtv_property_cache.atscmh_fic_ver; + break; + case DTV_ATSCMH_PARADE_ID: + tvp->u.data = fe->dtv_property_cache.atscmh_parade_id; + break; + case DTV_ATSCMH_NOG: + tvp->u.data = fe->dtv_property_cache.atscmh_nog; + break; + case DTV_ATSCMH_TNOG: + tvp->u.data = fe->dtv_property_cache.atscmh_tnog; + break; + case DTV_ATSCMH_SGN: + tvp->u.data = fe->dtv_property_cache.atscmh_sgn; + break; + case DTV_ATSCMH_PRC: + tvp->u.data = fe->dtv_property_cache.atscmh_prc; + break; + case DTV_ATSCMH_RS_FRAME_MODE: + tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_mode; + break; + case DTV_ATSCMH_RS_FRAME_ENSEMBLE: + tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_ensemble; + break; + case DTV_ATSCMH_RS_CODE_MODE_PRI: + tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_pri; + break; + case DTV_ATSCMH_RS_CODE_MODE_SEC: + tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_sec; + break; + case DTV_ATSCMH_SCCC_BLOCK_MODE: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_block_mode; + break; + case DTV_ATSCMH_SCCC_CODE_MODE_A: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_a; + break; + case DTV_ATSCMH_SCCC_CODE_MODE_B: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_b; + break; + case DTV_ATSCMH_SCCC_CODE_MODE_C: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_c; + break; + case DTV_ATSCMH_SCCC_CODE_MODE_D: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_d; + break; + case DTV_ATSCMH_FIC_ERR: + tvp->u.data = fe->dtv_property_cache.atscmh_fic_err; + break; + case DTV_ATSCMH_CRC_ERR: + tvp->u.data = fe->dtv_property_cache.atscmh_crc_err; + break; + case DTV_ATSCMH_RS_ERR: + tvp->u.data = fe->dtv_property_cache.atscmh_rs_err; + break; + default: return -EINVAL; } @@ -1708,6 +1789,15 @@ static int dtv_property_process_set(struct dvb_frontend *fe, case DTV_DVBT2_PLP_ID: c->dvbt2_plp_id = tvp->u.data; break; + + /* ATSC-MH */ + case DTV_ATSCMH_PARADE_ID: + fe->dtv_property_cache.atscmh_parade_id = tvp->u.data; + break; + case DTV_ATSCMH_RS_FRAME_ENSEMBLE: + fe->dtv_property_cache.atscmh_rs_frame_ensemble = tvp->u.data; + break; + default: return -EINVAL; } diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index d63a8215fe03..80f5c27ddc9f 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -372,6 +372,28 @@ struct dtv_frontend_properties { /* DVB-T2 specifics */ u32 dvbt2_plp_id; + + /* ATSC-MH specifics */ + u8 atscmh_fic_ver; + u8 atscmh_parade_id; + u8 atscmh_nog; + u8 atscmh_tnog; + u8 atscmh_sgn; + u8 atscmh_prc; + + u8 atscmh_rs_frame_mode; + u8 atscmh_rs_frame_ensemble; + u8 atscmh_rs_code_mode_pri; + u8 atscmh_rs_code_mode_sec; + u8 atscmh_sccc_block_mode; + u8 atscmh_sccc_code_mode_a; + u8 atscmh_sccc_code_mode_b; + u8 atscmh_sccc_code_mode_c; + u8 atscmh_sccc_code_mode_d; + + u16 atscmh_fic_err; + u16 atscmh_crc_err; + u16 atscmh_rs_err; }; struct dvb_frontend { diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index cb4428ab81ed..5aedd5ae7f8f 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -320,7 +320,27 @@ struct dvb_frontend_event { #define DTV_ENUM_DELSYS 44 -#define DTV_MAX_COMMAND DTV_ENUM_DELSYS +/* ATSC-MH */ +#define DTV_ATSCMH_FIC_VER 45 +#define DTV_ATSCMH_PARADE_ID 46 +#define DTV_ATSCMH_NOG 47 +#define DTV_ATSCMH_TNOG 48 +#define DTV_ATSCMH_SGN 49 +#define DTV_ATSCMH_PRC 50 +#define DTV_ATSCMH_RS_FRAME_MODE 51 +#define DTV_ATSCMH_RS_FRAME_ENSEMBLE 52 +#define DTV_ATSCMH_RS_CODE_MODE_PRI 53 +#define DTV_ATSCMH_RS_CODE_MODE_SEC 54 +#define DTV_ATSCMH_SCCC_BLOCK_MODE 55 +#define DTV_ATSCMH_SCCC_CODE_MODE_A 56 +#define DTV_ATSCMH_SCCC_CODE_MODE_B 57 +#define DTV_ATSCMH_SCCC_CODE_MODE_C 58 +#define DTV_ATSCMH_SCCC_CODE_MODE_D 59 +#define DTV_ATSCMH_FIC_ERR 60 +#define DTV_ATSCMH_CRC_ERR 61 +#define DTV_ATSCMH_RS_ERR 62 + +#define DTV_MAX_COMMAND DTV_ATSCMH_RS_ERR typedef enum fe_pilot { PILOT_ON, @@ -360,6 +380,38 @@ typedef enum fe_delivery_system { #define SYS_DVBC_ANNEX_AC SYS_DVBC_ANNEX_A +/* ATSC-MH */ + +enum atscmh_sccc_block_mode { + ATSCMH_SCCC_BLK_SEP = 0, + ATSCMH_SCCC_BLK_COMB = 1, + ATSCMH_SCCC_BLK_RES = 2, +}; + +enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, + ATSCMH_SCCC_CODE_RES = 2, +}; + +enum atscmh_rs_frame_ensemble { + ATSCMH_RSFRAME_ENS_PRI = 0, + ATSCMH_RSFRAME_ENS_SEC = 1, +}; + +enum atscmh_rs_frame_mode { + ATSCMH_RSFRAME_PRI_ONLY = 0, + ATSCMH_RSFRAME_PRI_SEC = 1, + ATSCMH_RSFRAME_RES = 2, +}; + +enum atscmh_rs_code_mode { + ATSCMH_RSCODE_211_187 = 0, + ATSCMH_RSCODE_223_187 = 1, + ATSCMH_RSCODE_235_187 = 2, + ATSCMH_RSCODE_RES = 3, +}; + struct dtv_cmds_h { char *name; /* A display name for debugging purposes */ -- cgit v1.2.3 From edaa136d393dd88cc32cf8d5b4d0f915ed6a9abe Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 29 Apr 2012 12:59:46 -0300 Subject: [media] DocBook: document new DTV Properties for ATSC-MH delivery system Document the following properties for controlling an ATSC-MH frontend: DTV_ATSCMH_FIC_VER - Version number of the FIC signaling data DTV_ATSCMH_PARADE_ID - Parade identification number DTV_ATSCMH_NOG - Number of MH groups per MH subframe for a designated parade DTV_ATSCMH_TNOG - Total number of MH groups in all parades in one subframe DTV_ATSCMH_SGN - Start group number DTV_ATSCMH_PRC - Parade repetition cycle DTV_ATSCMH_RS_FRAME_MODE - RS frame mode DTV_ATSCMH_RS_FRAME_ENSEMBLE - RS frame ensemble DTV_ATSCMH_RS_CODE_MODE_PRI - RS code mode (primary) DTV_ATSCMH_RS_CODE_MODE_SEC - RS code mode (secondary) DTV_ATSCMH_SCCC_BLOCK_MODE - Series Concatenated Convolutional Code Block Mode DTV_ATSCMH_SCCC_CODE_MODE_A - Series Concatenated Convolutional Code Rate A DTV_ATSCMH_SCCC_CODE_MODE_B - Series Concatenated Convolutional Code Rate B DTV_ATSCMH_SCCC_CODE_MODE_C - Series Concatenated Convolutional Code Rate C DTV_ATSCMH_SCCC_CODE_MODE_D - Series Concatenated Convolutional Code Rate D DTV_ATSCMH_FIC_ERR - FIC error count DTV_ATSCMH_CRC_ERR - CRC error count DTV_ATSCMH_RS_ERR - RS error count Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/dvb/dvbproperty.xml | 178 ++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index c7a4ca517859..d63153522b60 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -531,6 +531,154 @@ typedef enum fe_delivery_system { here are referring to what can be found in the TMCC-structure - independent of the mode. +
+ <constant>DTV_ATSCMH_FIC_VER</constant> + Version number of the FIC (Fast Information Channel) signaling data. + FIC is used for relaying information to allow rapid service acquisition by the receiver. + Possible values: 0, 1, 2, 3, ..., 30, 31 +
+
+ <constant>DTV_ATSCMH_PARADE_ID</constant> + Parade identification number + A parade is a collection of up to eight MH groups, conveying one or two ensembles. + Possible values: 0, 1, 2, 3, ..., 126, 127 +
+
+ <constant>DTV_ATSCMH_NOG</constant> + Number of MH groups per MH subframe for a designated parade. + Possible values: 1, 2, 3, 4, 5, 6, 7, 8 +
+
+ <constant>DTV_ATSCMH_TNOG</constant> + Total number of MH groups including all MH groups belonging to all MH parades in one MH subframe. + Possible values: 0, 1, 2, 3, ..., 30, 31 +
+
+ <constant>DTV_ATSCMH_SGN</constant> + Start group number. + Possible values: 0, 1, 2, 3, ..., 14, 15 +
+
+ <constant>DTV_ATSCMH_PRC</constant> + Parade repetition cycle. + Possible values: 1, 2, 3, 4, 5, 6, 7, 8 +
+
+ <constant>DTV_ATSCMH_RS_FRAME_MODE</constant> + RS frame mode. + Possible values are: + +typedef enum atscmh_rs_frame_mode { + ATSCMH_RSFRAME_PRI_ONLY = 0, + ATSCMH_RSFRAME_PRI_SEC = 1, +} atscmh_rs_frame_mode_t; + +
+
+ <constant>DTV_ATSCMH_RS_FRAME_ENSEMBLE</constant> + RS frame ensemble. + Possible values are: + +typedef enum atscmh_rs_frame_ensemble { + ATSCMH_RSFRAME_ENS_PRI = 0, + ATSCMH_RSFRAME_ENS_SEC = 1, +} atscmh_rs_frame_ensemble_t; + +
+
+ <constant>DTV_ATSCMH_RS_CODE_MODE_PRI</constant> + RS code mode (primary). + Possible values are: + +typedef enum atscmh_rs_code_mode { + ATSCMH_RSCODE_211_187 = 0, + ATSCMH_RSCODE_223_187 = 1, + ATSCMH_RSCODE_235_187 = 2, +} atscmh_rs_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_RS_CODE_MODE_SEC</constant> + RS code mode (secondary). + Possible values are: + +typedef enum atscmh_rs_code_mode { + ATSCMH_RSCODE_211_187 = 0, + ATSCMH_RSCODE_223_187 = 1, + ATSCMH_RSCODE_235_187 = 2, +} atscmh_rs_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_BLOCK_MODE</constant> + Series Concatenated Convolutional Code Block Mode. + Possible values are: + +typedef enum atscmh_sccc_block_mode { + ATSCMH_SCCC_BLK_SEP = 0, + ATSCMH_SCCC_BLK_COMB = 1, +} atscmh_sccc_block_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_CODE_MODE_A</constant> + Series Concatenated Convolutional Code Rate. + Possible values are: + +typedef enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, +} atscmh_sccc_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_CODE_MODE_B</constant> + Series Concatenated Convolutional Code Rate. + Possible values are: + +typedef enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, +} atscmh_sccc_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_CODE_MODE_C</constant> + Series Concatenated Convolutional Code Rate. + Possible values are: + +typedef enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, +} atscmh_sccc_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_CODE_MODE_D</constant> + Series Concatenated Convolutional Code Rate. + Possible values are: + +typedef enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, +} atscmh_sccc_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_FIC_ERR</constant> + FIC error count. + Possible values: 0, 1, 2, 3, ..., 0xffff +
+
+ <constant>DTV_ATSCMH_CRC_ERR</constant> + CRC error count. + Possible values: 0, 1, 2, 3, ..., 0xffff +
+
+ <constant>DTV_ATSCMH_RS_ERR</constant> + RS error count. + Possible values: 0, 1, 2, 3, ..., 0xffff +
<constant>DTV_API_VERSION</constant> @@ -774,6 +922,36 @@ typedef enum fe_hierarchy { DTV_BANDWIDTH_HZ
+
+ ATSC-MH delivery system + The following parameters are valid for ATSC-MH: + + DTV_API_VERSION + DTV_DELIVERY_SYSTEM + DTV_TUNE + DTV_CLEAR + DTV_FREQUENCY + DTV_BANDWIDTH_HZ + DTV_ATSCMH_FIC_VER + DTV_ATSCMH_PARADE_ID + DTV_ATSCMH_NOG + DTV_ATSCMH_TNOG + DTV_ATSCMH_SGN + DTV_ATSCMH_PRC + DTV_ATSCMH_RS_FRAME_MODE + DTV_ATSCMH_RS_FRAME_ENSEMBLE + DTV_ATSCMH_CODE_MODE_PRI + DTV_ATSCMH_CODE_MODE_SEC + DTV_ATSCMH_SCCC_BLOCK_MODE + DTV_ATSCMH_SCCC_CODE_MODE_A + DTV_ATSCMH_SCCC_CODE_MODE_B + DTV_ATSCMH_SCCC_CODE_MODE_C + DTV_ATSCMH_SCCC_CODE_MODE_D + DTV_ATSCMH_FIC_ERR + DTV_ATSCMH_CRC_ERR + DTV_ATSCMH_RS_ERR + +
Properties used on cable delivery systems -- cgit v1.2.3 From 03128fc8b56f99738f73856532dd3888e38fc063 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 29 Apr 2012 13:06:16 -0300 Subject: [media] increment DVB API to version 5.6 for ATSC-MH frontend control increment the DVB API version to 5.6 to signify support for controlling an ATSC-MH frontend. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- include/linux/dvb/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/dvb/version.h b/include/linux/dvb/version.h index 0559e2bd38f9..43d9e8d462d4 100644 --- a/include/linux/dvb/version.h +++ b/include/linux/dvb/version.h @@ -24,6 +24,6 @@ #define _DVBVERSION_H_ #define DVB_API_VERSION 5 -#define DVB_API_VERSION_MINOR 5 +#define DVB_API_VERSION_MINOR 6 #endif /*_DVBVERSION_H_*/ -- cgit v1.2.3 From 4ef70775070c28fdca34b967338aaff0b1f1e619 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 29 Jan 2012 19:35:24 -0300 Subject: [media] mxl111sf-tuner: tune SYS_ATSCMH just like SYS_ATSC The MxL111SF tuner is programmed the same way for ATSC-MH as it is programmed for ATSC. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/mxl111sf-tuner.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c index 72db6eef4b9c..74da5bb1ce99 100644 --- a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c +++ b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c @@ -284,6 +284,7 @@ static int mxl111sf_tuner_set_params(struct dvb_frontend *fe) switch (delsys) { case SYS_ATSC: + case SYS_ATSCMH: bw = 0; /* ATSC */ break; case SYS_DVBC_ANNEX_B: -- cgit v1.2.3 From e26f2ae4527b54fab94660826ff8d930f462449c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 29 Jan 2012 15:46:46 -0300 Subject: [media] DVB: add support for the LG2160 ATSC-MH demodulator This patch adds support for controlling the LG2160 and LG2161 ATSC-MH demodulators. [mchehab@redhat.com: fold with the next patch that was fixing the DVB ABI] Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/Kconfig | 8 + drivers/media/dvb/frontends/Makefile | 1 + drivers/media/dvb/frontends/lg2160.c | 1461 ++++++++++++++++++++++++++++++++++ drivers/media/dvb/frontends/lg2160.h | 84 ++ 4 files changed, 1554 insertions(+) create mode 100644 drivers/media/dvb/frontends/lg2160.c create mode 100644 drivers/media/dvb/frontends/lg2160.h diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index f47983472aed..b98ebb264e29 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -531,6 +531,14 @@ config DVB_LGDT3305 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +config DVB_LG2160 + tristate "LG Electronics LG216x based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + An ATSC/MH demodulator module. Say Y when you want + to support this frontend. + config DVB_S5H1409 tristate "Samsung S5H1409 based" depends on DVB_CORE && I2C diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index b0381dc8e176..cd1ac2fd5774 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_DVB_BCM3510) += bcm3510.o obj-$(CONFIG_DVB_S5H1420) += s5h1420.o obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o +obj-$(CONFIG_DVB_LG2160) += lg2160.o obj-$(CONFIG_DVB_CX24123) += cx24123.o obj-$(CONFIG_DVB_LNBP21) += lnbp21.o obj-$(CONFIG_DVB_LNBP22) += lnbp22.o diff --git a/drivers/media/dvb/frontends/lg2160.c b/drivers/media/dvb/frontends/lg2160.c new file mode 100644 index 000000000000..daa8596b9ec7 --- /dev/null +++ b/drivers/media/dvb/frontends/lg2160.c @@ -0,0 +1,1461 @@ +/* + * Support for LG2160 - ATSC/MH + * + * Copyright (C) 2010 Michael Krufky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include "lg2160.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); + +#define DBG_INFO 1 +#define DBG_REG 2 + +#define lg_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt, __func__, ##arg) + +#define lg_info(fmt, arg...) printk(KERN_INFO "lg2160: " fmt, ##arg) +#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) +#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) +#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ + lg_printk(KERN_DEBUG, fmt, ##arg) +#define lg_reg(fmt, arg...) if (debug & DBG_REG) \ + lg_printk(KERN_DEBUG, fmt, ##arg) + +#define lg_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if (__ret) \ + lg_err("error %d on line %d\n", ret, __LINE__); \ + __ret; \ +}) + +struct lg216x_state { + struct i2c_adapter *i2c_adap; + const struct lg2160_config *cfg; + + struct dvb_frontend frontend; + + u32 current_frequency; + u8 parade_id; + u8 fic_ver; + unsigned int last_reset; +}; + +/* ------------------------------------------------------------------------ */ + +static int lg216x_write_reg(struct lg216x_state *state, u16 reg, u8 val) +{ + int ret; + u8 buf[] = { reg >> 8, reg & 0xff, val }; + struct i2c_msg msg = { + .addr = state->cfg->i2c_addr, .flags = 0, + .buf = buf, .len = 3, + }; + + lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); + + ret = i2c_transfer(state->i2c_adap, &msg, 1); + + if (ret != 1) { + lg_err("error (addr %02x %02x <- %02x, err = %i)\n", + msg.buf[0], msg.buf[1], msg.buf[2], ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + return 0; +} + +static int lg216x_read_reg(struct lg216x_state *state, u16 reg, u8 *val) +{ + int ret; + u8 reg_buf[] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[] = { + { .addr = state->cfg->i2c_addr, + .flags = 0, .buf = reg_buf, .len = 2 }, + { .addr = state->cfg->i2c_addr, + .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + lg_reg("reg: 0x%04x\n", reg); + + ret = i2c_transfer(state->i2c_adap, msg, 2); + + if (ret != 2) { + lg_err("error (addr %02x reg %04x error (ret == %i)\n", + state->cfg->i2c_addr, reg, ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + return 0; +} + +struct lg216x_reg { + u16 reg; + u8 val; +}; + +static int lg216x_write_regs(struct lg216x_state *state, + struct lg216x_reg *regs, int len) +{ + int i, ret; + + lg_reg("writing %d registers...\n", len); + + for (i = 0; i < len - 1; i++) { + ret = lg216x_write_reg(state, regs[i].reg, regs[i].val); + if (lg_fail(ret)) + return ret; + } + return 0; +} + +static int lg216x_set_reg_bit(struct lg216x_state *state, + u16 reg, int bit, int onoff) +{ + u8 val; + int ret; + + lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); + + ret = lg216x_read_reg(state, reg, &val); + if (lg_fail(ret)) + goto fail; + + val &= ~(1 << bit); + val |= (onoff & 1) << bit; + + ret = lg216x_write_reg(state, reg, val); + lg_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + if (state->cfg->deny_i2c_rptr) + return 0; + + lg_dbg("(%d)\n", enable); + + ret = lg216x_set_reg_bit(state, 0x0000, 0, enable ? 0 : 1); + + msleep(1); + + return ret; +} + +static int lg216x_soft_reset(struct lg216x_state *state) +{ + int ret; + + lg_dbg("\n"); + + ret = lg216x_write_reg(state, 0x0002, 0x00); + if (lg_fail(ret)) + goto fail; + + msleep(20); + ret = lg216x_write_reg(state, 0x0002, 0x01); + if (lg_fail(ret)) + goto fail; + + state->last_reset = jiffies_to_msecs(jiffies); +fail: + return ret; +} + +static int lg216x_initialize(struct lg216x_state *state) +{ + int ret; + + static struct lg216x_reg lg2160_init[] = { +#if 0 + { .reg = 0x0015, .val = 0xe6 }, +#else + { .reg = 0x0015, .val = 0xf7 }, + { .reg = 0x001b, .val = 0x52 }, + { .reg = 0x0208, .val = 0x00 }, + { .reg = 0x0209, .val = 0x82 }, + { .reg = 0x0210, .val = 0xf9 }, + { .reg = 0x020a, .val = 0x00 }, + { .reg = 0x020b, .val = 0x82 }, + { .reg = 0x020d, .val = 0x28 }, + { .reg = 0x020f, .val = 0x14 }, +#endif + }; + + static struct lg216x_reg lg2161_init[] = { + { .reg = 0x0000, .val = 0x41 }, + { .reg = 0x0001, .val = 0xfb }, + { .reg = 0x0216, .val = 0x00 }, + { .reg = 0x0219, .val = 0x00 }, + { .reg = 0x021b, .val = 0x55 }, + { .reg = 0x0606, .val = 0x0a }, + }; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_write_regs(state, + lg2160_init, ARRAY_SIZE(lg2160_init)); + break; + case LG2161: + ret = lg216x_write_regs(state, + lg2161_init, ARRAY_SIZE(lg2161_init)); + break; + default: + ret = -EINVAL; + break; + } + if (lg_fail(ret)) + goto fail; + + ret = lg216x_soft_reset(state); + lg_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_set_if(struct lg216x_state *state) +{ + u8 val; + int ret; + + lg_dbg("%d KHz\n", state->cfg->if_khz); + + ret = lg216x_read_reg(state, 0x0132, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfb; + val |= (0 == state->cfg->if_khz) ? 0x04 : 0x00; + + ret = lg216x_write_reg(state, 0x0132, val); + lg_fail(ret); + + /* if NOT zero IF, 6 MHz is the default */ +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg2160_agc_fix(struct lg216x_state *state, + int if_agc_fix, int rf_agc_fix) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0100, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xf3; + val |= (if_agc_fix) ? 0x08 : 0x00; + val |= (rf_agc_fix) ? 0x04 : 0x00; + + ret = lg216x_write_reg(state, 0x0100, val); + lg_fail(ret); +fail: + return ret; +} + +#if 0 +static int lg2160_agc_freeze(struct lg216x_state *state, + int if_agc_freeze, int rf_agc_freeze) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0100, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xcf; + val |= (if_agc_freeze) ? 0x20 : 0x00; + val |= (rf_agc_freeze) ? 0x10 : 0x00; + + ret = lg216x_write_reg(state, 0x0100, val); + lg_fail(ret); +fail: + return ret; +} +#endif + +static int lg2160_agc_polarity(struct lg216x_state *state, + int if_agc_polarity, int rf_agc_polarity) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0100, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfc; + val |= (if_agc_polarity) ? 0x02 : 0x00; + val |= (rf_agc_polarity) ? 0x01 : 0x00; + + ret = lg216x_write_reg(state, 0x0100, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2160_tuner_pwr_save_polarity(struct lg216x_state *state, + int polarity) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0008, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfe; + val |= (polarity) ? 0x01 : 0x00; + + ret = lg216x_write_reg(state, 0x0008, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2160_spectrum_polarity(struct lg216x_state *state, + int inverted) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0132, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfd; + val |= (inverted) ? 0x02 : 0x00; + + ret = lg216x_write_reg(state, 0x0132, val); + lg_fail(ret); +fail: + return lg216x_soft_reset(state); +} + +static int lg2160_tuner_pwr_save(struct lg216x_state *state, int onoff) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0007, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xbf; + val |= (onoff) ? 0x40 : 0x00; + + ret = lg216x_write_reg(state, 0x0007, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg216x_set_parade(struct lg216x_state *state, int id) +{ + int ret; + + ret = lg216x_write_reg(state, 0x013e, id & 0x7f); + if (lg_fail(ret)) + goto fail; + + state->parade_id = id & 0x7f; +fail: + return ret; +} + +static int lg216x_set_ensemble(struct lg216x_state *state, int id) +{ + int ret; + u16 reg; + u8 val; + + switch (state->cfg->lg_chip) { + case LG2160: + reg = 0x0400; + break; + case LG2161: + reg = 0x0500; + break; + } + + ret = lg216x_read_reg(state, reg, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfe; + val |= (id) ? 0x01 : 0x00; + + ret = lg216x_write_reg(state, reg, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2160_set_spi_clock(struct lg216x_state *state) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0014, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xf3; + val |= (state->cfg->spi_clock << 2); + + ret = lg216x_write_reg(state, 0x0014, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2161_set_output_interface(struct lg216x_state *state) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0014, &val); + if (lg_fail(ret)) + goto fail; + + val &= ~0x07; + val |= state->cfg->output_if; /* FIXME: needs sanity check */ + + ret = lg216x_write_reg(state, 0x0014, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg216x_enable_fic(struct lg216x_state *state, int onoff) +{ + int ret; + + ret = lg216x_write_reg(state, 0x0017, 0x23); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_write_reg(state, 0x0016, 0xfc); + if (lg_fail(ret)) + goto fail; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_write_reg(state, 0x0016, + 0xfc | ((onoff) ? 0x02 : 0x00)); + break; + case LG2161: + ret = lg216x_write_reg(state, 0x0016, (onoff) ? 0x10 : 0x00); + break; + } + if (lg_fail(ret)) + goto fail; + + ret = lg216x_initialize(state); + if (lg_fail(ret)) + goto fail; + + if (onoff) { + ret = lg216x_write_reg(state, 0x0017, 0x03); + lg_fail(ret); + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_get_fic_version(struct lg216x_state *state, u8 *ficver) +{ + u8 val; + int ret; + + *ficver = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0128, &val); + if (lg_fail(ret)) + goto fail; + + *ficver = (val >> 3) & 0x1f; +fail: + return ret; +} + +#if 0 +static int lg2160_get_parade_id(struct lg216x_state *state, u8 *id) +{ + u8 val; + int ret; + + *id = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0123, &val); + if (lg_fail(ret)) + goto fail; + + *id = val & 0x7f; +fail: + return ret; +} +#endif + +static int lg216x_get_nog(struct lg216x_state *state, u8 *nog) +{ + u8 val; + int ret; + + *nog = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0124, &val); + if (lg_fail(ret)) + goto fail; + + *nog = ((val >> 4) & 0x07) + 1; +fail: + return ret; +} + +static int lg216x_get_tnog(struct lg216x_state *state, u8 *tnog) +{ + u8 val; + int ret; + + *tnog = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0125, &val); + if (lg_fail(ret)) + goto fail; + + *tnog = val & 0x1f; +fail: + return ret; +} + +static int lg216x_get_sgn(struct lg216x_state *state, u8 *sgn) +{ + u8 val; + int ret; + + *sgn = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0124, &val); + if (lg_fail(ret)) + goto fail; + + *sgn = val & 0x0f; +fail: + return ret; +} + +static int lg216x_get_prc(struct lg216x_state *state, u8 *prc) +{ + u8 val; + int ret; + + *prc = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0125, &val); + if (lg_fail(ret)) + goto fail; + + *prc = ((val >> 5) & 0x07) + 1; +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_get_rs_frame_mode(struct lg216x_state *state, + enum atscmh_rs_frame_mode *rs_framemode) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0410, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0513, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + switch ((val >> 4) & 0x03) { +#if 1 + default: +#endif + case 0x00: + *rs_framemode = ATSCMH_RSFRAME_PRI_ONLY; + break; + case 0x01: + *rs_framemode = ATSCMH_RSFRAME_PRI_SEC; + break; +#if 0 + default: + *rs_framemode = ATSCMH_RSFRAME_RES; + break; +#endif + } +fail: + return ret; +} + +static +int lg216x_get_rs_frame_ensemble(struct lg216x_state *state, + enum atscmh_rs_frame_ensemble *rs_frame_ens) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0400, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0500, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + val &= 0x01; + *rs_frame_ens = (enum atscmh_rs_frame_ensemble) val; +fail: + return ret; +} + +static int lg216x_get_rs_code_mode(struct lg216x_state *state, + enum atscmh_rs_code_mode *rs_code_pri, + enum atscmh_rs_code_mode *rs_code_sec) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0410, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0513, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + *rs_code_pri = (enum atscmh_rs_code_mode) ((val >> 2) & 0x03); + *rs_code_sec = (enum atscmh_rs_code_mode) (val & 0x03); +fail: + return ret; +} + +static int lg216x_get_sccc_block_mode(struct lg216x_state *state, + enum atscmh_sccc_block_mode *sccc_block) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0315, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0511, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + switch (val & 0x03) { + case 0x00: + *sccc_block = ATSCMH_SCCC_BLK_SEP; + break; + case 0x01: + *sccc_block = ATSCMH_SCCC_BLK_COMB; + break; + default: + *sccc_block = ATSCMH_SCCC_BLK_RES; + break; + } +fail: + return ret; +} + +static int lg216x_get_sccc_code_mode(struct lg216x_state *state, + enum atscmh_sccc_code_mode *mode_a, + enum atscmh_sccc_code_mode *mode_b, + enum atscmh_sccc_code_mode *mode_c, + enum atscmh_sccc_code_mode *mode_d) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0316, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0512, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + switch ((val >> 6) & 0x03) { + case 0x00: + *mode_a = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_a = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_a = ATSCMH_SCCC_CODE_RES; + break; + } + + switch ((val >> 4) & 0x03) { + case 0x00: + *mode_b = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_b = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_b = ATSCMH_SCCC_CODE_RES; + break; + } + + switch ((val >> 2) & 0x03) { + case 0x00: + *mode_c = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_c = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_c = ATSCMH_SCCC_CODE_RES; + break; + } + + switch (val & 0x03) { + case 0x00: + *mode_d = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_d = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_d = ATSCMH_SCCC_CODE_RES; + break; + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_read_fic_err_count(struct lg216x_state *state, u8 *err) +{ + u8 fic_err; + int ret; + + *err = 0; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0012, &fic_err); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x001e, &fic_err); + break; + } + if (lg_fail(ret)) + goto fail; + + *err = fic_err; +fail: + return ret; +} + +static int lg2160_read_crc_err_count(struct lg216x_state *state, u16 *err) +{ + u8 crc_err1, crc_err2; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0411, &crc_err1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0412, &crc_err2); + if (lg_fail(ret)) + goto fail; + + *err = (u16)(((crc_err2 & 0x0f) << 8) | crc_err1); +fail: + return ret; +} + +static int lg2161_read_crc_err_count(struct lg216x_state *state, u16 *err) +{ + u8 crc_err; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0612, &crc_err); + if (lg_fail(ret)) + goto fail; + + *err = (u16)crc_err; +fail: + return ret; +} + +static int lg216x_read_crc_err_count(struct lg216x_state *state, u16 *err) +{ + int ret; + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_read_crc_err_count(state, err); + break; + case LG2161: + ret = lg2161_read_crc_err_count(state, err); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int lg2160_read_rs_err_count(struct lg216x_state *state, u16 *err) +{ + u8 rs_err1, rs_err2; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0413, &rs_err1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0414, &rs_err2); + if (lg_fail(ret)) + goto fail; + + *err = (u16)(((rs_err2 & 0x0f) << 8) | rs_err1); +fail: + return ret; +} + +static int lg2161_read_rs_err_count(struct lg216x_state *state, u16 *err) +{ + u8 rs_err1, rs_err2; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0613, &rs_err1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0614, &rs_err2); + if (lg_fail(ret)) + goto fail; + + *err = (u16)((rs_err1 << 8) | rs_err2); +fail: + return ret; +} + +static int lg216x_read_rs_err_count(struct lg216x_state *state, u16 *err) +{ + int ret; + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_read_rs_err_count(state, err); + break; + case LG2161: + ret = lg2161_read_rs_err_count(state, err); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_get_frontend(struct dvb_frontend *fe) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + lg_dbg("\n"); + + fe->dtv_property_cache.modulation = VSB_8; + fe->dtv_property_cache.frequency = state->current_frequency; + fe->dtv_property_cache.delivery_system = SYS_ATSCMH; + + ret = lg216x_get_fic_version(state, + &fe->dtv_property_cache.atscmh_fic_ver); + if (lg_fail(ret)) + goto fail; + if (state->fic_ver != fe->dtv_property_cache.atscmh_fic_ver) { + state->fic_ver = fe->dtv_property_cache.atscmh_fic_ver; + +#if 0 + ret = lg2160_get_parade_id(state, + &fe->dtv_property_cache.atscmh_parade_id); + if (lg_fail(ret)) + goto fail; +/* #else */ + fe->dtv_property_cache.atscmh_parade_id = state->parade_id; +#endif + ret = lg216x_get_nog(state, + &fe->dtv_property_cache.atscmh_nog); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_tnog(state, + &fe->dtv_property_cache.atscmh_tnog); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_sgn(state, + &fe->dtv_property_cache.atscmh_sgn); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_prc(state, + &fe->dtv_property_cache.atscmh_prc); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_get_rs_frame_mode(state, + (enum atscmh_rs_frame_mode *) + &fe->dtv_property_cache.atscmh_rs_frame_mode); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_rs_frame_ensemble(state, + (enum atscmh_rs_frame_ensemble *) + &fe->dtv_property_cache.atscmh_rs_frame_ensemble); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_rs_code_mode(state, + (enum atscmh_rs_code_mode *) + &fe->dtv_property_cache.atscmh_rs_code_mode_pri, + (enum atscmh_rs_code_mode *) + &fe->dtv_property_cache.atscmh_rs_code_mode_sec); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_sccc_block_mode(state, + (enum atscmh_sccc_block_mode *) + &fe->dtv_property_cache.atscmh_sccc_block_mode); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_sccc_code_mode(state, + (enum atscmh_sccc_code_mode *) + &fe->dtv_property_cache.atscmh_sccc_code_mode_a, + (enum atscmh_sccc_code_mode *) + &fe->dtv_property_cache.atscmh_sccc_code_mode_b, + (enum atscmh_sccc_code_mode *) + &fe->dtv_property_cache.atscmh_sccc_code_mode_c, + (enum atscmh_sccc_code_mode *) + &fe->dtv_property_cache.atscmh_sccc_code_mode_d); + if (lg_fail(ret)) + goto fail; + } + ret = lg216x_read_fic_err_count(state, + (u8 *)&fe->dtv_property_cache.atscmh_fic_err); + if (lg_fail(ret)) + goto fail; + ret = lg216x_read_crc_err_count(state, + &fe->dtv_property_cache.atscmh_crc_err); + if (lg_fail(ret)) + goto fail; + ret = lg216x_read_rs_err_count(state, + &fe->dtv_property_cache.atscmh_rs_err); + if (lg_fail(ret)) + goto fail; + + switch (state->cfg->lg_chip) { + case LG2160: + if (((fe->dtv_property_cache.atscmh_rs_err >= 240) && + (fe->dtv_property_cache.atscmh_crc_err >= 240)) && + ((jiffies_to_msecs(jiffies) - state->last_reset) > 6000)) + ret = lg216x_soft_reset(state); + break; + case LG2161: + /* no fix needed here (as far as we know) */ + ret = 0; + break; + } + lg_fail(ret); +fail: + return ret; +} + +static int lg216x_get_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + return (DTV_ATSCMH_FIC_VER == tvp->cmd) ? + lg216x_get_frontend(fe) : 0; +} + + +static int lg2160_set_frontend(struct dvb_frontend *fe) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + lg_dbg("(%d)\n", fe->dtv_property_cache.frequency); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + if (lg_fail(ret)) + goto fail; + state->current_frequency = fe->dtv_property_cache.frequency; + } + + ret = lg2160_agc_fix(state, 0, 0); + if (lg_fail(ret)) + goto fail; + ret = lg2160_agc_polarity(state, 0, 0); + if (lg_fail(ret)) + goto fail; + ret = lg2160_tuner_pwr_save_polarity(state, 1); + if (lg_fail(ret)) + goto fail; + ret = lg216x_set_if(state); + if (lg_fail(ret)) + goto fail; + ret = lg2160_spectrum_polarity(state, state->cfg->spectral_inversion); + if (lg_fail(ret)) + goto fail; + + /* be tuned before this point */ + ret = lg216x_soft_reset(state); + if (lg_fail(ret)) + goto fail; + + ret = lg2160_tuner_pwr_save(state, 0); + if (lg_fail(ret)) + goto fail; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_set_spi_clock(state); + if (lg_fail(ret)) + goto fail; + break; + case LG2161: + ret = lg2161_set_output_interface(state); + if (lg_fail(ret)) + goto fail; + break; + } + + ret = lg216x_set_parade(state, fe->dtv_property_cache.atscmh_parade_id); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_set_ensemble(state, + fe->dtv_property_cache.atscmh_rs_frame_ensemble); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_initialize(state); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_enable_fic(state, 1); + lg_fail(ret); + + lg216x_get_frontend(fe); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg2160_read_lock_status(struct lg216x_state *state, + int *acq_lock, int *sync_lock) +{ + u8 val; + int ret; + + *acq_lock = 0; + *sync_lock = 0; + + ret = lg216x_read_reg(state, 0x011b, &val); + if (lg_fail(ret)) + goto fail; + + *sync_lock = (val & 0x20) ? 0 : 1; + *acq_lock = (val & 0x40) ? 0 : 1; +fail: + return ret; +} + +#ifdef USE_LG2161_LOCK_BITS +static int lg2161_read_lock_status(struct lg216x_state *state, + int *acq_lock, int *sync_lock) +{ + u8 val; + int ret; + + *acq_lock = 0; + *sync_lock = 0; + + ret = lg216x_read_reg(state, 0x0304, &val); + if (lg_fail(ret)) + goto fail; + + *sync_lock = (val & 0x80) ? 0 : 1; + + ret = lg216x_read_reg(state, 0x011b, &val); + if (lg_fail(ret)) + goto fail; + + *acq_lock = (val & 0x40) ? 0 : 1; +fail: + return ret; +} +#endif + +static int lg216x_read_lock_status(struct lg216x_state *state, + int *acq_lock, int *sync_lock) +{ +#ifdef USE_LG2161_LOCK_BITS + int ret; + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_read_lock_status(state, acq_lock, sync_lock); + break; + case LG2161: + ret = lg2161_read_lock_status(state, acq_lock, sync_lock); + break; + default: + ret = -EINVAL; + break; + } + return ret; +#else + return lg2160_read_lock_status(state, acq_lock, sync_lock); +#endif +} + +static int lg216x_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret, acq_lock, sync_lock; + + *status = 0; + + ret = lg216x_read_lock_status(state, &acq_lock, &sync_lock); + if (lg_fail(ret)) + goto fail; + + lg_dbg("%s%s\n", + acq_lock ? "SIGNALEXIST " : "", + sync_lock ? "SYNCLOCK" : ""); + + if (acq_lock) + *status |= FE_HAS_SIGNAL; + if (sync_lock) + *status |= FE_HAS_SYNC; + + if (*status) + *status |= FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; + +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg2160_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lg216x_state *state = fe->demodulator_priv; + u8 snr1, snr2; + int ret; + + *snr = 0; + + ret = lg216x_read_reg(state, 0x0202, &snr1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0203, &snr2); + if (lg_fail(ret)) + goto fail; + + if ((snr1 == 0xba) || (snr2 == 0xdf)) + *snr = 0; + else +#if 1 + *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 >> 4); +#else /* BCD */ + *snr = (snr2 | (snr1 << 8)); +#endif +fail: + return ret; +} + +static int lg2161_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lg216x_state *state = fe->demodulator_priv; + u8 snr1, snr2; + int ret; + + *snr = 0; + + ret = lg216x_read_reg(state, 0x0302, &snr1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0303, &snr2); + if (lg_fail(ret)) + goto fail; + + if ((snr1 == 0xba) || (snr2 == 0xfd)) + *snr = 0; + else + + *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 & 0x0f); +fail: + return ret; +} + +static int lg216x_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ +#if 0 + /* borrowed from lgdt330x.c + * + * Calculate strength from SNR up to 35dB + * Even though the SNR can go higher than 35dB, + * there is some comfort factor in having a range of + * strong signals that can show at 100% + */ + struct lg216x_state *state = fe->demodulator_priv; + u16 snr; + int ret; +#endif + *strength = 0; +#if 0 + ret = fe->ops.read_snr(fe, &snr); + if (lg_fail(ret)) + goto fail; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; +fail: + return ret; +#else + return 0; +#endif +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + ret = lg216x_read_rs_err_count(state, + &fe->dtv_property_cache.atscmh_rs_err); + if (lg_fail(ret)) + goto fail; + + *ucblocks = fe->dtv_property_cache.atscmh_rs_err; +fail: + return 0; +} + +static int lg216x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 500; + lg_dbg("\n"); + return 0; +} + +static void lg216x_release(struct dvb_frontend *fe) +{ + struct lg216x_state *state = fe->demodulator_priv; + lg_dbg("\n"); + kfree(state); +} + +static struct dvb_frontend_ops lg2160_ops = { + .delsys = { SYS_ATSCMH }, + .info = { + .name = "LG Electronics LG2160 ATSC/MH Frontend", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + }, + .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, +#if 0 + .init = lg216x_init, + .sleep = lg216x_sleep, +#endif + .get_property = lg216x_get_property, + + .set_frontend = lg2160_set_frontend, + .get_frontend = lg216x_get_frontend, + .get_tune_settings = lg216x_get_tune_settings, + .read_status = lg216x_read_status, +#if 0 + .read_ber = lg216x_read_ber, +#endif + .read_signal_strength = lg216x_read_signal_strength, + .read_snr = lg2160_read_snr, + .read_ucblocks = lg216x_read_ucblocks, + .release = lg216x_release, +}; + +static struct dvb_frontend_ops lg2161_ops = { + .delsys = { SYS_ATSCMH }, + .info = { + .name = "LG Electronics LG2161 ATSC/MH Frontend", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + }, + .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, +#if 0 + .init = lg216x_init, + .sleep = lg216x_sleep, +#endif + .get_property = lg216x_get_property, + + .set_frontend = lg2160_set_frontend, + .get_frontend = lg216x_get_frontend, + .get_tune_settings = lg216x_get_tune_settings, + .read_status = lg216x_read_status, +#if 0 + .read_ber = lg216x_read_ber, +#endif + .read_signal_strength = lg216x_read_signal_strength, + .read_snr = lg2161_read_snr, + .read_ucblocks = lg216x_read_ucblocks, + .release = lg216x_release, +}; + +struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, + struct i2c_adapter *i2c_adap) +{ + struct lg216x_state *state = NULL; + + lg_dbg("(%d-%04x)\n", + i2c_adap ? i2c_adapter_id(i2c_adap) : 0, + config ? config->i2c_addr : 0); + + state = kzalloc(sizeof(struct lg216x_state), GFP_KERNEL); + if (state == NULL) + goto fail; + + state->cfg = config; + state->i2c_adap = i2c_adap; + state->fic_ver = 0xff; + state->parade_id = 0xff; + + switch (config->lg_chip) { + default: + lg_warn("invalid chip requested, defaulting to LG2160"); + /* fall-thru */ + case LG2160: + memcpy(&state->frontend.ops, &lg2160_ops, + sizeof(struct dvb_frontend_ops)); + break; + case LG2161: + memcpy(&state->frontend.ops, &lg2161_ops, + sizeof(struct dvb_frontend_ops)); + break; + } + + state->frontend.demodulator_priv = state; + state->current_frequency = -1; + /* parade 1 by default */ + state->frontend.dtv_property_cache.atscmh_parade_id = 1; + + return &state->frontend; +fail: + lg_warn("unable to detect LG216x hardware\n"); + kfree(state); + return NULL; +} +EXPORT_SYMBOL(lg2160_attach); + +MODULE_DESCRIPTION("LG Electronics LG216x ATSC/MH Demodulator Driver"); +MODULE_AUTHOR("Michael Krufky "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.3"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/frontends/lg2160.h b/drivers/media/dvb/frontends/lg2160.h new file mode 100644 index 000000000000..9e2c0f41199a --- /dev/null +++ b/drivers/media/dvb/frontends/lg2160.h @@ -0,0 +1,84 @@ +/* + * Support for LG2160 - ATSC/MH + * + * Copyright (C) 2010 Michael Krufky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _LG2160_H_ +#define _LG2160_H_ + +#include +#include "dvb_frontend.h" + +enum lg_chip_type { + LG2160 = 0, + LG2161 = 1, +}; + +#define LG2161_1019 LG2161 +#define LG2161_1040 LG2161 + +enum lg2160_spi_clock { + LG2160_SPI_3_125_MHZ = 0, + LG2160_SPI_6_25_MHZ = 1, + LG2160_SPI_12_5_MHZ = 2, +}; + +#if 0 +enum lg2161_oif { + LG2161_OIF_EBI2_SLA = 1, + LG2161_OIF_SDIO_SLA = 2, + LG2161_OIF_SPI_SLA = 3, + LG2161_OIF_SPI_MAS = 4, + LG2161_OIF_SERIAL_TS = 7, +}; +#endif + +struct lg2160_config { + u8 i2c_addr; + + /* user defined IF frequency in KHz */ + u16 if_khz; + + /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ + int deny_i2c_rptr:1; + + /* spectral inversion - 0:disabled 1:enabled */ + int spectral_inversion:1; + + unsigned int output_if; + enum lg2160_spi_clock spi_clock; + enum lg_chip_type lg_chip; +}; + +#if defined(CONFIG_DVB_LG2160) || (defined(CONFIG_DVB_LG2160_MODULE) && \ + defined(MODULE)) +extern +struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, + struct i2c_adapter *i2c_adap); +#else +static inline +struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, + struct i2c_adapter *i2c_adap) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LG2160 */ + +#endif /* _LG2160_H_ */ -- cgit v1.2.3 From adabb266efb5a8d36f664ca44f7000063e566671 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 20 May 2012 09:55:06 -0300 Subject: [media] lg2160: Don't fill the legacy DVBv3 ops.type field This field should not be used anymore inside the frontend drivers. The DVB core won't rely on it. Cc: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/lg2160.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/dvb/frontends/lg2160.c b/drivers/media/dvb/frontends/lg2160.c index daa8596b9ec7..fafe522b013f 100644 --- a/drivers/media/dvb/frontends/lg2160.c +++ b/drivers/media/dvb/frontends/lg2160.c @@ -1350,7 +1350,6 @@ static struct dvb_frontend_ops lg2160_ops = { .delsys = { SYS_ATSCMH }, .info = { .name = "LG Electronics LG2160 ATSC/MH Frontend", - .type = FE_ATSC, .frequency_min = 54000000, .frequency_max = 858000000, .frequency_stepsize = 62500, @@ -1379,7 +1378,6 @@ static struct dvb_frontend_ops lg2161_ops = { .delsys = { SYS_ATSCMH }, .info = { .name = "LG Electronics LG2161 ATSC/MH Frontend", - .type = FE_ATSC, .frequency_min = 54000000, .frequency_max = 858000000, .frequency_stepsize = 62500, -- cgit v1.2.3 From 8e156702aadbec4b478f60b9fc6af91990fcd6d0 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 27 Aug 2011 18:46:37 -0300 Subject: [media] dvb-demux: add functionality to send raw payload to the dvr device If your driver needs to deliver the raw payload to userspace without passing through the kernel demux, use function: dvb_dmx_swfilter_raw Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_demux.c | 10 ++++++++++ drivers/media/dvb/dvb-core/dvb_demux.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index faa3671b649e..d82469f842e2 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -568,6 +568,16 @@ void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) } EXPORT_SYMBOL(dvb_dmx_swfilter_204); +void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count) +{ + spin_lock(&demux->lock); + + demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK); + + spin_unlock(&demux->lock); +} +EXPORT_SYMBOL(dvb_dmx_swfilter_raw); + static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux) { int i; diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h index a7d876fd02dd..fa7188a253aa 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.h +++ b/drivers/media/dvb/dvb-core/dvb_demux.h @@ -145,5 +145,7 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *dvbdmx, const u8 *buf, void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count); void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count); +void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, + size_t count); #endif /* _DVB_DEMUX_H_ */ -- cgit v1.2.3 From 379aa4d0c09ea1786b58089da5bbb915ba976ec0 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 8 Sep 2011 04:47:20 -0300 Subject: [media] dvb-usb: add support for dvb-usb-adapters that deliver raw payload Select this feature setting the dvb-usb-adapter caps field with DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dvb-usb-urb.c | 12 ++++++++++++ drivers/media/dvb/dvb-usb/dvb-usb.h | 1 + 2 files changed, 13 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c index 53a5c30b51b2..5c8f651344fc 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c @@ -80,6 +80,14 @@ static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buffer dvb_dmx_swfilter_204(&adap->demux, buffer, length); } +static void dvb_usb_data_complete_raw(struct usb_data_stream *stream, + u8 *buffer, size_t length) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB) + dvb_dmx_swfilter_raw(&adap->demux, buffer, length); +} + int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap) { int i, ret = 0; @@ -90,6 +98,10 @@ int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap) adap->fe_adap[i].stream.complete = dvb_usb_data_complete_204; else + if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD) + adap->fe_adap[i].stream.complete = + dvb_usb_data_complete_raw; + else adap->fe_adap[i].stream.complete = dvb_usb_data_complete; adap->fe_adap[i].stream.user_priv = adap; ret = usb_urb_init(&adap->fe_adap[i].stream, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 6d7d13f9ce68..86cfa86d2d23 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -141,6 +141,7 @@ struct dvb_usb_adapter_fe_properties { #define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02 #define DVB_USB_ADAP_NEED_PID_FILTERING 0x04 #define DVB_USB_ADAP_RECEIVES_204_BYTE_TS 0x08 +#define DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD 0x10 int caps; int pid_filter_count; -- cgit v1.2.3 From 52660885e5455b477ab2d9f620cadb34b2cae7cf Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 29 Jan 2012 15:53:12 -0300 Subject: [media] dvb-usb: increase MAX_NO_OF_FE_PER_ADAP from 2 to 3 The following patch adds support for a third frontend to exist on a single DVB adapter, in the mxl111sf driver. This patch allows that to be possible. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dvb-usb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 86cfa86d2d23..99f94409efa1 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -157,7 +157,7 @@ struct dvb_usb_adapter_fe_properties { int size_of_priv; }; -#define MAX_NO_OF_FE_PER_ADAP 2 +#define MAX_NO_OF_FE_PER_ADAP 3 struct dvb_usb_adapter_properties { int size_of_priv; -- cgit v1.2.3 From 311362149230cf31e0ac1b20bca4a03a2623ca89 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 29 Jan 2012 15:53:55 -0300 Subject: [media] mxl111sf: add ATSC-MH support Add support for the ATSC-MH frontend on the WinTV Aero-m Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/Kconfig | 1 + drivers/media/dvb/dvb-usb/mxl111sf.c | 871 +++++++++++++++++++++++++++++++++-- 2 files changed, 825 insertions(+), 47 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index be1db75091ff..a26949336b3d 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -409,6 +409,7 @@ config DVB_USB_MXL111SF tristate "MxL111SF DTV USB2.0 support" depends on DVB_USB select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select DVB_LG2160 if !DVB_FE_CUSTOMISE select VIDEO_TVEEPROM help Say Y here to support the MxL111SF USB2.0 DTV receiver. diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c index 91834c583580..cd842798f5af 100644 --- a/drivers/media/dvb/dvb-usb/mxl111sf.c +++ b/drivers/media/dvb/dvb-usb/mxl111sf.c @@ -21,6 +21,7 @@ #include "mxl111sf-tuner.h" #include "lgdt3305.h" +#include "lg2160.h" int dvb_usb_mxl111sf_debug; module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644); @@ -31,6 +32,10 @@ int dvb_usb_mxl111sf_isoc; module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644); MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc)."); +int dvb_usb_mxl111sf_spi; +module_param_named(spi, dvb_usb_mxl111sf_spi, int, 0644); +MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi)."); + #define ANT_PATH_AUTO 0 #define ANT_PATH_EXTERNAL 1 #define ANT_PATH_INTERNAL 2 @@ -360,6 +365,33 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return ret; } +static int mxl111sf_ep5_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int ret = 0; + + deb_info("%s(%d)\n", __func__, onoff); + + if (onoff) { + ret = mxl111sf_enable_usb_output(state); + mxl_fail(ret); + + ret = mxl111sf_init_i2s_port(state, 200); + mxl_fail(ret); + ret = mxl111sf_config_i2s(state, 0, 15); + mxl_fail(ret); + } else { + ret = mxl111sf_disable_i2s_port(state); + mxl_fail(ret); + } + if (state->chip_rev > MXL111SF_V6) + ret = mxl111sf_config_spi(state, onoff); + mxl_fail(ret); + + return ret; +} + static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { struct dvb_usb_device *d = adap->dev; @@ -452,6 +484,255 @@ fail: return ret; } +static struct lg2160_config hauppauge_lg2160_config = { + .lg_chip = LG2160, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, +}; + +static int mxl111sf_lg2160_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_MH; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + goto fail; + + adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach, + &hauppauge_lg2160_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[fe_id].fe) { + adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; + adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; + adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static struct lg2160_config hauppauge_lg2161_1019_config = { + .lg_chip = LG2161_1019, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 2, /* LG2161_OIF_SPI_MAS */ +}; + +static struct lg2160_config hauppauge_lg2161_1040_config = { + .lg_chip = LG2161_1040, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 4, /* LG2161_OIF_SPI_MAS */ +}; + +static int mxl111sf_lg2161_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_MH; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + goto fail; + + adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach, + (MXL111SF_V8_200 == state->chip_rev) ? + &hauppauge_lg2161_1040_config : + &hauppauge_lg2161_1019_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[fe_id].fe) { + adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; + adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; + adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static struct lg2160_config hauppauge_lg2161_1019_ep6_config = { + .lg_chip = LG2161_1019, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 1, /* LG2161_OIF_SERIAL_TS */ +}; + +static struct lg2160_config hauppauge_lg2161_1040_ep6_config = { + .lg_chip = LG2161_1040, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 7, /* LG2161_OIF_SERIAL_TS */ +}; + +static int mxl111sf_lg2161_ep6_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_MH; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 0; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + goto fail; + + adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach, + (MXL111SF_V8_200 == state->chip_rev) ? + &hauppauge_lg2161_1040_ep6_config : + &hauppauge_lg2161_1019_ep6_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[fe_id].fe) { + adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; + adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; + adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + static struct mxl111sf_demod_config mxl_demod_config = { .read_reg = mxl111sf_read_reg, .write_reg = mxl111sf_write_reg, @@ -649,6 +930,18 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties; static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties; static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties; static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_atsc_mh_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_atsc_mh_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mh_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mh_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_spi_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_spi_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_tp_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_tp_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_isoc_properties; static int mxl111sf_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -663,12 +956,50 @@ static int mxl111sf_probe(struct usb_interface *intf, THIS_MODULE, &d, adapter_nr) || 0 == dvb_usb_device_init(intf, &mxl111sf_atsc_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_atsc_mh_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mh_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + ((dvb_usb_mxl111sf_spi) && + (0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_spi_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_mh_spi_isoc_properties, + THIS_MODULE, &d, adapter_nr))) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_tp_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_mh_tp_isoc_properties, THIS_MODULE, &d, adapter_nr))) || 0 == dvb_usb_device_init(intf, &mxl111sf_dvbt_bulk_properties, THIS_MODULE, &d, adapter_nr) || 0 == dvb_usb_device_init(intf, &mxl111sf_atsc_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_atsc_mh_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mh_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + ((dvb_usb_mxl111sf_spi) && + (0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_spi_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_mh_spi_bulk_properties, + THIS_MODULE, &d, adapter_nr))) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_tp_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_mh_tp_bulk_properties, THIS_MODULE, &d, adapter_nr) || 0) { struct mxl111sf_state *state = d->priv; @@ -786,13 +1117,13 @@ MODULE_DEVICE_TABLE(usb, mxl111sf_table); } \ } -#define MXL111SF_EP6_BULK_STREAMING_CONFIG \ +#define MXL111SF_EP5_BULK_STREAMING_CONFIG \ .size_of_priv = sizeof(struct mxl111sf_adap_state), \ - .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ + .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, \ .stream = { \ .type = USB_BULK, \ .count = 5, \ - .endpoint = 0x06, \ + .endpoint = 0x05, \ .u = { \ .bulk = { \ .buffersize = 8192, \ @@ -800,25 +1131,55 @@ MODULE_DEVICE_TABLE(usb, mxl111sf_table); } \ } -/* FIXME */ -#define MXL111SF_EP6_ISOC_STREAMING_CONFIG \ +#define MXL111SF_EP5_ISOC_STREAMING_CONFIG \ .size_of_priv = sizeof(struct mxl111sf_adap_state), \ - .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ + .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, \ .stream = { \ .type = USB_ISOC, \ .count = 5, \ - .endpoint = 0x06, \ + .endpoint = 0x05, \ .u = { \ .isoc = { \ - .framesperurb = 24, \ - .framesize = 3072, \ + .framesperurb = 96, \ + .framesize = 200, \ .interval = 1, \ } \ } \ } -#define MXL111SF_DEFAULT_DEVICE_PROPERTIES \ - .caps = DVB_USB_IS_AN_I2C_ADAPTER, \ +#define MXL111SF_EP6_BULK_STREAMING_CONFIG \ + .size_of_priv = sizeof(struct mxl111sf_adap_state), \ + .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ + .stream = { \ + .type = USB_BULK, \ + .count = 5, \ + .endpoint = 0x06, \ + .u = { \ + .bulk = { \ + .buffersize = 8192, \ + } \ + } \ + } + +/* FIXME */ +#define MXL111SF_EP6_ISOC_STREAMING_CONFIG \ + .size_of_priv = sizeof(struct mxl111sf_adap_state), \ + .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ + .stream = { \ + .type = USB_ISOC, \ + .count = 5, \ + .endpoint = 0x06, \ + .u = { \ + .isoc = { \ + .framesperurb = 24, \ + .framesize = 3072, \ + .interval = 1, \ + } \ + } \ + } + +#define MXL111SF_DEFAULT_DEVICE_PROPERTIES \ + .caps = DVB_USB_IS_AN_I2C_ADAPTER, \ .usb_ctrl = DEVICE_SPECIFIC, \ /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), \ EP6 BULK transfer (atsc/qam), \ @@ -847,7 +1208,7 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = { } }, }, }, - .num_device_descs = 4, + .num_device_descs = 3, .devices = { { "Hauppauge 126xxx DVBT (bulk)", { NULL }, @@ -865,11 +1226,6 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = { &mxl111sf_table[24], &mxl111sf_table[26], NULL }, }, - { "Hauppauge 126xxx (tp-bulk)", - { NULL }, - { &mxl111sf_table[28], &mxl111sf_table[30], - NULL }, - }, } }; @@ -889,7 +1245,7 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = { } }, }, }, - .num_device_descs = 4, + .num_device_descs = 3, .devices = { { "Hauppauge 126xxx DVBT (isoc)", { NULL }, @@ -907,11 +1263,6 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = { &mxl111sf_table[24], &mxl111sf_table[26], NULL }, }, - { "Hauppauge 126xxx (tp-isoc)", - { NULL }, - { &mxl111sf_table[28], &mxl111sf_table[30], - NULL }, - }, } }; @@ -922,33 +1273,159 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { .adapter = { { .fe_ioctl_override = mxl111sf_fe_ioctl_override, - .num_frontends = 2, + .num_frontends = 1, .fe = {{ .frontend_attach = mxl111sf_lgdt3305_frontend_attach, .tuner_attach = mxl111sf_attach_tuner, MXL111SF_EP6_BULK_STREAMING_CONFIG, + }}, }, + }, + .num_device_descs = 2, + .devices = { + { "Hauppauge 126xxx ATSC (bulk)", + { NULL }, + { &mxl111sf_table[1], &mxl111sf_table[5], + NULL }, + }, + { "Hauppauge 117xxx ATSC (bulk)", + { NULL }, + { &mxl111sf_table[12], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { { - .frontend_attach = mxl111sf_attach_demod, + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 1, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, .tuner_attach = mxl111sf_attach_tuner, - MXL111SF_EP4_BULK_STREAMING_CONFIG, + MXL111SF_EP6_ISOC_STREAMING_CONFIG, }}, }, }, - .num_device_descs = 6, + .num_device_descs = 2, .devices = { - { "Hauppauge 126xxx ATSC (bulk)", + { "Hauppauge 126xxx ATSC (isoc)", { NULL }, { &mxl111sf_table[1], &mxl111sf_table[5], NULL }, }, - { "Hauppauge 117xxx ATSC (bulk)", + { "Hauppauge 117xxx ATSC (isoc)", { NULL }, { &mxl111sf_table[12], NULL }, }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_mh_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2160_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "HCW 126xxx (bulk)", + { NULL }, + { &mxl111sf_table[2], &mxl111sf_table[6], + NULL }, + }, + { "HCW 117xxx (bulk)", + { NULL }, + { &mxl111sf_table[13], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_mh_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2160_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "HCW 126xxx (isoc)", + { NULL }, + { &mxl111sf_table[2], &mxl111sf_table[6], + NULL }, + }, + { "HCW 117xxx (isoc)", + { NULL }, + { &mxl111sf_table[13], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_atsc_mh_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2160_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { { "Hauppauge 126xxx ATSC+ (bulk)", { NULL }, { &mxl111sf_table[0], &mxl111sf_table[3], @@ -962,13 +1439,96 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { &mxl111sf_table[32], &mxl111sf_table[33], NULL }, }, - { "Hauppauge Mercury (tp-bulk)", + } +}; + +static struct dvb_usb_device_properties mxl111sf_atsc_mh_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2160_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "Hauppauge 126xxx ATSC+ (isoc)", + { NULL }, + { &mxl111sf_table[0], &mxl111sf_table[3], + &mxl111sf_table[7], &mxl111sf_table[9], + &mxl111sf_table[10], NULL }, + }, + { "Hauppauge 117xxx ATSC+ (isoc)", + { NULL }, + { &mxl111sf_table[11], &mxl111sf_table[14], + &mxl111sf_table[16], &mxl111sf_table[17], + &mxl111sf_table[32], &mxl111sf_table[33], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_mercury_spi_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "Hauppauge Mercury (spi-bulk)", { NULL }, { &mxl111sf_table[19], &mxl111sf_table[21], &mxl111sf_table[23], &mxl111sf_table[25], - &mxl111sf_table[27], NULL }, + NULL }, }, - { "Hauppauge WinTV-Aero-M", + { "Hauppauge WinTV-Aero-M (spi-bulk)", { NULL }, { &mxl111sf_table[29], &mxl111sf_table[31], NULL }, @@ -976,14 +1536,14 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { } }; -static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { +static struct dvb_usb_device_properties mxl111sf_mercury_spi_isoc_properties = { MXL111SF_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, .adapter = { { .fe_ioctl_override = mxl111sf_fe_ioctl_override, - .num_frontends = 2, + .num_frontends = 3, .fe = {{ .frontend_attach = mxl111sf_lgdt3305_frontend_attach, .tuner_attach = mxl111sf_attach_tuner, @@ -995,34 +1555,111 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { .tuner_attach = mxl111sf_attach_tuner, MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_ISOC_STREAMING_CONFIG, }}, }, }, - .num_device_descs = 6, + .num_device_descs = 2, .devices = { - { "Hauppauge 126xxx ATSC (isoc)", + { "Hauppauge Mercury (spi-isoc)", { NULL }, - { &mxl111sf_table[1], &mxl111sf_table[5], + { &mxl111sf_table[19], &mxl111sf_table[21], + &mxl111sf_table[23], &mxl111sf_table[25], NULL }, }, - { "Hauppauge 117xxx ATSC (isoc)", + { "Hauppauge WinTV-Aero-M (spi-isoc)", { NULL }, - { &mxl111sf_table[12], + { &mxl111sf_table[29], &mxl111sf_table[31], NULL }, }, - { "Hauppauge 126xxx ATSC+ (isoc)", + } +}; + +static struct dvb_usb_device_properties mxl111sf_mercury_tp_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "Hauppauge Mercury (tp-bulk)", { NULL }, - { &mxl111sf_table[0], &mxl111sf_table[3], - &mxl111sf_table[7], &mxl111sf_table[9], - &mxl111sf_table[10], NULL }, + { &mxl111sf_table[19], &mxl111sf_table[21], + &mxl111sf_table[23], &mxl111sf_table[25], + &mxl111sf_table[27], NULL }, }, - { "Hauppauge 117xxx ATSC+ (isoc)", + { "Hauppauge WinTV-Aero-M", { NULL }, - { &mxl111sf_table[11], &mxl111sf_table[14], - &mxl111sf_table[16], &mxl111sf_table[17], - &mxl111sf_table[32], &mxl111sf_table[33], + { &mxl111sf_table[29], &mxl111sf_table[31], NULL }, }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_mercury_tp_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { { "Hauppauge Mercury (tp-isoc)", { NULL }, { &mxl111sf_table[19], &mxl111sf_table[21], @@ -1037,6 +1674,146 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { } }; +static +struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 1, + .devices = { + { "Hauppauge 126xxx (tp-bulk)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static +struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 1, + .devices = { + { "Hauppauge 126xxx (tp-isoc)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static +struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 1, + .devices = { + { "Hauppauge 126xxx (spi-bulk)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static +struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 1, + .devices = { + { "Hauppauge 126xxx (spi-isoc)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + static struct usb_driver mxl111sf_driver = { .name = "dvb_usb_mxl111sf", .probe = mxl111sf_probe, -- cgit v1.2.3 From 10d67371fc6e7d4e3b869166843ba174763fe5aa Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 14 May 2012 18:50:34 -0300 Subject: [media] DVB: remove "stats" property bits from ATSC-MH API property additions Mauro is proposing a new API to handle statistics. This functionality will be returned after the statistics API is ready. Just remove them for now. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/dvb/dvbproperty.xml | 18 ------------------ drivers/media/dvb/dvb-core/dvb_frontend.c | 12 ------------ drivers/media/dvb/dvb-core/dvb_frontend.h | 4 ---- drivers/media/dvb/frontends/lg2160.c | 9 ++++++++- include/linux/dvb/frontend.h | 5 +---- 5 files changed, 9 insertions(+), 39 deletions(-) diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index d63153522b60..e633c097a8d1 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -664,21 +664,6 @@ typedef enum atscmh_sccc_code_mode { } atscmh_sccc_code_mode_t;
-
- <constant>DTV_ATSCMH_FIC_ERR</constant> - FIC error count. - Possible values: 0, 1, 2, 3, ..., 0xffff -
-
- <constant>DTV_ATSCMH_CRC_ERR</constant> - CRC error count. - Possible values: 0, 1, 2, 3, ..., 0xffff -
-
- <constant>DTV_ATSCMH_RS_ERR</constant> - RS error count. - Possible values: 0, 1, 2, 3, ..., 0xffff -
<constant>DTV_API_VERSION</constant> @@ -947,9 +932,6 @@ typedef enum fe_hierarchy { DTV_ATSCMH_SCCC_CODE_MODE_B DTV_ATSCMH_SCCC_CODE_MODE_C DTV_ATSCMH_SCCC_CODE_MODE_D - DTV_ATSCMH_FIC_ERR - DTV_ATSCMH_CRC_ERR - DTV_ATSCMH_RS_ERR
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index cd23f303162a..aebcdf221dda 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1049,9 +1049,6 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = { _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0), _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0), _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0), - _DTV_CMD(DTV_ATSCMH_FIC_ERR, 0, 0), - _DTV_CMD(DTV_ATSCMH_CRC_ERR, 0, 0), - _DTV_CMD(DTV_ATSCMH_RS_ERR, 0, 0), }; static void dtv_property_dump(struct dtv_property *tvp) @@ -1438,15 +1435,6 @@ static int dtv_property_process_get(struct dvb_frontend *fe, case DTV_ATSCMH_SCCC_CODE_MODE_D: tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_d; break; - case DTV_ATSCMH_FIC_ERR: - tvp->u.data = fe->dtv_property_cache.atscmh_fic_err; - break; - case DTV_ATSCMH_CRC_ERR: - tvp->u.data = fe->dtv_property_cache.atscmh_crc_err; - break; - case DTV_ATSCMH_RS_ERR: - tvp->u.data = fe->dtv_property_cache.atscmh_rs_err; - break; default: return -EINVAL; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index 80f5c27ddc9f..e929d5697b87 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -390,10 +390,6 @@ struct dtv_frontend_properties { u8 atscmh_sccc_code_mode_b; u8 atscmh_sccc_code_mode_c; u8 atscmh_sccc_code_mode_d; - - u16 atscmh_fic_err; - u16 atscmh_crc_err; - u16 atscmh_rs_err; }; struct dvb_frontend { diff --git a/drivers/media/dvb/frontends/lg2160.c b/drivers/media/dvb/frontends/lg2160.c index fafe522b013f..7bc284217527 100644 --- a/drivers/media/dvb/frontends/lg2160.c +++ b/drivers/media/dvb/frontends/lg2160.c @@ -804,6 +804,7 @@ fail: /* ------------------------------------------------------------------------ */ +#if 0 static int lg216x_read_fic_err_count(struct lg216x_state *state, u8 *err) { u8 fic_err; @@ -936,6 +937,7 @@ static int lg216x_read_rs_err_count(struct lg216x_state *state, u16 *err) } return ret; } +#endif /* ------------------------------------------------------------------------ */ @@ -1016,6 +1018,7 @@ static int lg216x_get_frontend(struct dvb_frontend *fe) if (lg_fail(ret)) goto fail; } +#if 0 ret = lg216x_read_fic_err_count(state, (u8 *)&fe->dtv_property_cache.atscmh_fic_err); if (lg_fail(ret)) @@ -1042,6 +1045,7 @@ static int lg216x_get_frontend(struct dvb_frontend *fe) break; } lg_fail(ret); +#endif fail: return ret; } @@ -1319,13 +1323,16 @@ static int lg216x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { struct lg216x_state *state = fe->demodulator_priv; int ret; - +#if 0 ret = lg216x_read_rs_err_count(state, &fe->dtv_property_cache.atscmh_rs_err); if (lg_fail(ret)) goto fail; *ucblocks = fe->dtv_property_cache.atscmh_rs_err; +#else + *ucblocks = 0; +#endif fail: return 0; } diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index 5aedd5ae7f8f..f50d4058c5fb 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -336,11 +336,8 @@ struct dvb_frontend_event { #define DTV_ATSCMH_SCCC_CODE_MODE_B 57 #define DTV_ATSCMH_SCCC_CODE_MODE_C 58 #define DTV_ATSCMH_SCCC_CODE_MODE_D 59 -#define DTV_ATSCMH_FIC_ERR 60 -#define DTV_ATSCMH_CRC_ERR 61 -#define DTV_ATSCMH_RS_ERR 62 -#define DTV_MAX_COMMAND DTV_ATSCMH_RS_ERR +#define DTV_MAX_COMMAND DTV_ATSCMH_SCCC_CODE_MODE_D typedef enum fe_pilot { PILOT_ON, -- cgit v1.2.3 From 5444a1b76b3eb7e0acdaece07cfe2e2a4305bb77 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 20 May 2012 10:13:23 -0300 Subject: [media] lg2160: Fix a few warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/dvb/frontends/lg2160.c: In function ‘lg216x_read_ucblocks’: drivers/media/dvb/frontends/lg2160.c:1336:1: warning: label ‘fail’ defined but not used [-Wunused-label] drivers/media/dvb/frontends/lg2160.c:1325:6: warning: unused variable ‘ret’ [-Wunused-variable] drivers/media/dvb/frontends/lg2160.c:1324:23: warning: unused variable ‘state’ [-Wunused-variable] drivers/media/dvb/frontends/lg2160.c: In function ‘lg216x_set_ensemble’: drivers/media/dvb/frontends/lg2160.c:420:23: warning: ‘reg’ may be used uninitialized in this function [-Wuninitialized] Cc: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/lg2160.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/lg2160.c b/drivers/media/dvb/frontends/lg2160.c index 7bc284217527..a3ab1a5b6597 100644 --- a/drivers/media/dvb/frontends/lg2160.c +++ b/drivers/media/dvb/frontends/lg2160.c @@ -413,6 +413,7 @@ static int lg216x_set_ensemble(struct lg216x_state *state, int id) reg = 0x0400; break; case LG2161: + default: reg = 0x0500; break; } @@ -1321,19 +1322,20 @@ fail: static int lg216x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { +#if 0 struct lg216x_state *state = fe->demodulator_priv; int ret; -#if 0 + ret = lg216x_read_rs_err_count(state, &fe->dtv_property_cache.atscmh_rs_err); if (lg_fail(ret)) goto fail; *ucblocks = fe->dtv_property_cache.atscmh_rs_err; +fail: #else *ucblocks = 0; #endif -fail: return 0; } -- cgit v1.2.3 From fee5fcf6b39dd0e690283a592803efae09ddb636 Mon Sep 17 00:00:00 2001 From: Hans-Frieder Vogt Date: Sun, 6 May 2012 16:56:50 -0300 Subject: [media] fc001x: common header file for FC0012 and FC0013 Common defines for the FC0012 (v0.5) and FC0013 tuner drivers Signed-off-by: Hans-Frieder Vogt Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/fc001x-common.h | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 drivers/media/common/tuners/fc001x-common.h diff --git a/drivers/media/common/tuners/fc001x-common.h b/drivers/media/common/tuners/fc001x-common.h new file mode 100644 index 000000000000..718818156934 --- /dev/null +++ b/drivers/media/common/tuners/fc001x-common.h @@ -0,0 +1,39 @@ +/* + * Fitipower FC0012 & FC0013 tuner driver - common defines + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _FC001X_COMMON_H_ +#define _FC001X_COMMON_H_ + +enum fc001x_xtal_freq { + FC_XTAL_27_MHZ, /* 27000000 */ + FC_XTAL_28_8_MHZ, /* 28800000 */ + FC_XTAL_36_MHZ, /* 36000000 */ +}; + +/* + * enum fc001x_fe_callback_commands - Frontend callbacks + * + * @FC_FE_CALLBACK_VHF_ENABLE: enable VHF or UHF + */ +enum fc001x_fe_callback_commands { + FC_FE_CALLBACK_VHF_ENABLE, +}; + +#endif -- cgit v1.2.3 From ef89ec7ecd68f61c67155c1b70d7b1e7176875f0 Mon Sep 17 00:00:00 2001 From: Hans-Frieder Vogt Date: Sun, 6 May 2012 16:56:55 -0300 Subject: [media] fc001x: tuner driver for FC0012, version 0.5 Support for tuner Fitipower FC0012 Signed-off-by: Hans-Frieder Vogt Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/Kconfig | 7 + drivers/media/common/tuners/Makefile | 1 + drivers/media/common/tuners/fc0012-priv.h | 43 ++++ drivers/media/common/tuners/fc0012.c | 397 ++++++++++++++++++++++++++++++ drivers/media/common/tuners/fc0012.h | 44 ++++ 5 files changed, 492 insertions(+) create mode 100644 drivers/media/common/tuners/fc0012-priv.h create mode 100644 drivers/media/common/tuners/fc0012.c create mode 100644 drivers/media/common/tuners/fc0012.h diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 0fd15d925e15..851825145622 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -211,6 +211,13 @@ config MEDIA_TUNER_FC0011 help Fitipower FC0011 silicon tuner driver. +config MEDIA_TUNER_FC0012 + tristate "Fitipower FC0012 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0012 silicon tuner driver. + config MEDIA_TUNER_TDA18212 tristate "NXP TDA18212 silicon tuner" depends on VIDEO_MEDIA && I2C diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 64ee06fa83f1..f04610683404 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o +obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/fc0012-priv.h b/drivers/media/common/tuners/fc0012-priv.h new file mode 100644 index 000000000000..4577c917e616 --- /dev/null +++ b/drivers/media/common/tuners/fc0012-priv.h @@ -0,0 +1,43 @@ +/* + * Fitipower FC0012 tuner driver - private includes + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _FC0012_PRIV_H_ +#define _FC0012_PRIV_H_ + +#define LOG_PREFIX "fc0012" + +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +struct fc0012_priv { + struct i2c_adapter *i2c; + u8 addr; + u8 dual_master; + u8 xtal_freq; + + u32 frequency; + u32 bandwidth; +}; + +#endif diff --git a/drivers/media/common/tuners/fc0012.c b/drivers/media/common/tuners/fc0012.c new file mode 100644 index 000000000000..cd58f90d7d13 --- /dev/null +++ b/drivers/media/common/tuners/fc0012.c @@ -0,0 +1,397 @@ +/* + * Fitipower FC0012 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "fc0012.h" +#include "fc0012-priv.h" + +static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + return -EREMOTEIO; + } + return 0; +} + +static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + err("I2C read reg failed, reg: %02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int fc0012_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int fc0012_init(struct dvb_frontend *fe) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int i, ret = 0; + unsigned char reg[] = { + 0x00, /* dummy reg. 0 */ + 0x05, /* reg. 0x01 */ + 0x10, /* reg. 0x02 */ + 0x00, /* reg. 0x03 */ + 0x00, /* reg. 0x04 */ + 0x0f, /* reg. 0x05: may also be 0x0a */ + 0x00, /* reg. 0x06: divider 2, VCO slow */ + 0x00, /* reg. 0x07: may also be 0x0f */ + 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, + Loop Bw 1/8 */ + 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ + 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ + 0x82, /* reg. 0x0b: Output Clock is same as clock frequency, + may also be 0x83 */ + 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ + 0x02, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */ + 0x00, /* reg. 0x0e */ + 0x00, /* reg. 0x0f */ + 0x00, /* reg. 0x10: may also be 0x0d */ + 0x00, /* reg. 0x11 */ + 0x1f, /* reg. 0x12: Set to maximum gain */ + 0x08, /* reg. 0x13: Set to Middle Gain: 0x08, + Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */ + 0x00, /* reg. 0x14 */ + 0x04, /* reg. 0x15: Enable LNA COMPS */ + }; + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + case FC_XTAL_28_8_MHZ: + reg[0x07] |= 0x20; + break; + case FC_XTAL_36_MHZ: + default: + break; + } + + if (priv->dual_master) + reg[0x0c] |= 0x02; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i < sizeof(reg); i++) { + ret = fc0012_writereg(priv, i, reg[i]); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + err("fc0012_writereg failed: %d", ret); + + return ret; +} + +static int fc0012_sleep(struct dvb_frontend *fe) +{ + /* nothing to do here */ + return 0; +} + +static int fc0012_set_params(struct dvb_frontend *fe) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int i, ret = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 freq = p->frequency / 1000; + u32 delsys = p->delivery_system; + unsigned char reg[7], am, pm, multi, tmp; + unsigned long f_vco; + unsigned short xtal_freq_khz_2, xin, xdiv; + int vco_select = false; + + if (fe->callback) { + ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); + if (ret) + goto exit; + } + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + xtal_freq_khz_2 = 27000 / 2; + break; + case FC_XTAL_36_MHZ: + xtal_freq_khz_2 = 36000 / 2; + break; + case FC_XTAL_28_8_MHZ: + default: + xtal_freq_khz_2 = 28800 / 2; + break; + } + + /* select frequency divider and the frequency of VCO */ + if (freq < 37084) { /* freq * 96 < 3560000 */ + multi = 96; + reg[5] = 0x82; + reg[6] = 0x00; + } else if (freq < 55625) { /* freq * 64 < 3560000 */ + multi = 64; + reg[5] = 0x82; + reg[6] = 0x02; + } else if (freq < 74167) { /* freq * 48 < 3560000 */ + multi = 48; + reg[5] = 0x42; + reg[6] = 0x00; + } else if (freq < 111250) { /* freq * 32 < 3560000 */ + multi = 32; + reg[5] = 0x42; + reg[6] = 0x02; + } else if (freq < 148334) { /* freq * 24 < 3560000 */ + multi = 24; + reg[5] = 0x22; + reg[6] = 0x00; + } else if (freq < 222500) { /* freq * 16 < 3560000 */ + multi = 16; + reg[5] = 0x22; + reg[6] = 0x02; + } else if (freq < 296667) { /* freq * 12 < 3560000 */ + multi = 12; + reg[5] = 0x12; + reg[6] = 0x00; + } else if (freq < 445000) { /* freq * 8 < 3560000 */ + multi = 8; + reg[5] = 0x12; + reg[6] = 0x02; + } else if (freq < 593334) { /* freq * 6 < 3560000 */ + multi = 6; + reg[5] = 0x0a; + reg[6] = 0x00; + } else { + multi = 4; + reg[5] = 0x0a; + reg[6] = 0x02; + } + + f_vco = freq * multi; + + if (f_vco >= 3060000) { + reg[6] |= 0x08; + vco_select = true; + } + + if (freq >= 45000) { + /* From divided value (XDIV) determined the FA and FP value */ + xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); + if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) + xdiv++; + + pm = (unsigned char)(xdiv / 8); + am = (unsigned char)(xdiv - (8 * pm)); + + if (am < 2) { + reg[1] = am + 8; + reg[2] = pm - 1; + } else { + reg[1] = am; + reg[2] = pm; + } + } else { + /* fix for frequency less than 45 MHz */ + reg[1] = 0x06; + reg[2] = 0x11; + } + + /* fix clock out */ + reg[6] |= 0x20; + + /* From VCO frequency determines the XIN ( fractional part of Delta + Sigma PLL) and divided value (XDIV) */ + xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); + xin = (xin << 15) / xtal_freq_khz_2; + if (xin >= 16384) + xin += 32768; + + reg[3] = xin >> 8; /* xin with 9 bit resolution */ + reg[4] = xin & 0xff; + + if (delsys == SYS_DVBT) { + reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ + switch (p->bandwidth_hz) { + case 6000000: + reg[6] |= 0x80; + break; + case 7000000: + reg[6] |= 0x40; + break; + case 8000000: + default: + break; + } + } else { + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + + /* modified for Realtek demod */ + reg[5] |= 0x07; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i <= 6; i++) { + ret = fc0012_writereg(priv, i, reg[i]); + if (ret) + goto exit; + } + + /* VCO Calibration */ + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + + /* VCO Re-Calibration if needed */ + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + + if (!ret) { + msleep(10); + ret = fc0012_readreg(priv, 0x0e, &tmp); + } + if (ret) + goto exit; + + /* vco selection */ + tmp &= 0x3f; + + if (vco_select) { + if (tmp > 0x3c) { + reg[6] &= ~0x08; + ret = fc0012_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + } + } else { + if (tmp < 0x02) { + reg[6] |= 0x08; + ret = fc0012_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + } + } + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0012_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + /* CHECK: always ? */ + *frequency = 0; + return 0; +} + +static int fc0012_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0012_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + + +static const struct dvb_tuner_ops fc0012_tuner_ops = { + .info = { + .name = "Fitipower FC0012", + + .frequency_min = 37000000, /* estimate */ + .frequency_max = 862000000, /* estimate */ + .frequency_step = 0, + }, + + .release = fc0012_release, + + .init = fc0012_init, + .sleep = fc0012_sleep, + + .set_params = fc0012_set_params, + + .get_frequency = fc0012_get_frequency, + .get_if_frequency = fc0012_get_if_frequency, + .get_bandwidth = fc0012_get_bandwidth, +}; + +struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + struct fc0012_priv *priv = NULL; + + priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + priv->dual_master = dual_master; + priv->addr = i2c_address; + priv->xtal_freq = xtal_freq; + + info("Fitipower FC0012 successfully attached."); + + fe->tuner_priv = priv; + + memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +} +EXPORT_SYMBOL(fc0012_attach); + +MODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver"); +MODULE_AUTHOR("Hans-Frieder Vogt "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.5"); diff --git a/drivers/media/common/tuners/fc0012.h b/drivers/media/common/tuners/fc0012.h new file mode 100644 index 000000000000..4dbd5efe8845 --- /dev/null +++ b/drivers/media/common/tuners/fc0012.h @@ -0,0 +1,44 @@ +/* + * Fitipower FC0012 tuner driver - include + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _FC0012_H_ +#define _FC0012_H_ + +#include "dvb_frontend.h" +#include "fc001x-common.h" + +#if defined(CONFIG_MEDIA_TUNER_FC0012) || \ + (defined(CONFIG_MEDIA_TUNER_FC0012_MODULE) && defined(MODULE)) +extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq); +#else +static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif -- cgit v1.2.3 From e889adc91187de94c0fdfe1c3cb23f6e4da88c2b Mon Sep 17 00:00:00 2001 From: Hans-Frieder Vogt Date: Sun, 6 May 2012 16:57:02 -0300 Subject: [media] fc001x: tuner driver for FC0013 Support for tuner Fitipower FC0013 Signed-off-by: Hans-Frieder Vogt Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/Kconfig | 7 + drivers/media/common/tuners/Makefile | 1 + drivers/media/common/tuners/fc0013-priv.h | 44 +++ drivers/media/common/tuners/fc0013.c | 562 ++++++++++++++++++++++++++++++ drivers/media/common/tuners/fc0013.h | 57 +++ 5 files changed, 671 insertions(+) create mode 100644 drivers/media/common/tuners/fc0013-priv.h create mode 100644 drivers/media/common/tuners/fc0013.c create mode 100644 drivers/media/common/tuners/fc0013.h diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 851825145622..bbf4945149a9 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -218,6 +218,13 @@ config MEDIA_TUNER_FC0012 help Fitipower FC0012 silicon tuner driver. +config MEDIA_TUNER_FC0013 + tristate "Fitipower FC0013 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0013 silicon tuner driver. + config MEDIA_TUNER_TDA18212 tristate "NXP TDA18212 silicon tuner" depends on VIDEO_MEDIA && I2C diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index f04610683404..891b80e60808 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o +obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/fc0013-priv.h b/drivers/media/common/tuners/fc0013-priv.h new file mode 100644 index 000000000000..bfd49dedea22 --- /dev/null +++ b/drivers/media/common/tuners/fc0013-priv.h @@ -0,0 +1,44 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _FC0013_PRIV_H_ +#define _FC0013_PRIV_H_ + +#define LOG_PREFIX "fc0013" + +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +struct fc0013_priv { + struct i2c_adapter *i2c; + u8 addr; + u8 dual_master; + u8 xtal_freq; + + u32 frequency; + u32 bandwidth; +}; + +#endif diff --git a/drivers/media/common/tuners/fc0013.c b/drivers/media/common/tuners/fc0013.c new file mode 100644 index 000000000000..7398d7759e08 --- /dev/null +++ b/drivers/media/common/tuners/fc0013.c @@ -0,0 +1,562 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * partially based on driver code from Fitipower + * Copyright (C) 2010 Fitipower Integrated Technology 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 the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "fc0013.h" +#include "fc0013-priv.h" + +static int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + return -EREMOTEIO; + } + return 0; +} + +static int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + err("I2C read reg failed, reg: %02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int fc0013_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int fc0013_init(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int i, ret = 0; + unsigned char reg[] = { + 0x00, /* reg. 0x00: dummy */ + 0x09, /* reg. 0x01 */ + 0x16, /* reg. 0x02 */ + 0x00, /* reg. 0x03 */ + 0x00, /* reg. 0x04 */ + 0x17, /* reg. 0x05 */ + 0x02, /* reg. 0x06 */ + 0x0a, /* reg. 0x07: CHECK */ + 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, + Loop Bw 1/8 */ + 0x6f, /* reg. 0x09: enable LoopThrough */ + 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ + 0x82, /* reg. 0x0b: CHECK */ + 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ + 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */ + 0x00, /* reg. 0x0e */ + 0x00, /* reg. 0x0f */ + 0x00, /* reg. 0x10 */ + 0x00, /* reg. 0x11 */ + 0x00, /* reg. 0x12 */ + 0x00, /* reg. 0x13 */ + 0x50, /* reg. 0x14: DVB-t High Gain, UHF. + Middle Gain: 0x48, Low Gain: 0x40 */ + 0x01, /* reg. 0x15 */ + }; + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + case FC_XTAL_28_8_MHZ: + reg[0x07] |= 0x20; + break; + case FC_XTAL_36_MHZ: + default: + break; + } + + if (priv->dual_master) + reg[0x0c] |= 0x02; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i < sizeof(reg); i++) { + ret = fc0013_writereg(priv, i, reg[i]); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + err("fc0013_writereg failed: %d", ret); + + return ret; +} + +static int fc0013_sleep(struct dvb_frontend *fe) +{ + /* nothing to do here */ + return 0; +} + +int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + u8 rc_cal; + int val; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* push rc_cal value, get rc_cal value */ + ret = fc0013_writereg(priv, 0x10, 0x00); + if (ret) + goto error_out; + + /* get rc_cal value */ + ret = fc0013_readreg(priv, 0x10, &rc_cal); + if (ret) + goto error_out; + + rc_cal &= 0x0f; + + val = (int)rc_cal + rc_val; + + /* forcing rc_cal */ + ret = fc0013_writereg(priv, 0x0d, 0x11); + if (ret) + goto error_out; + + /* modify rc_cal value */ + if (val > 15) + ret = fc0013_writereg(priv, 0x10, 0x0f); + else if (val < 0) + ret = fc0013_writereg(priv, 0x10, 0x00); + else + ret = fc0013_writereg(priv, 0x10, (u8)val); + +error_out: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + return ret; +} +EXPORT_SYMBOL(fc0013_rc_cal_add); + +int fc0013_rc_cal_reset(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0013_writereg(priv, 0x0d, 0x01); + if (!ret) + ret = fc0013_writereg(priv, 0x10, 0x00); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + return ret; +} +EXPORT_SYMBOL(fc0013_rc_cal_reset); + +static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq) +{ + int ret; + u8 tmp; + + ret = fc0013_readreg(priv, 0x1d, &tmp); + if (ret) + goto error_out; + tmp &= 0xe3; + if (freq <= 177500) { /* VHF Track: 7 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); + } else if (freq <= 184500) { /* VHF Track: 6 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x18); + } else if (freq <= 191500) { /* VHF Track: 5 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x14); + } else if (freq <= 198500) { /* VHF Track: 4 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x10); + } else if (freq <= 205500) { /* VHF Track: 3 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c); + } else if (freq <= 219500) { /* VHF Track: 2 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x08); + } else if (freq < 300000) { /* VHF Track: 1 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x04); + } else { /* UHF and GPS */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); + } + if (ret) + goto error_out; +error_out: + return ret; +} + +static int fc0013_set_params(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int i, ret = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 freq = p->frequency / 1000; + u32 delsys = p->delivery_system; + unsigned char reg[7], am, pm, multi, tmp; + unsigned long f_vco; + unsigned short xtal_freq_khz_2, xin, xdiv; + int vco_select = false; + + if (fe->callback) { + ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); + if (ret) + goto exit; + } + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + xtal_freq_khz_2 = 27000 / 2; + break; + case FC_XTAL_36_MHZ: + xtal_freq_khz_2 = 36000 / 2; + break; + case FC_XTAL_28_8_MHZ: + default: + xtal_freq_khz_2 = 28800 / 2; + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* set VHF track */ + ret = fc0013_set_vhf_track(priv, freq); + if (ret) + goto exit; + + if (freq < 300000) { + /* enable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp | 0x10); + if (ret) + goto exit; + + /* disable UHF & disable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, tmp & 0x1f); + if (ret) + goto exit; + } else if (freq <= 862000) { + /* disable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp & 0xef); + if (ret) + goto exit; + + /* enable UHF & disable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40); + if (ret) + goto exit; + } else { + /* disable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp & 0xef); + if (ret) + goto exit; + + /* disable UHF & enable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20); + if (ret) + goto exit; + } + + /* select frequency divider and the frequency of VCO */ + if (freq < 37084) { /* freq * 96 < 3560000 */ + multi = 96; + reg[5] = 0x82; + reg[6] = 0x00; + } else if (freq < 55625) { /* freq * 64 < 3560000 */ + multi = 64; + reg[5] = 0x02; + reg[6] = 0x02; + } else if (freq < 74167) { /* freq * 48 < 3560000 */ + multi = 48; + reg[5] = 0x42; + reg[6] = 0x00; + } else if (freq < 111250) { /* freq * 32 < 3560000 */ + multi = 32; + reg[5] = 0x82; + reg[6] = 0x02; + } else if (freq < 148334) { /* freq * 24 < 3560000 */ + multi = 24; + reg[5] = 0x22; + reg[6] = 0x00; + } else if (freq < 222500) { /* freq * 16 < 3560000 */ + multi = 16; + reg[5] = 0x42; + reg[6] = 0x02; + } else if (freq < 296667) { /* freq * 12 < 3560000 */ + multi = 12; + reg[5] = 0x12; + reg[6] = 0x00; + } else if (freq < 445000) { /* freq * 8 < 3560000 */ + multi = 8; + reg[5] = 0x22; + reg[6] = 0x02; + } else if (freq < 593334) { /* freq * 6 < 3560000 */ + multi = 6; + reg[5] = 0x0a; + reg[6] = 0x00; + } else if (freq < 950000) { /* freq * 4 < 3800000 */ + multi = 4; + reg[5] = 0x12; + reg[6] = 0x02; + } else { + multi = 2; + reg[5] = 0x0a; + reg[6] = 0x02; + } + + f_vco = freq * multi; + + if (f_vco >= 3060000) { + reg[6] |= 0x08; + vco_select = true; + } + + if (freq >= 45000) { + /* From divided value (XDIV) determined the FA and FP value */ + xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); + if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) + xdiv++; + + pm = (unsigned char)(xdiv / 8); + am = (unsigned char)(xdiv - (8 * pm)); + + if (am < 2) { + reg[1] = am + 8; + reg[2] = pm - 1; + } else { + reg[1] = am; + reg[2] = pm; + } + } else { + /* fix for frequency less than 45 MHz */ + reg[1] = 0x06; + reg[2] = 0x11; + } + + /* fix clock out */ + reg[6] |= 0x20; + + /* From VCO frequency determines the XIN ( fractional part of Delta + Sigma PLL) and divided value (XDIV) */ + xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); + xin = (xin << 15) / xtal_freq_khz_2; + if (xin >= 16384) + xin += 32768; + + reg[3] = xin >> 8; + reg[4] = xin & 0xff; + + if (delsys == SYS_DVBT) { + reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ + switch (p->bandwidth_hz) { + case 6000000: + reg[6] |= 0x80; + break; + case 7000000: + reg[6] |= 0x40; + break; + case 8000000: + default: + break; + } + } else { + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + + /* modified for Realtek demod */ + reg[5] |= 0x07; + + for (i = 1; i <= 6; i++) { + ret = fc0013_writereg(priv, i, reg[i]); + if (ret) + goto exit; + } + + ret = fc0013_readreg(priv, 0x11, &tmp); + if (ret) + goto exit; + if (multi == 64) + ret = fc0013_writereg(priv, 0x11, tmp | 0x04); + else + ret = fc0013_writereg(priv, 0x11, tmp & 0xfb); + if (ret) + goto exit; + + /* VCO Calibration */ + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + + /* VCO Re-Calibration if needed */ + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + + if (!ret) { + msleep(10); + ret = fc0013_readreg(priv, 0x0e, &tmp); + } + if (ret) + goto exit; + + /* vco selection */ + tmp &= 0x3f; + + if (vco_select) { + if (tmp > 0x3c) { + reg[6] &= ~0x08; + ret = fc0013_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + } + } else { + if (tmp < 0x02) { + reg[6] |= 0x08; + ret = fc0013_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + } + } + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + return ret; +} + +static int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0013_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + /* always ? */ + *frequency = 0; + return 0; +} + +static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0013_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + + +static const struct dvb_tuner_ops fc0013_tuner_ops = { + .info = { + .name = "Fitipower FC0013", + + .frequency_min = 37000000, /* estimate */ + .frequency_max = 1680000000, /* CHECK */ + .frequency_step = 0, + }, + + .release = fc0013_release, + + .init = fc0013_init, + .sleep = fc0013_sleep, + + .set_params = fc0013_set_params, + + .get_frequency = fc0013_get_frequency, + .get_if_frequency = fc0013_get_if_frequency, + .get_bandwidth = fc0013_get_bandwidth, +}; + +struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + struct fc0013_priv *priv = NULL; + + priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + priv->dual_master = dual_master; + priv->addr = i2c_address; + priv->xtal_freq = xtal_freq; + + info("Fitipower FC0013 successfully attached."); + + fe->tuner_priv = priv; + + memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +} +EXPORT_SYMBOL(fc0013_attach); + +MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver"); +MODULE_AUTHOR("Hans-Frieder Vogt "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); diff --git a/drivers/media/common/tuners/fc0013.h b/drivers/media/common/tuners/fc0013.h new file mode 100644 index 000000000000..594efd64aeec --- /dev/null +++ b/drivers/media/common/tuners/fc0013.h @@ -0,0 +1,57 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _FC0013_H_ +#define _FC0013_H_ + +#include "dvb_frontend.h" +#include "fc001x-common.h" + +#if defined(CONFIG_MEDIA_TUNER_FC0013) || \ + (defined(CONFIG_MEDIA_TUNER_FC0013_MODULE) && defined(MODULE)) +extern struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq); +extern int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val); +extern int fc0013_rc_cal_reset(struct dvb_frontend *fe); +#else +static inline struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) +{ + return 0; +} + +static inline int fc0013_rc_cal_reset(struct dvb_frontend *fe) +{ + return 0; +} +#endif + +#endif -- cgit v1.2.3 From 4a14db7e61a1aa3ef379eb63ff2c5943218bb8cc Mon Sep 17 00:00:00 2001 From: Hans-Frieder Vogt Date: Sat, 12 May 2012 05:11:36 -0300 Subject: [media] fc0012 ver. 0.6: introduction of get_rf_strength function Changes compared to version 0.5 of driver (sent 6 May): - Initial implementation of get_rf_strength function. Signed-off-by: Hans-Frieder Vogt Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/fc0012.c | 72 +++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/tuners/fc0012.c b/drivers/media/common/tuners/fc0012.c index cd58f90d7d13..308135abd54c 100644 --- a/drivers/media/common/tuners/fc0012.c +++ b/drivers/media/common/tuners/fc0012.c @@ -343,6 +343,74 @@ static int fc0012_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return 0; } +#define INPUT_ADC_LEVEL -8 + +static int fc0012_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int ret; + unsigned char tmp; + int int_temp, lna_gain, int_lna, tot_agc_gain, power; + const int fc0012_lna_gain_table[] = { + /* low gain */ + -63, -58, -99, -73, + -63, -65, -54, -60, + /* middle gain */ + 71, 70, 68, 67, + 65, 63, 61, 58, + /* high gain */ + 197, 191, 188, 186, + 184, 182, 181, 179, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0012_writereg(priv, 0x12, 0x00); + if (ret) + goto err; + + ret = fc0012_readreg(priv, 0x12, &tmp); + if (ret) + goto err; + int_temp = tmp; + + ret = fc0012_readreg(priv, 0x13, &tmp); + if (ret) + goto err; + lna_gain = tmp & 0x1f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (lna_gain < ARRAY_SIZE(fc0012_lna_gain_table)) { + int_lna = fc0012_lna_gain_table[lna_gain]; + tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + + (int_temp & 0x1f)) * 2; + power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; + + if (power >= 45) + *strength = 255; /* 100% */ + else if (power < -95) + *strength = 0; + else + *strength = (power + 95) * 255 / 140; + + *strength |= *strength << 8; + } else { + ret = -1; + } + + goto exit; + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +exit: + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} static const struct dvb_tuner_ops fc0012_tuner_ops = { .info = { @@ -363,6 +431,8 @@ static const struct dvb_tuner_ops fc0012_tuner_ops = { .get_frequency = fc0012_get_frequency, .get_if_frequency = fc0012_get_if_frequency, .get_bandwidth = fc0012_get_bandwidth, + + .get_rf_strength = fc0012_get_rf_strength, }; struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, @@ -394,4 +464,4 @@ EXPORT_SYMBOL(fc0012_attach); MODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver"); MODULE_AUTHOR("Hans-Frieder Vogt "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.5"); +MODULE_VERSION("0.6"); -- cgit v1.2.3 From b144c98ca0962ee3cbdbbeafe77a1b300be0cb4f Mon Sep 17 00:00:00 2001 From: Hans-Frieder Vogt Date: Sat, 12 May 2012 05:11:47 -0300 Subject: [media] fc0013 ver. 0.2: introduction of get_rf_strength function Changes compared to version 0.1 of driver (sent 6 May): - Initial implementation of get_rf_strength function. - Introduction of a warning message Signed-off-by: Hans-Frieder Vogt Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/fc0013.c | 74 +++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/tuners/fc0013.c b/drivers/media/common/tuners/fc0013.c index 7398d7759e08..bd8f0f1e8f3b 100644 --- a/drivers/media/common/tuners/fc0013.c +++ b/drivers/media/common/tuners/fc0013.c @@ -484,6 +484,8 @@ static int fc0013_set_params(struct dvb_frontend *fe) exit: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + if (ret) + warn("%s: failed: %d", __func__, ret); return ret; } @@ -508,6 +510,74 @@ static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return 0; } +#define INPUT_ADC_LEVEL -8 + +static int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + unsigned char tmp; + int int_temp, lna_gain, int_lna, tot_agc_gain, power; + const int fc0013_lna_gain_table[] = { + /* low gain */ + -63, -58, -99, -73, + -63, -65, -54, -60, + /* middle gain */ + 71, 70, 68, 67, + 65, 63, 61, 58, + /* high gain */ + 197, 191, 188, 186, + 184, 182, 181, 179, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0013_writereg(priv, 0x13, 0x00); + if (ret) + goto err; + + ret = fc0013_readreg(priv, 0x13, &tmp); + if (ret) + goto err; + int_temp = tmp; + + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto err; + lna_gain = tmp & 0x1f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) { + int_lna = fc0013_lna_gain_table[lna_gain]; + tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + + (int_temp & 0x1f)) * 2; + power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; + + if (power >= 45) + *strength = 255; /* 100% */ + else if (power < -95) + *strength = 0; + else + *strength = (power + 95) * 255 / 140; + + *strength |= *strength << 8; + } else { + ret = -1; + } + + goto exit; + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +exit: + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} static const struct dvb_tuner_ops fc0013_tuner_ops = { .info = { @@ -528,6 +598,8 @@ static const struct dvb_tuner_ops fc0013_tuner_ops = { .get_frequency = fc0013_get_frequency, .get_if_frequency = fc0013_get_if_frequency, .get_bandwidth = fc0013_get_bandwidth, + + .get_rf_strength = fc0013_get_rf_strength, }; struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, @@ -559,4 +631,4 @@ EXPORT_SYMBOL(fc0013_attach); MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver"); MODULE_AUTHOR("Hans-Frieder Vogt "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); +MODULE_VERSION("0.2"); -- cgit v1.2.3 From 140534432e8a7edee5814d139dd59c20607479e3 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 17 May 2012 18:26:50 -0300 Subject: [media] drxk: fix GPIOs UIO-2 and UIO-3 were broken. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/drxk_hard.c | 4 ++-- drivers/media/dvb/frontends/drxk_map.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c index 8d99ac1598ae..60b868faeacf 100644 --- a/drivers/media/dvb/frontends/drxk_hard.c +++ b/drivers/media/dvb/frontends/drxk_hard.c @@ -5835,7 +5835,7 @@ static int WriteGPIO(struct drxk_state *state) } if (state->UIO_mask & 0x0002) { /* UIO-2 */ /* write to io pad configuration register - output mode */ - status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg); + status = write16(state, SIO_PDR_SMA_RX_CFG__A, state->m_GPIOCfg); if (status < 0) goto error; @@ -5854,7 +5854,7 @@ static int WriteGPIO(struct drxk_state *state) } if (state->UIO_mask & 0x0004) { /* UIO-3 */ /* write to io pad configuration register - output mode */ - status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg); + status = write16(state, SIO_PDR_GPIO_CFG__A, state->m_GPIOCfg); if (status < 0) goto error; diff --git a/drivers/media/dvb/frontends/drxk_map.h b/drivers/media/dvb/frontends/drxk_map.h index 9b11a8328869..23e16c12f234 100644 --- a/drivers/media/dvb/frontends/drxk_map.h +++ b/drivers/media/dvb/frontends/drxk_map.h @@ -432,6 +432,7 @@ #define SIO_PDR_UIO_OUT_LO__A 0x7F0016 #define SIO_PDR_OHW_CFG__A 0x7F001F #define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3 +#define SIO_PDR_GPIO_CFG__A 0x7F0021 #define SIO_PDR_MSTRT_CFG__A 0x7F0025 #define SIO_PDR_MERR_CFG__A 0x7F0026 #define SIO_PDR_MCLK_CFG__A 0x7F0028 @@ -446,4 +447,5 @@ #define SIO_PDR_MD5_CFG__A 0x7F0030 #define SIO_PDR_MD6_CFG__A 0x7F0031 #define SIO_PDR_MD7_CFG__A 0x7F0032 +#define SIO_PDR_SMA_RX_CFG__A 0x7F0037 #define SIO_PDR_SMA_TX_CFG__A 0x7F0038 -- cgit v1.2.3 From f6f379df6516d6fab27367706fcaafa88df41178 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 17 May 2012 19:09:06 -0300 Subject: [media] em28xx: disable LNA - PCTV QuatroStick nano (520e) Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-dvb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index ea3810fa5a15..3a5b89dc7a13 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -336,6 +336,8 @@ struct drxk_config pctv_520e_drxk = { .single_master = 1, .microcode_name = "dvb-demod-drxk-pctv.fw", .chunk_size = 58, + .antenna_dvbt = true, /* disable LNA */ + .antenna_gpio = (1 << 2), /* disable LNA */ }; static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) -- cgit v1.2.3 From eba672a045d8fbf62b229eac74ef444b6000c4c2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 15 May 2012 18:32:33 -0300 Subject: [media] rtl2830: implement .read_snr() Reports value as a 0.1 dB. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/rtl2830.c | 42 +++++++++++++++++++++++++++++- drivers/media/dvb/frontends/rtl2830_priv.h | 1 + 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c index 45196c5b0736..e6d7c501717a 100644 --- a/drivers/media/dvb/frontends/rtl2830.c +++ b/drivers/media/dvb/frontends/rtl2830.c @@ -404,8 +404,48 @@ err: static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr) { - *snr = 0; + struct rtl2830_priv *priv = fe->demodulator_priv; + int ret, hierarchy, constellation; + u8 buf[2], tmp; + u16 tmp16; +#define CONSTELLATION_NUM 3 +#define HIERARCHY_NUM 4 + static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { + { 70705899, 70705899, 70705899, 70705899 }, + { 82433173, 82433173, 87483115, 94445660 }, + { 92888734, 92888734, 95487525, 99770748 }, + }; + + /* reports SNR in resolution of 0.1 dB */ + + ret = rtl2830_rd_reg(priv, 0x33c, &tmp); + if (ret) + goto err; + + constellation = (tmp >> 2) & 0x03; /* [3:2] */ + if (constellation > CONSTELLATION_NUM - 1) + goto err; + + hierarchy = (tmp >> 4) & 0x07; /* [6:4] */ + if (hierarchy > HIERARCHY_NUM - 1) + goto err; + + ret = rtl2830_rd_regs(priv, 0x40c, buf, 2); + if (ret) + goto err; + + tmp16 = buf[0] << 8 | buf[1]; + + if (tmp16) + *snr = (snr_constant[constellation][hierarchy] - + intlog10(tmp16)) / ((1 << 24) / 100); + else + *snr = 0; + return 0; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; } static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber) diff --git a/drivers/media/dvb/frontends/rtl2830_priv.h b/drivers/media/dvb/frontends/rtl2830_priv.h index 4a464761b5b8..9b20557ccf6c 100644 --- a/drivers/media/dvb/frontends/rtl2830_priv.h +++ b/drivers/media/dvb/frontends/rtl2830_priv.h @@ -22,6 +22,7 @@ #define RTL2830_PRIV_H #include "dvb_frontend.h" +#include "dvb_math.h" #include "rtl2830.h" #define LOG_PREFIX "rtl2830" -- cgit v1.2.3 From 525ffc19b992f5d6a25413c36ba543a82585ed89 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 May 2012 12:23:42 -0300 Subject: [media] rtl2830: implement .read_ber() Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/rtl2830.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c index e6d7c501717a..266098656c48 100644 --- a/drivers/media/dvb/frontends/rtl2830.c +++ b/drivers/media/dvb/frontends/rtl2830.c @@ -450,8 +450,20 @@ err: static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber) { - *ber = 0; + struct rtl2830_priv *priv = fe->demodulator_priv; + int ret; + u8 buf[2]; + + ret = rtl2830_rd_regs(priv, 0x34e, buf, 2); + if (ret) + goto err; + + *ber = buf[0] << 8 | buf[1]; + return 0; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; } static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) -- cgit v1.2.3 From 78e750754bf0cc86d36149536bc7f3382710a2ee Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 May 2012 15:17:51 -0300 Subject: [media] rtl2830: implement .read_signal_strength() Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/rtl2830.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c index 266098656c48..6e4029de3c73 100644 --- a/drivers/media/dvb/frontends/rtl2830.c +++ b/drivers/media/dvb/frontends/rtl2830.c @@ -474,8 +474,29 @@ static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { - *strength = 0; + struct rtl2830_priv *priv = fe->demodulator_priv; + int ret; + u8 buf[2]; + u16 if_agc_raw, if_agc; + + ret = rtl2830_rd_regs(priv, 0x359, buf, 2); + if (ret) + goto err; + + if_agc_raw = (buf[0] << 8 | buf[1]) & 0x3fff; + + if (if_agc_raw & (1 << 9)) + if_agc = -(~(if_agc_raw - 1) & 0x1ff); + else + if_agc = if_agc_raw; + + *strength = (u8) (55 - if_agc / 182); + *strength |= *strength << 8; + return 0; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; } static struct dvb_frontend_ops rtl2830_ops; -- cgit v1.2.3 From 631a2b611288f87de4ef1098d1fb4bc4d246b103 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 May 2012 15:58:57 -0300 Subject: [media] rtl2830: implement .get_frontend() Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/rtl2830.c | 110 ++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c index 6e4029de3c73..507ef4bb047a 100644 --- a/drivers/media/dvb/frontends/rtl2830.c +++ b/drivers/media/dvb/frontends/rtl2830.c @@ -374,6 +374,115 @@ err: return ret; } +static int rtl2830_get_frontend(struct dvb_frontend *fe) +{ + struct rtl2830_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + u8 buf[3]; + + ret = rtl2830_rd_regs(priv, 0x33c, buf, 2); + if (ret) + goto err; + + ret = rtl2830_rd_reg(priv, 0x351, &buf[2]); + if (ret) + goto err; + + dbg("%s: TPS=%02x %02x %02x", __func__, buf[0], buf[1], buf[2]); + + switch ((buf[0] >> 2) & 3) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + } + + switch ((buf[2] >> 2) & 1) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + } + + switch ((buf[2] >> 0) & 3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[0] >> 4) & 7) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[1] >> 3) & 7) { + case 0: + c->code_rate_HP = FEC_1_2; + break; + case 1: + c->code_rate_HP = FEC_2_3; + break; + case 2: + c->code_rate_HP = FEC_3_4; + break; + case 3: + c->code_rate_HP = FEC_5_6; + break; + case 4: + c->code_rate_HP = FEC_7_8; + break; + } + + switch ((buf[1] >> 0) & 7) { + case 0: + c->code_rate_LP = FEC_1_2; + break; + case 1: + c->code_rate_LP = FEC_2_3; + break; + case 2: + c->code_rate_LP = FEC_3_4; + break; + case 3: + c->code_rate_LP = FEC_5_6; + break; + case 4: + c->code_rate_LP = FEC_7_8; + break; + } + + return 0; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct rtl2830_priv *priv = fe->demodulator_priv; @@ -622,6 +731,7 @@ static struct dvb_frontend_ops rtl2830_ops = { .get_tune_settings = rtl2830_get_tune_settings, .set_frontend = rtl2830_set_frontend, + .get_frontend = rtl2830_get_frontend, .read_status = rtl2830_read_status, .read_snr = rtl2830_read_snr, -- cgit v1.2.3 From c188637dc5a25aec6a21279524405c8fe96f2f4b Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 May 2012 16:02:55 -0300 Subject: [media] rtl2830: prevent hw access when sleeping to prevent I/O errors... Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/rtl2830.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c index 507ef4bb047a..93612ebac519 100644 --- a/drivers/media/dvb/frontends/rtl2830.c +++ b/drivers/media/dvb/frontends/rtl2830.c @@ -381,6 +381,9 @@ static int rtl2830_get_frontend(struct dvb_frontend *fe) int ret; u8 buf[3]; + if (priv->sleeping) + return 0; + ret = rtl2830_rd_regs(priv, 0x33c, buf, 2); if (ret) goto err; @@ -525,6 +528,9 @@ static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr) { 92888734, 92888734, 95487525, 99770748 }, }; + if (priv->sleeping) + return 0; + /* reports SNR in resolution of 0.1 dB */ ret = rtl2830_rd_reg(priv, 0x33c, &tmp); @@ -563,6 +569,9 @@ static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber) int ret; u8 buf[2]; + if (priv->sleeping) + return 0; + ret = rtl2830_rd_regs(priv, 0x34e, buf, 2); if (ret) goto err; @@ -588,6 +597,9 @@ static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength) u8 buf[2]; u16 if_agc_raw, if_agc; + if (priv->sleeping) + return 0; + ret = rtl2830_rd_regs(priv, 0x359, buf, 2); if (ret) goto err; -- cgit v1.2.3 From ccb5cf9bfcec739506d91737c4a9675e78868db2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 May 2012 16:07:46 -0300 Subject: [media] rtl28xxu: add small sleep for rtl2830 demod attach Demod needs some time to wake up after power on. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/rtl28xxu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c index 4e69e9db849e..41e1f5537f44 100644 --- a/drivers/media/dvb/dvb-usb/rtl28xxu.c +++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c @@ -322,6 +322,9 @@ static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap) * since there is some demod params needed to set according to tuner. */ + /* demod needs some time to wake up */ + msleep(20); + /* open demod I2C gate */ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate); if (ret) -- cgit v1.2.3 From 0982db20aba5fd124bb5942d679d8732478e992a Mon Sep 17 00:00:00 2001 From: Volokh Konstantin Date: Fri, 11 May 2012 02:18:37 -0300 Subject: [media] staging: media: go7007: Adlink MPG24 board issues This issuses applyed only for Adlink MPG24 board with go7007 & wis2804, all whese changes was tested for continuos load&restart mode This is minimal changes needed for start up go7007&wis2804 to work correctly in 3.4 branch Changes: - When go7007 reset device, i2c was not worked (need rewrite GPIO5) - As wis2804 has i2c_addr=0x00/*really*/, so Need set I2C_CLIENT_TEN flag for validity - some main nonzero initialization, rewrites with kzalloc instead kmalloc - STATUS_SHUTDOWN was placed in incorrect place, so if firmware wasn`t loaded, we failed v4l2_device_unregister with kernel panic (OOPS) - some new v4l2 style features as call_all(...s_stream...) for using subdev calls - wis-tw2804.ko module code was incompatible with 3.4 branch in initialization v4l2_subdev parts. now i2c_get_clientdata(...) contains v4l2_subdev struct instead non standart wis_tw2804 struct Adds: - Additional chipset tw2804 controls with: gain,auto gain,inputs[0,1],color kill,chroma gain,gain balances, for all 4 channels (from tw2804.pdf) - Power control for each 4 ADC (tw2804) up when s_stream(...,1), down otherwise Signed-off-by: Volokh Konstantin Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/README | 18 + drivers/staging/media/go7007/go7007-driver.c | 27 +- drivers/staging/media/go7007/go7007-priv.h | 2 +- drivers/staging/media/go7007/go7007-usb.c | 5 +- drivers/staging/media/go7007/go7007-v4l2.c | 7 +- drivers/staging/media/go7007/wis-tw2804.c | 512 +++++++++++++++++++-------- 6 files changed, 407 insertions(+), 164 deletions(-) diff --git a/drivers/staging/media/go7007/README b/drivers/staging/media/go7007/README index 48f447637817..082a6818c64e 100644 --- a/drivers/staging/media/go7007/README +++ b/drivers/staging/media/go7007/README @@ -5,6 +5,24 @@ Todo: and added to the build. - testing? - handle churn in v4l layer. + - Some features for wis-tw2804 subdev control (comb filter,motion detector sensitive & mask,more over...) + - go7007-v4l2.c need rewrite with new v4l2 style without nonstandart IO controls (set detector & bitrate) + +05/05/2012 3.4.0-rc+: +Changes: + - When go7007 reset device, i2c was not worked (need rewrite GPIO5) + - As wis2804 has i2c_addr=0x00/*really*/, so Need set I2C_CLIENT_TEN flag for validity + - Some main nonzero initialization, rewrites with kzalloc instead kmalloc + - STATUS_SHUTDOWN was placed in incorrect place, so if firmware wasn`t loaded, we + failed v4l2_device_unregister with kernel panic (OOPS) + - Some new v4l2 style features as call_all(...s_stream...) for using subdev calls + - wis-tw2804.ko module code was incompatible with 3.4.x branch in initialization v4l2_subdev parts. + now i2c_get_clientdata(...) contains v4l2_subdev struct instead non standart wis_tw2804 struct + +Adds: + - Additional chipset wis2804 controls with: gain,auto gain,inputs[0,1],color kill,chroma gain,gain balances, + for all 4 channels (from tw2804.pdf) + - Power control for each 4 ADC up when s_stream(...,1), down otherwise in wis-tw2804 module Please send patchs to Greg Kroah-Hartman and Cc: Ross Cohen as well. diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index ece2dd146487..2dff9b5906b9 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -173,6 +173,11 @@ static int go7007_init_encoder(struct go7007 *go) go7007_write_addr(go, 0x3c82, 0x0001); go7007_write_addr(go, 0x3c80, 0x00fe); } + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { + /* set GPIO5 to be an output, currently low */ + go7007_write_addr(go, 0x3c82, 0x0000); + go7007_write_addr(go, 0x3c80, 0x00df); + } return 0; } @@ -192,17 +197,23 @@ int go7007_reset_encoder(struct go7007 *go) /* * Attempt to instantiate an I2C client by ID, probably loading a module. */ -static int init_i2c_module(struct i2c_adapter *adapter, const char *type, - int addr) +static int init_i2c_module(struct i2c_adapter *adapter, const struct go_i2c *const i2c) { struct go7007 *go = i2c_get_adapdata(adapter); struct v4l2_device *v4l2_dev = &go->v4l2_dev; + struct i2c_board_info info; + + memset(&info, 0, sizeof(info)); + strlcpy(info.type, i2c->type, sizeof(info.type)); + info.addr = i2c->addr; - if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL)) + if (i2c->id == I2C_DRIVERID_WIS_TW2804) + info.flags |= I2C_CLIENT_TEN; + if (v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, NULL)) return 0; - printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type); - return -1; + printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", i2c->type); + return -EINVAL; } /* @@ -238,9 +249,7 @@ int go7007_register_encoder(struct go7007 *go) } if (go->i2c_adapter_online) { for (i = 0; i < go->board_info->num_i2c_devs; ++i) - init_i2c_module(&go->i2c_adapter, - go->board_info->i2c_devs[i].type, - go->board_info->i2c_devs[i].addr); + init_i2c_module(&go->i2c_adapter, &go->board_info->i2c_devs[i]); if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) i2c_clients_command(&go->i2c_adapter, DECODER_SET_CHANNEL, &go->channel_number); @@ -571,7 +580,7 @@ struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev) struct go7007 *go; int i; - go = kmalloc(sizeof(struct go7007), GFP_KERNEL); + go = kzalloc(sizeof(struct go7007), GFP_KERNEL); if (go == NULL) return NULL; go->dev = dev; diff --git a/drivers/staging/media/go7007/go7007-priv.h b/drivers/staging/media/go7007/go7007-priv.h index b58c394c6555..b7b939a1967e 100644 --- a/drivers/staging/media/go7007/go7007-priv.h +++ b/drivers/staging/media/go7007/go7007-priv.h @@ -88,7 +88,7 @@ struct go7007_board_info { int audio_bclk_div; int audio_main_div; int num_i2c_devs; - struct { + struct go_i2c { const char *type; int id; int addr; diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 5443e25086e9..9dbf5ecd05a2 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1110,9 +1110,6 @@ static int go7007_usb_probe(struct usb_interface *intf, } else { u16 channel; - /* set GPIO5 to be an output, currently low */ - go7007_write_addr(go, 0x3c82, 0x0000); - go7007_write_addr(go, 0x3c80, 0x00df); /* read channel number from GPIO[1:0] */ go7007_read_addr(go, 0x3c81, &channel); channel &= 0x3; @@ -1245,7 +1242,6 @@ static void go7007_usb_disconnect(struct usb_interface *intf) struct urb *vurb, *aurb; int i; - go->status = STATUS_SHUTDOWN; usb_kill_urb(usb->intr_urb); /* Free USB-related structs */ @@ -1269,6 +1265,7 @@ static void go7007_usb_disconnect(struct usb_interface *intf) kfree(go->hpi_context); go7007_remove(go); + go->status = STATUS_SHUTDOWN; } static struct usb_driver go7007_usb_driver = { diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index c184ad30fbd8..b8f2eb6bc3ef 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -98,7 +98,7 @@ static int go7007_open(struct file *file) if (go->status != STATUS_ONLINE) return -EBUSY; - gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL); + gofh = kzalloc(sizeof(struct go7007_file), GFP_KERNEL); if (gofh == NULL) return -ENOMEM; ++go->ref_count; @@ -953,6 +953,7 @@ static int vidioc_streamon(struct file *file, void *priv, } mutex_unlock(&go->hw_lock); mutex_unlock(&gofh->lock); + call_all(&go->v4l2_dev, video, s_stream, 1); return retval; } @@ -968,6 +969,7 @@ static int vidioc_streamoff(struct file *file, void *priv, mutex_lock(&gofh->lock); go7007_streamoff(go); mutex_unlock(&gofh->lock); + call_all(&go->v4l2_dev, video, s_stream, 0); return 0; } @@ -1832,5 +1834,6 @@ void go7007_v4l2_remove(struct go7007 *go) mutex_unlock(&go->hw_lock); if (go->video_dev) video_unregister_device(go->video_dev); - v4l2_device_unregister(&go->v4l2_dev); + if (go->status != STATUS_SHUTDOWN) + v4l2_device_unregister(&go->v4l2_dev); } diff --git a/drivers/staging/media/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c index 9134f03e3cf0..9afc5df5902e 100644 --- a/drivers/staging/media/go7007/wis-tw2804.c +++ b/drivers/staging/media/go7007/wis-tw2804.c @@ -21,16 +21,27 @@ #include #include #include +#include +#include #include "wis-i2c.h" struct wis_tw2804 { - int channel; + struct v4l2_subdev sd; + u8 channel:2; + u8 input:1; + u8 update:1; + u8 auto_gain:1; + u8 ckil:1; int norm; - int brightness; - int contrast; - int saturation; - int hue; + u8 brightness; + u8 contrast; + u8 saturation; + u8 hue; + u8 gain; + u8 cr_gain; + u8 r_balance; + u8 b_balance; }; static u8 global_registers[] = { @@ -41,6 +52,7 @@ static u8 global_registers[] = { 0x3d, 0x80, 0x3e, 0x82, 0x3f, 0x82, + 0x78, 0x0f, 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ }; @@ -103,29 +115,358 @@ static u8 channel_registers[] = { 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ }; -static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel) +static s32 write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel) { return i2c_smbus_write_byte_data(client, reg | (channel << 6), value); } -static int write_regs(struct i2c_client *client, u8 *regs, int channel) +static int write_regs(struct i2c_client *client, u8 *regs, u8 channel) { int i; for (i = 0; regs[i] != 0xff; i += 2) if (i2c_smbus_write_byte_data(client, regs[i] | (channel << 6), regs[i + 1]) < 0) - return -1; + return -EINVAL; return 0; } +static s32 read_reg(struct i2c_client *client, u8 reg, u8 channel) +{ + return i2c_smbus_read_byte_data(client, (reg) | (channel << 6)); +} + +static inline struct wis_tw2804 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct wis_tw2804, sd); +} + +static int tw2804_log_status(struct v4l2_subdev *sd) +{ + struct wis_tw2804 *state = to_state(sd); + v4l2_info(sd, "Standard: %s\n", state->norm == V4L2_STD_NTSC ? "NTSC" : + state->norm == V4L2_STD_PAL ? "PAL" : "unknown"); + v4l2_info(sd, "Channel: %d\n", state->channel); + v4l2_info(sd, "Input: %d\n", state->input); + v4l2_info(sd, "Brightness: %d\n", state->brightness); + v4l2_info(sd, "Contrast: %d\n", state->contrast); + v4l2_info(sd, "Saturation: %d\n", state->saturation); + v4l2_info(sd, "Hue: %d\n", state->hue); + return 0; +} + +static int tw2804_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query) +{ + static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUTOGAIN, + V4L2_CID_COLOR_KILLER, + V4L2_CID_GAIN, + V4L2_CID_CHROMA_GAIN, + V4L2_CID_BLUE_BALANCE, + V4L2_CID_RED_BALANCE, + 0 + }; + + static const u32 *ctrl_classes[] = { + user_ctrls, + NULL + }; + + query->id = v4l2_ctrl_next(ctrl_classes, query->id); + + switch (query->id) { + case V4L2_CID_USER_CLASS: + return v4l2_ctrl_query_fill(query, 0, 0, 0, 0); + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); + case V4L2_CID_AUTOGAIN: + return v4l2_ctrl_query_fill(query, 0, 1, 1, 0); + case V4L2_CID_COLOR_KILLER: + return v4l2_ctrl_query_fill(query, 0, 1, 1, 0); + case V4L2_CID_GAIN: + return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); + case V4L2_CID_CHROMA_GAIN: + return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); + case V4L2_CID_BLUE_BALANCE: + return v4l2_ctrl_query_fill(query, 0, 255, 1, 122); + case V4L2_CID_RED_BALANCE: + return v4l2_ctrl_query_fill(query, 0, 255, 1, 122); + default: + return -EINVAL; + } +} + +s32 get_ctrl_addr(int ctrl) +{ + switch (ctrl) { + case V4L2_CID_BRIGHTNESS: + return 0x12; + case V4L2_CID_CONTRAST: + return 0x11; + case V4L2_CID_SATURATION: + return 0x10; + case V4L2_CID_HUE: + return 0x0f; + case V4L2_CID_AUTOGAIN: + return 0x02; + case V4L2_CID_COLOR_KILLER: + return 0x14; + case V4L2_CID_GAIN: + return 0x3c; + case V4L2_CID_CHROMA_GAIN: + return 0x3d; + case V4L2_CID_RED_BALANCE: + return 0x3f; + case V4L2_CID_BLUE_BALANCE: + return 0x3e; + default: + return -EINVAL; + } +} + +static int tw2804_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct wis_tw2804 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + s32 addr = get_ctrl_addr(ctrl->id); + s32 val = 0; + + if (addr == -EINVAL) + return -EINVAL; + + if (state->update) { + val = read_reg(client, addr, ctrl->id == V4L2_CID_GAIN || + ctrl->id == V4L2_CID_CHROMA_GAIN || + ctrl->id == V4L2_CID_RED_BALANCE || + ctrl->id == V4L2_CID_BLUE_BALANCE ? 0 : state->channel); + if (val < 0) + return val; + } + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (state->update) + state->brightness = val; + ctrl->value = state->brightness; + break; + case V4L2_CID_CONTRAST: + if (state->update) + state->contrast = val; + ctrl->value = state->contrast; + break; + case V4L2_CID_SATURATION: + if (state->update) + state->saturation = val; + ctrl->value = state->saturation; + break; + case V4L2_CID_HUE: + if (state->update) + state->hue = val; + ctrl->value = state->hue; + break; + case V4L2_CID_AUTOGAIN: + if (state->update) + state->auto_gain = val & (1<<7) ? 1 : 0; + ctrl->value = state->auto_gain; + break; + case V4L2_CID_COLOR_KILLER: + if (state->update) + state->ckil = (val & 0x03) == 0x03 ? 1 : 0; + ctrl->value = state->ckil; + break; + case V4L2_CID_GAIN: + if (state->update) + state->gain = val; + ctrl->value = state->gain; + break; + case V4L2_CID_CHROMA_GAIN: + if (state->update) + state->cr_gain = val; + ctrl->value = state->cr_gain; + break; + case V4L2_CID_RED_BALANCE: + if (state->update) + state->r_balance = val; + ctrl->value = state->r_balance; + break; + case V4L2_CID_BLUE_BALANCE: + if (state->update) + state->b_balance = val; + ctrl->value = state->b_balance; + break; + default: + return -EINVAL; + } + + state->update = 0; + return 0; +} + +static int tw2804_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct wis_tw2804 *dec = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + s32 reg = 0; + s32 addr = get_ctrl_addr(ctrl->id); + + if (addr == -EINVAL) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + reg = read_reg(client, addr, dec->channel); + if (reg > 0) { + if (ctrl->value == 0) + ctrl->value = reg & ~(1<<7); + else + ctrl->value = reg | 1<<7; + } else + return reg; + break; + case V4L2_CID_COLOR_KILLER: + reg = read_reg(client, addr, dec->channel); + if (reg > 0) + ctrl->value = (reg & ~(0x03)) | (ctrl->value == 0 ? 0x02 : 0x03); + else + return reg; + break; + default: + break; + } + + ctrl->value = ctrl->value > 255 ? 255 : (ctrl->value < 0 ? 0 : ctrl->value); + reg = write_reg(client, addr, (u8)ctrl->value, ctrl->id == V4L2_CID_GAIN || + ctrl->id == V4L2_CID_CHROMA_GAIN || + ctrl->id == V4L2_CID_RED_BALANCE || + ctrl->id == V4L2_CID_BLUE_BALANCE ? 0 : dec->channel); + + if (reg < 0) { + v4l2_err(&dec->sd, "Can`t set_ctrl value:id=%d;value=%d\n", ctrl->id, ctrl->value); + return reg; + } + + dec->update = 1; + return tw2804_g_ctrl(sd, ctrl); +} + +static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct wis_tw2804 *dec = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + u8 regs[] = { + 0x01, norm&V4L2_STD_NTSC ? 0xc4 : 0x84, + 0x09, norm&V4L2_STD_NTSC ? 0x07 : 0x04, + 0x0a, norm&V4L2_STD_NTSC ? 0xf0 : 0x20, + 0x0b, norm&V4L2_STD_NTSC ? 0x07 : 0x04, + 0x0c, norm&V4L2_STD_NTSC ? 0xf0 : 0x20, + 0x0d, norm&V4L2_STD_NTSC ? 0x40 : 0x4a, + 0x16, norm&V4L2_STD_NTSC ? 0x00 : 0x40, + 0x17, norm&V4L2_STD_NTSC ? 0x00 : 0x40, + 0x20, norm&V4L2_STD_NTSC ? 0x07 : 0x0f, + 0x21, norm&V4L2_STD_NTSC ? 0x07 : 0x0f, + 0xff, 0xff, + }; + write_regs(client, regs, dec->channel); + dec->norm = norm; + return 0; +} + +static const struct v4l2_subdev_core_ops tw2804_core_ops = { + .log_status = tw2804_log_status, + .g_ctrl = tw2804_g_ctrl, + .s_ctrl = tw2804_s_ctrl, + .queryctrl = tw2804_queryctrl, + .s_std = tw2804_s_std, +}; + +static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct wis_tw2804 *dec = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + s32 reg = 0; + + if (0 > input || input > 1) + return -EINVAL; + + if (input == dec->input && !dec->update) + return 0; + + reg = read_reg(client, 0x22, dec->channel); + + if (reg >= 0) { + if (input == 0) + reg &= ~(1<<2); + else + reg |= 1<<2; + reg = write_reg(client, 0x22, (u8)reg, dec->channel); + } + + if (reg >= 0) { + dec->input = input; + dec->update = 0; + } else + return reg; + return 0; +} + +static int tw2804_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + /*TODO need select between 3fmt: + * bt_656, + * bt_601_8bit, + * bt_656_dual, + */ + return 0; +} + +int tw2804_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct wis_tw2804 *dec = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u32 reg = read_reg(client, 0x78, 0); + + if (enable == 1) + write_reg(client, 0x78, reg & ~(1<channel), 0); + else + write_reg(client, 0x78, reg | (1<channel), 0); + + return 0; +} + +static const struct v4l2_subdev_video_ops tw2804_video_ops = { + .s_routing = tw2804_s_video_routing, + .s_mbus_fmt = tw2804_s_mbus_fmt, + .s_stream = tw2804_s_stream, +}; + +static const struct v4l2_subdev_ops tw2804_ops = { + .core = &tw2804_core_ops, + .video = &tw2804_video_ops, +}; + static int wis_tw2804_command(struct i2c_client *client, unsigned int cmd, void *arg) { - struct wis_tw2804 *dec = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct wis_tw2804 *dec = to_state(sd); + int *input; if (cmd == DECODER_SET_CHANNEL) { - int *input = arg; + input = arg; if (*input < 0 || *input > 3) { printk(KERN_ERR "wis-tw2804: channel %d is not " @@ -154,139 +495,6 @@ static int wis_tw2804_command(struct i2c_client *client, "channel number is set\n", cmd); return 0; } - - switch (cmd) { - case VIDIOC_S_STD: - { - v4l2_std_id *input = arg; - u8 regs[] = { - 0x01, *input & V4L2_STD_NTSC ? 0xc4 : 0x84, - 0x09, *input & V4L2_STD_NTSC ? 0x07 : 0x04, - 0x0a, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, - 0x0b, *input & V4L2_STD_NTSC ? 0x07 : 0x04, - 0x0c, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, - 0x0d, *input & V4L2_STD_NTSC ? 0x40 : 0x4a, - 0x16, *input & V4L2_STD_NTSC ? 0x00 : 0x40, - 0x17, *input & V4L2_STD_NTSC ? 0x00 : 0x40, - 0x20, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, - 0x21, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, - 0xff, 0xff, - }; - write_regs(client, regs, dec->channel); - dec->norm = *input; - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_CONTRAST: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_SATURATION: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_HUE: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - } - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 255) - dec->brightness = 255; - else if (ctrl->value < 0) - dec->brightness = 0; - else - dec->brightness = ctrl->value; - write_reg(client, 0x12, dec->brightness, dec->channel); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 255) - dec->contrast = 255; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - write_reg(client, 0x11, dec->contrast, dec->channel); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 255) - dec->saturation = 255; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - write_reg(client, 0x10, dec->saturation, dec->channel); - break; - case V4L2_CID_HUE: - if (ctrl->value > 255) - dec->hue = 255; - else if (ctrl->value < 0) - dec->hue = 0; - else - dec->hue = ctrl->value; - write_reg(client, 0x0f, dec->hue, dec->channel); - break; - } - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } - break; - } - default: - break; - } return 0; } @@ -295,21 +503,28 @@ static int wis_tw2804_probe(struct i2c_client *client, { struct i2c_adapter *adapter = client->adapter; struct wis_tw2804 *dec; + struct v4l2_subdev *sd; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL); + dec = kzalloc(sizeof(struct wis_tw2804), GFP_KERNEL); + if (dec == NULL) return -ENOMEM; - + sd = &dec->sd; + dec->update = 1; dec->channel = -1; dec->norm = V4L2_STD_NTSC; dec->brightness = 128; dec->contrast = 128; dec->saturation = 128; dec->hue = 128; - i2c_set_clientdata(client, dec); + dec->gain = 128; + dec->cr_gain = 128; + dec->b_balance = 122; + dec->r_balance = 122; + v4l2_i2c_subdev_init(sd, client, &tw2804_ops); printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n", client->addr, adapter->name); @@ -319,9 +534,10 @@ static int wis_tw2804_probe(struct i2c_client *client, static int wis_tw2804_remove(struct i2c_client *client) { - struct wis_tw2804 *dec = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); - kfree(dec); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -- cgit v1.2.3 From e2b710bfde37dcc5e5c55fe09e640c1a218a81a2 Mon Sep 17 00:00:00 2001 From: Ismael Luceno Date: Fri, 11 May 2012 02:14:51 -0300 Subject: [media] au0828: Add USB ID used by many dongles Tested with Yfeng 680 ATV dongle. Signed-off-by: Ismael Luceno Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/au0828/au0828-cards.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index 1c6015a04f96..e3fe9a6637f6 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -325,6 +325,8 @@ struct usb_device_id au0828_usb_id_table[] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, { USB_DEVICE(0x2040, 0x7281), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, + { USB_DEVICE(0x05e1, 0x0480), + .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, { USB_DEVICE(0x2040, 0x8200), .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, { USB_DEVICE(0x2040, 0x7260), -- cgit v1.2.3 From 6b363f9f97af10767e72a04c2a5cabfc32133cc4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 11 May 2012 09:55:59 -0300 Subject: [media] uvcvideo: Fix V4L2 button controls that share the same UVC control The Logitech pan/tilt reset UVC control contains two V4L2 button controls to reset pan and tilt. As the UVC control is not marked as auto-update, the button bits are set but never reset. A pan reset that follows a tilt reset would thus reset both pan and tilt. Fix this by not caching the control value of write-only controls. All standard UVC controls are either readable or auto-update, so this will not cause any regression and will not result in extra USB requests. Reported-by: Hans de Goede Signed-off-by: Laurent Pinchart Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index f3bd66c500b6..af26bbe6f76e 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1351,9 +1351,12 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, /* Reset the loaded flag for auto-update controls that were * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent - * uvc_ctrl_get from using the cached value. + * uvc_ctrl_get from using the cached value, and for write-only + * controls to prevent uvc_ctrl_set from setting bits not + * explicitly set by the user. */ - if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE) + if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE || + !(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) ctrl->loaded = 0; if (!ctrl->dirty) -- cgit v1.2.3 From 7b134e85b1d9eaf91c5b05bf9832c2d18b747072 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 11 May 2012 11:45:42 -0300 Subject: [media] cx23885: TeVii s471 card support The card is similar to TeVii s470, but has different LNB power control. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/ds3000.c | 5 ++++- drivers/media/video/cx23885/cx23885-cards.c | 9 +++++++++ drivers/media/video/cx23885/cx23885-core.c | 7 +++++++ drivers/media/video/cx23885/cx23885-dvb.c | 7 +++++++ drivers/media/video/cx23885/cx23885.h | 1 + 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index af65d013db11..4c8ac2657c4a 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -1114,7 +1114,10 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) ds3000_writereg(state, ds3000_dvbs2_init_tab[i], ds3000_dvbs2_init_tab[i + 1]); - ds3000_writereg(state, 0xfe, 0x98); + if (c->symbol_rate >= 30000000) + ds3000_writereg(state, 0xfe, 0x54); + else + ds3000_writereg(state, 0xfe, 0x98); break; default: return 1; diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 19b5499d2624..13739e002a63 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -497,6 +497,10 @@ struct cx23885_board cx23885_boards[] = { .name = "TerraTec Cinergy T PCIe Dual", .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_TEVII_S471] = { + .name = "TeVii S471", + .portb = CX23885_MPEG_DVB, } }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -705,6 +709,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x153b, .subdevice = 0x117e, .card = CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL, + }, { + .subvendor = 0xd471, + .subdevice = 0x9022, + .card = CX23885_BOARD_TEVII_S471, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1460,6 +1468,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_TEVII_S471: case CX23885_BOARD_DVBWORLD_2005: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 6ad227029a0f..697728f09430 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -1046,6 +1046,13 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) if (cx23885_boards[dev->board].ci_type > 0) cx_clear(RDR_RDRCTL1, 1 << 8); + switch (dev->board) { + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_TEVII_S471: + cx_clear(RDR_RDRCTL1, 1 << 8); + break; + } + return 0; } diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 6835eb1fc093..a80a92c47455 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -1173,6 +1173,13 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + case CX23885_BOARD_TEVII_S471: + i2c_bus = &dev->i2c_bus[1]; + + fe0->dvb.frontend = dvb_attach(ds3000_attach, + &tevii_ds3000_config, + &i2c_bus->i2c_adap); + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index f020f0568df4..d884784a1c85 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -89,6 +89,7 @@ #define CX23885_BOARD_MPX885 32 #define CX23885_BOARD_MYGICA_X8507 33 #define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34 +#define CX23885_BOARD_TEVII_S471 35 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 -- cgit v1.2.3 From 973848978be863607282391868efd77ace64278d Mon Sep 17 00:00:00 2001 From: joseph daniel Date: Sun, 13 May 2012 11:36:57 -0300 Subject: [media] staging/media/as102: remove version.h include at as102_fe.c There was a warning when ran "make versioncheck" drivers/staging/media/as102/as102_fe.c: 20 linux/version.h not needed. Signed-off-by: joseph daniel Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/as102/as102_fe.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c index 5917657b9d0f..9ce8c9daa2e7 100644 --- a/drivers/staging/media/as102/as102_fe.c +++ b/drivers/staging/media/as102/as102_fe.c @@ -17,8 +17,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include - #include "as102_drv.h" #include "as10x_types.h" #include "as10x_cmd.h" -- cgit v1.2.3 From 9554d57ebbc78db6d66057bfc12552247a7567da Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 20 May 2012 11:30:00 -0300 Subject: Revert "[media] staging: media: go7007: Adlink MPG24 board issues" This patch were applied by mistake, as it were rejected by Don, who requested it to be broken into per-change patches. This reverts commit 0982db20aba5fd124bb5942d679d8732478e992a. Cc: Dan Carpenter Cc: Volokh Konstantin Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/README | 18 - drivers/staging/media/go7007/go7007-driver.c | 27 +- drivers/staging/media/go7007/go7007-priv.h | 2 +- drivers/staging/media/go7007/go7007-usb.c | 5 +- drivers/staging/media/go7007/go7007-v4l2.c | 7 +- drivers/staging/media/go7007/wis-tw2804.c | 512 ++++++++------------------- 6 files changed, 164 insertions(+), 407 deletions(-) diff --git a/drivers/staging/media/go7007/README b/drivers/staging/media/go7007/README index 082a6818c64e..48f447637817 100644 --- a/drivers/staging/media/go7007/README +++ b/drivers/staging/media/go7007/README @@ -5,24 +5,6 @@ Todo: and added to the build. - testing? - handle churn in v4l layer. - - Some features for wis-tw2804 subdev control (comb filter,motion detector sensitive & mask,more over...) - - go7007-v4l2.c need rewrite with new v4l2 style without nonstandart IO controls (set detector & bitrate) - -05/05/2012 3.4.0-rc+: -Changes: - - When go7007 reset device, i2c was not worked (need rewrite GPIO5) - - As wis2804 has i2c_addr=0x00/*really*/, so Need set I2C_CLIENT_TEN flag for validity - - Some main nonzero initialization, rewrites with kzalloc instead kmalloc - - STATUS_SHUTDOWN was placed in incorrect place, so if firmware wasn`t loaded, we - failed v4l2_device_unregister with kernel panic (OOPS) - - Some new v4l2 style features as call_all(...s_stream...) for using subdev calls - - wis-tw2804.ko module code was incompatible with 3.4.x branch in initialization v4l2_subdev parts. - now i2c_get_clientdata(...) contains v4l2_subdev struct instead non standart wis_tw2804 struct - -Adds: - - Additional chipset wis2804 controls with: gain,auto gain,inputs[0,1],color kill,chroma gain,gain balances, - for all 4 channels (from tw2804.pdf) - - Power control for each 4 ADC up when s_stream(...,1), down otherwise in wis-tw2804 module Please send patchs to Greg Kroah-Hartman and Cc: Ross Cohen as well. diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index 2dff9b5906b9..ece2dd146487 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -173,11 +173,6 @@ static int go7007_init_encoder(struct go7007 *go) go7007_write_addr(go, 0x3c82, 0x0001); go7007_write_addr(go, 0x3c80, 0x00fe); } - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { - /* set GPIO5 to be an output, currently low */ - go7007_write_addr(go, 0x3c82, 0x0000); - go7007_write_addr(go, 0x3c80, 0x00df); - } return 0; } @@ -197,23 +192,17 @@ int go7007_reset_encoder(struct go7007 *go) /* * Attempt to instantiate an I2C client by ID, probably loading a module. */ -static int init_i2c_module(struct i2c_adapter *adapter, const struct go_i2c *const i2c) +static int init_i2c_module(struct i2c_adapter *adapter, const char *type, + int addr) { struct go7007 *go = i2c_get_adapdata(adapter); struct v4l2_device *v4l2_dev = &go->v4l2_dev; - struct i2c_board_info info; - - memset(&info, 0, sizeof(info)); - strlcpy(info.type, i2c->type, sizeof(info.type)); - info.addr = i2c->addr; - if (i2c->id == I2C_DRIVERID_WIS_TW2804) - info.flags |= I2C_CLIENT_TEN; - if (v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, NULL)) + if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL)) return 0; - printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", i2c->type); - return -EINVAL; + printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type); + return -1; } /* @@ -249,7 +238,9 @@ int go7007_register_encoder(struct go7007 *go) } if (go->i2c_adapter_online) { for (i = 0; i < go->board_info->num_i2c_devs; ++i) - init_i2c_module(&go->i2c_adapter, &go->board_info->i2c_devs[i]); + init_i2c_module(&go->i2c_adapter, + go->board_info->i2c_devs[i].type, + go->board_info->i2c_devs[i].addr); if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) i2c_clients_command(&go->i2c_adapter, DECODER_SET_CHANNEL, &go->channel_number); @@ -580,7 +571,7 @@ struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev) struct go7007 *go; int i; - go = kzalloc(sizeof(struct go7007), GFP_KERNEL); + go = kmalloc(sizeof(struct go7007), GFP_KERNEL); if (go == NULL) return NULL; go->dev = dev; diff --git a/drivers/staging/media/go7007/go7007-priv.h b/drivers/staging/media/go7007/go7007-priv.h index b7b939a1967e..b58c394c6555 100644 --- a/drivers/staging/media/go7007/go7007-priv.h +++ b/drivers/staging/media/go7007/go7007-priv.h @@ -88,7 +88,7 @@ struct go7007_board_info { int audio_bclk_div; int audio_main_div; int num_i2c_devs; - struct go_i2c { + struct { const char *type; int id; int addr; diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 9dbf5ecd05a2..5443e25086e9 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1110,6 +1110,9 @@ static int go7007_usb_probe(struct usb_interface *intf, } else { u16 channel; + /* set GPIO5 to be an output, currently low */ + go7007_write_addr(go, 0x3c82, 0x0000); + go7007_write_addr(go, 0x3c80, 0x00df); /* read channel number from GPIO[1:0] */ go7007_read_addr(go, 0x3c81, &channel); channel &= 0x3; @@ -1242,6 +1245,7 @@ static void go7007_usb_disconnect(struct usb_interface *intf) struct urb *vurb, *aurb; int i; + go->status = STATUS_SHUTDOWN; usb_kill_urb(usb->intr_urb); /* Free USB-related structs */ @@ -1265,7 +1269,6 @@ static void go7007_usb_disconnect(struct usb_interface *intf) kfree(go->hpi_context); go7007_remove(go); - go->status = STATUS_SHUTDOWN; } static struct usb_driver go7007_usb_driver = { diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index b8f2eb6bc3ef..c184ad30fbd8 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -98,7 +98,7 @@ static int go7007_open(struct file *file) if (go->status != STATUS_ONLINE) return -EBUSY; - gofh = kzalloc(sizeof(struct go7007_file), GFP_KERNEL); + gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL); if (gofh == NULL) return -ENOMEM; ++go->ref_count; @@ -953,7 +953,6 @@ static int vidioc_streamon(struct file *file, void *priv, } mutex_unlock(&go->hw_lock); mutex_unlock(&gofh->lock); - call_all(&go->v4l2_dev, video, s_stream, 1); return retval; } @@ -969,7 +968,6 @@ static int vidioc_streamoff(struct file *file, void *priv, mutex_lock(&gofh->lock); go7007_streamoff(go); mutex_unlock(&gofh->lock); - call_all(&go->v4l2_dev, video, s_stream, 0); return 0; } @@ -1834,6 +1832,5 @@ void go7007_v4l2_remove(struct go7007 *go) mutex_unlock(&go->hw_lock); if (go->video_dev) video_unregister_device(go->video_dev); - if (go->status != STATUS_SHUTDOWN) - v4l2_device_unregister(&go->v4l2_dev); + v4l2_device_unregister(&go->v4l2_dev); } diff --git a/drivers/staging/media/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c index 9afc5df5902e..9134f03e3cf0 100644 --- a/drivers/staging/media/go7007/wis-tw2804.c +++ b/drivers/staging/media/go7007/wis-tw2804.c @@ -21,27 +21,16 @@ #include #include #include -#include -#include #include "wis-i2c.h" struct wis_tw2804 { - struct v4l2_subdev sd; - u8 channel:2; - u8 input:1; - u8 update:1; - u8 auto_gain:1; - u8 ckil:1; + int channel; int norm; - u8 brightness; - u8 contrast; - u8 saturation; - u8 hue; - u8 gain; - u8 cr_gain; - u8 r_balance; - u8 b_balance; + int brightness; + int contrast; + int saturation; + int hue; }; static u8 global_registers[] = { @@ -52,7 +41,6 @@ static u8 global_registers[] = { 0x3d, 0x80, 0x3e, 0x82, 0x3f, 0x82, - 0x78, 0x0f, 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ }; @@ -115,358 +103,29 @@ static u8 channel_registers[] = { 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ }; -static s32 write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel) +static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel) { return i2c_smbus_write_byte_data(client, reg | (channel << 6), value); } -static int write_regs(struct i2c_client *client, u8 *regs, u8 channel) +static int write_regs(struct i2c_client *client, u8 *regs, int channel) { int i; for (i = 0; regs[i] != 0xff; i += 2) if (i2c_smbus_write_byte_data(client, regs[i] | (channel << 6), regs[i + 1]) < 0) - return -EINVAL; + return -1; return 0; } -static s32 read_reg(struct i2c_client *client, u8 reg, u8 channel) -{ - return i2c_smbus_read_byte_data(client, (reg) | (channel << 6)); -} - -static inline struct wis_tw2804 *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct wis_tw2804, sd); -} - -static int tw2804_log_status(struct v4l2_subdev *sd) -{ - struct wis_tw2804 *state = to_state(sd); - v4l2_info(sd, "Standard: %s\n", state->norm == V4L2_STD_NTSC ? "NTSC" : - state->norm == V4L2_STD_PAL ? "PAL" : "unknown"); - v4l2_info(sd, "Channel: %d\n", state->channel); - v4l2_info(sd, "Input: %d\n", state->input); - v4l2_info(sd, "Brightness: %d\n", state->brightness); - v4l2_info(sd, "Contrast: %d\n", state->contrast); - v4l2_info(sd, "Saturation: %d\n", state->saturation); - v4l2_info(sd, "Hue: %d\n", state->hue); - return 0; -} - -static int tw2804_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query) -{ - static const u32 user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUTOGAIN, - V4L2_CID_COLOR_KILLER, - V4L2_CID_GAIN, - V4L2_CID_CHROMA_GAIN, - V4L2_CID_BLUE_BALANCE, - V4L2_CID_RED_BALANCE, - 0 - }; - - static const u32 *ctrl_classes[] = { - user_ctrls, - NULL - }; - - query->id = v4l2_ctrl_next(ctrl_classes, query->id); - - switch (query->id) { - case V4L2_CID_USER_CLASS: - return v4l2_ctrl_query_fill(query, 0, 0, 0, 0); - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); - case V4L2_CID_AUTOGAIN: - return v4l2_ctrl_query_fill(query, 0, 1, 1, 0); - case V4L2_CID_COLOR_KILLER: - return v4l2_ctrl_query_fill(query, 0, 1, 1, 0); - case V4L2_CID_GAIN: - return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); - case V4L2_CID_CHROMA_GAIN: - return v4l2_ctrl_query_fill(query, 0, 255, 1, 128); - case V4L2_CID_BLUE_BALANCE: - return v4l2_ctrl_query_fill(query, 0, 255, 1, 122); - case V4L2_CID_RED_BALANCE: - return v4l2_ctrl_query_fill(query, 0, 255, 1, 122); - default: - return -EINVAL; - } -} - -s32 get_ctrl_addr(int ctrl) -{ - switch (ctrl) { - case V4L2_CID_BRIGHTNESS: - return 0x12; - case V4L2_CID_CONTRAST: - return 0x11; - case V4L2_CID_SATURATION: - return 0x10; - case V4L2_CID_HUE: - return 0x0f; - case V4L2_CID_AUTOGAIN: - return 0x02; - case V4L2_CID_COLOR_KILLER: - return 0x14; - case V4L2_CID_GAIN: - return 0x3c; - case V4L2_CID_CHROMA_GAIN: - return 0x3d; - case V4L2_CID_RED_BALANCE: - return 0x3f; - case V4L2_CID_BLUE_BALANCE: - return 0x3e; - default: - return -EINVAL; - } -} - -static int tw2804_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct wis_tw2804 *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - s32 addr = get_ctrl_addr(ctrl->id); - s32 val = 0; - - if (addr == -EINVAL) - return -EINVAL; - - if (state->update) { - val = read_reg(client, addr, ctrl->id == V4L2_CID_GAIN || - ctrl->id == V4L2_CID_CHROMA_GAIN || - ctrl->id == V4L2_CID_RED_BALANCE || - ctrl->id == V4L2_CID_BLUE_BALANCE ? 0 : state->channel); - if (val < 0) - return val; - } - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (state->update) - state->brightness = val; - ctrl->value = state->brightness; - break; - case V4L2_CID_CONTRAST: - if (state->update) - state->contrast = val; - ctrl->value = state->contrast; - break; - case V4L2_CID_SATURATION: - if (state->update) - state->saturation = val; - ctrl->value = state->saturation; - break; - case V4L2_CID_HUE: - if (state->update) - state->hue = val; - ctrl->value = state->hue; - break; - case V4L2_CID_AUTOGAIN: - if (state->update) - state->auto_gain = val & (1<<7) ? 1 : 0; - ctrl->value = state->auto_gain; - break; - case V4L2_CID_COLOR_KILLER: - if (state->update) - state->ckil = (val & 0x03) == 0x03 ? 1 : 0; - ctrl->value = state->ckil; - break; - case V4L2_CID_GAIN: - if (state->update) - state->gain = val; - ctrl->value = state->gain; - break; - case V4L2_CID_CHROMA_GAIN: - if (state->update) - state->cr_gain = val; - ctrl->value = state->cr_gain; - break; - case V4L2_CID_RED_BALANCE: - if (state->update) - state->r_balance = val; - ctrl->value = state->r_balance; - break; - case V4L2_CID_BLUE_BALANCE: - if (state->update) - state->b_balance = val; - ctrl->value = state->b_balance; - break; - default: - return -EINVAL; - } - - state->update = 0; - return 0; -} - -static int tw2804_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct wis_tw2804 *dec = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - s32 reg = 0; - s32 addr = get_ctrl_addr(ctrl->id); - - if (addr == -EINVAL) - return -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - reg = read_reg(client, addr, dec->channel); - if (reg > 0) { - if (ctrl->value == 0) - ctrl->value = reg & ~(1<<7); - else - ctrl->value = reg | 1<<7; - } else - return reg; - break; - case V4L2_CID_COLOR_KILLER: - reg = read_reg(client, addr, dec->channel); - if (reg > 0) - ctrl->value = (reg & ~(0x03)) | (ctrl->value == 0 ? 0x02 : 0x03); - else - return reg; - break; - default: - break; - } - - ctrl->value = ctrl->value > 255 ? 255 : (ctrl->value < 0 ? 0 : ctrl->value); - reg = write_reg(client, addr, (u8)ctrl->value, ctrl->id == V4L2_CID_GAIN || - ctrl->id == V4L2_CID_CHROMA_GAIN || - ctrl->id == V4L2_CID_RED_BALANCE || - ctrl->id == V4L2_CID_BLUE_BALANCE ? 0 : dec->channel); - - if (reg < 0) { - v4l2_err(&dec->sd, "Can`t set_ctrl value:id=%d;value=%d\n", ctrl->id, ctrl->value); - return reg; - } - - dec->update = 1; - return tw2804_g_ctrl(sd, ctrl); -} - -static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct wis_tw2804 *dec = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - u8 regs[] = { - 0x01, norm&V4L2_STD_NTSC ? 0xc4 : 0x84, - 0x09, norm&V4L2_STD_NTSC ? 0x07 : 0x04, - 0x0a, norm&V4L2_STD_NTSC ? 0xf0 : 0x20, - 0x0b, norm&V4L2_STD_NTSC ? 0x07 : 0x04, - 0x0c, norm&V4L2_STD_NTSC ? 0xf0 : 0x20, - 0x0d, norm&V4L2_STD_NTSC ? 0x40 : 0x4a, - 0x16, norm&V4L2_STD_NTSC ? 0x00 : 0x40, - 0x17, norm&V4L2_STD_NTSC ? 0x00 : 0x40, - 0x20, norm&V4L2_STD_NTSC ? 0x07 : 0x0f, - 0x21, norm&V4L2_STD_NTSC ? 0x07 : 0x0f, - 0xff, 0xff, - }; - write_regs(client, regs, dec->channel); - dec->norm = norm; - return 0; -} - -static const struct v4l2_subdev_core_ops tw2804_core_ops = { - .log_status = tw2804_log_status, - .g_ctrl = tw2804_g_ctrl, - .s_ctrl = tw2804_s_ctrl, - .queryctrl = tw2804_queryctrl, - .s_std = tw2804_s_std, -}; - -static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, - u32 config) -{ - struct wis_tw2804 *dec = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - s32 reg = 0; - - if (0 > input || input > 1) - return -EINVAL; - - if (input == dec->input && !dec->update) - return 0; - - reg = read_reg(client, 0x22, dec->channel); - - if (reg >= 0) { - if (input == 0) - reg &= ~(1<<2); - else - reg |= 1<<2; - reg = write_reg(client, 0x22, (u8)reg, dec->channel); - } - - if (reg >= 0) { - dec->input = input; - dec->update = 0; - } else - return reg; - return 0; -} - -static int tw2804_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - /*TODO need select between 3fmt: - * bt_656, - * bt_601_8bit, - * bt_656_dual, - */ - return 0; -} - -int tw2804_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct wis_tw2804 *dec = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u32 reg = read_reg(client, 0x78, 0); - - if (enable == 1) - write_reg(client, 0x78, reg & ~(1<channel), 0); - else - write_reg(client, 0x78, reg | (1<channel), 0); - - return 0; -} - -static const struct v4l2_subdev_video_ops tw2804_video_ops = { - .s_routing = tw2804_s_video_routing, - .s_mbus_fmt = tw2804_s_mbus_fmt, - .s_stream = tw2804_s_stream, -}; - -static const struct v4l2_subdev_ops tw2804_ops = { - .core = &tw2804_core_ops, - .video = &tw2804_video_ops, -}; - static int wis_tw2804_command(struct i2c_client *client, unsigned int cmd, void *arg) { - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct wis_tw2804 *dec = to_state(sd); - int *input; + struct wis_tw2804 *dec = i2c_get_clientdata(client); if (cmd == DECODER_SET_CHANNEL) { - input = arg; + int *input = arg; if (*input < 0 || *input > 3) { printk(KERN_ERR "wis-tw2804: channel %d is not " @@ -495,6 +154,139 @@ static int wis_tw2804_command(struct i2c_client *client, "channel number is set\n", cmd); return 0; } + + switch (cmd) { + case VIDIOC_S_STD: + { + v4l2_std_id *input = arg; + u8 regs[] = { + 0x01, *input & V4L2_STD_NTSC ? 0xc4 : 0x84, + 0x09, *input & V4L2_STD_NTSC ? 0x07 : 0x04, + 0x0a, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, + 0x0b, *input & V4L2_STD_NTSC ? 0x07 : 0x04, + 0x0c, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, + 0x0d, *input & V4L2_STD_NTSC ? 0x40 : 0x4a, + 0x16, *input & V4L2_STD_NTSC ? 0x00 : 0x40, + 0x17, *input & V4L2_STD_NTSC ? 0x00 : 0x40, + 0x20, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, + 0x21, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, + 0xff, 0xff, + }; + write_regs(client, regs, dec->channel); + dec->norm = *input; + break; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + case V4L2_CID_CONTRAST: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + case V4L2_CID_SATURATION: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + case V4L2_CID_HUE: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + } + break; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value > 255) + dec->brightness = 255; + else if (ctrl->value < 0) + dec->brightness = 0; + else + dec->brightness = ctrl->value; + write_reg(client, 0x12, dec->brightness, dec->channel); + break; + case V4L2_CID_CONTRAST: + if (ctrl->value > 255) + dec->contrast = 255; + else if (ctrl->value < 0) + dec->contrast = 0; + else + dec->contrast = ctrl->value; + write_reg(client, 0x11, dec->contrast, dec->channel); + break; + case V4L2_CID_SATURATION: + if (ctrl->value > 255) + dec->saturation = 255; + else if (ctrl->value < 0) + dec->saturation = 0; + else + dec->saturation = ctrl->value; + write_reg(client, 0x10, dec->saturation, dec->channel); + break; + case V4L2_CID_HUE: + if (ctrl->value > 255) + dec->hue = 255; + else if (ctrl->value < 0) + dec->hue = 0; + else + dec->hue = ctrl->value; + write_reg(client, 0x0f, dec->hue, dec->channel); + break; + } + break; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dec->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dec->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dec->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dec->hue; + break; + } + break; + } + default: + break; + } return 0; } @@ -503,28 +295,21 @@ static int wis_tw2804_probe(struct i2c_client *client, { struct i2c_adapter *adapter = client->adapter; struct wis_tw2804 *dec; - struct v4l2_subdev *sd; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - dec = kzalloc(sizeof(struct wis_tw2804), GFP_KERNEL); - + dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL); if (dec == NULL) return -ENOMEM; - sd = &dec->sd; - dec->update = 1; + dec->channel = -1; dec->norm = V4L2_STD_NTSC; dec->brightness = 128; dec->contrast = 128; dec->saturation = 128; dec->hue = 128; - dec->gain = 128; - dec->cr_gain = 128; - dec->b_balance = 122; - dec->r_balance = 122; - v4l2_i2c_subdev_init(sd, client, &tw2804_ops); + i2c_set_clientdata(client, dec); printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n", client->addr, adapter->name); @@ -534,10 +319,9 @@ static int wis_tw2804_probe(struct i2c_client *client, static int wis_tw2804_remove(struct i2c_client *client) { - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct wis_tw2804 *dec = i2c_get_clientdata(client); - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); + kfree(dec); return 0; } -- cgit v1.2.3 From 5085c99eeb8e47bcfc2cce6ba0ae03db057057ba Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 14 May 2012 09:52:37 -0300 Subject: [media] ati_remote: add keymap for Medion X10 OR2x remotes Add another Medion X10 remote keymap. This is for the Medion OR2x remotes with the Windows MCE button. The receiver shipped with this remote has the same USB ID as the other Medion receivers, but the name is different and is therefore used to detect this variant. Signed-off-by: Anssi Hannula Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ati_remote.c | 30 ++++++- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-medion-x10-or2x.c | 108 ++++++++++++++++++++++++++ include/media/rc-map.h | 1 + 4 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 drivers/media/rc/keymaps/rc-medion-x10-or2x.c diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 26fa043d3de7..7be377fc1be8 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -161,10 +161,32 @@ static const char *get_medion_keymap(struct usb_interface *interface) { struct usb_device *udev = interface_to_usbdev(interface); - /* The receiver shipped with the "Digitainer" variant helpfully has - * a single additional bit set in its descriptor. */ - if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) - return RC_MAP_MEDION_X10_DIGITAINER; + /* + * There are many different Medion remotes shipped with a receiver + * with the same usb id, but the receivers have subtle differences + * in the USB descriptors allowing us to detect them. + */ + + if (udev->manufacturer && udev->product) { + if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) { + + if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc") + && !strcmp(udev->product, "USB Receiver")) + return RC_MAP_MEDION_X10_DIGITAINER; + + if (!strcmp(udev->manufacturer, "X10 WTI") + && !strcmp(udev->product, "RF receiver")) + return RC_MAP_MEDION_X10_OR2X; + } else { + + if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc") + && !strcmp(udev->product, "USB Receiver")) + return RC_MAP_MEDION_X10; + } + } + + dev_info(&interface->dev, + "Unknown Medion X10 receiver, using default ati_remote Medion keymap\n"); return RC_MAP_MEDION_X10; } diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 38ff6e0e099a..6d41a29861ac 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-manli.o \ rc-medion-x10.o \ rc-medion-x10-digitainer.o \ + rc-medion-x10-or2x.o \ rc-msi-digivox-ii.o \ rc-msi-digivox-iii.o \ rc-msi-tvanywhere.o \ diff --git a/drivers/media/rc/keymaps/rc-medion-x10-or2x.c b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c new file mode 100644 index 000000000000..b077300ecb5c --- /dev/null +++ b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c @@ -0,0 +1,108 @@ +/* + * Medion X10 OR22/OR24 RF remote keytable + * + * Copyright (C) 2012 Anssi Hannula + * + * This keymap is for several Medion X10 remotes that have the Windows MCE + * button. This has been tested with a "RF VISTA Remote Control", OR24V, + * P/N 20035335, but should work with other variants that have the same + * buttons, such as OR22V and OR24E. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +static struct rc_map_table medion_x10_or2x[] = { + { 0x02, KEY_POWER }, + { 0x16, KEY_TEXT }, /* "T" in a box, for teletext */ + + { 0x09, KEY_VOLUMEUP }, + { 0x08, KEY_VOLUMEDOWN }, + { 0x00, KEY_MUTE }, + { 0x0b, KEY_CHANNELUP }, + { 0x0c, KEY_CHANNELDOWN }, + + { 0x32, KEY_RED }, + { 0x33, KEY_GREEN }, + { 0x34, KEY_YELLOW }, + { 0x35, KEY_BLUE }, + + { 0x18, KEY_PVR }, /* record symbol inside a tv symbol */ + { 0x04, KEY_DVD }, /* disc symbol */ + { 0x31, KEY_EPG }, /* a tv schedule symbol */ + { 0x1c, KEY_TV }, /* play symbol inside a tv symbol */ + { 0x20, KEY_BACK }, + { 0x2f, KEY_INFO }, + + { 0x1a, KEY_UP }, + { 0x22, KEY_DOWN }, + { 0x1d, KEY_LEFT }, + { 0x1f, KEY_RIGHT }, + { 0x1e, KEY_OK }, + + { 0x1b, KEY_MEDIA }, /* Windows MCE button */ + + { 0x21, KEY_PREVIOUS }, + { 0x23, KEY_NEXT }, + { 0x24, KEY_REWIND }, + { 0x26, KEY_FORWARD }, + { 0x25, KEY_PLAY }, + { 0x28, KEY_STOP }, + { 0x29, KEY_PAUSE }, + { 0x27, KEY_RECORD }, + + { 0x0d, KEY_1 }, + { 0x0e, KEY_2 }, + { 0x0f, KEY_3 }, + { 0x10, KEY_4 }, + { 0x11, KEY_5 }, + { 0x12, KEY_6 }, + { 0x13, KEY_7 }, + { 0x14, KEY_8 }, + { 0x15, KEY_9 }, + { 0x17, KEY_0 }, + { 0x30, KEY_CLEAR }, + { 0x36, KEY_ENTER }, + { 0x37, KEY_NUMERIC_STAR }, + { 0x38, KEY_NUMERIC_POUND }, +}; + +static struct rc_map_list medion_x10_or2x_map = { + .map = { + .scan = medion_x10_or2x, + .size = ARRAY_SIZE(medion_x10_or2x), + .rc_type = RC_TYPE_OTHER, + .name = RC_MAP_MEDION_X10_OR2X, + } +}; + +static int __init init_rc_map_medion_x10_or2x(void) +{ + return rc_map_register(&medion_x10_or2x_map); +} + +static void __exit exit_rc_map_medion_x10_or2x(void) +{ + rc_map_unregister(&medion_x10_or2x_map); +} + +module_init(init_rc_map_medion_x10_or2x) +module_exit(exit_rc_map_medion_x10_or2x) + +MODULE_DESCRIPTION("Medion X10 OR22/OR24 RF remote keytable"); +MODULE_AUTHOR("Anssi Hannula "); +MODULE_LICENSE("GPL"); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 88583a6ff7f2..2e0f67db666f 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -114,6 +114,7 @@ void rc_map_init(void); #define RC_MAP_MANLI "rc-manli" #define RC_MAP_MEDION_X10 "rc-medion-x10" #define RC_MAP_MEDION_X10_DIGITAINER "rc-medion-x10-digitainer" +#define RC_MAP_MEDION_X10_OR2X "rc-medion-x10-or2x" #define RC_MAP_MSI_DIGIVOX_II "rc-msi-digivox-ii" #define RC_MAP_MSI_DIGIVOX_III "rc-msi-digivox-iii" #define RC_MAP_MSI_TVANYWHERE_PLUS "rc-msi-tvanywhere-plus" -- cgit v1.2.3 From be1027cc0f2b941fb45545a20940380673700472 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 14 May 2012 09:52:38 -0300 Subject: [media] ati_remote: add regular up/down buttons to Medion Digitainer keymap There are many different Medion X10 remotes that need slightly different keymaps. We may not yet have all the needed keymaps, in which case a wrong keymap may be used. This happened with Medion X10 OR2x remotes before the keymap for them was added, causing the ati_remote driver to select the Medion Digitainer keymap instead. Unfortunately, the Medion Digitainer keymap doesn't have the standard X10 up/down scancodes assigned to KEY_UP and KEY_DOWN keycodes, making wrongly assigned remotes mostly unusable. Add the regular KEY_UP and KEY_DOWN scancodes to the Medion X10 Digitainer keymap, making any Medion remote mostly usable even when wrongly used with that keymap (standard buttons, such as up/down/left/right, 0-9, play/stop/pause, have the same scancode in all the X10 remotes). Signed-off-by: Anssi Hannula Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/rc-medion-x10-digitainer.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c index 0a5ce84d9fd8..966f9b3c71da 100644 --- a/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c +++ b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c @@ -86,6 +86,14 @@ static struct rc_map_table medion_x10_digitainer[] = { { 0x14, KEY_8 }, { 0x15, KEY_9 }, { 0x17, KEY_0 }, + + /* these do not actually exist on this remote, but these scancodes + * exist on all other Medion X10 remotes and adding them here allows + * such remotes to be adequately usable with this keymap in case + * this keymap is wrongly used with them (which is quite possible as + * there are lots of different Medion X10 remotes): */ + { 0x1a, KEY_UP }, + { 0x22, KEY_DOWN }, }; static struct rc_map_list medion_x10_digitainer_map = { -- cgit v1.2.3 From 81cda577422b85f98efc350e2520f2a2dc5f3ddb Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Mon, 14 May 2012 17:01:17 -0300 Subject: [media] rc-it913x=v2 Incorrect assigned KEY_1 Correct incorrect scancode for KEY_1 Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/rc-it913x-v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/keymaps/rc-it913x-v2.c b/drivers/media/rc/keymaps/rc-it913x-v2.c index 28e376e18b99..bd42a30ec06f 100644 --- a/drivers/media/rc/keymaps/rc-it913x-v2.c +++ b/drivers/media/rc/keymaps/rc-it913x-v2.c @@ -40,7 +40,7 @@ static struct rc_map_table it913x_v2_rc[] = { /* Type 2 */ /* keys stereo, snapshot unassigned */ { 0x866b00, KEY_0 }, - { 0x866b1b, KEY_1 }, + { 0x866b01, KEY_1 }, { 0x866b02, KEY_2 }, { 0x866b03, KEY_3 }, { 0x866b04, KEY_4 }, -- cgit v1.2.3 From f27b853ea24a9b70585f9251384d97929e6551c3 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 14 May 2012 21:36:00 -0300 Subject: [media] rc: Fix invalid free_region and/or free_irq on probe failure fintek-cir, ite-cir and nuvoton-cir may try to free an I/O region and/or IRQ handler that was never allocated after a failure in their respective probe functions. Add and use separate labels on the failure path so they will do the right cleanup after each possible point of failure. Compile-tested only. Signed-off-by: Ben Hutchings Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/fintek-cir.c | 13 ++++++------- drivers/media/rc/ite-cir.c | 14 ++++++-------- drivers/media/rc/nuvoton-cir.c | 26 ++++++++++++-------------- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 4a3a238bcfbc..6aabf7ae3a31 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -556,11 +556,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, FINTEK_DRIVER_NAME, (void *)fintek)) - goto failure; + goto failure2; ret = rc_register_device(rdev); if (ret) - goto failure; + goto failure3; device_init_wakeup(&pdev->dev, true); fintek->rdev = rdev; @@ -570,12 +570,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id return 0; +failure3: + free_irq(fintek->cir_irq, fintek); +failure2: + release_region(fintek->cir_addr, fintek->cir_port_len); failure: - if (fintek->cir_irq) - free_irq(fintek->cir_irq, fintek); - if (fintek->cir_addr) - release_region(fintek->cir_addr, fintek->cir_port_len); - rc_free_device(rdev); kfree(fintek); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 0e49c99abf68..36fe5a349b95 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1598,24 +1598,22 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, ITE_DRIVER_NAME, (void *)itdev)) - goto failure; + goto failure2; ret = rc_register_device(rdev); if (ret) - goto failure; + goto failure3; itdev->rdev = rdev; ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); return 0; +failure3: + free_irq(itdev->cir_irq, itdev); +failure2: + release_region(itdev->cir_addr, itdev->params.io_region_size); failure: - if (itdev->cir_irq) - free_irq(itdev->cir_irq, itdev); - - if (itdev->cir_addr) - release_region(itdev->cir_addr, itdev->params.io_region_size); - rc_free_device(rdev); kfree(itdev); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 8b2c071ac0ab..dc8a7dddccd4 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1075,19 +1075,19 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure; + goto failure2; if (!request_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure; + goto failure3; if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure; + goto failure4; ret = rc_register_device(rdev); if (ret) - goto failure; + goto failure5; device_init_wakeup(&pdev->dev, true); nvt->rdev = rdev; @@ -1099,17 +1099,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) return 0; +failure5: + free_irq(nvt->cir_wake_irq, nvt); +failure4: + release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); +failure3: + free_irq(nvt->cir_irq, nvt); +failure2: + release_region(nvt->cir_addr, CIR_IOREG_LENGTH); failure: - if (nvt->cir_irq) - free_irq(nvt->cir_irq, nvt); - if (nvt->cir_addr) - release_region(nvt->cir_addr, CIR_IOREG_LENGTH); - - if (nvt->cir_wake_irq) - free_irq(nvt->cir_wake_irq, nvt); - if (nvt->cir_wake_addr) - release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); - rc_free_device(rdev); kfree(nvt); -- cgit v1.2.3 From bca7ad1a332a0754860bdd57b258f8e9ee5eb2a5 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 12 Apr 2012 12:39:36 -0300 Subject: [media] adv7180: add support to user controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Video user controls such as brightness, contrast, saturation, and hue are now handled. Signed-off-by: Federico Vaga Acked-by: Giancarlo Asnaghi Cc: Alan Cox Cc: Richard Röjfors Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/adv7180.c | 417 +++++++++++++++++++++++++++++++++++------- 1 file changed, 350 insertions(+), 67 deletions(-) diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c index b8b6c4b0cad4..174bffacf117 100644 --- a/drivers/media/video/adv7180.c +++ b/drivers/media/video/adv7180.c @@ -48,6 +48,7 @@ #define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 #define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 #define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 +#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f #define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 @@ -55,9 +56,29 @@ #define ADV7180_AUTODETECT_ENABLE_REG 0x07 #define ADV7180_AUTODETECT_DEFAULT 0x7f +#define ADV7180_CON_REG 0x08 /*Unsigned */ +#define CON_REG_MIN 0 +#define CON_REG_DEF 128 +#define CON_REG_MAX 255 + +#define ADV7180_BRI_REG 0x0a /*Signed */ +#define BRI_REG_MIN -128 +#define BRI_REG_DEF 0 +#define BRI_REG_MAX 127 + +#define ADV7180_HUE_REG 0x0b /*Signed, inverted */ +#define HUE_REG_MIN -127 +#define HUE_REG_DEF 0 +#define HUE_REG_MAX 128 + #define ADV7180_ADI_CTRL_REG 0x0e #define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 +#define ADV7180_PWR_MAN_REG 0x0f +#define ADV7180_PWR_MAN_ON 0x04 +#define ADV7180_PWR_MAN_OFF 0x24 +#define ADV7180_PWR_MAN_RES 0x80 + #define ADV7180_STATUS1_REG 0x10 #define ADV7180_STATUS1_IN_LOCK 0x01 #define ADV7180_STATUS1_AUTOD_MASK 0x70 @@ -78,6 +99,12 @@ #define ADV7180_ICONF1_PSYNC_ONLY 0x10 #define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 +#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ +#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ +#define SAT_REG_MIN 0 +#define SAT_REG_DEF 128 +#define SAT_REG_MAX 255 + #define ADV7180_IRQ1_LOCK 0x01 #define ADV7180_IRQ1_UNLOCK 0x02 #define ADV7180_ISR1_ADI 0x42 @@ -90,6 +117,9 @@ #define ADV7180_IMR3_ADI 0x4C #define ADV7180_IMR4_ADI 0x50 +#define ADV7180_NTSC_V_BIT_END_REG 0xE6 +#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F + struct adv7180_state { struct v4l2_subdev sd; struct work_struct work; @@ -97,6 +127,11 @@ struct adv7180_state { int irq; v4l2_std_id curr_norm; bool autodetect; + s8 brightness; + s16 hue; + u8 contrast; + u8 saturation; + u8 input; }; static v4l2_std_id adv7180_std_to_v4l2(u8 status1) @@ -155,7 +190,7 @@ static u32 adv7180_status_to_v4l2(u8 status1) } static int __adv7180_status(struct i2c_client *client, u32 *status, - v4l2_std_id *std) + v4l2_std_id *std) { int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); @@ -192,6 +227,36 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) return err; } +static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, + u32 output, u32 config) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (ret) + return ret; + + /*We cannot discriminate between LQFP and 40-pin LFCSP, so accept + * all inputs and let the card driver take care of validation + */ + if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) + goto out; + + ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); + + if (ret < 0) + goto out; + + ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, ret | input); + state->input = input; +out: + mutex_unlock(&state->mutex); + return ret; +} + static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) { struct adv7180_state *state = to_state(sd); @@ -205,7 +270,7 @@ static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) } static int adv7180_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) + struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -222,9 +287,10 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) /* all standards -> autodetect */ if (std == V4L2_STD_ALL) { - ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); if (ret < 0) goto out; @@ -236,7 +302,8 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) goto out; ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, ret); + ADV7180_INPUT_CONTROL_REG, + ret | state->input); if (ret < 0) goto out; @@ -249,14 +316,138 @@ out: return ret; } +static int adv7180_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, BRI_REG_MIN, BRI_REG_MAX, + 1, BRI_REG_DEF); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, HUE_REG_MIN, HUE_REG_MAX, + 1, HUE_REG_DEF); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, CON_REG_MIN, CON_REG_MAX, + 1, CON_REG_DEF); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, SAT_REG_MIN, SAT_REG_MAX, + 1, SAT_REG_DEF); + default: + break; + } + + return -EINVAL; +} + +static int adv7180_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->brightness; + break; + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + case V4L2_CID_CONTRAST: + ctrl->value = state->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = state->saturation; + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if ((ctrl->value > BRI_REG_MAX) + || (ctrl->value < BRI_REG_MIN)) { + ret = -ERANGE; + break; + } + state->brightness = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_BRI_REG, + state->brightness); + break; + case V4L2_CID_HUE: + if ((ctrl->value > HUE_REG_MAX) + || (ctrl->value < HUE_REG_MIN)) { + ret = -ERANGE; + break; + } + state->hue = ctrl->value; + /*Hue is inverted according to HSL chart */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_HUE_REG, -state->hue); + break; + case V4L2_CID_CONTRAST: + if ((ctrl->value > CON_REG_MAX) + || (ctrl->value < CON_REG_MIN)) { + ret = -ERANGE; + break; + } + state->contrast = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_CON_REG, + state->contrast); + break; + case V4L2_CID_SATURATION: + if ((ctrl->value > SAT_REG_MAX) + || (ctrl->value < SAT_REG_MIN)) { + ret = -ERANGE; + break; + } + /* + *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE + *Let's not confuse the user, everybody understands saturation + */ + state->saturation = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_SD_SAT_CB_REG, + state->saturation); + if (ret < 0) + break; + ret = i2c_smbus_write_byte_data(client, + ADV7180_SD_SAT_CR_REG, + state->saturation); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&state->mutex); + return ret; +} + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, + .s_routing = adv7180_s_routing, }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { .g_chip_ident = adv7180_g_chip_ident, .s_std = adv7180_s_std, + .queryctrl = adv7180_queryctrl, + .g_ctrl = adv7180_g_ctrl, + .s_ctrl = adv7180_s_ctrl, }; static const struct v4l2_subdev_ops adv7180_ops = { @@ -267,13 +458,13 @@ static const struct v4l2_subdev_ops adv7180_ops = { static void adv7180_work(struct work_struct *work) { struct adv7180_state *state = container_of(work, struct adv7180_state, - work); + work); struct i2c_client *client = v4l2_get_subdevdata(&state->sd); u8 isr3; mutex_lock(&state->mutex); i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); + ADV7180_ADI_CTRL_IRQ_SPACE); isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); /* clear */ i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); @@ -297,56 +488,51 @@ static irqreturn_t adv7180_irq(int irq, void *devid) return IRQ_HANDLED; } -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -static __devinit int adv7180_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int init_device(struct i2c_client *client, struct adv7180_state *state) { - struct adv7180_state *state; - struct v4l2_subdev *sd; int ret; - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); - if (state == NULL) { - ret = -ENOMEM; - goto err; - } - - state->irq = client->irq; - INIT_WORK(&state->work, adv7180_work); - mutex_init(&state->mutex); - state->autodetect = true; - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &adv7180_ops); - /* Initialize adv7180 */ /* Enable autodetection */ - ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); - if (ret < 0) - goto err_unreg_subdev; + if (state->autodetect) { + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); + if (ret < 0) + return ret; - ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG, - ADV7180_AUTODETECT_DEFAULT); - if (ret < 0) - goto err_unreg_subdev; + ret = + i2c_smbus_write_byte_data(client, + ADV7180_AUTODETECT_ENABLE_REG, + ADV7180_AUTODETECT_DEFAULT); + if (ret < 0) + return ret; + } else { + ret = v4l2_std_to_adv7180(state->curr_norm); + if (ret < 0) + return ret; + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ret | state->input); + if (ret < 0) + return ret; + + } /* ITU-R BT.656-4 compatible */ ret = i2c_smbus_write_byte_data(client, - ADV7180_EXTENDED_OUTPUT_CONTROL_REG, - ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); + ADV7180_EXTENDED_OUTPUT_CONTROL_REG, + ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); if (ret < 0) - goto err_unreg_subdev; + return ret; + + /* Manually set V bit end position in NTSC mode */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_NTSC_V_BIT_END_REG, + ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); + if (ret < 0) + return ret; /* read current norm */ __adv7180_status(client, NULL, &state->curr_norm); @@ -354,45 +540,109 @@ static __devinit int adv7180_probe(struct i2c_client *client, /* register for interrupts */ if (state->irq > 0) { ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME, - state); + state); if (ret) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); + ADV7180_ADI_CTRL_IRQ_SPACE); if (ret < 0) - goto err_unreg_subdev; + return ret; /* config the Interrupt pin to be active low */ ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, - ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY); + ADV7180_ICONF1_ACTIVE_LOW | + ADV7180_ICONF1_PSYNC_ONLY); if (ret < 0) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); if (ret < 0) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); if (ret < 0) - goto err_unreg_subdev; + return ret; /* enable AD change interrupts interrupts */ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, - ADV7180_IRQ3_AD_CHANGE); + ADV7180_IRQ3_AD_CHANGE); if (ret < 0) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); if (ret < 0) - goto err_unreg_subdev; + return ret; ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - 0); + 0); if (ret < 0) - goto err_unreg_subdev; + return ret; } + /*Set default value for controls */ + ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, + state->brightness); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, state->hue); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, + state->contrast); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, + state->saturation); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, + state->saturation); + if (ret < 0) + return ret; + + return 0; +} + +static __devinit int adv7180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7180_state *state; + struct v4l2_subdev *sd; + int ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr, client->adapter->name); + + state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); + if (state == NULL) { + ret = -ENOMEM; + goto err; + } + + state->irq = client->irq; + INIT_WORK(&state->work, adv7180_work); + mutex_init(&state->mutex); + state->autodetect = true; + state->brightness = BRI_REG_DEF; + state->hue = HUE_REG_DEF; + state->contrast = CON_REG_DEF; + state->saturation = SAT_REG_DEF; + state->input = 0; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &adv7180_ops); + + ret = init_device(client, state); + if (0 != ret) + goto err_unreg_subdev; return 0; err_unreg_subdev: @@ -432,16 +682,49 @@ static const struct i2c_device_id adv7180_id[] = { {}, }; +#ifdef CONFIG_PM +static int adv7180_suspend(struct i2c_client *client, pm_message_t state) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_OFF); + if (ret < 0) + return ret; + return 0; +} + +static int adv7180_resume(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_ON); + if (ret < 0) + return ret; + ret = init_device(client, state); + if (ret < 0) + return ret; + return 0; +} +#endif + MODULE_DEVICE_TABLE(i2c, adv7180_id); static struct i2c_driver adv7180_driver = { .driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME, - }, - .probe = adv7180_probe, - .remove = __devexit_p(adv7180_remove), - .id_table = adv7180_id, + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, + .probe = adv7180_probe, + .remove = __devexit_p(adv7180_remove), +#ifdef CONFIG_PM + .suspend = adv7180_suspend, + .resume = adv7180_resume, +#endif + .id_table = adv7180_id, }; module_i2c_driver(adv7180_driver); -- cgit v1.2.3 From a8f3c203e19b702fa5e8e83a9b6fb3c5a6d1cce4 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 12 Apr 2012 12:39:37 -0300 Subject: [media] videobuf-dma-contig: add cache support Signed-off-by: Federico Vaga Acked-by: Giancarlo Asnaghi Cc: Alan Cox Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf-dma-contig.c | 199 ++++++++++++++++++++++-------- include/media/videobuf-dma-contig.h | 10 ++ 2 files changed, 159 insertions(+), 50 deletions(-) diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index c9691115f2d2..b6b5cc1a43cb 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -27,6 +27,7 @@ struct videobuf_dma_contig_memory { u32 magic; void *vaddr; dma_addr_t dma_handle; + bool cached; unsigned long size; }; @@ -37,8 +38,58 @@ struct videobuf_dma_contig_memory { BUG(); \ } -static void -videobuf_vm_open(struct vm_area_struct *vma) +static int __videobuf_dc_alloc(struct device *dev, + struct videobuf_dma_contig_memory *mem, + unsigned long size, unsigned long flags) +{ + mem->size = size; + if (mem->cached) { + mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA); + if (mem->vaddr) { + int err; + + mem->dma_handle = dma_map_single(dev, mem->vaddr, + mem->size, + DMA_FROM_DEVICE); + err = dma_mapping_error(dev, mem->dma_handle); + if (err) { + dev_err(dev, "dma_map_single failed\n"); + + free_pages_exact(mem->vaddr, mem->size); + mem->vaddr = 0; + return err; + } + } + } else + mem->vaddr = dma_alloc_coherent(dev, mem->size, + &mem->dma_handle, flags); + + if (!mem->vaddr) { + dev_err(dev, "memory alloc size %ld failed\n", mem->size); + return -ENOMEM; + } + + dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size); + + return 0; +} + +static void __videobuf_dc_free(struct device *dev, + struct videobuf_dma_contig_memory *mem) +{ + if (mem->cached) { + if (!mem->vaddr) + return; + dma_unmap_single(dev, mem->dma_handle, mem->size, + DMA_FROM_DEVICE); + free_pages_exact(mem->vaddr, mem->size); + } else + dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); + + mem->vaddr = NULL; +} + +static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; @@ -91,12 +142,11 @@ static void videobuf_vm_close(struct vm_area_struct *vma) dev_dbg(q->dev, "buf[%d] freeing %p\n", i, mem->vaddr); - dma_free_coherent(q->dev, mem->size, - mem->vaddr, mem->dma_handle); + __videobuf_dc_free(q->dev, mem); mem->vaddr = NULL; } - q->bufs[i]->map = NULL; + q->bufs[i]->map = NULL; q->bufs[i]->baddr = 0; } @@ -107,8 +157,8 @@ static void videobuf_vm_close(struct vm_area_struct *vma) } static const struct vm_operations_struct videobuf_vm_ops = { - .open = videobuf_vm_open, - .close = videobuf_vm_close, + .open = videobuf_vm_open, + .close = videobuf_vm_close, }; /** @@ -178,26 +228,38 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, pages_done++; } - out_up: +out_up: up_read(¤t->mm->mmap_sem); return ret; } -static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) { struct videobuf_dma_contig_memory *mem; struct videobuf_buffer *vb; vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); if (vb) { - mem = vb->priv = ((char *)vb) + size; + vb->priv = ((char *)vb) + size; + mem = vb->priv; mem->magic = MAGIC_DC_MEM; + mem->cached = cached; } return vb; } +static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size) +{ + return __videobuf_alloc_vb(size, false); +} + +static struct videobuf_buffer *__videobuf_alloc_cached(size_t size) +{ + return __videobuf_alloc_vb(size, true); +} + static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; @@ -235,28 +297,32 @@ static int __videobuf_iolock(struct videobuf_queue *q, return videobuf_dma_contig_user_get(mem, vb); /* allocate memory for the read() method */ - mem->size = PAGE_ALIGN(vb->size); - mem->vaddr = dma_alloc_coherent(q->dev, mem->size, - &mem->dma_handle, GFP_KERNEL); - if (!mem->vaddr) { - dev_err(q->dev, "dma_alloc_coherent %ld failed\n", - mem->size); + if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size), + GFP_KERNEL)) return -ENOMEM; - } - - dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n", - mem->vaddr, mem->size); break; case V4L2_MEMORY_OVERLAY: default: - dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", - __func__); + dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__); return -EINVAL; } return 0; } +static int __videobuf_sync(struct videobuf_queue *q, + struct videobuf_buffer *buf) +{ + struct videobuf_dma_contig_memory *mem = buf->priv; + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, + DMA_FROM_DEVICE); + + return 0; +} + static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_buffer *buf, struct vm_area_struct *vma) @@ -265,6 +331,8 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_mapping *map; int retval; unsigned long size; + unsigned long pos, start = vma->vm_start; + struct page *page; dev_dbg(q->dev, "%s\n", __func__); @@ -282,41 +350,50 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - mem->size = PAGE_ALIGN(buf->bsize); - mem->vaddr = dma_alloc_coherent(q->dev, mem->size, - &mem->dma_handle, GFP_KERNEL); - if (!mem->vaddr) { - dev_err(q->dev, "dma_alloc_coherent size %ld failed\n", - mem->size); + if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize), + GFP_KERNEL | __GFP_COMP)) goto error; - } - dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n", - mem->vaddr, mem->size); /* Try to remap memory */ size = vma->vm_end - vma->vm_start; size = (size < mem->size) ? size : mem->size; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - retval = remap_pfn_range(vma, vma->vm_start, - mem->dma_handle >> PAGE_SHIFT, - size, vma->vm_page_prot); - if (retval) { - dev_err(q->dev, "mmap: remap failed with error %d. ", retval); - dma_free_coherent(q->dev, mem->size, - mem->vaddr, mem->dma_handle); - goto error; + if (!mem->cached) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + pos = (unsigned long)mem->vaddr; + + while (size > 0) { + page = virt_to_page((void *)pos); + if (NULL == page) { + dev_err(q->dev, "mmap: virt_to_page failed\n"); + __videobuf_dc_free(q->dev, mem); + goto error; + } + retval = vm_insert_page(vma, start, page); + if (retval) { + dev_err(q->dev, "mmap: insert failed with error %d\n", + retval); + __videobuf_dc_free(q->dev, mem); + goto error; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } - vma->vm_ops = &videobuf_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; + vma->vm_ops = &videobuf_vm_ops; + vma->vm_flags |= VM_DONTEXPAND; vma->vm_private_data = map; dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", map, q, vma->vm_start, vma->vm_end, - (long int)buf->bsize, - vma->vm_pgoff, buf->i); + (long int)buf->bsize, vma->vm_pgoff, buf->i); videobuf_vm_open(vma); @@ -328,12 +405,20 @@ error: } static struct videobuf_qtype_ops qops = { - .magic = MAGIC_QTYPE_OPS, + .magic = MAGIC_QTYPE_OPS, + .alloc_vb = __videobuf_alloc_uncached, + .iolock = __videobuf_iolock, + .mmap_mapper = __videobuf_mmap_mapper, + .vaddr = __videobuf_to_vaddr, +}; - .alloc_vb = __videobuf_alloc_vb, - .iolock = __videobuf_iolock, - .mmap_mapper = __videobuf_mmap_mapper, - .vaddr = __videobuf_to_vaddr, +static struct videobuf_qtype_ops qops_cached = { + .magic = MAGIC_QTYPE_OPS, + .alloc_vb = __videobuf_alloc_cached, + .iolock = __videobuf_iolock, + .sync = __videobuf_sync, + .mmap_mapper = __videobuf_mmap_mapper, + .vaddr = __videobuf_to_vaddr, }; void videobuf_queue_dma_contig_init(struct videobuf_queue *q, @@ -351,6 +436,20 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, } EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); +void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, + const struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv, struct mutex *ext_lock) +{ + videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, + priv, &qops_cached, ext_lock); +} +EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached); + dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; @@ -389,7 +488,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, /* read() method */ if (mem->vaddr) { - dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle); + __videobuf_dc_free(q->dev, mem); mem->vaddr = NULL; } } diff --git a/include/media/videobuf-dma-contig.h b/include/media/videobuf-dma-contig.h index f0ed82543d9f..f473aeb86d3f 100644 --- a/include/media/videobuf-dma-contig.h +++ b/include/media/videobuf-dma-contig.h @@ -26,6 +26,16 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, void *priv, struct mutex *ext_lock); +void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, + const struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv, + struct mutex *ext_lock); + dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf); void videobuf_dma_contig_free(struct videobuf_queue *q, struct videobuf_buffer *buf); -- cgit v1.2.3 From efeb98b4e2b2ce50e008affce4c493e58167144a Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 12 Apr 2012 12:39:38 -0300 Subject: [media] STA2X11 VIP: new V4L2 driver V4L2 driver for the Video Input Port within STA2X11 board Signed-off-by: Federico Vaga Acked-by: Giancarlo Asnaghi Cc: Alan Cox Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 13 + drivers/media/video/Makefile | 1 + drivers/media/video/sta2x11_vip.c | 1550 +++++++++++++++++++++++++++++++++++++ drivers/media/video/sta2x11_vip.h | 40 + 4 files changed, 1604 insertions(+) create mode 100644 drivers/media/video/sta2x11_vip.c create mode 100644 drivers/media/video/sta2x11_vip.h diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 268b36db1411..d3b08e8f5ffb 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -797,6 +797,19 @@ source "drivers/media/video/saa7164/Kconfig" source "drivers/media/video/zoran/Kconfig" +config STA2X11_VIP + tristate "STA2X11 VIP Video For Linux" + depends on STA2X11 + select VIDEO_ADV7180 if VIDEO_HELPER_CHIPS_AUTO + select VIDEOBUF_DMA_CONTIG + depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS + help + Say Y for support for STA2X11 VIP (Video Input Port) capture + device. + + To compile this driver as a module, choose M here: the + module will be called sta2x11_vip. + endif # V4L_PCI_DRIVERS # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 5a97da2ae33b..d209de0e0ca8 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -123,6 +123,7 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o +obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o diff --git a/drivers/media/video/sta2x11_vip.c b/drivers/media/video/sta2x11_vip.c new file mode 100644 index 000000000000..636643f0a186 --- /dev/null +++ b/drivers/media/video/sta2x11_vip.c @@ -0,0 +1,1550 @@ +/* + * This is the driver for the STA2x11 Video Input Port. + * + * Copyright (C) 2010 WindRiver Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Author: Andreas Kies + * Vlad Lungu + * + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sta2x11_vip.h" + +#define DRV_NAME "sta2x11_vip" +#define DRV_VERSION "1.3" + +#ifndef PCI_DEVICE_ID_STMICRO_VIP +#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D +#endif + +#define MAX_FRAMES 4 + +/*Register offsets*/ +#define DVP_CTL 0x00 +#define DVP_TFO 0x04 +#define DVP_TFS 0x08 +#define DVP_BFO 0x0C +#define DVP_BFS 0x10 +#define DVP_VTP 0x14 +#define DVP_VBP 0x18 +#define DVP_VMP 0x1C +#define DVP_ITM 0x98 +#define DVP_ITS 0x9C +#define DVP_STA 0xA0 +#define DVP_HLFLN 0xA8 +#define DVP_RGB 0xC0 +#define DVP_PKZ 0xF0 + +/*Register fields*/ +#define DVP_CTL_ENA 0x00000001 +#define DVP_CTL_RST 0x80000000 +#define DVP_CTL_DIS (~0x00040001) + +#define DVP_IT_VSB 0x00000008 +#define DVP_IT_VST 0x00000010 +#define DVP_IT_FIFO 0x00000020 + +#define DVP_HLFLN_SD 0x00000001 + +#define REG_WRITE(vip, reg, value) iowrite32((value), (vip->iomem)+(reg)) +#define REG_READ(vip, reg) ioread32((vip->iomem)+(reg)) + +#define SAVE_COUNT 8 +#define AUX_COUNT 3 +#define IRQ_COUNT 1 + +/** + * struct sta2x11_vip - All internal data for one instance of device + * @v4l2_dev: device registered in v4l layer + * @video_dev: properties of our device + * @pdev: PCI device + * @adapter: contains I2C adapter information + * @register_save_area: All relevant register are saved here during suspend + * @decoder: contains information about video DAC + * @format: pixel format, fixed UYVY + * @std: video standard (e.g. PAL/NTSC) + * @input: input line for video signal ( 0 or 1 ) + * @users: Number of open of device ( max. 1 ) + * @disabled: Device is in power down state + * @mutex: ensures exclusive opening of device + * @slock: for excluse acces of registers + * @vb_vidq: queue maintained by videobuf layer + * @capture: linked list of capture buffer + * @active: struct videobuf_buffer currently beingg filled + * @started: device is ready to capture frame + * @closing: device will be shut down + * @tcount: Number of top frames + * @bcount: Number of bottom frames + * @overflow: Number of FIFO overflows + * @mem_spare: small buffer of unused frame + * @dma_spare: dma addres of mem_spare + * @iomem: hardware base address + * @config: I2C and gpio config from platform + * + * All non-local data is accessed via this structure. + */ + +struct sta2x11_vip { + struct v4l2_device v4l2_dev; + struct video_device *video_dev; + struct pci_dev *pdev; + struct i2c_adapter *adapter; + unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT]; + struct v4l2_subdev *decoder; + struct v4l2_pix_format format; + v4l2_std_id std; + unsigned int input; + int users; + int disabled; + struct mutex mutex; /* exclusive access during open */ + spinlock_t slock; /* spin lock for hardware and queue access */ + struct videobuf_queue vb_vidq; + struct list_head capture; + struct videobuf_buffer *active; + int started, closing, tcount, bcount; + int overflow; + void *mem_spare; + dma_addr_t dma_spare; + void *iomem; + struct vip_config *config; +}; + +static const unsigned int registers_to_save[AUX_COUNT] = { + DVP_HLFLN, DVP_RGB, DVP_PKZ +}; + +static struct v4l2_pix_format formats_50[] = { + { /*PAL interlaced */ + .width = 720, + .height = 576, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 576, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + { /*PAL top */ + .width = 720, + .height = 288, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_TOP, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 288, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + { /*PAL bottom */ + .width = 720, + .height = 288, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_BOTTOM, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 288, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + +}; + +static struct v4l2_pix_format formats_60[] = { + { /*NTSC interlaced */ + .width = 720, + .height = 480, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 480, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + { /*NTSC top */ + .width = 720, + .height = 240, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_TOP, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 240, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + { /*NTSC bottom */ + .width = 720, + .height = 240, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_BOTTOM, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 240, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, +}; + +/** + * buf_setup - Get size and number of video buffer + * @vq: queue in videobuf + * @count: Number of buffers (1..MAX_FRAMES). + * 0 use default value. + * @size: size of buffer in bytes + * + * returns size and number of buffers + * a preset value of 0 returns the default number. + * return value: 0, always succesfull. + */ +static int buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct sta2x11_vip *vip = vq->priv_data; + + *size = vip->format.width * vip->format.height * 2; + if (0 == *count || MAX_FRAMES < *count) + *count = MAX_FRAMES; + return 0; +}; + +/** + * buf_prepare - prepare buffer for usage + * @vq: queue in videobuf layer + * @vb: buffer to be prepared + * @field: type of video data (interlaced/non-interlaced) + * + * Allocate or realloc buffer + * return value: 0, successful. + * + * -EINVAL, supplied buffer is too small. + * + * other, buffer could not be locked. + */ +static int buf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct sta2x11_vip *vip = vq->priv_data; + int ret; + + vb->size = vip->format.width * vip->format.height * 2; + if ((0 != vb->baddr) && (vb->bsize < vb->size)) + return -EINVAL; + vb->width = vip->format.width; + vb->height = vip->format.height; + vb->field = field; + + if (VIDEOBUF_NEEDS_INIT == vb->state) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + } + vb->state = VIDEOBUF_PREPARED; + return 0; +fail: + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; + return ret; +} + +/** + * buf_queu - queue buffer for filling + * @vq: queue in videobuf layer + * @vb: buffer to be queued + * + * if capturing is already running, the buffer will be queued. Otherwise + * capture is started and the buffer is used directly. + */ +static void buf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct sta2x11_vip *vip = vq->priv_data; + u32 dma; + + vb->state = VIDEOBUF_QUEUED; + + if (vip->active) { + list_add_tail(&vb->queue, &vip->capture); + return; + } + + vip->started = 1; + vip->tcount = 0; + vip->bcount = 0; + vip->active = vb; + vb->state = VIDEOBUF_ACTIVE; + + dma = videobuf_to_dma_contig(vb); + + REG_WRITE(vip, DVP_TFO, (0 << 16) | (0)); + /* despite of interlace mode, upper and lower frames start at zero */ + REG_WRITE(vip, DVP_BFO, (0 << 16) | (0)); + + switch (vip->format.field) { + case V4L2_FIELD_INTERLACED: + REG_WRITE(vip, DVP_TFS, + ((vip->format.height / 2 - 1) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_BFS, ((vip->format.height / 2 - 1) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); + REG_WRITE(vip, DVP_VMP, 4 * vip->format.width); + break; + case V4L2_FIELD_TOP: + REG_WRITE(vip, DVP_TFS, + ((vip->format.height - 1) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_BFS, ((0) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma); + REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); + break; + case V4L2_FIELD_BOTTOM: + REG_WRITE(vip, DVP_TFS, ((0) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_BFS, + ((vip->format.height) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma); + REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); + break; + + default: + pr_warning("VIP: unknown field format\n"); + return; + } + + REG_WRITE(vip, DVP_CTL, DVP_CTL_ENA); +} + +/** + * buff_release - release buffer + * @vq: queue in videobuf layer + * @vb: buffer to be released + * + * release buffer in videobuf layer + */ +static void buf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops vip_qops = { + .buf_setup = buf_setup, + .buf_prepare = buf_prepare, + .buf_queue = buf_queue, + .buf_release = buf_release, +}; + +/** + * vip_open - open video device + * @file: descriptor of device + * + * open device, make sure it is only opened once. + * return value: 0, no error. + * + * -EBUSY, device is already opened + * + * -ENOMEM, no memory for auxiliary DMA buffer + */ +static int vip_open(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct sta2x11_vip *vip = video_get_drvdata(dev); + + mutex_lock(&vip->mutex); + vip->users++; + + if (vip->users > 1) { + vip->users--; + mutex_unlock(&vip->mutex); + return -EBUSY; + } + + file->private_data = dev; + vip->overflow = 0; + vip->started = 0; + vip->closing = 0; + vip->active = NULL; + + INIT_LIST_HEAD(&vip->capture); + vip->mem_spare = dma_alloc_coherent(&vip->pdev->dev, 64, + &vip->dma_spare, GFP_KERNEL); + if (!vip->mem_spare) { + vip->users--; + mutex_unlock(&vip->mutex); + return -ENOMEM; + } + + mutex_unlock(&vip->mutex); + videobuf_queue_dma_contig_init_cached(&vip->vb_vidq, + &vip_qops, + &vip->pdev->dev, + &vip->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct videobuf_buffer), + vip, NULL); + REG_READ(vip, DVP_ITS); + REG_WRITE(vip, DVP_HLFLN, DVP_HLFLN_SD); + REG_WRITE(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST); + REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); + REG_WRITE(vip, DVP_CTL, 0); + REG_READ(vip, DVP_ITS); + return 0; +} + +/** + * vip_close - close video device + * @file: descriptor of device + * + * close video device, wait until all pending operations are finished + * ( maximum FRAME_MAX buffers pending ) + * Turn off interrupts. + * + * return value: 0, always succesful. + */ +static int vip_close(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct sta2x11_vip *vip = video_get_drvdata(dev); + + vip->closing = 1; + if (vip->active) + videobuf_waiton(&vip->vb_vidq, vip->active, 0, 0); + spin_lock_irq(&vip->slock); + + REG_WRITE(vip, DVP_ITM, 0); + REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); + REG_WRITE(vip, DVP_CTL, 0); + REG_READ(vip, DVP_ITS); + + vip->started = 0; + vip->active = NULL; + + spin_unlock_irq(&vip->slock); + + videobuf_stop(&vip->vb_vidq); + videobuf_mmap_free(&vip->vb_vidq); + + dma_free_coherent(&vip->pdev->dev, 64, vip->mem_spare, vip->dma_spare); + file->private_data = NULL; + mutex_lock(&vip->mutex); + vip->users--; + mutex_unlock(&vip->mutex); + return 0; +} + +/** + * vip_read - read from video input + * @file: descriptor of device + * @data: user buffer + * @count: number of bytes to be read + * @ppos: position within stream + * + * read video data from video device. + * handling is done in generic videobuf layer + * return value: provided by videobuf layer + */ +static ssize_t vip_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct video_device *dev = file->private_data; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_read_stream(&vip->vb_vidq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +/** + * vip_mmap - map user buffer + * @file: descriptor of device + * @vma: user buffer + * + * map user space buffer into kernel mode, including DMA address. + * handling is done in generic videobuf layer. + * return value: provided by videobuf layer + */ +static int vip_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = file->private_data; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_mmap_mapper(&vip->vb_vidq, vma); +} + +/** + * vip_poll - poll for event + * @file: descriptor of device + * @wait: contains events to be waited for + * + * wait for event related to video device. + * handling is done in generic videobuf layer. + * return value: provided by videobuf layer + */ +static unsigned int vip_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *dev = file->private_data; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_poll_stream(file, &vip->vb_vidq, wait); +} + +/** + * vidioc_querycap - return capabilities of device + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @cap: contains return values + * + * the capabilities of the device are returned + * + * return value: 0, no error. + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + memset(cap, 0, sizeof(struct v4l2_capability)); + strcpy(cap->driver, DRV_NAME); + strcpy(cap->card, DRV_NAME); + cap->version = 0; + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(vip->pdev)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + return 0; +} + +/** + * vidioc_s_std - set video standard + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @std: contains standard to be set + * + * the video standard is set + * + * return value: 0, no error. + * + * -EIO, no input signal detected + * + * other, returned from video DAC. + */ +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + v4l2_std_id oldstd = vip->std, newstd; + int status; + + if (V4L2_STD_ALL == *std) { + v4l2_subdev_call(vip->decoder, core, s_std, *std); + ssleep(2); + v4l2_subdev_call(vip->decoder, video, querystd, &newstd); + v4l2_subdev_call(vip->decoder, video, g_input_status, &status); + if (status & V4L2_IN_ST_NO_SIGNAL) + return -EIO; + *std = vip->std = newstd; + if (oldstd != *std) { + if (V4L2_STD_NTSC & (*std)) + vip->format = formats_60[0]; + else + vip->format = formats_50[0]; + } + return 0; + } + + if (oldstd != *std) { + if (V4L2_STD_NTSC & (*std)) + vip->format = formats_60[0]; + else + vip->format = formats_50[0]; + } + + return v4l2_subdev_call(vip->decoder, core, s_std, *std); +} + +/** + * vidioc_g_std - get video standard + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @std: contains return values + * + * the current video standard is returned + * + * return value: 0, no error. + */ +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + *std = vip->std; + return 0; +} + +/** + * vidioc_querystd - get possible video standards + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @std: contains return values + * + * all possible video standards are returned + * + * return value: delivered by video DAC routine. + */ +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return v4l2_subdev_call(vip->decoder, video, querystd, std); + +} + +/** + * vidioc_queryctl - get possible control settings + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @ctrl: contains return values + * + * return possible values for a control + * return value: delivered by video DAC routine. + */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *ctrl) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return v4l2_subdev_call(vip->decoder, core, queryctrl, ctrl); +} + +/** + * vidioc_g_ctl - get control value + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @ctrl: contains return values + * + * return setting for a control value + * return value: delivered by video DAC routine. + */ +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return v4l2_subdev_call(vip->decoder, core, g_ctrl, ctrl); +} + +/** + * vidioc_s_ctl - set control value + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @ctrl: contains value to be set + * + * set value for a specific control + * return value: delivered by video DAC routine. + */ +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return v4l2_subdev_call(vip->decoder, core, s_ctrl, ctrl); +} + +/** + * vidioc_enum_input - return name of input line + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @inp: contains return values + * + * the user friendly name of the input line is returned + * + * return value: 0, no error. + * + * -EINVAL, input line number out of range + */ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index > 1) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_ALL; + sprintf(inp->name, "Camera %u", inp->index); + + return 0; +} + +/** + * vidioc_s_input - set input line + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @i: new input line number + * + * the current active input line is set + * + * return value: 0, no error. + * + * -EINVAL, line number out of range + */ +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + int ret; + + if (i > 1) + return -EINVAL; + ret = v4l2_subdev_call(vip->decoder, video, s_routing, i, 0, 0); + + if (!ret) + vip->input = i; + + return 0; +} + +/** + * vidioc_g_input - return input line + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @i: returned input line number + * + * the current active input line is returned + * + * return value: always 0. + */ +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + *i = vip->input; + return 0; +} + +/** + * vidioc_enum_fmt_vid_cap - return video capture format + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @f: returned format information + * + * returns name and format of video capture + * Only UYVY is supported by hardware. + * + * return value: always 0. + */ +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + + if (f->index != 0) + return -EINVAL; + + strcpy(f->description, "4:2:2, packed, UYVY"); + f->pixelformat = V4L2_PIX_FMT_UYVY; + f->flags = 0; + return 0; +} + +/** + * vidioc_try_fmt_vid_cap - set video capture format + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @f: new format + * + * new video format is set which includes width and + * field type. width is fixed to 720, no scaling. + * Only UYVY is supported by this hardware. + * the minimum height is 200, the maximum is 576 (PAL) + * + * return value: 0, no error + * + * -EINVAL, pixel or field format not supported + * + */ +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + int interlace_lim; + + if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) + return -EINVAL; + + if (V4L2_STD_NTSC & vip->std) + interlace_lim = 240; + else + interlace_lim = 288; + + switch (f->fmt.pix.field) { + case V4L2_FIELD_ANY: + if (interlace_lim < f->fmt.pix.height) + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + else + f->fmt.pix.field = V4L2_FIELD_BOTTOM; + break; + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + if (interlace_lim < f->fmt.pix.height) + f->fmt.pix.height = interlace_lim; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + f->fmt.pix.height &= ~1; + if (2 * interlace_lim < f->fmt.pix.height) + f->fmt.pix.height = 2 * interlace_lim; + if (200 > f->fmt.pix.height) + f->fmt.pix.height = 200; + f->fmt.pix.width = 720; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.priv = 0; + return 0; +} + +/** + * vidioc_s_fmt_vid_cap - set current video format parameters + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @f: returned format information + * + * set new capture format + * return value: 0, no error + * + * other, delivered by video DAC routine. + */ +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + int ret; + + ret = vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + memcpy(&vip->format, &f->fmt.pix, sizeof(struct v4l2_pix_format)); + return 0; +} + +/** + * vidioc_g_fmt_vid_cap - get current video format parameters + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @f: contains format information + * + * returns current video format parameters + * + * return value: 0, always successful + */ +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + memcpy(&f->fmt.pix, &vip->format, sizeof(struct v4l2_pix_format)); + return 0; +} + +/** + * vidioc_reqfs - request buffer + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @p: video buffer + * + * Handling is done in generic videobuf layer. + */ +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_reqbufs(&vip->vb_vidq, p); +} + +/** + * vidioc_querybuf - query buffer + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @p: video buffer + * + * query buffer state. + * Handling is done in generic videobuf layer. + */ +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_querybuf(&vip->vb_vidq, p); +} + +/** + * vidioc_qbuf - queue a buffer + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @p: video buffer + * + * Handling is done in generic videobuf layer. + */ +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_qbuf(&vip->vb_vidq, p); +} + +/** + * vidioc_dqbuf - dequeue a buffer + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @p: video buffer + * + * Handling is done in generic videobuf layer. + */ +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_dqbuf(&vip->vb_vidq, p, file->f_flags & O_NONBLOCK); +} + +/** + * vidioc_streamon - turn on streaming + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @type: type of capture + * + * turn on streaming. + * Handling is done in generic videobuf layer. + */ +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_streamon(&vip->vb_vidq); +} + +/** + * vidioc_streamoff - turn off streaming + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @type: type of capture + * + * turn off streaming. + * Handling is done in generic videobuf layer. + */ +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_streamoff(&vip->vb_vidq); +} + +static const struct v4l2_file_operations vip_fops = { + .owner = THIS_MODULE, + .open = vip_open, + .release = vip_close, + .ioctl = video_ioctl2, + .read = vip_read, + .mmap = vip_mmap, + .poll = vip_poll +}; + +static const struct v4l2_ioctl_ops vip_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_std = vidioc_g_std, + .vidioc_querystd = vidioc_querystd, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +}; + +static struct video_device video_dev_template = { + .name = DRV_NAME, + .release = video_device_release, + .fops = &vip_fops, + .ioctl_ops = &vip_ioctl_ops, + .tvnorms = V4L2_STD_ALL, +}; + +/** + * vip_irq - interrupt routine + * @irq: Number of interrupt ( not used, correct number is assumed ) + * @vip: local data structure containing all information + * + * check for both frame interrupts set ( top and bottom ). + * check FIFO overflow, but limit number of log messages after open. + * signal a complete buffer if done. + * dequeue a new buffer if available. + * disable VIP if no buffer available. + * + * return value: IRQ_NONE, interrupt was not generated by VIP + * + * IRQ_HANDLED, interrupt done. + */ +static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) +{ + u32 status, dma; + unsigned long flags; + struct videobuf_buffer *vb; + + status = REG_READ(vip, DVP_ITS); + + if (!status) { + pr_debug("VIP: irq ignored\n"); + return IRQ_NONE; + } + + if (!vip->started) + return IRQ_HANDLED; + + if (status & DVP_IT_VSB) + vip->bcount++; + + if (status & DVP_IT_VST) + vip->tcount++; + + if ((DVP_IT_VSB | DVP_IT_VST) == (status & (DVP_IT_VST | DVP_IT_VSB))) { + /* this is bad, we are too slow, hope the condition is gone + * on the next frame */ + pr_info("VIP: both irqs\n"); + return IRQ_HANDLED; + } + + if (status & DVP_IT_FIFO) { + if (5 > vip->overflow++) + pr_info("VIP: fifo overflow\n"); + } + + if (2 > vip->tcount) + return IRQ_HANDLED; + + if (status & DVP_IT_VSB) + return IRQ_HANDLED; + + spin_lock_irqsave(&vip->slock, flags); + + REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA); + if (vip->active) { + do_gettimeofday(&vip->active->ts); + vip->active->field_count++; + vip->active->state = VIDEOBUF_DONE; + wake_up(&vip->active->done); + vip->active = NULL; + } + if (!vip->closing) { + if (list_empty(&vip->capture)) + goto done; + + vb = list_first_entry(&vip->capture, struct videobuf_buffer, + queue); + if (NULL == vb) { + pr_info("VIP: no buffer\n"); + goto done; + } + vb->state = VIDEOBUF_ACTIVE; + list_del(&vb->queue); + vip->active = vb; + dma = videobuf_to_dma_contig(vb); + switch (vip->format.field) { + case V4L2_FIELD_INTERLACED: + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); + break; + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma); + break; + default: + pr_warning("VIP: unknown field format\n"); + goto done; + break; + } + REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) | DVP_CTL_ENA); + } +done: + spin_unlock_irqrestore(&vip->slock, flags); + return IRQ_HANDLED; +} + +/** + * vip_gpio_reserve - reserve gpio pin + * @dev: device + * @pin: GPIO pin number + * @dir: direction, input or output + * @name: GPIO pin name + * + */ +static int vip_gpio_reserve(struct device *dev, int pin, int dir, + const char *name) +{ + int ret; + + if (pin == -1) + return 0; + + ret = gpio_request(pin, name); + if (ret) { + dev_err(dev, "Failed to allocate pin %d (%s)\n", pin, name); + return ret; + } + + ret = gpio_direction_output(pin, dir); + if (ret) { + dev_err(dev, "Failed to set direction for pin %d (%s)\n", + pin, name); + gpio_free(pin); + return ret; + } + + ret = gpio_export(pin, false); + if (ret) { + dev_err(dev, "Failed to export pin %d (%s)\n", pin, name); + gpio_free(pin); + return ret; + } + + return 0; +} + +/** + * vip_gpio_release - release gpio pin + * @dev: device + * @pin: GPIO pin number + * @name: GPIO pin name + * + */ +static void vip_gpio_release(struct device *dev, int pin, const char *name) +{ + if (pin != -1) { + dev_dbg(dev, "releasing pin %d (%s)\n", pin, name); + gpio_unexport(pin); + gpio_free(pin); + } +} + +/** + * sta2x11_vip_init_one - init one instance of video device + * @pdev: PCI device + * @ent: (not used) + * + * allocate reset pins for DAC. + * Reset video DAC, this is done via reset line. + * allocate memory for managing device + * request interrupt + * map IO region + * register device + * find and initialize video DAC + * + * return value: 0, no error + * + * -ENOMEM, no memory + * + * -ENODEV, device could not be detected or registered + */ +static int __devinit sta2x11_vip_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret; + struct sta2x11_vip *vip; + struct vip_config *config; + + ret = pci_enable_device(pdev); + if (ret) + return ret; + + config = dev_get_platdata(&pdev->dev); + if (!config) { + dev_info(&pdev->dev, "VIP slot disabled\n"); + ret = -EINVAL; + goto disable; + } + + ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0, + config->pwr_name); + if (ret) + goto disable; + + if (config->reset_pin >= 0) { + ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0, + config->reset_name); + if (ret) { + vip_gpio_release(&pdev->dev, config->pwr_pin, + config->pwr_name); + goto disable; + } + } + + if (config->pwr_pin != -1) { + /* Datasheet says 5ms between PWR and RST */ + usleep_range(5000, 25000); + ret = gpio_direction_output(config->pwr_pin, 1); + } + + if (config->reset_pin != -1) { + /* Datasheet says 5ms between PWR and RST */ + usleep_range(5000, 25000); + ret = gpio_direction_output(config->reset_pin, 1); + } + usleep_range(5000, 25000); + + vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL); + if (!vip) { + ret = -ENOMEM; + goto release_gpios; + } + + vip->pdev = pdev; + vip->std = V4L2_STD_PAL; + vip->format = formats_50[0]; + vip->config = config; + + if (v4l2_device_register(&pdev->dev, &vip->v4l2_dev)) + goto free_mem; + + dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n", + (unsigned long)pci_resource_start(pdev, 0), + (unsigned long)pci_resource_len(pdev, 0), pdev->irq); + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret) + goto unreg; + + vip->iomem = pci_iomap(pdev, 0, 0x100); + if (!vip->iomem) { + ret = -ENOMEM; /* FIXME */ + goto release; + } + + pci_enable_msi(pdev); + + INIT_LIST_HEAD(&vip->capture); + spin_lock_init(&vip->slock); + mutex_init(&vip->mutex); + vip->started = 0; + vip->disabled = 0; + + ret = request_irq(pdev->irq, + (irq_handler_t) vip_irq, + IRQF_SHARED, DRV_NAME, vip); + if (ret) { + dev_err(&pdev->dev, "request_irq failed\n"); + ret = -ENODEV; + goto unmap; + } + + vip->video_dev = video_device_alloc(); + if (!vip->video_dev) { + ret = -ENOMEM; + goto release_irq; + } + + *(vip->video_dev) = video_dev_template; + video_set_drvdata(vip->video_dev, vip); + + ret = video_register_device(vip->video_dev, VFL_TYPE_GRABBER, -1); + if (ret) + goto vrelease; + + vip->adapter = i2c_get_adapter(vip->config->i2c_id); + if (!vip->adapter) { + ret = -ENODEV; + dev_err(&pdev->dev, "no I2C adapter found\n"); + goto vunreg; + } + + vip->decoder = v4l2_i2c_new_subdev(&vip->v4l2_dev, vip->adapter, + "adv7180", vip->config->i2c_addr, + NULL); + if (!vip->decoder) { + ret = -ENODEV; + dev_err(&pdev->dev, "no decoder found\n"); + goto vunreg; + } + + i2c_put_adapter(vip->adapter); + + v4l2_subdev_call(vip->decoder, core, init, 0); + + pr_info("STA2X11 Video Input Port (VIP) loaded\n"); + return 0; + +vunreg: + video_set_drvdata(vip->video_dev, NULL); +vrelease: + if (video_is_registered(vip->video_dev)) + video_unregister_device(vip->video_dev); + else + video_device_release(vip->video_dev); +release_irq: + free_irq(pdev->irq, vip); + pci_disable_msi(pdev); +unmap: + pci_iounmap(pdev, vip->iomem); + mutex_destroy(&vip->mutex); +release: + pci_release_regions(pdev); +unreg: + v4l2_device_unregister(&vip->v4l2_dev); +free_mem: + kfree(vip); +release_gpios: + vip_gpio_release(&pdev->dev, config->reset_pin, config->reset_name); + vip_gpio_release(&pdev->dev, config->pwr_pin, config->pwr_name); +disable: + /* + * do not call pci_disable_device on sta2x11 because it break all + * other Bus masters on this EP + */ + return ret; +} + +/** + * sta2x11_vip_remove_one - release device + * @pdev: PCI device + * + * Undo everything done in .._init_one + * + * unregister video device + * free interrupt + * unmap ioadresses + * free memory + * free GPIO pins + */ +static void __devexit sta2x11_vip_remove_one(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct sta2x11_vip *vip = + container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); + + video_set_drvdata(vip->video_dev, NULL); + video_unregister_device(vip->video_dev); + /*do not call video_device_release() here, is already done */ + free_irq(pdev->irq, vip); + pci_disable_msi(pdev); + pci_iounmap(pdev, vip->iomem); + pci_release_regions(pdev); + + v4l2_device_unregister(&vip->v4l2_dev); + mutex_destroy(&vip->mutex); + + vip_gpio_release(&pdev->dev, vip->config->pwr_pin, + vip->config->pwr_name); + vip_gpio_release(&pdev->dev, vip->config->reset_pin, + vip->config->reset_name); + + kfree(vip); + /* + * do not call pci_disable_device on sta2x11 because it break all + * other Bus masters on this EP + */ +} + +#ifdef CONFIG_PM + +/** + * sta2x11_vip_suspend - set device into power save mode + * @pdev: PCI device + * @state: new state of device + * + * all relevant registers are saved and an attempt to set a new state is made. + * + * return value: 0 always indicate success, + * even if device could not be disabled. (workaround for hardware problem) + * + * reurn value : 0, always succesful, even if hardware does not not support + * power down mode. + */ +static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct sta2x11_vip *vip = + container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); + unsigned long flags; + int i; + + spin_lock_irqsave(&vip->slock, flags); + vip->register_save_area[0] = REG_READ(vip, DVP_CTL); + REG_WRITE(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS); + vip->register_save_area[SAVE_COUNT] = REG_READ(vip, DVP_ITM); + REG_WRITE(vip, DVP_ITM, 0); + for (i = 1; i < SAVE_COUNT; i++) + vip->register_save_area[i] = REG_READ(vip, 4 * i); + for (i = 0; i < AUX_COUNT; i++) + vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = + REG_READ(vip, registers_to_save[i]); + spin_unlock_irqrestore(&vip->slock, flags); + /* save pci state */ + pci_save_state(pdev); + if (pci_set_power_state(pdev, pci_choose_state(pdev, state))) { + /* + * do not call pci_disable_device on sta2x11 because it + * break all other Bus masters on this EP + */ + vip->disabled = 1; + } + + pr_info("VIP: suspend\n"); + return 0; +} + +/** + * sta2x11_vip_resume - resume device operation + * @pdev : PCI device + * + * re-enable device, set PCI state to powered and restore registers. + * resume normal device operation afterwards. + * + * return value: 0, no error. + * + * other, could not set device to power on state. + */ +static int sta2x11_vip_resume(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct sta2x11_vip *vip = + container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); + unsigned long flags; + int ret, i; + + pr_info("VIP: resume\n"); + /* restore pci state */ + if (vip->disabled) { + ret = pci_enable_device(pdev); + if (ret) { + pr_warning("VIP: Can't enable device.\n"); + return ret; + } + vip->disabled = 0; + } + ret = pci_set_power_state(pdev, PCI_D0); + if (ret) { + /* + * do not call pci_disable_device on sta2x11 because it + * break all other Bus masters on this EP + */ + pr_warning("VIP: Can't enable device.\n"); + vip->disabled = 1; + return ret; + } + + pci_restore_state(pdev); + + spin_lock_irqsave(&vip->slock, flags); + for (i = 1; i < SAVE_COUNT; i++) + REG_WRITE(vip, 4 * i, vip->register_save_area[i]); + for (i = 0; i < AUX_COUNT; i++) + REG_WRITE(vip, registers_to_save[i], + vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]); + REG_WRITE(vip, DVP_CTL, vip->register_save_area[0]); + REG_WRITE(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]); + spin_unlock_irqrestore(&vip->slock, flags); + return 0; +} + +#endif + +static DEFINE_PCI_DEVICE_TABLE(sta2x11_vip_pci_tbl) = { + {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, + {0,} +}; + +static struct pci_driver sta2x11_vip_driver = { + .name = DRV_NAME, + .probe = sta2x11_vip_init_one, + .remove = __devexit_p(sta2x11_vip_remove_one), + .id_table = sta2x11_vip_pci_tbl, +#ifdef CONFIG_PM + .suspend = sta2x11_vip_suspend, + .resume = sta2x11_vip_resume, +#endif +}; + +static int __init sta2x11_vip_init_module(void) +{ + return pci_register_driver(&sta2x11_vip_driver); +} + +static void __exit sta2x11_vip_exit_module(void) +{ + pci_unregister_driver(&sta2x11_vip_driver); +} + +#ifdef MODULE +module_init(sta2x11_vip_init_module); +module_exit(sta2x11_vip_exit_module); +#else +late_initcall_sync(sta2x11_vip_init_module); +#endif + +MODULE_DESCRIPTION("STA2X11 Video Input Port driver"); +MODULE_AUTHOR("Wind River"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("sta2x11 video input"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, sta2x11_vip_pci_tbl); diff --git a/drivers/media/video/sta2x11_vip.h b/drivers/media/video/sta2x11_vip.h new file mode 100644 index 000000000000..4f81a13666eb --- /dev/null +++ b/drivers/media/video/sta2x11_vip.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Wind River Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Anders Wallin + * + */ + +#ifndef __STA2X11_VIP_H +#define __STA2X11_VIP_H + +/** + * struct vip_config - video input configuration data + * @pwr_name: ADV powerdown name + * @pwr_pin: ADV powerdown pin + * @reset_name: ADV reset name + * @reset_pin: ADV reset pin + */ +struct vip_config { + const char *pwr_name; + int pwr_pin; + const char *reset_name; + int reset_pin; + int i2c_id; + int i2c_addr; +}; + +#endif /* __STA2X11_VIP_H */ -- cgit v1.2.3 From 6ae009a8ba512d5b07386bbb1172cfd7a02986aa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 20 May 2012 12:07:41 -0300 Subject: [media] sta2x11_vip: Fix 60Hz video standard handling This device supports V4L2_STD_ALL, but its check for 60Hz standards is broken, as NTSC is not the only standard that uses 60Hz. Cc: Federico Vaga Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/sta2x11_vip.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/sta2x11_vip.c b/drivers/media/video/sta2x11_vip.c index 636643f0a186..4c10205264d4 100644 --- a/drivers/media/video/sta2x11_vip.c +++ b/drivers/media/video/sta2x11_vip.c @@ -572,7 +572,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) return -EIO; *std = vip->std = newstd; if (oldstd != *std) { - if (V4L2_STD_NTSC & (*std)) + if (V4L2_STD_525_60 & (*std)) vip->format = formats_60[0]; else vip->format = formats_50[0]; @@ -581,7 +581,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) } if (oldstd != *std) { - if (V4L2_STD_NTSC & (*std)) + if (V4L2_STD_525_60 & (*std)) vip->format = formats_60[0]; else vip->format = formats_50[0]; @@ -804,7 +804,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) return -EINVAL; - if (V4L2_STD_NTSC & vip->std) + if (V4L2_STD_525_60 & vip->std) interlace_lim = 240; else interlace_lim = 288; -- cgit v1.2.3 From 38431a98a183b37b4512225d1a95336d82efbf9b Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 8 May 2012 04:25:24 -0300 Subject: [media] m88rs2000: LNB voltage control implemented Trival patch to get it working with my cards stuff. Signed-off-by: Igor M. Liplianin Acked-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/m88rs2000.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb/frontends/m88rs2000.c b/drivers/media/dvb/frontends/m88rs2000.c index 82cc14542479..0ad5bd8c1834 100644 --- a/drivers/media/dvb/frontends/m88rs2000.c +++ b/drivers/media/dvb/frontends/m88rs2000.c @@ -416,9 +416,25 @@ static int m88rs2000_tab_set(struct m88rs2000_state *state, static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) { - deb_info("%s: %s\n", __func__, - volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : - volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); + struct m88rs2000_state *state = fe->demodulator_priv; + u8 data; + + data = m88rs2000_demod_read(state, 0xb2); + data |= 0x03; /* bit0 V/H, bit1 off/on */ + + switch (volt) { + case SEC_VOLTAGE_18: + data &= ~0x03; + break; + case SEC_VOLTAGE_13: + data &= ~0x03; + data |= 0x01; + break; + case SEC_VOLTAGE_OFF: + break; + } + + m88rs2000_demod_write(state, 0xb2, data); return 0; } -- cgit v1.2.3 From e58c11f234b58a7692bd41ecb775b4062ea45b83 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Mon, 14 May 2012 16:43:50 -0300 Subject: [media] m88rs2000 - only flip bit 2 on reg 0x70 on 16th try Continuous flip of bit2 reg 0x70 can cause device to become unresponsive. Also correct reg read mistake. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/m88rs2000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/m88rs2000.c b/drivers/media/dvb/frontends/m88rs2000.c index 0ad5bd8c1834..312588e84dae 100644 --- a/drivers/media/dvb/frontends/m88rs2000.c +++ b/drivers/media/dvb/frontends/m88rs2000.c @@ -787,13 +787,13 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) return -ENODEV; for (i = 0; i < 25; i++) { - u8 reg = m88rs2000_demod_read(state, 0x8c); + reg = m88rs2000_demod_read(state, 0x8c); if ((reg & 0x7) == 0x7) { status = FE_HAS_LOCK; break; } state->no_lock_count++; - if (state->no_lock_count > 15) { + if (state->no_lock_count == 15) { reg = m88rs2000_demod_read(state, 0x70); reg ^= 0x4; m88rs2000_demod_write(state, 0x70, reg); -- cgit v1.2.3 From 5b84325a314d5d1008e1fe59e5d74d99b5b7768b Mon Sep 17 00:00:00 2001 From: Jozsef Marton Date: Tue, 15 May 2012 12:05:36 -0300 Subject: [media] media: add support to gspca/pac7302.c for 093a:2627 (Genius FaceCam 300) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gspca_pac7302 module supports the webcam with usb id: 093a:2627. It is a Genius FaceCam 300. The module does not need any changes but listing the usb id along with a vertical flip flag. The included patch adds this to the module source. Signed-off-by: Jozsef Marton Acked-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/pac7302.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index e6c2842407a4..1e6b6531bbcc 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -276,6 +276,7 @@ pac7302 093a:2622 Genius Eye 312 pac7302 093a:2624 PAC7302 pac7302 093a:2625 Genius iSlim 310 pac7302 093a:2626 Labtec 2200 +pac7302 093a:2627 Genius FaceCam 300 pac7302 093a:2628 Genius iLook 300 pac7302 093a:2629 Genious iSlim 300 pac7302 093a:262a Webcam 300k diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index f196a0ff4fab..a0369a58c4bb 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -972,6 +972,7 @@ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x2625)}, {USB_DEVICE(0x093a, 0x2626)}, + {USB_DEVICE(0x093a, 0x2627), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x2628)}, {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x262a)}, -- cgit v1.2.3 From 13c6a9f706d21fa02495f31b3bbd3ae4d42d108f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 15 May 2012 19:48:40 -0300 Subject: [media] zl10353: change .read_snr() to report SNR as a 0.1 dB Report SNR in 0.1 dB scale instead of raw hardware register values. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/zl10353.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c index 4de3691610bc..82946cd517f5 100644 --- a/drivers/media/dvb/frontends/zl10353.c +++ b/drivers/media/dvb/frontends/zl10353.c @@ -525,7 +525,7 @@ static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr) zl10353_dump_regs(fe); _snr = zl10353_read_register(state, SNR); - *snr = (_snr << 8) | _snr; + *snr = 10 * _snr / 8; return 0; } -- cgit v1.2.3 From 2547428de05d5bc45d3144a0ebc51e3f249a2bc0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 22 Apr 2012 08:24:33 -0300 Subject: [media] smiapp: Allow using external clock from the clock framework Instead of providing a function in platform data, allow also providing the name of the external clock and use it through the clock framework. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-core.c | 55 ++++++++++++++++++++++++++++---- drivers/media/video/smiapp/smiapp.h | 1 + include/media/smiapp.h | 1 + 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index a8a1db9563b1..999f3fc867c7 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -26,6 +26,7 @@ * */ +#include #include #include #include @@ -1111,8 +1112,11 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) } usleep_range(1000, 1000); - rval = sensor->platform_data->set_xclk(&sensor->src->sd, - sensor->platform_data->ext_clk); + if (sensor->platform_data->set_xclk) + rval = sensor->platform_data->set_xclk( + &sensor->src->sd, sensor->platform_data->ext_clk); + else + rval = clk_enable(sensor->ext_clk); if (rval < 0) { dev_dbg(&client->dev, "failed to set xclk\n"); goto out_xclk_fail; @@ -1231,7 +1235,10 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) out_cci_addr_fail: if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) gpio_set_value(sensor->platform_data->xshutdown, 0); - sensor->platform_data->set_xclk(&sensor->src->sd, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); out_xclk_fail: regulator_disable(sensor->vana); @@ -1256,7 +1263,10 @@ static void smiapp_power_off(struct smiapp_sensor *sensor) if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) gpio_set_value(sensor->platform_data->xshutdown, 0); - sensor->platform_data->set_xclk(&sensor->src->sd, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); usleep_range(5000, 5000); regulator_disable(sensor->vana); sensor->streaming = 0; @@ -2327,6 +2337,28 @@ static int smiapp_registered(struct v4l2_subdev *subdev) return -ENODEV; } + if (!sensor->platform_data->set_xclk) { + sensor->ext_clk = clk_get(&client->dev, + sensor->platform_data->ext_clk_name); + if (IS_ERR(sensor->ext_clk)) { + dev_err(&client->dev, "could not get clock %s\n", + sensor->platform_data->ext_clk_name); + rval = -ENODEV; + goto out_clk_get; + } + + rval = clk_set_rate(sensor->ext_clk, + sensor->platform_data->ext_clk); + if (rval < 0) { + dev_err(&client->dev, + "unable to set clock %s freq to %u\n", + sensor->platform_data->ext_clk_name, + sensor->platform_data->ext_clk); + rval = -ENODEV; + goto out_clk_set_rate; + } + } + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { if (gpio_request_one(sensor->platform_data->xshutdown, 0, "SMIA++ xshutdown") != 0) { @@ -2334,7 +2366,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) "unable to acquire reset gpio %d\n", sensor->platform_data->xshutdown); rval = -ENODEV; - goto out_gpio_request; + goto out_clk_set_rate; } } @@ -2589,7 +2621,11 @@ out_smiapp_power_on: if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) gpio_free(sensor->platform_data->xshutdown); -out_gpio_request: +out_clk_set_rate: + clk_put(sensor->ext_clk); + sensor->ext_clk = NULL; + +out_clk_get: regulator_put(sensor->vana); sensor->vana = NULL; return rval; @@ -2778,7 +2814,10 @@ static int __exit smiapp_remove(struct i2c_client *client) if (sensor->power_count) { if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) gpio_set_value(sensor->platform_data->xshutdown, 0); - sensor->platform_data->set_xclk(&sensor->src->sd, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); sensor->power_count = 0; } @@ -2794,6 +2833,8 @@ static int __exit smiapp_remove(struct i2c_client *client) smiapp_free_controls(sensor); if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) gpio_free(sensor->platform_data->xshutdown); + if (sensor->ext_clk) + clk_put(sensor->ext_clk); if (sensor->vana) regulator_put(sensor->vana); diff --git a/drivers/media/video/smiapp/smiapp.h b/drivers/media/video/smiapp/smiapp.h index 35b9216e48cd..587f7f11238d 100644 --- a/drivers/media/video/smiapp/smiapp.h +++ b/drivers/media/video/smiapp/smiapp.h @@ -198,6 +198,7 @@ struct smiapp_sensor { struct smiapp_subdev *pixel_array; struct smiapp_platform_data *platform_data; struct regulator *vana; + struct clk *ext_clk; u32 limits[SMIAPP_LIMIT_LAST]; u8 nbinning_subtypes; struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; diff --git a/include/media/smiapp.h b/include/media/smiapp.h index a7877cd0733d..9ab07fd45d5c 100644 --- a/include/media/smiapp.h +++ b/include/media/smiapp.h @@ -77,6 +77,7 @@ struct smiapp_platform_data { struct smiapp_flash_strobe_parms *strobe_setup; int (*set_xclk)(struct v4l2_subdev *sd, int hz); + char *ext_clk_name; int xshutdown; /* gpio or SMIAPP_NO_XSHUTDOWN */ }; -- cgit v1.2.3 From 1e73eea781bc302ba7f35da89d627bd355a7814a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 22 Apr 2012 08:55:10 -0300 Subject: [media] smiapp: Pass struct sensor to register writing commands instead of i2c_client Pass struct sensor to register access commands. This allows taking quirks into account in register access. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-core.c | 154 ++++++++++++++---------------- drivers/media/video/smiapp/smiapp-quirk.c | 4 +- drivers/media/video/smiapp/smiapp-regs.c | 7 +- drivers/media/video/smiapp/smiapp-regs.h | 6 +- 4 files changed, 84 insertions(+), 87 deletions(-) diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 999f3fc867c7..de5c94762916 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -75,12 +75,12 @@ static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) int embedded_start = -1, embedded_end = -1; int image_start = 0; - rval = smiapp_read(client, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, + rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, &fmt_model_type); if (rval) return rval; - rval = smiapp_read(client, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, + rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, &fmt_model_subtype); if (rval) return rval; @@ -106,7 +106,7 @@ static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { rval = smiapp_read( - client, + sensor, SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i), &desc); if (rval) @@ -120,7 +120,7 @@ static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) } else if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { rval = smiapp_read( - client, + sensor, SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i), &desc); if (rval) @@ -199,44 +199,43 @@ static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) static int smiapp_pll_configure(struct smiapp_sensor *sensor) { - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct smiapp_pll *pll = &sensor->pll; int rval; rval = smiapp_write( - client, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); + sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); if (rval < 0) return rval; rval = smiapp_write( - client, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); + sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); if (rval < 0) return rval; rval = smiapp_write( - client, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); + sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); if (rval < 0) return rval; rval = smiapp_write( - client, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); + sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); if (rval < 0) return rval; /* Lane op clock ratio does not apply here. */ rval = smiapp_write( - client, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, + sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) return rval; rval = smiapp_write( - client, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); + sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); if (rval < 0) return rval; return smiapp_write( - client, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); + sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); } static int smiapp_pll_update(struct smiapp_sensor *sensor) @@ -425,7 +424,6 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) struct smiapp_sensor *sensor = container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) ->sensor; - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); u32 orient = 0; int exposure; int rval; @@ -433,12 +431,12 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: return smiapp_write( - client, + sensor, SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); case V4L2_CID_EXPOSURE: return smiapp_write( - client, + sensor, SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); case V4L2_CID_HFLIP: @@ -453,7 +451,7 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; orient ^= sensor->hvflip_inv_mask; - rval = smiapp_write(client, + rval = smiapp_write(sensor, SMIAPP_REG_U8_IMAGE_ORIENTATION, orient); if (rval < 0) @@ -478,13 +476,13 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) } return smiapp_write( - client, SMIAPP_REG_U16_FRAME_LENGTH_LINES, + sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + ctrl->val); case V4L2_CID_HBLANK: return smiapp_write( - client, SMIAPP_REG_U16_LINE_LENGTH_PCK, + sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + ctrl->val); @@ -624,7 +622,7 @@ static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, for (i = 0; i < n; i++) { rval = smiapp_read( - client, smiapp_reg_limits[limit[i]].addr, &val); + sensor, smiapp_reg_limits[limit[i]].addr, &val); if (rval) return rval; sensor->limits[limit[i]] = val; @@ -696,13 +694,13 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) int rval; rval = smiapp_read( - client, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); + sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); if (rval) return rval; dev_dbg(&client->dev, "data_format_model_type %d\n", type); - rval = smiapp_read(client, SMIAPP_REG_U8_PIXEL_ORDER, + rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER, &pixel_order); if (rval) return rval; @@ -733,7 +731,7 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) unsigned int fmt, j; rval = smiapp_read( - client, + sensor, SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); if (rval) return rval; @@ -831,13 +829,13 @@ static int smiapp_update_mode(struct smiapp_sensor *sensor) | sensor->binning_vertical; rval = smiapp_write( - client, SMIAPP_REG_U8_BINNING_TYPE, binning_type); + sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); if (rval < 0) return rval; binning_mode = 1; } - rval = smiapp_write(client, SMIAPP_REG_U8_BINNING_MODE, binning_mode); + rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); if (rval < 0) return rval; @@ -874,19 +872,18 @@ static int smiapp_update_mode(struct smiapp_sensor *sensor) static int smiapp_read_nvm(struct smiapp_sensor *sensor, unsigned char *nvm) { - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); u32 i, s, p, np, v; int rval, rval2; np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; for (p = 0; p < np; p++) { rval = smiapp_write( - client, + sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); if (rval) goto out; - rval = smiapp_write(client, + rval = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); @@ -895,7 +892,7 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor, for (i = 0; i < 1000; i++) { rval = smiapp_read( - client, + sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); if (rval) @@ -913,7 +910,7 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor, for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { rval = smiapp_read( - client, + sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, &v); if (rval) @@ -924,7 +921,7 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor, } out: - rval2 = smiapp_write(client, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); + rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); if (rval < 0) return rval; else @@ -944,7 +941,7 @@ static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) client->addr = sensor->platform_data->i2c_addr_dfl; - rval = smiapp_write(client, + rval = smiapp_write(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, sensor->platform_data->i2c_addr_alt << 1); if (rval) @@ -953,7 +950,7 @@ static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) client->addr = sensor->platform_data->i2c_addr_alt; /* verify addr change went ok */ - rval = smiapp_read(client, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); + rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); if (rval) return rval; @@ -970,7 +967,6 @@ static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) */ static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) { - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct smiapp_flash_strobe_parms *strobe_setup; unsigned int ext_freq = sensor->platform_data->ext_clk; u32 tmp; @@ -1060,33 +1056,33 @@ static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) strobe_width_high_rs = (tmp + strobe_adjustment - 1) / strobe_adjustment; - rval = smiapp_write(client, SMIAPP_REG_U8_FLASH_MODE_RS, + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS, strobe_setup->mode); if (rval < 0) goto out; - rval = smiapp_write(client, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, strobe_adjustment); if (rval < 0) goto out; rval = smiapp_write( - client, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, + sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, strobe_width_high_rs); if (rval < 0) goto out; - rval = smiapp_write(client, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, + rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, strobe_setup->strobe_delay); if (rval < 0) goto out; - rval = smiapp_write(client, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, + rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, strobe_setup->stobe_start_point); if (rval < 0) goto out; - rval = smiapp_write(client, SMIAPP_REG_U8_FLASH_TRIGGER_RS, + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS, strobe_setup->trigger); out: @@ -1148,7 +1144,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) } } - rval = smiapp_write(client, SMIAPP_REG_U8_SOFTWARE_RESET, + rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, SMIAPP_SOFTWARE_RESET); if (rval < 0) { dev_err(&client->dev, "software reset failed\n"); @@ -1163,7 +1159,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) } } - rval = smiapp_write(client, SMIAPP_REG_U16_COMPRESSION_MODE, + rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE, SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); if (rval) { dev_err(&client->dev, "compression mode set failed\n"); @@ -1171,28 +1167,28 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) } rval = smiapp_write( - client, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, + sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, sensor->platform_data->ext_clk / (1000000 / (1 << 8))); if (rval) { dev_err(&client->dev, "extclk frequency set failed\n"); goto out_cci_addr_fail; } - rval = smiapp_write(client, SMIAPP_REG_U8_CSI_LANE_MODE, + rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE, sensor->platform_data->lanes - 1); if (rval) { dev_err(&client->dev, "csi lane mode set failed\n"); goto out_cci_addr_fail; } - rval = smiapp_write(client, SMIAPP_REG_U8_FAST_STANDBY_CTRL, + rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL, SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); if (rval) { dev_err(&client->dev, "fast standby set failed\n"); goto out_cci_addr_fail; } - rval = smiapp_write(client, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, + rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, sensor->platform_data->csi_signalling_mode); if (rval) { dev_err(&client->dev, "csi signalling mode set failed\n"); @@ -1200,7 +1196,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) } /* DPHY control done by sensor based on requested link rate */ - rval = smiapp_write(client, SMIAPP_REG_U8_DPHY_CTRL, + rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL, SMIAPP_DPHY_CTRL_UI); if (rval < 0) return rval; @@ -1247,8 +1243,6 @@ out_xclk_fail: static void smiapp_power_off(struct smiapp_sensor *sensor) { - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - /* * Currently power/clock to lens are enable/disabled separately * but they are essentially the same signals. So if the sensor is @@ -1257,7 +1251,7 @@ static void smiapp_power_off(struct smiapp_sensor *sensor) * will fail. So do a soft reset explicitly here. */ if (sensor->platform_data->i2c_addr_alt) - smiapp_write(client, + smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, SMIAPP_SOFTWARE_RESET); @@ -1315,7 +1309,7 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) mutex_lock(&sensor->mutex); - rval = smiapp_write(client, SMIAPP_REG_U16_CSI_DATA_FORMAT, + rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT, (sensor->csi_format->width << 8) | sensor->csi_format->compressed); if (rval) @@ -1326,26 +1320,26 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) goto out; /* Analog crop start coordinates */ - rval = smiapp_write(client, SMIAPP_REG_U16_X_ADDR_START, + rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START, sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); if (rval < 0) goto out; - rval = smiapp_write(client, SMIAPP_REG_U16_Y_ADDR_START, + rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START, sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); if (rval < 0) goto out; /* Analog crop end coordinates */ rval = smiapp_write( - client, SMIAPP_REG_U16_X_ADDR_END, + sensor, SMIAPP_REG_U16_X_ADDR_END, sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); if (rval < 0) goto out; rval = smiapp_write( - client, SMIAPP_REG_U16_Y_ADDR_END, + sensor, SMIAPP_REG_U16_Y_ADDR_END, sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); if (rval < 0) @@ -1360,25 +1354,25 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { rval = smiapp_write( - client, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, + sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, sensor->scaler->crop[SMIAPP_PAD_SINK].left); if (rval < 0) goto out; rval = smiapp_write( - client, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, + sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, sensor->scaler->crop[SMIAPP_PAD_SINK].top); if (rval < 0) goto out; rval = smiapp_write( - client, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, + sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, sensor->scaler->crop[SMIAPP_PAD_SINK].width); if (rval < 0) goto out; rval = smiapp_write( - client, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, + sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, sensor->scaler->crop[SMIAPP_PAD_SINK].height); if (rval < 0) goto out; @@ -1387,23 +1381,23 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) /* Scaling */ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] != SMIAPP_SCALING_CAPABILITY_NONE) { - rval = smiapp_write(client, SMIAPP_REG_U16_SCALING_MODE, + rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE, sensor->scaling_mode); if (rval < 0) goto out; - rval = smiapp_write(client, SMIAPP_REG_U16_SCALE_M, + rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M, sensor->scale_m); if (rval < 0) goto out; } /* Output size from sensor */ - rval = smiapp_write(client, SMIAPP_REG_U16_X_OUTPUT_SIZE, + rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE, sensor->src->crop[SMIAPP_PAD_SRC].width); if (rval < 0) goto out; - rval = smiapp_write(client, SMIAPP_REG_U16_Y_OUTPUT_SIZE, + rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE, sensor->src->crop[SMIAPP_PAD_SRC].height); if (rval < 0) goto out; @@ -1424,7 +1418,7 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) goto out; } - rval = smiapp_write(client, SMIAPP_REG_U8_MODE_SELECT, + rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, SMIAPP_MODE_SELECT_STREAMING); out: @@ -1439,7 +1433,7 @@ static int smiapp_stop_streaming(struct smiapp_sensor *sensor) int rval; mutex_lock(&sensor->mutex); - rval = smiapp_write(client, SMIAPP_REG_U8_MODE_SELECT, + rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); if (rval) goto out; @@ -2203,50 +2197,50 @@ static int smiapp_identify_module(struct v4l2_subdev *subdev) minfo->name = SMIAPP_NAME; /* Module info */ - rval = smiapp_read(client, SMIAPP_REG_U8_MANUFACTURER_ID, + rval = smiapp_read(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, &minfo->manufacturer_id); if (!rval) - rval = smiapp_read(client, SMIAPP_REG_U16_MODEL_ID, + rval = smiapp_read(sensor, SMIAPP_REG_U16_MODEL_ID, &minfo->model_id); if (!rval) - rval = smiapp_read(client, SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, + rval = smiapp_read(sensor, SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, &minfo->revision_number_major); if (!rval) - rval = smiapp_read(client, SMIAPP_REG_U8_REVISION_NUMBER_MINOR, + rval = smiapp_read(sensor, SMIAPP_REG_U8_REVISION_NUMBER_MINOR, &minfo->revision_number_minor); if (!rval) - rval = smiapp_read(client, SMIAPP_REG_U8_MODULE_DATE_YEAR, + rval = smiapp_read(sensor, SMIAPP_REG_U8_MODULE_DATE_YEAR, &minfo->module_year); if (!rval) - rval = smiapp_read(client, SMIAPP_REG_U8_MODULE_DATE_MONTH, + rval = smiapp_read(sensor, SMIAPP_REG_U8_MODULE_DATE_MONTH, &minfo->module_month); if (!rval) - rval = smiapp_read(client, SMIAPP_REG_U8_MODULE_DATE_DAY, + rval = smiapp_read(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, &minfo->module_day); /* Sensor info */ if (!rval) - rval = smiapp_read(client, + rval = smiapp_read(sensor, SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, &minfo->sensor_manufacturer_id); if (!rval) - rval = smiapp_read(client, SMIAPP_REG_U16_SENSOR_MODEL_ID, + rval = smiapp_read(sensor, SMIAPP_REG_U16_SENSOR_MODEL_ID, &minfo->sensor_model_id); if (!rval) - rval = smiapp_read(client, + rval = smiapp_read(sensor, SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, &minfo->sensor_revision_number); if (!rval) - rval = smiapp_read(client, + rval = smiapp_read(sensor, SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, &minfo->sensor_firmware_version); /* SMIA */ if (!rval) - rval = smiapp_read(client, SMIAPP_REG_U8_SMIA_VERSION, + rval = smiapp_read(sensor, SMIAPP_REG_U8_SMIA_VERSION, &minfo->smia_version); if (!rval) - rval = smiapp_read(client, SMIAPP_REG_U8_SMIAPP_VERSION, + rval = smiapp_read(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, &minfo->smiapp_version); if (rval) { @@ -2415,7 +2409,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { u32 val; - rval = smiapp_read(client, + rval = smiapp_read(sensor, SMIAPP_REG_U8_BINNING_SUBTYPES, &val); if (rval < 0) { rval = -ENODEV; @@ -2426,7 +2420,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) for (i = 0; i < sensor->nbinning_subtypes; i++) { rval = smiapp_read( - client, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); + sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); if (rval < 0) { rval = -ENODEV; goto out_power_off; @@ -2600,7 +2594,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) sensor->dev_init_done = true; /* check flash capability */ - rval = smiapp_read(client, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); + rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); sensor->flash_capability = tmp; if (rval) goto out_nvm_release; diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c index fb018de4bfa8..81c2be3d47cb 100644 --- a/drivers/media/video/smiapp/smiapp-quirk.c +++ b/drivers/media/video/smiapp/smiapp-quirk.c @@ -28,9 +28,7 @@ static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) { - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - - return smiapp_write(client, (SMIA_REG_8BIT << 16) | reg, val); + return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val); } static int smiapp_write_8s(struct smiapp_sensor *sensor, diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c index a5055f18091f..e5e5f4356dd2 100644 --- a/drivers/media/video/smiapp/smiapp-regs.c +++ b/drivers/media/video/smiapp/smiapp-regs.c @@ -25,6 +25,7 @@ #include #include +#include "smiapp.h" #include "smiapp-regs.h" static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, @@ -77,8 +78,9 @@ static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, * Read a 8/16/32-bit i2c register. The value is returned in 'val'. * Returns zero if successful, or non-zero otherwise. */ -int smiapp_read(struct i2c_client *client, u32 reg, u32 *val) +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) { + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct i2c_msg msg; unsigned char data[4]; unsigned int len = (u8)(reg >> 16); @@ -145,8 +147,9 @@ err: * Write to a 8/16-bit register. * Returns zero if successful, or non-zero otherwise. */ -int smiapp_write(struct i2c_client *client, u32 reg, u32 val) +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) { + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct i2c_msg msg; unsigned char data[6]; unsigned int retries; diff --git a/drivers/media/video/smiapp/smiapp-regs.h b/drivers/media/video/smiapp/smiapp-regs.h index 58e8009d4aa5..1edfd206fb76 100644 --- a/drivers/media/video/smiapp/smiapp-regs.h +++ b/drivers/media/video/smiapp/smiapp-regs.h @@ -40,7 +40,9 @@ struct smia_reg { u32 val; /* 8/16/32-bit value */ }; -int smiapp_read(struct i2c_client *client, u32 reg, u32 *val); -int smiapp_write(struct i2c_client *client, u32 reg, u32 val); +struct smiapp_sensor; + +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); #endif -- cgit v1.2.3 From ceb9e30e9f4a892997a61f1f5a30bc5b561c9e67 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 22 Apr 2012 09:11:26 -0300 Subject: [media] smiapp: Quirk for sensors that only do 8-bit reads Some sensors implement only 8-bit read functionality and fail on wider reads. Add a quirk flag for such sensors. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-quirk.h | 1 + drivers/media/video/smiapp/smiapp-regs.c | 73 +++++++++++++++++++++++++++---- drivers/media/video/smiapp/smiapp-regs.h | 1 + 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/smiapp/smiapp-quirk.h b/drivers/media/video/smiapp/smiapp-quirk.h index 7a1b3a02a7bd..de82cdf43a89 100644 --- a/drivers/media/video/smiapp/smiapp-quirk.h +++ b/drivers/media/video/smiapp/smiapp-quirk.h @@ -46,6 +46,7 @@ struct smiapp_quirk { /* op pix clock is for all lanes in total normally */ #define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1) struct smiapp_reg_8 { u16 reg; diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c index e5e5f4356dd2..9c430647b047 100644 --- a/drivers/media/video/smiapp/smiapp-regs.c +++ b/drivers/media/video/smiapp/smiapp-regs.c @@ -78,19 +78,15 @@ static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, * Read a 8/16/32-bit i2c register. The value is returned in 'val'. * Returns zero if successful, or non-zero otherwise. */ -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, + u16 len, u32 *val) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct i2c_msg msg; unsigned char data[4]; - unsigned int len = (u8)(reg >> 16); u16 offset = reg; int r; - if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT - && len != SMIA_REG_32BIT) - return -EINVAL; - msg.addr = client->addr; msg.flags = 0; msg.len = 2; @@ -132,9 +128,6 @@ int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) BUG(); } - if (reg & SMIA_REG_FLAG_FLOAT) - *val = float_to_u32_mul_1000000(client, *val); - return 0; err: @@ -143,6 +136,68 @@ err: return r; } +/* Read a register using 8-bit access only. */ +static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg, + u16 len, u32 *val) +{ + unsigned int i; + int rval; + + *val = 0; + + for (i = 0; i < len; i++) { + u32 val8; + + rval = ____smiapp_read(sensor, reg + i, 1, &val8); + if (rval < 0) + return rval; + *val |= val8 << ((len - i - 1) << 3); + } + + return 0; +} + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, + bool only8) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int len = (u8)(reg >> 16); + int rval; + + if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT + && len != SMIA_REG_32BIT) + return -EINVAL; + + if (len == SMIA_REG_8BIT && !only8) + rval = ____smiapp_read(sensor, (u16)reg, len, val); + else + rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); + if (rval < 0) + return rval; + + if (reg & SMIA_REG_FLAG_FLOAT) + *val = float_to_u32_mul_1000000(client, *val); + + return 0; +} + +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + return __smiapp_read( + sensor, reg, val, + smiapp_needs_quirk(sensor, + SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); +} + +int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + return __smiapp_read(sensor, reg, val, true); +} + /* * Write to a 8/16-bit register. * Returns zero if successful, or non-zero otherwise. diff --git a/drivers/media/video/smiapp/smiapp-regs.h b/drivers/media/video/smiapp/smiapp-regs.h index 1edfd206fb76..7f9013b47971 100644 --- a/drivers/media/video/smiapp/smiapp-regs.h +++ b/drivers/media/video/smiapp/smiapp-regs.h @@ -43,6 +43,7 @@ struct smia_reg { struct smiapp_sensor; int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); #endif -- cgit v1.2.3 From 98add8e80764f49219de354a867f4cf8ade94ba8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 22 Apr 2012 09:30:19 -0300 Subject: [media] smiapp: Use 8-bit reads only before identifying the sensor Some sensors only allow 8-bit access, so use safe 8-bit access before the sensor has been identified. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-core.c | 63 +++++++++++++++++--------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index de5c94762916..3bf086f836fb 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -2197,51 +2197,56 @@ static int smiapp_identify_module(struct v4l2_subdev *subdev) minfo->name = SMIAPP_NAME; /* Module info */ - rval = smiapp_read(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, - &minfo->manufacturer_id); + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, + &minfo->manufacturer_id); if (!rval) - rval = smiapp_read(sensor, SMIAPP_REG_U16_MODEL_ID, - &minfo->model_id); + rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID, + &minfo->model_id); if (!rval) - rval = smiapp_read(sensor, SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, - &minfo->revision_number_major); + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, + &minfo->revision_number_major); if (!rval) - rval = smiapp_read(sensor, SMIAPP_REG_U8_REVISION_NUMBER_MINOR, - &minfo->revision_number_minor); + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_REVISION_NUMBER_MINOR, + &minfo->revision_number_minor); if (!rval) - rval = smiapp_read(sensor, SMIAPP_REG_U8_MODULE_DATE_YEAR, - &minfo->module_year); + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_MODULE_DATE_YEAR, + &minfo->module_year); if (!rval) - rval = smiapp_read(sensor, SMIAPP_REG_U8_MODULE_DATE_MONTH, - &minfo->module_month); + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_MODULE_DATE_MONTH, + &minfo->module_month); if (!rval) - rval = smiapp_read(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, - &minfo->module_day); + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, + &minfo->module_day); /* Sensor info */ if (!rval) - rval = smiapp_read(sensor, - SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, - &minfo->sensor_manufacturer_id); + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, + &minfo->sensor_manufacturer_id); if (!rval) - rval = smiapp_read(sensor, SMIAPP_REG_U16_SENSOR_MODEL_ID, - &minfo->sensor_model_id); + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U16_SENSOR_MODEL_ID, + &minfo->sensor_model_id); if (!rval) - rval = smiapp_read(sensor, - SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, - &minfo->sensor_revision_number); + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, + &minfo->sensor_revision_number); if (!rval) - rval = smiapp_read(sensor, - SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, - &minfo->sensor_firmware_version); + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, + &minfo->sensor_firmware_version); /* SMIA */ if (!rval) - rval = smiapp_read(sensor, SMIAPP_REG_U8_SMIA_VERSION, - &minfo->smia_version); + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, + &minfo->smia_version); if (!rval) - rval = smiapp_read(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, - &minfo->smiapp_version); + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, + &minfo->smiapp_version); if (rval) { dev_err(&client->dev, "sensor detection failed\n"); -- cgit v1.2.3 From 6f367993988720c86e863bb64db8a62a002d6d9d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Apr 2012 11:21:30 -0300 Subject: [media] smiapp: Round minimum pre_pll up rather than down in ip_clk_freq check The pre_pll divisor must be such that ext_clk / pre_pll divisor does not result in a frequency that is greater than pll_ip_clk_freq. Fix this. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp-pll.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c index a416e27a4282..a2e41a21dc65 100644 --- a/drivers/media/video/smiapp-pll.c +++ b/drivers/media/video/smiapp-pll.c @@ -124,8 +124,9 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, limits->min_pll_ip_freq_hz)); limits->min_pre_pll_clk_div = max_t(uint16_t, limits->min_pre_pll_clk_div, - clk_div_even(pll->ext_clk_freq_hz / - limits->max_pll_ip_freq_hz)); + clk_div_even_up( + DIV_ROUND_UP(pll->ext_clk_freq_hz, + limits->max_pll_ip_freq_hz))); dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); -- cgit v1.2.3 From 0458294751f8f9a883d518e55a13ea002df1d829 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Apr 2012 12:05:50 -0300 Subject: [media] smiapp: Initialise rval in smiapp_read_nvm() rval was not properly initialised in smiapp_read_nvm(). Do that. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 3bf086f836fb..6524091a9a4c 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -873,7 +873,7 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor, unsigned char *nvm) { u32 i, s, p, np, v; - int rval, rval2; + int rval = 0, rval2; np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; for (p = 0; p < np; p++) { -- cgit v1.2.3 From 3de886e0e4e1a981442e26edca5a32777299b079 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Apr 2012 11:55:13 -0300 Subject: [media] smiapp: Use non-binning limits if the binning limit is zero Some sensors do use binning but do not have valid limits in binning registers. Use non-binning limits in that case. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-core.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 6524091a9a4c..47d6901b4022 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -653,6 +653,7 @@ static int smiapp_get_all_limits(struct smiapp_sensor *sensor) static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) { + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); static u32 const limits[] = { SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, @@ -671,11 +672,11 @@ static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, }; + unsigned int i; + int rval; if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == SMIAPP_BINNING_CAPABILITY_NO) { - unsigned int i; - for (i = 0; i < ARRAY_SIZE(limits); i++) sensor->limits[limits[i]] = sensor->limits[limits_replace[i]]; @@ -683,7 +684,31 @@ static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) return 0; } - return smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); + rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); + if (rval < 0) + return rval; + + /* + * Sanity check whether the binning limits are valid. If not, + * use the non-binning ones. + */ + if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] + && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] + && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]) + return 0; + + for (i = 0; i < ARRAY_SIZE(limits); i++) { + dev_dbg(&client->dev, + "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limits[i]].addr, + smiapp_reg_limits[limits[i]].what, + sensor->limits[limits_replace[i]], + sensor->limits[limits_replace[i]]); + sensor->limits[limits[i]] = + sensor->limits[limits_replace[i]]; + } + + return 0; } static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) -- cgit v1.2.3 From 27b2e76dbca0b84cdae1b23c90fb353e63bb2fbb Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Apr 2012 13:39:44 -0300 Subject: [media] smiapp: Allow generic quirk registers Implement more generic quirk registers than just limit and capability registers. This comes with the expense of a little bit more access time so these should be only used when really needed. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-quirk.c | 46 +++++++++++++++++++++++++++++++ drivers/media/video/smiapp/smiapp-quirk.h | 10 +++++++ drivers/media/video/smiapp/smiapp-regs.c | 4 +++ 3 files changed, 60 insertions(+) diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c index 81c2be3d47cb..55e87950dcea 100644 --- a/drivers/media/video/smiapp/smiapp-quirk.c +++ b/drivers/media/video/smiapp/smiapp-quirk.c @@ -81,6 +81,52 @@ int smiapp_replace_limit_at(struct smiapp_sensor *sensor, return -EINVAL; } +bool smiapp_quirk_reg(struct smiapp_sensor *sensor, + u32 reg, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct smia_reg *sreg; + + if (!sensor->minfo.quirk) + return false; + + sreg = sensor->minfo.quirk->regs; + + if (!sreg) + return false; + + while (sreg->type) { + u16 type = reg >> 16; + u16 reg16 = reg; + + if (sreg->type != type || sreg->reg != reg16) { + sreg++; + continue; + } + + switch ((u8)type) { + case SMIA_REG_8BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n", + reg, sreg->val); + break; + case SMIA_REG_16BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n", + reg, sreg->val); + break; + case SMIA_REG_32BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n", + reg, sreg->val); + break; + } + + *val = sreg->val; + + return true; + } + + return false; +} + static int jt8ew9_limits(struct smiapp_sensor *sensor) { if (sensor->minfo.revision_number_major < 0x03) diff --git a/drivers/media/video/smiapp/smiapp-quirk.h b/drivers/media/video/smiapp/smiapp-quirk.h index de82cdf43a89..f4dcaabaefe7 100644 --- a/drivers/media/video/smiapp/smiapp-quirk.h +++ b/drivers/media/video/smiapp/smiapp-quirk.h @@ -41,6 +41,7 @@ struct smiapp_quirk { int (*post_poweron)(struct smiapp_sensor *sensor); int (*pre_streamon)(struct smiapp_sensor *sensor); int (*post_streamoff)(struct smiapp_sensor *sensor); + const struct smia_reg *regs; unsigned long flags; }; @@ -55,6 +56,15 @@ struct smiapp_reg_8 { void smiapp_replace_limit(struct smiapp_sensor *sensor, u32 limit, u32 val); +bool smiapp_quirk_reg(struct smiapp_sensor *sensor, + u32 reg, u32 *val); + +#define SMIAPP_MK_QUIRK_REG(_reg, _val) \ + { \ + .type = (_reg >> 16), \ + .reg = (u16)_reg, \ + .val = _val, \ + } #define smiapp_call_quirk(_sensor, _quirk, ...) \ (_sensor->minfo.quirk && \ diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c index 9c430647b047..b1812b17a407 100644 --- a/drivers/media/video/smiapp/smiapp-regs.c +++ b/drivers/media/video/smiapp/smiapp-regs.c @@ -172,6 +172,9 @@ static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, && len != SMIA_REG_32BIT) return -EINVAL; + if (smiapp_quirk_reg(sensor, reg, val)) + goto found_quirk; + if (len == SMIA_REG_8BIT && !only8) rval = ____smiapp_read(sensor, (u16)reg, len, val); else @@ -179,6 +182,7 @@ static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, if (rval < 0) return rval; +found_quirk: if (reg & SMIA_REG_FLAG_FLOAT) *val = float_to_u32_mul_1000000(client, *val); -- cgit v1.2.3 From b8cc8d7a5719754d39a9f3b4d9752389651df58a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 27 Apr 2012 10:01:46 -0300 Subject: [media] smiapp: Add support for 8-bit uncompressed formats Add support for uncompressed 8-bit raw bayer formats. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 47d6901b4022..ffc6eb71fe06 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -367,6 +367,10 @@ static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, }; const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; -- cgit v1.2.3 From 06b491fb9a8a13225d6c9fd798b5feb92f26d126 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 6 May 2012 07:03:46 -0300 Subject: [media] smiapp: Use v4l2_ctrl_new_int_menu() instead of v4l2_ctrl_new_custom() Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-core.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index ffc6eb71fe06..f518026cb67b 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -508,7 +508,7 @@ static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { static int smiapp_init_controls(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct v4l2_ctrl_config cfg; + unsigned int max; int rval; rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); @@ -572,17 +572,12 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) goto error; sensor->src->ctrl_handler.lock = &sensor->mutex; - memset(&cfg, 0, sizeof(cfg)); + for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); - cfg.ops = &smiapp_ctrl_ops; - cfg.id = V4L2_CID_LINK_FREQ; - cfg.type = V4L2_CTRL_TYPE_INTEGER_MENU; - while (sensor->platform_data->op_sys_clock[cfg.max + 1]) - cfg.max++; - cfg.qmenu_int = sensor->platform_data->op_sys_clock; - - sensor->link_freq = v4l2_ctrl_new_custom( - &sensor->src->ctrl_handler, &cfg, NULL); + sensor->link_freq = v4l2_ctrl_new_int_menu( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_LINK_FREQ, max, 0, + sensor->platform_data->op_sys_clock); sensor->pixel_rate_csi = v4l2_ctrl_new_std( &sensor->src->ctrl_handler, &smiapp_ctrl_ops, -- cgit v1.2.3 From 3491a88ea0499cb10024cf7ec4d2d261634fa2b7 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 17 May 2012 04:55:01 -0300 Subject: [media] [resend] radio-sf16fmr2: add PnP support for SF16-FMD2 Add PnP support to radio-sf16fmr2 driver to support SF16-FMD2 card (SB16 + TEA5757). The driver can now handle two cards (FMR2 is hardwired to 0x384, FMD2 can be put at 0x384 or 0x284 by PnP). Tested with both SF16-FMR2 and SF16-FMD2 (the can work at the same time by using kernel parameter "pnp_reserve_io=0x384,2" so the FMD2 is put at 0x284). Signed-off-by: Ondrej Zary Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/Kconfig | 2 +- drivers/media/radio/radio-sf16fmr2.c | 144 +++++++++++++++++++++++++++-------- 2 files changed, 114 insertions(+), 32 deletions(-) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 8db2d7f4b52a..270f7b7e813b 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -329,7 +329,7 @@ config RADIO_SF16FMI module will be called radio-sf16fmi. config RADIO_SF16FMR2 - tristate "SF16FMR2 Radio" + tristate "SF16-FMR2/SF16-FMD2 Radio" depends on ISA && VIDEO_V4L2 && SND ---help--- Choose Y here if you have one of these FM radio cards. diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 7c69214334bf..52b8011f1b23 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -1,4 +1,4 @@ -/* SF16-FMR2 radio driver for Linux +/* SF16-FMR2 and SF16-FMD2 radio driver for Linux * Copyright (c) 2011 Ondrej Zary * * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com @@ -13,15 +13,19 @@ #include /* request_region */ #include /* outb, outb_p */ #include +#include #include MODULE_AUTHOR("Ondrej Zary"); -MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver"); +MODULE_DESCRIPTION("MediaForte SF16-FMR2 and SF16-FMD2 FM radio card driver"); MODULE_LICENSE("GPL"); -static int radio_nr = -1; -module_param(radio_nr, int, 0444); -MODULE_PARM_DESC(radio_nr, "Radio device number"); +/* these cards can only use two different ports (0x384 and 0x284) */ +#define FMR2_MAX 2 + +static int radio_nr[FMR2_MAX] = { [0 ... (FMR2_MAX - 1)] = -1 }; +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); struct fmr2 { int io; @@ -29,9 +33,15 @@ struct fmr2 { struct snd_tea575x tea; struct v4l2_ctrl *volume; struct v4l2_ctrl *balance; + bool is_fmd2; }; -/* the port is hardwired so no need to support multiple cards */ +static int num_fmr2_cards; +static struct fmr2 *fmr2_cards[FMR2_MAX]; +static bool isa_registered; +static bool pnp_registered; + +/* the port is hardwired on SF16-FMR2 */ #define FMR2_PORT 0x384 /* TEA575x tuner pins */ @@ -174,7 +184,8 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea) { struct fmr2 *fmr2 = tea->private_data; - if (inb(fmr2->io) & FMR2_HASVOL) { + /* FMR2 can have volume control, FMD2 can't (uses SB16 mixer) */ + if (!fmr2->is_fmd2 && inb(fmr2->io) & FMR2_HASVOL) { fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56); fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0); if (tea->ctrl_handler.error) { @@ -186,22 +197,28 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea) return 0; } -static int __devinit fmr2_probe(struct device *pdev, unsigned int dev) +static struct pnp_device_id fmr2_pnp_ids[] __devinitdata = { + { .id = "MFRad13" }, /* tuner subdevice of SF16-FMD2 */ + { .id = "" } +}; +MODULE_DEVICE_TABLE(pnp, fmr2_pnp_ids); + +static int __devinit fmr2_probe(struct fmr2 *fmr2, struct device *pdev, int io) { - struct fmr2 *fmr2; - int err; + int err, i; + char *card_name = fmr2->is_fmd2 ? "SF16-FMD2" : "SF16-FMR2"; - fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); - if (fmr2 == NULL) - return -ENOMEM; + /* avoid errors if a card was already registered at given port */ + for (i = 0; i < num_fmr2_cards; i++) + if (io == fmr2_cards[i]->io) + return -EBUSY; - strlcpy(fmr2->v4l2_dev.name, dev_name(pdev), - sizeof(fmr2->v4l2_dev.name)); - fmr2->io = FMR2_PORT; + strlcpy(fmr2->v4l2_dev.name, "radio-sf16fmr2", + sizeof(fmr2->v4l2_dev.name)), + fmr2->io = io; if (!request_region(fmr2->io, 2, fmr2->v4l2_dev.name)) { printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io); - kfree(fmr2); return -EBUSY; } @@ -210,56 +227,121 @@ static int __devinit fmr2_probe(struct device *pdev, unsigned int dev) if (err < 0) { v4l2_err(&fmr2->v4l2_dev, "Could not register v4l2_device\n"); release_region(fmr2->io, 2); - kfree(fmr2); return err; } fmr2->tea.v4l2_dev = &fmr2->v4l2_dev; fmr2->tea.private_data = fmr2; - fmr2->tea.radio_nr = radio_nr; + fmr2->tea.radio_nr = radio_nr[num_fmr2_cards]; fmr2->tea.ops = &fmr2_tea_ops; fmr2->tea.ext_init = fmr2_tea_ext_init; - strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card)); - snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "ISA:%s", - fmr2->v4l2_dev.name); + strlcpy(fmr2->tea.card, card_name, sizeof(fmr2->tea.card)); + snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "%s:%s", + fmr2->is_fmd2 ? "PnP" : "ISA", dev_name(pdev)); if (snd_tea575x_init(&fmr2->tea)) { printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n"); release_region(fmr2->io, 2); - kfree(fmr2); return -ENODEV; } - printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io); + printk(KERN_INFO "radio-sf16fmr2: %s radio card at 0x%x.\n", + card_name, fmr2->io); return 0; } -static int __exit fmr2_remove(struct device *pdev, unsigned int dev) +static int __devinit fmr2_isa_match(struct device *pdev, unsigned int ndev) +{ + struct fmr2 *fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); + if (!fmr2) + return 0; + + if (fmr2_probe(fmr2, pdev, FMR2_PORT)) { + kfree(fmr2); + return 0; + } + dev_set_drvdata(pdev, fmr2); + fmr2_cards[num_fmr2_cards++] = fmr2; + + return 1; +} + +static int __devinit fmr2_pnp_probe(struct pnp_dev *pdev, + const struct pnp_device_id *id) { - struct fmr2 *fmr2 = dev_get_drvdata(pdev); + int ret; + struct fmr2 *fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); + if (!fmr2) + return -ENOMEM; + fmr2->is_fmd2 = true; + ret = fmr2_probe(fmr2, &pdev->dev, pnp_port_start(pdev, 0)); + if (ret) { + kfree(fmr2); + return ret; + } + pnp_set_drvdata(pdev, fmr2); + fmr2_cards[num_fmr2_cards++] = fmr2; + + return 0; +} + +static void __devexit fmr2_remove(struct fmr2 *fmr2) +{ snd_tea575x_exit(&fmr2->tea); release_region(fmr2->io, 2); v4l2_device_unregister(&fmr2->v4l2_dev); kfree(fmr2); +} + +static int __devexit fmr2_isa_remove(struct device *pdev, unsigned int ndev) +{ + fmr2_remove(dev_get_drvdata(pdev)); + dev_set_drvdata(pdev, NULL); + return 0; } -struct isa_driver fmr2_driver = { - .probe = fmr2_probe, - .remove = fmr2_remove, +static void __devexit fmr2_pnp_remove(struct pnp_dev *pdev) +{ + fmr2_remove(pnp_get_drvdata(pdev)); + pnp_set_drvdata(pdev, NULL); +} + +struct isa_driver fmr2_isa_driver = { + .match = fmr2_isa_match, + .remove = __devexit_p(fmr2_isa_remove), .driver = { .name = "radio-sf16fmr2", }, }; +struct pnp_driver fmr2_pnp_driver = { + .name = "radio-sf16fmr2", + .id_table = fmr2_pnp_ids, + .probe = fmr2_pnp_probe, + .remove = __devexit_p(fmr2_pnp_remove), +}; + static int __init fmr2_init(void) { - return isa_register_driver(&fmr2_driver, 1); + int ret; + + ret = pnp_register_driver(&fmr2_pnp_driver); + if (!ret) + pnp_registered = true; + ret = isa_register_driver(&fmr2_isa_driver, 1); + if (!ret) + isa_registered = true; + + return (pnp_registered || isa_registered) ? 0 : ret; } static void __exit fmr2_exit(void) { - isa_unregister_driver(&fmr2_driver); + if (pnp_registered) + pnp_unregister_driver(&fmr2_pnp_driver); + if (isa_registered) + isa_unregister_driver(&fmr2_isa_driver); } module_init(fmr2_init); -- cgit v1.2.3 From 26c8a729d795f2ef946c2cbf1ff159a5b128a8b2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 17 May 2012 18:22:02 -0300 Subject: [media] em28xx: simple comment fix Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-dvb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 3a5b89dc7a13..16410ac20092 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -476,8 +476,8 @@ static void terratec_h5_init(struct em28xx *dev) static void pctv_520e_init(struct em28xx *dev) { /* - * Init TDA8295(?) analog demodulator. Looks like I2C traffic to - * digital demodulator and tuner are routed via TDA8295. + * Init AVF4910B analog decoder. Looks like I2C traffic to + * digital demodulator and tuner are routed via AVF4910B. */ int i; struct { -- cgit v1.2.3 From 711e1bfb5b7e77e5317b25fc5a2faf3d47cf5d7b Mon Sep 17 00:00:00 2001 From: Ismael Luceno Date: Thu, 17 May 2012 20:33:21 -0300 Subject: [media] au0828: Move the Kconfig knob under V4L_USB_DRIVERS This driver is for USB devices, but was incorrectly listed under V4L_PCI_DRIVERS. Signed-off-by: Ismael Luceno Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index d3b08e8f5ffb..99937c94d7df 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -649,6 +649,8 @@ menuconfig V4L_USB_DRIVERS if V4L_USB_DRIVERS +source "drivers/media/video/au0828/Kconfig" + source "drivers/media/video/uvc/Kconfig" source "drivers/media/video/gspca/Kconfig" @@ -724,8 +726,6 @@ menuconfig V4L_PCI_DRIVERS if V4L_PCI_DRIVERS -source "drivers/media/video/au0828/Kconfig" - source "drivers/media/video/bt8xx/Kconfig" source "drivers/media/video/cx18/Kconfig" -- cgit v1.2.3 From 75c7dbcab43865ea247747ffbf5ab48da75ba5ce Mon Sep 17 00:00:00 2001 From: remi schwartz Date: Sat, 19 May 2012 06:11:47 -0300 Subject: [media] patch for Asus My Cinema PS3-100 (1043:48cd) Signed-off-by: Remi Schwartz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-asus-ps3-100.c | 91 +++++++++++++++++++++++++++++ drivers/media/video/saa7134/saa7134-cards.c | 51 ++++++++++++++++ drivers/media/video/saa7134/saa7134-dvb.c | 39 +++++++++++++ drivers/media/video/saa7134/saa7134-input.c | 7 +++ drivers/media/video/saa7134/saa7134.h | 1 + include/media/rc-map.h | 1 + 7 files changed, 191 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-asus-ps3-100.c diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 6d41a29861ac..ab84d66c67c1 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-anysee.o \ rc-apac-viewcomp.o \ rc-asus-pc39.o \ + rc-asus-ps3-100.o \ rc-ati-tv-wonder-hd-600.o \ rc-ati-x10.o \ rc-avermedia-a16d.o \ diff --git a/drivers/media/rc/keymaps/rc-asus-ps3-100.c b/drivers/media/rc/keymaps/rc-asus-ps3-100.c new file mode 100644 index 000000000000..ba76609c5936 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-asus-ps3-100.c @@ -0,0 +1,91 @@ +/* asus-ps3-100.h - Keytable for asus_ps3_100 Remote Controller + * + * Copyright (c) 2012 by Mauro Carvalho Chehab + * + * Based on a previous patch from Remi Schwartz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table asus_ps3_100[] = { + { 0x081c, KEY_HOME }, /* home */ + { 0x081e, KEY_TV }, /* tv */ + { 0x0803, KEY_TEXT }, /* teletext */ + { 0x0829, KEY_POWER }, /* close */ + + { 0x080b, KEY_RED }, /* red */ + { 0x080d, KEY_YELLOW }, /* yellow */ + { 0x0806, KEY_BLUE }, /* blue */ + { 0x0807, KEY_GREEN }, /* green */ + + /* Keys 0 to 9 */ + { 0x082a, KEY_0 }, + { 0x0816, KEY_1 }, + { 0x0812, KEY_2 }, + { 0x0814, KEY_3 }, + { 0x0836, KEY_4 }, + { 0x0832, KEY_5 }, + { 0x0834, KEY_6 }, + { 0x080e, KEY_7 }, + { 0x080a, KEY_8 }, + { 0x080c, KEY_9 }, + + { 0x0815, KEY_VOLUMEUP }, + { 0x0826, KEY_VOLUMEDOWN }, + { 0x0835, KEY_CHANNELUP }, /* channel / program + */ + { 0x0824, KEY_CHANNELDOWN }, /* channel / program - */ + + { 0x0808, KEY_UP }, + { 0x0804, KEY_DOWN }, + { 0x0818, KEY_LEFT }, + { 0x0810, KEY_RIGHT }, + { 0x0825, KEY_ENTER }, /* enter */ + + { 0x0822, KEY_EXIT }, /* back */ + { 0x082c, KEY_AB }, /* recall */ + + { 0x0820, KEY_AUDIO }, /* TV audio */ + { 0x0837, KEY_SCREEN }, /* snapshot */ + { 0x082e, KEY_ZOOM }, /* full screen */ + { 0x0802, KEY_MUTE }, /* mute */ + + { 0x0831, KEY_REWIND }, /* backward << */ + { 0x0811, KEY_RECORD }, /* recording */ + { 0x0809, KEY_STOP }, + { 0x0805, KEY_FASTFORWARD }, /* forward >> */ + { 0x0821, KEY_PREVIOUS }, /* rew */ + { 0x081a, KEY_PAUSE }, /* pause */ + { 0x0839, KEY_PLAY }, /* play */ + { 0x0819, KEY_NEXT }, /* forward */ +}; + +static struct rc_map_list asus_ps3_100_map = { +.map = { + .scan = asus_ps3_100, + .size = ARRAY_SIZE(asus_ps3_100), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_ASUS_PS3_100, +} +}; + +static int __init init_rc_map_asus_ps3_100(void) +{ +return rc_map_register(&asus_ps3_100_map); +} + +static void __exit exit_rc_map_asus_ps3_100(void) +{ +rc_map_unregister(&asus_ps3_100_map); +} + +module_init(init_rc_map_asus_ps3_100) +module_exit(exit_rc_map_asus_ps3_100) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 53aae5968ffb..0d3cfcf7a1fd 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5080,6 +5080,36 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }, }, + [SAA7134_BOARD_ASUSTeK_PS3_100] = { + .name = "Asus My Cinema PS3-100", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 1 << 21, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, [SAA7134_BOARD_REAL_ANGEL_220] = { .name = "Zogis Real Angel 220", .audio_clock = 0x00187de7, @@ -6875,6 +6905,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1043, .subdevice = 0x4878, /* REV:1.02G */ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x48cd, + .driver_data = SAA7134_BOARD_ASUSTeK_PS3_100, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x17de, + .subdevice = 0x7128, + .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, }, { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -7347,6 +7389,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_TERMINATOR: case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: case SAA7134_BOARD_FLYDVBT_LR301: + case SAA7134_BOARD_ASUSTeK_PS3_100: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: @@ -7811,6 +7854,14 @@ int saa7134_board_init2(struct saa7134_dev *dev) i2c_transfer(&dev->i2c_adap, &msg, 1); break; } + case SAA7134_BOARD_ASUSTeK_PS3_100: + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data, + .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + } case SAA7134_BOARD_FLYDVB_TRIO: { u8 temp = 0; diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index aaa5c97a7216..5dfd826d734e 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -881,6 +881,20 @@ static struct tda1004x_config asus_tiger_3in1_config = { .request_firmware = philips_tda1004x_request_firmware }; +static struct tda1004x_config asus_ps3_100_config = { + .demod_address = 0x0b, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch = 1, + .request_firmware = philips_tda1004x_request_firmware +}; + /* ------------------------------------------------------------------ * special case: this card uses saa713x GPIO22 for the mode switch */ @@ -1647,6 +1661,31 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, 0, 0) == NULL) { wprintk("%s: Asus Tiger 3in1, no lnbp21" " found!\n", __func__); + goto dettach_frontend; + } + } + } + break; + case SAA7134_BOARD_ASUSTeK_PS3_100: + if (!use_frontend) { /* terrestrial */ + if (configure_tda827x_fe(dev, &asus_ps3_100_config, + &tda827x_cfg_2) < 0) + goto dettach_frontend; + } else { /* satellite */ + fe0->dvb.frontend = dvb_attach(tda10086_attach, + &flydvbs, &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(tda826x_attach, + fe0->dvb.frontend, 0x60, + &dev->i2c_adap, 0) == NULL) { + wprintk("%s: Asus My Cinema PS3-100, no " + "tda826x found!\n", __func__); + goto dettach_frontend; + } + if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0, 0) == NULL) { + wprintk("%s: Asus My Cinema PS3-100, no lnbp21" + " found!\n", __func__); goto dettach_frontend; } } diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 48d2878699b7..05c6e217d8a7 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -753,6 +753,13 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0xffff; raw_decode = true; break; + case SAA7134_BOARD_ASUSTeK_PS3_100: + ir_codes = RC_MAP_ASUS_PS3_100; + mask_keydown = 0x0040000; + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; case SAA7134_BOARD_ENCORE_ENLTV: case SAA7134_BOARD_ENCORE_ENLTV_FM: ir_codes = RC_MAP_ENCORE_ENLTV; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index f625060e6a0f..89c8333736a2 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -332,6 +332,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_BEHOLD_503FM 187 #define SAA7134_BOARD_SENSORAY811_911 188 #define SAA7134_BOARD_KWORLD_PC150U 189 +#define SAA7134_BOARD_ASUSTeK_PS3_100 190 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 2e0f67db666f..cfd5163ff7f3 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -62,6 +62,7 @@ void rc_map_init(void); #define RC_MAP_ANYSEE "rc-anysee" #define RC_MAP_APAC_VIEWCOMP "rc-apac-viewcomp" #define RC_MAP_ASUS_PC39 "rc-asus-pc39" +#define RC_MAP_ASUS_PS3_100 "rc-asus-ps3-100" #define RC_MAP_ATI_TV_WONDER_HD_600 "rc-ati-tv-wonder-hd-600" #define RC_MAP_ATI_X10 "rc-ati-x10" #define RC_MAP_AVERMEDIA_A16D "rc-avermedia-a16d" -- cgit v1.2.3 From e243c3c4f7305310a283cd2f762f889a48dad90a Mon Sep 17 00:00:00 2001 From: Michel Machado Date: Fri, 18 May 2012 09:26:03 -0300 Subject: [media] rc-loopback: remove duplicate line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch just removes the second assignment "rc->priv = &loopdev;" that happens a fews lines after the first one. Signed-off-by: Michel Machado CC: Mauro Carvalho Chehab CC: "David Härdeman" Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-loopback.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index efc6a514348a..fae1615e0ff2 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -221,7 +221,6 @@ static int __init loop_init(void) rc->s_idle = loop_set_idle; rc->s_learning_mode = loop_set_learning_mode; rc->s_carrier_report = loop_set_carrier_report; - rc->priv = &loopdev; loopdev.txmask = RXMASK_REGULAR; loopdev.txcarrier = 36000; -- cgit v1.2.3 From abed623ca59a7d1abed6c4e7459be03e25a90a1e Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sat, 19 May 2012 13:18:26 -0300 Subject: [media] radio-sf16fmi: add support for SF16-FMD Add support for SF16-FMD card to radio-sf16fmi driver. Only new PnP ID is added and texts changed. Signed-off-by: Ondrej Zary Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/Kconfig | 2 +- drivers/media/radio/radio-sf16fmi.c | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 270f7b7e813b..c257da13d766 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -320,7 +320,7 @@ config RADIO_MIROPCM20 module will be called radio-miropcm20. config RADIO_SF16FMI - tristate "SF16-FMI/SF16-FMP Radio" + tristate "SF16-FMI/SF16-FMP/SF16-FMD Radio" depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have one of these FM radio cards. diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 22c5743bf9db..a81d723b8c77 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -1,4 +1,4 @@ -/* SF16-FMI and SF16-FMP radio driver for Linux radio support +/* SF16-FMI, SF16-FMP and SF16-FMD radio driver for Linux radio support * heavily based on rtrack driver... * (c) 1997 M. Kirkwood * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz @@ -11,7 +11,7 @@ * * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); * No volume control - only mute/unmute - you have to use line volume - * control on SB-part of SF16-FMI/SF16-FMP + * control on SB-part of SF16-FMI/SF16-FMP/SF16-FMD * * Converted to V4L2 API by Mauro Carvalho Chehab */ @@ -29,7 +29,7 @@ #include MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); -MODULE_DESCRIPTION("A driver for the SF16-FMI and SF16-FMP radio."); +MODULE_DESCRIPTION("A driver for the SF16-FMI, SF16-FMP and SF16-FMD radio."); MODULE_LICENSE("GPL"); MODULE_VERSION("0.0.3"); @@ -37,7 +37,7 @@ static int io = -1; static int radio_nr = -1; module_param(io, int, 0); -MODULE_PARM_DESC(io, "I/O address of the SF16-FMI or SF16-FMP card (0x284 or 0x384)"); +MODULE_PARM_DESC(io, "I/O address of the SF16-FMI/SF16-FMP/SF16-FMD card (0x284 or 0x384)"); module_param(radio_nr, int, 0); struct fmi @@ -130,7 +130,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); - strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); + strlcpy(v->card, "SF16-FMI/FMP/FMD radio", sizeof(v->card)); strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; return 0; @@ -277,8 +277,12 @@ static const struct v4l2_ioctl_ops fmi_ioctl_ops = { /* ladis: this is my card. does any other types exist? */ static struct isapnp_device_id id_table[] __devinitdata = { + /* SF16-FMI */ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0}, + /* SF16-FMD */ + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad12), 0}, { ISAPNP_CARD_END, }, }; -- cgit v1.2.3 From 71006fb22b0f5a2045605b3887ee99a0e9adafe4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 May 2012 12:45:14 -0300 Subject: [media] saa7134-cards: Remove a PCI entry added by mistake changeset 75c7dbcab added a wrong PCI ID address by mistake. Remove it. Reported-by: Remi Schwartz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7134/saa7134-cards.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 0d3cfcf7a1fd..bc08f1dbc293 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -6911,12 +6911,6 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1043, .subdevice = 0x48cd, .driver_data = SAA7134_BOARD_ASUSTeK_PS3_100, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x17de, - .subdevice = 0x7128, - .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, }, { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, -- cgit v1.2.3