38188 lines
1.1 MiB
38188 lines
1.1 MiB
Driver updates with all media stuff
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/az6007.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/az6007.c
|
|
@@ -0,0 +1,957 @@
|
|
+/*
|
|
+ * Driver for AzureWave 6007 DVB-C/T USB2.0 and clones
|
|
+ *
|
|
+ * Copyright (c) Henry Wang <Henry.wang@AzureWave.com>
|
|
+ *
|
|
+ * This driver was made publicly available by Terratec, at:
|
|
+ * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
|
|
+ * The original driver's license is GPL, as declared with MODULE_LICENSE()
|
|
+ *
|
|
+ * Copyright (c) 2010-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
|
|
+ * Driver modified by in order to work with upstream drxk driver, and
|
|
+ * tons of bugs got fixed.
|
|
+ *
|
|
+ * 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 under version 2 of the License.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ */
|
|
+
|
|
+#include "drxk.h"
|
|
+#include "mt2063.h"
|
|
+#include "dvb_ca_en50221.h"
|
|
+
|
|
+#define DVB_USB_LOG_PREFIX "az6007"
|
|
+#include "dvb-usb.h"
|
|
+
|
|
+/* debug */
|
|
+int dvb_usb_az6007_debug;
|
|
+module_param_named(debug, dvb_usb_az6007_debug, int, 0644);
|
|
+MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))."
|
|
+ DVB_USB_DEBUG_STATUS);
|
|
+
|
|
+#define deb_info(args...) dprintk(dvb_usb_az6007_debug, 0x01, args)
|
|
+#define deb_xfer(args...) dprintk(dvb_usb_az6007_debug, 0x02, args)
|
|
+#define deb_rc(args...) dprintk(dvb_usb_az6007_debug, 0x04, args)
|
|
+#define deb_fe(args...) dprintk(dvb_usb_az6007_debug, 0x08, args)
|
|
+
|
|
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
|
+
|
|
+/* Known requests (Cypress FX2 firmware + az6007 "private" ones*/
|
|
+
|
|
+#define FX2_OED 0xb5
|
|
+#define AZ6007_READ_DATA 0xb7
|
|
+#define AZ6007_I2C_RD 0xb9
|
|
+#define AZ6007_POWER 0xbc
|
|
+#define AZ6007_I2C_WR 0xbd
|
|
+#define FX2_SCON1 0xc0
|
|
+#define AZ6007_TS_THROUGH 0xc7
|
|
+#define AZ6007_READ_IR 0xb4
|
|
+
|
|
+struct az6007_device_state {
|
|
+ struct mutex mutex;
|
|
+ struct mutex ca_mutex;
|
|
+ struct dvb_ca_en50221 ca;
|
|
+ unsigned warm:1;
|
|
+ int (*gate_ctrl) (struct dvb_frontend *, int);
|
|
+ unsigned char data[4096];
|
|
+};
|
|
+
|
|
+static struct drxk_config terratec_h7_drxk = {
|
|
+ .adr = 0x29,
|
|
+ .parallel_ts = true,
|
|
+ .dynamic_clk = true,
|
|
+ .single_master = true,
|
|
+ .enable_merr_cfg = true,
|
|
+ .no_i2c_bridge = false,
|
|
+ .chunk_size = 64,
|
|
+ .mpeg_out_clk_strength = 0x02,
|
|
+ .microcode_name = "dvb-usb-terratec-h7-drxk.fw",
|
|
+};
|
|
+
|
|
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
|
|
+{
|
|
+ struct dvb_usb_adapter *adap = fe->sec_priv;
|
|
+ struct az6007_device_state *st;
|
|
+ int status = 0;
|
|
+
|
|
+ deb_info("%s: %s\n", __func__, enable ? "enable" : "disable");
|
|
+
|
|
+ if (!adap)
|
|
+ return -EINVAL;
|
|
+
|
|
+ st = adap->dev->priv;
|
|
+
|
|
+ if (!st)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (enable)
|
|
+ status = st->gate_ctrl(fe, 1);
|
|
+ else
|
|
+ status = st->gate_ctrl(fe, 0);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static struct mt2063_config az6007_mt2063_config = {
|
|
+ .tuner_address = 0x60,
|
|
+ .refclock = 36125000,
|
|
+};
|
|
+
|
|
+static int __az6007_read(struct usb_device *udev, u8 req, u16 value,
|
|
+ u16 index, u8 *b, int blen)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = usb_control_msg(udev,
|
|
+ usb_rcvctrlpipe(udev, 0),
|
|
+ req,
|
|
+ USB_TYPE_VENDOR | USB_DIR_IN,
|
|
+ value, index, b, blen, 5000);
|
|
+ if (ret < 0) {
|
|
+ warn("usb read operation failed. (%d)", ret);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
|
|
+ index);
|
|
+ debug_dump(b, blen, deb_xfer);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int az6007_read(struct dvb_usb_device *d, u8 req, u16 value,
|
|
+ u16 index, u8 *b, int blen)
|
|
+{
|
|
+ struct az6007_device_state *st = d->priv;
|
|
+ int ret;
|
|
+
|
|
+ if (mutex_lock_interruptible(&st->mutex) < 0)
|
|
+ return -EAGAIN;
|
|
+
|
|
+ ret = __az6007_read(d->udev, req, value, index, b, blen);
|
|
+
|
|
+ mutex_unlock(&st->mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int __az6007_write(struct usb_device *udev, u8 req, u16 value,
|
|
+ u16 index, u8 *b, int blen)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
|
|
+ index);
|
|
+ debug_dump(b, blen, deb_xfer);
|
|
+
|
|
+ if (blen > 64) {
|
|
+ err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n",
|
|
+ blen);
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ ret = usb_control_msg(udev,
|
|
+ usb_sndctrlpipe(udev, 0),
|
|
+ req,
|
|
+ USB_TYPE_VENDOR | USB_DIR_OUT,
|
|
+ value, index, b, blen, 5000);
|
|
+ if (ret != blen) {
|
|
+ err("usb write operation failed. (%d)", ret);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int az6007_write(struct dvb_usb_device *d, u8 req, u16 value,
|
|
+ u16 index, u8 *b, int blen)
|
|
+{
|
|
+ struct az6007_device_state *st = d->priv;
|
|
+ int ret;
|
|
+
|
|
+ if (mutex_lock_interruptible(&st->mutex) < 0)
|
|
+ return -EAGAIN;
|
|
+
|
|
+ ret = __az6007_write(d->udev, req, value, index, b, blen);
|
|
+
|
|
+ mutex_unlock(&st->mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
|
|
+{
|
|
+ struct dvb_usb_device *d = adap->dev;
|
|
+
|
|
+ deb_info("%s: %s", __func__, onoff ? "enable" : "disable");
|
|
+
|
|
+ return az6007_write(d, 0xbc, onoff, 0, NULL, 0);
|
|
+}
|
|
+
|
|
+/* remote control stuff (does not work with my box) */
|
|
+static int az6007_rc_query(struct dvb_usb_device *d)
|
|
+{
|
|
+ struct az6007_device_state *st = d->priv;
|
|
+ unsigned code = 0;
|
|
+
|
|
+ az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10);
|
|
+
|
|
+ if (st->data[1] == 0x44)
|
|
+ return 0;
|
|
+
|
|
+ if ((st->data[1] ^ st->data[2]) == 0xff)
|
|
+ code = st->data[1];
|
|
+ else
|
|
+ code = st->data[1] << 8 | st->data[2];
|
|
+
|
|
+ if ((st->data[3] ^ st->data[4]) == 0xff)
|
|
+ code = code << 8 | st->data[3];
|
|
+ else
|
|
+ code = code << 16 | st->data[3] << 8 | st->data[4];
|
|
+
|
|
+ rc_keydown(d->rc_dev, code, st->data[5]);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
|
|
+ int slot,
|
|
+ int address)
|
|
+{
|
|
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
|
|
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
|
|
+
|
|
+ int ret;
|
|
+ u8 req;
|
|
+ u16 value;
|
|
+ u16 index;
|
|
+ int blen;
|
|
+ u8 *b;
|
|
+
|
|
+ if (slot != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ b = kmalloc(12, GFP_KERNEL);
|
|
+ if (!b)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mutex_lock(&state->ca_mutex);
|
|
+
|
|
+ req = 0xC1;
|
|
+ value = address;
|
|
+ index = 0;
|
|
+ blen = 1;
|
|
+
|
|
+ ret = az6007_read(d, req, value, index, b, blen);
|
|
+ if (ret < 0) {
|
|
+ warn("usb in operation failed. (%d)", ret);
|
|
+ ret = -EINVAL;
|
|
+ } else {
|
|
+ ret = b[0];
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&state->ca_mutex);
|
|
+ kfree(b);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
|
|
+ int slot,
|
|
+ int address,
|
|
+ u8 value)
|
|
+{
|
|
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
|
|
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
|
|
+
|
|
+ int ret;
|
|
+ u8 req;
|
|
+ u16 value1;
|
|
+ u16 index;
|
|
+ int blen;
|
|
+
|
|
+ deb_info("%s %d", __func__, slot);
|
|
+ if (slot != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&state->ca_mutex);
|
|
+ req = 0xC2;
|
|
+ value1 = address;
|
|
+ index = value;
|
|
+ blen = 0;
|
|
+
|
|
+ ret = az6007_write(d, req, value1, index, NULL, blen);
|
|
+ if (ret != 0)
|
|
+ warn("usb out operation failed. (%d)", ret);
|
|
+
|
|
+ mutex_unlock(&state->ca_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca,
|
|
+ int slot,
|
|
+ u8 address)
|
|
+{
|
|
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
|
|
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
|
|
+
|
|
+ int ret;
|
|
+ u8 req;
|
|
+ u16 value;
|
|
+ u16 index;
|
|
+ int blen;
|
|
+ u8 *b;
|
|
+
|
|
+ if (slot != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ b = kmalloc(12, GFP_KERNEL);
|
|
+ if (!b)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mutex_lock(&state->ca_mutex);
|
|
+
|
|
+ req = 0xC3;
|
|
+ value = address;
|
|
+ index = 0;
|
|
+ blen = 2;
|
|
+
|
|
+ ret = az6007_read(d, req, value, index, b, blen);
|
|
+ if (ret < 0) {
|
|
+ warn("usb in operation failed. (%d)", ret);
|
|
+ ret = -EINVAL;
|
|
+ } else {
|
|
+ if (b[0] == 0)
|
|
+ warn("Read CI IO error");
|
|
+
|
|
+ ret = b[1];
|
|
+ deb_info("read cam data = %x from 0x%x", b[1], value);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&state->ca_mutex);
|
|
+ kfree(b);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca,
|
|
+ int slot,
|
|
+ u8 address,
|
|
+ u8 value)
|
|
+{
|
|
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
|
|
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
|
|
+
|
|
+ int ret;
|
|
+ u8 req;
|
|
+ u16 value1;
|
|
+ u16 index;
|
|
+ int blen;
|
|
+
|
|
+ if (slot != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&state->ca_mutex);
|
|
+ req = 0xC4;
|
|
+ value1 = address;
|
|
+ index = value;
|
|
+ blen = 0;
|
|
+
|
|
+ ret = az6007_write(d, req, value1, index, NULL, blen);
|
|
+ if (ret != 0) {
|
|
+ warn("usb out operation failed. (%d)", ret);
|
|
+ goto failed;
|
|
+ }
|
|
+
|
|
+failed:
|
|
+ mutex_unlock(&state->ca_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot)
|
|
+{
|
|
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
|
|
+
|
|
+ int ret;
|
|
+ u8 req;
|
|
+ u16 value;
|
|
+ u16 index;
|
|
+ int blen;
|
|
+ u8 *b;
|
|
+
|
|
+ b = kmalloc(12, GFP_KERNEL);
|
|
+ if (!b)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ req = 0xC8;
|
|
+ value = 0;
|
|
+ index = 0;
|
|
+ blen = 1;
|
|
+
|
|
+ ret = az6007_read(d, req, value, index, b, blen);
|
|
+ if (ret < 0) {
|
|
+ warn("usb in operation failed. (%d)", ret);
|
|
+ ret = -EIO;
|
|
+ } else{
|
|
+ ret = b[0];
|
|
+ }
|
|
+ kfree(b);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
|
|
+{
|
|
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
|
|
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
|
|
+
|
|
+ int ret, i;
|
|
+ u8 req;
|
|
+ u16 value;
|
|
+ u16 index;
|
|
+ int blen;
|
|
+
|
|
+ mutex_lock(&state->ca_mutex);
|
|
+
|
|
+ req = 0xC6;
|
|
+ value = 1;
|
|
+ index = 0;
|
|
+ blen = 0;
|
|
+
|
|
+ ret = az6007_write(d, req, value, index, NULL, blen);
|
|
+ if (ret != 0) {
|
|
+ warn("usb out operation failed. (%d)", ret);
|
|
+ goto failed;
|
|
+ }
|
|
+
|
|
+ msleep(500);
|
|
+ req = 0xC6;
|
|
+ value = 0;
|
|
+ index = 0;
|
|
+ blen = 0;
|
|
+
|
|
+ ret = az6007_write(d, req, value, index, NULL, blen);
|
|
+ if (ret != 0) {
|
|
+ warn("usb out operation failed. (%d)", ret);
|
|
+ goto failed;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < 15; i++) {
|
|
+ msleep(100);
|
|
+
|
|
+ if (CI_CamReady(ca, slot)) {
|
|
+ deb_info("CAM Ready");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ msleep(5000);
|
|
+
|
|
+failed:
|
|
+ mutex_unlock(&state->ca_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
|
|
+{
|
|
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
|
|
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
|
|
+
|
|
+ int ret;
|
|
+ u8 req;
|
|
+ u16 value;
|
|
+ u16 index;
|
|
+ int blen;
|
|
+
|
|
+ deb_info("%s", __func__);
|
|
+ mutex_lock(&state->ca_mutex);
|
|
+ req = 0xC7;
|
|
+ value = 1;
|
|
+ index = 0;
|
|
+ blen = 0;
|
|
+
|
|
+ ret = az6007_write(d, req, value, index, NULL, blen);
|
|
+ if (ret != 0) {
|
|
+ warn("usb out operation failed. (%d)", ret);
|
|
+ goto failed;
|
|
+ }
|
|
+
|
|
+failed:
|
|
+ mutex_unlock(&state->ca_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
|
|
+{
|
|
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
|
|
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
|
|
+ int ret;
|
|
+ u8 req;
|
|
+ u16 value;
|
|
+ u16 index;
|
|
+ int blen;
|
|
+ u8 *b;
|
|
+
|
|
+ b = kmalloc(12, GFP_KERNEL);
|
|
+ if (!b)
|
|
+ return -ENOMEM;
|
|
+ mutex_lock(&state->ca_mutex);
|
|
+
|
|
+ req = 0xC5;
|
|
+ value = 0;
|
|
+ index = 0;
|
|
+ blen = 1;
|
|
+
|
|
+ ret = az6007_read(d, req, value, index, b, blen);
|
|
+ if (ret < 0) {
|
|
+ warn("usb in operation failed. (%d)", ret);
|
|
+ ret = -EIO;
|
|
+ } else
|
|
+ ret = 0;
|
|
+
|
|
+ if (!ret && b[0] == 1) {
|
|
+ ret = DVB_CA_EN50221_POLL_CAM_PRESENT |
|
|
+ DVB_CA_EN50221_POLL_CAM_READY;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&state->ca_mutex);
|
|
+ kfree(b);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static void az6007_ci_uninit(struct dvb_usb_device *d)
|
|
+{
|
|
+ struct az6007_device_state *state;
|
|
+
|
|
+ deb_info("%s", __func__);
|
|
+
|
|
+ if (NULL == d)
|
|
+ return;
|
|
+
|
|
+ state = (struct az6007_device_state *)d->priv;
|
|
+ if (NULL == state)
|
|
+ return;
|
|
+
|
|
+ if (NULL == state->ca.data)
|
|
+ return;
|
|
+
|
|
+ dvb_ca_en50221_release(&state->ca);
|
|
+
|
|
+ memset(&state->ca, 0, sizeof(state->ca));
|
|
+}
|
|
+
|
|
+
|
|
+static int az6007_ci_init(struct dvb_usb_adapter *a)
|
|
+{
|
|
+ struct dvb_usb_device *d = a->dev;
|
|
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
|
|
+ int ret;
|
|
+
|
|
+ deb_info("%s", __func__);
|
|
+
|
|
+ mutex_init(&state->ca_mutex);
|
|
+
|
|
+ state->ca.owner = THIS_MODULE;
|
|
+ state->ca.read_attribute_mem = az6007_ci_read_attribute_mem;
|
|
+ state->ca.write_attribute_mem = az6007_ci_write_attribute_mem;
|
|
+ state->ca.read_cam_control = az6007_ci_read_cam_control;
|
|
+ state->ca.write_cam_control = az6007_ci_write_cam_control;
|
|
+ state->ca.slot_reset = az6007_ci_slot_reset;
|
|
+ state->ca.slot_shutdown = az6007_ci_slot_shutdown;
|
|
+ state->ca.slot_ts_enable = az6007_ci_slot_ts_enable;
|
|
+ state->ca.poll_slot_status = az6007_ci_poll_slot_status;
|
|
+ state->ca.data = d;
|
|
+
|
|
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
|
|
+ &state->ca,
|
|
+ 0, /* flags */
|
|
+ 1);/* n_slots */
|
|
+ if (ret != 0) {
|
|
+ err("Cannot initialize CI: Error %d.", ret);
|
|
+ memset(&state->ca, 0, sizeof(state->ca));
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ deb_info("CI initialized.");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
|
|
+{
|
|
+ struct az6007_device_state *st = d->priv;
|
|
+ int ret;
|
|
+
|
|
+ ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
|
|
+ memcpy(mac, st->data, sizeof(mac));
|
|
+
|
|
+ if (ret > 0)
|
|
+ deb_info("%s: mac is %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
+ __func__, mac[0], mac[1], mac[2],
|
|
+ mac[3], mac[4], mac[5]);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ struct az6007_device_state *st = adap->dev->priv;
|
|
+
|
|
+ deb_info("attaching demod drxk");
|
|
+
|
|
+ adap->fe_adap[0].fe = dvb_attach(drxk_attach, &terratec_h7_drxk,
|
|
+ &adap->dev->i2c_adap);
|
|
+ if (!adap->fe_adap[0].fe)
|
|
+ return -EINVAL;
|
|
+
|
|
+ adap->fe_adap[0].fe->sec_priv = adap;
|
|
+ st->gate_ctrl = adap->fe_adap[0].fe->ops.i2c_gate_ctrl;
|
|
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
|
|
+
|
|
+ az6007_ci_init(adap);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int az6007_tuner_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ deb_info("attaching tuner mt2063");
|
|
+
|
|
+ /* Attach mt2063 to DVB-C frontend */
|
|
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
|
|
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1);
|
|
+ if (!dvb_attach(mt2063_attach, adap->fe_adap[0].fe,
|
|
+ &az6007_mt2063_config,
|
|
+ &adap->dev->i2c_adap))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
|
|
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
|
|
+{
|
|
+ struct az6007_device_state *st = d->priv;
|
|
+ int ret;
|
|
+
|
|
+ deb_info("%s()\n", __func__);
|
|
+
|
|
+ if (!st->warm) {
|
|
+ mutex_init(&st->mutex);
|
|
+
|
|
+ ret = az6007_write(d, AZ6007_POWER, 0, 2, NULL, 0);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ msleep(60);
|
|
+ ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ msleep(100);
|
|
+ ret = az6007_write(d, AZ6007_POWER, 1, 3, NULL, 0);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ msleep(20);
|
|
+ ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ msleep(400);
|
|
+ ret = az6007_write(d, FX2_SCON1, 0, 3, NULL, 0);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ msleep(150);
|
|
+ ret = az6007_write(d, FX2_SCON1, 1, 3, NULL, 0);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ msleep(430);
|
|
+ ret = az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ st->warm = true;
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!onoff)
|
|
+ return 0;
|
|
+
|
|
+ az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
|
|
+ az6007_write(d, AZ6007_TS_THROUGH, 0, 0, NULL, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* I2C */
|
|
+static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|
+ int num)
|
|
+{
|
|
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
|
|
+ struct az6007_device_state *st = d->priv;
|
|
+ int i, j, len;
|
|
+ int ret = 0;
|
|
+ u16 index;
|
|
+ u16 value;
|
|
+ int length;
|
|
+ u8 req, addr;
|
|
+
|
|
+ if (mutex_lock_interruptible(&st->mutex) < 0)
|
|
+ return -EAGAIN;
|
|
+
|
|
+ for (i = 0; i < num; i++) {
|
|
+ addr = msgs[i].addr << 1;
|
|
+ if (((i + 1) < num)
|
|
+ && (msgs[i].len == 1)
|
|
+ && (!msgs[i].flags & I2C_M_RD)
|
|
+ && (msgs[i + 1].flags & I2C_M_RD)
|
|
+ && (msgs[i].addr == msgs[i + 1].addr)) {
|
|
+ /*
|
|
+ * A write + read xfer for the same address, where
|
|
+ * the first xfer has just 1 byte length.
|
|
+ * Need to join both into one operation
|
|
+ */
|
|
+ if (dvb_usb_az6007_debug & 2)
|
|
+ printk(KERN_DEBUG
|
|
+ "az6007 I2C xfer write+read addr=0x%x len=%d/%d: ",
|
|
+ addr, msgs[i].len, msgs[i + 1].len);
|
|
+ req = AZ6007_I2C_RD;
|
|
+ index = msgs[i].buf[0];
|
|
+ value = addr | (1 << 8);
|
|
+ length = 6 + msgs[i + 1].len;
|
|
+ len = msgs[i + 1].len;
|
|
+ ret = __az6007_read(d->udev, req, value, index,
|
|
+ st->data, length);
|
|
+ if (ret >= len) {
|
|
+ for (j = 0; j < len; j++) {
|
|
+ msgs[i + 1].buf[j] = st->data[j + 5];
|
|
+ if (dvb_usb_az6007_debug & 2)
|
|
+ printk(KERN_CONT
|
|
+ "0x%02x ",
|
|
+ msgs[i + 1].buf[j]);
|
|
+ }
|
|
+ } else
|
|
+ ret = -EIO;
|
|
+ i++;
|
|
+ } else if (!(msgs[i].flags & I2C_M_RD)) {
|
|
+ /* write bytes */
|
|
+ if (dvb_usb_az6007_debug & 2)
|
|
+ printk(KERN_DEBUG
|
|
+ "az6007 I2C xfer write addr=0x%x len=%d: ",
|
|
+ addr, msgs[i].len);
|
|
+ req = AZ6007_I2C_WR;
|
|
+ index = msgs[i].buf[0];
|
|
+ value = addr | (1 << 8);
|
|
+ length = msgs[i].len - 1;
|
|
+ len = msgs[i].len - 1;
|
|
+ if (dvb_usb_az6007_debug & 2)
|
|
+ printk(KERN_CONT "(0x%02x) ", msgs[i].buf[0]);
|
|
+ for (j = 0; j < len; j++) {
|
|
+ st->data[j] = msgs[i].buf[j + 1];
|
|
+ if (dvb_usb_az6007_debug & 2)
|
|
+ printk(KERN_CONT "0x%02x ",
|
|
+ st->data[j]);
|
|
+ }
|
|
+ ret = __az6007_write(d->udev, req, value, index,
|
|
+ st->data, length);
|
|
+ } else {
|
|
+ /* read bytes */
|
|
+ if (dvb_usb_az6007_debug & 2)
|
|
+ printk(KERN_DEBUG
|
|
+ "az6007 I2C xfer read addr=0x%x len=%d: ",
|
|
+ addr, msgs[i].len);
|
|
+ req = AZ6007_I2C_RD;
|
|
+ index = msgs[i].buf[0];
|
|
+ value = addr;
|
|
+ length = msgs[i].len + 6;
|
|
+ len = msgs[i].len;
|
|
+ ret = __az6007_read(d->udev, req, value, index,
|
|
+ st->data, length);
|
|
+ for (j = 0; j < len; j++) {
|
|
+ msgs[i].buf[j] = st->data[j + 5];
|
|
+ if (dvb_usb_az6007_debug & 2)
|
|
+ printk(KERN_CONT
|
|
+ "0x%02x ", st->data[j + 5]);
|
|
+ }
|
|
+ }
|
|
+ if (dvb_usb_az6007_debug & 2)
|
|
+ printk(KERN_CONT "\n");
|
|
+ if (ret < 0)
|
|
+ goto err;
|
|
+ }
|
|
+err:
|
|
+ mutex_unlock(&st->mutex);
|
|
+
|
|
+ if (ret < 0) {
|
|
+ info("%s ERROR: %i", __func__, ret);
|
|
+ return ret;
|
|
+ }
|
|
+ return num;
|
|
+}
|
|
+
|
|
+static u32 az6007_i2c_func(struct i2c_adapter *adapter)
|
|
+{
|
|
+ return I2C_FUNC_I2C;
|
|
+}
|
|
+
|
|
+static struct i2c_algorithm az6007_i2c_algo = {
|
|
+ .master_xfer = az6007_i2c_xfer,
|
|
+ .functionality = az6007_i2c_func,
|
|
+};
|
|
+
|
|
+int az6007_identify_state(struct usb_device *udev,
|
|
+ struct dvb_usb_device_properties *props,
|
|
+ struct dvb_usb_device_description **desc, int *cold)
|
|
+{
|
|
+ int ret;
|
|
+ u8 *mac;
|
|
+
|
|
+ mac = kmalloc(6, GFP_ATOMIC);
|
|
+ if (!mac)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Try to read the mac address */
|
|
+ ret = __az6007_read(udev, AZ6007_READ_DATA, 6, 0, mac, 6);
|
|
+ if (ret == 6)
|
|
+ *cold = 0;
|
|
+ else
|
|
+ *cold = 1;
|
|
+
|
|
+ kfree(mac);
|
|
+
|
|
+ if (*cold) {
|
|
+ __az6007_write(udev, 0x09, 1, 0, NULL, 0);
|
|
+ __az6007_write(udev, 0x00, 0, 0, NULL, 0);
|
|
+ __az6007_write(udev, 0x00, 0, 0, NULL, 0);
|
|
+ }
|
|
+
|
|
+ deb_info("Device is on %s state\n", *cold ? "warm" : "cold");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct dvb_usb_device_properties az6007_properties;
|
|
+
|
|
+static void az6007_usb_disconnect(struct usb_interface *intf)
|
|
+{
|
|
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
|
|
+ az6007_ci_uninit(d);
|
|
+ dvb_usb_device_exit(intf);
|
|
+}
|
|
+
|
|
+static int az6007_usb_probe(struct usb_interface *intf,
|
|
+ const struct usb_device_id *id)
|
|
+{
|
|
+ return dvb_usb_device_init(intf, &az6007_properties,
|
|
+ THIS_MODULE, NULL, adapter_nr);
|
|
+}
|
|
+
|
|
+static struct usb_device_id az6007_usb_table[] = {
|
|
+ {USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007)},
|
|
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7)},
|
|
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2)},
|
|
+ {0},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(usb, az6007_usb_table);
|
|
+
|
|
+static struct dvb_usb_device_properties az6007_properties = {
|
|
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
|
|
+ .usb_ctrl = CYPRESS_FX2,
|
|
+ .firmware = "dvb-usb-terratec-h7-az6007.fw",
|
|
+ .no_reconnect = 1,
|
|
+ .size_of_priv = sizeof(struct az6007_device_state),
|
|
+ .identify_state = az6007_identify_state,
|
|
+ .num_adapters = 1,
|
|
+ .adapter = {
|
|
+ {
|
|
+ .num_frontends = 1,
|
|
+ .fe = {{
|
|
+ .streaming_ctrl = az6007_streaming_ctrl,
|
|
+ .tuner_attach = az6007_tuner_attach,
|
|
+ .frontend_attach = az6007_frontend_attach,
|
|
+
|
|
+ /* parameter for the MPEG2-data transfer */
|
|
+ .stream = {
|
|
+ .type = USB_BULK,
|
|
+ .count = 10,
|
|
+ .endpoint = 0x02,
|
|
+ .u = {
|
|
+ .bulk = {
|
|
+ .buffersize = 4096,
|
|
+ }
|
|
+ }
|
|
+ },
|
|
+ } }
|
|
+ } },
|
|
+ .power_ctrl = az6007_power_ctrl,
|
|
+ .read_mac_address = az6007_read_mac_addr,
|
|
+
|
|
+ .rc.core = {
|
|
+ .rc_interval = 400,
|
|
+ .rc_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
|
|
+ .module_name = "az6007",
|
|
+ .rc_query = az6007_rc_query,
|
|
+ .allowed_protos = RC_TYPE_NEC,
|
|
+ },
|
|
+ .i2c_algo = &az6007_i2c_algo,
|
|
+
|
|
+ .num_device_descs = 2,
|
|
+ .devices = {
|
|
+ { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)",
|
|
+ .cold_ids = { &az6007_usb_table[0], NULL },
|
|
+ .warm_ids = { NULL },
|
|
+ },
|
|
+ { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)",
|
|
+ .cold_ids = { &az6007_usb_table[1], &az6007_usb_table[2], NULL },
|
|
+ .warm_ids = { NULL },
|
|
+ },
|
|
+ { NULL },
|
|
+ }
|
|
+};
|
|
+
|
|
+/* usb specific object needed to register this driver with the usb subsystem */
|
|
+static struct usb_driver az6007_usb_driver = {
|
|
+ .name = "dvb_usb_az6007",
|
|
+ .probe = az6007_usb_probe,
|
|
+ .disconnect = az6007_usb_disconnect,
|
|
+ .id_table = az6007_usb_table,
|
|
+};
|
|
+
|
|
+/* module stuff */
|
|
+static int __init az6007_usb_module_init(void)
|
|
+{
|
|
+ int result;
|
|
+ deb_info("az6007 usb module init\n");
|
|
+
|
|
+ result = usb_register(&az6007_usb_driver);
|
|
+ if (result) {
|
|
+ err("usb_register failed. (%d)", result);
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit az6007_usb_module_exit(void)
|
|
+{
|
|
+ /* deregister this driver from the USB subsystem */
|
|
+ deb_info("az6007 usb module exit\n");
|
|
+ usb_deregister(&az6007_usb_driver);
|
|
+}
|
|
+
|
|
+module_init(az6007_usb_module_init);
|
|
+module_exit(az6007_usb_module_exit);
|
|
+
|
|
+MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
|
|
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
|
|
+MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
|
|
+MODULE_VERSION("1.1");
|
|
+MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/az6007.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/az6007.h
|
|
@@ -0,0 +1,18 @@
|
|
+#ifndef _DVB_USB_AZ6007_H_
|
|
+#define _DVB_USB_AZ6007_H_
|
|
+
|
|
+#define DVB_USB_LOG_PREFIX "az6007"
|
|
+#include "dvb-usb.h"
|
|
+
|
|
+
|
|
+extern int dvb_usb_az6007_debug;
|
|
+#define deb_info(args...) dprintk(dvb_usb_az6007_debug,0x01,args)
|
|
+#define deb_xfer(args...) dprintk(dvb_usb_az6007_debug,0x02,args)
|
|
+#define deb_rc(args...) dprintk(dvb_usb_az6007_debug,0x04,args)
|
|
+#define deb_fe(args...) dprintk(dvb_usb_az6007_debug,0x08,args)
|
|
+
|
|
+
|
|
+extern int vp702x_usb_out_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec);
|
|
+extern int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
|
|
+
|
|
+#endif
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/mt2063.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/common/tuners/mt2063.c
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/mt2063.c
|
|
@@ -350,7 +350,7 @@ static int MT2063_Sleep(struct dvb_front
|
|
/*
|
|
* ToDo: Add code here to implement a OS blocking
|
|
*/
|
|
- msleep(10);
|
|
+ msleep(100);
|
|
|
|
return 0;
|
|
}
|
|
@@ -2226,7 +2226,7 @@ static struct dvb_tuner_ops mt2063_ops =
|
|
.info = {
|
|
.name = "MT2063 Silicon Tuner",
|
|
.frequency_min = 45000000,
|
|
- .frequency_max = 850000000,
|
|
+ .frequency_max = 865000000,
|
|
.frequency_step = 0,
|
|
},
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
|
|
@@ -51,6 +51,7 @@
|
|
#define USB_VID_PINNACLE 0x2304
|
|
#define USB_VID_PCTV 0x2013
|
|
#define USB_VID_PIXELVIEW 0x1554
|
|
+#define USB_VID_REALTEK 0x0bda
|
|
#define USB_VID_TECHNOTREND 0x0b48
|
|
#define USB_VID_TERRATEC 0x0ccd
|
|
#define USB_VID_TELESTAR 0x10b9
|
|
@@ -75,11 +76,14 @@
|
|
#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
|
|
#define USB_PID_ANYSEE 0x861f
|
|
#define USB_PID_AZUREWAVE_AD_TU700 0x3237
|
|
+#define USB_PID_AZUREWAVE_6007 0x0ccd
|
|
#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
|
|
#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
|
|
#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
|
|
@@ -125,6 +129,8 @@
|
|
#define USB_PID_E3C_EC168_3 0xfffb
|
|
#define USB_PID_E3C_EC168_4 0x1001
|
|
#define USB_PID_E3C_EC168_5 0x1002
|
|
+#define USB_PID_FREECOM_DVBT 0x0160
|
|
+#define USB_PID_FREECOM_DVBT_2 0x0161
|
|
#define USB_PID_UNIWILL_STK7700P 0x6003
|
|
#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012
|
|
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
|
|
@@ -148,6 +154,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
|
|
@@ -217,6 +224,11 @@
|
|
#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_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
|
|
@@ -226,6 +238,8 @@
|
|
#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062
|
|
#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078
|
|
#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab
|
|
+#define USB_PID_TERRATEC_H7 0x10b4
|
|
+#define USB_PID_TERRATEC_H7_2 0x10a3
|
|
#define USB_PID_TERRATEC_T3 0x10a0
|
|
#define USB_PID_TERRATEC_T5 0x10a1
|
|
#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
|
|
@@ -249,6 +263,8 @@
|
|
#define USB_PID_PCTV_400E 0x020f
|
|
#define USB_PID_PCTV_450E 0x0222
|
|
#define USB_PID_PCTV_452E 0x021f
|
|
+#define USB_PID_REALTEK_RTL2831U 0x2831
|
|
+#define USB_PID_REALTEK_RTL2832U 0x2832
|
|
#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007
|
|
#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a
|
|
#define USB_PID_NEBULA_DIGITV 0x0201
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/ddbridge/ddbridge-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/ddbridge/ddbridge-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/ddbridge/ddbridge-core.c
|
|
@@ -578,6 +578,7 @@ static int demod_attach_drxk(struct ddb_
|
|
struct drxk_config config;
|
|
|
|
memset(&config, 0, sizeof(config));
|
|
+ config.microcode_name = "drxk_a3.mc";
|
|
config.adr = 0x29 + (input->nr & 1);
|
|
|
|
fe = input->fe = dvb_attach(drxk_attach, &config, i2c);
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/drxk_hard.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/drxk_hard.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/drxk_hard.c
|
|
@@ -28,7 +28,6 @@
|
|
#include <linux/delay.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/i2c.h>
|
|
-#include <linux/version.h>
|
|
#include <asm/div64.h>
|
|
|
|
#include "dvb_frontend.h"
|
|
@@ -91,10 +90,6 @@ bool IsA1WithRomCode(struct drxk_state *
|
|
#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03)
|
|
#endif
|
|
|
|
-#ifndef DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH
|
|
-#define DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH (0x06)
|
|
-#endif
|
|
-
|
|
#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700
|
|
#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500
|
|
|
|
@@ -650,9 +645,6 @@ static int init_state(struct drxk_state
|
|
u32 ulQual83 = DEFAULT_MER_83;
|
|
u32 ulQual93 = DEFAULT_MER_93;
|
|
|
|
- u32 ulDVBTStaticTSClock = 1;
|
|
- u32 ulDVBCStaticTSClock = 1;
|
|
-
|
|
u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
|
|
u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
|
|
|
|
@@ -662,7 +654,6 @@ static int init_state(struct drxk_state
|
|
u32 ulGPIOCfg = 0x0113;
|
|
u32 ulInvertTSClock = 0;
|
|
u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
|
|
- u32 ulTSClockkStrength = DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH;
|
|
u32 ulDVBTBitrate = 50000000;
|
|
u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
|
|
|
|
@@ -815,8 +806,7 @@ static int init_state(struct drxk_state
|
|
state->m_invertSTR = false; /* If TRUE; invert STR signals */
|
|
state->m_invertVAL = false; /* If TRUE; invert VAL signals */
|
|
state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */
|
|
- state->m_DVBTStaticCLK = (ulDVBTStaticTSClock != 0);
|
|
- state->m_DVBCStaticCLK = (ulDVBCStaticTSClock != 0);
|
|
+
|
|
/* If TRUE; static MPEG clockrate will be used;
|
|
otherwise clockrate will adapt to the bitrate of the TS */
|
|
|
|
@@ -824,7 +814,6 @@ static int init_state(struct drxk_state
|
|
state->m_DVBCBitrate = ulDVBCBitrate;
|
|
|
|
state->m_TSDataStrength = (ulTSDataStrength & 0x07);
|
|
- state->m_TSClockkStrength = (ulTSClockkStrength & 0x07);
|
|
|
|
/* Maximum bitrate in b/s in case static clockrate is selected */
|
|
state->m_mpegTsStaticBitrate = 19392658;
|
|
@@ -1189,6 +1178,7 @@ static int MPEGTSConfigurePins(struct dr
|
|
int status = -1;
|
|
u16 sioPdrMclkCfg = 0;
|
|
u16 sioPdrMdxCfg = 0;
|
|
+ u16 err_cfg = 0;
|
|
|
|
dprintk(1, ": mpeg %s, %s mode\n",
|
|
mpegEnable ? "enable" : "disable",
|
|
@@ -1254,12 +1244,17 @@ static int MPEGTSConfigurePins(struct dr
|
|
status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg);
|
|
if (status < 0)
|
|
goto error;
|
|
- status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000); /* Disable */
|
|
+
|
|
+ if (state->enable_merr_cfg)
|
|
+ err_cfg = sioPdrMdxCfg;
|
|
+
|
|
+ status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg);
|
|
if (status < 0)
|
|
goto error;
|
|
- status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000); /* Disable */
|
|
+ status = write16(state, SIO_PDR_MVAL_CFG__A, err_cfg);
|
|
if (status < 0)
|
|
goto error;
|
|
+
|
|
if (state->m_enableParallel == true) {
|
|
/* paralel -> enable MD1 to MD7 */
|
|
status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg);
|
|
@@ -6070,9 +6065,7 @@ static int init_drxk(struct drxk_state *
|
|
if (status < 0)
|
|
goto error;
|
|
|
|
- if (!state->microcode_name)
|
|
- load_microcode(state, "drxk_a3.mc");
|
|
- else
|
|
+ if (state->microcode_name)
|
|
load_microcode(state, state->microcode_name);
|
|
|
|
/* disable token-ring bus through OFDM block for possible ucode upload */
|
|
@@ -6323,15 +6316,12 @@ static int drxk_get_tune_settings(struct
|
|
switch (p->delivery_system) {
|
|
case SYS_DVBC_ANNEX_A:
|
|
case SYS_DVBC_ANNEX_C:
|
|
+ case SYS_DVBT:
|
|
sets->min_delay_ms = 3000;
|
|
sets->max_drift = 0;
|
|
sets->step_size = 0;
|
|
return 0;
|
|
default:
|
|
- /*
|
|
- * For DVB-T, let it use the default DVB core way, that is:
|
|
- * fepriv->step_size = fe->ops.info.frequency_stepsize * 2
|
|
- */
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
@@ -6391,6 +6381,21 @@ struct dvb_frontend *drxk_attach(const s
|
|
state->antenna_gpio = config->antenna_gpio;
|
|
state->antenna_dvbt = config->antenna_dvbt;
|
|
state->m_ChunkSize = config->chunk_size;
|
|
+ state->enable_merr_cfg = config->enable_merr_cfg;
|
|
+
|
|
+ if (config->dynamic_clk) {
|
|
+ state->m_DVBTStaticCLK = 0;
|
|
+ state->m_DVBCStaticCLK = 0;
|
|
+ } else {
|
|
+ state->m_DVBTStaticCLK = 1;
|
|
+ state->m_DVBCStaticCLK = 1;
|
|
+ }
|
|
+
|
|
+
|
|
+ if (config->mpeg_out_clk_strength)
|
|
+ state->m_TSClockkStrength = config->mpeg_out_clk_strength & 0x07;
|
|
+ else
|
|
+ state->m_TSClockkStrength = 0x06;
|
|
|
|
if (config->parallel_ts)
|
|
state->m_enableParallel = true;
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/ngene/ngene-cards.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/ngene/ngene-cards.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/ngene/ngene-cards.c
|
|
@@ -216,6 +216,7 @@ static int demod_attach_drxk(struct ngen
|
|
struct drxk_config config;
|
|
|
|
memset(&config, 0, sizeof(config));
|
|
+ config.microcode_name = "drxk_a3.mc";
|
|
config.adr = 0x29 + (chan->number ^ 2);
|
|
|
|
chan->fe = dvb_attach(drxk_attach, &config, i2c);
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/drxk.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/drxk.h
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/drxk.h
|
|
@@ -7,15 +7,19 @@
|
|
/**
|
|
* struct drxk_config - Configure the initial parameters for DRX-K
|
|
*
|
|
- * adr: I2C Address of the DRX-K
|
|
- * parallel_ts: true means that the device uses parallel TS,
|
|
+ * @adr: I2C Address of the DRX-K
|
|
+ * @parallel_ts: True means that the device uses parallel TS,
|
|
* Serial otherwise.
|
|
- * single_master: Device is on the single master mode
|
|
- * no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
|
|
- * antenna_gpio: GPIO bit used to control the antenna
|
|
- * antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
|
|
+ * @dynamic_clk: True means that the clock will be dynamically
|
|
+ * adjusted. Static clock otherwise.
|
|
+ * @enable_merr_cfg: Enable SIO_PDR_PERR_CFG/SIO_PDR_MVAL_CFG.
|
|
+ * @single_master: Device is on the single master mode
|
|
+ * @no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
|
|
+ * @antenna_gpio: GPIO bit used to control the antenna
|
|
+ * @antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
|
|
* means that 1=DVBC, 0 = DVBT. Zero means the opposite.
|
|
- * microcode_name: Name of the firmware file with the microcode
|
|
+ * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength.
|
|
+ * @microcode_name: Name of the firmware file with the microcode
|
|
*
|
|
* On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
|
|
* UIO-3.
|
|
@@ -25,11 +29,14 @@ struct drxk_config {
|
|
bool single_master;
|
|
bool no_i2c_bridge;
|
|
bool parallel_ts;
|
|
+ bool dynamic_clk;
|
|
+ bool enable_merr_cfg;
|
|
|
|
bool antenna_dvbt;
|
|
u16 antenna_gpio;
|
|
|
|
- int chunk_size;
|
|
+ u8 mpeg_out_clk_strength;
|
|
+ int chunk_size;
|
|
|
|
const char *microcode_name;
|
|
};
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/drxk_hard.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/drxk_hard.h
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/drxk_hard.h
|
|
@@ -332,6 +332,7 @@ struct drxk_state {
|
|
|
|
u16 UIO_mask; /* Bits used by UIO */
|
|
|
|
+ bool enable_merr_cfg;
|
|
bool single_master;
|
|
bool no_i2c_bridge;
|
|
bool antenna_dvbt;
|
|
Index: linux-3.3.x86_64/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
|
|
+++ linux-3.3.x86_64/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
|
|
@@ -18,6 +18,8 @@
|
|
*/
|
|
|
|
static struct rc_map_table nec_terratec_cinergy_xs[] = {
|
|
+
|
|
+ /* Terratec Grey IR, with most keys in orange */
|
|
{ 0x1441, KEY_HOME},
|
|
{ 0x1401, KEY_POWER2},
|
|
|
|
@@ -78,6 +80,56 @@ static struct rc_map_table nec_terratec_
|
|
{ 0x144e, KEY_REWIND},
|
|
{ 0x144f, KEY_FASTFORWARD},
|
|
{ 0x145c, KEY_NEXT},
|
|
+
|
|
+ /* Terratec Black IR, with most keys in black */
|
|
+ { 0x04eb01, KEY_POWER2},
|
|
+
|
|
+ { 0x04eb02, KEY_1},
|
|
+ { 0x04eb03, KEY_2},
|
|
+ { 0x04eb04, KEY_3},
|
|
+ { 0x04eb05, KEY_4},
|
|
+ { 0x04eb06, KEY_5},
|
|
+ { 0x04eb07, KEY_6},
|
|
+ { 0x04eb08, KEY_7},
|
|
+ { 0x04eb09, KEY_8},
|
|
+ { 0x04eb0a, KEY_9},
|
|
+ { 0x04eb0c, KEY_0},
|
|
+
|
|
+ { 0x04eb0b, KEY_TEXT}, /* TXT */
|
|
+ { 0x04eb0d, KEY_REFRESH}, /* Refresh */
|
|
+
|
|
+ { 0x04eb0e, KEY_HOME},
|
|
+ { 0x04eb0f, KEY_EPG},
|
|
+
|
|
+ { 0x04eb10, KEY_UP},
|
|
+ { 0x04eb11, KEY_LEFT},
|
|
+ { 0x04eb12, KEY_OK},
|
|
+ { 0x04eb13, KEY_RIGHT},
|
|
+ { 0x04eb14, KEY_DOWN},
|
|
+
|
|
+ { 0x04eb15, KEY_BACKSPACE},
|
|
+ { 0x04eb16, KEY_INFO},
|
|
+
|
|
+ { 0x04eb17, KEY_RED},
|
|
+ { 0x04eb18, KEY_GREEN},
|
|
+ { 0x04eb19, KEY_YELLOW},
|
|
+ { 0x04eb1a, KEY_BLUE},
|
|
+
|
|
+ { 0x04eb1c, KEY_VOLUMEUP},
|
|
+ { 0x04eb1e, KEY_VOLUMEDOWN},
|
|
+
|
|
+ { 0x04eb1d, KEY_MUTE},
|
|
+
|
|
+ { 0x04eb1b, KEY_CHANNELUP},
|
|
+ { 0x04eb1f, KEY_CHANNELDOWN},
|
|
+
|
|
+ { 0x04eb40, KEY_RECORD},
|
|
+ { 0x04eb4c, KEY_PLAY},
|
|
+ { 0x04eb58, KEY_PAUSE},
|
|
+
|
|
+ { 0x04eb54, KEY_REWIND},
|
|
+ { 0x04eb48, KEY_STOP},
|
|
+ { 0x04eb5c, KEY_NEXT},
|
|
};
|
|
|
|
static struct rc_map_list nec_terratec_cinergy_xs_map = {
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/Kconfig
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/Kconfig
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/Kconfig
|
|
@@ -361,6 +361,14 @@ config DVB_USB_EC168
|
|
help
|
|
Say Y here to support the E3C EC168 DVB-T USB2.0 receiver.
|
|
|
|
+config DVB_USB_AZ6007
|
|
+ tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support"
|
|
+ depends on DVB_USB
|
|
+ select DVB_DRXK if !DVB_FE_CUSTOMISE
|
|
+ select MEDIA_TUNER_MT2063 if !DVB_FE_CUSTOMISE
|
|
+ help
|
|
+ Say Y here to support theAfatech AF9005 based DVB-T/DVB-C receivers.
|
|
+
|
|
config DVB_USB_AZ6027
|
|
tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support"
|
|
depends on DVB_USB
|
|
@@ -378,6 +386,7 @@ config DVB_USB_LME2510
|
|
select DVB_IX2505V if !DVB_FE_CUSTOMISE
|
|
select DVB_STV0299 if !DVB_FE_CUSTOMISE
|
|
select DVB_PLL if !DVB_FE_CUSTOMISE
|
|
+ select DVB_M88RS2000 if !DVB_FE_CUSTOMISE
|
|
help
|
|
Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 .
|
|
|
|
@@ -403,3 +412,25 @@ config DVB_USB_MXL111SF
|
|
select VIDEO_TVEEPROM
|
|
help
|
|
Say Y here to support the MxL111SF USB2.0 DTV receiver.
|
|
+
|
|
+config DVB_USB_RTL28XXU
|
|
+ tristate "Realtek RTL28xxU DVB USB support"
|
|
+ depends on DVB_USB && EXPERIMENTAL
|
|
+ select DVB_RTL2830
|
|
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
|
|
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
|
|
+ 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
|
|
+ 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.
|
|
+
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/Makefile
|
|
@@ -54,7 +54,6 @@ obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb
|
|
dvb-usb-opera-objs = opera1.o
|
|
obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o
|
|
|
|
-
|
|
dvb-usb-af9005-objs = af9005.o af9005-fe.o
|
|
obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o
|
|
|
|
@@ -88,6 +87,9 @@ obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-f
|
|
dvb-usb-ec168-objs = ec168.o
|
|
obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
|
|
|
|
+dvb-usb-az6007-objs = az6007.o
|
|
+obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o
|
|
+
|
|
dvb-usb-az6027-objs = az6027.o
|
|
obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o
|
|
|
|
@@ -105,8 +107,15 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-us
|
|
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
|
|
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
|
|
|
|
-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
|
|
+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
|
|
-ccflags-y += -Idrivers/media/common/tuners
|
|
-EXTRA_CFLAGS += -Idrivers/media/dvb/ttpci
|
|
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/ttpci
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/omap/omap_vout.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/omap/omap_vout.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/omap/omap_vout.c
|
|
@@ -2268,13 +2268,12 @@ static struct platform_driver omap_vout_
|
|
.driver = {
|
|
.name = VOUT_NAME,
|
|
},
|
|
- .probe = omap_vout_probe,
|
|
.remove = omap_vout_remove,
|
|
};
|
|
|
|
static int __init omap_vout_init(void)
|
|
{
|
|
- if (platform_driver_register(&omap_vout_driver) != 0) {
|
|
+ if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) {
|
|
printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n");
|
|
return -EINVAL;
|
|
}
|
|
Index: linux-3.3.x86_64/Documentation/DocBook/media/v4l/compat.xml
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/Documentation/DocBook/media/v4l/compat.xml
|
|
+++ linux-3.3.x86_64/Documentation/DocBook/media/v4l/compat.xml
|
|
@@ -2393,6 +2393,10 @@ details.</para>
|
|
to the <link linkend="control">User controls class</link>.
|
|
</para>
|
|
</listitem>
|
|
+ <listitem>
|
|
+ <para>Added the device_caps field to struct v4l2_capabilities and added the new
|
|
+ V4L2_CAP_DEVICE_CAPS capability.</para>
|
|
+ </listitem>
|
|
</orderedlist>
|
|
</section>
|
|
|
|
Index: linux-3.3.x86_64/Documentation/DocBook/media/v4l/v4l2.xml
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/Documentation/DocBook/media/v4l/v4l2.xml
|
|
+++ linux-3.3.x86_64/Documentation/DocBook/media/v4l/v4l2.xml
|
|
@@ -128,6 +128,13 @@ structs, ioctls) must be noted in more d
|
|
applications. -->
|
|
|
|
<revision>
|
|
+ <revnumber>3.3</revnumber>
|
|
+ <date>2012-01-11</date>
|
|
+ <authorinitials>hv</authorinitials>
|
|
+ <revremark>Added device_caps field to struct v4l2_capabilities.</revremark>
|
|
+ </revision>
|
|
+
|
|
+ <revision>
|
|
<revnumber>3.2</revnumber>
|
|
<date>2011-08-26</date>
|
|
<authorinitials>hv</authorinitials>
|
|
@@ -417,7 +424,7 @@ and discussions on the V4L mailing list.
|
|
</partinfo>
|
|
|
|
<title>Video for Linux Two API Specification</title>
|
|
- <subtitle>Revision 3.2</subtitle>
|
|
+ <subtitle>Revision 3.3</subtitle>
|
|
|
|
<chapter id="common">
|
|
&sub-common;
|
|
Index: linux-3.3.x86_64/Documentation/DocBook/media/v4l/vidioc-querycap.xml
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/Documentation/DocBook/media/v4l/vidioc-querycap.xml
|
|
+++ linux-3.3.x86_64/Documentation/DocBook/media/v4l/vidioc-querycap.xml
|
|
@@ -124,12 +124,35 @@ printf ("Version: %u.%u.%u\n",
|
|
<row>
|
|
<entry>__u32</entry>
|
|
<entry><structfield>capabilities</structfield></entry>
|
|
- <entry>Device capabilities, see <xref
|
|
- linkend="device-capabilities" />.</entry>
|
|
+ <entry>Available capabilities of the physical device as a whole, see <xref
|
|
+ linkend="device-capabilities" />. The same physical device can export
|
|
+ multiple devices in /dev (e.g. /dev/videoX, /dev/vbiY and /dev/radioZ).
|
|
+ The <structfield>capabilities</structfield> field should contain a union
|
|
+ of all capabilities available around the several V4L2 devices exported
|
|
+ to userspace.
|
|
+ For all those devices the <structfield>capabilities</structfield> field
|
|
+ returns the same set of capabilities. This allows applications to open
|
|
+ just one of the devices (typically the video device) and discover whether
|
|
+ video, vbi and/or radio are also supported.
|
|
+ </entry>
|
|
</row>
|
|
<row>
|
|
<entry>__u32</entry>
|
|
- <entry><structfield>reserved</structfield>[4]</entry>
|
|
+ <entry><structfield>device_caps</structfield></entry>
|
|
+ <entry>Device capabilities of the opened device, see <xref
|
|
+ linkend="device-capabilities" />. Should contain the available capabilities
|
|
+ of that specific device node. So, for example, <structfield>device_caps</structfield>
|
|
+ of a radio device will only contain radio related capabilities and
|
|
+ no video or vbi capabilities. This field is only set if the <structfield>capabilities</structfield>
|
|
+ field contains the <constant>V4L2_CAP_DEVICE_CAPS</constant> capability.
|
|
+ Only the <structfield>capabilities</structfield> field can have the
|
|
+ <constant>V4L2_CAP_DEVICE_CAPS</constant> capability, <structfield>device_caps</structfield>
|
|
+ will never set <constant>V4L2_CAP_DEVICE_CAPS</constant>.
|
|
+ </entry>
|
|
+ </row>
|
|
+ <row>
|
|
+ <entry>__u32</entry>
|
|
+ <entry><structfield>reserved</structfield>[3]</entry>
|
|
<entry>Reserved for future extensions. Drivers must set
|
|
this array to zero.</entry>
|
|
</row>
|
|
@@ -276,6 +299,13 @@ linkend="async">asynchronous</link> I/O
|
|
<entry>The device supports the <link
|
|
linkend="mmap">streaming</link> I/O method.</entry>
|
|
</row>
|
|
+ <row>
|
|
+ <entry><constant>V4L2_CAP_DEVICE_CAPS</constant></entry>
|
|
+ <entry>0x80000000</entry>
|
|
+ <entry>The driver fills the <structfield>device_caps</structfield>
|
|
+ field. This capability can only appear in the <structfield>capabilities</structfield>
|
|
+ field and never in the <structfield>device_caps</structfield> field.</entry>
|
|
+ </row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
Index: linux-3.3.x86_64/drivers/media/video/cx231xx/cx231xx-417.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cx231xx/cx231xx-417.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cx231xx/cx231xx-417.c
|
|
@@ -1686,7 +1686,6 @@ static struct v4l2_capability pvr_capabi
|
|
.capabilities = (V4L2_CAP_VIDEO_CAPTURE |
|
|
V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
|
|
V4L2_CAP_STREAMING | V4L2_CAP_READWRITE),
|
|
- .reserved = {0, 0, 0, 0}
|
|
};
|
|
static int vidioc_querycap(struct file *file, void *priv,
|
|
struct v4l2_capability *cap)
|
|
Index: linux-3.3.x86_64/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
|
|
@@ -96,7 +96,6 @@ static struct v4l2_capability pvr_capabi
|
|
.capabilities = (V4L2_CAP_VIDEO_CAPTURE |
|
|
V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
|
|
V4L2_CAP_READWRITE),
|
|
- .reserved = {0,0,0,0}
|
|
};
|
|
|
|
static struct v4l2_fmtdesc pvr_fmtdesc [] = {
|
|
Index: linux-3.3.x86_64/drivers/media/video/v4l2-ioctl.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/v4l2-ioctl.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/v4l2-ioctl.c
|
|
@@ -260,6 +260,8 @@ static const char *v4l2_ioctls[] = {
|
|
[_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",
|
|
|
|
@@ -540,10 +542,12 @@ static long __video_do_ioctl(struct file
|
|
if (!ret)
|
|
dbgarg(cmd, "driver=%s, card=%s, bus=%s, "
|
|
"version=0x%08x, "
|
|
- "capabilities=0x%08x\n",
|
|
+ "capabilities=0x%08x, "
|
|
+ "device_caps=0x%08x\n",
|
|
cap->driver, cap->card, cap->bus_info,
|
|
cap->version,
|
|
- cap->capabilities);
|
|
+ cap->capabilities,
|
|
+ cap->device_caps);
|
|
break;
|
|
}
|
|
|
|
@@ -1762,6 +1766,32 @@ static long __video_do_ioctl(struct file
|
|
dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
|
|
break;
|
|
}
|
|
+ case VIDIOC_DECODER_CMD:
|
|
+ {
|
|
+ struct v4l2_decoder_cmd *p = arg;
|
|
+
|
|
+ if (!ops->vidioc_decoder_cmd)
|
|
+ break;
|
|
+ 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);
|
|
+ break;
|
|
+ }
|
|
+ case VIDIOC_TRY_DECODER_CMD:
|
|
+ {
|
|
+ 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);
|
|
+ break;
|
|
+ }
|
|
case VIDIOC_G_PARM:
|
|
{
|
|
struct v4l2_streamparm *p = arg;
|
|
@@ -1909,7 +1939,13 @@ static long __video_do_ioctl(struct file
|
|
{
|
|
if (!ops->vidioc_log_status)
|
|
break;
|
|
+ if (vfd->v4l2_dev)
|
|
+ pr_info("%s: ================= START STATUS =================\n",
|
|
+ vfd->v4l2_dev->name);
|
|
ret = ops->vidioc_log_status(file, fh);
|
|
+ if (vfd->v4l2_dev)
|
|
+ pr_info("%s: ================== END STATUS ==================\n",
|
|
+ vfd->v4l2_dev->name);
|
|
break;
|
|
}
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
@@ -2419,7 +2455,7 @@ video_usercopy(struct file *file, unsign
|
|
/* Handles IOCTL */
|
|
err = func(file, cmd, parg);
|
|
if (err == -ENOIOCTLCMD)
|
|
- err = -EINVAL;
|
|
+ err = -ENOTTY;
|
|
|
|
if (has_array_args) {
|
|
*kernel_ptr = user_ptr;
|
|
Index: linux-3.3.x86_64/include/linux/videodev2.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/linux/videodev2.h
|
|
+++ linux-3.3.x86_64/include/linux/videodev2.h
|
|
@@ -235,16 +235,25 @@ struct v4l2_fract {
|
|
__u32 denominator;
|
|
};
|
|
|
|
-/*
|
|
- * D R I V E R C A P A B I L I T I E S
|
|
- */
|
|
+/**
|
|
+ * struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP
|
|
+ *
|
|
+ * @driver: name of the driver module (e.g. "bttv")
|
|
+ * @card: name of the card (e.g. "Hauppauge WinTV")
|
|
+ * @bus_info: name of the bus (e.g. "PCI:" + pci_name(pci_dev) )
|
|
+ * @version: KERNEL_VERSION
|
|
+ * @capabilities: capabilities of the physical device as a whole
|
|
+ * @device_caps: capabilities accessed via this particular device (node)
|
|
+ * @reserved: reserved fields for future extensions
|
|
+ */
|
|
struct v4l2_capability {
|
|
- __u8 driver[16]; /* i.e. "bttv" */
|
|
- __u8 card[32]; /* i.e. "Hauppauge WinTV" */
|
|
- __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
|
|
- __u32 version; /* should use KERNEL_VERSION() */
|
|
- __u32 capabilities; /* Device capabilities */
|
|
- __u32 reserved[4];
|
|
+ __u8 driver[16];
|
|
+ __u8 card[32];
|
|
+ __u8 bus_info[32];
|
|
+ __u32 version;
|
|
+ __u32 capabilities;
|
|
+ __u32 device_caps;
|
|
+ __u32 reserved[3];
|
|
};
|
|
|
|
/* Values for 'capabilities' field */
|
|
@@ -274,6 +283,8 @@ struct v4l2_capability {
|
|
#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */
|
|
#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
|
|
|
|
+#define V4L2_CAP_DEVICE_CAPS 0x80000000 /* sets device capabilities field */
|
|
+
|
|
/*
|
|
* V I D E O I M A G E F O R M A T
|
|
*/
|
|
@@ -751,20 +762,20 @@ struct v4l2_crop {
|
|
|
|
/* Selection targets */
|
|
|
|
-/* current cropping area */
|
|
-#define V4L2_SEL_TGT_CROP_ACTIVE 0
|
|
-/* default cropping area */
|
|
-#define V4L2_SEL_TGT_CROP_DEFAULT 1
|
|
-/* cropping bounds */
|
|
-#define V4L2_SEL_TGT_CROP_BOUNDS 2
|
|
-/* current composing area */
|
|
-#define V4L2_SEL_TGT_COMPOSE_ACTIVE 256
|
|
-/* default composing area */
|
|
-#define V4L2_SEL_TGT_COMPOSE_DEFAULT 257
|
|
-/* composing bounds */
|
|
-#define V4L2_SEL_TGT_COMPOSE_BOUNDS 258
|
|
-/* current composing area plus all padding pixels */
|
|
-#define V4L2_SEL_TGT_COMPOSE_PADDED 259
|
|
+/* Current cropping area */
|
|
+#define V4L2_SEL_TGT_CROP_ACTIVE 0x0000
|
|
+/* Default cropping area */
|
|
+#define V4L2_SEL_TGT_CROP_DEFAULT 0x0001
|
|
+/* Cropping bounds */
|
|
+#define V4L2_SEL_TGT_CROP_BOUNDS 0x0002
|
|
+/* Current composing area */
|
|
+#define V4L2_SEL_TGT_COMPOSE_ACTIVE 0x0100
|
|
+/* Default composing area */
|
|
+#define V4L2_SEL_TGT_COMPOSE_DEFAULT 0x0101
|
|
+/* Composing bounds */
|
|
+#define V4L2_SEL_TGT_COMPOSE_BOUNDS 0x0102
|
|
+/* Current composing area plus all padding pixels */
|
|
+#define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103
|
|
|
|
/**
|
|
* struct v4l2_selection - selection info
|
|
@@ -774,7 +785,7 @@ struct v4l2_crop {
|
|
* @r: coordinates of selection window
|
|
* @reserved: for future use, rounds structure size to 64 bytes, set to zero
|
|
*
|
|
- * Hardware may use multiple helper window to process a video stream.
|
|
+ * 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.
|
|
*/
|
|
@@ -1125,6 +1136,7 @@ struct v4l2_ext_controls {
|
|
#define V4L2_CTRL_CLASS_CAMERA 0x009a0000 /* Camera class 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_ID_MASK (0x0fffffff)
|
|
#define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL)
|
|
@@ -1396,6 +1408,16 @@ enum v4l2_mpeg_audio_ac3_bitrate {
|
|
V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17,
|
|
V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18,
|
|
};
|
|
+#define V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK (V4L2_CID_MPEG_BASE+112)
|
|
+enum v4l2_mpeg_audio_dec_playback {
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO = 0,
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO = 1,
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT = 2,
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_RIGHT = 3,
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO = 4,
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO = 5,
|
|
+};
|
|
+#define V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK (V4L2_CID_MPEG_BASE+113)
|
|
|
|
/* MPEG video controls specific to multiplexed streams */
|
|
#define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200)
|
|
@@ -1446,6 +1468,9 @@ enum v4l2_mpeg_video_multi_slice_mode {
|
|
V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES = 2,
|
|
};
|
|
#define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_MPEG_BASE+222)
|
|
+#define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_MPEG_BASE+223)
|
|
+#define V4L2_CID_MPEG_VIDEO_DEC_FRAME (V4L2_CID_MPEG_BASE+224)
|
|
+
|
|
#define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_MPEG_BASE+300)
|
|
#define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_MPEG_BASE+301)
|
|
#define V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (V4L2_CID_MPEG_BASE+302)
|
|
@@ -1734,6 +1759,29 @@ enum v4l2_flash_strobe_source {
|
|
#define V4L2_CID_FLASH_CHARGE (V4L2_CID_FLASH_CLASS_BASE + 11)
|
|
#define V4L2_CID_FLASH_READY (V4L2_CID_FLASH_CLASS_BASE + 12)
|
|
|
|
+/* JPEG-class control IDs defined by V4L2 */
|
|
+#define V4L2_CID_JPEG_CLASS_BASE (V4L2_CTRL_CLASS_JPEG | 0x900)
|
|
+#define V4L2_CID_JPEG_CLASS (V4L2_CTRL_CLASS_JPEG | 1)
|
|
+
|
|
+#define V4L2_CID_JPEG_CHROMA_SUBSAMPLING (V4L2_CID_JPEG_CLASS_BASE + 1)
|
|
+enum v4l2_jpeg_chroma_subsampling {
|
|
+ V4L2_JPEG_CHROMA_SUBSAMPLING_444 = 0,
|
|
+ V4L2_JPEG_CHROMA_SUBSAMPLING_422 = 1,
|
|
+ V4L2_JPEG_CHROMA_SUBSAMPLING_420 = 2,
|
|
+ V4L2_JPEG_CHROMA_SUBSAMPLING_411 = 3,
|
|
+ V4L2_JPEG_CHROMA_SUBSAMPLING_410 = 4,
|
|
+ V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY = 5,
|
|
+};
|
|
+#define V4L2_CID_JPEG_RESTART_INTERVAL (V4L2_CID_JPEG_CLASS_BASE + 2)
|
|
+#define V4L2_CID_JPEG_COMPRESSION_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 3)
|
|
+
|
|
+#define V4L2_CID_JPEG_ACTIVE_MARKER (V4L2_CID_JPEG_CLASS_BASE + 4)
|
|
+#define V4L2_JPEG_ACTIVE_MARKER_APP0 (1 << 0)
|
|
+#define V4L2_JPEG_ACTIVE_MARKER_APP1 (1 << 1)
|
|
+#define V4L2_JPEG_ACTIVE_MARKER_COM (1 << 16)
|
|
+#define V4L2_JPEG_ACTIVE_MARKER_DQT (1 << 17)
|
|
+#define V4L2_JPEG_ACTIVE_MARKER_DHT (1 << 18)
|
|
+
|
|
/*
|
|
* T U N I N G
|
|
*/
|
|
@@ -1897,6 +1945,54 @@ struct v4l2_encoder_cmd {
|
|
};
|
|
};
|
|
|
|
+/* Decoder commands */
|
|
+#define V4L2_DEC_CMD_START (0)
|
|
+#define V4L2_DEC_CMD_STOP (1)
|
|
+#define V4L2_DEC_CMD_PAUSE (2)
|
|
+#define V4L2_DEC_CMD_RESUME (3)
|
|
+
|
|
+/* Flags for V4L2_DEC_CMD_START */
|
|
+#define V4L2_DEC_CMD_START_MUTE_AUDIO (1 << 0)
|
|
+
|
|
+/* Flags for V4L2_DEC_CMD_PAUSE */
|
|
+#define V4L2_DEC_CMD_PAUSE_TO_BLACK (1 << 0)
|
|
+
|
|
+/* Flags for V4L2_DEC_CMD_STOP */
|
|
+#define V4L2_DEC_CMD_STOP_TO_BLACK (1 << 0)
|
|
+#define V4L2_DEC_CMD_STOP_IMMEDIATELY (1 << 1)
|
|
+
|
|
+/* Play format requirements (returned by the driver): */
|
|
+
|
|
+/* The decoder has no special format requirements */
|
|
+#define V4L2_DEC_START_FMT_NONE (0)
|
|
+/* The decoder requires full GOPs */
|
|
+#define V4L2_DEC_START_FMT_GOP (1)
|
|
+
|
|
+/* The structure must be zeroed before use by the application
|
|
+ This ensures it can be extended safely in the future. */
|
|
+struct v4l2_decoder_cmd {
|
|
+ __u32 cmd;
|
|
+ __u32 flags;
|
|
+ union {
|
|
+ struct {
|
|
+ __u64 pts;
|
|
+ } stop;
|
|
+
|
|
+ struct {
|
|
+ /* 0 or 1000 specifies normal speed,
|
|
+ 1 specifies forward single stepping,
|
|
+ -1 specifies backward single stepping,
|
|
+ >1: playback at speed/1000 of the normal speed,
|
|
+ <-1: reverse playback at (-speed/1000) of the normal speed. */
|
|
+ __s32 speed;
|
|
+ __u32 format;
|
|
+ } start;
|
|
+
|
|
+ struct {
|
|
+ __u32 data[16];
|
|
+ } raw;
|
|
+ };
|
|
+};
|
|
#endif
|
|
|
|
|
|
@@ -2307,6 +2403,11 @@ struct v4l2_create_buffers {
|
|
#define VIDIOC_G_SELECTION _IOWR('V', 94, struct v4l2_selection)
|
|
#define VIDIOC_S_SELECTION _IOWR('V', 95, struct v4l2_selection)
|
|
|
|
+/* Experimental, these two ioctls may change over the next couple of kernel
|
|
+ versions. */
|
|
+#define VIDIOC_DECODER_CMD _IOWR('V', 96, struct v4l2_decoder_cmd)
|
|
+#define VIDIOC_TRY_DECODER_CMD _IOWR('V', 97, struct v4l2_decoder_cmd)
|
|
+
|
|
/* Reminder: when adding new ioctls please add support for them to
|
|
drivers/media/video/v4l2-compat-ioctl32.c as well! */
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/vivi.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/vivi.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/vivi.c
|
|
@@ -819,8 +819,9 @@ static int vidioc_querycap(struct file *
|
|
strcpy(cap->driver, "vivi");
|
|
strcpy(cap->card, "vivi");
|
|
strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
|
|
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \
|
|
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
|
|
V4L2_CAP_READWRITE;
|
|
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
|
return 0;
|
|
}
|
|
|
|
@@ -958,14 +959,6 @@ static int vidioc_streamoff(struct file
|
|
return vb2_streamoff(&dev->vb_vidq, i);
|
|
}
|
|
|
|
-static int vidioc_log_status(struct file *file, void *priv)
|
|
-{
|
|
- struct vivi_dev *dev = video_drvdata(file);
|
|
-
|
|
- v4l2_ctrl_handler_log_status(&dev->ctrl_handler, dev->v4l2_dev.name);
|
|
- return 0;
|
|
-}
|
|
-
|
|
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
|
|
{
|
|
return 0;
|
|
@@ -1008,17 +1001,6 @@ static int vidioc_s_input(struct file *f
|
|
return 0;
|
|
}
|
|
|
|
-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;
|
|
- }
|
|
-}
|
|
-
|
|
/* --- controls ---------------------------------------------- */
|
|
|
|
static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
|
@@ -1057,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)
|
|
@@ -1209,8 +1184,8 @@ static const struct v4l2_ioctl_ops vivi_
|
|
.vidioc_s_input = vidioc_s_input,
|
|
.vidioc_streamon = vidioc_streamon,
|
|
.vidioc_streamoff = vidioc_streamoff,
|
|
- .vidioc_log_status = vidioc_log_status,
|
|
- .vidioc_subscribe_event = vidioc_subscribe_event,
|
|
+ .vidioc_log_status = v4l2_ctrl_log_status,
|
|
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
|
};
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-driver.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/ivtv-driver.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-driver.h
|
|
@@ -331,6 +331,7 @@ struct ivtv_stream {
|
|
struct ivtv *itv; /* for ease of use */
|
|
const char *name; /* name of the stream */
|
|
int type; /* stream type */
|
|
+ u32 caps; /* V4L2 capabilities */
|
|
|
|
struct v4l2_fh *fh; /* pointer to the streaming filehandle */
|
|
spinlock_t qlock; /* locks access to the queues */
|
|
@@ -630,6 +631,16 @@ struct ivtv {
|
|
|
|
struct v4l2_device v4l2_dev;
|
|
struct cx2341x_handler cxhdl;
|
|
+ struct {
|
|
+ /* PTS/Frame count control cluster */
|
|
+ struct v4l2_ctrl *ctrl_pts;
|
|
+ struct v4l2_ctrl *ctrl_frame;
|
|
+ };
|
|
+ struct {
|
|
+ /* Audio Playback control cluster */
|
|
+ struct v4l2_ctrl *ctrl_audio_playback;
|
|
+ struct v4l2_ctrl *ctrl_audio_multilingual_playback;
|
|
+ };
|
|
struct v4l2_ctrl_handler hdl_gpio;
|
|
struct v4l2_subdev sd_gpio; /* GPIO sub-device */
|
|
u16 instance;
|
|
@@ -649,7 +660,6 @@ struct ivtv {
|
|
u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */
|
|
u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */
|
|
|
|
-
|
|
/* Locking */
|
|
spinlock_t lock; /* lock access to this struct */
|
|
struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-ioctl.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/ivtv-ioctl.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-ioctl.c
|
|
@@ -246,34 +246,40 @@ static int ivtv_validate_speed(int cur_s
|
|
}
|
|
|
|
static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
|
|
- struct video_command *vc, int try)
|
|
+ struct v4l2_decoder_cmd *dc, int try)
|
|
{
|
|
struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
|
|
|
|
if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
|
|
return -EINVAL;
|
|
|
|
- switch (vc->cmd) {
|
|
- case VIDEO_CMD_PLAY: {
|
|
- vc->flags = 0;
|
|
- vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed);
|
|
- if (vc->play.speed < 0)
|
|
- vc->play.format = VIDEO_PLAY_FMT_GOP;
|
|
+ switch (dc->cmd) {
|
|
+ case V4L2_DEC_CMD_START: {
|
|
+ dc->flags &= V4L2_DEC_CMD_START_MUTE_AUDIO;
|
|
+ dc->start.speed = ivtv_validate_speed(itv->speed, dc->start.speed);
|
|
+ if (dc->start.speed < 0)
|
|
+ dc->start.format = V4L2_DEC_START_FMT_GOP;
|
|
+ else
|
|
+ dc->start.format = V4L2_DEC_START_FMT_NONE;
|
|
+ if (dc->start.speed != 500 && dc->start.speed != 1500)
|
|
+ dc->flags = dc->start.speed == 1000 ? 0 :
|
|
+ V4L2_DEC_CMD_START_MUTE_AUDIO;
|
|
if (try) break;
|
|
|
|
+ itv->speed_mute_audio = dc->flags & V4L2_DEC_CMD_START_MUTE_AUDIO;
|
|
if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)
|
|
return -EBUSY;
|
|
if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
|
|
/* forces ivtv_set_speed to be called */
|
|
itv->speed = 0;
|
|
}
|
|
- return ivtv_start_decoding(id, vc->play.speed);
|
|
+ return ivtv_start_decoding(id, dc->start.speed);
|
|
}
|
|
|
|
- case VIDEO_CMD_STOP:
|
|
- vc->flags &= VIDEO_CMD_STOP_IMMEDIATELY|VIDEO_CMD_STOP_TO_BLACK;
|
|
- if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY)
|
|
- vc->stop.pts = 0;
|
|
+ case V4L2_DEC_CMD_STOP:
|
|
+ dc->flags &= V4L2_DEC_CMD_STOP_IMMEDIATELY | V4L2_DEC_CMD_STOP_TO_BLACK;
|
|
+ if (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY)
|
|
+ dc->stop.pts = 0;
|
|
if (try) break;
|
|
if (atomic_read(&itv->decoding) == 0)
|
|
return 0;
|
|
@@ -281,22 +287,22 @@ static int ivtv_video_command(struct ivt
|
|
return -EBUSY;
|
|
|
|
itv->output_mode = OUT_NONE;
|
|
- return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts);
|
|
+ return ivtv_stop_v4l2_decode_stream(s, dc->flags, dc->stop.pts);
|
|
|
|
- case VIDEO_CMD_FREEZE:
|
|
- vc->flags &= VIDEO_CMD_FREEZE_TO_BLACK;
|
|
+ case V4L2_DEC_CMD_PAUSE:
|
|
+ dc->flags &= V4L2_DEC_CMD_PAUSE_TO_BLACK;
|
|
if (try) break;
|
|
if (itv->output_mode != OUT_MPG)
|
|
return -EBUSY;
|
|
if (atomic_read(&itv->decoding) > 0) {
|
|
ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,
|
|
- (vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0);
|
|
+ (dc->flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? 1 : 0);
|
|
set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
|
|
}
|
|
break;
|
|
|
|
- case VIDEO_CMD_CONTINUE:
|
|
- vc->flags = 0;
|
|
+ case V4L2_DEC_CMD_RESUME:
|
|
+ dc->flags = 0;
|
|
if (try) break;
|
|
if (itv->output_mode != OUT_MPG)
|
|
return -EBUSY;
|
|
@@ -754,12 +760,15 @@ static int ivtv_s_register(struct file *
|
|
|
|
static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap)
|
|
{
|
|
- struct ivtv *itv = fh2id(fh)->itv;
|
|
+ struct ivtv_open_id *id = fh2id(file->private_data);
|
|
+ struct ivtv *itv = id->itv;
|
|
+ struct ivtv_stream *s = &itv->streams[id->type];
|
|
|
|
strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
|
|
strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
|
|
snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
|
|
- vcap->capabilities = itv->v4l2_cap; /* capabilities */
|
|
+ vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS;
|
|
+ vcap->device_caps = s->caps;
|
|
return 0;
|
|
}
|
|
|
|
@@ -1476,8 +1485,6 @@ static int ivtv_log_status(struct file *
|
|
struct v4l2_audio audin;
|
|
int i;
|
|
|
|
- IVTV_INFO("================= START STATUS CARD #%d =================\n",
|
|
- itv->instance);
|
|
IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
|
|
if (itv->hw_flags & IVTV_HW_TVEEPROM) {
|
|
struct tveeprom tv;
|
|
@@ -1501,13 +1508,6 @@ static int ivtv_log_status(struct file *
|
|
"YUV Frames",
|
|
"Passthrough",
|
|
};
|
|
- static const char * const audio_modes[5] = {
|
|
- "Stereo",
|
|
- "Left",
|
|
- "Right",
|
|
- "Mono",
|
|
- "Swapped"
|
|
- };
|
|
static const char * const alpha_mode[4] = {
|
|
"None",
|
|
"Global",
|
|
@@ -1536,9 +1536,6 @@ static int ivtv_log_status(struct file *
|
|
ivtv_get_output(itv, itv->active_output, &vidout);
|
|
ivtv_get_audio_output(itv, 0, &audout);
|
|
IVTV_INFO("Video Output: %s\n", vidout.name);
|
|
- IVTV_INFO("Audio Output: %s (Stereo/Bilingual: %s/%s)\n", audout.name,
|
|
- audio_modes[itv->audio_stereo_mode],
|
|
- audio_modes[itv->audio_bilingual_mode]);
|
|
if (mode < 0 || mode > OUT_PASSTHROUGH)
|
|
mode = OUT_NONE;
|
|
IVTV_INFO("Output Mode: %s\n", output_modes[mode]);
|
|
@@ -1566,12 +1563,27 @@ static int ivtv_log_status(struct file *
|
|
IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n",
|
|
(long long)itv->mpg_data_received,
|
|
(long long)itv->vbi_data_inserted);
|
|
- IVTV_INFO("================== END STATUS CARD #%d ==================\n",
|
|
- itv->instance);
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
+static int ivtv_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
|
|
+{
|
|
+ struct ivtv_open_id *id = fh2id(file->private_data);
|
|
+ struct ivtv *itv = id->itv;
|
|
+
|
|
+ IVTV_DEBUG_IOCTL("VIDIOC_DECODER_CMD %d\n", dec->cmd);
|
|
+ return ivtv_video_command(itv, id, dec, false);
|
|
+}
|
|
+
|
|
+static int ivtv_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
|
|
+{
|
|
+ struct ivtv_open_id *id = fh2id(file->private_data);
|
|
+ struct ivtv *itv = id->itv;
|
|
+
|
|
+ IVTV_DEBUG_IOCTL("VIDIOC_TRY_DECODER_CMD %d\n", dec->cmd);
|
|
+ return ivtv_video_command(itv, id, dec, true);
|
|
+}
|
|
+
|
|
static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
|
|
{
|
|
struct ivtv_open_id *id = fh2id(filp->private_data);
|
|
@@ -1605,9 +1617,15 @@ static int ivtv_decoder_ioctls(struct fi
|
|
return ivtv_yuv_prep_frame(itv, args);
|
|
}
|
|
|
|
+ case IVTV_IOC_PASSTHROUGH_MODE:
|
|
+ IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n");
|
|
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
|
|
+ return -EINVAL;
|
|
+ return ivtv_passthrough_mode(itv, *(int *)arg != 0);
|
|
+
|
|
case VIDEO_GET_PTS: {
|
|
- u32 data[CX2341X_MBOX_MAX_DATA];
|
|
- u64 *pts = arg;
|
|
+ s64 *pts = arg;
|
|
+ s64 frame;
|
|
|
|
IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n");
|
|
if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
|
|
@@ -1616,29 +1634,12 @@ static int ivtv_decoder_ioctls(struct fi
|
|
}
|
|
if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
|
|
return -EINVAL;
|
|
-
|
|
- if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
|
|
- *pts = (u64) ((u64)itv->last_dec_timing[2] << 32) |
|
|
- (u64)itv->last_dec_timing[1];
|
|
- break;
|
|
- }
|
|
- *pts = 0;
|
|
- if (atomic_read(&itv->decoding)) {
|
|
- if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
|
|
- IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
|
|
- return -EIO;
|
|
- }
|
|
- memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
|
|
- set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
|
|
- *pts = (u64) ((u64) data[2] << 32) | (u64) data[1];
|
|
- /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/
|
|
- }
|
|
- break;
|
|
+ return ivtv_g_pts_frame(itv, pts, &frame);
|
|
}
|
|
|
|
case VIDEO_GET_FRAME_COUNT: {
|
|
- u32 data[CX2341X_MBOX_MAX_DATA];
|
|
- u64 *frame = arg;
|
|
+ s64 *frame = arg;
|
|
+ s64 pts;
|
|
|
|
IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n");
|
|
if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
|
|
@@ -1647,71 +1648,58 @@ static int ivtv_decoder_ioctls(struct fi
|
|
}
|
|
if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
|
|
return -EINVAL;
|
|
-
|
|
- if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
|
|
- *frame = itv->last_dec_timing[0];
|
|
- break;
|
|
- }
|
|
- *frame = 0;
|
|
- if (atomic_read(&itv->decoding)) {
|
|
- if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
|
|
- IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
|
|
- return -EIO;
|
|
- }
|
|
- memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
|
|
- set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
|
|
- *frame = data[0];
|
|
- }
|
|
- break;
|
|
+ return ivtv_g_pts_frame(itv, &pts, frame);
|
|
}
|
|
|
|
case VIDEO_PLAY: {
|
|
- struct video_command vc;
|
|
+ struct v4l2_decoder_cmd dc;
|
|
|
|
IVTV_DEBUG_IOCTL("VIDEO_PLAY\n");
|
|
- memset(&vc, 0, sizeof(vc));
|
|
- vc.cmd = VIDEO_CMD_PLAY;
|
|
- return ivtv_video_command(itv, id, &vc, 0);
|
|
+ memset(&dc, 0, sizeof(dc));
|
|
+ dc.cmd = V4L2_DEC_CMD_START;
|
|
+ return ivtv_video_command(itv, id, &dc, 0);
|
|
}
|
|
|
|
case VIDEO_STOP: {
|
|
- struct video_command vc;
|
|
+ struct v4l2_decoder_cmd dc;
|
|
|
|
IVTV_DEBUG_IOCTL("VIDEO_STOP\n");
|
|
- memset(&vc, 0, sizeof(vc));
|
|
- vc.cmd = VIDEO_CMD_STOP;
|
|
- vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY;
|
|
- return ivtv_video_command(itv, id, &vc, 0);
|
|
+ memset(&dc, 0, sizeof(dc));
|
|
+ dc.cmd = V4L2_DEC_CMD_STOP;
|
|
+ dc.flags = V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY;
|
|
+ return ivtv_video_command(itv, id, &dc, 0);
|
|
}
|
|
|
|
case VIDEO_FREEZE: {
|
|
- struct video_command vc;
|
|
+ struct v4l2_decoder_cmd dc;
|
|
|
|
IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n");
|
|
- memset(&vc, 0, sizeof(vc));
|
|
- vc.cmd = VIDEO_CMD_FREEZE;
|
|
- return ivtv_video_command(itv, id, &vc, 0);
|
|
+ memset(&dc, 0, sizeof(dc));
|
|
+ dc.cmd = V4L2_DEC_CMD_PAUSE;
|
|
+ return ivtv_video_command(itv, id, &dc, 0);
|
|
}
|
|
|
|
case VIDEO_CONTINUE: {
|
|
- struct video_command vc;
|
|
+ struct v4l2_decoder_cmd dc;
|
|
|
|
IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n");
|
|
- memset(&vc, 0, sizeof(vc));
|
|
- vc.cmd = VIDEO_CMD_CONTINUE;
|
|
- return ivtv_video_command(itv, id, &vc, 0);
|
|
+ memset(&dc, 0, sizeof(dc));
|
|
+ dc.cmd = V4L2_DEC_CMD_RESUME;
|
|
+ return ivtv_video_command(itv, id, &dc, 0);
|
|
}
|
|
|
|
case VIDEO_COMMAND:
|
|
case VIDEO_TRY_COMMAND: {
|
|
- struct video_command *vc = arg;
|
|
+ /* Note: struct v4l2_decoder_cmd has the same layout as
|
|
+ struct video_command */
|
|
+ struct v4l2_decoder_cmd *dc = arg;
|
|
int try = (cmd == VIDEO_TRY_COMMAND);
|
|
|
|
if (try)
|
|
- IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", vc->cmd);
|
|
+ IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", dc->cmd);
|
|
else
|
|
- IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", vc->cmd);
|
|
- return ivtv_video_command(itv, id, vc, try);
|
|
+ IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", dc->cmd);
|
|
+ return ivtv_video_command(itv, id, dc, try);
|
|
}
|
|
|
|
case VIDEO_GET_EVENT: {
|
|
@@ -1775,17 +1763,13 @@ static int ivtv_decoder_ioctls(struct fi
|
|
IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n");
|
|
if (iarg > AUDIO_STEREO_SWAPPED)
|
|
return -EINVAL;
|
|
- itv->audio_stereo_mode = iarg;
|
|
- ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
|
|
- return 0;
|
|
+ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg + 1);
|
|
|
|
case AUDIO_BILINGUAL_CHANNEL_SELECT:
|
|
IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n");
|
|
if (iarg > AUDIO_STEREO_SWAPPED)
|
|
return -EINVAL;
|
|
- itv->audio_bilingual_mode = iarg;
|
|
- ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
|
|
- return 0;
|
|
+ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg + 1);
|
|
|
|
default:
|
|
return -EINVAL;
|
|
@@ -1800,6 +1784,7 @@ static long ivtv_default(struct file *fi
|
|
|
|
if (!valid_prio) {
|
|
switch (cmd) {
|
|
+ case IVTV_IOC_PASSTHROUGH_MODE:
|
|
case VIDEO_PLAY:
|
|
case VIDEO_STOP:
|
|
case VIDEO_FREEZE:
|
|
@@ -1825,6 +1810,7 @@ static long ivtv_default(struct file *fi
|
|
}
|
|
|
|
case IVTV_IOC_DMA_FRAME:
|
|
+ case IVTV_IOC_PASSTHROUGH_MODE:
|
|
case VIDEO_GET_PTS:
|
|
case VIDEO_GET_FRAME_COUNT:
|
|
case VIDEO_GET_EVENT:
|
|
@@ -1889,6 +1875,8 @@ static const struct v4l2_ioctl_ops ivtv_
|
|
.vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap,
|
|
.vidioc_encoder_cmd = ivtv_encoder_cmd,
|
|
.vidioc_try_encoder_cmd = ivtv_try_encoder_cmd,
|
|
+ .vidioc_decoder_cmd = ivtv_decoder_cmd,
|
|
+ .vidioc_try_decoder_cmd = ivtv_try_decoder_cmd,
|
|
.vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out,
|
|
.vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap,
|
|
.vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap,
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-streams.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/ivtv-streams.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-streams.c
|
|
@@ -78,60 +78,73 @@ static struct {
|
|
int num_offset;
|
|
int dma, pio;
|
|
enum v4l2_buf_type buf_type;
|
|
+ u32 v4l2_caps;
|
|
const struct v4l2_file_operations *fops;
|
|
} ivtv_stream_info[] = {
|
|
{ /* IVTV_ENC_STREAM_TYPE_MPG */
|
|
"encoder MPG",
|
|
VFL_TYPE_GRABBER, 0,
|
|
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
|
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
|
|
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
|
|
&ivtv_v4l2_enc_fops
|
|
},
|
|
{ /* IVTV_ENC_STREAM_TYPE_YUV */
|
|
"encoder YUV",
|
|
VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
|
|
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
|
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
|
|
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
|
|
&ivtv_v4l2_enc_fops
|
|
},
|
|
{ /* IVTV_ENC_STREAM_TYPE_VBI */
|
|
"encoder VBI",
|
|
VFL_TYPE_VBI, 0,
|
|
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE,
|
|
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER |
|
|
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
|
|
&ivtv_v4l2_enc_fops
|
|
},
|
|
{ /* IVTV_ENC_STREAM_TYPE_PCM */
|
|
"encoder PCM",
|
|
VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
|
|
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE,
|
|
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
|
|
&ivtv_v4l2_enc_fops
|
|
},
|
|
{ /* IVTV_ENC_STREAM_TYPE_RAD */
|
|
"encoder radio",
|
|
VFL_TYPE_RADIO, 0,
|
|
PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE,
|
|
+ V4L2_CAP_RADIO | V4L2_CAP_TUNER,
|
|
&ivtv_v4l2_enc_fops
|
|
},
|
|
{ /* IVTV_DEC_STREAM_TYPE_MPG */
|
|
"decoder MPG",
|
|
VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
|
|
PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
|
|
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
|
|
&ivtv_v4l2_dec_fops
|
|
},
|
|
{ /* IVTV_DEC_STREAM_TYPE_VBI */
|
|
"decoder VBI",
|
|
VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
|
|
PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE,
|
|
+ V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE,
|
|
&ivtv_v4l2_enc_fops
|
|
},
|
|
{ /* IVTV_DEC_STREAM_TYPE_VOUT */
|
|
"decoder VOUT",
|
|
VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
|
|
PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT,
|
|
+ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
|
|
&ivtv_v4l2_dec_fops
|
|
},
|
|
{ /* IVTV_DEC_STREAM_TYPE_YUV */
|
|
"decoder YUV",
|
|
VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
|
|
PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
|
|
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
|
|
&ivtv_v4l2_dec_fops
|
|
}
|
|
};
|
|
@@ -149,6 +162,7 @@ static void ivtv_stream_init(struct ivtv
|
|
s->itv = itv;
|
|
s->type = type;
|
|
s->name = ivtv_stream_info[type].name;
|
|
+ s->caps = ivtv_stream_info[type].v4l2_caps;
|
|
|
|
if (ivtv_stream_info[type].pio)
|
|
s->dma = PCI_DMA_NONE;
|
|
@@ -209,8 +223,8 @@ static int ivtv_prep_dev(struct ivtv *it
|
|
|
|
s->vdev->num = num;
|
|
s->vdev->v4l2_dev = &itv->v4l2_dev;
|
|
- s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
|
|
s->vdev->fops = ivtv_stream_info[type].fops;
|
|
+ s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
|
|
s->vdev->release = video_device_release;
|
|
s->vdev->tvnorms = V4L2_STD_ALL;
|
|
s->vdev->lock = &itv->serialize_lock;
|
|
@@ -891,7 +905,7 @@ int ivtv_stop_v4l2_decode_stream(struct
|
|
IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags);
|
|
|
|
/* Stop Decoder */
|
|
- if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) {
|
|
+ if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) {
|
|
u32 tmp = 0;
|
|
|
|
/* Wait until the decoder is no longer running */
|
|
@@ -911,7 +925,7 @@ int ivtv_stop_v4l2_decode_stream(struct
|
|
break;
|
|
}
|
|
}
|
|
- ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0);
|
|
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0);
|
|
|
|
/* turn off notification of dual/stereo mode change */
|
|
ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/it913x.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/it913x.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/it913x.c
|
|
@@ -64,6 +64,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr
|
|
struct it913x_state {
|
|
u8 id;
|
|
struct ite_config it913x_config;
|
|
+ u8 pid_filter_onoff;
|
|
};
|
|
|
|
struct ite_config it913x_config;
|
|
@@ -237,12 +238,27 @@ static int it913x_read_reg(struct usb_de
|
|
|
|
static u32 it913x_query(struct usb_device *udev, u8 pro)
|
|
{
|
|
- int ret;
|
|
+ int ret, i;
|
|
u8 data[4];
|
|
- ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ,
|
|
- 0x1222, 0, &data[0], 3);
|
|
+ u8 ver;
|
|
+
|
|
+ for (i = 0; i < 5; i++) {
|
|
+ ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ,
|
|
+ 0x1222, 0, &data[0], 3);
|
|
+ ver = data[0];
|
|
+ if (ver > 0 && ver < 3)
|
|
+ break;
|
|
+ msleep(100);
|
|
+ }
|
|
|
|
- it913x_config.chip_ver = data[0];
|
|
+ if (ver < 1 || ver > 2) {
|
|
+ info("Failed to identify chip version applying 1");
|
|
+ it913x_config.chip_ver = 0x1;
|
|
+ it913x_config.chip_type = 0x9135;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ it913x_config.chip_ver = ver;
|
|
it913x_config.chip_type = (u16)(data[2] << 8) + data[1];
|
|
|
|
info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver,
|
|
@@ -259,15 +275,16 @@ static u32 it913x_query(struct usb_devic
|
|
|
|
static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
|
|
{
|
|
+ struct it913x_state *st = adap->dev->priv;
|
|
struct usb_device *udev = adap->dev->udev;
|
|
int ret;
|
|
u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
|
|
|
|
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
|
|
- return -EAGAIN;
|
|
+ mutex_lock(&adap->dev->i2c_mutex);
|
|
+
|
|
deb_info(1, "PID_C (%02x)", onoff);
|
|
|
|
- ret = it913x_wr_reg(udev, pro, PID_EN, onoff);
|
|
+ ret = it913x_wr_reg(udev, pro, PID_EN, st->pid_filter_onoff);
|
|
|
|
mutex_unlock(&adap->dev->i2c_mutex);
|
|
return ret;
|
|
@@ -276,12 +293,13 @@ static int it913x_pid_filter_ctrl(struct
|
|
static int it913x_pid_filter(struct dvb_usb_adapter *adap,
|
|
int index, u16 pid, int onoff)
|
|
{
|
|
+ struct it913x_state *st = adap->dev->priv;
|
|
struct usb_device *udev = adap->dev->udev;
|
|
int ret;
|
|
u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
|
|
|
|
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
|
|
- return -EAGAIN;
|
|
+ mutex_lock(&adap->dev->i2c_mutex);
|
|
+
|
|
deb_info(1, "PID_F (%02x)", onoff);
|
|
|
|
ret = it913x_wr_reg(udev, pro, PID_LSB, (u8)(pid & 0xff));
|
|
@@ -292,6 +310,13 @@ static int it913x_pid_filter(struct dvb_
|
|
|
|
ret |= it913x_wr_reg(udev, pro, PID_INX, (u8)(index & 0x1f));
|
|
|
|
+ if (udev->speed == USB_SPEED_HIGH && pid == 0x2000) {
|
|
+ ret |= it913x_wr_reg(udev, pro, PID_EN, !onoff);
|
|
+ st->pid_filter_onoff = !onoff;
|
|
+ } else
|
|
+ st->pid_filter_onoff =
|
|
+ adap->fe_adap[adap->active_fe].pid_filtering;
|
|
+
|
|
mutex_unlock(&adap->dev->i2c_mutex);
|
|
return 0;
|
|
}
|
|
@@ -316,8 +341,8 @@ static int it913x_i2c_xfer(struct i2c_ad
|
|
int ret;
|
|
u32 reg;
|
|
u8 pro;
|
|
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
|
|
- return -EAGAIN;
|
|
+
|
|
+ mutex_lock(&d->i2c_mutex);
|
|
|
|
debug_data_snipet(1, "Message out", msg[0].buf);
|
|
deb_info(2, "num of messages %d address %02x", num, msg[0].addr);
|
|
@@ -358,8 +383,7 @@ static int it913x_rc_query(struct dvb_us
|
|
int ret;
|
|
u32 key;
|
|
/* Avoid conflict with frontends*/
|
|
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
|
|
- return -EAGAIN;
|
|
+ mutex_lock(&d->i2c_mutex);
|
|
|
|
ret = it913x_io(d->udev, READ_LONG, PRO_LINK, CMD_IR_GET,
|
|
0, 0, &ibuf[0], sizeof(ibuf));
|
|
@@ -388,19 +412,12 @@ static int ite_firmware_select(struct us
|
|
{
|
|
int sw;
|
|
/* auto switch */
|
|
- if (le16_to_cpu(udev->descriptor.idProduct) ==
|
|
- USB_PID_ITETECH_IT9135)
|
|
- sw = IT9135_V1_FW;
|
|
- else if (le16_to_cpu(udev->descriptor.idProduct) ==
|
|
- USB_PID_ITETECH_IT9135_9005)
|
|
+ if (le16_to_cpu(udev->descriptor.idVendor) == USB_VID_KWORLD_2)
|
|
+ sw = IT9137_FW;
|
|
+ else if (it913x_config.chip_ver == 1)
|
|
sw = IT9135_V1_FW;
|
|
- else if (le16_to_cpu(udev->descriptor.idProduct) ==
|
|
- USB_PID_ITETECH_IT9135_9006) {
|
|
+ else
|
|
sw = IT9135_V2_FW;
|
|
- if (it913x_config.tuner_id_0 == 0)
|
|
- it913x_config.tuner_id_0 = IT9135_60;
|
|
- } else
|
|
- sw = IT9137_FW;
|
|
|
|
/* force switch */
|
|
if (dvb_usb_it913x_firmware != IT9135_AUTO)
|
|
@@ -410,41 +427,103 @@ static int ite_firmware_select(struct us
|
|
case IT9135_V1_FW:
|
|
it913x_config.firmware_ver = 1;
|
|
it913x_config.adc_x2 = 1;
|
|
+ it913x_config.read_slevel = false;
|
|
props->firmware = fw_it9135_v1;
|
|
break;
|
|
case IT9135_V2_FW:
|
|
it913x_config.firmware_ver = 1;
|
|
it913x_config.adc_x2 = 1;
|
|
+ it913x_config.read_slevel = false;
|
|
props->firmware = fw_it9135_v2;
|
|
+ switch (it913x_config.tuner_id_0) {
|
|
+ case IT9135_61:
|
|
+ case IT9135_62:
|
|
+ break;
|
|
+ default:
|
|
+ info("Unknown tuner ID applying default 0x60");
|
|
+ case IT9135_60:
|
|
+ it913x_config.tuner_id_0 = IT9135_60;
|
|
+ }
|
|
break;
|
|
case IT9137_FW:
|
|
default:
|
|
it913x_config.firmware_ver = 0;
|
|
it913x_config.adc_x2 = 0;
|
|
+ it913x_config.read_slevel = true;
|
|
props->firmware = fw_it9137;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
+static void it913x_select_remote(struct usb_device *udev,
|
|
+ struct dvb_usb_device_properties *props)
|
|
+{
|
|
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
|
|
+ case USB_PID_ITETECH_IT9135_9005:
|
|
+ props->rc.core.rc_codes = RC_MAP_IT913X_V2;
|
|
+ return;
|
|
+ default:
|
|
+ props->rc.core.rc_codes = RC_MAP_IT913X_V1;
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
#define TS_MPEG_PKT_SIZE 188
|
|
#define EP_LOW 21
|
|
#define TS_BUFFER_SIZE_PID (EP_LOW*TS_MPEG_PKT_SIZE)
|
|
#define EP_HIGH 348
|
|
#define TS_BUFFER_SIZE_MAX (EP_HIGH*TS_MPEG_PKT_SIZE)
|
|
|
|
-static int it913x_identify_state(struct usb_device *udev,
|
|
- struct dvb_usb_device_properties *props,
|
|
- struct dvb_usb_device_description **desc,
|
|
- int *cold)
|
|
+static int it913x_select_config(struct usb_device *udev,
|
|
+ struct dvb_usb_device_properties *props)
|
|
{
|
|
- int ret = 0, firm_no;
|
|
- u8 reg, remote;
|
|
+ int ret = 0, reg;
|
|
+ bool proprietary_ir = false;
|
|
|
|
- firm_no = it913x_return_status(udev);
|
|
+ if (it913x_config.chip_ver == 0x02
|
|
+ && it913x_config.chip_type == 0x9135)
|
|
+ reg = it913x_read_reg(udev, 0x461d);
|
|
+ else
|
|
+ reg = it913x_read_reg(udev, 0x461b);
|
|
|
|
- /* checnk for dual mode */
|
|
- it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5);
|
|
+ if (reg < 0)
|
|
+ return reg;
|
|
+
|
|
+ if (reg == 0) {
|
|
+ it913x_config.dual_mode = 0;
|
|
+ it913x_config.tuner_id_0 = IT9135_38;
|
|
+ proprietary_ir = true;
|
|
+ } else {
|
|
+ /* TS mode */
|
|
+ reg = it913x_read_reg(udev, 0x49c5);
|
|
+ if (reg < 0)
|
|
+ return reg;
|
|
+ it913x_config.dual_mode = reg;
|
|
+
|
|
+ /* IR mode type */
|
|
+ reg = it913x_read_reg(udev, 0x49ac);
|
|
+ if (reg < 0)
|
|
+ return reg;
|
|
+ if (reg == 5) {
|
|
+ info("Remote propriety (raw) mode");
|
|
+ proprietary_ir = true;
|
|
+ } else if (reg == 1) {
|
|
+ info("Remote HID mode NOT SUPPORTED");
|
|
+ proprietary_ir = false;
|
|
+ props->rc.core.rc_codes = NULL;
|
|
+ } else
|
|
+ props->rc.core.rc_codes = NULL;
|
|
+
|
|
+ /* Tuner_id */
|
|
+ reg = it913x_read_reg(udev, 0x49d0);
|
|
+ if (reg < 0)
|
|
+ return reg;
|
|
+ it913x_config.tuner_id_0 = reg;
|
|
+ }
|
|
+
|
|
+ if (proprietary_ir)
|
|
+ it913x_select_remote(udev, props);
|
|
|
|
if (udev->speed != USB_SPEED_HIGH) {
|
|
props->adapter[0].fe[0].pid_filter_count = 5;
|
|
@@ -459,17 +538,6 @@ static int it913x_identify_state(struct
|
|
if(props->adapter[0].fe[0].pid_filter_count == 5)
|
|
props->adapter[0].fe[0].pid_filter_count = 31;
|
|
|
|
- /* TODO different remotes */
|
|
- remote = it913x_read_reg(udev, 0x49ac); /* Remote */
|
|
- if (remote == 0)
|
|
- props->rc.core.rc_codes = NULL;
|
|
-
|
|
- /* TODO at the moment tuner_id is always assigned to 0x38 */
|
|
- it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0);
|
|
-
|
|
- info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode
|
|
- , remote, it913x_config.tuner_id_0);
|
|
-
|
|
/* Select Stream Buffer Size and pid filter option*/
|
|
if (pid_filter) {
|
|
props->adapter[0].fe[0].stream.u.bulk.buffersize =
|
|
@@ -490,8 +558,29 @@ static int it913x_identify_state(struct
|
|
} else
|
|
props->num_adapters = 1;
|
|
|
|
+ info("Dual mode=%x Tuner Type=%x", it913x_config.dual_mode,
|
|
+ it913x_config.tuner_id_0);
|
|
+
|
|
ret = ite_firmware_select(udev, props);
|
|
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int it913x_identify_state(struct usb_device *udev,
|
|
+ struct dvb_usb_device_properties *props,
|
|
+ struct dvb_usb_device_description **desc,
|
|
+ int *cold)
|
|
+{
|
|
+ int ret = 0, firm_no;
|
|
+ u8 reg;
|
|
+
|
|
+ firm_no = it913x_return_status(udev);
|
|
+
|
|
+ /* Read and select config */
|
|
+ ret = it913x_select_config(udev, props);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
if (firm_no > 0) {
|
|
*cold = 0;
|
|
return 0;
|
|
@@ -538,18 +627,22 @@ static int it913x_identify_state(struct
|
|
|
|
static int it913x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
|
|
{
|
|
+ struct it913x_state *st = adap->dev->priv;
|
|
int ret = 0;
|
|
u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
|
|
|
|
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
|
|
- return -EAGAIN;
|
|
deb_info(1, "STM (%02x)", onoff);
|
|
|
|
- if (!onoff)
|
|
+ if (!onoff) {
|
|
+ mutex_lock(&adap->dev->i2c_mutex);
|
|
+
|
|
ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1);
|
|
|
|
+ mutex_unlock(&adap->dev->i2c_mutex);
|
|
+ st->pid_filter_onoff =
|
|
+ adap->fe_adap[adap->active_fe].pid_filtering;
|
|
|
|
- mutex_unlock(&adap->dev->i2c_mutex);
|
|
+ }
|
|
|
|
return ret;
|
|
}
|
|
@@ -582,30 +675,41 @@ static int it913x_download_firmware(stru
|
|
if ((packet_size > min_pkt) || (i == fw->size)) {
|
|
fw_data = (u8 *)(fw->data + pos);
|
|
pos += packet_size;
|
|
- if (packet_size > 0)
|
|
- ret |= it913x_io(udev, WRITE_DATA,
|
|
+ if (packet_size > 0) {
|
|
+ ret = it913x_io(udev, WRITE_DATA,
|
|
DEV_0, CMD_SCATTER_WRITE, 0,
|
|
0, fw_data, packet_size);
|
|
+ if (ret < 0)
|
|
+ break;
|
|
+ }
|
|
udelay(1000);
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
- ret |= it913x_io(udev, WRITE_CMD, DEV_0, CMD_BOOT, 0, 0, NULL, 0);
|
|
-
|
|
- msleep(100);
|
|
-
|
|
if (ret < 0)
|
|
- info("FRM Firmware Download Failed (%04x)" , ret);
|
|
+ info("FRM Firmware Download Failed (%d)" , ret);
|
|
else
|
|
info("FRM Firmware Download Completed - Resetting Device");
|
|
|
|
- ret |= it913x_return_status(udev);
|
|
+ msleep(30);
|
|
+
|
|
+ ret = it913x_io(udev, WRITE_CMD, DEV_0, CMD_BOOT, 0, 0, NULL, 0);
|
|
+ if (ret < 0)
|
|
+ info("FRM Device not responding to reboot");
|
|
+
|
|
+ ret = it913x_return_status(udev);
|
|
+ if (ret == 0) {
|
|
+ info("FRM Failed to reboot device");
|
|
+ return -ENODEV;
|
|
+ }
|
|
|
|
msleep(30);
|
|
|
|
- ret |= it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400);
|
|
+ ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400);
|
|
+
|
|
+ msleep(30);
|
|
|
|
/* Tuner function */
|
|
if (it913x_config.dual_mode)
|
|
@@ -789,7 +893,7 @@ static struct dvb_usb_device_properties
|
|
.rc_query = it913x_rc_query,
|
|
.rc_interval = IT913X_POLL,
|
|
.allowed_protos = RC_TYPE_NEC,
|
|
- .rc_codes = RC_MAP_MSI_DIGIVOX_III,
|
|
+ .rc_codes = RC_MAP_IT913X_V1,
|
|
},
|
|
.i2c_algo = &it913x_i2c_algo,
|
|
.num_device_descs = 5,
|
|
@@ -823,5 +927,5 @@ module_usb_driver(it913x_driver);
|
|
|
|
MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
|
|
MODULE_DESCRIPTION("it913x USB 2 Driver");
|
|
-MODULE_VERSION("1.22");
|
|
+MODULE_VERSION("1.28");
|
|
MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/base/driver.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/base/driver.c
|
|
+++ linux-3.3.x86_64/drivers/base/driver.c
|
|
@@ -234,7 +234,6 @@ int driver_register(struct device_driver
|
|
|
|
other = driver_find(drv->name, drv->bus);
|
|
if (other) {
|
|
- put_driver(other);
|
|
printk(KERN_ERR "Error: Driver '%s' is already registered, "
|
|
"aborting...\n", drv->name);
|
|
return -EBUSY;
|
|
@@ -275,7 +274,9 @@ EXPORT_SYMBOL_GPL(driver_unregister);
|
|
* Call kset_find_obj() to iterate over list of drivers on
|
|
* a bus to find driver by name. Return driver if found.
|
|
*
|
|
- * Note that kset_find_obj increments driver's reference count.
|
|
+ * This routine provides no locking to prevent the driver it returns
|
|
+ * from being unregistered or unloaded while the caller is using it.
|
|
+ * The caller is responsible for preventing this.
|
|
*/
|
|
struct device_driver *driver_find(const char *name, struct bus_type *bus)
|
|
{
|
|
@@ -283,6 +284,8 @@ struct device_driver *driver_find(const
|
|
struct driver_private *priv;
|
|
|
|
if (k) {
|
|
+ /* Drop reference added by kset_find_obj() */
|
|
+ kobject_put(k);
|
|
priv = to_driver(k);
|
|
return priv->driver;
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/input/gameport/gameport.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/input/gameport/gameport.c
|
|
+++ linux-3.3.x86_64/drivers/input/gameport/gameport.c
|
|
@@ -449,7 +449,6 @@ static ssize_t gameport_rebind_driver(st
|
|
} else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {
|
|
gameport_disconnect_port(gameport);
|
|
error = gameport_bind_driver(gameport, to_gameport_driver(drv));
|
|
- put_driver(drv);
|
|
} else {
|
|
error = -EINVAL;
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/input/serio/serio.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/input/serio/serio.c
|
|
+++ linux-3.3.x86_64/drivers/input/serio/serio.c
|
|
@@ -441,7 +441,6 @@ static ssize_t serio_rebind_driver(struc
|
|
} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
|
|
serio_disconnect_port(serio);
|
|
error = serio_bind_driver(serio, to_serio_driver(drv));
|
|
- put_driver(drv);
|
|
serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
|
|
} else {
|
|
error = -EINVAL;
|
|
Index: linux-3.3.x86_64/drivers/media/video/cx18/cx18-alsa-main.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cx18/cx18-alsa-main.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cx18/cx18-alsa-main.c
|
|
@@ -285,7 +285,6 @@ 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);
|
|
- put_driver(drv);
|
|
|
|
cx18_ext_init = NULL;
|
|
printk(KERN_INFO "cx18-alsa: module unload complete\n");
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/ivtvfb.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/ivtvfb.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/ivtvfb.c
|
|
@@ -1293,7 +1293,6 @@ static int __init ivtvfb_init(void)
|
|
|
|
drv = driver_find("ivtv", &pci_bus_type);
|
|
err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init);
|
|
- put_driver(drv);
|
|
if (!registered) {
|
|
printk(KERN_ERR "ivtvfb: no cards found\n");
|
|
return -ENODEV;
|
|
@@ -1310,7 +1309,6 @@ static void ivtvfb_cleanup(void)
|
|
|
|
drv = driver_find("ivtv", &pci_bus_type);
|
|
err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
|
|
- put_driver(drv);
|
|
}
|
|
|
|
module_init(ivtvfb_init);
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-fimc/fimc-mdevice.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-fimc/fimc-mdevice.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-fimc/fimc-mdevice.c
|
|
@@ -344,16 +344,13 @@ static int fimc_md_register_platform_ent
|
|
return -ENODEV;
|
|
ret = driver_for_each_device(driver, NULL, fmd,
|
|
fimc_register_callback);
|
|
- put_driver(driver);
|
|
if (ret)
|
|
return ret;
|
|
|
|
driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
|
|
- if (driver) {
|
|
+ if (driver)
|
|
ret = driver_for_each_device(driver, NULL, fmd,
|
|
csis_register_callback);
|
|
- put_driver(driver);
|
|
- }
|
|
return ret;
|
|
}
|
|
|
|
@@ -753,7 +750,7 @@ static int __devinit fimc_md_probe(struc
|
|
struct fimc_md *fmd;
|
|
int ret;
|
|
|
|
- fmd = kzalloc(sizeof(struct fimc_md), GFP_KERNEL);
|
|
+ fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL);
|
|
if (!fmd)
|
|
return -ENOMEM;
|
|
|
|
@@ -774,7 +771,7 @@ static int __devinit fimc_md_probe(struc
|
|
ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev);
|
|
if (ret < 0) {
|
|
v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
|
|
- goto err1;
|
|
+ return ret;
|
|
}
|
|
ret = media_device_register(&fmd->media_dev);
|
|
if (ret < 0) {
|
|
@@ -816,8 +813,6 @@ err3:
|
|
fimc_md_unregister_entities(fmd);
|
|
err2:
|
|
v4l2_device_unregister(&fmd->v4l2_dev);
|
|
-err1:
|
|
- kfree(fmd);
|
|
return ret;
|
|
}
|
|
|
|
@@ -831,7 +826,6 @@ static int __devexit fimc_md_remove(stru
|
|
fimc_md_unregister_entities(fmd);
|
|
media_device_unregister(&fmd->media_dev);
|
|
fimc_md_put_clocks(fmd);
|
|
- kfree(fmd);
|
|
return 0;
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-tv/mixer_video.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-tv/mixer_video.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-tv/mixer_video.c
|
|
@@ -58,7 +58,6 @@ static struct v4l2_subdev *find_and_regi
|
|
}
|
|
|
|
done:
|
|
- put_driver(drv);
|
|
return sd;
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/s390/net/smsgiucv_app.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/s390/net/smsgiucv_app.c
|
|
+++ linux-3.3.x86_64/drivers/s390/net/smsgiucv_app.c
|
|
@@ -168,7 +168,7 @@ static int __init smsgiucv_app_init(void
|
|
rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
|
|
if (rc) {
|
|
kfree(smsg_app_dev);
|
|
- goto fail_put_driver;
|
|
+ goto fail;
|
|
}
|
|
smsg_app_dev->bus = &iucv_bus;
|
|
smsg_app_dev->parent = iucv_root;
|
|
@@ -177,7 +177,7 @@ static int __init smsgiucv_app_init(void
|
|
rc = device_register(smsg_app_dev);
|
|
if (rc) {
|
|
put_device(smsg_app_dev);
|
|
- goto fail_put_driver;
|
|
+ goto fail;
|
|
}
|
|
|
|
/* convert sender to uppercase characters */
|
|
@@ -191,12 +191,11 @@ static int __init smsgiucv_app_init(void
|
|
rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
|
|
if (rc) {
|
|
device_unregister(smsg_app_dev);
|
|
- goto fail_put_driver;
|
|
+ goto fail;
|
|
}
|
|
|
|
rc = 0;
|
|
-fail_put_driver:
|
|
- put_driver(smsgiucv_drv);
|
|
+fail:
|
|
return rc;
|
|
}
|
|
module_init(smsgiucv_app_init);
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/anysee.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/anysee.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/anysee.c
|
|
@@ -58,7 +58,7 @@ static int anysee_ctrl_msg(struct dvb_us
|
|
u8 *rbuf, u8 rlen)
|
|
{
|
|
struct anysee_state *state = d->priv;
|
|
- int act_len, ret;
|
|
+ int act_len, ret, i;
|
|
u8 buf[64];
|
|
|
|
memcpy(&buf[0], sbuf, slen);
|
|
@@ -73,26 +73,52 @@ static int anysee_ctrl_msg(struct dvb_us
|
|
/* We need receive one message more after dvb_usb_generic_rw due
|
|
to weird transaction flow, which is 1 x send + 2 x receive. */
|
|
ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0);
|
|
- if (!ret) {
|
|
+ if (ret)
|
|
+ goto error_unlock;
|
|
+
|
|
+ /* TODO FIXME: dvb_usb_generic_rw() fails rarely with error code -32
|
|
+ * (EPIPE, Broken pipe). Function supports currently msleep() as a
|
|
+ * parameter but I would not like to use it, since according to
|
|
+ * Documentation/timers/timers-howto.txt it should not be used such
|
|
+ * short, under < 20ms, sleeps. Repeating failed message would be
|
|
+ * better choice as not to add unwanted delays...
|
|
+ * Fixing that correctly is one of those or both;
|
|
+ * 1) use repeat if possible
|
|
+ * 2) add suitable delay
|
|
+ */
|
|
+
|
|
+ /* get answer, retry few times if error returned */
|
|
+ for (i = 0; i < 3; i++) {
|
|
/* receive 2nd answer */
|
|
ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev,
|
|
d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf),
|
|
&act_len, 2000);
|
|
- if (ret)
|
|
- err("%s: recv bulk message failed: %d", __func__, ret);
|
|
- else {
|
|
+
|
|
+ if (ret) {
|
|
+ deb_info("%s: recv bulk message failed: %d",
|
|
+ __func__, ret);
|
|
+ } else {
|
|
deb_xfer("<<< ");
|
|
debug_dump(buf, rlen, deb_xfer);
|
|
|
|
if (buf[63] != 0x4f)
|
|
deb_info("%s: cmd failed\n", __func__);
|
|
+
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
+ if (ret) {
|
|
+ /* all retries failed, it is fatal */
|
|
+ err("%s: recv bulk message failed: %d", __func__, ret);
|
|
+ goto error_unlock;
|
|
+ }
|
|
+
|
|
/* read request, copy returned data to return buf */
|
|
- if (!ret && rbuf && rlen)
|
|
+ if (rbuf && rlen)
|
|
memcpy(rbuf, buf, rlen);
|
|
|
|
+error_unlock:
|
|
mutex_unlock(&anysee_usb_mutex);
|
|
|
|
return ret;
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-tv/hdmi_drv.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-tv/hdmi_drv.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-tv/hdmi_drv.c
|
|
@@ -30,6 +30,7 @@
|
|
#include <linux/clk.h>
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
+#include <media/s5p_hdmi.h>
|
|
#include <media/v4l2-common.h>
|
|
#include <media/v4l2-dev.h>
|
|
#include <media/v4l2-device.h>
|
|
@@ -66,6 +67,8 @@ struct hdmi_device {
|
|
struct v4l2_device v4l2_dev;
|
|
/** subdev of HDMIPHY interface */
|
|
struct v4l2_subdev *phy_sd;
|
|
+ /** subdev of MHL interface */
|
|
+ struct v4l2_subdev *mhl_sd;
|
|
/** configuration of current graphic mode */
|
|
const struct hdmi_preset_conf *cur_conf;
|
|
/** current preset */
|
|
@@ -74,10 +77,6 @@ struct hdmi_device {
|
|
struct hdmi_resources res;
|
|
};
|
|
|
|
-struct hdmi_driver_data {
|
|
- int hdmiphy_bus;
|
|
-};
|
|
-
|
|
struct hdmi_tg_regs {
|
|
u8 cmd;
|
|
u8 h_fsz_l;
|
|
@@ -129,23 +128,11 @@ struct hdmi_preset_conf {
|
|
struct v4l2_mbus_framefmt mbus_fmt;
|
|
};
|
|
|
|
-/* I2C module and id for HDMIPHY */
|
|
-static struct i2c_board_info hdmiphy_info = {
|
|
- I2C_BOARD_INFO("hdmiphy", 0x38),
|
|
-};
|
|
-
|
|
-static struct hdmi_driver_data hdmi_driver_data[] = {
|
|
- { .hdmiphy_bus = 3 },
|
|
- { .hdmiphy_bus = 8 },
|
|
-};
|
|
-
|
|
static struct platform_device_id hdmi_driver_types[] = {
|
|
{
|
|
.name = "s5pv210-hdmi",
|
|
- .driver_data = (unsigned long)&hdmi_driver_data[0],
|
|
}, {
|
|
.name = "exynos4-hdmi",
|
|
- .driver_data = (unsigned long)&hdmi_driver_data[1],
|
|
}, {
|
|
/* end node */
|
|
}
|
|
@@ -587,7 +574,15 @@ static int hdmi_streamon(struct hdmi_dev
|
|
if (tries == 0) {
|
|
dev_err(dev, "hdmiphy's pll could not reach steady state.\n");
|
|
v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
|
|
- hdmi_dumpregs(hdev, "s_stream");
|
|
+ hdmi_dumpregs(hdev, "hdmiphy - s_stream");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ /* starting MHL */
|
|
+ ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1);
|
|
+ if (hdev->mhl_sd && ret) {
|
|
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
|
|
+ hdmi_dumpregs(hdev, "mhl - s_stream");
|
|
return -EIO;
|
|
}
|
|
|
|
@@ -618,6 +613,7 @@ static int hdmi_streamoff(struct hdmi_de
|
|
clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
|
|
clk_enable(res->sclk_hdmi);
|
|
|
|
+ v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0);
|
|
v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
|
|
|
|
hdmi_dumpregs(hdev, "streamoff");
|
|
@@ -739,6 +735,7 @@ static int hdmi_runtime_suspend(struct d
|
|
struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
+ v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0);
|
|
hdmi_resource_poweroff(&hdev->res);
|
|
return 0;
|
|
}
|
|
@@ -757,6 +754,11 @@ static int hdmi_runtime_resume(struct de
|
|
if (ret)
|
|
goto fail;
|
|
|
|
+ /* starting MHL */
|
|
+ ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1);
|
|
+ if (hdev->mhl_sd && ret)
|
|
+ goto fail;
|
|
+
|
|
dev_dbg(dev, "poweron succeed\n");
|
|
|
|
return 0;
|
|
@@ -867,15 +869,21 @@ static int __devinit hdmi_probe(struct p
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct resource *res;
|
|
- struct i2c_adapter *phy_adapter;
|
|
+ struct i2c_adapter *adapter;
|
|
struct v4l2_subdev *sd;
|
|
struct hdmi_device *hdmi_dev = NULL;
|
|
- struct hdmi_driver_data *drv_data;
|
|
+ struct s5p_hdmi_platform_data *pdata = dev->platform_data;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "probe start\n");
|
|
|
|
- hdmi_dev = kzalloc(sizeof(*hdmi_dev), GFP_KERNEL);
|
|
+ if (!pdata) {
|
|
+ dev_err(dev, "platform data is missing\n");
|
|
+ ret = -ENODEV;
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL);
|
|
if (!hdmi_dev) {
|
|
dev_err(dev, "out of memory\n");
|
|
ret = -ENOMEM;
|
|
@@ -886,7 +894,7 @@ static int __devinit hdmi_probe(struct p
|
|
|
|
ret = hdmi_resources_init(hdmi_dev);
|
|
if (ret)
|
|
- goto fail_hdev;
|
|
+ goto fail;
|
|
|
|
/* mapping HDMI registers */
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
@@ -896,24 +904,26 @@ static int __devinit hdmi_probe(struct p
|
|
goto fail_init;
|
|
}
|
|
|
|
- hdmi_dev->regs = ioremap(res->start, resource_size(res));
|
|
+ hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start,
|
|
+ resource_size(res));
|
|
if (hdmi_dev->regs == NULL) {
|
|
dev_err(dev, "register mapping failed.\n");
|
|
ret = -ENXIO;
|
|
- goto fail_hdev;
|
|
+ goto fail_init;
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
if (res == NULL) {
|
|
dev_err(dev, "get interrupt resource failed.\n");
|
|
ret = -ENXIO;
|
|
- goto fail_regs;
|
|
+ goto fail_init;
|
|
}
|
|
|
|
- ret = request_irq(res->start, hdmi_irq_handler, 0, "hdmi", hdmi_dev);
|
|
+ ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0,
|
|
+ "hdmi", hdmi_dev);
|
|
if (ret) {
|
|
dev_err(dev, "request interrupt failed.\n");
|
|
- goto fail_regs;
|
|
+ goto fail_init;
|
|
}
|
|
hdmi_dev->irq = res->start;
|
|
|
|
@@ -924,28 +934,54 @@ static int __devinit hdmi_probe(struct p
|
|
ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev);
|
|
if (ret) {
|
|
dev_err(dev, "could not register v4l2 device.\n");
|
|
- goto fail_irq;
|
|
+ goto fail_init;
|
|
}
|
|
|
|
- drv_data = (struct hdmi_driver_data *)
|
|
- platform_get_device_id(pdev)->driver_data;
|
|
- phy_adapter = i2c_get_adapter(drv_data->hdmiphy_bus);
|
|
- if (phy_adapter == NULL) {
|
|
- dev_err(dev, "adapter request failed\n");
|
|
+ /* testing if hdmiphy info is present */
|
|
+ if (!pdata->hdmiphy_info) {
|
|
+ dev_err(dev, "hdmiphy info is missing in platform data\n");
|
|
+ ret = -ENXIO;
|
|
+ goto fail_vdev;
|
|
+ }
|
|
+
|
|
+ adapter = i2c_get_adapter(pdata->hdmiphy_bus);
|
|
+ if (adapter == NULL) {
|
|
+ dev_err(dev, "hdmiphy adapter request failed\n");
|
|
ret = -ENXIO;
|
|
goto fail_vdev;
|
|
}
|
|
|
|
hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev,
|
|
- phy_adapter, &hdmiphy_info, NULL);
|
|
+ adapter, pdata->hdmiphy_info, NULL);
|
|
/* on failure or not adapter is no longer useful */
|
|
- i2c_put_adapter(phy_adapter);
|
|
+ i2c_put_adapter(adapter);
|
|
if (hdmi_dev->phy_sd == NULL) {
|
|
dev_err(dev, "missing subdev for hdmiphy\n");
|
|
ret = -ENODEV;
|
|
goto fail_vdev;
|
|
}
|
|
|
|
+ /* initialization of MHL interface if present */
|
|
+ if (pdata->mhl_info) {
|
|
+ adapter = i2c_get_adapter(pdata->mhl_bus);
|
|
+ if (adapter == NULL) {
|
|
+ dev_err(dev, "MHL adapter request failed\n");
|
|
+ ret = -ENXIO;
|
|
+ goto fail_vdev;
|
|
+ }
|
|
+
|
|
+ hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board(
|
|
+ &hdmi_dev->v4l2_dev, adapter,
|
|
+ pdata->mhl_info, NULL);
|
|
+ /* on failure or not adapter is no longer useful */
|
|
+ i2c_put_adapter(adapter);
|
|
+ if (hdmi_dev->mhl_sd == NULL) {
|
|
+ dev_err(dev, "missing subdev for MHL\n");
|
|
+ ret = -ENODEV;
|
|
+ goto fail_vdev;
|
|
+ }
|
|
+ }
|
|
+
|
|
clk_enable(hdmi_dev->res.hdmi);
|
|
|
|
pm_runtime_enable(dev);
|
|
@@ -969,18 +1005,9 @@ static int __devinit hdmi_probe(struct p
|
|
fail_vdev:
|
|
v4l2_device_unregister(&hdmi_dev->v4l2_dev);
|
|
|
|
-fail_irq:
|
|
- free_irq(hdmi_dev->irq, hdmi_dev);
|
|
-
|
|
-fail_regs:
|
|
- iounmap(hdmi_dev->regs);
|
|
-
|
|
fail_init:
|
|
hdmi_resources_cleanup(hdmi_dev);
|
|
|
|
-fail_hdev:
|
|
- kfree(hdmi_dev);
|
|
-
|
|
fail:
|
|
dev_err(dev, "probe failed\n");
|
|
return ret;
|
|
@@ -996,8 +1023,6 @@ static int __devexit hdmi_remove(struct
|
|
clk_disable(hdmi_dev->res.hdmi);
|
|
v4l2_device_unregister(&hdmi_dev->v4l2_dev);
|
|
disable_irq(hdmi_dev->irq);
|
|
- free_irq(hdmi_dev->irq, hdmi_dev);
|
|
- iounmap(hdmi_dev->regs);
|
|
hdmi_resources_cleanup(hdmi_dev);
|
|
kfree(hdmi_dev);
|
|
dev_info(dev, "remove sucessful\n");
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/lmedm04.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/lmedm04.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/lmedm04.c
|
|
@@ -77,6 +77,7 @@
|
|
#include "stv0299.h"
|
|
#include "dvb-pll.h"
|
|
#include "z0194a.h"
|
|
+#include "m88rs2000.h"
|
|
|
|
|
|
|
|
@@ -104,7 +105,7 @@ MODULE_PARM_DESC(firmware, "set default
|
|
|
|
static int pid_filter;
|
|
module_param_named(pid, pid_filter, int, 0644);
|
|
-MODULE_PARM_DESC(pid, "set default 0=on 1=off");
|
|
+MODULE_PARM_DESC(pid, "set default 0=default 1=off 2=on");
|
|
|
|
|
|
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
|
@@ -113,6 +114,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr
|
|
#define TUNER_LG 0x1
|
|
#define TUNER_S7395 0x2
|
|
#define TUNER_S0194 0x3
|
|
+#define TUNER_RS2000 0x4
|
|
|
|
struct lme2510_state {
|
|
u8 id;
|
|
@@ -121,6 +123,8 @@ struct lme2510_state {
|
|
u8 signal_level;
|
|
u8 signal_sn;
|
|
u8 time_key;
|
|
+ u8 last_key;
|
|
+ u8 key_timeout;
|
|
u8 i2c_talk_onoff;
|
|
u8 i2c_gate;
|
|
u8 i2c_tuner_gate_w;
|
|
@@ -128,6 +132,7 @@ struct lme2510_state {
|
|
u8 i2c_tuner_addr;
|
|
u8 stream_on;
|
|
u8 pid_size;
|
|
+ u8 pid_off;
|
|
void *buffer;
|
|
struct urb *lme_urb;
|
|
void *usb_buffer;
|
|
@@ -178,14 +183,8 @@ static int lme2510_usb_talk(struct dvb_u
|
|
/* the read/write capped at 64 */
|
|
memcpy(buff, wbuf, (wlen < 64) ? wlen : 64);
|
|
|
|
- ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01));
|
|
-
|
|
ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01);
|
|
|
|
- msleep(10);
|
|
-
|
|
- ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01));
|
|
-
|
|
ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ?
|
|
rlen : 64 , 0x01);
|
|
|
|
@@ -199,9 +198,14 @@ static int lme2510_usb_talk(struct dvb_u
|
|
|
|
static int lme2510_stream_restart(struct dvb_usb_device *d)
|
|
{
|
|
- static u8 stream_on[] = LME_ST_ON_W;
|
|
+ struct lme2510_state *st = d->priv;
|
|
+ u8 all_pids[] = LME_ALL_PIDS;
|
|
+ u8 stream_on[] = LME_ST_ON_W;
|
|
int ret;
|
|
- u8 rbuff[10];
|
|
+ u8 rbuff[1];
|
|
+ if (st->pid_off)
|
|
+ ret = lme2510_usb_talk(d, all_pids, sizeof(all_pids),
|
|
+ rbuff, sizeof(rbuff));
|
|
/*Restart Stream Command*/
|
|
ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on),
|
|
rbuff, sizeof(rbuff));
|
|
@@ -308,6 +312,14 @@ static void lme2510_int_response(struct
|
|
((ibuf[2] & 0x01) << 0x03);
|
|
}
|
|
break;
|
|
+ case TUNER_RS2000:
|
|
+ if (ibuf[2] > 0)
|
|
+ st->signal_lock = 0xff;
|
|
+ else
|
|
+ st->signal_lock = 0xf0;
|
|
+ st->signal_level = ibuf[4];
|
|
+ st->signal_sn = ibuf[5];
|
|
+ st->time_key = ibuf[7];
|
|
default:
|
|
break;
|
|
}
|
|
@@ -359,19 +371,20 @@ static int lme2510_int_read(struct dvb_u
|
|
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_CLEAR_PID;
|
|
+ static u8 clear_pid_reg[] = LME_ALL_PIDS;
|
|
static u8 rbuf[1];
|
|
int ret;
|
|
|
|
deb_info(1, "PID Clearing Filter");
|
|
|
|
- ret = mutex_lock_interruptible(&adap->dev->i2c_mutex);
|
|
- if (ret < 0)
|
|
- return -EAGAIN;
|
|
+ mutex_lock(&adap->dev->i2c_mutex);
|
|
|
|
- if (!onoff)
|
|
+ if (!onoff) {
|
|
ret |= lme2510_usb_talk(adap->dev, clear_pid_reg,
|
|
sizeof(clear_pid_reg), rbuf, sizeof(rbuf));
|
|
+ st->pid_off = true;
|
|
+ } else
|
|
+ st->pid_off = false;
|
|
|
|
st->pid_size = 0;
|
|
|
|
@@ -389,11 +402,9 @@ static int lme2510_pid_filter(struct dvb
|
|
pid, index, onoff);
|
|
|
|
if (onoff) {
|
|
- ret = mutex_lock_interruptible(&adap->dev->i2c_mutex);
|
|
- if (ret < 0)
|
|
- return -EAGAIN;
|
|
- ret |= lme2510_enable_pid(adap->dev, index, pid);
|
|
- mutex_unlock(&adap->dev->i2c_mutex);
|
|
+ mutex_lock(&adap->dev->i2c_mutex);
|
|
+ ret |= lme2510_enable_pid(adap->dev, index, pid);
|
|
+ mutex_unlock(&adap->dev->i2c_mutex);
|
|
}
|
|
|
|
|
|
@@ -425,9 +436,6 @@ static int lme2510_msg(struct dvb_usb_de
|
|
int ret = 0;
|
|
struct lme2510_state *st = d->priv;
|
|
|
|
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
|
|
- return -EAGAIN;
|
|
-
|
|
if (st->i2c_talk_onoff == 1) {
|
|
|
|
ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
|
|
@@ -456,8 +464,6 @@ static int lme2510_msg(struct dvb_usb_de
|
|
st->i2c_talk_onoff = 0;
|
|
}
|
|
}
|
|
- if ((wbuf[3] != 0x6) & (wbuf[3] != 0x5))
|
|
- msleep(5);
|
|
}
|
|
break;
|
|
case TUNER_S0194:
|
|
@@ -472,10 +478,12 @@ static int lme2510_msg(struct dvb_usb_de
|
|
}
|
|
}
|
|
break;
|
|
+ case TUNER_RS2000:
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
+ /* TODO rewrite this section */
|
|
switch (st->tuner_config) {
|
|
case TUNER_LG:
|
|
switch (wbuf[3]) {
|
|
@@ -559,6 +567,24 @@ static int lme2510_msg(struct dvb_usb_de
|
|
break;
|
|
}
|
|
break;
|
|
+ case TUNER_RS2000:
|
|
+ switch (wbuf[3]) {
|
|
+ case 0x8c:
|
|
+ rbuf[0] = 0x55;
|
|
+ rbuf[1] = 0xff;
|
|
+ if (st->last_key == st->time_key) {
|
|
+ st->key_timeout++;
|
|
+ if (st->key_timeout > 5)
|
|
+ rbuf[1] = 0;
|
|
+ } else
|
|
+ st->key_timeout = 0;
|
|
+ st->last_key = st->time_key;
|
|
+ break;
|
|
+ default:
|
|
+ lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
|
|
+ st->i2c_talk_onoff = 1;
|
|
+ break;
|
|
+ }
|
|
default:
|
|
break;
|
|
}
|
|
@@ -568,8 +594,6 @@ static int lme2510_msg(struct dvb_usb_de
|
|
|
|
}
|
|
|
|
- mutex_unlock(&d->i2c_mutex);
|
|
-
|
|
return ret;
|
|
}
|
|
|
|
@@ -584,6 +608,8 @@ static int lme2510_i2c_xfer(struct i2c_a
|
|
u16 len;
|
|
u8 gate = st->i2c_gate;
|
|
|
|
+ mutex_lock(&d->i2c_mutex);
|
|
+
|
|
if (gate == 0)
|
|
gate = 5;
|
|
|
|
@@ -622,6 +648,7 @@ static int lme2510_i2c_xfer(struct i2c_a
|
|
|
|
if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) {
|
|
deb_info(1, "i2c transfer failed.");
|
|
+ mutex_unlock(&d->i2c_mutex);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
@@ -634,6 +661,8 @@ static int lme2510_i2c_xfer(struct i2c_a
|
|
}
|
|
}
|
|
}
|
|
+
|
|
+ mutex_unlock(&d->i2c_mutex);
|
|
return i;
|
|
}
|
|
|
|
@@ -653,7 +682,7 @@ static int lme2510_identify_state(struct
|
|
struct dvb_usb_device_description **desc,
|
|
int *cold)
|
|
{
|
|
- if (pid_filter > 0)
|
|
+ if (pid_filter != 2)
|
|
props->adapter[0].fe[0].caps &=
|
|
~DVB_USB_ADAP_NEED_PID_FILTERING;
|
|
*cold = 0;
|
|
@@ -663,7 +692,7 @@ static int lme2510_identify_state(struct
|
|
static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
|
|
{
|
|
struct lme2510_state *st = adap->dev->priv;
|
|
- static u8 clear_reg_3[] = LME_CLEAR_PID;
|
|
+ static u8 clear_reg_3[] = LME_ALL_PIDS;
|
|
static u8 rbuf[1];
|
|
int ret = 0, rlen = sizeof(rbuf);
|
|
|
|
@@ -675,8 +704,7 @@ static int lme2510_streaming_ctrl(struct
|
|
else {
|
|
deb_info(1, "STM Steam Off");
|
|
/* mutex is here only to avoid collision with I2C */
|
|
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
|
|
- return -EAGAIN;
|
|
+ mutex_lock(&adap->dev->i2c_mutex);
|
|
|
|
ret = lme2510_usb_talk(adap->dev, clear_reg_3,
|
|
sizeof(clear_reg_3), rbuf, rlen);
|
|
@@ -781,16 +809,18 @@ static int lme_firmware_switch(struct us
|
|
const char fw_c_s7395[] = "dvb-usb-lme2510c-s7395.fw";
|
|
const char fw_c_lg[] = "dvb-usb-lme2510c-lg.fw";
|
|
const char fw_c_s0194[] = "dvb-usb-lme2510c-s0194.fw";
|
|
+ const char fw_c_rs2000[] = "dvb-usb-lme2510c-rs2000.fw";
|
|
const char fw_lg[] = "dvb-usb-lme2510-lg.fw";
|
|
const char fw_s0194[] = "dvb-usb-lme2510-s0194.fw";
|
|
const char *fw_lme;
|
|
- int ret, cold_fw;
|
|
+ int ret = 0, cold_fw;
|
|
|
|
cold = (cold > 0) ? (cold & 1) : 0;
|
|
|
|
cold_fw = !cold;
|
|
|
|
- if (le16_to_cpu(udev->descriptor.idProduct) == 0x1122) {
|
|
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
|
|
+ case 0x1122:
|
|
switch (dvb_usb_lme2510_firmware) {
|
|
default:
|
|
dvb_usb_lme2510_firmware = TUNER_S0194;
|
|
@@ -813,7 +843,8 @@ static int lme_firmware_switch(struct us
|
|
cold_fw = 0;
|
|
break;
|
|
}
|
|
- } else {
|
|
+ break;
|
|
+ case 0x1120:
|
|
switch (dvb_usb_lme2510_firmware) {
|
|
default:
|
|
dvb_usb_lme2510_firmware = TUNER_S7395;
|
|
@@ -842,8 +873,17 @@ static int lme_firmware_switch(struct us
|
|
cold_fw = 0;
|
|
break;
|
|
}
|
|
+ break;
|
|
+ case 0x22f0:
|
|
+ fw_lme = fw_c_rs2000;
|
|
+ ret = request_firmware(&fw, fw_lme, &udev->dev);
|
|
+ dvb_usb_lme2510_firmware = TUNER_RS2000;
|
|
+ break;
|
|
+ default:
|
|
+ fw_lme = fw_c_s7395;
|
|
}
|
|
|
|
+
|
|
if (cold_fw) {
|
|
info("FRM Loading %s file", fw_lme);
|
|
ret = lme2510_download_firmware(udev, fw);
|
|
@@ -906,6 +946,29 @@ static struct stv0299_config sharp_z0194
|
|
.set_symbol_rate = sharp_z0194a_set_symbol_rate,
|
|
};
|
|
|
|
+static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe,
|
|
+ int caller)
|
|
+{
|
|
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
|
|
+ struct dvb_usb_device *d = adap->dev;
|
|
+ struct lme2510_state *st = d->priv;
|
|
+
|
|
+ mutex_lock(&d->i2c_mutex);
|
|
+ if ((st->i2c_talk_onoff == 1) && (st->stream_on & 1)) {
|
|
+ st->i2c_talk_onoff = 0;
|
|
+ lme2510_stream_restart(d);
|
|
+ }
|
|
+ mutex_unlock(&d->i2c_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct m88rs2000_config m88rs2000_config = {
|
|
+ .demod_addr = 0xd0,
|
|
+ .tuner_addr = 0xc0,
|
|
+ .set_ts_params = dm04_rs2000_set_ts_param,
|
|
+};
|
|
+
|
|
static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
|
|
fe_sec_voltage_t voltage)
|
|
{
|
|
@@ -915,8 +978,7 @@ static int dm04_lme2510_set_voltage(stru
|
|
static u8 rbuf[1];
|
|
int ret = 0, len = 3, rlen = 1;
|
|
|
|
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
|
|
- return -EAGAIN;
|
|
+ mutex_lock(&adap->dev->i2c_mutex);
|
|
|
|
switch (voltage) {
|
|
case SEC_VOLTAGE_18:
|
|
@@ -937,12 +999,31 @@ static int dm04_lme2510_set_voltage(stru
|
|
return (ret < 0) ? -ENODEV : 0;
|
|
}
|
|
|
|
+static int dm04_rs2000_read_signal_strength(struct dvb_frontend *fe,
|
|
+ u16 *strength)
|
|
+{
|
|
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
|
|
+ struct lme2510_state *st = adap->dev->priv;
|
|
+
|
|
+ *strength = (u16)((u32)st->signal_level * 0xffff / 0x7f);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dm04_rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
|
|
+{
|
|
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
|
|
+ struct lme2510_state *st = adap->dev->priv;
|
|
+
|
|
+ *snr = (u16)((u32)st->signal_sn * 0xffff / 0xff);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int lme_name(struct dvb_usb_adapter *adap)
|
|
{
|
|
struct lme2510_state *st = adap->dev->priv;
|
|
const char *desc = adap->dev->desc->name;
|
|
char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395",
|
|
- " SHARP:BS2F7HZ0194"};
|
|
+ " SHARP:BS2F7HZ0194", " RS2000"};
|
|
char *name = adap->fe_adap[0].fe->ops.info.name;
|
|
|
|
strlcpy(name, desc, 128);
|
|
@@ -958,60 +1039,82 @@ static int dm04_lme2510_frontend_attach(
|
|
int ret = 0;
|
|
|
|
st->i2c_talk_onoff = 1;
|
|
+ switch (le16_to_cpu(adap->dev->udev->descriptor.idProduct)) {
|
|
+ case 0x1122:
|
|
+ case 0x1120:
|
|
+ st->i2c_gate = 4;
|
|
+ adap->fe_adap[0].fe = dvb_attach(tda10086_attach,
|
|
+ &tda10086_config, &adap->dev->i2c_adap);
|
|
+ if (adap->fe_adap[0].fe) {
|
|
+ info("TUN Found Frontend TDA10086");
|
|
+ st->i2c_tuner_gate_w = 4;
|
|
+ st->i2c_tuner_gate_r = 4;
|
|
+ st->i2c_tuner_addr = 0xc0;
|
|
+ st->tuner_config = TUNER_LG;
|
|
+ if (dvb_usb_lme2510_firmware != TUNER_LG) {
|
|
+ dvb_usb_lme2510_firmware = TUNER_LG;
|
|
+ ret = lme_firmware_switch(adap->dev->udev, 1);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
|
|
- st->i2c_gate = 4;
|
|
- adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config,
|
|
- &adap->dev->i2c_adap);
|
|
-
|
|
- if (adap->fe_adap[0].fe) {
|
|
- info("TUN Found Frontend TDA10086");
|
|
- st->i2c_tuner_gate_w = 4;
|
|
- st->i2c_tuner_gate_r = 4;
|
|
- st->i2c_tuner_addr = 0xc0;
|
|
- st->tuner_config = TUNER_LG;
|
|
- if (dvb_usb_lme2510_firmware != TUNER_LG) {
|
|
- dvb_usb_lme2510_firmware = TUNER_LG;
|
|
- ret = lme_firmware_switch(adap->dev->udev, 1);
|
|
+ st->i2c_gate = 4;
|
|
+ adap->fe_adap[0].fe = dvb_attach(stv0299_attach,
|
|
+ &sharp_z0194_config, &adap->dev->i2c_adap);
|
|
+ if (adap->fe_adap[0].fe) {
|
|
+ info("FE Found Stv0299");
|
|
+ st->i2c_tuner_gate_w = 4;
|
|
+ st->i2c_tuner_gate_r = 5;
|
|
+ st->i2c_tuner_addr = 0xc0;
|
|
+ st->tuner_config = TUNER_S0194;
|
|
+ if (dvb_usb_lme2510_firmware != TUNER_S0194) {
|
|
+ dvb_usb_lme2510_firmware = TUNER_S0194;
|
|
+ ret = lme_firmware_switch(adap->dev->udev, 1);
|
|
+ }
|
|
+ break;
|
|
}
|
|
- goto end;
|
|
- }
|
|
|
|
- st->i2c_gate = 4;
|
|
- adap->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194_config,
|
|
+ st->i2c_gate = 5;
|
|
+ adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config,
|
|
&adap->dev->i2c_adap);
|
|
- if (adap->fe_adap[0].fe) {
|
|
- info("FE Found Stv0299");
|
|
- st->i2c_tuner_gate_w = 4;
|
|
- st->i2c_tuner_gate_r = 5;
|
|
- st->i2c_tuner_addr = 0xc0;
|
|
- st->tuner_config = TUNER_S0194;
|
|
- if (dvb_usb_lme2510_firmware != TUNER_S0194) {
|
|
- dvb_usb_lme2510_firmware = TUNER_S0194;
|
|
- ret = lme_firmware_switch(adap->dev->udev, 1);
|
|
+
|
|
+ if (adap->fe_adap[0].fe) {
|
|
+ info("FE Found Stv0288");
|
|
+ st->i2c_tuner_gate_w = 4;
|
|
+ st->i2c_tuner_gate_r = 5;
|
|
+ st->i2c_tuner_addr = 0xc0;
|
|
+ st->tuner_config = TUNER_S7395;
|
|
+ if (dvb_usb_lme2510_firmware != TUNER_S7395) {
|
|
+ dvb_usb_lme2510_firmware = TUNER_S7395;
|
|
+ ret = lme_firmware_switch(adap->dev->udev, 1);
|
|
+ }
|
|
+ break;
|
|
}
|
|
- goto end;
|
|
- }
|
|
+ case 0x22f0:
|
|
+ st->i2c_gate = 5;
|
|
+ adap->fe_adap[0].fe = dvb_attach(m88rs2000_attach,
|
|
+ &m88rs2000_config, &adap->dev->i2c_adap);
|
|
|
|
- st->i2c_gate = 5;
|
|
- adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config,
|
|
- &adap->dev->i2c_adap);
|
|
- if (adap->fe_adap[0].fe) {
|
|
- info("FE Found Stv0288");
|
|
- st->i2c_tuner_gate_w = 4;
|
|
- st->i2c_tuner_gate_r = 5;
|
|
- st->i2c_tuner_addr = 0xc0;
|
|
- st->tuner_config = TUNER_S7395;
|
|
- if (dvb_usb_lme2510_firmware != TUNER_S7395) {
|
|
- dvb_usb_lme2510_firmware = TUNER_S7395;
|
|
- ret = lme_firmware_switch(adap->dev->udev, 1);
|
|
+ if (adap->fe_adap[0].fe) {
|
|
+ info("FE Found M88RS2000");
|
|
+ st->i2c_tuner_gate_w = 5;
|
|
+ st->i2c_tuner_gate_r = 5;
|
|
+ st->i2c_tuner_addr = 0xc0;
|
|
+ st->tuner_config = TUNER_RS2000;
|
|
+ adap->fe_adap[0].fe->ops.read_signal_strength =
|
|
+ dm04_rs2000_read_signal_strength;
|
|
+ adap->fe_adap[0].fe->ops.read_snr =
|
|
+ dm04_rs2000_read_snr;
|
|
}
|
|
- } else {
|
|
- info("DM04 Not Supported");
|
|
- return -ENODEV;
|
|
+ break;
|
|
}
|
|
|
|
+ if (adap->fe_adap[0].fe == NULL) {
|
|
+ info("DM04/QQBOX Not Powered up or not Supported");
|
|
+ return -ENODEV;
|
|
+ }
|
|
|
|
-end: if (ret) {
|
|
+ if (ret) {
|
|
if (adap->fe_adap[0].fe) {
|
|
dvb_frontend_detach(adap->fe_adap[0].fe);
|
|
adap->fe_adap[0].fe = NULL;
|
|
@@ -1028,7 +1131,7 @@ end: if (ret) {
|
|
static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap)
|
|
{
|
|
struct lme2510_state *st = adap->dev->priv;
|
|
- char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA"};
|
|
+ char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA", "RS2000"};
|
|
int ret = 0;
|
|
|
|
switch (st->tuner_config) {
|
|
@@ -1047,6 +1150,9 @@ static int dm04_lme2510_tuner(struct dvb
|
|
&adap->dev->i2c_adap, DVB_PLL_OPERA1))
|
|
ret = st->tuner_config;
|
|
break;
|
|
+ case TUNER_RS2000:
|
|
+ ret = st->tuner_config;
|
|
+ break;
|
|
default:
|
|
break;
|
|
}
|
|
@@ -1054,7 +1160,7 @@ static int dm04_lme2510_tuner(struct dvb
|
|
if (ret)
|
|
info("TUN Found %s tuner", tun_msg[ret]);
|
|
else {
|
|
- info("TUN No tuner found --- reseting device");
|
|
+ info("TUN No tuner found --- resetting device");
|
|
lme_coldreset(adap->dev->udev);
|
|
return -ENODEV;
|
|
}
|
|
@@ -1075,10 +1181,9 @@ static int lme2510_powerup(struct dvb_us
|
|
static u8 lnb_on[] = LNB_ON;
|
|
static u8 lnb_off[] = LNB_OFF;
|
|
static u8 rbuf[1];
|
|
- int ret, len = 3, rlen = 1;
|
|
+ int ret = 0, len = 3, rlen = 1;
|
|
|
|
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
|
|
- return -EAGAIN;
|
|
+ mutex_lock(&d->i2c_mutex);
|
|
|
|
if (onoff)
|
|
ret = lme2510_usb_talk(d, lnb_on, len, rbuf, rlen);
|
|
@@ -1136,6 +1241,7 @@ static int lme2510_probe(struct usb_inte
|
|
static struct usb_device_id lme2510_table[] = {
|
|
{ USB_DEVICE(0x3344, 0x1122) }, /* LME2510 */
|
|
{ USB_DEVICE(0x3344, 0x1120) }, /* LME2510C */
|
|
+ { USB_DEVICE(0x3344, 0x22f0) }, /* LME2510C RS2000 */
|
|
{} /* Terminating entry */
|
|
};
|
|
|
|
@@ -1153,7 +1259,7 @@ static struct dvb_usb_device_properties
|
|
DVB_USB_ADAP_NEED_PID_FILTERING|
|
|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
|
|
.streaming_ctrl = lme2510_streaming_ctrl,
|
|
- .pid_filter_count = 15,
|
|
+ .pid_filter_count = 32,
|
|
.pid_filter = lme2510_pid_filter,
|
|
.pid_filter_ctrl = lme2510_pid_filter_ctrl,
|
|
.frontend_attach = dm04_lme2510_frontend_attach,
|
|
@@ -1204,7 +1310,7 @@ static struct dvb_usb_device_properties
|
|
DVB_USB_ADAP_NEED_PID_FILTERING|
|
|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
|
|
.streaming_ctrl = lme2510_streaming_ctrl,
|
|
- .pid_filter_count = 15,
|
|
+ .pid_filter_count = 32,
|
|
.pid_filter = lme2510_pid_filter,
|
|
.pid_filter_ctrl = lme2510_pid_filter_ctrl,
|
|
.frontend_attach = dm04_lme2510_frontend_attach,
|
|
@@ -1234,11 +1340,14 @@ static struct dvb_usb_device_properties
|
|
.identify_state = lme2510_identify_state,
|
|
.i2c_algo = &lme2510_i2c_algo,
|
|
.generic_bulk_ctrl_endpoint = 0,
|
|
- .num_device_descs = 1,
|
|
+ .num_device_descs = 2,
|
|
.devices = {
|
|
{ "DM04_LME2510C_DVB-S",
|
|
{ &lme2510_table[1], NULL },
|
|
},
|
|
+ { "DM04_LME2510C_DVB-S RS2000",
|
|
+ { &lme2510_table[2], NULL },
|
|
+ },
|
|
}
|
|
};
|
|
|
|
@@ -1295,5 +1404,5 @@ module_usb_driver(lme2510_driver);
|
|
|
|
MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
|
|
MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0");
|
|
-MODULE_VERSION("1.91");
|
|
+MODULE_VERSION("1.99");
|
|
MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/media/video/ov6650.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ov6650.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ov6650.c
|
|
@@ -649,7 +649,7 @@ static int ov6650_s_fmt(struct v4l2_subd
|
|
clkrc = CLKRC_24MHz;
|
|
} else {
|
|
dev_err(&client->dev,
|
|
- "unspported input clock, check platform data\n");
|
|
+ "unsupported input clock, check platform data\n");
|
|
return -EINVAL;
|
|
}
|
|
mclk = sense->master_clock;
|
|
@@ -1046,18 +1046,7 @@ static struct i2c_driver ov6650_i2c_driv
|
|
.id_table = ov6650_id,
|
|
};
|
|
|
|
-static int __init ov6650_module_init(void)
|
|
-{
|
|
- return i2c_add_driver(&ov6650_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit ov6650_module_exit(void)
|
|
-{
|
|
- i2c_del_driver(&ov6650_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(ov6650_module_init);
|
|
-module_exit(ov6650_module_exit);
|
|
+module_i2c_driver(ov6650_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650");
|
|
MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/tda1004x.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/tda1004x.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/tda1004x.c
|
|
@@ -1272,7 +1272,7 @@ struct dvb_frontend* tda10045_attach(con
|
|
/* allocate memory for the internal state */
|
|
state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
|
|
if (!state) {
|
|
- printk(KERN_ERR "Can't alocate memory for tda10045 state\n");
|
|
+ printk(KERN_ERR "Can't allocate memory for tda10045 state\n");
|
|
return NULL;
|
|
}
|
|
|
|
@@ -1342,7 +1342,7 @@ struct dvb_frontend* tda10046_attach(con
|
|
/* allocate memory for the internal state */
|
|
state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
|
|
if (!state) {
|
|
- printk(KERN_ERR "Can't alocate memory for tda10046 state\n");
|
|
+ printk(KERN_ERR "Can't allocate memory for tda10046 state\n");
|
|
return NULL;
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/davinci/vpif.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/davinci/vpif.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/davinci/vpif.h
|
|
@@ -18,8 +18,6 @@
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/videodev2.h>
|
|
-#include <mach/hardware.h>
|
|
-#include <mach/dm646x.h>
|
|
#include <media/davinci/vpif_types.h>
|
|
|
|
/* Maximum channel allowed */
|
|
Index: linux-3.3.x86_64/drivers/media/video/davinci/vpif_display.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/davinci/vpif_display.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/davinci/vpif_display.c
|
|
@@ -39,8 +39,6 @@
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <media/v4l2-chip-ident.h>
|
|
|
|
-#include <mach/dm646x.h>
|
|
-
|
|
#include "vpif_display.h"
|
|
#include "vpif.h"
|
|
|
|
Index: linux-3.3.x86_64/include/media/davinci/vpif_types.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/media/davinci/vpif_types.h
|
|
+++ linux-3.3.x86_64/include/media/davinci/vpif_types.h
|
|
@@ -17,6 +17,8 @@
|
|
#ifndef _VPIF_TYPES_H
|
|
#define _VPIF_TYPES_H
|
|
|
|
+#include <linux/i2c.h>
|
|
+
|
|
#define VPIF_CAPTURE_MAX_CHANNELS 2
|
|
|
|
enum vpif_if_type {
|
|
Index: linux-3.3.x86_64/drivers/media/video/tm6000/tm6000-input.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tm6000/tm6000-input.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tm6000/tm6000-input.c
|
|
@@ -481,8 +481,6 @@ int tm6000_ir_fini(struct tm6000_core *d
|
|
|
|
dprintk(2, "%s\n",__func__);
|
|
|
|
- rc_unregister_device(ir->rc);
|
|
-
|
|
if (!ir->polling)
|
|
__tm6000_ir_int_stop(ir->rc);
|
|
|
|
@@ -492,6 +490,7 @@ int tm6000_ir_fini(struct tm6000_core *d
|
|
tm6000_flash_led(dev, 0);
|
|
ir->pwled = 0;
|
|
|
|
+ rc_unregister_device(ir->rc);
|
|
|
|
kfree(ir);
|
|
dev->ir = NULL;
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/mantis/mantis_hif.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/mantis/mantis_hif.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/mantis/mantis_hif.c
|
|
@@ -76,7 +76,7 @@ static int mantis_hif_write_wait(struct
|
|
udelay(500);
|
|
timeout++;
|
|
if (timeout > 100) {
|
|
- dprintk(MANTIS_ERROR, 1, "Adater(%d) Slot(0): Write operation timed out!", mantis->num);
|
|
+ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write operation timed out!", mantis->num);
|
|
rc = -ETIMEDOUT;
|
|
break;
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/max2165.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/common/tuners/max2165.c
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/max2165.c
|
|
@@ -168,7 +168,7 @@ int fixpt_div32(u32 dividend, u32 diviso
|
|
int i;
|
|
|
|
if (0 == divisor)
|
|
- return -1;
|
|
+ return -EINVAL;
|
|
|
|
q = dividend / divisor;
|
|
remainder = dividend - q * divisor;
|
|
@@ -194,10 +194,13 @@ static int max2165_set_rf(struct max2165
|
|
u8 tf_ntch;
|
|
u32 t;
|
|
u32 quotient, fraction;
|
|
+ int ret;
|
|
|
|
/* Set PLL divider according to RF frequency */
|
|
- fixpt_div32(freq / 1000, priv->config->osc_clk * 1000,
|
|
- "ient, &fraction);
|
|
+ ret = fixpt_div32(freq / 1000, priv->config->osc_clk * 1000,
|
|
+ "ient, &fraction);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
|
|
/* 20-bit fraction */
|
|
fraction >>= 12;
|
|
Index: linux-3.3.x86_64/drivers/media/video/v4l2-compat-ioctl32.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/v4l2-compat-ioctl32.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/v4l2-compat-ioctl32.c
|
|
@@ -14,12 +14,11 @@
|
|
*/
|
|
|
|
#include <linux/compat.h>
|
|
-#include <linux/videodev2.h>
|
|
#include <linux/module.h>
|
|
+#include <linux/videodev2.h>
|
|
+#include <media/v4l2-dev.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
|
|
-#ifdef CONFIG_COMPAT
|
|
-
|
|
static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
long ret = -ENOIOCTLCMD;
|
|
@@ -937,6 +936,7 @@ static long do_video_ioctl(struct file *
|
|
|
|
long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
+ struct video_device *vdev = video_devdata(file);
|
|
long ret = -ENOIOCTLCMD;
|
|
|
|
if (!file->f_op->unlocked_ioctl)
|
|
@@ -1005,6 +1005,8 @@ long v4l2_compat_ioctl32(struct file *fi
|
|
case VIDIOC_G_ENC_INDEX:
|
|
case VIDIOC_ENCODER_CMD:
|
|
case VIDIOC_TRY_ENCODER_CMD:
|
|
+ case VIDIOC_DECODER_CMD:
|
|
+ case VIDIOC_TRY_DECODER_CMD:
|
|
case VIDIOC_DBG_S_REGISTER:
|
|
case VIDIOC_DBG_G_REGISTER:
|
|
case VIDIOC_DBG_G_CHIP_IDENT:
|
|
@@ -1025,14 +1027,16 @@ long v4l2_compat_ioctl32(struct file *fi
|
|
break;
|
|
|
|
default:
|
|
- printk(KERN_WARNING "compat_ioctl32: "
|
|
- "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
|
|
- _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd);
|
|
+ if (vdev->fops->compat_ioctl32)
|
|
+ ret = vdev->fops->compat_ioctl32(file, cmd, arg);
|
|
+
|
|
+ if (ret == -ENOIOCTLCMD)
|
|
+ printk(KERN_WARNING "compat_ioctl32: "
|
|
+ "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
|
|
+ _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd),
|
|
+ cmd);
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32);
|
|
-#endif
|
|
-
|
|
-MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/include/media/v4l2-ioctl.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/media/v4l2-ioctl.h
|
|
+++ linux-3.3.x86_64/include/media/v4l2-ioctl.h
|
|
@@ -211,6 +211,10 @@ struct v4l2_ioctl_ops {
|
|
struct v4l2_encoder_cmd *a);
|
|
int (*vidioc_try_encoder_cmd) (struct file *file, void *fh,
|
|
struct v4l2_encoder_cmd *a);
|
|
+ int (*vidioc_decoder_cmd) (struct file *file, void *fh,
|
|
+ struct v4l2_decoder_cmd *a);
|
|
+ int (*vidioc_try_decoder_cmd) (struct file *file, void *fh,
|
|
+ struct v4l2_decoder_cmd *a);
|
|
|
|
/* Stream type-dependent parameter ioctls */
|
|
int (*vidioc_g_parm) (struct file *file, void *fh,
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-driver.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/ivtv-driver.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-driver.c
|
|
@@ -55,7 +55,7 @@
|
|
#include "ivtv-routing.h"
|
|
#include "ivtv-controls.h"
|
|
#include "ivtv-gpio.h"
|
|
-
|
|
+#include <linux/dma-mapping.h>
|
|
#include <media/tveeprom.h>
|
|
#include <media/saa7115.h>
|
|
#include <media/v4l2-chip-ident.h>
|
|
@@ -99,7 +99,7 @@ static int i2c_clock_period[IVTV_MAX_CAR
|
|
|
|
static unsigned int cardtype_c = 1;
|
|
static unsigned int tuner_c = 1;
|
|
-static bool radio_c = 1;
|
|
+static int radio_c = 1;
|
|
static unsigned int i2c_clock_period_c = 1;
|
|
static char pal[] = "---";
|
|
static char secam[] = "--";
|
|
@@ -139,7 +139,7 @@ static int tunertype = -1;
|
|
static int newi2c = -1;
|
|
|
|
module_param_array(tuner, int, &tuner_c, 0644);
|
|
-module_param_array(radio, bool, &radio_c, 0644);
|
|
+module_param_array(radio, int, &radio_c, 0644);
|
|
module_param_array(cardtype, int, &cardtype_c, 0644);
|
|
module_param_string(pal, pal, sizeof(pal), 0644);
|
|
module_param_string(secam, secam, sizeof(secam), 0644);
|
|
@@ -744,8 +744,6 @@ static int __devinit ivtv_init_struct1(s
|
|
|
|
itv->cur_dma_stream = -1;
|
|
itv->cur_pio_stream = -1;
|
|
- itv->audio_stereo_mode = AUDIO_STEREO;
|
|
- itv->audio_bilingual_mode = AUDIO_MONO_LEFT;
|
|
|
|
/* Ctrls */
|
|
itv->speed = 1000;
|
|
@@ -815,7 +813,7 @@ static int ivtv_setup_pci(struct ivtv *i
|
|
IVTV_ERR("Can't enable device!\n");
|
|
return -EIO;
|
|
}
|
|
- if (pci_set_dma_mask(pdev, 0xffffffff)) {
|
|
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
|
|
IVTV_ERR("No suitable DMA available.\n");
|
|
return -EIO;
|
|
}
|
|
@@ -1200,6 +1198,32 @@ static int __devinit ivtv_probe(struct p
|
|
itv->tuner_std = itv->std;
|
|
|
|
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
|
|
+ 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);
|
|
+ itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
|
|
+ V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0);
|
|
+ /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported,
|
|
+ mask that menu item. */
|
|
+ itv->ctrl_audio_playback =
|
|
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
|
|
+ V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK,
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
|
|
+ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO);
|
|
+ itv->ctrl_audio_multilingual_playback =
|
|
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
|
|
+ V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK,
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
|
|
+ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
|
|
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT);
|
|
+ if (hdl->error) {
|
|
+ retval = hdl->error;
|
|
+ goto free_i2c;
|
|
+ }
|
|
+ v4l2_ctrl_cluster(2, &itv->ctrl_pts);
|
|
+ v4l2_ctrl_cluster(2, &itv->ctrl_audio_playback);
|
|
ivtv_call_all(itv, video, s_std_output, itv->std);
|
|
/* Turn off the output signal. The mpeg decoder is not yet
|
|
active so without this you would get a green image until the
|
|
@@ -1236,6 +1260,7 @@ free_streams:
|
|
free_irq:
|
|
free_irq(itv->pdev->irq, (void *)itv);
|
|
free_i2c:
|
|
+ v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
|
|
exit_ivtv_i2c(itv);
|
|
free_io:
|
|
ivtv_iounmap(itv);
|
|
@@ -1375,7 +1400,7 @@ static void ivtv_remove(struct pci_dev *
|
|
else
|
|
type = IVTV_DEC_STREAM_TYPE_MPG;
|
|
ivtv_stop_v4l2_decode_stream(&itv->streams[type],
|
|
- VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
|
|
+ V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
|
|
}
|
|
ivtv_halt_firmware(itv);
|
|
}
|
|
@@ -1391,6 +1416,8 @@ static void ivtv_remove(struct pci_dev *
|
|
ivtv_streams_cleanup(itv, 1);
|
|
ivtv_udma_free(itv);
|
|
|
|
+ v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
|
|
+
|
|
exit_ivtv_i2c(itv);
|
|
|
|
free_irq(itv->pdev->irq, (void *)itv);
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-fileops.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/ivtv-fileops.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-fileops.c
|
|
@@ -746,8 +746,9 @@ unsigned int ivtv_v4l2_dec_poll(struct f
|
|
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 f
|
|
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);
|
|
@@ -900,7 +902,7 @@ int ivtv_v4l2_close(struct file *filp)
|
|
if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
|
|
struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT];
|
|
|
|
- ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
|
|
+ ivtv_stop_decoding(id, V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
|
|
|
|
/* If all output streams are closed, and if the user doesn't have
|
|
IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */
|
|
Index: linux-3.3.x86_64/drivers/media/video/v4l2-ctrls.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/v4l2-ctrls.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/v4l2-ctrls.c
|
|
@@ -175,6 +175,15 @@ const char * const *v4l2_ctrl_get_menu(u
|
|
"16-bit CRC",
|
|
NULL
|
|
};
|
|
+ static const char * const mpeg_audio_dec_playback[] = {
|
|
+ "Auto",
|
|
+ "Stereo",
|
|
+ "Left",
|
|
+ "Right",
|
|
+ "Mono",
|
|
+ "Swapped Stereo",
|
|
+ NULL
|
|
+ };
|
|
static const char * const mpeg_video_encoding[] = {
|
|
"MPEG-1",
|
|
"MPEG-2",
|
|
@@ -236,8 +245,8 @@ const char * const *v4l2_ctrl_get_menu(u
|
|
};
|
|
static const char * const tune_preemphasis[] = {
|
|
"No Preemphasis",
|
|
- "50 useconds",
|
|
- "75 useconds",
|
|
+ "50 Microseconds",
|
|
+ "75 Microseconds",
|
|
NULL,
|
|
};
|
|
static const char * const header_mode[] = {
|
|
@@ -334,7 +343,7 @@ const char * const *v4l2_ctrl_get_menu(u
|
|
};
|
|
static const char * const mpeg4_profile[] = {
|
|
"Simple",
|
|
- "Adcanved Simple",
|
|
+ "Advanced Simple",
|
|
"Core",
|
|
"Simple Scalable",
|
|
"Advanced Coding Efficency",
|
|
@@ -353,6 +362,16 @@ const char * const *v4l2_ctrl_get_menu(u
|
|
NULL,
|
|
};
|
|
|
|
+ static const char * const jpeg_chroma_subsampling[] = {
|
|
+ "4:4:4",
|
|
+ "4:2:2",
|
|
+ "4:2:0",
|
|
+ "4:1:1",
|
|
+ "4:1:0",
|
|
+ "Gray",
|
|
+ NULL,
|
|
+ };
|
|
+
|
|
switch (id) {
|
|
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
|
|
return mpeg_audio_sampling_freq;
|
|
@@ -374,6 +393,9 @@ const char * const *v4l2_ctrl_get_menu(u
|
|
return mpeg_audio_emphasis;
|
|
case V4L2_CID_MPEG_AUDIO_CRC:
|
|
return mpeg_audio_crc;
|
|
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
|
|
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
|
|
+ return mpeg_audio_dec_playback;
|
|
case V4L2_CID_MPEG_VIDEO_ENCODING:
|
|
return mpeg_video_encoding;
|
|
case V4L2_CID_MPEG_VIDEO_ASPECT:
|
|
@@ -414,6 +436,9 @@ const char * const *v4l2_ctrl_get_menu(u
|
|
return mpeg_mpeg4_level;
|
|
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
|
|
return mpeg4_profile;
|
|
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
|
|
+ return jpeg_chroma_subsampling;
|
|
+
|
|
default:
|
|
return NULL;
|
|
}
|
|
@@ -492,6 +517,8 @@ const char *v4l2_ctrl_get_name(u32 id)
|
|
case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
|
|
case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
|
|
case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
|
|
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: return "Audio Playback";
|
|
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback";
|
|
case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
|
|
case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
|
|
case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
|
|
@@ -546,6 +573,8 @@ const char *v4l2_ctrl_get_name(u32 id)
|
|
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice";
|
|
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method";
|
|
case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size";
|
|
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS";
|
|
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count";
|
|
|
|
/* CAMERA controls */
|
|
/* Keep the order of the 'case's the same as in videodev2.h! */
|
|
@@ -607,6 +636,14 @@ const char *v4l2_ctrl_get_name(u32 id)
|
|
case V4L2_CID_FLASH_CHARGE: return "Charge";
|
|
case V4L2_CID_FLASH_READY: return "Ready to Strobe";
|
|
|
|
+ /* JPEG encoder controls */
|
|
+ /* Keep the order of the 'case's the same as in videodev2.h! */
|
|
+ case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls";
|
|
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling";
|
|
+ case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval";
|
|
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality";
|
|
+ case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers";
|
|
+
|
|
default:
|
|
return NULL;
|
|
}
|
|
@@ -674,6 +711,8 @@ void v4l2_ctrl_fill(u32 id, const char *
|
|
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
|
|
case V4L2_CID_MPEG_AUDIO_EMPHASIS:
|
|
case V4L2_CID_MPEG_AUDIO_CRC:
|
|
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
|
|
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
|
|
case V4L2_CID_MPEG_VIDEO_ENCODING:
|
|
case V4L2_CID_MPEG_VIDEO_ASPECT:
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
|
|
@@ -693,6 +732,7 @@ void v4l2_ctrl_fill(u32 id, const char *
|
|
case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
|
|
case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
|
|
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
|
|
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
|
|
*type = V4L2_CTRL_TYPE_MENU;
|
|
break;
|
|
case V4L2_CID_RDS_TX_PS_NAME:
|
|
@@ -704,6 +744,7 @@ void v4l2_ctrl_fill(u32 id, const char *
|
|
case V4L2_CID_MPEG_CLASS:
|
|
case V4L2_CID_FM_TX_CLASS:
|
|
case V4L2_CID_FLASH_CLASS:
|
|
+ case V4L2_CID_JPEG_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;
|
|
@@ -717,6 +758,7 @@ void v4l2_ctrl_fill(u32 id, const char *
|
|
*max = 0xFFFFFF;
|
|
break;
|
|
case V4L2_CID_FLASH_FAULT:
|
|
+ case V4L2_CID_JPEG_ACTIVE_MARKER:
|
|
*type = V4L2_CTRL_TYPE_BITMASK;
|
|
break;
|
|
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
|
|
@@ -724,6 +766,11 @@ void v4l2_ctrl_fill(u32 id, const char *
|
|
*type = V4L2_CTRL_TYPE_INTEGER;
|
|
*flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
|
break;
|
|
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
|
|
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS:
|
|
+ *type = V4L2_CTRL_TYPE_INTEGER64;
|
|
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE;
|
|
+ break;
|
|
default:
|
|
*type = V4L2_CTRL_TYPE_INTEGER;
|
|
break;
|
|
@@ -1470,7 +1517,7 @@ EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
|
|
int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
|
|
struct v4l2_ctrl_handler *add)
|
|
{
|
|
- struct v4l2_ctrl *ctrl;
|
|
+ struct v4l2_ctrl_ref *ref;
|
|
int ret = 0;
|
|
|
|
/* Do nothing if either handler is NULL or if they are the same */
|
|
@@ -1479,7 +1526,9 @@ int v4l2_ctrl_add_handler(struct v4l2_ct
|
|
if (hdl->error)
|
|
return hdl->error;
|
|
mutex_lock(&add->lock);
|
|
- list_for_each_entry(ctrl, &add->ctrls, node) {
|
|
+ list_for_each_entry(ref, &add->ctrl_refs, node) {
|
|
+ struct v4l2_ctrl *ctrl = ref->ctrl;
|
|
+
|
|
/* Skip handler-private controls. */
|
|
if (ctrl->is_private)
|
|
continue;
|
|
@@ -2359,3 +2408,35 @@ void v4l2_ctrl_del_event(struct v4l2_ctr
|
|
v4l2_ctrl_unlock(ctrl);
|
|
}
|
|
EXPORT_SYMBOL(v4l2_ctrl_del_event);
|
|
+
|
|
+int v4l2_ctrl_log_status(struct file *file, void *fh)
|
|
+{
|
|
+ struct video_device *vfd = video_devdata(file);
|
|
+ struct v4l2_fh *vfh = file->private_data;
|
|
+
|
|
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev)
|
|
+ v4l2_ctrl_handler_log_status(vfh->ctrl_handler,
|
|
+ vfd->v4l2_dev->name);
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(v4l2_ctrl_log_status);
|
|
+
|
|
+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 -EINVAL;
|
|
+}
|
|
+EXPORT_SYMBOL(v4l2_ctrl_subscribe_event);
|
|
+
|
|
+unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
|
|
+{
|
|
+ struct v4l2_fh *fh = file->private_data;
|
|
+
|
|
+ if (v4l2_event_pending(fh))
|
|
+ return POLLPRI;
|
|
+ poll_wait(file, &fh->wait, wait);
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(v4l2_ctrl_poll);
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-controls.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/ivtv-controls.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-controls.c
|
|
@@ -21,6 +21,7 @@
|
|
#include "ivtv-driver.h"
|
|
#include "ivtv-ioctl.h"
|
|
#include "ivtv-controls.h"
|
|
+#include "ivtv-mailbox.h"
|
|
|
|
static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
|
|
{
|
|
@@ -99,3 +100,64 @@ struct cx2341x_handler_ops ivtv_cxhdl_op
|
|
.s_video_encoding = ivtv_s_video_encoding,
|
|
.s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt,
|
|
};
|
|
+
|
|
+int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame)
|
|
+{
|
|
+ u32 data[CX2341X_MBOX_MAX_DATA];
|
|
+
|
|
+ if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
|
|
+ *pts = (s64)((u64)itv->last_dec_timing[2] << 32) |
|
|
+ (u64)itv->last_dec_timing[1];
|
|
+ *frame = itv->last_dec_timing[0];
|
|
+ return 0;
|
|
+ }
|
|
+ *pts = 0;
|
|
+ *frame = 0;
|
|
+ if (atomic_read(&itv->decoding)) {
|
|
+ if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
|
|
+ IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+ memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
|
|
+ set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
|
|
+ *pts = (s64)((u64) data[2] << 32) | (u64) data[1];
|
|
+ *frame = data[0];
|
|
+ /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
|
|
+
|
|
+ switch (ctrl->id) {
|
|
+ /* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME
|
|
+ control cluster */
|
|
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS:
|
|
+ return ivtv_g_pts_frame(itv, &itv->ctrl_pts->val64,
|
|
+ &itv->ctrl_frame->val64);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ivtv_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
|
|
+
|
|
+ switch (ctrl->id) {
|
|
+ /* V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK and MULTILINGUAL_PLAYBACK
|
|
+ control cluster */
|
|
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
|
|
+ itv->audio_stereo_mode = itv->ctrl_audio_playback->val - 1;
|
|
+ itv->audio_bilingual_mode = itv->ctrl_audio_multilingual_playback->val - 1;
|
|
+ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+const struct v4l2_ctrl_ops ivtv_hdl_out_ops = {
|
|
+ .s_ctrl = ivtv_s_ctrl,
|
|
+ .g_volatile_ctrl = ivtv_g_volatile_ctrl,
|
|
+};
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-controls.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/ivtv-controls.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-controls.h
|
|
@@ -22,5 +22,7 @@
|
|
#define IVTV_CONTROLS_H
|
|
|
|
extern struct cx2341x_handler_ops ivtv_cxhdl_ops;
|
|
+extern const struct v4l2_ctrl_ops ivtv_hdl_out_ops;
|
|
+int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame);
|
|
|
|
#endif
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/ddbridge/ddbridge.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/ddbridge/ddbridge.h
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/ddbridge/ddbridge.h
|
|
@@ -32,8 +32,6 @@
|
|
#include <asm/dma.h>
|
|
#include <linux/dvb/frontend.h>
|
|
#include <linux/dvb/ca.h>
|
|
-#include <linux/dvb/video.h>
|
|
-#include <linux/dvb/audio.h>
|
|
#include <linux/socket.h>
|
|
|
|
#include "dmxdev.h"
|
|
Index: linux-3.3.x86_64/drivers/media/video/cx18/cx18-driver.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cx18/cx18-driver.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/cx18/cx18-driver.h
|
|
@@ -44,8 +44,6 @@
|
|
#include <linux/slab.h>
|
|
#include <asm/byteorder.h>
|
|
|
|
-#include <linux/dvb/video.h>
|
|
-#include <linux/dvb/audio.h>
|
|
#include <media/v4l2-common.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <media/v4l2-device.h>
|
|
Index: linux-3.3.x86_64/include/linux/ivtv.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/linux/ivtv.h
|
|
+++ linux-3.3.x86_64/include/linux/ivtv.h
|
|
@@ -58,7 +58,11 @@ struct ivtv_dma_frame {
|
|
__u32 src_height;
|
|
};
|
|
|
|
-#define IVTV_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame)
|
|
+#define IVTV_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame)
|
|
+
|
|
+/* Select the passthrough mode (if the argument is non-zero). In the passthrough
|
|
+ mode the output of the encoder is passed immediately into the decoder. */
|
|
+#define IVTV_IOC_PASSTHROUGH_MODE _IOW ('V', BASE_VIDIOC_PRIVATE+1, int)
|
|
|
|
/* Deprecated defines: applications should use the defines from videodev2.h */
|
|
#define IVTV_SLICED_TYPE_TELETEXT_B V4L2_MPEG_VBI_IVTV_TELETEXT_B
|
|
Index: linux-3.3.x86_64/drivers/media/rc/keymaps/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/keymaps/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/rc/keymaps/Makefile
|
|
@@ -41,8 +41,11 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t
|
|
rc-imon-mce.o \
|
|
rc-imon-pad.o \
|
|
rc-iodata-bctv7e.o \
|
|
+ rc-it913x-v1.o \
|
|
+ rc-it913x-v2.o \
|
|
rc-kaiomy.o \
|
|
rc-kworld-315u.o \
|
|
+ rc-kworld-pc150u.o \
|
|
rc-kworld-plus-tv-analog.o \
|
|
rc-leadtek-y04g0051.o \
|
|
rc-lirc.o \
|
|
Index: linux-3.3.x86_64/drivers/media/rc/keymaps/rc-it913x-v1.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/rc/keymaps/rc-it913x-v1.c
|
|
@@ -0,0 +1,95 @@
|
|
+/* ITE Generic remotes Version 1
|
|
+ *
|
|
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <media/rc-map.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+
|
|
+static struct rc_map_table it913x_v1_rc[] = {
|
|
+ /* Type 1 */
|
|
+ { 0x61d601, KEY_VIDEO }, /* Source */
|
|
+ { 0x61d602, KEY_3 },
|
|
+ { 0x61d603, KEY_POWER }, /* ShutDown */
|
|
+ { 0x61d604, KEY_1 },
|
|
+ { 0x61d605, KEY_5 },
|
|
+ { 0x61d606, KEY_6 },
|
|
+ { 0x61d607, KEY_CHANNELDOWN }, /* CH- */
|
|
+ { 0x61d608, KEY_2 },
|
|
+ { 0x61d609, KEY_CHANNELUP }, /* CH+ */
|
|
+ { 0x61d60a, KEY_9 },
|
|
+ { 0x61d60b, KEY_ZOOM }, /* Zoom */
|
|
+ { 0x61d60c, KEY_7 },
|
|
+ { 0x61d60d, KEY_8 },
|
|
+ { 0x61d60e, KEY_VOLUMEUP }, /* Vol+ */
|
|
+ { 0x61d60f, KEY_4 },
|
|
+ { 0x61d610, KEY_ESC }, /* [back up arrow] */
|
|
+ { 0x61d611, KEY_0 },
|
|
+ { 0x61d612, KEY_OK }, /* [enter arrow] */
|
|
+ { 0x61d613, KEY_VOLUMEDOWN }, /* Vol- */
|
|
+ { 0x61d614, KEY_RECORD }, /* Rec */
|
|
+ { 0x61d615, KEY_STOP }, /* Stop */
|
|
+ { 0x61d616, KEY_PLAY }, /* Play */
|
|
+ { 0x61d617, KEY_MUTE }, /* Mute */
|
|
+ { 0x61d618, KEY_UP },
|
|
+ { 0x61d619, KEY_DOWN },
|
|
+ { 0x61d61a, KEY_LEFT },
|
|
+ { 0x61d61b, KEY_RIGHT },
|
|
+ { 0x61d61c, KEY_RED },
|
|
+ { 0x61d61d, KEY_GREEN },
|
|
+ { 0x61d61e, KEY_YELLOW },
|
|
+ { 0x61d61f, KEY_BLUE },
|
|
+ { 0x61d643, KEY_POWER2 }, /* [red power button] */
|
|
+ /* Type 2 - 20 buttons */
|
|
+ { 0x807f0d, KEY_0 },
|
|
+ { 0x807f04, KEY_1 },
|
|
+ { 0x807f05, KEY_2 },
|
|
+ { 0x807f06, KEY_3 },
|
|
+ { 0x807f07, KEY_4 },
|
|
+ { 0x807f08, KEY_5 },
|
|
+ { 0x807f09, KEY_6 },
|
|
+ { 0x807f0a, KEY_7 },
|
|
+ { 0x807f1b, KEY_8 },
|
|
+ { 0x807f1f, KEY_9 },
|
|
+ { 0x807f12, KEY_POWER },
|
|
+ { 0x807f01, KEY_MEDIA_REPEAT}, /* Recall */
|
|
+ { 0x807f19, KEY_PAUSE }, /* Timeshift */
|
|
+ { 0x807f1e, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
|
|
+ { 0x807f03, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
|
|
+ { 0x807f1a, KEY_CHANNELUP },
|
|
+ { 0x807f02, KEY_CHANNELDOWN },
|
|
+ { 0x807f0c, KEY_ZOOM },
|
|
+ { 0x807f00, KEY_RECORD },
|
|
+ { 0x807f0e, KEY_STOP },
|
|
+};
|
|
+
|
|
+static struct rc_map_list it913x_v1_map = {
|
|
+ .map = {
|
|
+ .scan = it913x_v1_rc,
|
|
+ .size = ARRAY_SIZE(it913x_v1_rc),
|
|
+ .rc_type = RC_TYPE_NEC,
|
|
+ .name = RC_MAP_IT913X_V1,
|
|
+ }
|
|
+};
|
|
+
|
|
+static int __init init_rc_it913x_v1_map(void)
|
|
+{
|
|
+ return rc_map_register(&it913x_v1_map);
|
|
+}
|
|
+
|
|
+static void __exit exit_rc_it913x_v1_map(void)
|
|
+{
|
|
+ rc_map_unregister(&it913x_v1_map);
|
|
+}
|
|
+
|
|
+module_init(init_rc_it913x_v1_map)
|
|
+module_exit(exit_rc_it913x_v1_map)
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
|
|
Index: linux-3.3.x86_64/drivers/media/rc/keymaps/rc-it913x-v2.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/rc/keymaps/rc-it913x-v2.c
|
|
@@ -0,0 +1,94 @@
|
|
+/* ITE Generic remotes Version 2
|
|
+ *
|
|
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <media/rc-map.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+
|
|
+static struct rc_map_table it913x_v2_rc[] = {
|
|
+ /* Type 1 */
|
|
+ /* 9005 remote */
|
|
+ { 0x807f12, KEY_POWER2 }, /* Power (RED POWER BUTTON)*/
|
|
+ { 0x807f1a, KEY_VIDEO }, /* Source */
|
|
+ { 0x807f1e, KEY_MUTE }, /* Mute */
|
|
+ { 0x807f01, KEY_RECORD }, /* Record */
|
|
+ { 0x807f02, KEY_CHANNELUP }, /* Channel+ */
|
|
+ { 0x807f03, KEY_TIME }, /* TimeShift */
|
|
+ { 0x807f04, KEY_VOLUMEUP }, /* Volume- */
|
|
+ { 0x807f05, KEY_SCREEN }, /* FullScreen */
|
|
+ { 0x807f06, KEY_VOLUMEDOWN }, /* Volume- */
|
|
+ { 0x807f07, KEY_0 }, /* 0 */
|
|
+ { 0x807f08, KEY_CHANNELDOWN }, /* Channel- */
|
|
+ { 0x807f09, KEY_PREVIOUS }, /* Recall */
|
|
+ { 0x807f0a, KEY_1 }, /* 1 */
|
|
+ { 0x807f1b, KEY_2 }, /* 2 */
|
|
+ { 0x807f1f, KEY_3 }, /* 3 */
|
|
+ { 0x807f0c, KEY_4 }, /* 4 */
|
|
+ { 0x807f0d, KEY_5 }, /* 5 */
|
|
+ { 0x807f0e, KEY_6 }, /* 6 */
|
|
+ { 0x807f00, KEY_7 }, /* 7 */
|
|
+ { 0x807f0f, KEY_8 }, /* 8 */
|
|
+ { 0x807f19, KEY_9 }, /* 9 */
|
|
+
|
|
+ /* Type 2 */
|
|
+ /* keys stereo, snapshot unassigned */
|
|
+ { 0x866b00, KEY_0 },
|
|
+ { 0x866b1b, KEY_1 },
|
|
+ { 0x866b02, KEY_2 },
|
|
+ { 0x866b03, KEY_3 },
|
|
+ { 0x866b04, KEY_4 },
|
|
+ { 0x866b05, KEY_5 },
|
|
+ { 0x866b06, KEY_6 },
|
|
+ { 0x866b07, KEY_7 },
|
|
+ { 0x866b08, KEY_8 },
|
|
+ { 0x866b09, KEY_9 },
|
|
+ { 0x866b12, KEY_POWER },
|
|
+ { 0x866b13, KEY_MUTE },
|
|
+ { 0x866b0a, KEY_PREVIOUS }, /* Recall */
|
|
+ { 0x866b1e, KEY_PAUSE },
|
|
+ { 0x866b0c, KEY_VOLUMEUP },
|
|
+ { 0x866b18, KEY_VOLUMEDOWN },
|
|
+ { 0x866b0b, KEY_CHANNELUP },
|
|
+ { 0x866b18, KEY_CHANNELDOWN },
|
|
+ { 0x866b10, KEY_ZOOM },
|
|
+ { 0x866b1d, KEY_RECORD },
|
|
+ { 0x866b0e, KEY_STOP },
|
|
+ { 0x866b11, KEY_EPG},
|
|
+ { 0x866b1a, KEY_FASTFORWARD },
|
|
+ { 0x866b0f, KEY_REWIND },
|
|
+ { 0x866b1c, KEY_TV },
|
|
+ { 0x866b1b, KEY_TEXT },
|
|
+
|
|
+};
|
|
+
|
|
+static struct rc_map_list it913x_v2_map = {
|
|
+ .map = {
|
|
+ .scan = it913x_v2_rc,
|
|
+ .size = ARRAY_SIZE(it913x_v2_rc),
|
|
+ .rc_type = RC_TYPE_NEC,
|
|
+ .name = RC_MAP_IT913X_V2,
|
|
+ }
|
|
+};
|
|
+
|
|
+static int __init init_rc_it913x_v2_map(void)
|
|
+{
|
|
+ return rc_map_register(&it913x_v2_map);
|
|
+}
|
|
+
|
|
+static void __exit exit_rc_it913x_v2_map(void)
|
|
+{
|
|
+ rc_map_unregister(&it913x_v2_map);
|
|
+}
|
|
+
|
|
+module_init(init_rc_it913x_v2_map)
|
|
+module_exit(exit_rc_it913x_v2_map)
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
|
|
Index: linux-3.3.x86_64/include/media/rc-map.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/media/rc-map.h
|
|
+++ linux-3.3.x86_64/include/media/rc-map.h
|
|
@@ -102,8 +102,11 @@ void rc_map_init(void);
|
|
#define RC_MAP_IMON_MCE "rc-imon-mce"
|
|
#define RC_MAP_IMON_PAD "rc-imon-pad"
|
|
#define RC_MAP_IODATA_BCTV7E "rc-iodata-bctv7e"
|
|
+#define RC_MAP_IT913X_V1 "rc-it913x-v1"
|
|
+#define RC_MAP_IT913X_V2 "rc-it913x-v2"
|
|
#define RC_MAP_KAIOMY "rc-kaiomy"
|
|
#define RC_MAP_KWORLD_315U "rc-kworld-315u"
|
|
+#define RC_MAP_KWORLD_PC150U "rc-kworld-pc150u"
|
|
#define RC_MAP_KWORLD_PLUS_TV_ANALOG "rc-kworld-plus-tv-analog"
|
|
#define RC_MAP_LEADTEK_Y04G0051 "rc-leadtek-y04g0051"
|
|
#define RC_MAP_LIRC "rc-lirc"
|
|
Index: linux-3.3.x86_64/drivers/media/video/bt8xx/bttv-driver.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/bt8xx/bttv-driver.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/bt8xx/bttv-driver.c
|
|
@@ -2035,11 +2035,7 @@ static int bttv_log_status(struct file *
|
|
struct bttv_fh *fh = f;
|
|
struct bttv *btv = fh->btv;
|
|
|
|
- pr_info("%d: ======== START STATUS CARD #%d ========\n",
|
|
- btv->c.nr, btv->c.nr);
|
|
bttv_call_all(btv, core, log_status);
|
|
- pr_info("%d: ======== END STATUS CARD #%d ========\n",
|
|
- btv->c.nr, btv->c.nr);
|
|
return 0;
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/cx18/cx18-ioctl.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cx18/cx18-ioctl.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cx18/cx18-ioctl.c
|
|
@@ -1085,8 +1085,6 @@ static int cx18_log_status(struct file *
|
|
struct v4l2_audio audin;
|
|
int i;
|
|
|
|
- CX18_INFO("================= START STATUS CARD #%d "
|
|
- "=================\n", cx->instance);
|
|
CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name);
|
|
if (cx->hw_flags & CX18_HW_TVEEPROM) {
|
|
struct tveeprom tv;
|
|
@@ -1120,8 +1118,6 @@ static int cx18_log_status(struct file *
|
|
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
|
|
(long long)cx->mpg_data_received,
|
|
(long long)cx->vbi_data_inserted);
|
|
- CX18_INFO("================== END STATUS CARD #%d "
|
|
- "==================\n", cx->instance);
|
|
return 0;
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/pwc/pwc-v4l.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/pwc/pwc-v4l.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/pwc/pwc-v4l.c
|
|
@@ -1146,14 +1146,6 @@ leave:
|
|
return ret;
|
|
}
|
|
|
|
-static int pwc_log_status(struct file *file, void *priv)
|
|
-{
|
|
- struct pwc_device *pdev = video_drvdata(file);
|
|
-
|
|
- v4l2_ctrl_handler_log_status(&pdev->ctrl_handler, PWC_NAME);
|
|
- return 0;
|
|
-}
|
|
-
|
|
const struct v4l2_ioctl_ops pwc_ioctl_ops = {
|
|
.vidioc_querycap = pwc_querycap,
|
|
.vidioc_enum_input = pwc_enum_input,
|
|
@@ -1169,9 +1161,11 @@ const struct v4l2_ioctl_ops pwc_ioctl_op
|
|
.vidioc_dqbuf = pwc_dqbuf,
|
|
.vidioc_streamon = pwc_streamon,
|
|
.vidioc_streamoff = pwc_streamoff,
|
|
- .vidioc_log_status = pwc_log_status,
|
|
+ .vidioc_log_status = v4l2_ctrl_log_status,
|
|
.vidioc_enum_framesizes = pwc_enum_framesizes,
|
|
.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,
|
|
};
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7164/saa7164-encoder.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7164/saa7164-encoder.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7164/saa7164-encoder.c
|
|
@@ -791,11 +791,6 @@ static int vidioc_s_fmt_vid_cap(struct f
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_log_status(struct file *file, void *priv)
|
|
-{
|
|
- return 0;
|
|
-}
|
|
-
|
|
static int fill_queryctrl(struct saa7164_encoder_params *params,
|
|
struct v4l2_queryctrl *c)
|
|
{
|
|
@@ -1347,7 +1342,6 @@ static const struct v4l2_ioctl_ops mpeg_
|
|
.vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
|
|
.vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
|
|
.vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
|
|
- .vidioc_log_status = vidioc_log_status,
|
|
.vidioc_queryctrl = vidioc_queryctrl,
|
|
.vidioc_g_chip_ident = saa7164_g_chip_ident,
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7164/saa7164-vbi.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7164/saa7164-vbi.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7164/saa7164-vbi.c
|
|
@@ -730,11 +730,6 @@ static int vidioc_s_fmt_vid_cap(struct f
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_log_status(struct file *file, void *priv)
|
|
-{
|
|
- return 0;
|
|
-}
|
|
-
|
|
static int fill_queryctrl(struct saa7164_vbi_params *params,
|
|
struct v4l2_queryctrl *c)
|
|
{
|
|
@@ -1256,7 +1251,6 @@ static const struct v4l2_ioctl_ops vbi_i
|
|
.vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
|
|
.vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
|
|
.vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
|
|
- .vidioc_log_status = vidioc_log_status,
|
|
.vidioc_queryctrl = vidioc_queryctrl,
|
|
#if 0
|
|
.vidioc_g_chip_ident = saa7164_g_chip_ident,
|
|
Index: linux-3.3.x86_64/include/media/v4l2-ctrls.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/media/v4l2-ctrls.h
|
|
+++ linux-3.3.x86_64/include/media/v4l2-ctrls.h
|
|
@@ -33,6 +33,7 @@ struct video_device;
|
|
struct v4l2_subdev;
|
|
struct v4l2_subscribed_event;
|
|
struct v4l2_fh;
|
|
+struct poll_table_struct;
|
|
|
|
/** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
|
|
* @g_volatile_ctrl: Get a new value for this control. Generally only relevant
|
|
@@ -492,6 +493,18 @@ void v4l2_ctrl_add_event(struct v4l2_ctr
|
|
void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
|
|
struct v4l2_subscribed_event *sev);
|
|
|
|
+/* 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);
|
|
+
|
|
+/* Can be used as a vidioc_subscribe_event function that just subscribes
|
|
+ control events. */
|
|
+int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
|
|
+ struct v4l2_event_subscription *sub);
|
|
+
|
|
+/* Can be used as a poll function that just polls for control events. */
|
|
+unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait);
|
|
+
|
|
/* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */
|
|
int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc);
|
|
int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm);
|
|
Index: linux-3.3.x86_64/drivers/media/video/v4l2-subdev.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/v4l2-subdev.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/v4l2-subdev.c
|
|
@@ -194,8 +194,16 @@ static long subdev_do_ioctl(struct file
|
|
}
|
|
#endif
|
|
|
|
- case VIDIOC_LOG_STATUS:
|
|
- return v4l2_subdev_call(sd, core, log_status);
|
|
+ case VIDIOC_LOG_STATUS: {
|
|
+ int ret;
|
|
+
|
|
+ pr_info("%s: ================= START STATUS =================\n",
|
|
+ sd->name);
|
|
+ ret = v4l2_subdev_call(sd, core, log_status);
|
|
+ pr_info("%s: ================== END STATUS ==================\n",
|
|
+ sd->name);
|
|
+ return ret;
|
|
+ }
|
|
|
|
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
|
|
case VIDIOC_SUBDEV_G_FMT: {
|
|
Index: linux-3.3.x86_64/drivers/media/radio/Kconfig
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/Kconfig
|
|
+++ linux-3.3.x86_64/drivers/media/radio/Kconfig
|
|
@@ -43,7 +43,7 @@ config USB_DSBR
|
|
|
|
config RADIO_MAXIRADIO
|
|
tristate "Guillemot MAXI Radio FM 2000 radio"
|
|
- depends on VIDEO_V4L2 && PCI
|
|
+ depends on VIDEO_V4L2 && PCI && SND
|
|
---help---
|
|
Choose Y here if you have this radio card. This card may also be
|
|
found as Gemtek PCI FM.
|
|
@@ -80,6 +80,16 @@ config RADIO_SI4713
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-si4713.
|
|
|
|
+config USB_KEENE
|
|
+ tristate "Keene FM Transmitter USB support"
|
|
+ depends on USB && VIDEO_V4L2
|
|
+ ---help---
|
|
+ Say Y here if you want to connect this type of FM transmitter
|
|
+ to your computer's USB port.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called radio-keene.
|
|
+
|
|
config RADIO_TEA5764
|
|
tristate "TEA5764 I2C FM radio support"
|
|
depends on I2C && VIDEO_V4L2
|
|
@@ -167,6 +177,10 @@ menuconfig V4L_RADIO_ISA_DRIVERS
|
|
|
|
if V4L_RADIO_ISA_DRIVERS
|
|
|
|
+config RADIO_ISA
|
|
+ depends on ISA
|
|
+ tristate
|
|
+
|
|
config RADIO_CADET
|
|
tristate "ADS Cadet AM/FM Tuner"
|
|
depends on ISA && VIDEO_V4L2
|
|
@@ -174,20 +188,13 @@ config RADIO_CADET
|
|
Choose Y here if you have one of these AM/FM radio cards, and then
|
|
fill in the port address below.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>.
|
|
-
|
|
- Further documentation on this driver can be found on the WWW at
|
|
- <http://linux.blackhawke.net/cadet/>.
|
|
-
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-cadet.
|
|
|
|
config RADIO_RTRACK
|
|
tristate "AIMSlab RadioTrack (aka RadioReveal) support"
|
|
depends on ISA && VIDEO_V4L2
|
|
+ select RADIO_ISA
|
|
---help---
|
|
Choose Y here if you have one of these FM radio cards, and then fill
|
|
in the port address below.
|
|
@@ -201,11 +208,7 @@ config RADIO_RTRACK
|
|
You must also pass the module a suitable io parameter, 0x248 has
|
|
been reported to be used by these cards.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>. More information is
|
|
- contained in the file
|
|
+ More information is contained in the file
|
|
<file:Documentation/video4linux/radiotrack.txt>.
|
|
|
|
To compile this driver as a module, choose M here: the
|
|
@@ -214,7 +217,7 @@ config RADIO_RTRACK
|
|
config RADIO_RTRACK_PORT
|
|
hex "RadioTrack i/o port (0x20f or 0x30f)"
|
|
depends on RADIO_RTRACK=y
|
|
- default "20f"
|
|
+ default "30f"
|
|
help
|
|
Enter either 0x30f or 0x20f here. The card default is 0x30f, if you
|
|
haven't changed the jumper setting on the card.
|
|
@@ -222,14 +225,14 @@ config RADIO_RTRACK_PORT
|
|
config RADIO_RTRACK2
|
|
tristate "AIMSlab RadioTrack II support"
|
|
depends on ISA && VIDEO_V4L2
|
|
+ select RADIO_ISA
|
|
---help---
|
|
Choose Y here if you have this FM radio card, and then fill in the
|
|
port address below.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>.
|
|
+ Note: this driver hasn't been tested since a long time due to lack
|
|
+ of hardware. If you have this hardware, then please contact the
|
|
+ linux-media mailinglist.
|
|
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-rtrack2.
|
|
@@ -245,15 +248,11 @@ config RADIO_RTRACK2_PORT
|
|
config RADIO_AZTECH
|
|
tristate "Aztech/Packard Bell Radio"
|
|
depends on ISA && VIDEO_V4L2
|
|
+ select RADIO_ISA
|
|
---help---
|
|
Choose Y here if you have one of these FM radio cards, and then fill
|
|
in the port address below.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>.
|
|
-
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-aztech.
|
|
|
|
@@ -269,6 +268,7 @@ config RADIO_AZTECH_PORT
|
|
config RADIO_GEMTEK
|
|
tristate "GemTek Radio card (or compatible) support"
|
|
depends on ISA && VIDEO_V4L2
|
|
+ select RADIO_ISA
|
|
---help---
|
|
Choose Y here if you have this FM radio card, and then fill in the
|
|
I/O port address and settings below. The following cards either have
|
|
@@ -278,23 +278,21 @@ config RADIO_GEMTEK
|
|
- Typhoon Radio card (some models)
|
|
- Hama Radio card
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>.
|
|
-
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-gemtek.
|
|
|
|
config RADIO_GEMTEK_PORT
|
|
- hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0c24c or 0x28c)"
|
|
+ hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c)"
|
|
depends on RADIO_GEMTEK=y
|
|
default "34c"
|
|
help
|
|
- Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is
|
|
- 0x34c, if you haven't changed the jumper setting on the card. On
|
|
- Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
|
|
+ Enter either 0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c here. The
|
|
+ card default is 0x34c, if you haven't changed the jumper setting
|
|
+ on the card.
|
|
+
|
|
+ On Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
|
|
port is 0x20c, 0x248 or 0x28c.
|
|
+
|
|
If automatic I/O port probing is enabled this port will be used only
|
|
in case of automatic probing failure, ie. as a fallback.
|
|
|
|
@@ -318,11 +316,6 @@ config RADIO_MIROPCM20
|
|
sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this
|
|
is required for the radio-miropcm20.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>.
|
|
-
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-miropcm20.
|
|
|
|
@@ -332,11 +325,6 @@ config RADIO_SF16FMI
|
|
---help---
|
|
Choose Y here if you have one of these FM radio cards.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>.
|
|
-
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-sf16fmi.
|
|
|
|
@@ -346,50 +334,35 @@ config RADIO_SF16FMR2
|
|
---help---
|
|
Choose Y here if you have one of these FM radio cards.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found on the WWW at
|
|
- <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
|
|
-
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-sf16fmr2.
|
|
|
|
config RADIO_TERRATEC
|
|
tristate "TerraTec ActiveRadio ISA Standalone"
|
|
depends on ISA && VIDEO_V4L2
|
|
+ select RADIO_ISA
|
|
---help---
|
|
- Choose Y here if you have this FM radio card, and then fill in the
|
|
- port address below. (TODO)
|
|
-
|
|
- Note: This driver is in its early stages. Right now volume and
|
|
- frequency control and muting works at least for me, but
|
|
- unfortunately I have not found anybody who wants to use this card
|
|
- with Linux. So if it is this what YOU are trying to do right now,
|
|
- PLEASE DROP ME A NOTE!! Rolf Offermanns <rolf@offermanns.de>.
|
|
+ Choose Y here if you have this FM radio card.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>.
|
|
+ Note: this driver hasn't been tested since a long time due to lack
|
|
+ of hardware. If you have this hardware, then please contact the
|
|
+ linux-media mailinglist.
|
|
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-terratec.
|
|
|
|
-config RADIO_TERRATEC_PORT
|
|
- hex "Terratec i/o port (normally 0x590)"
|
|
- depends on RADIO_TERRATEC=y
|
|
- default "590"
|
|
- help
|
|
- Fill in the I/O port of your TerraTec FM radio card. If unsure, go
|
|
- with the default.
|
|
-
|
|
config RADIO_TRUST
|
|
tristate "Trust FM radio card"
|
|
depends on ISA && VIDEO_V4L2
|
|
+ select RADIO_ISA
|
|
help
|
|
This is a driver for the Trust FM radio cards. Say Y if you have
|
|
such a card and want to use it under Linux.
|
|
|
|
+ Note: this driver hasn't been tested since a long time due to lack
|
|
+ of hardware. If you have this hardware, then please contact the
|
|
+ linux-media mailinglist.
|
|
+
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-trust.
|
|
|
|
@@ -404,14 +377,14 @@ config RADIO_TRUST_PORT
|
|
config RADIO_TYPHOON
|
|
tristate "Typhoon Radio (a.k.a. EcoRadio)"
|
|
depends on ISA && VIDEO_V4L2
|
|
+ select RADIO_ISA
|
|
---help---
|
|
Choose Y here if you have one of these FM radio cards, and then fill
|
|
in the port address and the frequency used for muting below.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>.
|
|
+ Note: this driver hasn't been tested since a long time due to lack
|
|
+ of hardware. If you have this hardware, then please contact the
|
|
+ linux-media mailinglist.
|
|
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-typhoon.
|
|
@@ -438,14 +411,14 @@ config RADIO_TYPHOON_MUTEFREQ
|
|
config RADIO_ZOLTRIX
|
|
tristate "Zoltrix Radio"
|
|
depends on ISA && VIDEO_V4L2
|
|
+ select RADIO_ISA
|
|
---help---
|
|
Choose Y here if you have one of these FM radio cards, and then fill
|
|
in the port address below.
|
|
|
|
- In order to control your radio card, you will need to use programs
|
|
- that are compatible with the Video For Linux API. Information on
|
|
- this API and pointers to "v4l" programs may be found at
|
|
- <file:Documentation/video4linux/API.html>.
|
|
+ Note: this driver hasn't been tested since a long time due to lack
|
|
+ of hardware. If you have this hardware, then please contact the
|
|
+ linux-media mailinglist.
|
|
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called radio-zoltrix.
|
|
Index: linux-3.3.x86_64/drivers/media/radio/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/radio/Makefile
|
|
@@ -2,6 +2,7 @@
|
|
# Makefile for the kernel character device drivers.
|
|
#
|
|
|
|
+obj-$(CONFIG_RADIO_ISA) += radio-isa.o
|
|
obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
|
|
obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o
|
|
obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o
|
|
@@ -20,6 +21,7 @@ obj-$(CONFIG_RADIO_MIROPCM20) += radio-m
|
|
obj-$(CONFIG_USB_DSBR) += dsbr100.o
|
|
obj-$(CONFIG_RADIO_SI470X) += si470x/
|
|
obj-$(CONFIG_USB_MR800) += radio-mr800.o
|
|
+obj-$(CONFIG_USB_KEENE) += radio-keene.o
|
|
obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
|
|
obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o
|
|
obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-keene.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-keene.c
|
|
@@ -0,0 +1,427 @@
|
|
+/*
|
|
+ * Copyright (c) 2012 Hans Verkuil <hverkuil@xs4all.nl>
|
|
+ *
|
|
+ * 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
|
|
+ */
|
|
+
|
|
+/* kernel includes */
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/input.h>
|
|
+#include <linux/videodev2.h>
|
|
+#include <media/v4l2-device.h>
|
|
+#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-event.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/mutex.h>
|
|
+
|
|
+/* driver and module definitions */
|
|
+MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
|
|
+MODULE_DESCRIPTION("Keene FM Transmitter driver");
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+/* Actually, it advertises itself as a Logitech */
|
|
+#define USB_KEENE_VENDOR 0x046d
|
|
+#define USB_KEENE_PRODUCT 0x0a0e
|
|
+
|
|
+/* Probably USB_TIMEOUT should be modified in module parameter */
|
|
+#define BUFFER_LENGTH 8
|
|
+#define USB_TIMEOUT 500
|
|
+
|
|
+/* Frequency limits in MHz */
|
|
+#define FREQ_MIN 76U
|
|
+#define FREQ_MAX 108U
|
|
+#define FREQ_MUL 16000U
|
|
+
|
|
+/* USB Device ID List */
|
|
+static struct usb_device_id usb_keene_device_table[] = {
|
|
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_KEENE_VENDOR, USB_KEENE_PRODUCT,
|
|
+ USB_CLASS_HID, 0, 0) },
|
|
+ { } /* Terminating entry */
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(usb, usb_keene_device_table);
|
|
+
|
|
+struct keene_device {
|
|
+ struct usb_device *usbdev;
|
|
+ struct usb_interface *intf;
|
|
+ struct video_device vdev;
|
|
+ struct v4l2_device v4l2_dev;
|
|
+ struct v4l2_ctrl_handler hdl;
|
|
+ struct mutex lock;
|
|
+
|
|
+ u8 *buffer;
|
|
+ unsigned curfreq;
|
|
+ u8 tx;
|
|
+ u8 pa;
|
|
+ bool stereo;
|
|
+ bool muted;
|
|
+ bool preemph_75_us;
|
|
+};
|
|
+
|
|
+static inline struct keene_device *to_keene_dev(struct v4l2_device *v4l2_dev)
|
|
+{
|
|
+ return container_of(v4l2_dev, struct keene_device, v4l2_dev);
|
|
+}
|
|
+
|
|
+/* Set frequency (if non-0), PA, mute and turn on/off the FM transmitter. */
|
|
+static int keene_cmd_main(struct keene_device *radio, unsigned freq, bool play)
|
|
+{
|
|
+ unsigned short freq_send = freq ? (freq - 76 * 16000) / 800 : 0;
|
|
+ int ret;
|
|
+
|
|
+ radio->buffer[0] = 0x00;
|
|
+ radio->buffer[1] = 0x50;
|
|
+ radio->buffer[2] = (freq_send >> 8) & 0xff;
|
|
+ radio->buffer[3] = freq_send & 0xff;
|
|
+ radio->buffer[4] = radio->pa;
|
|
+ /* If bit 4 is set, then tune to the frequency.
|
|
+ If bit 3 is set, then unmute; if bit 2 is set, then mute.
|
|
+ If bit 1 is set, then enter idle mode; if bit 0 is set,
|
|
+ then enter transit mode.
|
|
+ */
|
|
+ radio->buffer[5] = (radio->muted ? 4 : 8) | (play ? 1 : 2) |
|
|
+ (freq ? 0x10 : 0);
|
|
+ radio->buffer[6] = 0x00;
|
|
+ radio->buffer[7] = 0x00;
|
|
+
|
|
+ ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
|
|
+ 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
|
|
+
|
|
+ if (ret < 0) {
|
|
+ dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
|
|
+ return ret;
|
|
+ }
|
|
+ if (freq)
|
|
+ radio->curfreq = freq;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Set TX, stereo and preemphasis mode (50 us vs 75 us). */
|
|
+static int keene_cmd_set(struct keene_device *radio)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ radio->buffer[0] = 0x00;
|
|
+ radio->buffer[1] = 0x51;
|
|
+ radio->buffer[2] = radio->tx;
|
|
+ /* If bit 0 is set, then transmit mono, otherwise stereo.
|
|
+ If bit 2 is set, then enable 75 us preemphasis, otherwise
|
|
+ it is 50 us. */
|
|
+ radio->buffer[3] = (!radio->stereo) | (radio->preemph_75_us ? 4 : 0);
|
|
+ radio->buffer[4] = 0x00;
|
|
+ radio->buffer[5] = 0x00;
|
|
+ radio->buffer[6] = 0x00;
|
|
+ radio->buffer[7] = 0x00;
|
|
+
|
|
+ ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
|
|
+ 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
|
|
+
|
|
+ if (ret < 0) {
|
|
+ dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
|
|
+ return ret;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Handle unplugging the device.
|
|
+ * We call video_unregister_device in any case.
|
|
+ * The last function called in this procedure is
|
|
+ * usb_keene_device_release.
|
|
+ */
|
|
+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);
|
|
+ v4l2_device_disconnect(&radio->v4l2_dev);
|
|
+ mutex_unlock(&radio->lock);
|
|
+ v4l2_device_put(&radio->v4l2_dev);
|
|
+}
|
|
+
|
|
+static int vidioc_querycap(struct file *file, void *priv,
|
|
+ struct v4l2_capability *v)
|
|
+{
|
|
+ struct keene_device *radio = video_drvdata(file);
|
|
+
|
|
+ strlcpy(v->driver, "radio-keene", sizeof(v->driver));
|
|
+ strlcpy(v->card, "Keene FM Transmitter", sizeof(v->card));
|
|
+ usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
|
|
+ v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_MODULATOR;
|
|
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_g_modulator(struct file *file, void *priv,
|
|
+ struct v4l2_modulator *v)
|
|
+{
|
|
+ struct keene_device *radio = video_drvdata(file);
|
|
+
|
|
+ if (v->index > 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ strlcpy(v->name, "FM", sizeof(v->name));
|
|
+ v->rangelow = FREQ_MIN * FREQ_MUL;
|
|
+ v->rangehigh = FREQ_MAX * FREQ_MUL;
|
|
+ v->txsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
|
|
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_s_modulator(struct file *file, void *priv,
|
|
+ struct v4l2_modulator *v)
|
|
+{
|
|
+ struct keene_device *radio = video_drvdata(file);
|
|
+
|
|
+ if (v->index > 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ radio->stereo = (v->txsubchans == V4L2_TUNER_SUB_STEREO);
|
|
+ return keene_cmd_set(radio);
|
|
+}
|
|
+
|
|
+static int vidioc_s_frequency(struct file *file, void *priv,
|
|
+ struct v4l2_frequency *f)
|
|
+{
|
|
+ struct keene_device *radio = video_drvdata(file);
|
|
+
|
|
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
+ return -EINVAL;
|
|
+ f->frequency = clamp(f->frequency,
|
|
+ FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL);
|
|
+ return keene_cmd_main(radio, f->frequency, true);
|
|
+}
|
|
+
|
|
+static int vidioc_g_frequency(struct file *file, void *priv,
|
|
+ struct v4l2_frequency *f)
|
|
+{
|
|
+ struct keene_device *radio = video_drvdata(file);
|
|
+
|
|
+ if (f->tuner != 0)
|
|
+ return -EINVAL;
|
|
+ f->type = V4L2_TUNER_RADIO;
|
|
+ f->frequency = radio->curfreq;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int keene_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ static const u8 db2tx[] = {
|
|
+ /* -15, -12, -9, -6, -3, 0 dB */
|
|
+ 0x03, 0x13, 0x02, 0x12, 0x22, 0x32,
|
|
+ /* 3, 6, 9, 12, 15, 18 dB */
|
|
+ 0x21, 0x31, 0x20, 0x30, 0x40, 0x50
|
|
+ };
|
|
+ struct keene_device *radio =
|
|
+ container_of(ctrl->handler, struct keene_device, hdl);
|
|
+
|
|
+ switch (ctrl->id) {
|
|
+ case V4L2_CID_AUDIO_MUTE:
|
|
+ radio->muted = ctrl->val;
|
|
+ return keene_cmd_main(radio, 0, true);
|
|
+
|
|
+ case V4L2_CID_TUNE_POWER_LEVEL:
|
|
+ /* To go from dBuV to the register value we apply the
|
|
+ following formula: */
|
|
+ radio->pa = (ctrl->val - 71) * 100 / 62;
|
|
+ return keene_cmd_main(radio, 0, true);
|
|
+
|
|
+ case V4L2_CID_TUNE_PREEMPHASIS:
|
|
+ radio->preemph_75_us = ctrl->val == V4L2_PREEMPHASIS_75_uS;
|
|
+ return keene_cmd_set(radio);
|
|
+
|
|
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN:
|
|
+ radio->tx = db2tx[(ctrl->val - ctrl->minimum) / ctrl->step];
|
|
+ return keene_cmd_set(radio);
|
|
+ }
|
|
+ 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,
|
|
+ .open = v4l2_fh_open,
|
|
+ .release = v4l2_fh_release,
|
|
+ .poll = v4l2_ctrl_poll,
|
|
+ .unlocked_ioctl = video_ioctl2,
|
|
+};
|
|
+
|
|
+static const struct v4l2_ctrl_ops keene_ctrl_ops = {
|
|
+ .s_ctrl = keene_s_ctrl,
|
|
+};
|
|
+
|
|
+static const struct v4l2_ioctl_ops usb_keene_ioctl_ops = {
|
|
+ .vidioc_querycap = vidioc_querycap,
|
|
+ .vidioc_g_modulator = vidioc_g_modulator,
|
|
+ .vidioc_s_modulator = vidioc_s_modulator,
|
|
+ .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_unsubscribe_event = v4l2_event_unsubscribe,
|
|
+};
|
|
+
|
|
+static void usb_keene_video_device_release(struct v4l2_device *v4l2_dev)
|
|
+{
|
|
+ struct keene_device *radio = to_keene_dev(v4l2_dev);
|
|
+
|
|
+ /* free rest memory */
|
|
+ v4l2_ctrl_handler_free(&radio->hdl);
|
|
+ kfree(radio->buffer);
|
|
+ kfree(radio);
|
|
+}
|
|
+
|
|
+/* check if the device is present and register with v4l and usb if it is */
|
|
+static int usb_keene_probe(struct usb_interface *intf,
|
|
+ const struct usb_device_id *id)
|
|
+{
|
|
+ struct usb_device *dev = interface_to_usbdev(intf);
|
|
+ struct keene_device *radio;
|
|
+ struct v4l2_ctrl_handler *hdl;
|
|
+ int retval = 0;
|
|
+
|
|
+ /*
|
|
+ * The Keene FM transmitter USB device has the same USB ID as
|
|
+ * the Logitech AudioHub Speaker, but it should ignore the hid.
|
|
+ * Check if the name is that of the Keene device.
|
|
+ * If not, then someone connected the AudioHub and we shouldn't
|
|
+ * attempt to handle this driver.
|
|
+ * For reference: the product name of the AudioHub is
|
|
+ * "AudioHub Speaker".
|
|
+ */
|
|
+ if (dev->product && strcmp(dev->product, "B-LINK USB Audio "))
|
|
+ return -ENODEV;
|
|
+
|
|
+ radio = kzalloc(sizeof(struct keene_device), GFP_KERNEL);
|
|
+ if (radio)
|
|
+ radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
|
|
+
|
|
+ if (!radio || !radio->buffer) {
|
|
+ dev_err(&intf->dev, "kmalloc for keene_device failed\n");
|
|
+ kfree(radio);
|
|
+ retval = -ENOMEM;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ hdl = &radio->hdl;
|
|
+ v4l2_ctrl_handler_init(hdl, 4);
|
|
+ v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_MUTE,
|
|
+ 0, 1, 1, 0);
|
|
+ v4l2_ctrl_new_std_menu(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS,
|
|
+ V4L2_PREEMPHASIS_75_uS, 1, V4L2_PREEMPHASIS_50_uS);
|
|
+ v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_POWER_LEVEL,
|
|
+ 84, 118, 1, 118);
|
|
+ v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_COMPRESSION_GAIN,
|
|
+ -15, 18, 3, 0);
|
|
+ radio->pa = 118;
|
|
+ radio->tx = 0x32;
|
|
+ radio->stereo = true;
|
|
+ radio->curfreq = 95.16 * FREQ_MUL;
|
|
+ if (hdl->error) {
|
|
+ retval = hdl->error;
|
|
+
|
|
+ v4l2_ctrl_handler_free(hdl);
|
|
+ goto err_v4l2;
|
|
+ }
|
|
+ retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
|
|
+ if (retval < 0) {
|
|
+ dev_err(&intf->dev, "couldn't register v4l2_device\n");
|
|
+ goto err_v4l2;
|
|
+ }
|
|
+
|
|
+ mutex_init(&radio->lock);
|
|
+
|
|
+ radio->v4l2_dev.ctrl_handler = hdl;
|
|
+ radio->v4l2_dev.release = usb_keene_video_device_release;
|
|
+ strlcpy(radio->vdev.name, radio->v4l2_dev.name,
|
|
+ sizeof(radio->vdev.name));
|
|
+ radio->vdev.v4l2_dev = &radio->v4l2_dev;
|
|
+ radio->vdev.fops = &usb_keene_fops;
|
|
+ radio->vdev.ioctl_ops = &usb_keene_ioctl_ops;
|
|
+ radio->vdev.lock = &radio->lock;
|
|
+ radio->vdev.release = video_device_release_empty;
|
|
+
|
|
+ radio->usbdev = interface_to_usbdev(intf);
|
|
+ radio->intf = intf;
|
|
+ usb_set_intfdata(intf, &radio->v4l2_dev);
|
|
+
|
|
+ video_set_drvdata(&radio->vdev, radio);
|
|
+ set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
|
|
+
|
|
+ retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
|
|
+ if (retval < 0) {
|
|
+ dev_err(&intf->dev, "could not register video device\n");
|
|
+ goto err_vdev;
|
|
+ }
|
|
+ v4l2_ctrl_handler_setup(hdl);
|
|
+ dev_info(&intf->dev, "V4L2 device registered as %s\n",
|
|
+ video_device_node_name(&radio->vdev));
|
|
+ return 0;
|
|
+
|
|
+err_vdev:
|
|
+ v4l2_device_unregister(&radio->v4l2_dev);
|
|
+err_v4l2:
|
|
+ kfree(radio->buffer);
|
|
+ kfree(radio);
|
|
+err:
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/* USB subsystem interface */
|
|
+static struct usb_driver usb_keene_driver = {
|
|
+ .name = "radio-keene",
|
|
+ .probe = usb_keene_probe,
|
|
+ .disconnect = usb_keene_disconnect,
|
|
+ .id_table = usb_keene_device_table,
|
|
+};
|
|
+
|
|
+static int __init keene_init(void)
|
|
+{
|
|
+ int retval = usb_register(&usb_keene_driver);
|
|
+
|
|
+ if (retval)
|
|
+ pr_err(KBUILD_MODNAME
|
|
+ ": usb_register failed. Error number %d\n", retval);
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static void __exit keene_exit(void)
|
|
+{
|
|
+ usb_deregister(&usb_keene_driver);
|
|
+}
|
|
+
|
|
+module_init(keene_init);
|
|
+module_exit(keene_exit);
|
|
+
|
|
Index: linux-3.3.x86_64/drivers/media/rc/fintek-cir.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/fintek-cir.c
|
|
+++ linux-3.3.x86_64/drivers/media/rc/fintek-cir.c
|
|
@@ -117,7 +117,7 @@ static u8 fintek_cir_reg_read(struct fin
|
|
static void cir_dump_regs(struct fintek_dev *fintek)
|
|
{
|
|
fintek_config_mode_enable(fintek);
|
|
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
|
|
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
|
|
|
|
pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
|
|
pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
|
|
@@ -143,7 +143,7 @@ static int fintek_hw_detect(struct finte
|
|
u8 chip_major, chip_minor;
|
|
u8 vendor_major, vendor_minor;
|
|
u8 portsel, ir_class;
|
|
- u16 vendor;
|
|
+ u16 vendor, chip;
|
|
int ret = 0;
|
|
|
|
fintek_config_mode_enable(fintek);
|
|
@@ -176,6 +176,7 @@ static int fintek_hw_detect(struct finte
|
|
|
|
chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI);
|
|
chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO);
|
|
+ chip = chip_major << 8 | chip_minor;
|
|
|
|
vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI);
|
|
vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO);
|
|
@@ -192,6 +193,15 @@ static int fintek_hw_detect(struct finte
|
|
fintek->chip_major = chip_major;
|
|
fintek->chip_minor = chip_minor;
|
|
fintek->chip_vendor = vendor;
|
|
+
|
|
+ /*
|
|
+ * Newer reviews of this chipset uses port 8 instead of 5
|
|
+ */
|
|
+ if ((chip != 0x0408) || (chip != 0x0804))
|
|
+ fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV2;
|
|
+ else
|
|
+ fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV1;
|
|
+
|
|
spin_unlock_irqrestore(&fintek->fintek_lock, flags);
|
|
|
|
return ret;
|
|
@@ -200,7 +210,7 @@ static int fintek_hw_detect(struct finte
|
|
static void fintek_cir_ldev_init(struct fintek_dev *fintek)
|
|
{
|
|
/* Select CIR logical device and enable */
|
|
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
|
|
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
|
|
fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
|
|
|
|
/* Write allocated CIR address and IRQ information to hardware */
|
|
@@ -381,7 +391,7 @@ static irqreturn_t fintek_cir_isr(int ir
|
|
fit_dbg_verbose("%s firing", __func__);
|
|
|
|
fintek_config_mode_enable(fintek);
|
|
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
|
|
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
|
|
fintek_config_mode_disable(fintek);
|
|
|
|
/*
|
|
@@ -422,7 +432,7 @@ static void fintek_enable_cir(struct fin
|
|
fintek_config_mode_enable(fintek);
|
|
|
|
/* enable the CIR logical device */
|
|
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
|
|
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
|
|
fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
|
|
|
|
fintek_config_mode_disable(fintek);
|
|
@@ -439,7 +449,7 @@ static void fintek_disable_cir(struct fi
|
|
fintek_config_mode_enable(fintek);
|
|
|
|
/* disable the CIR logical device */
|
|
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
|
|
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
|
|
fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
|
|
|
|
fintek_config_mode_disable(fintek);
|
|
@@ -611,7 +621,7 @@ static int fintek_suspend(struct pnp_dev
|
|
fintek_config_mode_enable(fintek);
|
|
|
|
/* disable cir logical dev */
|
|
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
|
|
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
|
|
fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
|
|
|
|
fintek_config_mode_disable(fintek);
|
|
@@ -634,7 +644,7 @@ static int fintek_resume(struct pnp_dev
|
|
|
|
/* Enable CIR logical device */
|
|
fintek_config_mode_enable(fintek);
|
|
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
|
|
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
|
|
fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
|
|
|
|
fintek_config_mode_disable(fintek);
|
|
Index: linux-3.3.x86_64/drivers/media/rc/fintek-cir.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/fintek-cir.h
|
|
+++ linux-3.3.x86_64/drivers/media/rc/fintek-cir.h
|
|
@@ -88,6 +88,7 @@ struct fintek_dev {
|
|
u8 chip_major;
|
|
u8 chip_minor;
|
|
u16 chip_vendor;
|
|
+ u8 logical_dev_cir;
|
|
|
|
/* hardware features */
|
|
bool hw_learning_capable;
|
|
@@ -172,7 +173,8 @@ struct fintek_dev {
|
|
#define LOGICAL_DEV_ENABLE 0x01
|
|
|
|
/* Logical device number of the CIR function */
|
|
-#define LOGICAL_DEV_CIR 0x05
|
|
+#define LOGICAL_DEV_CIR_REV1 0x05
|
|
+#define LOGICAL_DEV_CIR_REV2 0x08
|
|
|
|
/* CIR Logical Device (LDN 0x08) config registers */
|
|
#define CIR_CR_COMMAND_INDEX 0x04
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-isa.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-isa.c
|
|
@@ -0,0 +1,340 @@
|
|
+/*
|
|
+ * Framework for ISA radio drivers.
|
|
+ * This takes care of all the V4L2 scaffolding, allowing the ISA drivers
|
|
+ * to concentrate on the actual hardware operation.
|
|
+ *
|
|
+ * Copyright (C) 2012 Hans Verkuil <hans.verkuil@cisco.com>
|
|
+ *
|
|
+ * 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 <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/ioport.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/videodev2.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/slab.h>
|
|
+#include <media/v4l2-device.h>
|
|
+#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-fh.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-event.h>
|
|
+
|
|
+#include "radio-isa.h"
|
|
+
|
|
+MODULE_AUTHOR("Hans Verkuil");
|
|
+MODULE_DESCRIPTION("A framework for ISA radio drivers.");
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+#define FREQ_LOW (87U * 16000U)
|
|
+#define FREQ_HIGH (108U * 16000U)
|
|
+
|
|
+static int radio_isa_querycap(struct file *file, void *priv,
|
|
+ struct v4l2_capability *v)
|
|
+{
|
|
+ struct radio_isa_card *isa = video_drvdata(file);
|
|
+
|
|
+ strlcpy(v->driver, isa->drv->driver.driver.name, sizeof(v->driver));
|
|
+ strlcpy(v->card, isa->drv->card, sizeof(v->card));
|
|
+ snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", isa->v4l2_dev.name);
|
|
+
|
|
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
+ v->device_caps = v->capabilities | V4L2_CAP_DEVICE_CAPS;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_isa_g_tuner(struct file *file, void *priv,
|
|
+ struct v4l2_tuner *v)
|
|
+{
|
|
+ struct radio_isa_card *isa = video_drvdata(file);
|
|
+ const struct radio_isa_ops *ops = isa->drv->ops;
|
|
+
|
|
+ if (v->index > 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ strlcpy(v->name, "FM", sizeof(v->name));
|
|
+ v->type = V4L2_TUNER_RADIO;
|
|
+ v->rangelow = FREQ_LOW;
|
|
+ v->rangehigh = FREQ_HIGH;
|
|
+ v->capability = V4L2_TUNER_CAP_LOW;
|
|
+ if (isa->drv->has_stereo)
|
|
+ v->capability |= V4L2_TUNER_CAP_STEREO;
|
|
+
|
|
+ if (ops->g_rxsubchans)
|
|
+ v->rxsubchans = ops->g_rxsubchans(isa);
|
|
+ else
|
|
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
|
|
+ v->audmode = isa->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
|
|
+ if (ops->g_signal)
|
|
+ v->signal = ops->g_signal(isa);
|
|
+ else
|
|
+ v->signal = (v->rxsubchans & V4L2_TUNER_SUB_STEREO) ?
|
|
+ 0xffff : 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_isa_s_tuner(struct file *file, void *priv,
|
|
+ struct v4l2_tuner *v)
|
|
+{
|
|
+ struct radio_isa_card *isa = video_drvdata(file);
|
|
+ const struct radio_isa_ops *ops = isa->drv->ops;
|
|
+
|
|
+ if (v->index)
|
|
+ return -EINVAL;
|
|
+ if (ops->s_stereo) {
|
|
+ isa->stereo = (v->audmode == V4L2_TUNER_MODE_STEREO);
|
|
+ return ops->s_stereo(isa, isa->stereo);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_isa_s_frequency(struct file *file, void *priv,
|
|
+ struct v4l2_frequency *f)
|
|
+{
|
|
+ struct radio_isa_card *isa = video_drvdata(file);
|
|
+ int res;
|
|
+
|
|
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
+ return -EINVAL;
|
|
+ f->frequency = clamp(f->frequency, FREQ_LOW, FREQ_HIGH);
|
|
+ res = isa->drv->ops->s_frequency(isa, f->frequency);
|
|
+ if (res == 0)
|
|
+ isa->freq = f->frequency;
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static int radio_isa_g_frequency(struct file *file, void *priv,
|
|
+ struct v4l2_frequency *f)
|
|
+{
|
|
+ struct radio_isa_card *isa = video_drvdata(file);
|
|
+
|
|
+ if (f->tuner != 0)
|
|
+ return -EINVAL;
|
|
+ f->type = V4L2_TUNER_RADIO;
|
|
+ f->frequency = isa->freq;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_isa_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ struct radio_isa_card *isa =
|
|
+ container_of(ctrl->handler, struct radio_isa_card, hdl);
|
|
+
|
|
+ switch (ctrl->id) {
|
|
+ case V4L2_CID_AUDIO_MUTE:
|
|
+ return isa->drv->ops->s_mute_volume(isa, ctrl->val,
|
|
+ isa->volume ? isa->volume->val : 0);
|
|
+ }
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int radio_isa_log_status(struct file *file, void *priv)
|
|
+{
|
|
+ struct radio_isa_card *isa = video_drvdata(file);
|
|
+
|
|
+ v4l2_info(&isa->v4l2_dev, "I/O Port = 0x%03x\n", isa->io);
|
|
+ v4l2_ctrl_handler_log_status(&isa->hdl, isa->v4l2_dev.name);
|
|
+ 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,
|
|
+};
|
|
+
|
|
+static const struct v4l2_file_operations radio_isa_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = v4l2_fh_open,
|
|
+ .release = v4l2_fh_release,
|
|
+ .poll = v4l2_ctrl_poll,
|
|
+ .unlocked_ioctl = video_ioctl2,
|
|
+};
|
|
+
|
|
+static const struct v4l2_ioctl_ops radio_isa_ioctl_ops = {
|
|
+ .vidioc_querycap = radio_isa_querycap,
|
|
+ .vidioc_g_tuner = radio_isa_g_tuner,
|
|
+ .vidioc_s_tuner = radio_isa_s_tuner,
|
|
+ .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_unsubscribe_event = v4l2_event_unsubscribe,
|
|
+};
|
|
+
|
|
+int radio_isa_match(struct device *pdev, unsigned int dev)
|
|
+{
|
|
+ struct radio_isa_driver *drv = pdev->platform_data;
|
|
+
|
|
+ return drv->probe || drv->io_params[dev] >= 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(radio_isa_match);
|
|
+
|
|
+static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < drv->num_of_io_ports; i++)
|
|
+ if (drv->io_ports[i] == io)
|
|
+ return true;
|
|
+ return false;
|
|
+}
|
|
+
|
|
+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;
|
|
+ int res;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) {
|
|
+ v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io);
|
|
+ kfree(isa);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ res = v4l2_device_register(pdev, v4l2_dev);
|
|
+ if (res < 0) {
|
|
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
+ goto err_dev_reg;
|
|
+ }
|
|
+
|
|
+ v4l2_ctrl_handler_init(&isa->hdl, 1);
|
|
+ isa->mute = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops,
|
|
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
|
|
+ if (drv->max_volume)
|
|
+ isa->volume = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops,
|
|
+ V4L2_CID_AUDIO_VOLUME, 0, drv->max_volume, 1,
|
|
+ drv->max_volume);
|
|
+ v4l2_dev->ctrl_handler = &isa->hdl;
|
|
+ if (isa->hdl.error) {
|
|
+ res = isa->hdl.error;
|
|
+ v4l2_err(v4l2_dev, "Could not register controls\n");
|
|
+ goto err_hdl;
|
|
+ }
|
|
+ if (drv->max_volume)
|
|
+ v4l2_ctrl_cluster(2, &isa->mute);
|
|
+ v4l2_dev->ctrl_handler = &isa->hdl;
|
|
+
|
|
+ mutex_init(&isa->lock);
|
|
+ isa->vdev.lock = &isa->lock;
|
|
+ strlcpy(isa->vdev.name, v4l2_dev->name, sizeof(isa->vdev.name));
|
|
+ isa->vdev.v4l2_dev = v4l2_dev;
|
|
+ isa->vdev.fops = &radio_isa_fops;
|
|
+ isa->vdev.ioctl_ops = &radio_isa_ioctl_ops;
|
|
+ isa->vdev.release = video_device_release_empty;
|
|
+ set_bit(V4L2_FL_USE_FH_PRIO, &isa->vdev.flags);
|
|
+ video_set_drvdata(&isa->vdev, isa);
|
|
+ isa->freq = FREQ_LOW;
|
|
+ isa->stereo = drv->has_stereo;
|
|
+
|
|
+ if (ops->init)
|
|
+ res = ops->init(isa);
|
|
+ if (!res)
|
|
+ res = v4l2_ctrl_handler_setup(&isa->hdl);
|
|
+ if (!res)
|
|
+ res = ops->s_frequency(isa, isa->freq);
|
|
+ if (!res && ops->s_stereo)
|
|
+ res = ops->s_stereo(isa, isa->stereo);
|
|
+ if (res < 0) {
|
|
+ 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]);
|
|
+ if (res < 0) {
|
|
+ v4l2_err(v4l2_dev, "Could not register device node\n");
|
|
+ goto err_node_reg;
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+err_dev_reg:
|
|
+ release_region(isa->io, drv->region_size);
|
|
+ kfree(isa);
|
|
+ return res;
|
|
+}
|
|
+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);
|
|
+ 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);
|
|
+ v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card);
|
|
+ kfree(isa);
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(radio_isa_remove);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-isa.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-isa.h
|
|
@@ -0,0 +1,105 @@
|
|
+/*
|
|
+ * Framework for ISA radio drivers.
|
|
+ * This takes care of all the V4L2 scaffolding, allowing the ISA drivers
|
|
+ * to concentrate on the actual hardware operation.
|
|
+ *
|
|
+ * Copyright (C) 2012 Hans Verkuil <hans.verkuil@cisco.com>
|
|
+ *
|
|
+ * 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 _RADIO_ISA_H_
|
|
+#define _RADIO_ISA_H_
|
|
+
|
|
+#include <linux/isa.h>
|
|
+#include <linux/videodev2.h>
|
|
+#include <media/v4l2-device.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+
|
|
+struct radio_isa_driver;
|
|
+struct radio_isa_ops;
|
|
+
|
|
+/* Core structure for radio ISA cards */
|
|
+struct radio_isa_card {
|
|
+ const struct radio_isa_driver *drv;
|
|
+ struct v4l2_device v4l2_dev;
|
|
+ struct v4l2_ctrl_handler hdl;
|
|
+ struct video_device vdev;
|
|
+ struct mutex lock;
|
|
+ const struct radio_isa_ops *ops;
|
|
+ struct { /* mute/volume cluster */
|
|
+ struct v4l2_ctrl *mute;
|
|
+ struct v4l2_ctrl *volume;
|
|
+ };
|
|
+ /* I/O port */
|
|
+ int io;
|
|
+
|
|
+ /* Card is in stereo audio mode */
|
|
+ bool stereo;
|
|
+ /* Current frequency */
|
|
+ u32 freq;
|
|
+};
|
|
+
|
|
+struct radio_isa_ops {
|
|
+ /* Allocate and initialize a radio_isa_card struct */
|
|
+ struct radio_isa_card *(*alloc)(void);
|
|
+ /* Probe whether a card is present at the given port */
|
|
+ bool (*probe)(struct radio_isa_card *isa, int io);
|
|
+ /* Special card initialization can be done here, this is called after
|
|
+ * the standard controls are registered, but before they are setup,
|
|
+ * thus allowing drivers to add their own controls here. */
|
|
+ int (*init)(struct radio_isa_card *isa);
|
|
+ /* Set mute and volume. */
|
|
+ int (*s_mute_volume)(struct radio_isa_card *isa, bool mute, int volume);
|
|
+ /* Set frequency */
|
|
+ int (*s_frequency)(struct radio_isa_card *isa, u32 freq);
|
|
+ /* Set stereo/mono audio mode */
|
|
+ int (*s_stereo)(struct radio_isa_card *isa, bool stereo);
|
|
+ /* Get rxsubchans value for VIDIOC_G_TUNER */
|
|
+ u32 (*g_rxsubchans)(struct radio_isa_card *isa);
|
|
+ /* Get the signal strength for VIDIOC_G_TUNER */
|
|
+ u32 (*g_signal)(struct radio_isa_card *isa);
|
|
+};
|
|
+
|
|
+/* Top level structure needed to instantiate the cards */
|
|
+struct radio_isa_driver {
|
|
+ struct isa_driver driver;
|
|
+ const struct radio_isa_ops *ops;
|
|
+ /* The module_param_array with the specified I/O ports */
|
|
+ int *io_params;
|
|
+ /* The module_param_array with the radio_nr values */
|
|
+ int *radio_nr_params;
|
|
+ /* Whether we should probe for possible cards */
|
|
+ bool probe;
|
|
+ /* The list of possible I/O ports */
|
|
+ const int *io_ports;
|
|
+ /* The size of that list */
|
|
+ int num_of_io_ports;
|
|
+ /* The region size to request */
|
|
+ unsigned region_size;
|
|
+ /* The name of the card */
|
|
+ const char *card;
|
|
+ /* Card can capture stereo audio */
|
|
+ bool has_stereo;
|
|
+ /* The maximum volume for the volume control. If 0, then there
|
|
+ is no volume control possible. */
|
|
+ int max_volume;
|
|
+};
|
|
+
|
|
+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);
|
|
+
|
|
+#endif
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-aimslab.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-aimslab.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-aimslab.c
|
|
@@ -1,16 +1,13 @@
|
|
-/* radiotrack (radioreveal) driver for Linux radio support
|
|
- * (c) 1997 M. Kirkwood
|
|
+/*
|
|
+ * AimsLab RadioTrack (aka RadioVeveal) driver
|
|
+ *
|
|
+ * Copyright 1997 M. Kirkwood
|
|
+ *
|
|
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
|
|
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
|
|
* Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
|
|
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
|
|
*
|
|
- * History:
|
|
- * 1999-02-24 Russell Kroll <rkroll@exploits.org>
|
|
- * Fine tuning/VIDEO_TUNER_LOW
|
|
- * Frequency range expanded to start at 87 MHz
|
|
- *
|
|
- * TODO: Allow for more than one of these foolish entities :-)
|
|
- *
|
|
* Notes on the hardware (reverse engineered from other peoples'
|
|
* reverse engineering of AIMS' code :-)
|
|
*
|
|
@@ -26,6 +23,7 @@
|
|
* wait(a_wee_while);
|
|
* out(port, stop_changing_the_volume);
|
|
*
|
|
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
|
|
*/
|
|
|
|
#include <linux/module.h> /* Modules */
|
|
@@ -34,401 +32,179 @@
|
|
#include <linux/delay.h> /* msleep */
|
|
#include <linux/videodev2.h> /* kernel radio structs */
|
|
#include <linux/io.h> /* outb, outb_p */
|
|
+#include <linux/slab.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include "radio-isa.h"
|
|
|
|
-MODULE_AUTHOR("M.Kirkwood");
|
|
+MODULE_AUTHOR("M. Kirkwood");
|
|
MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
|
|
MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION("0.0.3");
|
|
+MODULE_VERSION("1.0.0");
|
|
|
|
#ifndef CONFIG_RADIO_RTRACK_PORT
|
|
#define CONFIG_RADIO_RTRACK_PORT -1
|
|
#endif
|
|
|
|
-static int io = CONFIG_RADIO_RTRACK_PORT;
|
|
-static int radio_nr = -1;
|
|
+#define RTRACK_MAX 2
|
|
|
|
-module_param(io, int, 0);
|
|
-MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
|
|
-module_param(radio_nr, int, 0);
|
|
+static int io[RTRACK_MAX] = { [0] = CONFIG_RADIO_RTRACK_PORT,
|
|
+ [1 ... (RTRACK_MAX - 1)] = -1 };
|
|
+static int radio_nr[RTRACK_MAX] = { [0 ... (RTRACK_MAX - 1)] = -1 };
|
|
+
|
|
+module_param_array(io, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)");
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|
|
|
-struct rtrack
|
|
-{
|
|
- struct v4l2_device v4l2_dev;
|
|
- struct video_device vdev;
|
|
- int port;
|
|
+struct rtrack {
|
|
+ struct radio_isa_card isa;
|
|
int curvol;
|
|
- unsigned long curfreq;
|
|
- int muted;
|
|
- int io;
|
|
- struct mutex lock;
|
|
};
|
|
|
|
-static struct rtrack rtrack_card;
|
|
-
|
|
-/* local things */
|
|
-
|
|
-static void rt_decvol(struct rtrack *rt)
|
|
-{
|
|
- outb(0x58, rt->io); /* volume down + sigstr + on */
|
|
- msleep(100);
|
|
- outb(0xd8, rt->io); /* volume steady + sigstr + on */
|
|
-}
|
|
-
|
|
-static void rt_incvol(struct rtrack *rt)
|
|
-{
|
|
- outb(0x98, rt->io); /* volume up + sigstr + on */
|
|
- msleep(100);
|
|
- outb(0xd8, rt->io); /* volume steady + sigstr + on */
|
|
-}
|
|
-
|
|
-static void rt_mute(struct rtrack *rt)
|
|
-{
|
|
- rt->muted = 1;
|
|
- mutex_lock(&rt->lock);
|
|
- outb(0xd0, rt->io); /* volume steady, off */
|
|
- mutex_unlock(&rt->lock);
|
|
-}
|
|
-
|
|
-static int rt_setvol(struct rtrack *rt, int vol)
|
|
+static struct radio_isa_card *rtrack_alloc(void)
|
|
{
|
|
- int i;
|
|
-
|
|
- mutex_lock(&rt->lock);
|
|
-
|
|
- if (vol == rt->curvol) { /* requested volume = current */
|
|
- if (rt->muted) { /* user is unmuting the card */
|
|
- rt->muted = 0;
|
|
- outb(0xd8, rt->io); /* enable card */
|
|
- }
|
|
- mutex_unlock(&rt->lock);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if (vol == 0) { /* volume = 0 means mute the card */
|
|
- outb(0x48, rt->io); /* volume down but still "on" */
|
|
- msleep(2000); /* make sure it's totally down */
|
|
- outb(0xd0, rt->io); /* volume steady, off */
|
|
- rt->curvol = 0; /* track the volume state! */
|
|
- mutex_unlock(&rt->lock);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- rt->muted = 0;
|
|
- if (vol > rt->curvol)
|
|
- for (i = rt->curvol; i < vol; i++)
|
|
- rt_incvol(rt);
|
|
- else
|
|
- for (i = rt->curvol; i > vol; i--)
|
|
- rt_decvol(rt);
|
|
+ struct rtrack *rt = kzalloc(sizeof(struct rtrack), GFP_KERNEL);
|
|
|
|
- rt->curvol = vol;
|
|
- mutex_unlock(&rt->lock);
|
|
- return 0;
|
|
+ if (rt)
|
|
+ rt->curvol = 0xff;
|
|
+ return rt ? &rt->isa : NULL;
|
|
}
|
|
|
|
-/* the 128+64 on these outb's is to keep the volume stable while tuning
|
|
- * without them, the volume _will_ creep up with each frequency change
|
|
- * and bit 4 (+16) is to keep the signal strength meter enabled
|
|
+/* The 128+64 on these outb's is to keep the volume stable while tuning.
|
|
+ * Without them, the volume _will_ creep up with each frequency change
|
|
+ * and bit 4 (+16) is to keep the signal strength meter enabled.
|
|
*/
|
|
|
|
-static void send_0_byte(struct rtrack *rt)
|
|
+static void send_0_byte(struct radio_isa_card *isa, int on)
|
|
{
|
|
- if (rt->curvol == 0 || rt->muted) {
|
|
- outb_p(128+64+16+ 1, rt->io); /* wr-enable + data low */
|
|
- outb_p(128+64+16+2+1, rt->io); /* clock */
|
|
- }
|
|
- else {
|
|
- outb_p(128+64+16+8+ 1, rt->io); /* on + wr-enable + data low */
|
|
- outb_p(128+64+16+8+2+1, rt->io); /* clock */
|
|
- }
|
|
+ outb_p(128+64+16+on+1, isa->io); /* wr-enable + data low */
|
|
+ outb_p(128+64+16+on+2+1, isa->io); /* clock */
|
|
msleep(1);
|
|
}
|
|
|
|
-static void send_1_byte(struct rtrack *rt)
|
|
+static void send_1_byte(struct radio_isa_card *isa, int on)
|
|
{
|
|
- if (rt->curvol == 0 || rt->muted) {
|
|
- outb_p(128+64+16+4 +1, rt->io); /* wr-enable+data high */
|
|
- outb_p(128+64+16+4+2+1, rt->io); /* clock */
|
|
- }
|
|
- else {
|
|
- outb_p(128+64+16+8+4 +1, rt->io); /* on+wr-enable+data high */
|
|
- outb_p(128+64+16+8+4+2+1, rt->io); /* clock */
|
|
- }
|
|
-
|
|
+ outb_p(128+64+16+on+4+1, isa->io); /* wr-enable+data high */
|
|
+ outb_p(128+64+16+on+4+2+1, isa->io); /* clock */
|
|
msleep(1);
|
|
}
|
|
|
|
-static int rt_setfreq(struct rtrack *rt, unsigned long freq)
|
|
+static int rtrack_s_frequency(struct radio_isa_card *isa, u32 freq)
|
|
{
|
|
+ int on = v4l2_ctrl_g_ctrl(isa->mute) ? 0 : 8;
|
|
int i;
|
|
|
|
- mutex_lock(&rt->lock); /* Stop other ops interfering */
|
|
-
|
|
- rt->curfreq = freq;
|
|
-
|
|
- /* now uses VIDEO_TUNER_LOW for fine tuning */
|
|
-
|
|
freq += 171200; /* Add 10.7 MHz IF */
|
|
freq /= 800; /* Convert to 50 kHz units */
|
|
|
|
- send_0_byte(rt); /* 0: LSB of frequency */
|
|
+ send_0_byte(isa, on); /* 0: LSB of frequency */
|
|
|
|
for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
|
|
if (freq & (1 << i))
|
|
- send_1_byte(rt);
|
|
+ send_1_byte(isa, on);
|
|
else
|
|
- send_0_byte(rt);
|
|
+ send_0_byte(isa, on);
|
|
|
|
- send_0_byte(rt); /* 14: test bit - always 0 */
|
|
- send_0_byte(rt); /* 15: test bit - always 0 */
|
|
+ send_0_byte(isa, on); /* 14: test bit - always 0 */
|
|
+ send_0_byte(isa, on); /* 15: test bit - always 0 */
|
|
|
|
- send_0_byte(rt); /* 16: band data 0 - always 0 */
|
|
- send_0_byte(rt); /* 17: band data 1 - always 0 */
|
|
- send_0_byte(rt); /* 18: band data 2 - always 0 */
|
|
- send_0_byte(rt); /* 19: time base - always 0 */
|
|
-
|
|
- send_0_byte(rt); /* 20: spacing (0 = 25 kHz) */
|
|
- send_1_byte(rt); /* 21: spacing (1 = 25 kHz) */
|
|
- send_0_byte(rt); /* 22: spacing (0 = 25 kHz) */
|
|
- send_1_byte(rt); /* 23: AM/FM (FM = 1, always) */
|
|
-
|
|
- if (rt->curvol == 0 || rt->muted)
|
|
- outb(0xd0, rt->io); /* volume steady + sigstr */
|
|
- else
|
|
- outb(0xd8, rt->io); /* volume steady + sigstr + on */
|
|
+ send_0_byte(isa, on); /* 16: band data 0 - always 0 */
|
|
+ send_0_byte(isa, on); /* 17: band data 1 - always 0 */
|
|
+ send_0_byte(isa, on); /* 18: band data 2 - always 0 */
|
|
+ send_0_byte(isa, on); /* 19: time base - always 0 */
|
|
|
|
- mutex_unlock(&rt->lock);
|
|
+ send_0_byte(isa, on); /* 20: spacing (0 = 25 kHz) */
|
|
+ send_1_byte(isa, on); /* 21: spacing (1 = 25 kHz) */
|
|
+ send_0_byte(isa, on); /* 22: spacing (0 = 25 kHz) */
|
|
+ send_1_byte(isa, on); /* 23: AM/FM (FM = 1, always) */
|
|
|
|
+ outb(0xd0 + on, isa->io); /* volume steady + sigstr */
|
|
return 0;
|
|
}
|
|
|
|
-static int rt_getsigstr(struct rtrack *rt)
|
|
+static u32 rtrack_g_signal(struct radio_isa_card *isa)
|
|
{
|
|
- int sig = 1;
|
|
-
|
|
- mutex_lock(&rt->lock);
|
|
- if (inb(rt->io) & 2) /* bit set = no signal present */
|
|
- sig = 0;
|
|
- mutex_unlock(&rt->lock);
|
|
- return sig;
|
|
-}
|
|
-
|
|
-static int vidioc_querycap(struct file *file, void *priv,
|
|
- struct v4l2_capability *v)
|
|
-{
|
|
- strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
|
|
- strlcpy(v->card, "RadioTrack", sizeof(v->card));
|
|
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- struct rtrack *rt = video_drvdata(file);
|
|
-
|
|
- if (v->index > 0)
|
|
- return -EINVAL;
|
|
-
|
|
- strlcpy(v->name, "FM", sizeof(v->name));
|
|
- v->type = V4L2_TUNER_RADIO;
|
|
- v->rangelow = 87 * 16000;
|
|
- v->rangehigh = 108 * 16000;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
|
|
- v->capability = V4L2_TUNER_CAP_LOW;
|
|
- v->audmode = V4L2_TUNER_MODE_MONO;
|
|
- v->signal = 0xffff * rt_getsigstr(rt);
|
|
- return 0;
|
|
+ /* bit set = no signal present */
|
|
+ return 0xffff * !(inb(isa->io) & 2);
|
|
}
|
|
|
|
-static int vidioc_s_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
+static int rtrack_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
|
|
{
|
|
- return v->index ? -EINVAL : 0;
|
|
-}
|
|
+ struct rtrack *rt = container_of(isa, struct rtrack, isa);
|
|
+ int curvol = rt->curvol;
|
|
|
|
-static int vidioc_s_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct rtrack *rt = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
- return -EINVAL;
|
|
- rt_setfreq(rt, f->frequency);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct rtrack *rt = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0)
|
|
- return -EINVAL;
|
|
- f->type = V4L2_TUNER_RADIO;
|
|
- f->frequency = rt->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);
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct rtrack *rt = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- ctrl->value = rt->muted;
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- ctrl->value = rt->curvol;
|
|
+ if (mute) {
|
|
+ outb(0xd0, isa->io); /* volume steady + sigstr + off */
|
|
return 0;
|
|
}
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct rtrack *rt = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- if (ctrl->value)
|
|
- rt_mute(rt);
|
|
- else
|
|
- rt_setvol(rt, rt->curvol);
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- rt_setvol(rt, ctrl->value);
|
|
- return 0;
|
|
+ if (vol == 0) { /* volume = 0 means mute the card */
|
|
+ outb(0x48, isa->io); /* volume down but still "on" */
|
|
+ msleep(curvol * 3); /* make sure it's totally down */
|
|
+ } else if (curvol < vol) {
|
|
+ outb(0x98, isa->io); /* volume up + sigstr + on */
|
|
+ for (; curvol < vol; curvol++)
|
|
+ udelay(3000);
|
|
+ } else if (curvol > vol) {
|
|
+ outb(0x58, isa->io); /* volume down + sigstr + on */
|
|
+ for (; curvol > vol; curvol--)
|
|
+ udelay(3000);
|
|
}
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
|
|
-{
|
|
- *i = 0;
|
|
+ outb(0xd8, isa->io); /* volume steady + sigstr + on */
|
|
+ rt->curvol = vol;
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
|
|
-{
|
|
- return i ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
+/* Mute card - prevents noisy bootups */
|
|
+static int rtrack_initialize(struct radio_isa_card *isa)
|
|
{
|
|
- a->index = 0;
|
|
- strlcpy(a->name, "Radio", sizeof(a->name));
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
+ /* this ensures that the volume is all the way up */
|
|
+ outb(0x90, isa->io); /* volume up but still "on" */
|
|
+ msleep(3000); /* make sure it's totally up */
|
|
+ outb(0xc0, isa->io); /* steady volume, mute card */
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_s_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- return a->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static const struct v4l2_file_operations rtrack_fops = {
|
|
- .owner = THIS_MODULE,
|
|
- .unlocked_ioctl = video_ioctl2,
|
|
+static const struct radio_isa_ops rtrack_ops = {
|
|
+ .alloc = rtrack_alloc,
|
|
+ .init = rtrack_initialize,
|
|
+ .s_mute_volume = rtrack_s_mute_volume,
|
|
+ .s_frequency = rtrack_s_frequency,
|
|
+ .g_signal = rtrack_g_signal,
|
|
};
|
|
|
|
-static const struct v4l2_ioctl_ops rtrack_ioctl_ops = {
|
|
- .vidioc_querycap = vidioc_querycap,
|
|
- .vidioc_g_tuner = vidioc_g_tuner,
|
|
- .vidioc_s_tuner = vidioc_s_tuner,
|
|
- .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_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,
|
|
+static const int rtrack_ioports[] = { 0x20f, 0x30f };
|
|
+
|
|
+static struct radio_isa_driver rtrack_driver = {
|
|
+ .driver = {
|
|
+ .match = radio_isa_match,
|
|
+ .probe = radio_isa_probe,
|
|
+ .remove = radio_isa_remove,
|
|
+ .driver = {
|
|
+ .name = "radio-aimslab",
|
|
+ },
|
|
+ },
|
|
+ .io_params = io,
|
|
+ .radio_nr_params = radio_nr,
|
|
+ .io_ports = rtrack_ioports,
|
|
+ .num_of_io_ports = ARRAY_SIZE(rtrack_ioports),
|
|
+ .region_size = 2,
|
|
+ .card = "AIMSlab RadioTrack/RadioReveal",
|
|
+ .ops = &rtrack_ops,
|
|
+ .has_stereo = true,
|
|
+ .max_volume = 0xff,
|
|
};
|
|
|
|
static int __init rtrack_init(void)
|
|
{
|
|
- struct rtrack *rt = &rtrack_card;
|
|
- struct v4l2_device *v4l2_dev = &rt->v4l2_dev;
|
|
- int res;
|
|
-
|
|
- strlcpy(v4l2_dev->name, "rtrack", sizeof(v4l2_dev->name));
|
|
- rt->io = io;
|
|
-
|
|
- if (rt->io == -1) {
|
|
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x20f or 0x30f\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- if (!request_region(rt->io, 2, "rtrack")) {
|
|
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", rt->io);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- res = v4l2_device_register(NULL, v4l2_dev);
|
|
- if (res < 0) {
|
|
- release_region(rt->io, 2);
|
|
- v4l2_err(v4l2_dev, "could not register v4l2_device\n");
|
|
- return res;
|
|
- }
|
|
-
|
|
- strlcpy(rt->vdev.name, v4l2_dev->name, sizeof(rt->vdev.name));
|
|
- rt->vdev.v4l2_dev = v4l2_dev;
|
|
- rt->vdev.fops = &rtrack_fops;
|
|
- rt->vdev.ioctl_ops = &rtrack_ioctl_ops;
|
|
- rt->vdev.release = video_device_release_empty;
|
|
- video_set_drvdata(&rt->vdev, rt);
|
|
-
|
|
- /* Set up the I/O locking */
|
|
-
|
|
- mutex_init(&rt->lock);
|
|
-
|
|
- /* mute card - prevents noisy bootups */
|
|
-
|
|
- /* this ensures that the volume is all the way down */
|
|
- outb(0x48, rt->io); /* volume down but still "on" */
|
|
- msleep(2000); /* make sure it's totally down */
|
|
- outb(0xc0, rt->io); /* steady volume, mute card */
|
|
-
|
|
- if (video_register_device(&rt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
|
- v4l2_device_unregister(&rt->v4l2_dev);
|
|
- release_region(rt->io, 2);
|
|
- return -EINVAL;
|
|
- }
|
|
- v4l2_info(v4l2_dev, "AIMSlab RadioTrack/RadioReveal card driver.\n");
|
|
-
|
|
- return 0;
|
|
+ return isa_register_driver(&rtrack_driver.driver, RTRACK_MAX);
|
|
}
|
|
|
|
static void __exit rtrack_exit(void)
|
|
{
|
|
- struct rtrack *rt = &rtrack_card;
|
|
-
|
|
- video_unregister_device(&rt->vdev);
|
|
- v4l2_device_unregister(&rt->v4l2_dev);
|
|
- release_region(rt->io, 2);
|
|
+ isa_unregister_driver(&rtrack_driver.driver);
|
|
}
|
|
|
|
module_init(rtrack_init);
|
|
module_exit(rtrack_exit);
|
|
-
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-aztech.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-aztech.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-aztech.c
|
|
@@ -1,5 +1,7 @@
|
|
-/* radio-aztech.c - Aztech radio card driver for Linux 2.2
|
|
+/*
|
|
+ * radio-aztech.c - Aztech radio card driver
|
|
*
|
|
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@xs4all.nl>
|
|
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
|
|
* Adapted to support the Video for Linux API by
|
|
* Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
|
|
@@ -10,19 +12,7 @@
|
|
* Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
|
|
* William McGrath (wmcgrath@twilight.vtc.vsc.edu)
|
|
*
|
|
- * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
|
|
- * along with more information on the card itself.
|
|
- *
|
|
- * History:
|
|
- * 1999-02-24 Russell Kroll <rkroll@exploits.org>
|
|
- * Fine tuning/VIDEO_TUNER_LOW
|
|
- * Range expanded to 87-108 MHz (from 87.9-107.8)
|
|
- *
|
|
- * Notable changes from the original source:
|
|
- * - includes stripped down to the essentials
|
|
- * - for loops used as delays replaced with udelay()
|
|
- * - #defines removed, changed to static values
|
|
- * - tuning structure changed - no more character arrays, other changes
|
|
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
|
|
*/
|
|
|
|
#include <linux/module.h> /* Modules */
|
|
@@ -31,126 +21,72 @@
|
|
#include <linux/delay.h> /* udelay */
|
|
#include <linux/videodev2.h> /* kernel radio structs */
|
|
#include <linux/io.h> /* outb, outb_p */
|
|
+#include <linux/slab.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include "radio-isa.h"
|
|
|
|
MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
|
|
MODULE_DESCRIPTION("A driver for the Aztech radio card.");
|
|
MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION("0.0.3");
|
|
+MODULE_VERSION("1.0.0");
|
|
|
|
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
|
|
-
|
|
#ifndef CONFIG_RADIO_AZTECH_PORT
|
|
#define CONFIG_RADIO_AZTECH_PORT -1
|
|
#endif
|
|
|
|
-static int io = CONFIG_RADIO_AZTECH_PORT;
|
|
-static int radio_nr = -1;
|
|
-static int radio_wait_time = 1000;
|
|
-
|
|
-module_param(io, int, 0);
|
|
-module_param(radio_nr, int, 0);
|
|
-MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
|
|
+#define AZTECH_MAX 2
|
|
|
|
-struct aztech
|
|
-{
|
|
- struct v4l2_device v4l2_dev;
|
|
- struct video_device vdev;
|
|
- int io;
|
|
+static int io[AZTECH_MAX] = { [0] = CONFIG_RADIO_AZTECH_PORT,
|
|
+ [1 ... (AZTECH_MAX - 1)] = -1 };
|
|
+static int radio_nr[AZTECH_MAX] = { [0 ... (AZTECH_MAX - 1)] = -1 };
|
|
+static const int radio_wait_time = 1000;
|
|
+
|
|
+module_param_array(io, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(io, "I/O addresses of the Aztech card (0x350 or 0x358)");
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|
+
|
|
+struct aztech {
|
|
+ struct radio_isa_card isa;
|
|
int curvol;
|
|
- unsigned long curfreq;
|
|
- int stereo;
|
|
- struct mutex lock;
|
|
};
|
|
|
|
-static struct aztech aztech_card;
|
|
-
|
|
-static int volconvert(int level)
|
|
-{
|
|
- level >>= 14; /* Map 16bits down to 2 bit */
|
|
- level &= 3;
|
|
-
|
|
- /* convert to card-friendly values */
|
|
- switch (level) {
|
|
- case 0:
|
|
- return 0;
|
|
- case 1:
|
|
- return 1;
|
|
- case 2:
|
|
- return 4;
|
|
- case 3:
|
|
- return 5;
|
|
- }
|
|
- return 0; /* Quieten gcc */
|
|
-}
|
|
-
|
|
static void send_0_byte(struct aztech *az)
|
|
{
|
|
udelay(radio_wait_time);
|
|
- outb_p(2 + volconvert(az->curvol), az->io);
|
|
- outb_p(64 + 2 + volconvert(az->curvol), az->io);
|
|
+ outb_p(2 + az->curvol, az->isa.io);
|
|
+ outb_p(64 + 2 + az->curvol, az->isa.io);
|
|
}
|
|
|
|
static void send_1_byte(struct aztech *az)
|
|
{
|
|
- udelay (radio_wait_time);
|
|
- outb_p(128 + 2 + volconvert(az->curvol), az->io);
|
|
- outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io);
|
|
-}
|
|
-
|
|
-static int az_setvol(struct aztech *az, int vol)
|
|
-{
|
|
- mutex_lock(&az->lock);
|
|
- outb(volconvert(vol), az->io);
|
|
- mutex_unlock(&az->lock);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/* thanks to Michael Dwyer for giving me a dose of clues in
|
|
- * the signal strength department..
|
|
- *
|
|
- * This card has a stereo bit - bit 0 set = mono, not set = stereo
|
|
- * It also has a "signal" bit - bit 1 set = bad signal, not set = good
|
|
- *
|
|
- */
|
|
-
|
|
-static int az_getsigstr(struct aztech *az)
|
|
-{
|
|
- int sig = 1;
|
|
-
|
|
- mutex_lock(&az->lock);
|
|
- if (inb(az->io) & 2) /* bit set = no signal present */
|
|
- sig = 0;
|
|
- mutex_unlock(&az->lock);
|
|
- return sig;
|
|
+ udelay(radio_wait_time);
|
|
+ outb_p(128 + 2 + az->curvol, az->isa.io);
|
|
+ outb_p(128 + 64 + 2 + az->curvol, az->isa.io);
|
|
}
|
|
|
|
-static int az_getstereo(struct aztech *az)
|
|
+static struct radio_isa_card *aztech_alloc(void)
|
|
{
|
|
- int stereo = 1;
|
|
+ struct aztech *az = kzalloc(sizeof(*az), GFP_KERNEL);
|
|
|
|
- mutex_lock(&az->lock);
|
|
- if (inb(az->io) & 1) /* bit set = mono */
|
|
- stereo = 0;
|
|
- mutex_unlock(&az->lock);
|
|
- return stereo;
|
|
+ return az ? &az->isa : NULL;
|
|
}
|
|
|
|
-static int az_setfreq(struct aztech *az, unsigned long frequency)
|
|
+static int aztech_s_frequency(struct radio_isa_card *isa, u32 freq)
|
|
{
|
|
+ struct aztech *az = container_of(isa, struct aztech, isa);
|
|
int i;
|
|
|
|
- mutex_lock(&az->lock);
|
|
-
|
|
- az->curfreq = frequency;
|
|
- frequency += 171200; /* Add 10.7 MHz IF */
|
|
- frequency /= 800; /* Convert to 50 kHz units */
|
|
+ freq += 171200; /* Add 10.7 MHz IF */
|
|
+ freq /= 800; /* Convert to 50 kHz units */
|
|
|
|
send_0_byte(az); /* 0: LSB of frequency */
|
|
|
|
for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
|
|
- if (frequency & (1 << i))
|
|
+ if (freq & (1 << i))
|
|
send_1_byte(az);
|
|
else
|
|
send_0_byte(az);
|
|
@@ -158,7 +94,7 @@ static int az_setfreq(struct aztech *az,
|
|
send_0_byte(az); /* 14: test bit - always 0 */
|
|
send_0_byte(az); /* 15: test bit - always 0 */
|
|
send_0_byte(az); /* 16: band data 0 - always 0 */
|
|
- if (az->stereo) /* 17: stereo (1 to enable) */
|
|
+ if (isa->stereo) /* 17: stereo (1 to enable) */
|
|
send_1_byte(az);
|
|
else
|
|
send_0_byte(az);
|
|
@@ -173,225 +109,77 @@ static int az_setfreq(struct aztech *az,
|
|
/* latch frequency */
|
|
|
|
udelay(radio_wait_time);
|
|
- outb_p(128 + 64 + volconvert(az->curvol), az->io);
|
|
-
|
|
- mutex_unlock(&az->lock);
|
|
-
|
|
- return 0;
|
|
-}
|
|
+ outb_p(128 + 64 + az->curvol, az->isa.io);
|
|
|
|
-static int vidioc_querycap(struct file *file, void *priv,
|
|
- struct v4l2_capability *v)
|
|
-{
|
|
- strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
|
|
- strlcpy(v->card, "Aztech Radio", sizeof(v->card));
|
|
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_g_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- struct aztech *az = video_drvdata(file);
|
|
-
|
|
- if (v->index > 0)
|
|
- return -EINVAL;
|
|
-
|
|
- strlcpy(v->name, "FM", sizeof(v->name));
|
|
- v->type = V4L2_TUNER_RADIO;
|
|
-
|
|
- v->rangelow = 87 * 16000;
|
|
- v->rangehigh = 108 * 16000;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
|
|
- v->capability = V4L2_TUNER_CAP_LOW;
|
|
- if (az_getstereo(az))
|
|
- v->audmode = V4L2_TUNER_MODE_STEREO;
|
|
- else
|
|
- v->audmode = V4L2_TUNER_MODE_MONO;
|
|
- v->signal = 0xFFFF * az_getsigstr(az);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- return v->index ? -EINVAL : 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_g_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
+/* thanks to Michael Dwyer for giving me a dose of clues in
|
|
+ * the signal strength department..
|
|
+ *
|
|
+ * This card has a stereo bit - bit 0 set = mono, not set = stereo
|
|
+ */
|
|
+static u32 aztech_g_rxsubchans(struct radio_isa_card *isa)
|
|
{
|
|
- a->index = 0;
|
|
- strlcpy(a->name, "Radio", sizeof(a->name));
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
- return 0;
|
|
+ if (inb(isa->io) & 1)
|
|
+ return V4L2_TUNER_SUB_MONO;
|
|
+ return V4L2_TUNER_SUB_STEREO;
|
|
}
|
|
|
|
-static int vidioc_s_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
+static int aztech_s_stereo(struct radio_isa_card *isa, bool stereo)
|
|
{
|
|
- return a->index ? -EINVAL : 0;
|
|
+ return aztech_s_frequency(isa, isa->freq);
|
|
}
|
|
|
|
-static int vidioc_s_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
+static int aztech_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
|
|
{
|
|
- struct aztech *az = video_drvdata(file);
|
|
+ struct aztech *az = container_of(isa, struct aztech, isa);
|
|
|
|
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
- return -EINVAL;
|
|
- az_setfreq(az, f->frequency);
|
|
+ if (mute)
|
|
+ vol = 0;
|
|
+ az->curvol = (vol & 1) + ((vol & 2) << 1);
|
|
+ outb(az->curvol, isa->io);
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_g_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct aztech *az = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0)
|
|
- return -EINVAL;
|
|
- f->type = V4L2_TUNER_RADIO;
|
|
- f->frequency = az->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);
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct aztech *az = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- if (az->curvol == 0)
|
|
- ctrl->value = 1;
|
|
- else
|
|
- ctrl->value = 0;
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- ctrl->value = az->curvol * 6554;
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct aztech *az = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- if (ctrl->value)
|
|
- az_setvol(az, 0);
|
|
- else
|
|
- az_setvol(az, az->curvol);
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- az_setvol(az, ctrl->value);
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static const struct v4l2_file_operations aztech_fops = {
|
|
- .owner = THIS_MODULE,
|
|
- .unlocked_ioctl = video_ioctl2,
|
|
+static const struct radio_isa_ops aztech_ops = {
|
|
+ .alloc = aztech_alloc,
|
|
+ .s_mute_volume = aztech_s_mute_volume,
|
|
+ .s_frequency = aztech_s_frequency,
|
|
+ .s_stereo = aztech_s_stereo,
|
|
+ .g_rxsubchans = aztech_g_rxsubchans,
|
|
};
|
|
|
|
-static const struct v4l2_ioctl_ops aztech_ioctl_ops = {
|
|
- .vidioc_querycap = vidioc_querycap,
|
|
- .vidioc_g_tuner = vidioc_g_tuner,
|
|
- .vidioc_s_tuner = vidioc_s_tuner,
|
|
- .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_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,
|
|
+static const int aztech_ioports[] = { 0x350, 0x358 };
|
|
+
|
|
+static struct radio_isa_driver aztech_driver = {
|
|
+ .driver = {
|
|
+ .match = radio_isa_match,
|
|
+ .probe = radio_isa_probe,
|
|
+ .remove = radio_isa_remove,
|
|
+ .driver = {
|
|
+ .name = "radio-aztech",
|
|
+ },
|
|
+ },
|
|
+ .io_params = io,
|
|
+ .radio_nr_params = radio_nr,
|
|
+ .io_ports = aztech_ioports,
|
|
+ .num_of_io_ports = ARRAY_SIZE(aztech_ioports),
|
|
+ .region_size = 2,
|
|
+ .card = "Aztech Radio",
|
|
+ .ops = &aztech_ops,
|
|
+ .has_stereo = true,
|
|
+ .max_volume = 3,
|
|
};
|
|
|
|
static int __init aztech_init(void)
|
|
{
|
|
- struct aztech *az = &aztech_card;
|
|
- struct v4l2_device *v4l2_dev = &az->v4l2_dev;
|
|
- int res;
|
|
-
|
|
- strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name));
|
|
- az->io = io;
|
|
-
|
|
- if (az->io == -1) {
|
|
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x350 or 0x358\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- if (!request_region(az->io, 2, "aztech")) {
|
|
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- res = v4l2_device_register(NULL, v4l2_dev);
|
|
- if (res < 0) {
|
|
- release_region(az->io, 2);
|
|
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
- return res;
|
|
- }
|
|
-
|
|
- mutex_init(&az->lock);
|
|
- strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name));
|
|
- az->vdev.v4l2_dev = v4l2_dev;
|
|
- az->vdev.fops = &aztech_fops;
|
|
- az->vdev.ioctl_ops = &aztech_ioctl_ops;
|
|
- az->vdev.release = video_device_release_empty;
|
|
- video_set_drvdata(&az->vdev, az);
|
|
- /* mute card - prevents noisy bootups */
|
|
- outb(0, az->io);
|
|
-
|
|
- if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
|
- v4l2_device_unregister(v4l2_dev);
|
|
- release_region(az->io, 2);
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
|
|
- return 0;
|
|
+ return isa_register_driver(&aztech_driver.driver, AZTECH_MAX);
|
|
}
|
|
|
|
static void __exit aztech_exit(void)
|
|
{
|
|
- struct aztech *az = &aztech_card;
|
|
-
|
|
- video_unregister_device(&az->vdev);
|
|
- v4l2_device_unregister(&az->v4l2_dev);
|
|
- release_region(az->io, 2);
|
|
+ isa_unregister_driver(&aztech_driver.driver);
|
|
}
|
|
|
|
module_init(aztech_init);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-gemtek.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-gemtek.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-gemtek.c
|
|
@@ -1,4 +1,7 @@
|
|
-/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
|
|
+/*
|
|
+ * GemTek radio card driver
|
|
+ *
|
|
+ * Copyright 1998 Jonas Munsin <jmunsin@iki.fi>
|
|
*
|
|
* GemTek hasn't released any specs on the card, so the protocol had to
|
|
* be reverse engineered with dosemu.
|
|
@@ -11,9 +14,12 @@
|
|
* Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
|
|
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
|
|
*
|
|
- * TODO: Allow for more than one of these foolish entities :-)
|
|
- *
|
|
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
|
|
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
|
|
+ *
|
|
+ * Note: this card seems to swap the left and right audio channels!
|
|
+ *
|
|
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
|
|
*/
|
|
|
|
#include <linux/module.h> /* Modules */
|
|
@@ -23,8 +29,10 @@
|
|
#include <linux/videodev2.h> /* kernel radio structs */
|
|
#include <linux/mutex.h>
|
|
#include <linux/io.h> /* outb, outb_p */
|
|
+#include <linux/slab.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <media/v4l2-device.h>
|
|
+#include "radio-isa.h"
|
|
|
|
/*
|
|
* Module info.
|
|
@@ -33,7 +41,7 @@
|
|
MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>");
|
|
MODULE_DESCRIPTION("A driver for the GemTek Radio card.");
|
|
MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION("0.0.4");
|
|
+MODULE_VERSION("1.0.0");
|
|
|
|
/*
|
|
* Module params.
|
|
@@ -46,45 +54,29 @@ MODULE_VERSION("0.0.4");
|
|
#define CONFIG_RADIO_GEMTEK_PROBE 1
|
|
#endif
|
|
|
|
-static int io = CONFIG_RADIO_GEMTEK_PORT;
|
|
-static bool probe = CONFIG_RADIO_GEMTEK_PROBE;
|
|
-static bool hardmute;
|
|
-static bool shutdown = 1;
|
|
-static bool keepmuted = 1;
|
|
-static bool initmute = 1;
|
|
-static int radio_nr = -1;
|
|
+#define GEMTEK_MAX 4
|
|
|
|
-module_param(io, int, 0444);
|
|
-MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "
|
|
- "probing is disabled or fails. The most common I/O ports are: 0x20c "
|
|
- "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
|
|
- "work for the combined sound/radiocard).");
|
|
+static bool probe = CONFIG_RADIO_GEMTEK_PROBE;
|
|
+static bool hardmute;
|
|
+static int io[GEMTEK_MAX] = { [0] = CONFIG_RADIO_GEMTEK_PORT,
|
|
+ [1 ... (GEMTEK_MAX - 1)] = -1 };
|
|
+static int radio_nr[GEMTEK_MAX] = { [0 ... (GEMTEK_MAX - 1)] = -1 };
|
|
|
|
module_param(probe, bool, 0444);
|
|
-MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most "
|
|
- "common I/O ports used by the card are probed.");
|
|
+MODULE_PARM_DESC(probe, "Enable automatic device probing.");
|
|
|
|
module_param(hardmute, bool, 0644);
|
|
-MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may "
|
|
+MODULE_PARM_DESC(hardmute, "Enable 'hard muting' by shutting down PLL, may "
|
|
"reduce static noise.");
|
|
|
|
-module_param(shutdown, bool, 0644);
|
|
-MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when "
|
|
- "module is unloaded.");
|
|
-
|
|
-module_param(keepmuted, bool, 0644);
|
|
-MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed.");
|
|
-
|
|
-module_param(initmute, bool, 0444);
|
|
-MODULE_PARM_DESC(initmute, "Mute card when module is loaded.");
|
|
-
|
|
-module_param(radio_nr, int, 0444);
|
|
+module_param_array(io, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(io, "Force I/O ports for the GemTek Radio card if automatic "
|
|
+ "probing is disabled or fails. The most common I/O ports are: 0x20c "
|
|
+ "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
|
|
+ "work for the combined sound/radiocard).");
|
|
|
|
-/*
|
|
- * Functions for controlling the card.
|
|
- */
|
|
-#define GEMTEK_LOWFREQ (87*16000)
|
|
-#define GEMTEK_HIGHFREQ (108*16000)
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|
|
|
/*
|
|
* Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal
|
|
@@ -108,18 +100,11 @@ module_param(radio_nr, int, 0444);
|
|
#define LONG_DELAY 75 /* usec */
|
|
|
|
struct gemtek {
|
|
- struct v4l2_device v4l2_dev;
|
|
- struct video_device vdev;
|
|
- struct mutex lock;
|
|
- unsigned long lastfreq;
|
|
- int muted;
|
|
- int verified;
|
|
- int io;
|
|
+ struct radio_isa_card isa;
|
|
+ bool muted;
|
|
u32 bu2614data;
|
|
};
|
|
|
|
-static struct gemtek gemtek_card;
|
|
-
|
|
#define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */
|
|
#define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */
|
|
#define BU2614_VOID_BITS 4 /* unused */
|
|
@@ -166,31 +151,24 @@ static struct gemtek gemtek_card;
|
|
*/
|
|
static void gemtek_bu2614_transmit(struct gemtek *gt)
|
|
{
|
|
+ struct radio_isa_card *isa = >->isa;
|
|
int i, bit, q, mute;
|
|
|
|
- mutex_lock(>->lock);
|
|
-
|
|
mute = gt->muted ? GEMTEK_MT : 0x00;
|
|
|
|
- outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
|
|
- udelay(SHORT_DELAY);
|
|
- outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
|
|
+ outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, isa->io);
|
|
udelay(LONG_DELAY);
|
|
|
|
for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) {
|
|
bit = (q & 1) ? GEMTEK_DA : 0;
|
|
- outb_p(mute | GEMTEK_CE | bit, gt->io);
|
|
+ outb_p(mute | GEMTEK_CE | bit, isa->io);
|
|
udelay(SHORT_DELAY);
|
|
- outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io);
|
|
+ outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, isa->io);
|
|
udelay(SHORT_DELAY);
|
|
}
|
|
|
|
- outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
|
|
+ outb_p(mute | GEMTEK_DA | GEMTEK_CK, isa->io);
|
|
udelay(SHORT_DELAY);
|
|
- outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
|
|
- udelay(LONG_DELAY);
|
|
-
|
|
- mutex_unlock(>->lock);
|
|
}
|
|
|
|
/*
|
|
@@ -198,21 +176,27 @@ static void gemtek_bu2614_transmit(struc
|
|
*/
|
|
static unsigned long gemtek_convfreq(unsigned long freq)
|
|
{
|
|
- return ((freq<<FSCALE) + IF_OFFSET + REF_FREQ/2) / REF_FREQ;
|
|
+ return ((freq << FSCALE) + IF_OFFSET + REF_FREQ / 2) / REF_FREQ;
|
|
+}
|
|
+
|
|
+static struct radio_isa_card *gemtek_alloc(void)
|
|
+{
|
|
+ struct gemtek *gt = kzalloc(sizeof(*gt), GFP_KERNEL);
|
|
+
|
|
+ if (gt)
|
|
+ gt->muted = true;
|
|
+ return gt ? >->isa : NULL;
|
|
}
|
|
|
|
/*
|
|
* Set FM-frequency.
|
|
*/
|
|
-static void gemtek_setfreq(struct gemtek *gt, unsigned long freq)
|
|
+static int gemtek_s_frequency(struct radio_isa_card *isa, u32 freq)
|
|
{
|
|
- if (keepmuted && hardmute && gt->muted)
|
|
- return;
|
|
+ struct gemtek *gt = container_of(isa, struct gemtek, isa);
|
|
|
|
- freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ);
|
|
-
|
|
- gt->lastfreq = freq;
|
|
- gt->muted = 0;
|
|
+ if (hardmute && gt->muted)
|
|
+ return 0;
|
|
|
|
gemtek_bu2614_set(gt, BU2614_PORT, 0);
|
|
gemtek_bu2614_set(gt, BU2614_FMES, 0);
|
|
@@ -220,23 +204,25 @@ static void gemtek_setfreq(struct gemtek
|
|
gemtek_bu2614_set(gt, BU2614_SWAL, 0);
|
|
gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */
|
|
gemtek_bu2614_set(gt, BU2614_TEST, 0);
|
|
-
|
|
gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ);
|
|
gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq));
|
|
-
|
|
gemtek_bu2614_transmit(gt);
|
|
+ return 0;
|
|
}
|
|
|
|
/*
|
|
* Set mute flag.
|
|
*/
|
|
-static void gemtek_mute(struct gemtek *gt)
|
|
+static int gemtek_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
|
|
{
|
|
+ struct gemtek *gt = container_of(isa, struct gemtek, isa);
|
|
int i;
|
|
|
|
- gt->muted = 1;
|
|
-
|
|
+ gt->muted = mute;
|
|
if (hardmute) {
|
|
+ if (!mute)
|
|
+ return gemtek_s_frequency(isa, isa->freq);
|
|
+
|
|
/* Turn off PLL, disable data output */
|
|
gemtek_bu2614_set(gt, BU2614_PORT, 0);
|
|
gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */
|
|
@@ -247,367 +233,85 @@ static void gemtek_mute(struct gemtek *g
|
|
gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF);
|
|
gemtek_bu2614_set(gt, BU2614_FREQ, 0);
|
|
gemtek_bu2614_transmit(gt);
|
|
- return;
|
|
+ return 0;
|
|
}
|
|
|
|
- mutex_lock(>->lock);
|
|
-
|
|
/* Read bus contents (CE, CK and DA). */
|
|
- i = inb_p(gt->io);
|
|
+ i = inb_p(isa->io);
|
|
/* Write it back with mute flag set. */
|
|
- outb_p((i >> 5) | GEMTEK_MT, gt->io);
|
|
+ outb_p((i >> 5) | (mute ? GEMTEK_MT : 0), isa->io);
|
|
udelay(SHORT_DELAY);
|
|
-
|
|
- mutex_unlock(>->lock);
|
|
-}
|
|
-
|
|
-/*
|
|
- * Unset mute flag.
|
|
- */
|
|
-static void gemtek_unmute(struct gemtek *gt)
|
|
-{
|
|
- int i;
|
|
-
|
|
- gt->muted = 0;
|
|
- if (hardmute) {
|
|
- /* Turn PLL back on. */
|
|
- gemtek_setfreq(gt, gt->lastfreq);
|
|
- return;
|
|
- }
|
|
- mutex_lock(>->lock);
|
|
-
|
|
- i = inb_p(gt->io);
|
|
- outb_p(i >> 5, gt->io);
|
|
- udelay(SHORT_DELAY);
|
|
-
|
|
- mutex_unlock(>->lock);
|
|
+ return 0;
|
|
}
|
|
|
|
-/*
|
|
- * Get signal strength (= stereo status).
|
|
- */
|
|
-static inline int gemtek_getsigstr(struct gemtek *gt)
|
|
+static u32 gemtek_g_rxsubchans(struct radio_isa_card *isa)
|
|
{
|
|
- int sig;
|
|
-
|
|
- mutex_lock(>->lock);
|
|
- sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1;
|
|
- mutex_unlock(>->lock);
|
|
- return sig;
|
|
+ if (inb_p(isa->io) & GEMTEK_NS)
|
|
+ return V4L2_TUNER_SUB_MONO;
|
|
+ return V4L2_TUNER_SUB_STEREO;
|
|
}
|
|
|
|
/*
|
|
* Check if requested card acts like GemTek Radio card.
|
|
*/
|
|
-static int gemtek_verify(struct gemtek *gt, int port)
|
|
+static bool gemtek_probe(struct radio_isa_card *isa, int io)
|
|
{
|
|
int i, q;
|
|
|
|
- if (gt->verified == port)
|
|
- return 1;
|
|
-
|
|
- mutex_lock(>->lock);
|
|
-
|
|
- q = inb_p(port); /* Read bus contents before probing. */
|
|
+ q = inb_p(io); /* Read bus contents before probing. */
|
|
/* Try to turn on CE, CK and DA respectively and check if card responds
|
|
properly. */
|
|
for (i = 0; i < 3; ++i) {
|
|
- outb_p(1 << i, port);
|
|
+ outb_p(1 << i, io);
|
|
udelay(SHORT_DELAY);
|
|
|
|
- if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) {
|
|
- mutex_unlock(>->lock);
|
|
- return 0;
|
|
- }
|
|
+ if ((inb_p(io) & ~GEMTEK_NS) != (0x17 | (1 << (i + 5))))
|
|
+ return false;
|
|
}
|
|
- outb_p(q >> 5, port); /* Write bus contents back. */
|
|
+ outb_p(q >> 5, io); /* Write bus contents back. */
|
|
udelay(SHORT_DELAY);
|
|
-
|
|
- mutex_unlock(>->lock);
|
|
- gt->verified = port;
|
|
-
|
|
- return 1;
|
|
-}
|
|
-
|
|
-/*
|
|
- * Automatic probing for card.
|
|
- */
|
|
-static int gemtek_probe(struct gemtek *gt)
|
|
-{
|
|
- struct v4l2_device *v4l2_dev = >->v4l2_dev;
|
|
- int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
|
|
- int i;
|
|
-
|
|
- if (!probe) {
|
|
- v4l2_info(v4l2_dev, "Automatic device probing disabled.\n");
|
|
- return -1;
|
|
- }
|
|
-
|
|
- v4l2_info(v4l2_dev, "Automatic device probing enabled.\n");
|
|
-
|
|
- for (i = 0; i < ARRAY_SIZE(ioports); ++i) {
|
|
- v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]);
|
|
-
|
|
- if (!request_region(ioports[i], 1, "gemtek-probe")) {
|
|
- v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n",
|
|
- ioports[i]);
|
|
- continue;
|
|
- }
|
|
-
|
|
- if (gemtek_verify(gt, ioports[i])) {
|
|
- v4l2_info(v4l2_dev, "Card found from I/O port "
|
|
- "0x%x!\n", ioports[i]);
|
|
-
|
|
- release_region(ioports[i], 1);
|
|
- gt->io = ioports[i];
|
|
- return gt->io;
|
|
- }
|
|
-
|
|
- release_region(ioports[i], 1);
|
|
- }
|
|
-
|
|
- v4l2_err(v4l2_dev, "Automatic probing failed!\n");
|
|
- return -1;
|
|
+ return true;
|
|
}
|
|
|
|
-/*
|
|
- * Video 4 Linux stuff.
|
|
- */
|
|
-
|
|
-static const struct v4l2_file_operations gemtek_fops = {
|
|
- .owner = THIS_MODULE,
|
|
- .unlocked_ioctl = video_ioctl2,
|
|
+static const struct radio_isa_ops gemtek_ops = {
|
|
+ .alloc = gemtek_alloc,
|
|
+ .probe = gemtek_probe,
|
|
+ .s_mute_volume = gemtek_s_mute_volume,
|
|
+ .s_frequency = gemtek_s_frequency,
|
|
+ .g_rxsubchans = gemtek_g_rxsubchans,
|
|
};
|
|
|
|
-static int vidioc_querycap(struct file *file, void *priv,
|
|
- struct v4l2_capability *v)
|
|
-{
|
|
- strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));
|
|
- strlcpy(v->card, "GemTek", sizeof(v->card));
|
|
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
|
|
-{
|
|
- struct gemtek *gt = video_drvdata(file);
|
|
-
|
|
- if (v->index > 0)
|
|
- return -EINVAL;
|
|
-
|
|
- strlcpy(v->name, "FM", sizeof(v->name));
|
|
- v->type = V4L2_TUNER_RADIO;
|
|
- v->rangelow = GEMTEK_LOWFREQ;
|
|
- v->rangehigh = GEMTEK_HIGHFREQ;
|
|
- v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
|
|
- v->signal = 0xffff * gemtek_getsigstr(gt);
|
|
- if (v->signal) {
|
|
- v->audmode = V4L2_TUNER_MODE_STEREO;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_STEREO;
|
|
- } else {
|
|
- v->audmode = V4L2_TUNER_MODE_MONO;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
|
|
-{
|
|
- return (v->index != 0) ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct gemtek *gt = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0)
|
|
- return -EINVAL;
|
|
- f->type = V4L2_TUNER_RADIO;
|
|
- f->frequency = gt->lastfreq;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct gemtek *gt = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
- return -EINVAL;
|
|
- gemtek_setfreq(gt, f->frequency);
|
|
- 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, 0);
|
|
- default:
|
|
- return -EINVAL;
|
|
- }
|
|
-}
|
|
-
|
|
-static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct gemtek *gt = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- ctrl->value = gt->muted;
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
+static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
|
|
|
|
-static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct gemtek *gt = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- if (ctrl->value)
|
|
- gemtek_mute(gt);
|
|
- else
|
|
- gemtek_unmute(gt);
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-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 != 0) ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
|
|
-{
|
|
- a->index = 0;
|
|
- strlcpy(a->name, "Radio", sizeof(a->name));
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
|
|
-{
|
|
- return (a->index != 0) ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static const struct v4l2_ioctl_ops gemtek_ioctl_ops = {
|
|
- .vidioc_querycap = vidioc_querycap,
|
|
- .vidioc_g_tuner = vidioc_g_tuner,
|
|
- .vidioc_s_tuner = vidioc_s_tuner,
|
|
- .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_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
|
|
+static struct radio_isa_driver gemtek_driver = {
|
|
+ .driver = {
|
|
+ .match = radio_isa_match,
|
|
+ .probe = radio_isa_probe,
|
|
+ .remove = radio_isa_remove,
|
|
+ .driver = {
|
|
+ .name = "radio-gemtek",
|
|
+ },
|
|
+ },
|
|
+ .io_params = io,
|
|
+ .radio_nr_params = radio_nr,
|
|
+ .io_ports = gemtek_ioports,
|
|
+ .num_of_io_ports = ARRAY_SIZE(gemtek_ioports),
|
|
+ .region_size = 1,
|
|
+ .card = "GemTek Radio",
|
|
+ .ops = &gemtek_ops,
|
|
+ .has_stereo = true,
|
|
};
|
|
|
|
-/*
|
|
- * Initialization / cleanup related stuff.
|
|
- */
|
|
-
|
|
static int __init gemtek_init(void)
|
|
{
|
|
- struct gemtek *gt = &gemtek_card;
|
|
- struct v4l2_device *v4l2_dev = >->v4l2_dev;
|
|
- int res;
|
|
-
|
|
- strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name));
|
|
-
|
|
- v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n");
|
|
-
|
|
- mutex_init(>->lock);
|
|
-
|
|
- gt->verified = -1;
|
|
- gt->io = io;
|
|
- gemtek_probe(gt);
|
|
- if (gt->io) {
|
|
- if (!request_region(gt->io, 1, "gemtek")) {
|
|
- v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- if (!gemtek_verify(gt, gt->io))
|
|
- v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not "
|
|
- "respond properly, check your "
|
|
- "configuration.\n", gt->io);
|
|
- else
|
|
- v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io);
|
|
- } else if (probe) {
|
|
- v4l2_err(v4l2_dev, "Automatic probing failed and no "
|
|
- "fixed I/O port defined.\n");
|
|
- return -ENODEV;
|
|
- } else {
|
|
- v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed "
|
|
- "I/O port defined.");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- res = v4l2_device_register(NULL, v4l2_dev);
|
|
- if (res < 0) {
|
|
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
- release_region(gt->io, 1);
|
|
- return res;
|
|
- }
|
|
-
|
|
- strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name));
|
|
- gt->vdev.v4l2_dev = v4l2_dev;
|
|
- gt->vdev.fops = &gemtek_fops;
|
|
- gt->vdev.ioctl_ops = &gemtek_ioctl_ops;
|
|
- gt->vdev.release = video_device_release_empty;
|
|
- video_set_drvdata(>->vdev, gt);
|
|
-
|
|
- /* Set defaults */
|
|
- gt->lastfreq = GEMTEK_LOWFREQ;
|
|
- gt->bu2614data = 0;
|
|
-
|
|
- if (initmute)
|
|
- gemtek_mute(gt);
|
|
-
|
|
- if (video_register_device(>->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
|
- v4l2_device_unregister(v4l2_dev);
|
|
- release_region(gt->io, 1);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- return 0;
|
|
+ gemtek_driver.probe = probe;
|
|
+ return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX);
|
|
}
|
|
|
|
-/*
|
|
- * Module cleanup
|
|
- */
|
|
static void __exit gemtek_exit(void)
|
|
{
|
|
- struct gemtek *gt = &gemtek_card;
|
|
- struct v4l2_device *v4l2_dev = >->v4l2_dev;
|
|
-
|
|
- if (shutdown) {
|
|
- hardmute = 1; /* Turn off PLL */
|
|
- gemtek_mute(gt);
|
|
- } else {
|
|
- v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n");
|
|
- }
|
|
-
|
|
- video_unregister_device(>->vdev);
|
|
- v4l2_device_unregister(>->v4l2_dev);
|
|
- release_region(gt->io, 1);
|
|
+ hardmute = 1; /* Turn off PLL */
|
|
+ isa_unregister_driver(&gemtek_driver.driver);
|
|
}
|
|
|
|
module_init(gemtek_init);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-rtrack2.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-rtrack2.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-rtrack2.c
|
|
@@ -1,11 +1,12 @@
|
|
-/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
|
|
+/*
|
|
+ * RadioTrack II driver
|
|
+ * Copyright 1998 Ben Pfaff
|
|
*
|
|
* Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
|
|
* Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
|
|
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
|
|
*
|
|
- * TODO: Allow for more than one of these foolish entities :-)
|
|
- *
|
|
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
|
|
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
|
|
*/
|
|
|
|
@@ -16,325 +17,123 @@
|
|
#include <linux/videodev2.h> /* kernel radio structs */
|
|
#include <linux/mutex.h>
|
|
#include <linux/io.h> /* outb, outb_p */
|
|
+#include <linux/slab.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
+#include "radio-isa.h"
|
|
|
|
MODULE_AUTHOR("Ben Pfaff");
|
|
MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
|
|
MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION("0.0.3");
|
|
+MODULE_VERSION("0.1.99");
|
|
|
|
#ifndef CONFIG_RADIO_RTRACK2_PORT
|
|
#define CONFIG_RADIO_RTRACK2_PORT -1
|
|
#endif
|
|
|
|
-static int io = CONFIG_RADIO_RTRACK2_PORT;
|
|
-static int radio_nr = -1;
|
|
-
|
|
-module_param(io, int, 0);
|
|
-MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
|
|
-module_param(radio_nr, int, 0);
|
|
-
|
|
-struct rtrack2
|
|
-{
|
|
- struct v4l2_device v4l2_dev;
|
|
- struct video_device vdev;
|
|
- int io;
|
|
- unsigned long curfreq;
|
|
- int muted;
|
|
- struct mutex lock;
|
|
-};
|
|
-
|
|
-static struct rtrack2 rtrack2_card;
|
|
+#define RTRACK2_MAX 2
|
|
|
|
+static int io[RTRACK2_MAX] = { [0] = CONFIG_RADIO_RTRACK2_PORT,
|
|
+ [1 ... (RTRACK2_MAX - 1)] = -1 };
|
|
+static int radio_nr[RTRACK2_MAX] = { [0 ... (RTRACK2_MAX - 1)] = -1 };
|
|
|
|
-/* local things */
|
|
-
|
|
-static void rt_mute(struct rtrack2 *dev)
|
|
-{
|
|
- if (dev->muted)
|
|
- return;
|
|
- mutex_lock(&dev->lock);
|
|
- outb(1, dev->io);
|
|
- mutex_unlock(&dev->lock);
|
|
- dev->muted = 1;
|
|
-}
|
|
+module_param_array(io, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)");
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|
|
|
-static void rt_unmute(struct rtrack2 *dev)
|
|
+static struct radio_isa_card *rtrack2_alloc(void)
|
|
{
|
|
- if(dev->muted == 0)
|
|
- return;
|
|
- mutex_lock(&dev->lock);
|
|
- outb(0, dev->io);
|
|
- mutex_unlock(&dev->lock);
|
|
- dev->muted = 0;
|
|
+ return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL);
|
|
}
|
|
|
|
-static void zero(struct rtrack2 *dev)
|
|
+static void zero(struct radio_isa_card *isa)
|
|
{
|
|
- outb_p(1, dev->io);
|
|
- outb_p(3, dev->io);
|
|
- outb_p(1, dev->io);
|
|
+ outb_p(1, isa->io);
|
|
+ outb_p(3, isa->io);
|
|
+ outb_p(1, isa->io);
|
|
}
|
|
|
|
-static void one(struct rtrack2 *dev)
|
|
+static void one(struct radio_isa_card *isa)
|
|
{
|
|
- outb_p(5, dev->io);
|
|
- outb_p(7, dev->io);
|
|
- outb_p(5, dev->io);
|
|
+ outb_p(5, isa->io);
|
|
+ outb_p(7, isa->io);
|
|
+ outb_p(5, isa->io);
|
|
}
|
|
|
|
-static int rt_setfreq(struct rtrack2 *dev, unsigned long freq)
|
|
+static int rtrack2_s_frequency(struct radio_isa_card *isa, u32 freq)
|
|
{
|
|
int i;
|
|
|
|
- mutex_lock(&dev->lock);
|
|
- dev->curfreq = freq;
|
|
freq = freq / 200 + 856;
|
|
|
|
- outb_p(0xc8, dev->io);
|
|
- outb_p(0xc9, dev->io);
|
|
- outb_p(0xc9, dev->io);
|
|
+ outb_p(0xc8, isa->io);
|
|
+ outb_p(0xc9, isa->io);
|
|
+ outb_p(0xc9, isa->io);
|
|
|
|
for (i = 0; i < 10; i++)
|
|
- zero(dev);
|
|
+ zero(isa);
|
|
|
|
for (i = 14; i >= 0; i--)
|
|
if (freq & (1 << i))
|
|
- one(dev);
|
|
+ one(isa);
|
|
else
|
|
- zero(dev);
|
|
-
|
|
- outb_p(0xc8, dev->io);
|
|
- if (!dev->muted)
|
|
- outb_p(0, dev->io);
|
|
-
|
|
- mutex_unlock(&dev->lock);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_querycap(struct file *file, void *priv,
|
|
- struct v4l2_capability *v)
|
|
-{
|
|
- strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver));
|
|
- strlcpy(v->card, "RadioTrack II", sizeof(v->card));
|
|
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- return v->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static int rt_getsigstr(struct rtrack2 *dev)
|
|
-{
|
|
- int sig = 1;
|
|
-
|
|
- mutex_lock(&dev->lock);
|
|
- if (inb(dev->io) & 2) /* bit set = no signal present */
|
|
- sig = 0;
|
|
- mutex_unlock(&dev->lock);
|
|
- return sig;
|
|
-}
|
|
+ zero(isa);
|
|
|
|
-static int vidioc_g_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- struct rtrack2 *rt = video_drvdata(file);
|
|
-
|
|
- if (v->index > 0)
|
|
- return -EINVAL;
|
|
-
|
|
- strlcpy(v->name, "FM", sizeof(v->name));
|
|
- v->type = V4L2_TUNER_RADIO;
|
|
- v->rangelow = 88 * 16000;
|
|
- v->rangehigh = 108 * 16000;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
|
|
- v->capability = V4L2_TUNER_CAP_LOW;
|
|
- v->audmode = V4L2_TUNER_MODE_MONO;
|
|
- v->signal = 0xFFFF * rt_getsigstr(rt);
|
|
+ outb_p(0xc8, isa->io);
|
|
+ if (!v4l2_ctrl_g_ctrl(isa->mute))
|
|
+ outb_p(0, isa->io);
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_s_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
+static u32 rtrack2_g_signal(struct radio_isa_card *isa)
|
|
{
|
|
- struct rtrack2 *rt = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
- return -EINVAL;
|
|
- rt_setfreq(rt, f->frequency);
|
|
- return 0;
|
|
+ /* bit set = no signal present */
|
|
+ return (inb(isa->io) & 2) ? 0 : 0xffff;
|
|
}
|
|
|
|
-static int vidioc_g_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
+static int rtrack2_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
|
|
{
|
|
- struct rtrack2 *rt = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0)
|
|
- return -EINVAL;
|
|
- f->type = V4L2_TUNER_RADIO;
|
|
- f->frequency = rt->curfreq;
|
|
+ outb(mute, isa->io);
|
|
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);
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535, 65535);
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct rtrack2 *rt = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- ctrl->value = rt->muted;
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- if (rt->muted)
|
|
- ctrl->value = 0;
|
|
- else
|
|
- ctrl->value = 65535;
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct rtrack2 *rt = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- if (ctrl->value)
|
|
- rt_mute(rt);
|
|
- else
|
|
- rt_unmute(rt);
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- if (ctrl->value)
|
|
- rt_unmute(rt);
|
|
- else
|
|
- rt_mute(rt);
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-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_g_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- a->index = 0;
|
|
- strlcpy(a->name, "Radio", sizeof(a->name));
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- return a->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static const struct v4l2_file_operations rtrack2_fops = {
|
|
- .owner = THIS_MODULE,
|
|
- .unlocked_ioctl = video_ioctl2,
|
|
+static const struct radio_isa_ops rtrack2_ops = {
|
|
+ .alloc = rtrack2_alloc,
|
|
+ .s_mute_volume = rtrack2_s_mute_volume,
|
|
+ .s_frequency = rtrack2_s_frequency,
|
|
+ .g_signal = rtrack2_g_signal,
|
|
};
|
|
|
|
-static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = {
|
|
- .vidioc_querycap = vidioc_querycap,
|
|
- .vidioc_g_tuner = vidioc_g_tuner,
|
|
- .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,
|
|
+static const int rtrack2_ioports[] = { 0x20f, 0x30f };
|
|
+
|
|
+static struct radio_isa_driver rtrack2_driver = {
|
|
+ .driver = {
|
|
+ .match = radio_isa_match,
|
|
+ .probe = radio_isa_probe,
|
|
+ .remove = radio_isa_remove,
|
|
+ .driver = {
|
|
+ .name = "radio-rtrack2",
|
|
+ },
|
|
+ },
|
|
+ .io_params = io,
|
|
+ .radio_nr_params = radio_nr,
|
|
+ .io_ports = rtrack2_ioports,
|
|
+ .num_of_io_ports = ARRAY_SIZE(rtrack2_ioports),
|
|
+ .region_size = 4,
|
|
+ .card = "AIMSlab RadioTrack II",
|
|
+ .ops = &rtrack2_ops,
|
|
+ .has_stereo = true,
|
|
};
|
|
|
|
static int __init rtrack2_init(void)
|
|
{
|
|
- struct rtrack2 *dev = &rtrack2_card;
|
|
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
|
|
- int res;
|
|
-
|
|
- strlcpy(v4l2_dev->name, "rtrack2", sizeof(v4l2_dev->name));
|
|
- dev->io = io;
|
|
- if (dev->io == -1) {
|
|
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or io=0x30c\n");
|
|
- return -EINVAL;
|
|
- }
|
|
- if (!request_region(dev->io, 4, "rtrack2")) {
|
|
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", dev->io);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- res = v4l2_device_register(NULL, v4l2_dev);
|
|
- if (res < 0) {
|
|
- release_region(dev->io, 4);
|
|
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
- return res;
|
|
- }
|
|
-
|
|
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
|
|
- dev->vdev.v4l2_dev = v4l2_dev;
|
|
- dev->vdev.fops = &rtrack2_fops;
|
|
- dev->vdev.ioctl_ops = &rtrack2_ioctl_ops;
|
|
- dev->vdev.release = video_device_release_empty;
|
|
- video_set_drvdata(&dev->vdev, dev);
|
|
-
|
|
- /* mute card - prevents noisy bootups */
|
|
- outb(1, dev->io);
|
|
- dev->muted = 1;
|
|
-
|
|
- mutex_init(&dev->lock);
|
|
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
|
- v4l2_device_unregister(v4l2_dev);
|
|
- release_region(dev->io, 4);
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- v4l2_info(v4l2_dev, "AIMSlab Radiotrack II card driver.\n");
|
|
-
|
|
- return 0;
|
|
+ return isa_register_driver(&rtrack2_driver.driver, RTRACK2_MAX);
|
|
}
|
|
|
|
static void __exit rtrack2_exit(void)
|
|
{
|
|
- struct rtrack2 *dev = &rtrack2_card;
|
|
-
|
|
- video_unregister_device(&dev->vdev);
|
|
- v4l2_device_unregister(&dev->v4l2_dev);
|
|
- release_region(dev->io, 4);
|
|
+ isa_unregister_driver(&rtrack2_driver.driver);
|
|
}
|
|
|
|
module_init(rtrack2_init);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-terratec.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-terratec.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-terratec.c
|
|
@@ -16,11 +16,7 @@
|
|
* Frequency control is done digitally -- ie out(port,encodefreq(95.8));
|
|
* Volume Control is done digitally
|
|
*
|
|
- * there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday
|
|
- * (as soon i have understand how to get started :)
|
|
- * If you can help me out with that, please contact me!!
|
|
- *
|
|
- *
|
|
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
|
|
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
|
|
*/
|
|
|
|
@@ -30,43 +26,24 @@
|
|
#include <linux/videodev2.h> /* kernel radio structs */
|
|
#include <linux/mutex.h>
|
|
#include <linux/io.h> /* outb, outb_p */
|
|
+#include <linux/slab.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
+#include "radio-isa.h"
|
|
|
|
-MODULE_AUTHOR("R.OFFERMANNS & others");
|
|
+MODULE_AUTHOR("R. Offermans & others");
|
|
MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
|
|
MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION("0.0.3");
|
|
-
|
|
-#ifndef CONFIG_RADIO_TERRATEC_PORT
|
|
-#define CONFIG_RADIO_TERRATEC_PORT 0x590
|
|
-#endif
|
|
+MODULE_VERSION("0.1.99");
|
|
|
|
-static int io = CONFIG_RADIO_TERRATEC_PORT;
|
|
+/* Note: there seems to be only one possible port (0x590), but without
|
|
+ hardware this is hard to verify. For now, this is the only one we will
|
|
+ support. */
|
|
+static int io = 0x590;
|
|
static int radio_nr = -1;
|
|
|
|
-module_param(io, int, 0);
|
|
-MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
|
|
-module_param(radio_nr, int, 0);
|
|
-
|
|
-static struct v4l2_queryctrl radio_qctrl[] = {
|
|
- {
|
|
- .id = V4L2_CID_AUDIO_MUTE,
|
|
- .name = "Mute",
|
|
- .minimum = 0,
|
|
- .maximum = 1,
|
|
- .default_value = 1,
|
|
- .type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
- },{
|
|
- .id = V4L2_CID_AUDIO_VOLUME,
|
|
- .name = "Volume",
|
|
- .minimum = 0,
|
|
- .maximum = 0xff,
|
|
- .step = 1,
|
|
- .default_value = 0xff,
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
- }
|
|
-};
|
|
+module_param(radio_nr, int, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device number");
|
|
|
|
#define WRT_DIS 0x00
|
|
#define CLK_OFF 0x00
|
|
@@ -76,63 +53,24 @@ static struct v4l2_queryctrl radio_qctrl
|
|
#define CLK_ON 0x08
|
|
#define WRT_EN 0x10
|
|
|
|
-struct terratec
|
|
+static struct radio_isa_card *terratec_alloc(void)
|
|
{
|
|
- struct v4l2_device v4l2_dev;
|
|
- struct video_device vdev;
|
|
- int io;
|
|
- int curvol;
|
|
- unsigned long curfreq;
|
|
- int muted;
|
|
- struct mutex lock;
|
|
-};
|
|
-
|
|
-static struct terratec terratec_card;
|
|
-
|
|
-/* local things */
|
|
+ return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL);
|
|
+}
|
|
|
|
-static void tt_write_vol(struct terratec *tt, int volume)
|
|
+static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
|
|
{
|
|
int i;
|
|
|
|
- volume = volume + (volume * 32); /* change both channels */
|
|
- mutex_lock(&tt->lock);
|
|
+ if (mute)
|
|
+ vol = 0;
|
|
+ vol = vol + (vol * 32); /* change both channels */
|
|
for (i = 0; i < 8; i++) {
|
|
- if (volume & (0x80 >> i))
|
|
- outb(0x80, tt->io + 1);
|
|
+ if (vol & (0x80 >> i))
|
|
+ outb(0x80, isa->io + 1);
|
|
else
|
|
- outb(0x00, tt->io + 1);
|
|
+ outb(0x00, isa->io + 1);
|
|
}
|
|
- mutex_unlock(&tt->lock);
|
|
-}
|
|
-
|
|
-
|
|
-
|
|
-static void tt_mute(struct terratec *tt)
|
|
-{
|
|
- tt->muted = 1;
|
|
- tt_write_vol(tt, 0);
|
|
-}
|
|
-
|
|
-static int tt_setvol(struct terratec *tt, int vol)
|
|
-{
|
|
- if (vol == tt->curvol) { /* requested volume = current */
|
|
- if (tt->muted) { /* user is unmuting the card */
|
|
- tt->muted = 0;
|
|
- tt_write_vol(tt, vol); /* enable card */
|
|
- }
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if (vol == 0) { /* volume = 0 means mute the card */
|
|
- tt_write_vol(tt, 0); /* "turn off card" by setting vol to 0 */
|
|
- tt->curvol = vol; /* track the volume state! */
|
|
- return 0;
|
|
- }
|
|
-
|
|
- tt->muted = 0;
|
|
- tt_write_vol(tt, vol);
|
|
- tt->curvol = vol;
|
|
return 0;
|
|
}
|
|
|
|
@@ -140,20 +78,15 @@ static int tt_setvol(struct terratec *tt
|
|
/* this is the worst part in this driver */
|
|
/* many more or less strange things are going on here, but hey, it works :) */
|
|
|
|
-static int tt_setfreq(struct terratec *tt, unsigned long freq1)
|
|
+static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq)
|
|
{
|
|
- int freq;
|
|
int i;
|
|
int p;
|
|
- int temp;
|
|
+ int temp;
|
|
long rest;
|
|
unsigned char buffer[25]; /* we have to bit shift 25 registers */
|
|
|
|
- mutex_lock(&tt->lock);
|
|
-
|
|
- tt->curfreq = freq1;
|
|
-
|
|
- freq = freq1 / 160; /* convert the freq. to a nice to handle value */
|
|
+ freq = freq / 160; /* convert the freq. to a nice to handle value */
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
rest = freq * 10 + 10700; /* I once had understood what is going on here */
|
|
@@ -175,239 +108,61 @@ static int tt_setfreq(struct terratec *t
|
|
|
|
for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */
|
|
if (buffer[i] == 1) {
|
|
- outb(WRT_EN | DATA, tt->io);
|
|
- outb(WRT_EN | DATA | CLK_ON, tt->io);
|
|
- outb(WRT_EN | DATA, tt->io);
|
|
+ outb(WRT_EN | DATA, isa->io);
|
|
+ outb(WRT_EN | DATA | CLK_ON, isa->io);
|
|
+ outb(WRT_EN | DATA, isa->io);
|
|
} else {
|
|
- outb(WRT_EN | 0x00, tt->io);
|
|
- outb(WRT_EN | 0x00 | CLK_ON, tt->io);
|
|
- }
|
|
- }
|
|
- outb(0x00, tt->io);
|
|
-
|
|
- mutex_unlock(&tt->lock);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int tt_getsigstr(struct terratec *tt)
|
|
-{
|
|
- if (inb(tt->io) & 2) /* bit set = no signal present */
|
|
- return 0;
|
|
- return 1; /* signal present */
|
|
-}
|
|
-
|
|
-static int vidioc_querycap(struct file *file, void *priv,
|
|
- struct v4l2_capability *v)
|
|
-{
|
|
- strlcpy(v->driver, "radio-terratec", sizeof(v->driver));
|
|
- strlcpy(v->card, "ActiveRadio", sizeof(v->card));
|
|
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- struct terratec *tt = video_drvdata(file);
|
|
-
|
|
- if (v->index > 0)
|
|
- return -EINVAL;
|
|
-
|
|
- strlcpy(v->name, "FM", sizeof(v->name));
|
|
- v->type = V4L2_TUNER_RADIO;
|
|
- v->rangelow = 87 * 16000;
|
|
- v->rangehigh = 108 * 16000;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
|
|
- v->capability = V4L2_TUNER_CAP_LOW;
|
|
- v->audmode = V4L2_TUNER_MODE_MONO;
|
|
- v->signal = 0xFFFF * tt_getsigstr(tt);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- return v->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct terratec *tt = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
- return -EINVAL;
|
|
- tt_setfreq(tt, f->frequency);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct terratec *tt = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0)
|
|
- return -EINVAL;
|
|
- f->type = V4L2_TUNER_RADIO;
|
|
- f->frequency = tt->curfreq;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_queryctrl(struct file *file, void *priv,
|
|
- struct v4l2_queryctrl *qc)
|
|
-{
|
|
- int i;
|
|
-
|
|
- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
|
|
- if (qc->id && qc->id == radio_qctrl[i].id) {
|
|
- memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
|
|
- return 0;
|
|
+ outb(WRT_EN | 0x00, isa->io);
|
|
+ outb(WRT_EN | 0x00 | CLK_ON, isa->io);
|
|
}
|
|
}
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct terratec *tt = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- if (tt->muted)
|
|
- ctrl->value = 1;
|
|
- else
|
|
- ctrl->value = 0;
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- ctrl->value = tt->curvol * 6554;
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct terratec *tt = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- if (ctrl->value)
|
|
- tt_mute(tt);
|
|
- else
|
|
- tt_setvol(tt,tt->curvol);
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- tt_setvol(tt,ctrl->value);
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
|
|
-{
|
|
- *i = 0;
|
|
+ outb(0x00, isa->io);
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
|
|
+static u32 terratec_g_signal(struct radio_isa_card *isa)
|
|
{
|
|
- return i ? -EINVAL : 0;
|
|
+ /* bit set = no signal present */
|
|
+ return (inb(isa->io) & 2) ? 0 : 0xffff;
|
|
}
|
|
|
|
-static int vidioc_g_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- a->index = 0;
|
|
- strlcpy(a->name, "Radio", sizeof(a->name));
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- return a->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static const struct v4l2_file_operations terratec_fops = {
|
|
- .owner = THIS_MODULE,
|
|
- .unlocked_ioctl = video_ioctl2,
|
|
+static const struct radio_isa_ops terratec_ops = {
|
|
+ .alloc = terratec_alloc,
|
|
+ .s_mute_volume = terratec_s_mute_volume,
|
|
+ .s_frequency = terratec_s_frequency,
|
|
+ .g_signal = terratec_g_signal,
|
|
};
|
|
|
|
-static const struct v4l2_ioctl_ops terratec_ioctl_ops = {
|
|
- .vidioc_querycap = vidioc_querycap,
|
|
- .vidioc_g_tuner = vidioc_g_tuner,
|
|
- .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,
|
|
+static const int terratec_ioports[] = { 0x590 };
|
|
+
|
|
+static struct radio_isa_driver terratec_driver = {
|
|
+ .driver = {
|
|
+ .match = radio_isa_match,
|
|
+ .probe = radio_isa_probe,
|
|
+ .remove = radio_isa_remove,
|
|
+ .driver = {
|
|
+ .name = "radio-terratec",
|
|
+ },
|
|
+ },
|
|
+ .io_params = &io,
|
|
+ .radio_nr_params = &radio_nr,
|
|
+ .io_ports = terratec_ioports,
|
|
+ .num_of_io_ports = ARRAY_SIZE(terratec_ioports),
|
|
+ .region_size = 2,
|
|
+ .card = "TerraTec ActiveRadio",
|
|
+ .ops = &terratec_ops,
|
|
+ .has_stereo = true,
|
|
+ .max_volume = 10,
|
|
};
|
|
|
|
static int __init terratec_init(void)
|
|
{
|
|
- struct terratec *tt = &terratec_card;
|
|
- struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
|
|
- int res;
|
|
-
|
|
- strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name));
|
|
- tt->io = io;
|
|
- if (tt->io == -1) {
|
|
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n");
|
|
- return -EINVAL;
|
|
- }
|
|
- if (!request_region(tt->io, 2, "terratec")) {
|
|
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", io);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- res = v4l2_device_register(NULL, v4l2_dev);
|
|
- if (res < 0) {
|
|
- release_region(tt->io, 2);
|
|
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
- return res;
|
|
- }
|
|
-
|
|
- strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name));
|
|
- tt->vdev.v4l2_dev = v4l2_dev;
|
|
- tt->vdev.fops = &terratec_fops;
|
|
- tt->vdev.ioctl_ops = &terratec_ioctl_ops;
|
|
- tt->vdev.release = video_device_release_empty;
|
|
- video_set_drvdata(&tt->vdev, tt);
|
|
-
|
|
- mutex_init(&tt->lock);
|
|
-
|
|
- /* mute card - prevents noisy bootups */
|
|
- tt_write_vol(tt, 0);
|
|
-
|
|
- if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
|
- v4l2_device_unregister(&tt->v4l2_dev);
|
|
- release_region(tt->io, 2);
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n");
|
|
- return 0;
|
|
+ return isa_register_driver(&terratec_driver.driver, 1);
|
|
}
|
|
|
|
static void __exit terratec_exit(void)
|
|
{
|
|
- struct terratec *tt = &terratec_card;
|
|
- struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
|
|
-
|
|
- video_unregister_device(&tt->vdev);
|
|
- v4l2_device_unregister(&tt->v4l2_dev);
|
|
- release_region(tt->io, 2);
|
|
- v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n");
|
|
+ isa_unregister_driver(&terratec_driver.driver);
|
|
}
|
|
|
|
module_init(terratec_init);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-trust.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-trust.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-trust.c
|
|
@@ -21,13 +21,15 @@
|
|
#include <linux/ioport.h>
|
|
#include <linux/videodev2.h>
|
|
#include <linux/io.h>
|
|
+#include <linux/slab.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
+#include "radio-isa.h"
|
|
|
|
MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
|
|
MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
|
|
MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION("0.0.3");
|
|
+MODULE_VERSION("0.1.99");
|
|
|
|
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
|
|
|
|
@@ -35,39 +37,38 @@ MODULE_VERSION("0.0.3");
|
|
#define CONFIG_RADIO_TRUST_PORT -1
|
|
#endif
|
|
|
|
-static int io = CONFIG_RADIO_TRUST_PORT;
|
|
-static int radio_nr = -1;
|
|
+#define TRUST_MAX 2
|
|
|
|
-module_param(io, int, 0);
|
|
-MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
|
|
-module_param(radio_nr, int, 0);
|
|
+static int io[TRUST_MAX] = { [0] = CONFIG_RADIO_TRUST_PORT,
|
|
+ [1 ... (TRUST_MAX - 1)] = -1 };
|
|
+static int radio_nr[TRUST_MAX] = { [0 ... (TRUST_MAX - 1)] = -1 };
|
|
+
|
|
+module_param_array(io, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(io, "I/O addresses of the Trust FM Radio card (0x350 or 0x358)");
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|
|
|
struct trust {
|
|
- struct v4l2_device v4l2_dev;
|
|
- struct video_device vdev;
|
|
- int io;
|
|
+ struct radio_isa_card isa;
|
|
int ioval;
|
|
- __u16 curvol;
|
|
- __u16 curbass;
|
|
- __u16 curtreble;
|
|
- int muted;
|
|
- unsigned long curfreq;
|
|
- int curstereo;
|
|
- int curmute;
|
|
- struct mutex lock;
|
|
};
|
|
|
|
-static struct trust trust_card;
|
|
+static struct radio_isa_card *trust_alloc(void)
|
|
+{
|
|
+ struct trust *tr = kzalloc(sizeof(*tr), GFP_KERNEL);
|
|
+
|
|
+ return tr ? &tr->isa : NULL;
|
|
+}
|
|
|
|
/* i2c addresses */
|
|
#define TDA7318_ADDR 0x88
|
|
#define TSA6060T_ADDR 0xc4
|
|
|
|
-#define TR_DELAY do { inb(tr->io); inb(tr->io); inb(tr->io); } while (0)
|
|
-#define TR_SET_SCL outb(tr->ioval |= 2, tr->io)
|
|
-#define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->io)
|
|
-#define TR_SET_SDA outb(tr->ioval |= 1, tr->io)
|
|
-#define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->io)
|
|
+#define TR_DELAY do { inb(tr->isa.io); inb(tr->isa.io); inb(tr->isa.io); } while (0)
|
|
+#define TR_SET_SCL outb(tr->ioval |= 2, tr->isa.io)
|
|
+#define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->isa.io)
|
|
+#define TR_SET_SDA outb(tr->ioval |= 1, tr->isa.io)
|
|
+#define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->isa.io)
|
|
|
|
static void write_i2c(struct trust *tr, int n, ...)
|
|
{
|
|
@@ -84,10 +85,10 @@ static void write_i2c(struct trust *tr,
|
|
TR_CLR_SCL;
|
|
TR_DELAY;
|
|
|
|
- for(; n; n--) {
|
|
+ for (; n; n--) {
|
|
val = va_arg(args, unsigned);
|
|
- for(mask = 0x80; mask; mask >>= 1) {
|
|
- if(val & mask)
|
|
+ for (mask = 0x80; mask; mask >>= 1) {
|
|
+ if (val & mask)
|
|
TR_SET_SDA;
|
|
else
|
|
TR_CLR_SDA;
|
|
@@ -115,317 +116,128 @@ static void write_i2c(struct trust *tr,
|
|
va_end(args);
|
|
}
|
|
|
|
-static void tr_setvol(struct trust *tr, __u16 vol)
|
|
+static int trust_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
|
|
{
|
|
- mutex_lock(&tr->lock);
|
|
- tr->curvol = vol / 2048;
|
|
- write_i2c(tr, 2, TDA7318_ADDR, tr->curvol ^ 0x1f);
|
|
- mutex_unlock(&tr->lock);
|
|
-}
|
|
+ struct trust *tr = container_of(isa, struct trust, isa);
|
|
|
|
-static int basstreble2chip[15] = {
|
|
- 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
|
|
-};
|
|
-
|
|
-static void tr_setbass(struct trust *tr, __u16 bass)
|
|
-{
|
|
- mutex_lock(&tr->lock);
|
|
- tr->curbass = bass / 4370;
|
|
- write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[tr->curbass]);
|
|
- mutex_unlock(&tr->lock);
|
|
+ tr->ioval = (tr->ioval & 0xf7) | (mute << 3);
|
|
+ outb(tr->ioval, isa->io);
|
|
+ write_i2c(tr, 2, TDA7318_ADDR, vol ^ 0x1f);
|
|
+ return 0;
|
|
}
|
|
|
|
-static void tr_settreble(struct trust *tr, __u16 treble)
|
|
+static int trust_s_stereo(struct radio_isa_card *isa, bool stereo)
|
|
{
|
|
- mutex_lock(&tr->lock);
|
|
- tr->curtreble = treble / 4370;
|
|
- write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[tr->curtreble]);
|
|
- mutex_unlock(&tr->lock);
|
|
-}
|
|
+ struct trust *tr = container_of(isa, struct trust, isa);
|
|
|
|
-static void tr_setstereo(struct trust *tr, int stereo)
|
|
-{
|
|
- mutex_lock(&tr->lock);
|
|
- tr->curstereo = !!stereo;
|
|
- tr->ioval = (tr->ioval & 0xfb) | (!tr->curstereo << 2);
|
|
- outb(tr->ioval, tr->io);
|
|
- mutex_unlock(&tr->lock);
|
|
-}
|
|
-
|
|
-static void tr_setmute(struct trust *tr, int mute)
|
|
-{
|
|
- mutex_lock(&tr->lock);
|
|
- tr->curmute = !!mute;
|
|
- tr->ioval = (tr->ioval & 0xf7) | (tr->curmute << 3);
|
|
- outb(tr->ioval, tr->io);
|
|
- mutex_unlock(&tr->lock);
|
|
+ tr->ioval = (tr->ioval & 0xfb) | (!stereo << 2);
|
|
+ outb(tr->ioval, isa->io);
|
|
+ return 0;
|
|
}
|
|
|
|
-static int tr_getsigstr(struct trust *tr)
|
|
+static u32 trust_g_signal(struct radio_isa_card *isa)
|
|
{
|
|
int i, v;
|
|
|
|
- mutex_lock(&tr->lock);
|
|
for (i = 0, v = 0; i < 100; i++)
|
|
- v |= inb(tr->io);
|
|
- mutex_unlock(&tr->lock);
|
|
+ v |= inb(isa->io);
|
|
return (v & 1) ? 0 : 0xffff;
|
|
}
|
|
|
|
-static int tr_getstereo(struct trust *tr)
|
|
-{
|
|
- /* don't know how to determine it, just return the setting */
|
|
- return tr->curstereo;
|
|
-}
|
|
-
|
|
-static void tr_setfreq(struct trust *tr, unsigned long f)
|
|
-{
|
|
- mutex_lock(&tr->lock);
|
|
- tr->curfreq = f;
|
|
- f /= 160; /* Convert to 10 kHz units */
|
|
- f += 1070; /* Add 10.7 MHz IF */
|
|
- write_i2c(tr, 5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
|
|
- mutex_unlock(&tr->lock);
|
|
-}
|
|
-
|
|
-static int vidioc_querycap(struct file *file, void *priv,
|
|
- struct v4l2_capability *v)
|
|
-{
|
|
- strlcpy(v->driver, "radio-trust", sizeof(v->driver));
|
|
- strlcpy(v->card, "Trust FM Radio", sizeof(v->card));
|
|
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- struct trust *tr = video_drvdata(file);
|
|
-
|
|
- if (v->index > 0)
|
|
- return -EINVAL;
|
|
-
|
|
- strlcpy(v->name, "FM", sizeof(v->name));
|
|
- v->type = V4L2_TUNER_RADIO;
|
|
- v->rangelow = 87.5 * 16000;
|
|
- v->rangehigh = 108 * 16000;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
|
|
- v->capability = V4L2_TUNER_CAP_LOW;
|
|
- if (tr_getstereo(tr))
|
|
- v->audmode = V4L2_TUNER_MODE_STEREO;
|
|
- else
|
|
- v->audmode = V4L2_TUNER_MODE_MONO;
|
|
- v->signal = tr_getsigstr(tr);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
+static int trust_s_frequency(struct radio_isa_card *isa, u32 freq)
|
|
{
|
|
- struct trust *tr = video_drvdata(file);
|
|
+ struct trust *tr = container_of(isa, struct trust, isa);
|
|
|
|
- if (v->index)
|
|
- return -EINVAL;
|
|
- tr_setstereo(tr, v->audmode == V4L2_TUNER_MODE_STEREO);
|
|
+ freq /= 160; /* Convert to 10 kHz units */
|
|
+ freq += 1070; /* Add 10.7 MHz IF */
|
|
+ write_i2c(tr, 5, TSA6060T_ADDR, (freq << 1) | 1,
|
|
+ freq >> 7, 0x60 | ((freq >> 15) & 1), 0);
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_s_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct trust *tr = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
- return -EINVAL;
|
|
- tr_setfreq(tr, f->frequency);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct trust *tr = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0)
|
|
- return -EINVAL;
|
|
- f->type = V4L2_TUNER_RADIO;
|
|
- f->frequency = tr->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);
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- return v4l2_ctrl_query_fill(qc, 0, 65535, 2048, 65535);
|
|
- case V4L2_CID_AUDIO_BASS:
|
|
- case V4L2_CID_AUDIO_TREBLE:
|
|
- return v4l2_ctrl_query_fill(qc, 0, 65535, 4370, 32768);
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct trust *tr = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- ctrl->value = tr->curmute;
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- ctrl->value = tr->curvol * 2048;
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_BASS:
|
|
- ctrl->value = tr->curbass * 4370;
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_TREBLE:
|
|
- ctrl->value = tr->curtreble * 4370;
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
+static int basstreble2chip[15] = {
|
|
+ 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
|
|
+};
|
|
|
|
-static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
+static int trust_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
- struct trust *tr = video_drvdata(file);
|
|
+ struct radio_isa_card *isa =
|
|
+ container_of(ctrl->handler, struct radio_isa_card, hdl);
|
|
+ struct trust *tr = container_of(isa, struct trust, isa);
|
|
|
|
switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- tr_setmute(tr, ctrl->value);
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- tr_setvol(tr, ctrl->value);
|
|
- return 0;
|
|
case V4L2_CID_AUDIO_BASS:
|
|
- tr_setbass(tr, ctrl->value);
|
|
+ write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[ctrl->val]);
|
|
return 0;
|
|
case V4L2_CID_AUDIO_TREBLE:
|
|
- tr_settreble(tr, ctrl->value);
|
|
+ write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[ctrl->val]);
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
-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_g_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- a->index = 0;
|
|
- strlcpy(a->name, "Radio", sizeof(a->name));
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- return a->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static const struct v4l2_file_operations trust_fops = {
|
|
- .owner = THIS_MODULE,
|
|
- .unlocked_ioctl = video_ioctl2,
|
|
-};
|
|
-
|
|
-static const struct v4l2_ioctl_ops trust_ioctl_ops = {
|
|
- .vidioc_querycap = vidioc_querycap,
|
|
- .vidioc_g_tuner = vidioc_g_tuner,
|
|
- .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,
|
|
+static const struct v4l2_ctrl_ops trust_ctrl_ops = {
|
|
+ .s_ctrl = trust_s_ctrl,
|
|
};
|
|
|
|
-static int __init trust_init(void)
|
|
+static int trust_initialize(struct radio_isa_card *isa)
|
|
{
|
|
- struct trust *tr = &trust_card;
|
|
- struct v4l2_device *v4l2_dev = &tr->v4l2_dev;
|
|
- int res;
|
|
+ struct trust *tr = container_of(isa, struct trust, isa);
|
|
|
|
- strlcpy(v4l2_dev->name, "trust", sizeof(v4l2_dev->name));
|
|
- tr->io = io;
|
|
tr->ioval = 0xf;
|
|
- mutex_init(&tr->lock);
|
|
-
|
|
- if (tr->io == -1) {
|
|
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x0x350 or 0x358\n");
|
|
- return -EINVAL;
|
|
- }
|
|
- if (!request_region(tr->io, 2, "Trust FM Radio")) {
|
|
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", tr->io);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- res = v4l2_device_register(NULL, v4l2_dev);
|
|
- if (res < 0) {
|
|
- release_region(tr->io, 2);
|
|
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
- return res;
|
|
- }
|
|
-
|
|
- strlcpy(tr->vdev.name, v4l2_dev->name, sizeof(tr->vdev.name));
|
|
- tr->vdev.v4l2_dev = v4l2_dev;
|
|
- tr->vdev.fops = &trust_fops;
|
|
- tr->vdev.ioctl_ops = &trust_ioctl_ops;
|
|
- tr->vdev.release = video_device_release_empty;
|
|
- video_set_drvdata(&tr->vdev, tr);
|
|
-
|
|
write_i2c(tr, 2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */
|
|
write_i2c(tr, 2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */
|
|
write_i2c(tr, 2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */
|
|
write_i2c(tr, 2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */
|
|
write_i2c(tr, 2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */
|
|
|
|
- tr_setvol(tr, 0xffff);
|
|
- tr_setbass(tr, 0x8000);
|
|
- tr_settreble(tr, 0x8000);
|
|
- tr_setstereo(tr, 1);
|
|
-
|
|
- /* mute card - prevents noisy bootups */
|
|
- tr_setmute(tr, 1);
|
|
-
|
|
- if (video_register_device(&tr->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
|
- v4l2_device_unregister(v4l2_dev);
|
|
- release_region(tr->io, 2);
|
|
- return -EINVAL;
|
|
- }
|
|
+ v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops,
|
|
+ V4L2_CID_AUDIO_BASS, 0, 15, 1, 8);
|
|
+ v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops,
|
|
+ V4L2_CID_AUDIO_TREBLE, 0, 15, 1, 8);
|
|
+ return isa->hdl.error;
|
|
+}
|
|
|
|
- v4l2_info(v4l2_dev, "Trust FM Radio card driver v1.0.\n");
|
|
+static const struct radio_isa_ops trust_ops = {
|
|
+ .init = trust_initialize,
|
|
+ .alloc = trust_alloc,
|
|
+ .s_mute_volume = trust_s_mute_volume,
|
|
+ .s_frequency = trust_s_frequency,
|
|
+ .s_stereo = trust_s_stereo,
|
|
+ .g_signal = trust_g_signal,
|
|
+};
|
|
|
|
- return 0;
|
|
-}
|
|
+static const int trust_ioports[] = { 0x350, 0x358 };
|
|
+
|
|
+static struct radio_isa_driver trust_driver = {
|
|
+ .driver = {
|
|
+ .match = radio_isa_match,
|
|
+ .probe = radio_isa_probe,
|
|
+ .remove = radio_isa_remove,
|
|
+ .driver = {
|
|
+ .name = "radio-trust",
|
|
+ },
|
|
+ },
|
|
+ .io_params = io,
|
|
+ .radio_nr_params = radio_nr,
|
|
+ .io_ports = trust_ioports,
|
|
+ .num_of_io_ports = ARRAY_SIZE(trust_ioports),
|
|
+ .region_size = 2,
|
|
+ .card = "Trust FM Radio",
|
|
+ .ops = &trust_ops,
|
|
+ .has_stereo = true,
|
|
+ .max_volume = 31,
|
|
+};
|
|
|
|
-static void __exit cleanup_trust_module(void)
|
|
+static int __init trust_init(void)
|
|
{
|
|
- struct trust *tr = &trust_card;
|
|
+ return isa_register_driver(&trust_driver.driver, TRUST_MAX);
|
|
+}
|
|
|
|
- video_unregister_device(&tr->vdev);
|
|
- v4l2_device_unregister(&tr->v4l2_dev);
|
|
- release_region(tr->io, 2);
|
|
+static void __exit trust_exit(void)
|
|
+{
|
|
+ isa_unregister_driver(&trust_driver.driver);
|
|
}
|
|
|
|
module_init(trust_init);
|
|
-module_exit(cleanup_trust_module);
|
|
+module_exit(trust_exit);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-typhoon.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-typhoon.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-typhoon.c
|
|
@@ -33,63 +33,53 @@
|
|
#include <linux/ioport.h> /* request_region */
|
|
#include <linux/videodev2.h> /* kernel radio structs */
|
|
#include <linux/io.h> /* outb, outb_p */
|
|
+#include <linux/slab.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
+#include "radio-isa.h"
|
|
|
|
#define DRIVER_VERSION "0.1.2"
|
|
|
|
MODULE_AUTHOR("Dr. Henrik Seidel");
|
|
MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
|
|
MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION(DRIVER_VERSION);
|
|
+MODULE_VERSION("0.1.99");
|
|
|
|
#ifndef CONFIG_RADIO_TYPHOON_PORT
|
|
#define CONFIG_RADIO_TYPHOON_PORT -1
|
|
#endif
|
|
|
|
#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
|
|
-#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
|
|
+#define CONFIG_RADIO_TYPHOON_MUTEFREQ 87000
|
|
#endif
|
|
|
|
-static int io = CONFIG_RADIO_TYPHOON_PORT;
|
|
-static int radio_nr = -1;
|
|
-
|
|
-module_param(io, int, 0);
|
|
-MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
|
|
-
|
|
-module_param(radio_nr, int, 0);
|
|
+#define TYPHOON_MAX 2
|
|
|
|
+static int io[TYPHOON_MAX] = { [0] = CONFIG_RADIO_TYPHOON_PORT,
|
|
+ [1 ... (TYPHOON_MAX - 1)] = -1 };
|
|
+static int radio_nr[TYPHOON_MAX] = { [0 ... (TYPHOON_MAX - 1)] = -1 };
|
|
static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ;
|
|
+
|
|
+module_param_array(io, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(io, "I/O addresses of the Typhoon card (0x316 or 0x336)");
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|
module_param(mutefreq, ulong, 0);
|
|
MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
|
|
|
|
-#define BANNER "Typhoon Radio Card driver v" DRIVER_VERSION "\n"
|
|
-
|
|
struct typhoon {
|
|
- struct v4l2_device v4l2_dev;
|
|
- struct video_device vdev;
|
|
- int io;
|
|
- int curvol;
|
|
+ struct radio_isa_card isa;
|
|
int muted;
|
|
- unsigned long curfreq;
|
|
- unsigned long mutefreq;
|
|
- struct mutex lock;
|
|
};
|
|
|
|
-static struct typhoon typhoon_card;
|
|
-
|
|
-static void typhoon_setvol_generic(struct typhoon *dev, int vol)
|
|
+static struct radio_isa_card *typhoon_alloc(void)
|
|
{
|
|
- mutex_lock(&dev->lock);
|
|
- vol >>= 14; /* Map 16 bit to 2 bit */
|
|
- vol &= 3;
|
|
- outb_p(vol / 2, dev->io); /* Set the volume, high bit. */
|
|
- outb_p(vol % 2, dev->io + 2); /* Set the volume, low bit. */
|
|
- mutex_unlock(&dev->lock);
|
|
+ struct typhoon *ty = kzalloc(sizeof(*ty), GFP_KERNEL);
|
|
+
|
|
+ return ty ? &ty->isa : NULL;
|
|
}
|
|
|
|
-static int typhoon_setfreq_generic(struct typhoon *dev,
|
|
- unsigned long frequency)
|
|
+static int typhoon_s_frequency(struct radio_isa_card *isa, u32 freq)
|
|
{
|
|
unsigned long outval;
|
|
unsigned long x;
|
|
@@ -105,302 +95,86 @@ static int typhoon_setfreq_generic(struc
|
|
*
|
|
*/
|
|
|
|
- mutex_lock(&dev->lock);
|
|
- x = frequency / 160;
|
|
+ x = freq / 160;
|
|
outval = (x * x + 2500) / 5000;
|
|
outval = (outval * x + 5000) / 10000;
|
|
outval -= (10 * x * x + 10433) / 20866;
|
|
outval += 4 * x - 11505;
|
|
|
|
- outb_p((outval >> 8) & 0x01, dev->io + 4);
|
|
- outb_p(outval >> 9, dev->io + 6);
|
|
- outb_p(outval & 0xff, dev->io + 8);
|
|
- mutex_unlock(&dev->lock);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int typhoon_setfreq(struct typhoon *dev, unsigned long frequency)
|
|
-{
|
|
- typhoon_setfreq_generic(dev, frequency);
|
|
- dev->curfreq = frequency;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void typhoon_mute(struct typhoon *dev)
|
|
-{
|
|
- if (dev->muted == 1)
|
|
- return;
|
|
- typhoon_setvol_generic(dev, 0);
|
|
- typhoon_setfreq_generic(dev, dev->mutefreq);
|
|
- dev->muted = 1;
|
|
-}
|
|
-
|
|
-static void typhoon_unmute(struct typhoon *dev)
|
|
-{
|
|
- if (dev->muted == 0)
|
|
- return;
|
|
- typhoon_setfreq_generic(dev, dev->curfreq);
|
|
- typhoon_setvol_generic(dev, dev->curvol);
|
|
- dev->muted = 0;
|
|
-}
|
|
-
|
|
-static int typhoon_setvol(struct typhoon *dev, int vol)
|
|
-{
|
|
- if (dev->muted && vol != 0) { /* user is unmuting the card */
|
|
- dev->curvol = vol;
|
|
- typhoon_unmute(dev);
|
|
- return 0;
|
|
- }
|
|
- if (vol == dev->curvol) /* requested volume == current */
|
|
- return 0;
|
|
-
|
|
- if (vol == 0) { /* volume == 0 means mute the card */
|
|
- typhoon_mute(dev);
|
|
- dev->curvol = vol;
|
|
- return 0;
|
|
- }
|
|
- typhoon_setvol_generic(dev, vol);
|
|
- dev->curvol = vol;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_querycap(struct file *file, void *priv,
|
|
- struct v4l2_capability *v)
|
|
-{
|
|
- strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
|
|
- strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
|
|
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- if (v->index > 0)
|
|
- return -EINVAL;
|
|
-
|
|
- strlcpy(v->name, "FM", sizeof(v->name));
|
|
- v->type = V4L2_TUNER_RADIO;
|
|
- v->rangelow = 87.5 * 16000;
|
|
- v->rangehigh = 108 * 16000;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
|
|
- v->capability = V4L2_TUNER_CAP_LOW;
|
|
- v->audmode = V4L2_TUNER_MODE_MONO;
|
|
- v->signal = 0xFFFF; /* We can't get the signal strength */
|
|
+ outb_p((outval >> 8) & 0x01, isa->io + 4);
|
|
+ outb_p(outval >> 9, isa->io + 6);
|
|
+ outb_p(outval & 0xff, isa->io + 8);
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_s_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- return v->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
+static int typhoon_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
|
|
{
|
|
- struct typhoon *dev = video_drvdata(file);
|
|
+ struct typhoon *ty = container_of(isa, struct typhoon, isa);
|
|
|
|
- if (f->tuner != 0)
|
|
- return -EINVAL;
|
|
- f->type = V4L2_TUNER_RADIO;
|
|
- f->frequency = dev->curfreq;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct typhoon *dev = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
- return -EINVAL;
|
|
- dev->curfreq = f->frequency;
|
|
- typhoon_setfreq(dev, dev->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);
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- return v4l2_ctrl_query_fill(qc, 0, 65535, 16384, 65535);
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct typhoon *dev = video_drvdata(file);
|
|
+ if (mute)
|
|
+ vol = 0;
|
|
+ vol >>= 14; /* Map 16 bit to 2 bit */
|
|
+ vol &= 3;
|
|
+ outb_p(vol / 2, isa->io); /* Set the volume, high bit. */
|
|
+ outb_p(vol % 2, isa->io + 2); /* Set the volume, low bit. */
|
|
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- ctrl->value = dev->muted;
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- ctrl->value = dev->curvol;
|
|
- return 0;
|
|
+ if (vol == 0 && !ty->muted) {
|
|
+ ty->muted = true;
|
|
+ return typhoon_s_frequency(isa, mutefreq << 4);
|
|
}
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_s_ctrl (struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct typhoon *dev = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- if (ctrl->value)
|
|
- typhoon_mute(dev);
|
|
- else
|
|
- typhoon_unmute(dev);
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- typhoon_setvol(dev, ctrl->value);
|
|
- return 0;
|
|
+ if (vol && ty->muted) {
|
|
+ ty->muted = false;
|
|
+ return typhoon_s_frequency(isa, isa->freq);
|
|
}
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-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_g_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- a->index = 0;
|
|
- strlcpy(a->name, "Radio", sizeof(a->name));
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- return a->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static int vidioc_log_status(struct file *file, void *priv)
|
|
-{
|
|
- struct typhoon *dev = video_drvdata(file);
|
|
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
|
|
-
|
|
- v4l2_info(v4l2_dev, BANNER);
|
|
-#ifdef MODULE
|
|
- v4l2_info(v4l2_dev, "Load type: Driver loaded as a module\n\n");
|
|
-#else
|
|
- v4l2_info(v4l2_dev, "Load type: Driver compiled into kernel\n\n");
|
|
-#endif
|
|
- v4l2_info(v4l2_dev, "frequency = %lu kHz\n", dev->curfreq >> 4);
|
|
- v4l2_info(v4l2_dev, "volume = %d\n", dev->curvol);
|
|
- v4l2_info(v4l2_dev, "mute = %s\n", dev->muted ? "on" : "off");
|
|
- v4l2_info(v4l2_dev, "io = 0x%x\n", dev->io);
|
|
- v4l2_info(v4l2_dev, "mute frequency = %lu kHz\n", dev->mutefreq >> 4);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static const struct v4l2_file_operations typhoon_fops = {
|
|
- .owner = THIS_MODULE,
|
|
- .unlocked_ioctl = video_ioctl2,
|
|
+static const struct radio_isa_ops typhoon_ops = {
|
|
+ .alloc = typhoon_alloc,
|
|
+ .s_mute_volume = typhoon_s_mute_volume,
|
|
+ .s_frequency = typhoon_s_frequency,
|
|
};
|
|
|
|
-static const struct v4l2_ioctl_ops typhoon_ioctl_ops = {
|
|
- .vidioc_log_status = vidioc_log_status,
|
|
- .vidioc_querycap = vidioc_querycap,
|
|
- .vidioc_g_tuner = vidioc_g_tuner,
|
|
- .vidioc_s_tuner = vidioc_s_tuner,
|
|
- .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_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,
|
|
+static const int typhoon_ioports[] = { 0x316, 0x336 };
|
|
+
|
|
+static struct radio_isa_driver typhoon_driver = {
|
|
+ .driver = {
|
|
+ .match = radio_isa_match,
|
|
+ .probe = radio_isa_probe,
|
|
+ .remove = radio_isa_remove,
|
|
+ .driver = {
|
|
+ .name = "radio-typhoon",
|
|
+ },
|
|
+ },
|
|
+ .io_params = io,
|
|
+ .radio_nr_params = radio_nr,
|
|
+ .io_ports = typhoon_ioports,
|
|
+ .num_of_io_ports = ARRAY_SIZE(typhoon_ioports),
|
|
+ .region_size = 8,
|
|
+ .card = "Typhoon Radio",
|
|
+ .ops = &typhoon_ops,
|
|
+ .has_stereo = true,
|
|
+ .max_volume = 3,
|
|
};
|
|
|
|
static int __init typhoon_init(void)
|
|
{
|
|
- struct typhoon *dev = &typhoon_card;
|
|
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
|
|
- int res;
|
|
-
|
|
- strlcpy(v4l2_dev->name, "typhoon", sizeof(v4l2_dev->name));
|
|
- dev->io = io;
|
|
-
|
|
- if (dev->io == -1) {
|
|
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x316 or io=0x336\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- if (mutefreq < 87000 || mutefreq > 108500) {
|
|
- v4l2_err(v4l2_dev, "You must set a frequency (in kHz) used when muting the card,\n");
|
|
- v4l2_err(v4l2_dev, "e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
|
|
- return -EINVAL;
|
|
+ if (mutefreq < 87000 || mutefreq > 108000) {
|
|
+ printk(KERN_ERR "%s: You must set a frequency (in kHz) used when muting the card,\n",
|
|
+ typhoon_driver.driver.driver.name);
|
|
+ printk(KERN_ERR "%s: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108000)\n",
|
|
+ typhoon_driver.driver.driver.name);
|
|
+ return -ENODEV;
|
|
}
|
|
- dev->curfreq = dev->mutefreq = mutefreq << 4;
|
|
-
|
|
- mutex_init(&dev->lock);
|
|
- if (!request_region(dev->io, 8, "typhoon")) {
|
|
- v4l2_err(v4l2_dev, "port 0x%x already in use\n",
|
|
- dev->io);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- res = v4l2_device_register(NULL, v4l2_dev);
|
|
- if (res < 0) {
|
|
- release_region(dev->io, 8);
|
|
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
- return res;
|
|
- }
|
|
- v4l2_info(v4l2_dev, BANNER);
|
|
-
|
|
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
|
|
- dev->vdev.v4l2_dev = v4l2_dev;
|
|
- dev->vdev.fops = &typhoon_fops;
|
|
- dev->vdev.ioctl_ops = &typhoon_ioctl_ops;
|
|
- dev->vdev.release = video_device_release_empty;
|
|
- video_set_drvdata(&dev->vdev, dev);
|
|
-
|
|
- /* mute card - prevents noisy bootups */
|
|
- typhoon_mute(dev);
|
|
-
|
|
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
|
- v4l2_device_unregister(&dev->v4l2_dev);
|
|
- release_region(dev->io, 8);
|
|
- return -EINVAL;
|
|
- }
|
|
- v4l2_info(v4l2_dev, "port 0x%x.\n", dev->io);
|
|
- v4l2_info(v4l2_dev, "mute frequency is %lu kHz.\n", mutefreq);
|
|
-
|
|
- return 0;
|
|
+ return isa_register_driver(&typhoon_driver.driver, TYPHOON_MAX);
|
|
}
|
|
|
|
static void __exit typhoon_exit(void)
|
|
{
|
|
- struct typhoon *dev = &typhoon_card;
|
|
-
|
|
- video_unregister_device(&dev->vdev);
|
|
- v4l2_device_unregister(&dev->v4l2_dev);
|
|
- release_region(dev->io, 8);
|
|
+ isa_unregister_driver(&typhoon_driver.driver);
|
|
}
|
|
|
|
+
|
|
module_init(typhoon_init);
|
|
module_exit(typhoon_exit);
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-zoltrix.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-zoltrix.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-zoltrix.c
|
|
@@ -1,5 +1,6 @@
|
|
-/* zoltrix radio plus driver for Linux radio support
|
|
- * (c) 1998 C. van Schaik <carl@leg.uct.ac.za>
|
|
+/*
|
|
+ * Zoltrix Radio Plus driver
|
|
+ * Copyright 1998 C. van Schaik <carl@leg.uct.ac.za>
|
|
*
|
|
* BUGS
|
|
* Due to the inconsistency in reading from the signal flags
|
|
@@ -27,6 +28,14 @@
|
|
*
|
|
* 2006-07-24 - Converted to V4L2 API
|
|
* by Mauro Carvalho Chehab <mchehab@infradead.org>
|
|
+ *
|
|
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
|
|
+ *
|
|
+ * Note that this is the driver for the Zoltrix Radio Plus.
|
|
+ * This driver does not work for the Zoltrix Radio Plus 108 or the
|
|
+ * Zoltrix Radio Plus for Windows.
|
|
+ *
|
|
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
|
|
*/
|
|
|
|
#include <linux/module.h> /* Modules */
|
|
@@ -36,82 +45,70 @@
|
|
#include <linux/videodev2.h> /* kernel radio structs */
|
|
#include <linux/mutex.h>
|
|
#include <linux/io.h> /* outb, outb_p */
|
|
+#include <linux/slab.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
+#include "radio-isa.h"
|
|
|
|
-MODULE_AUTHOR("C.van Schaik");
|
|
+MODULE_AUTHOR("C. van Schaik");
|
|
MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
|
|
MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION("0.0.3");
|
|
+MODULE_VERSION("0.1.99");
|
|
|
|
#ifndef CONFIG_RADIO_ZOLTRIX_PORT
|
|
#define CONFIG_RADIO_ZOLTRIX_PORT -1
|
|
#endif
|
|
|
|
-static int io = CONFIG_RADIO_ZOLTRIX_PORT;
|
|
-static int radio_nr = -1;
|
|
+#define ZOLTRIX_MAX 2
|
|
|
|
-module_param(io, int, 0);
|
|
-MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
|
|
-module_param(radio_nr, int, 0);
|
|
+static int io[ZOLTRIX_MAX] = { [0] = CONFIG_RADIO_ZOLTRIX_PORT,
|
|
+ [1 ... (ZOLTRIX_MAX - 1)] = -1 };
|
|
+static int radio_nr[ZOLTRIX_MAX] = { [0 ... (ZOLTRIX_MAX - 1)] = -1 };
|
|
+
|
|
+module_param_array(io, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(io, "I/O addresses of the Zoltrix Radio Plus card (0x20c or 0x30c)");
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|
|
|
struct zoltrix {
|
|
- struct v4l2_device v4l2_dev;
|
|
- struct video_device vdev;
|
|
- int io;
|
|
+ struct radio_isa_card isa;
|
|
int curvol;
|
|
- unsigned long curfreq;
|
|
- int muted;
|
|
- unsigned int stereo;
|
|
- struct mutex lock;
|
|
+ bool muted;
|
|
};
|
|
|
|
-static struct zoltrix zoltrix_card;
|
|
+static struct radio_isa_card *zoltrix_alloc(void)
|
|
+{
|
|
+ struct zoltrix *zol = kzalloc(sizeof(*zol), GFP_KERNEL);
|
|
|
|
-static int zol_setvol(struct zoltrix *zol, int vol)
|
|
+ return zol ? &zol->isa : NULL;
|
|
+}
|
|
+
|
|
+static int zoltrix_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
|
|
{
|
|
- zol->curvol = vol;
|
|
- if (zol->muted)
|
|
- return 0;
|
|
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
|
|
|
|
- mutex_lock(&zol->lock);
|
|
- if (vol == 0) {
|
|
- outb(0, zol->io);
|
|
- outb(0, zol->io);
|
|
- inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
|
|
- mutex_unlock(&zol->lock);
|
|
+ zol->curvol = vol;
|
|
+ zol->muted = mute;
|
|
+ if (mute || vol == 0) {
|
|
+ outb(0, isa->io);
|
|
+ outb(0, isa->io);
|
|
+ inb(isa->io + 3); /* Zoltrix needs to be read to confirm */
|
|
return 0;
|
|
}
|
|
|
|
- outb(zol->curvol-1, zol->io);
|
|
+ outb(vol - 1, isa->io);
|
|
msleep(10);
|
|
- inb(zol->io + 2);
|
|
- mutex_unlock(&zol->lock);
|
|
+ inb(isa->io + 2);
|
|
return 0;
|
|
}
|
|
|
|
-static void zol_mute(struct zoltrix *zol)
|
|
-{
|
|
- zol->muted = 1;
|
|
- mutex_lock(&zol->lock);
|
|
- outb(0, zol->io);
|
|
- outb(0, zol->io);
|
|
- inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
|
|
- mutex_unlock(&zol->lock);
|
|
-}
|
|
-
|
|
-static void zol_unmute(struct zoltrix *zol)
|
|
-{
|
|
- zol->muted = 0;
|
|
- zol_setvol(zol, zol->curvol);
|
|
-}
|
|
-
|
|
-static int zol_setfreq(struct zoltrix *zol, unsigned long freq)
|
|
+/* tunes the radio to the desired frequency */
|
|
+static int zoltrix_s_frequency(struct radio_isa_card *isa, u32 freq)
|
|
{
|
|
- /* tunes the radio to the desired frequency */
|
|
- struct v4l2_device *v4l2_dev = &zol->v4l2_dev;
|
|
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
|
|
+ struct v4l2_device *v4l2_dev = &isa->v4l2_dev;
|
|
unsigned long long bitmask, f, m;
|
|
- unsigned int stereo = zol->stereo;
|
|
+ bool stereo = isa->stereo;
|
|
int i;
|
|
|
|
if (freq == 0) {
|
|
@@ -125,340 +122,125 @@ static int zol_setfreq(struct zoltrix *z
|
|
bitmask = 0xc480402c10080000ull;
|
|
i = 45;
|
|
|
|
- mutex_lock(&zol->lock);
|
|
-
|
|
- zol->curfreq = freq;
|
|
+ outb(0, isa->io);
|
|
+ outb(0, isa->io);
|
|
+ inb(isa->io + 3); /* Zoltrix needs to be read to confirm */
|
|
|
|
- outb(0, zol->io);
|
|
- outb(0, zol->io);
|
|
- inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
|
|
-
|
|
- outb(0x40, zol->io);
|
|
- outb(0xc0, zol->io);
|
|
+ outb(0x40, isa->io);
|
|
+ outb(0xc0, isa->io);
|
|
|
|
bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31));
|
|
while (i--) {
|
|
if ((bitmask & 0x8000000000000000ull) != 0) {
|
|
- outb(0x80, zol->io);
|
|
+ outb(0x80, isa->io);
|
|
udelay(50);
|
|
- outb(0x00, zol->io);
|
|
+ outb(0x00, isa->io);
|
|
udelay(50);
|
|
- outb(0x80, zol->io);
|
|
+ outb(0x80, isa->io);
|
|
udelay(50);
|
|
} else {
|
|
- outb(0xc0, zol->io);
|
|
+ outb(0xc0, isa->io);
|
|
udelay(50);
|
|
- outb(0x40, zol->io);
|
|
+ outb(0x40, isa->io);
|
|
udelay(50);
|
|
- outb(0xc0, zol->io);
|
|
+ outb(0xc0, isa->io);
|
|
udelay(50);
|
|
}
|
|
bitmask *= 2;
|
|
}
|
|
/* termination sequence */
|
|
- outb(0x80, zol->io);
|
|
- outb(0xc0, zol->io);
|
|
- outb(0x40, zol->io);
|
|
+ outb(0x80, isa->io);
|
|
+ outb(0xc0, isa->io);
|
|
+ outb(0x40, isa->io);
|
|
udelay(1000);
|
|
- inb(zol->io + 2);
|
|
-
|
|
+ inb(isa->io + 2);
|
|
udelay(1000);
|
|
|
|
- if (zol->muted) {
|
|
- outb(0, zol->io);
|
|
- outb(0, zol->io);
|
|
- inb(zol->io + 3);
|
|
- udelay(1000);
|
|
- }
|
|
-
|
|
- mutex_unlock(&zol->lock);
|
|
-
|
|
- if (!zol->muted)
|
|
- zol_setvol(zol, zol->curvol);
|
|
- return 0;
|
|
+ return zoltrix_s_mute_volume(isa, zol->muted, zol->curvol);
|
|
}
|
|
|
|
/* Get signal strength */
|
|
-static int zol_getsigstr(struct zoltrix *zol)
|
|
+static u32 zoltrix_g_rxsubchans(struct radio_isa_card *isa)
|
|
{
|
|
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
|
|
int a, b;
|
|
|
|
- mutex_lock(&zol->lock);
|
|
- outb(0x00, zol->io); /* This stuff I found to do nothing */
|
|
- outb(zol->curvol, zol->io);
|
|
+ outb(0x00, isa->io); /* This stuff I found to do nothing */
|
|
+ outb(zol->curvol, isa->io);
|
|
msleep(20);
|
|
|
|
- a = inb(zol->io);
|
|
+ a = inb(isa->io);
|
|
msleep(10);
|
|
- b = inb(zol->io);
|
|
-
|
|
- mutex_unlock(&zol->lock);
|
|
+ b = inb(isa->io);
|
|
|
|
- if (a != b)
|
|
- return 0;
|
|
-
|
|
- /* I found this out by playing with a binary scanner on the card io */
|
|
- return a == 0xcf || a == 0xdf || a == 0xef;
|
|
+ return (a == b && a == 0xcf) ?
|
|
+ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
|
|
}
|
|
|
|
-static int zol_is_stereo(struct zoltrix *zol)
|
|
+static u32 zoltrix_g_signal(struct radio_isa_card *isa)
|
|
{
|
|
- int x1, x2;
|
|
-
|
|
- mutex_lock(&zol->lock);
|
|
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
|
|
+ int a, b;
|
|
|
|
- outb(0x00, zol->io);
|
|
- outb(zol->curvol, zol->io);
|
|
+ outb(0x00, isa->io); /* This stuff I found to do nothing */
|
|
+ outb(zol->curvol, isa->io);
|
|
msleep(20);
|
|
|
|
- x1 = inb(zol->io);
|
|
+ a = inb(isa->io);
|
|
msleep(10);
|
|
- x2 = inb(zol->io);
|
|
-
|
|
- mutex_unlock(&zol->lock);
|
|
-
|
|
- return x1 == x2 && x1 == 0xcf;
|
|
-}
|
|
-
|
|
-static int vidioc_querycap(struct file *file, void *priv,
|
|
- struct v4l2_capability *v)
|
|
-{
|
|
- strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver));
|
|
- strlcpy(v->card, "Zoltrix Radio", sizeof(v->card));
|
|
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- struct zoltrix *zol = video_drvdata(file);
|
|
-
|
|
- if (v->index > 0)
|
|
- return -EINVAL;
|
|
-
|
|
- strlcpy(v->name, "FM", sizeof(v->name));
|
|
- v->type = V4L2_TUNER_RADIO;
|
|
- v->rangelow = 88 * 16000;
|
|
- v->rangehigh = 108 * 16000;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
|
|
- v->capability = V4L2_TUNER_CAP_LOW;
|
|
- if (zol_is_stereo(zol))
|
|
- v->audmode = V4L2_TUNER_MODE_STEREO;
|
|
- else
|
|
- v->audmode = V4L2_TUNER_MODE_MONO;
|
|
- v->signal = 0xFFFF * zol_getsigstr(zol);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- return v->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct zoltrix *zol = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
- return -EINVAL;
|
|
- if (zol_setfreq(zol, f->frequency) != 0)
|
|
- return -EINVAL;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct zoltrix *zol = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0)
|
|
- return -EINVAL;
|
|
- f->type = V4L2_TUNER_RADIO;
|
|
- f->frequency = zol->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);
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- return v4l2_ctrl_query_fill(qc, 0, 65535, 4096, 65535);
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct zoltrix *zol = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- ctrl->value = zol->muted;
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- ctrl->value = zol->curvol * 4096;
|
|
- return 0;
|
|
- }
|
|
- return -EINVAL;
|
|
-}
|
|
+ b = inb(isa->io);
|
|
|
|
-static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
-{
|
|
- struct zoltrix *zol = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- if (ctrl->value)
|
|
- zol_mute(zol);
|
|
- else {
|
|
- zol_unmute(zol);
|
|
- zol_setvol(zol, zol->curvol);
|
|
- }
|
|
- return 0;
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
- zol_setvol(zol, ctrl->value / 4096);
|
|
+ if (a != b)
|
|
return 0;
|
|
- }
|
|
- zol->stereo = 1;
|
|
- if (zol_setfreq(zol, zol->curfreq) != 0)
|
|
- return -EINVAL;
|
|
-#if 0
|
|
-/* FIXME: Implement stereo/mono switch on V4L2 */
|
|
- if (v->mode & VIDEO_SOUND_STEREO) {
|
|
- zol->stereo = 1;
|
|
- zol_setfreq(zol, zol->curfreq);
|
|
- }
|
|
- if (v->mode & VIDEO_SOUND_MONO) {
|
|
- zol->stereo = 0;
|
|
- zol_setfreq(zol, zol->curfreq);
|
|
- }
|
|
-#endif
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-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_g_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- a->index = 0;
|
|
- strlcpy(a->name, "Radio", sizeof(a->name));
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
- return 0;
|
|
+ /* I found this out by playing with a binary scanner on the card io */
|
|
+ return (a == 0xcf || a == 0xdf || a == 0xef) ? 0xffff : 0;
|
|
}
|
|
|
|
-static int vidioc_s_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
+static int zoltrix_s_stereo(struct radio_isa_card *isa, bool stereo)
|
|
{
|
|
- return a->index ? -EINVAL : 0;
|
|
+ return zoltrix_s_frequency(isa, isa->freq);
|
|
}
|
|
|
|
-static const struct v4l2_file_operations zoltrix_fops =
|
|
-{
|
|
- .owner = THIS_MODULE,
|
|
- .unlocked_ioctl = video_ioctl2,
|
|
+static const struct radio_isa_ops zoltrix_ops = {
|
|
+ .alloc = zoltrix_alloc,
|
|
+ .s_mute_volume = zoltrix_s_mute_volume,
|
|
+ .s_frequency = zoltrix_s_frequency,
|
|
+ .s_stereo = zoltrix_s_stereo,
|
|
+ .g_rxsubchans = zoltrix_g_rxsubchans,
|
|
+ .g_signal = zoltrix_g_signal,
|
|
};
|
|
|
|
-static const struct v4l2_ioctl_ops zoltrix_ioctl_ops = {
|
|
- .vidioc_querycap = vidioc_querycap,
|
|
- .vidioc_g_tuner = vidioc_g_tuner,
|
|
- .vidioc_s_tuner = vidioc_s_tuner,
|
|
- .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_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,
|
|
+static const int zoltrix_ioports[] = { 0x20c, 0x30c };
|
|
+
|
|
+static struct radio_isa_driver zoltrix_driver = {
|
|
+ .driver = {
|
|
+ .match = radio_isa_match,
|
|
+ .probe = radio_isa_probe,
|
|
+ .remove = radio_isa_remove,
|
|
+ .driver = {
|
|
+ .name = "radio-zoltrix",
|
|
+ },
|
|
+ },
|
|
+ .io_params = io,
|
|
+ .radio_nr_params = radio_nr,
|
|
+ .io_ports = zoltrix_ioports,
|
|
+ .num_of_io_ports = ARRAY_SIZE(zoltrix_ioports),
|
|
+ .region_size = 2,
|
|
+ .card = "Zoltrix Radio Plus",
|
|
+ .ops = &zoltrix_ops,
|
|
+ .has_stereo = true,
|
|
+ .max_volume = 15,
|
|
};
|
|
|
|
static int __init zoltrix_init(void)
|
|
{
|
|
- struct zoltrix *zol = &zoltrix_card;
|
|
- struct v4l2_device *v4l2_dev = &zol->v4l2_dev;
|
|
- int res;
|
|
-
|
|
- strlcpy(v4l2_dev->name, "zoltrix", sizeof(v4l2_dev->name));
|
|
- zol->io = io;
|
|
- if (zol->io == -1) {
|
|
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or 0x30c\n");
|
|
- return -EINVAL;
|
|
- }
|
|
- if (zol->io != 0x20c && zol->io != 0x30c) {
|
|
- v4l2_err(v4l2_dev, "invalid port, try 0x20c or 0x30c\n");
|
|
- return -ENXIO;
|
|
- }
|
|
-
|
|
- if (!request_region(zol->io, 2, "zoltrix")) {
|
|
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", zol->io);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- res = v4l2_device_register(NULL, v4l2_dev);
|
|
- if (res < 0) {
|
|
- release_region(zol->io, 2);
|
|
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
- return res;
|
|
- }
|
|
-
|
|
- mutex_init(&zol->lock);
|
|
-
|
|
- /* mute card - prevents noisy bootups */
|
|
-
|
|
- /* this ensures that the volume is all the way down */
|
|
-
|
|
- outb(0, zol->io);
|
|
- outb(0, zol->io);
|
|
- msleep(20);
|
|
- inb(zol->io + 3);
|
|
-
|
|
- zol->curvol = 0;
|
|
- zol->stereo = 1;
|
|
-
|
|
- strlcpy(zol->vdev.name, v4l2_dev->name, sizeof(zol->vdev.name));
|
|
- zol->vdev.v4l2_dev = v4l2_dev;
|
|
- zol->vdev.fops = &zoltrix_fops;
|
|
- zol->vdev.ioctl_ops = &zoltrix_ioctl_ops;
|
|
- zol->vdev.release = video_device_release_empty;
|
|
- video_set_drvdata(&zol->vdev, zol);
|
|
-
|
|
- if (video_register_device(&zol->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
|
- v4l2_device_unregister(v4l2_dev);
|
|
- release_region(zol->io, 2);
|
|
- return -EINVAL;
|
|
- }
|
|
- v4l2_info(v4l2_dev, "Zoltrix Radio Plus card driver.\n");
|
|
-
|
|
- return 0;
|
|
+ return isa_register_driver(&zoltrix_driver.driver, ZOLTRIX_MAX);
|
|
}
|
|
|
|
static void __exit zoltrix_exit(void)
|
|
{
|
|
- struct zoltrix *zol = &zoltrix_card;
|
|
-
|
|
- video_unregister_device(&zol->vdev);
|
|
- v4l2_device_unregister(&zol->v4l2_dev);
|
|
- release_region(zol->io, 2);
|
|
+ isa_unregister_driver(&zoltrix_driver.driver);
|
|
}
|
|
|
|
module_init(zoltrix_init);
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-core/dvb_frontend.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-core/dvb_frontend.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-core/dvb_frontend.c
|
|
@@ -148,7 +148,7 @@ static int dtv_property_legacy_params_sy
|
|
|
|
static bool has_get_frontend(struct dvb_frontend *fe)
|
|
{
|
|
- return fe->ops.get_frontend;
|
|
+ return fe->ops.get_frontend != NULL;
|
|
}
|
|
|
|
/*
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/xc5000.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/common/tuners/xc5000.c
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/xc5000.c
|
|
@@ -49,9 +49,6 @@ static LIST_HEAD(hybrid_tuner_instance_l
|
|
#define dprintk(level, fmt, arg...) if (debug >= level) \
|
|
printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
|
|
|
|
-#define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
|
|
-#define XC5000_DEFAULT_FIRMWARE_SIZE 12401
|
|
-
|
|
struct xc5000_priv {
|
|
struct tuner_i2c_props i2c_props;
|
|
struct list_head hybrid_tuner_instance_list;
|
|
@@ -62,6 +59,8 @@ struct xc5000_priv {
|
|
u8 video_standard;
|
|
u8 rf_mode;
|
|
u8 radio_input;
|
|
+
|
|
+ int chip_id;
|
|
};
|
|
|
|
/* Misc Defines */
|
|
@@ -204,6 +203,33 @@ static struct XC_TV_STANDARD XC5000_Stan
|
|
{"FM Radio-INPUT1_MONO", 0x0278, 0x9002}
|
|
};
|
|
|
|
+
|
|
+struct xc5000_fw_cfg {
|
|
+ char *name;
|
|
+ u16 size;
|
|
+};
|
|
+
|
|
+static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
|
|
+ .name = "dvb-fe-xc5000-1.6.114.fw",
|
|
+ .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 inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
|
|
+{
|
|
+ switch (chip_id) {
|
|
+ default:
|
|
+ case XC5000A:
|
|
+ return &xc5000a_1_6_114;
|
|
+ case XC5000C:
|
|
+ return &xc5000c_41_024_5_31875;
|
|
+ }
|
|
+}
|
|
+
|
|
static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
|
|
static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
|
|
static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
|
|
@@ -552,12 +578,14 @@ static int xc5000_fwupload(struct dvb_fr
|
|
struct xc5000_priv *priv = fe->tuner_priv;
|
|
const struct firmware *fw;
|
|
int ret;
|
|
+ const struct xc5000_fw_cfg *desired_fw =
|
|
+ xc5000_assign_firmware(priv->chip_id);
|
|
|
|
/* request the firmware, this will block and timeout */
|
|
printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
|
|
- XC5000_DEFAULT_FIRMWARE);
|
|
+ desired_fw->name);
|
|
|
|
- ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE,
|
|
+ ret = request_firmware(&fw, desired_fw->name,
|
|
priv->i2c_props.adap->dev.parent);
|
|
if (ret) {
|
|
printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
|
|
@@ -569,7 +597,7 @@ static int xc5000_fwupload(struct dvb_fr
|
|
ret = XC_RESULT_SUCCESS;
|
|
}
|
|
|
|
- if (fw->size != XC5000_DEFAULT_FIRMWARE_SIZE) {
|
|
+ if (fw->size != desired_fw->size) {
|
|
printk(KERN_ERR "xc5000: firmware incorrect size\n");
|
|
ret = XC_RESULT_RESET_FAILURE;
|
|
} else {
|
|
@@ -1139,6 +1167,13 @@ struct dvb_frontend *xc5000_attach(struc
|
|
if (priv->radio_input == 0)
|
|
priv->radio_input = cfg->radio_input;
|
|
|
|
+ /* don't override chip id if it's already been set
|
|
+ unless explicitly specified */
|
|
+ if ((priv->chip_id == 0) || (cfg->chip_id))
|
|
+ /* use default chip id if none specified, set to 0 so
|
|
+ it can be overridden if this is a hybrid driver */
|
|
+ priv->chip_id = (cfg->chip_id) ? cfg->chip_id : 0;
|
|
+
|
|
/* Check if firmware has been loaded. It is possible that another
|
|
instance of the driver has loaded the firmware.
|
|
*/
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/xc5000.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/common/tuners/xc5000.h
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/xc5000.h
|
|
@@ -27,10 +27,15 @@
|
|
struct dvb_frontend;
|
|
struct i2c_adapter;
|
|
|
|
+#define XC5000A 1
|
|
+#define XC5000C 2
|
|
+
|
|
struct xc5000_config {
|
|
u8 i2c_address;
|
|
u32 if_khz;
|
|
u8 radio_input;
|
|
+
|
|
+ int chip_id;
|
|
};
|
|
|
|
/* xc5000 callback command */
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/tuner-types.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/common/tuners/tuner-types.c
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/tuner-types.c
|
|
@@ -1868,6 +1868,10 @@ struct tunertype tuners[] = {
|
|
.params = tuner_tena_tnf_5337_params,
|
|
.count = ARRAY_SIZE(tuner_tena_tnf_5337_params),
|
|
},
|
|
+ [TUNER_XC5000C] = { /* Xceive 5000C */
|
|
+ .name = "Xceive 5000C tuner",
|
|
+ /* see xc5000.c for details */
|
|
+ },
|
|
};
|
|
EXPORT_SYMBOL(tuners);
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/tuner-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tuner-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tuner-core.c
|
|
@@ -380,6 +380,21 @@ static void set_type(struct i2c_client *
|
|
tune_now = 0;
|
|
break;
|
|
}
|
|
+ case TUNER_XC5000C:
|
|
+ {
|
|
+ struct xc5000_config xc5000c_cfg = {
|
|
+ .i2c_address = t->i2c->addr,
|
|
+ /* if_khz will be set at dvb_attach() */
|
|
+ .if_khz = 0,
|
|
+ .chip_id = XC5000C,
|
|
+ };
|
|
+
|
|
+ if (!dvb_attach(xc5000_attach,
|
|
+ &t->fe, t->i2c->adapter, &xc5000c_cfg))
|
|
+ goto attach_failed;
|
|
+ tune_now = 0;
|
|
+ break;
|
|
+ }
|
|
case TUNER_NXP_TDA18271:
|
|
{
|
|
struct tda18271_config cfg = {
|
|
@@ -1314,18 +1329,7 @@ static struct i2c_driver tuner_driver =
|
|
.id_table = tuner_id,
|
|
};
|
|
|
|
-static __init int init_tuner(void)
|
|
-{
|
|
- return i2c_add_driver(&tuner_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_tuner(void)
|
|
-{
|
|
- i2c_del_driver(&tuner_driver);
|
|
-}
|
|
-
|
|
-module_init(init_tuner);
|
|
-module_exit(exit_tuner);
|
|
+module_i2c_driver(tuner_driver);
|
|
|
|
MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
|
|
MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
|
|
Index: linux-3.3.x86_64/include/media/tuner.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/media/tuner.h
|
|
+++ linux-3.3.x86_64/include/media/tuner.h
|
|
@@ -136,6 +136,7 @@
|
|
#define TUNER_TENA_TNF_5337 86
|
|
|
|
#define TUNER_XC4000 87 /* Xceive Silicon Tuner */
|
|
+#define TUNER_XC5000C 88 /* Xceive Silicon Tuner */
|
|
|
|
/* tv card specific */
|
|
#define TDA9887_PRESENT (1<<0)
|
|
Index: linux-3.3.x86_64/drivers/media/video/tveeprom.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tveeprom.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tveeprom.c
|
|
@@ -286,8 +286,16 @@ hauppauge_tuner[] =
|
|
{ TUNER_ABSENT, "MaxLinear 301"},
|
|
{ TUNER_ABSENT, "Mirics MSi001"},
|
|
{ TUNER_ABSENT, "MaxLinear MxL241SF"},
|
|
- { TUNER_ABSENT, "Xceive XC5000C"},
|
|
+ { TUNER_XC5000C, "Xceive XC5000C"},
|
|
{ TUNER_ABSENT, "Montage M68TS2020"},
|
|
+ { TUNER_ABSENT, "Siano SMS1530"},
|
|
+ { TUNER_ABSENT, "Dibcom 7090"},
|
|
+ { TUNER_ABSENT, "Xceive XC5200C"},
|
|
+ { TUNER_ABSENT, "NXP 18273"},
|
|
+ { TUNER_ABSENT, "Montage M88TS2022"},
|
|
+ /* 180-189 */
|
|
+ { TUNER_ABSENT, "NXP 18272M"},
|
|
+ { TUNER_ABSENT, "NXP 18272S"},
|
|
};
|
|
|
|
/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-sf16fmr2.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-sf16fmr2.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-sf16fmr2.c
|
|
@@ -9,16 +9,23 @@
|
|
#include <linux/delay.h>
|
|
#include <linux/module.h> /* Modules */
|
|
#include <linux/init.h> /* Initdata */
|
|
+#include <linux/slab.h>
|
|
#include <linux/ioport.h> /* request_region */
|
|
#include <linux/io.h> /* outb, outb_p */
|
|
+#include <linux/isa.h>
|
|
#include <sound/tea575x-tuner.h>
|
|
|
|
MODULE_AUTHOR("Ondrej Zary");
|
|
MODULE_DESCRIPTION("MediaForte SF16-FMR2 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");
|
|
+
|
|
struct fmr2 {
|
|
int io;
|
|
+ struct v4l2_device v4l2_dev;
|
|
struct snd_tea575x tea;
|
|
struct v4l2_ctrl *volume;
|
|
struct v4l2_ctrl *balance;
|
|
@@ -26,7 +33,6 @@ struct fmr2 {
|
|
|
|
/* the port is hardwired so no need to support multiple cards */
|
|
#define FMR2_PORT 0x384
|
|
-static struct fmr2 fmr2_card;
|
|
|
|
/* TEA575x tuner pins */
|
|
#define STR_DATA (1 << 0)
|
|
@@ -172,7 +178,7 @@ static int fmr2_tea_ext_init(struct snd_
|
|
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) {
|
|
- printk(KERN_ERR "radio-sf16fmr2: can't initialize contrls\n");
|
|
+ printk(KERN_ERR "radio-sf16fmr2: can't initialize controls\n");
|
|
return tea->ctrl_handler.error;
|
|
}
|
|
}
|
|
@@ -180,26 +186,46 @@ static int fmr2_tea_ext_init(struct snd_
|
|
return 0;
|
|
}
|
|
|
|
-static int __init fmr2_init(void)
|
|
+static int __devinit fmr2_probe(struct device *pdev, unsigned int dev)
|
|
{
|
|
- struct fmr2 *fmr2 = &fmr2_card;
|
|
+ struct fmr2 *fmr2;
|
|
+ int err;
|
|
+
|
|
+ fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL);
|
|
+ if (fmr2 == NULL)
|
|
+ return -ENOMEM;
|
|
|
|
+ strlcpy(fmr2->v4l2_dev.name, dev_name(pdev),
|
|
+ sizeof(fmr2->v4l2_dev.name));
|
|
fmr2->io = FMR2_PORT;
|
|
|
|
- if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
|
|
+ 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;
|
|
}
|
|
|
|
+ dev_set_drvdata(pdev, fmr2);
|
|
+ err = v4l2_device_register(pdev, &fmr2->v4l2_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.ops = &fmr2_tea_ops;
|
|
fmr2->tea.ext_init = fmr2_tea_ext_init;
|
|
strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
|
|
- strcpy(fmr2->tea.bus_info, "ISA");
|
|
+ snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "ISA:%s",
|
|
+ fmr2->v4l2_dev.name);
|
|
|
|
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;
|
|
}
|
|
|
|
@@ -207,12 +233,33 @@ static int __init fmr2_init(void)
|
|
return 0;
|
|
}
|
|
|
|
-static void __exit fmr2_exit(void)
|
|
+static int __exit fmr2_remove(struct device *pdev, unsigned int dev)
|
|
{
|
|
- struct fmr2 *fmr2 = &fmr2_card;
|
|
+ struct fmr2 *fmr2 = dev_get_drvdata(pdev);
|
|
|
|
snd_tea575x_exit(&fmr2->tea);
|
|
release_region(fmr2->io, 2);
|
|
+ v4l2_device_unregister(&fmr2->v4l2_dev);
|
|
+ kfree(fmr2);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct isa_driver fmr2_driver = {
|
|
+ .probe = fmr2_probe,
|
|
+ .remove = fmr2_remove,
|
|
+ .driver = {
|
|
+ .name = "radio-sf16fmr2",
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init fmr2_init(void)
|
|
+{
|
|
+ return isa_register_driver(&fmr2_driver, 1);
|
|
+}
|
|
+
|
|
+static void __exit fmr2_exit(void)
|
|
+{
|
|
+ isa_unregister_driver(&fmr2_driver);
|
|
}
|
|
|
|
module_init(fmr2_init);
|
|
Index: linux-3.3.x86_64/drivers/media/video/davinci/dm355_ccdc.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/davinci/dm355_ccdc.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/davinci/dm355_ccdc.c
|
|
@@ -292,7 +292,7 @@ static int validate_ccdc_param(struct cc
|
|
if ((ccdcparam->med_filt_thres < 0) ||
|
|
(ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
|
|
dev_dbg(ccdc_cfg.dev,
|
|
- "Invalid value of median filter thresold\n");
|
|
+ "Invalid value of median filter threshold\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/Kconfig
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/Kconfig
|
|
+++ linux-3.3.x86_64/drivers/media/video/Kconfig
|
|
@@ -273,6 +273,16 @@ config VIDEO_ADV7180
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called adv7180.
|
|
|
|
+config VIDEO_ADV7183
|
|
+ tristate "Analog Devices ADV7183 decoder"
|
|
+ depends on VIDEO_V4L2 && I2C
|
|
+ ---help---
|
|
+ V4l2 subdevice driver for the Analog Devices
|
|
+ ADV7183 video decoder.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called adv7183.
|
|
+
|
|
config VIDEO_BT819
|
|
tristate "BT819A VideoStream decoder"
|
|
depends on VIDEO_V4L2 && I2C
|
|
@@ -459,6 +469,9 @@ config VIDEO_AK881X
|
|
|
|
comment "Camera sensor devices"
|
|
|
|
+config VIDEO_APTINA_PLL
|
|
+ tristate
|
|
+
|
|
config VIDEO_OV7670
|
|
tristate "OmniVision OV7670 sensor support"
|
|
depends on I2C && VIDEO_V4L2
|
|
@@ -467,9 +480,28 @@ config VIDEO_OV7670
|
|
OV7670 VGA camera. It currently only works with the M88ALP01
|
|
controller.
|
|
|
|
+config VIDEO_VS6624
|
|
+ tristate "ST VS6624 sensor support"
|
|
+ depends on VIDEO_V4L2 && I2C
|
|
+ ---help---
|
|
+ This is a Video4Linux2 sensor-level driver for the ST VS6624
|
|
+ camera.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called vs6624.
|
|
+
|
|
+config VIDEO_MT9M032
|
|
+ tristate "MT9M032 camera sensor support"
|
|
+ depends on I2C && VIDEO_V4L2
|
|
+ select VIDEO_APTINA_PLL
|
|
+ ---help---
|
|
+ This driver supports MT9M032 camera sensors from Aptina, monochrome
|
|
+ models only.
|
|
+
|
|
config VIDEO_MT9P031
|
|
tristate "Aptina MT9P031 support"
|
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
|
+ select VIDEO_APTINA_PLL
|
|
---help---
|
|
This is a Video4Linux2 sensor-level driver for the Aptina
|
|
(Micron) mt9p031 5 Mpixel camera.
|
|
@@ -851,6 +883,8 @@ source "drivers/media/video/davinci/Kcon
|
|
|
|
source "drivers/media/video/omap/Kconfig"
|
|
|
|
+source "drivers/media/video/blackfin/Kconfig"
|
|
+
|
|
config VIDEO_SH_VOU
|
|
tristate "SuperH VOU video output driver"
|
|
depends on VIDEO_DEV && ARCH_SHMOBILE
|
|
@@ -1087,7 +1121,7 @@ config VIDEO_MX2_HOSTSUPPORT
|
|
config VIDEO_MX2
|
|
tristate "i.MX27/i.MX25 Camera Sensor Interface driver"
|
|
depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || ARCH_MX25)
|
|
- select VIDEOBUF_DMA_CONTIG
|
|
+ select VIDEOBUF2_DMA_CONTIG
|
|
select VIDEO_MX2_HOSTSUPPORT
|
|
---help---
|
|
This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor
|
|
@@ -1116,7 +1150,8 @@ config VIDEO_ATMEL_ISI
|
|
|
|
config VIDEO_S5P_MIPI_CSIS
|
|
tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver"
|
|
- depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API
|
|
+ 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.
|
|
|
|
@@ -1176,4 +1211,14 @@ config VIDEO_SAMSUNG_S5P_MFC
|
|
help
|
|
MFC 5.1 driver for V4L2.
|
|
|
|
+config VIDEO_MX2_EMMAPRP
|
|
+ tristate "MX2 eMMa-PrP support"
|
|
+ depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27
|
|
+ select VIDEOBUF2_DMA_CONTIG
|
|
+ select V4L2_MEM2MEM_DEV
|
|
+ help
|
|
+ MX2X chips have a PrP that can be used to process buffers from
|
|
+ memory to memory. Operations include resizing and format
|
|
+ conversion.
|
|
+
|
|
endif # V4L_MEM2MEM_DRIVERS
|
|
Index: linux-3.3.x86_64/drivers/media/video/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/video/Makefile
|
|
@@ -12,16 +12,19 @@ omap2cam-objs := omap24xxcam.o omap24xxc
|
|
|
|
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
|
|
v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
|
|
+ifeq ($(CONFIG_COMPAT),y)
|
|
+ videodev-objs += v4l2-compat-ioctl32.o
|
|
+endif
|
|
|
|
# V4L2 core modules
|
|
|
|
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
|
|
-ifeq ($(CONFIG_COMPAT),y)
|
|
- obj-$(CONFIG_VIDEO_DEV) += v4l2-compat-ioctl32.o
|
|
-endif
|
|
-
|
|
obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o
|
|
|
|
+# Helper modules
|
|
+
|
|
+obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
|
|
+
|
|
# All i2c modules must come first:
|
|
|
|
obj-$(CONFIG_VIDEO_TUNER) += tuner.o
|
|
@@ -40,8 +43,10 @@ obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
|
|
obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
|
|
obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
|
|
obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
|
|
+obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
|
|
obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
|
|
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
|
|
+obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
|
|
obj-$(CONFIG_VIDEO_BT819) += bt819.o
|
|
obj-$(CONFIG_VIDEO_BT856) += bt856.o
|
|
obj-$(CONFIG_VIDEO_BT866) += bt866.o
|
|
@@ -65,6 +70,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083
|
|
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
|
|
obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
|
|
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
|
|
+obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
|
|
obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
|
|
obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
|
|
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
|
|
@@ -177,6 +183,8 @@ obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_
|
|
obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
|
|
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
|
|
|
|
+obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
|
|
+
|
|
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
|
|
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
|
|
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
|
|
@@ -184,6 +192,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5
|
|
|
|
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
|
|
|
|
+obj-$(CONFIG_BLACKFIN) += blackfin/
|
|
+
|
|
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
|
|
|
|
obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
|
|
@@ -199,6 +209,6 @@ obj-y += davinci/
|
|
|
|
obj-$(CONFIG_ARCH_OMAP) += omap/
|
|
|
|
-ccflags-y += -Idrivers/media/dvb/dvb-core
|
|
-ccflags-y += -Idrivers/media/dvb/frontends
|
|
-ccflags-y += -Idrivers/media/common/tuners
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
|
|
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
|
|
Index: linux-3.3.x86_64/drivers/media/video/mx2_emmaprp.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/mx2_emmaprp.c
|
|
@@ -0,0 +1,1008 @@
|
|
+/*
|
|
+ * Support eMMa-PrP through mem2mem framework.
|
|
+ *
|
|
+ * eMMa-PrP is a piece of HW that allows fetching buffers
|
|
+ * from one memory location and do several operations on
|
|
+ * them such as scaling or format conversion giving, as a result
|
|
+ * a new processed buffer in another memory location.
|
|
+ *
|
|
+ * Based on mem2mem_testdev.c by Pawel Osciak.
|
|
+ *
|
|
+ * Copyright (c) 2011 Vista Silicon S.L.
|
|
+ * Javier Martin <javier.martin@vista-silicon.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by the
|
|
+ * Free Software Foundation; either version 2 of the
|
|
+ * License, or (at your option) any later version
|
|
+ */
|
|
+#include <linux/module.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/io.h>
|
|
+
|
|
+#include <linux/platform_device.h>
|
|
+#include <media/v4l2-mem2mem.h>
|
|
+#include <media/v4l2-device.h>
|
|
+#include <media/v4l2-ioctl.h>
|
|
+#include <media/videobuf2-dma-contig.h>
|
|
+#include <asm/sizes.h>
|
|
+
|
|
+#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp"
|
|
+
|
|
+MODULE_DESCRIPTION("Mem-to-mem device which supports eMMa-PrP present in mx2 SoCs");
|
|
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_VERSION("0.0.1");
|
|
+
|
|
+static bool debug;
|
|
+module_param(debug, bool, 0644);
|
|
+
|
|
+#define MIN_W 32
|
|
+#define MIN_H 32
|
|
+#define MAX_W 2040
|
|
+#define MAX_H 2046
|
|
+
|
|
+#define S_ALIGN 1 /* multiple of 2 */
|
|
+#define W_ALIGN_YUV420 3 /* multiple of 8 */
|
|
+#define W_ALIGN_OTHERS 2 /* multiple of 4 */
|
|
+#define H_ALIGN 1 /* multiple of 2 */
|
|
+
|
|
+/* Flags that indicate a format can be used for capture/output */
|
|
+#define MEM2MEM_CAPTURE (1 << 0)
|
|
+#define MEM2MEM_OUTPUT (1 << 1)
|
|
+
|
|
+#define MEM2MEM_NAME "m2m-emmaprp"
|
|
+
|
|
+/* In bytes, per queue */
|
|
+#define MEM2MEM_VID_MEM_LIMIT SZ_16M
|
|
+
|
|
+#define dprintk(dev, fmt, arg...) \
|
|
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
|
|
+
|
|
+/* EMMA PrP */
|
|
+#define PRP_CNTL 0x00
|
|
+#define PRP_INTR_CNTL 0x04
|
|
+#define PRP_INTRSTATUS 0x08
|
|
+#define PRP_SOURCE_Y_PTR 0x0c
|
|
+#define PRP_SOURCE_CB_PTR 0x10
|
|
+#define PRP_SOURCE_CR_PTR 0x14
|
|
+#define PRP_DEST_RGB1_PTR 0x18
|
|
+#define PRP_DEST_RGB2_PTR 0x1c
|
|
+#define PRP_DEST_Y_PTR 0x20
|
|
+#define PRP_DEST_CB_PTR 0x24
|
|
+#define PRP_DEST_CR_PTR 0x28
|
|
+#define PRP_SRC_FRAME_SIZE 0x2c
|
|
+#define PRP_DEST_CH1_LINE_STRIDE 0x30
|
|
+#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
|
|
+#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
|
|
+#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
|
|
+#define PRP_CH2_OUT_IMAGE_SIZE 0x40
|
|
+#define PRP_SRC_LINE_STRIDE 0x44
|
|
+#define PRP_CSC_COEF_012 0x48
|
|
+#define PRP_CSC_COEF_345 0x4c
|
|
+#define PRP_CSC_COEF_678 0x50
|
|
+#define PRP_CH1_RZ_HORI_COEF1 0x54
|
|
+#define PRP_CH1_RZ_HORI_COEF2 0x58
|
|
+#define PRP_CH1_RZ_HORI_VALID 0x5c
|
|
+#define PRP_CH1_RZ_VERT_COEF1 0x60
|
|
+#define PRP_CH1_RZ_VERT_COEF2 0x64
|
|
+#define PRP_CH1_RZ_VERT_VALID 0x68
|
|
+#define PRP_CH2_RZ_HORI_COEF1 0x6c
|
|
+#define PRP_CH2_RZ_HORI_COEF2 0x70
|
|
+#define PRP_CH2_RZ_HORI_VALID 0x74
|
|
+#define PRP_CH2_RZ_VERT_COEF1 0x78
|
|
+#define PRP_CH2_RZ_VERT_COEF2 0x7c
|
|
+#define PRP_CH2_RZ_VERT_VALID 0x80
|
|
+
|
|
+#define PRP_CNTL_CH1EN (1 << 0)
|
|
+#define PRP_CNTL_CH2EN (1 << 1)
|
|
+#define PRP_CNTL_CSIEN (1 << 2)
|
|
+#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
|
|
+#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
|
|
+#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
|
|
+#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
|
|
+#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
|
|
+#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
|
|
+#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
|
|
+#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
|
|
+#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
|
|
+#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
|
|
+#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
|
|
+#define PRP_CNTL_CH1_LEN (1 << 9)
|
|
+#define PRP_CNTL_CH2_LEN (1 << 10)
|
|
+#define PRP_CNTL_SKIP_FRAME (1 << 11)
|
|
+#define PRP_CNTL_SWRST (1 << 12)
|
|
+#define PRP_CNTL_CLKEN (1 << 13)
|
|
+#define PRP_CNTL_WEN (1 << 14)
|
|
+#define PRP_CNTL_CH1BYP (1 << 15)
|
|
+#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
|
|
+#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
|
|
+#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
|
|
+#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
|
|
+#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
|
|
+#define PRP_CNTL_CH2B1EN (1 << 29)
|
|
+#define PRP_CNTL_CH2B2EN (1 << 30)
|
|
+#define PRP_CNTL_CH2FEN (1 << 31)
|
|
+
|
|
+#define PRP_SIZE_HEIGHT(x) (x)
|
|
+#define PRP_SIZE_WIDTH(x) ((x) << 16)
|
|
+
|
|
+/* IRQ Enable and status register */
|
|
+#define PRP_INTR_RDERR (1 << 0)
|
|
+#define PRP_INTR_CH1WERR (1 << 1)
|
|
+#define PRP_INTR_CH2WERR (1 << 2)
|
|
+#define PRP_INTR_CH1FC (1 << 3)
|
|
+#define PRP_INTR_CH2FC (1 << 5)
|
|
+#define PRP_INTR_LBOVF (1 << 7)
|
|
+#define PRP_INTR_CH2OVF (1 << 8)
|
|
+
|
|
+#define PRP_INTR_ST_RDERR (1 << 0)
|
|
+#define PRP_INTR_ST_CH1WERR (1 << 1)
|
|
+#define PRP_INTR_ST_CH2WERR (1 << 2)
|
|
+#define PRP_INTR_ST_CH2B2CI (1 << 3)
|
|
+#define PRP_INTR_ST_CH2B1CI (1 << 4)
|
|
+#define PRP_INTR_ST_CH1B2CI (1 << 5)
|
|
+#define PRP_INTR_ST_CH1B1CI (1 << 6)
|
|
+#define PRP_INTR_ST_LBOVF (1 << 7)
|
|
+#define PRP_INTR_ST_CH2OVF (1 << 8)
|
|
+
|
|
+struct emmaprp_fmt {
|
|
+ char *name;
|
|
+ u32 fourcc;
|
|
+ /* Types the format can be used for */
|
|
+ u32 types;
|
|
+};
|
|
+
|
|
+static struct emmaprp_fmt formats[] = {
|
|
+ {
|
|
+ .name = "YUV 4:2:0 Planar",
|
|
+ .fourcc = V4L2_PIX_FMT_YUV420,
|
|
+ .types = MEM2MEM_CAPTURE,
|
|
+ },
|
|
+ {
|
|
+ .name = "4:2:2, packed, YUYV",
|
|
+ .fourcc = V4L2_PIX_FMT_YUYV,
|
|
+ .types = MEM2MEM_OUTPUT,
|
|
+ },
|
|
+};
|
|
+
|
|
+/* Per-queue, driver-specific private data */
|
|
+struct emmaprp_q_data {
|
|
+ unsigned int width;
|
|
+ unsigned int height;
|
|
+ unsigned int sizeimage;
|
|
+ struct emmaprp_fmt *fmt;
|
|
+};
|
|
+
|
|
+enum {
|
|
+ V4L2_M2M_SRC = 0,
|
|
+ V4L2_M2M_DST = 1,
|
|
+};
|
|
+
|
|
+#define NUM_FORMATS ARRAY_SIZE(formats)
|
|
+
|
|
+static struct emmaprp_fmt *find_format(struct v4l2_format *f)
|
|
+{
|
|
+ struct emmaprp_fmt *fmt;
|
|
+ unsigned int k;
|
|
+
|
|
+ for (k = 0; k < NUM_FORMATS; k++) {
|
|
+ fmt = &formats[k];
|
|
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (k == NUM_FORMATS)
|
|
+ return NULL;
|
|
+
|
|
+ return &formats[k];
|
|
+}
|
|
+
|
|
+struct emmaprp_dev {
|
|
+ struct v4l2_device v4l2_dev;
|
|
+ struct video_device *vfd;
|
|
+
|
|
+ struct mutex dev_mutex;
|
|
+ spinlock_t irqlock;
|
|
+
|
|
+ int irq_emma;
|
|
+ void __iomem *base_emma;
|
|
+ struct clk *clk_emma;
|
|
+ struct resource *res_emma;
|
|
+
|
|
+ struct v4l2_m2m_dev *m2m_dev;
|
|
+ struct vb2_alloc_ctx *alloc_ctx;
|
|
+};
|
|
+
|
|
+struct emmaprp_ctx {
|
|
+ struct emmaprp_dev *dev;
|
|
+ /* Abort requested by m2m */
|
|
+ int aborting;
|
|
+ struct emmaprp_q_data q_data[2];
|
|
+ struct v4l2_m2m_ctx *m2m_ctx;
|
|
+};
|
|
+
|
|
+static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx,
|
|
+ enum v4l2_buf_type type)
|
|
+{
|
|
+ switch (type) {
|
|
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
|
|
+ return &(ctx->q_data[V4L2_M2M_SRC]);
|
|
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
|
+ return &(ctx->q_data[V4L2_M2M_DST]);
|
|
+ default:
|
|
+ BUG();
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * mem2mem callbacks
|
|
+ */
|
|
+static void emmaprp_job_abort(void *priv)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+ struct emmaprp_dev *pcdev = ctx->dev;
|
|
+
|
|
+ ctx->aborting = 1;
|
|
+
|
|
+ dprintk(pcdev, "Aborting task\n");
|
|
+
|
|
+ v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx);
|
|
+}
|
|
+
|
|
+static void emmaprp_lock(void *priv)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+ struct emmaprp_dev *pcdev = ctx->dev;
|
|
+ mutex_lock(&pcdev->dev_mutex);
|
|
+}
|
|
+
|
|
+static void emmaprp_unlock(void *priv)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+ struct emmaprp_dev *pcdev = ctx->dev;
|
|
+ mutex_unlock(&pcdev->dev_mutex);
|
|
+}
|
|
+
|
|
+static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev)
|
|
+{
|
|
+ dprintk(pcdev,
|
|
+ "eMMa-PrP Registers:\n"
|
|
+ " SOURCE_Y_PTR = 0x%08X\n"
|
|
+ " SRC_FRAME_SIZE = 0x%08X\n"
|
|
+ " DEST_Y_PTR = 0x%08X\n"
|
|
+ " DEST_CR_PTR = 0x%08X\n"
|
|
+ " DEST_CB_PTR = 0x%08X\n"
|
|
+ " CH2_OUT_IMAGE_SIZE = 0x%08X\n"
|
|
+ " CNTL = 0x%08X\n",
|
|
+ readl(pcdev->base_emma + PRP_SOURCE_Y_PTR),
|
|
+ readl(pcdev->base_emma + PRP_SRC_FRAME_SIZE),
|
|
+ readl(pcdev->base_emma + PRP_DEST_Y_PTR),
|
|
+ readl(pcdev->base_emma + PRP_DEST_CR_PTR),
|
|
+ readl(pcdev->base_emma + PRP_DEST_CB_PTR),
|
|
+ readl(pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE),
|
|
+ readl(pcdev->base_emma + PRP_CNTL));
|
|
+}
|
|
+
|
|
+static void emmaprp_device_run(void *priv)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+ struct emmaprp_q_data *s_q_data, *d_q_data;
|
|
+ struct vb2_buffer *src_buf, *dst_buf;
|
|
+ struct emmaprp_dev *pcdev = ctx->dev;
|
|
+ unsigned int s_width, s_height;
|
|
+ unsigned int d_width, d_height;
|
|
+ unsigned int d_size;
|
|
+ dma_addr_t p_in, p_out;
|
|
+ u32 tmp;
|
|
+
|
|
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
|
|
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
|
|
+
|
|
+ s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
|
|
+ s_width = s_q_data->width;
|
|
+ s_height = s_q_data->height;
|
|
+
|
|
+ d_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
|
|
+ d_width = d_q_data->width;
|
|
+ d_height = d_q_data->height;
|
|
+ d_size = d_width * d_height;
|
|
+
|
|
+ p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0);
|
|
+ p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
|
|
+ if (!p_in || !p_out) {
|
|
+ v4l2_err(&pcdev->v4l2_dev,
|
|
+ "Acquiring kernel pointers to buffers failed\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Input frame parameters */
|
|
+ writel(p_in, pcdev->base_emma + PRP_SOURCE_Y_PTR);
|
|
+ writel(PRP_SIZE_WIDTH(s_width) | PRP_SIZE_HEIGHT(s_height),
|
|
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
|
|
+
|
|
+ /* Output frame parameters */
|
|
+ writel(p_out, pcdev->base_emma + PRP_DEST_Y_PTR);
|
|
+ writel(p_out + d_size, pcdev->base_emma + PRP_DEST_CB_PTR);
|
|
+ writel(p_out + d_size + (d_size >> 2),
|
|
+ pcdev->base_emma + PRP_DEST_CR_PTR);
|
|
+ writel(PRP_SIZE_WIDTH(d_width) | PRP_SIZE_HEIGHT(d_height),
|
|
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
|
|
+
|
|
+ /* IRQ configuration */
|
|
+ tmp = readl(pcdev->base_emma + PRP_INTR_CNTL);
|
|
+ writel(tmp | PRP_INTR_RDERR |
|
|
+ PRP_INTR_CH2WERR |
|
|
+ PRP_INTR_CH2FC,
|
|
+ pcdev->base_emma + PRP_INTR_CNTL);
|
|
+
|
|
+ emmaprp_dump_regs(pcdev);
|
|
+
|
|
+ /* Enable transfer */
|
|
+ tmp = readl(pcdev->base_emma + PRP_CNTL);
|
|
+ writel(tmp | PRP_CNTL_CH2_OUT_YUV420 |
|
|
+ PRP_CNTL_DATA_IN_YUV422 |
|
|
+ PRP_CNTL_CH2EN,
|
|
+ pcdev->base_emma + PRP_CNTL);
|
|
+}
|
|
+
|
|
+static irqreturn_t emmaprp_irq(int irq_emma, void *data)
|
|
+{
|
|
+ struct emmaprp_dev *pcdev = data;
|
|
+ struct emmaprp_ctx *curr_ctx;
|
|
+ struct vb2_buffer *src_vb, *dst_vb;
|
|
+ unsigned long flags;
|
|
+ u32 irqst;
|
|
+
|
|
+ /* Check irq flags and clear irq */
|
|
+ irqst = readl(pcdev->base_emma + PRP_INTRSTATUS);
|
|
+ writel(irqst, pcdev->base_emma + PRP_INTRSTATUS);
|
|
+ dprintk(pcdev, "irqst = 0x%08x\n", irqst);
|
|
+
|
|
+ curr_ctx = v4l2_m2m_get_curr_priv(pcdev->m2m_dev);
|
|
+ if (curr_ctx == NULL) {
|
|
+ pr_err("Instance released before the end of transaction\n");
|
|
+ return IRQ_HANDLED;
|
|
+ }
|
|
+
|
|
+ if (!curr_ctx->aborting) {
|
|
+ if ((irqst & PRP_INTR_ST_RDERR) ||
|
|
+ (irqst & PRP_INTR_ST_CH2WERR)) {
|
|
+ pr_err("PrP bus error ocurred, this transfer is probably corrupted\n");
|
|
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
|
|
+ } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */
|
|
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
|
|
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
|
|
+
|
|
+ spin_lock_irqsave(&pcdev->irqlock, flags);
|
|
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
|
|
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
|
|
+ spin_unlock_irqrestore(&pcdev->irqlock, flags);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx);
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * video ioctls
|
|
+ */
|
|
+static int vidioc_querycap(struct file *file, void *priv,
|
|
+ struct v4l2_capability *cap)
|
|
+{
|
|
+ strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
|
|
+ strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
|
|
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
|
|
+ | V4L2_CAP_STREAMING;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
|
|
+{
|
|
+ int i, num;
|
|
+ struct emmaprp_fmt *fmt;
|
|
+
|
|
+ num = 0;
|
|
+
|
|
+ for (i = 0; i < NUM_FORMATS; ++i) {
|
|
+ if (formats[i].types & type) {
|
|
+ /* index-th format of type type found ? */
|
|
+ if (num == f->index)
|
|
+ break;
|
|
+ /* Correct type but haven't reached our index yet,
|
|
+ * just increment per-type index */
|
|
+ ++num;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (i < NUM_FORMATS) {
|
|
+ /* Format found */
|
|
+ fmt = &formats[i];
|
|
+ strlcpy(f->description, fmt->name, sizeof(f->description) - 1);
|
|
+ f->pixelformat = fmt->fourcc;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Format not found */
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_fmtdesc *f)
|
|
+{
|
|
+ return enum_fmt(f, MEM2MEM_CAPTURE);
|
|
+}
|
|
+
|
|
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
|
|
+ struct v4l2_fmtdesc *f)
|
|
+{
|
|
+ return enum_fmt(f, MEM2MEM_OUTPUT);
|
|
+}
|
|
+
|
|
+static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
|
|
+{
|
|
+ struct vb2_queue *vq;
|
|
+ struct emmaprp_q_data *q_data;
|
|
+
|
|
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
|
|
+ if (!vq)
|
|
+ return -EINVAL;
|
|
+
|
|
+ q_data = get_q_data(ctx, f->type);
|
|
+
|
|
+ f->fmt.pix.width = q_data->width;
|
|
+ f->fmt.pix.height = q_data->height;
|
|
+ f->fmt.pix.field = V4L2_FIELD_NONE;
|
|
+ f->fmt.pix.pixelformat = q_data->fmt->fourcc;
|
|
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
|
|
+ f->fmt.pix.bytesperline = q_data->width * 3 / 2;
|
|
+ else /* YUYV */
|
|
+ f->fmt.pix.bytesperline = q_data->width * 2;
|
|
+ f->fmt.pix.sizeimage = q_data->sizeimage;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ return vidioc_g_fmt(priv, f);
|
|
+}
|
|
+
|
|
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ return vidioc_g_fmt(priv, f);
|
|
+}
|
|
+
|
|
+static int vidioc_try_fmt(struct v4l2_format *f)
|
|
+{
|
|
+ enum v4l2_field field;
|
|
+
|
|
+
|
|
+ if (!find_format(f))
|
|
+ return -EINVAL;
|
|
+
|
|
+ field = f->fmt.pix.field;
|
|
+ if (field == V4L2_FIELD_ANY)
|
|
+ field = V4L2_FIELD_NONE;
|
|
+ else if (V4L2_FIELD_NONE != field)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* V4L2 specification suggests the driver corrects the format struct
|
|
+ * if any of the dimensions is unsupported */
|
|
+ f->fmt.pix.field = field;
|
|
+
|
|
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
|
|
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
|
|
+ W_ALIGN_YUV420, &f->fmt.pix.height,
|
|
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
|
|
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
|
|
+ } else {
|
|
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
|
|
+ W_ALIGN_OTHERS, &f->fmt.pix.height,
|
|
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
|
|
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
|
|
+ }
|
|
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct emmaprp_fmt *fmt;
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+
|
|
+ fmt = find_format(f);
|
|
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
|
|
+ v4l2_err(&ctx->dev->v4l2_dev,
|
|
+ "Fourcc format (0x%08x) invalid.\n",
|
|
+ f->fmt.pix.pixelformat);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return vidioc_try_fmt(f);
|
|
+}
|
|
+
|
|
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct emmaprp_fmt *fmt;
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+
|
|
+ fmt = find_format(f);
|
|
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
|
|
+ v4l2_err(&ctx->dev->v4l2_dev,
|
|
+ "Fourcc format (0x%08x) invalid.\n",
|
|
+ f->fmt.pix.pixelformat);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return vidioc_try_fmt(f);
|
|
+}
|
|
+
|
|
+static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
|
|
+{
|
|
+ struct emmaprp_q_data *q_data;
|
|
+ struct vb2_queue *vq;
|
|
+ int ret;
|
|
+
|
|
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
|
|
+ if (!vq)
|
|
+ return -EINVAL;
|
|
+
|
|
+ q_data = get_q_data(ctx, f->type);
|
|
+ if (!q_data)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (vb2_is_busy(vq)) {
|
|
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ ret = vidioc_try_fmt(f);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ q_data->fmt = find_format(f);
|
|
+ q_data->width = f->fmt.pix.width;
|
|
+ q_data->height = f->fmt.pix.height;
|
|
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
|
|
+ q_data->sizeimage = q_data->width * q_data->height * 3 / 2;
|
|
+ else /* YUYV */
|
|
+ q_data->sizeimage = q_data->width * q_data->height * 2;
|
|
+
|
|
+ dprintk(ctx->dev,
|
|
+ "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
|
|
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return vidioc_s_fmt(priv, f);
|
|
+}
|
|
+
|
|
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = vidioc_try_fmt_vid_out(file, priv, f);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return vidioc_s_fmt(priv, f);
|
|
+}
|
|
+
|
|
+static int vidioc_reqbufs(struct file *file, void *priv,
|
|
+ struct v4l2_requestbuffers *reqbufs)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+
|
|
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
|
|
+}
|
|
+
|
|
+static int vidioc_querybuf(struct file *file, void *priv,
|
|
+ struct v4l2_buffer *buf)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+
|
|
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
|
|
+}
|
|
+
|
|
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+
|
|
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
|
|
+}
|
|
+
|
|
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+
|
|
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
|
|
+}
|
|
+
|
|
+static int vidioc_streamon(struct file *file, void *priv,
|
|
+ enum v4l2_buf_type type)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+
|
|
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
|
|
+}
|
|
+
|
|
+static int vidioc_streamoff(struct file *file, void *priv,
|
|
+ enum v4l2_buf_type type)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+
|
|
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
|
|
+}
|
|
+
|
|
+static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = {
|
|
+ .vidioc_querycap = vidioc_querycap,
|
|
+
|
|
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
|
|
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
|
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
|
|
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
|
|
+
|
|
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
|
|
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
|
|
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
|
|
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
|
|
+
|
|
+ .vidioc_reqbufs = vidioc_reqbufs,
|
|
+ .vidioc_querybuf = vidioc_querybuf,
|
|
+
|
|
+ .vidioc_qbuf = vidioc_qbuf,
|
|
+ .vidioc_dqbuf = vidioc_dqbuf,
|
|
+
|
|
+ .vidioc_streamon = vidioc_streamon,
|
|
+ .vidioc_streamoff = vidioc_streamoff,
|
|
+};
|
|
+
|
|
+
|
|
+/*
|
|
+ * Queue operations
|
|
+ */
|
|
+static int emmaprp_queue_setup(struct vb2_queue *vq,
|
|
+ const struct v4l2_format *fmt,
|
|
+ unsigned int *nbuffers, unsigned int *nplanes,
|
|
+ unsigned int sizes[], void *alloc_ctxs[])
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq);
|
|
+ struct emmaprp_q_data *q_data;
|
|
+ unsigned int size, count = *nbuffers;
|
|
+
|
|
+ q_data = get_q_data(ctx, vq->type);
|
|
+
|
|
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
|
|
+ size = q_data->width * q_data->height * 3 / 2;
|
|
+ else
|
|
+ size = q_data->width * q_data->height * 2;
|
|
+
|
|
+ while (size * count > MEM2MEM_VID_MEM_LIMIT)
|
|
+ (count)--;
|
|
+
|
|
+ *nplanes = 1;
|
|
+ *nbuffers = count;
|
|
+ sizes[0] = size;
|
|
+
|
|
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
|
|
+
|
|
+ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int emmaprp_buf_prepare(struct vb2_buffer *vb)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
|
|
+ struct emmaprp_q_data *q_data;
|
|
+
|
|
+ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
|
|
+
|
|
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
|
|
+
|
|
+ if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
|
|
+ dprintk(ctx->dev, "%s data will not fit into plane"
|
|
+ "(%lu < %lu)\n", __func__,
|
|
+ vb2_plane_size(vb, 0),
|
|
+ (long)q_data->sizeimage);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void emmaprp_buf_queue(struct vb2_buffer *vb)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
|
|
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
|
|
+}
|
|
+
|
|
+static struct vb2_ops emmaprp_qops = {
|
|
+ .queue_setup = emmaprp_queue_setup,
|
|
+ .buf_prepare = emmaprp_buf_prepare,
|
|
+ .buf_queue = emmaprp_buf_queue,
|
|
+};
|
|
+
|
|
+static int queue_init(void *priv, struct vb2_queue *src_vq,
|
|
+ struct vb2_queue *dst_vq)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = priv;
|
|
+ int ret;
|
|
+
|
|
+ memset(src_vq, 0, sizeof(*src_vq));
|
|
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
|
|
+ src_vq->io_modes = VB2_MMAP;
|
|
+ src_vq->drv_priv = ctx;
|
|
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
|
|
+ src_vq->ops = &emmaprp_qops;
|
|
+ src_vq->mem_ops = &vb2_dma_contig_memops;
|
|
+
|
|
+ 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;
|
|
+ dst_vq->io_modes = VB2_MMAP;
|
|
+ dst_vq->drv_priv = ctx;
|
|
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
|
|
+ dst_vq->ops = &emmaprp_qops;
|
|
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
|
|
+
|
|
+ return vb2_queue_init(dst_vq);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * File operations
|
|
+ */
|
|
+static int emmaprp_open(struct file *file)
|
|
+{
|
|
+ struct emmaprp_dev *pcdev = video_drvdata(file);
|
|
+ struct emmaprp_ctx *ctx;
|
|
+
|
|
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
|
|
+ if (!ctx)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ file->private_data = ctx;
|
|
+ ctx->dev = pcdev;
|
|
+
|
|
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init);
|
|
+
|
|
+ if (IS_ERR(ctx->m2m_ctx)) {
|
|
+ int ret = PTR_ERR(ctx->m2m_ctx);
|
|
+
|
|
+ kfree(ctx);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ clk_enable(pcdev->clk_emma);
|
|
+ ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1];
|
|
+ ctx->q_data[V4L2_M2M_DST].fmt = &formats[0];
|
|
+
|
|
+ dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int emmaprp_release(struct file *file)
|
|
+{
|
|
+ struct emmaprp_dev *pcdev = video_drvdata(file);
|
|
+ struct emmaprp_ctx *ctx = file->private_data;
|
|
+
|
|
+ dprintk(pcdev, "Releasing instance %p\n", ctx);
|
|
+
|
|
+ clk_disable(pcdev->clk_emma);
|
|
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
|
|
+ kfree(ctx);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static unsigned int emmaprp_poll(struct file *file,
|
|
+ struct poll_table_struct *wait)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = file->private_data;
|
|
+
|
|
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
|
|
+}
|
|
+
|
|
+static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma)
|
|
+{
|
|
+ struct emmaprp_ctx *ctx = file->private_data;
|
|
+
|
|
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
|
|
+}
|
|
+
|
|
+static const struct v4l2_file_operations emmaprp_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = emmaprp_open,
|
|
+ .release = emmaprp_release,
|
|
+ .poll = emmaprp_poll,
|
|
+ .unlocked_ioctl = video_ioctl2,
|
|
+ .mmap = emmaprp_mmap,
|
|
+};
|
|
+
|
|
+static struct video_device emmaprp_videodev = {
|
|
+ .name = MEM2MEM_NAME,
|
|
+ .fops = &emmaprp_fops,
|
|
+ .ioctl_ops = &emmaprp_ioctl_ops,
|
|
+ .minor = -1,
|
|
+ .release = video_device_release,
|
|
+};
|
|
+
|
|
+static struct v4l2_m2m_ops m2m_ops = {
|
|
+ .device_run = emmaprp_device_run,
|
|
+ .job_abort = emmaprp_job_abort,
|
|
+ .lock = emmaprp_lock,
|
|
+ .unlock = emmaprp_unlock,
|
|
+};
|
|
+
|
|
+static int emmaprp_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct emmaprp_dev *pcdev;
|
|
+ struct video_device *vfd;
|
|
+ struct resource *res_emma;
|
|
+ int irq_emma;
|
|
+ int ret;
|
|
+
|
|
+ pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL);
|
|
+ if (!pcdev)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ spin_lock_init(&pcdev->irqlock);
|
|
+
|
|
+ pcdev->clk_emma = clk_get(&pdev->dev, NULL);
|
|
+ if (IS_ERR(pcdev->clk_emma)) {
|
|
+ ret = PTR_ERR(pcdev->clk_emma);
|
|
+ goto free_dev;
|
|
+ }
|
|
+
|
|
+ irq_emma = platform_get_irq(pdev, 0);
|
|
+ res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ if (irq_emma < 0 || res_emma == NULL) {
|
|
+ dev_err(&pdev->dev, "Missing platform resources data\n");
|
|
+ ret = -ENODEV;
|
|
+ goto free_clk;
|
|
+ }
|
|
+
|
|
+ ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
|
|
+ if (ret)
|
|
+ goto free_clk;
|
|
+
|
|
+ mutex_init(&pcdev->dev_mutex);
|
|
+
|
|
+ vfd = video_device_alloc();
|
|
+ if (!vfd) {
|
|
+ v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto unreg_dev;
|
|
+ }
|
|
+
|
|
+ *vfd = emmaprp_videodev;
|
|
+ vfd->lock = &pcdev->dev_mutex;
|
|
+
|
|
+ video_set_drvdata(vfd, pcdev);
|
|
+ snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name);
|
|
+ pcdev->vfd = vfd;
|
|
+ v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME
|
|
+ " Device registered as /dev/video%d\n", vfd->num);
|
|
+
|
|
+ platform_set_drvdata(pdev, pcdev);
|
|
+
|
|
+ if (devm_request_mem_region(&pdev->dev, res_emma->start,
|
|
+ resource_size(res_emma), MEM2MEM_NAME) == NULL)
|
|
+ goto rel_vdev;
|
|
+
|
|
+ pcdev->base_emma = devm_ioremap(&pdev->dev, res_emma->start,
|
|
+ resource_size(res_emma));
|
|
+ if (!pcdev->base_emma)
|
|
+ goto rel_vdev;
|
|
+
|
|
+ pcdev->irq_emma = irq_emma;
|
|
+ pcdev->res_emma = res_emma;
|
|
+
|
|
+ if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq,
|
|
+ 0, MEM2MEM_NAME, pcdev) < 0)
|
|
+ goto rel_vdev;
|
|
+
|
|
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
|
|
+ if (IS_ERR(pcdev->alloc_ctx)) {
|
|
+ v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n");
|
|
+ ret = PTR_ERR(pcdev->alloc_ctx);
|
|
+ goto rel_vdev;
|
|
+ }
|
|
+
|
|
+ pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops);
|
|
+ if (IS_ERR(pcdev->m2m_dev)) {
|
|
+ v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n");
|
|
+ ret = PTR_ERR(pcdev->m2m_dev);
|
|
+ goto rel_ctx;
|
|
+ }
|
|
+
|
|
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
|
|
+ if (ret) {
|
|
+ v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
|
|
+ goto rel_m2m;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+
|
|
+rel_m2m:
|
|
+ v4l2_m2m_release(pcdev->m2m_dev);
|
|
+rel_ctx:
|
|
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
|
|
+rel_vdev:
|
|
+ video_device_release(vfd);
|
|
+unreg_dev:
|
|
+ v4l2_device_unregister(&pcdev->v4l2_dev);
|
|
+free_clk:
|
|
+ clk_put(pcdev->clk_emma);
|
|
+free_dev:
|
|
+ kfree(pcdev);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int emmaprp_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct emmaprp_dev *pcdev = platform_get_drvdata(pdev);
|
|
+
|
|
+ v4l2_info(&pcdev->v4l2_dev, "Removing " EMMAPRP_MODULE_NAME);
|
|
+
|
|
+ video_unregister_device(pcdev->vfd);
|
|
+ v4l2_m2m_release(pcdev->m2m_dev);
|
|
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
|
|
+ v4l2_device_unregister(&pcdev->v4l2_dev);
|
|
+ clk_put(pcdev->clk_emma);
|
|
+ kfree(pcdev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct platform_driver emmaprp_pdrv = {
|
|
+ .probe = emmaprp_probe,
|
|
+ .remove = emmaprp_remove,
|
|
+ .driver = {
|
|
+ .name = MEM2MEM_NAME,
|
|
+ .owner = THIS_MODULE,
|
|
+ },
|
|
+};
|
|
+
|
|
+static void __exit emmaprp_exit(void)
|
|
+{
|
|
+ platform_driver_unregister(&emmaprp_pdrv);
|
|
+}
|
|
+
|
|
+static int __init emmaprp_init(void)
|
|
+{
|
|
+ return platform_driver_register(&emmaprp_pdrv);
|
|
+}
|
|
+
|
|
+module_init(emmaprp_init);
|
|
+module_exit(emmaprp_exit);
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/mt2063.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/common/tuners/mt2063.h
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/mt2063.h
|
|
@@ -23,10 +23,6 @@ static inline struct dvb_frontend *mt206
|
|
return NULL;
|
|
}
|
|
|
|
-int mt2063_setTune(struct dvb_frontend *fe, u32 f_in,
|
|
- u32 bw_in,
|
|
- enum MTTune_atv_standard tv_type);
|
|
-
|
|
/* FIXME: Should use the standard DVB attachment interfaces */
|
|
unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe);
|
|
unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe);
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-tv/sdo_drv.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-tv/sdo_drv.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-tv/sdo_drv.c
|
|
@@ -301,7 +301,7 @@ static int __devinit sdo_probe(struct pl
|
|
struct clk *sclk_vpll;
|
|
|
|
dev_info(dev, "probe start\n");
|
|
- sdev = kzalloc(sizeof *sdev, GFP_KERNEL);
|
|
+ sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL);
|
|
if (!sdev) {
|
|
dev_err(dev, "not enough memory.\n");
|
|
ret = -ENOMEM;
|
|
@@ -314,14 +314,14 @@ static int __devinit sdo_probe(struct pl
|
|
if (res == NULL) {
|
|
dev_err(dev, "get memory resource failed.\n");
|
|
ret = -ENXIO;
|
|
- goto fail_sdev;
|
|
+ goto fail;
|
|
}
|
|
|
|
- sdev->regs = ioremap(res->start, resource_size(res));
|
|
+ sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
|
if (sdev->regs == NULL) {
|
|
dev_err(dev, "register mapping failed.\n");
|
|
ret = -ENXIO;
|
|
- goto fail_sdev;
|
|
+ goto fail;
|
|
}
|
|
|
|
/* acquiring interrupt */
|
|
@@ -329,12 +329,13 @@ static int __devinit sdo_probe(struct pl
|
|
if (res == NULL) {
|
|
dev_err(dev, "get interrupt resource failed.\n");
|
|
ret = -ENXIO;
|
|
- goto fail_regs;
|
|
+ goto fail;
|
|
}
|
|
- ret = request_irq(res->start, sdo_irq_handler, 0, "s5p-sdo", sdev);
|
|
+ ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0,
|
|
+ "s5p-sdo", sdev);
|
|
if (ret) {
|
|
dev_err(dev, "request interrupt failed.\n");
|
|
- goto fail_regs;
|
|
+ goto fail;
|
|
}
|
|
sdev->irq = res->start;
|
|
|
|
@@ -343,7 +344,7 @@ static int __devinit sdo_probe(struct pl
|
|
if (IS_ERR_OR_NULL(sdev->sclk_dac)) {
|
|
dev_err(dev, "failed to get clock 'sclk_dac'\n");
|
|
ret = -ENXIO;
|
|
- goto fail_irq;
|
|
+ goto fail;
|
|
}
|
|
sdev->dac = clk_get(dev, "dac");
|
|
if (IS_ERR_OR_NULL(sdev->dac)) {
|
|
@@ -415,12 +416,6 @@ fail_dac:
|
|
clk_put(sdev->dac);
|
|
fail_sclk_dac:
|
|
clk_put(sdev->sclk_dac);
|
|
-fail_irq:
|
|
- free_irq(sdev->irq, sdev);
|
|
-fail_regs:
|
|
- iounmap(sdev->regs);
|
|
-fail_sdev:
|
|
- kfree(sdev);
|
|
fail:
|
|
dev_info(dev, "probe failed\n");
|
|
return ret;
|
|
@@ -439,9 +434,6 @@ static int __devexit sdo_remove(struct p
|
|
clk_put(sdev->dacphy);
|
|
clk_put(sdev->dac);
|
|
clk_put(sdev->sclk_dac);
|
|
- free_irq(sdev->irq, sdev);
|
|
- iounmap(sdev->regs);
|
|
- kfree(sdev);
|
|
|
|
dev_info(&pdev->dev, "remove successful\n");
|
|
return 0;
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-tv/Kconfig
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-tv/Kconfig
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-tv/Kconfig
|
|
@@ -46,6 +46,16 @@ config VIDEO_SAMSUNG_S5P_HDMIPHY
|
|
as module. It is an I2C driver, that exposes a V4L2
|
|
subdev for use by other drivers.
|
|
|
|
+config VIDEO_SAMSUNG_S5P_SII9234
|
|
+ tristate "Samsung SII9234 Driver"
|
|
+ depends on VIDEO_DEV && VIDEO_V4L2 && I2C
|
|
+ depends on VIDEO_SAMSUNG_S5P_TV
|
|
+ help
|
|
+ Say Y here if you want support for the MHL interface
|
|
+ in S5P Samsung SoC. The driver can be compiled
|
|
+ as module. It is an I2C driver, that exposes a V4L2
|
|
+ subdev for use by other drivers.
|
|
+
|
|
config VIDEO_SAMSUNG_S5P_SDO
|
|
tristate "Samsung Analog TV Driver"
|
|
depends on VIDEO_DEV && VIDEO_V4L2
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-tv/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-tv/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-tv/Makefile
|
|
@@ -8,6 +8,8 @@
|
|
|
|
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o
|
|
s5p-hdmiphy-y += hdmiphy_drv.o
|
|
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o
|
|
+s5p-sii9234-y += sii9234_drv.o
|
|
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o
|
|
s5p-hdmi-y += hdmi_drv.o
|
|
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-tv/sii9234_drv.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-tv/sii9234_drv.c
|
|
@@ -0,0 +1,432 @@
|
|
+/*
|
|
+ * Samsung MHL interface driver
|
|
+ *
|
|
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
|
|
+ * Author: Tomasz Stanislawski <t.stanislaws@samsung.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU General Public License as published by the
|
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
|
+ * option) any later version.
|
|
+ */
|
|
+
|
|
+#include <linux/delay.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/freezer.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/kthread.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/pm_runtime.h>
|
|
+#include <linux/regulator/machine.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#include <mach/gpio.h>
|
|
+#include <plat/gpio-cfg.h>
|
|
+
|
|
+#include <media/sii9234.h>
|
|
+#include <media/v4l2-subdev.h>
|
|
+
|
|
+MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
|
|
+MODULE_DESCRIPTION("Samsung MHL interface driver");
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+struct sii9234_context {
|
|
+ struct i2c_client *client;
|
|
+ struct regulator *power;
|
|
+ int gpio_n_reset;
|
|
+ struct v4l2_subdev sd;
|
|
+};
|
|
+
|
|
+static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd)
|
|
+{
|
|
+ return container_of(sd, struct sii9234_context, sd);
|
|
+}
|
|
+
|
|
+static inline int sii9234_readb(struct i2c_client *client, int addr)
|
|
+{
|
|
+ return i2c_smbus_read_byte_data(client, addr);
|
|
+}
|
|
+
|
|
+static inline int sii9234_writeb(struct i2c_client *client, int addr, int value)
|
|
+{
|
|
+ return i2c_smbus_write_byte_data(client, addr, value);
|
|
+}
|
|
+
|
|
+static inline int sii9234_writeb_mask(struct i2c_client *client, int addr,
|
|
+ int value, int mask)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = i2c_smbus_read_byte_data(client, addr);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = (ret & ~mask) | (value & mask);
|
|
+ return i2c_smbus_write_byte_data(client, addr, ret);
|
|
+}
|
|
+
|
|
+static inline int sii9234_readb_idx(struct i2c_client *client, int addr)
|
|
+{
|
|
+ int ret;
|
|
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ return i2c_smbus_read_byte_data(client, 0xbe);
|
|
+}
|
|
+
|
|
+static inline int sii9234_writeb_idx(struct i2c_client *client, int addr,
|
|
+ int value)
|
|
+{
|
|
+ int ret;
|
|
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = i2c_smbus_write_byte_data(client, 0xbe, value);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr,
|
|
+ int value, int mask)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = sii9234_readb_idx(client, addr);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = (ret & ~mask) | (value & mask);
|
|
+ return sii9234_writeb_idx(client, addr, ret);
|
|
+}
|
|
+
|
|
+static int sii9234_reset(struct sii9234_context *ctx)
|
|
+{
|
|
+ struct i2c_client *client = ctx->client;
|
|
+ struct device *dev = &client->dev;
|
|
+ int ret, tries;
|
|
+
|
|
+ gpio_direction_output(ctx->gpio_n_reset, 1);
|
|
+ mdelay(1);
|
|
+ gpio_direction_output(ctx->gpio_n_reset, 0);
|
|
+ mdelay(1);
|
|
+ gpio_direction_output(ctx->gpio_n_reset, 1);
|
|
+ mdelay(1);
|
|
+
|
|
+ /* going to TTPI mode */
|
|
+ ret = sii9234_writeb(client, 0xc7, 0);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "failed to set TTPI mode\n");
|
|
+ return ret;
|
|
+ }
|
|
+ for (tries = 0; tries < 100 ; ++tries) {
|
|
+ ret = sii9234_readb(client, 0x1b);
|
|
+ if (ret > 0)
|
|
+ break;
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "failed to reset device\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+ mdelay(1);
|
|
+ }
|
|
+ if (tries == 100) {
|
|
+ dev_err(dev, "maximal number of tries reached\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sii9234_verify_version(struct i2c_client *client)
|
|
+{
|
|
+ struct device *dev = &client->dev;
|
|
+ int family, rev, tpi_rev, dev_id, sub_id, hdcp, id;
|
|
+
|
|
+ family = sii9234_readb(client, 0x1b);
|
|
+ rev = sii9234_readb(client, 0x1c) & 0x0f;
|
|
+ tpi_rev = sii9234_readb(client, 0x1d) & 0x7f;
|
|
+ dev_id = sii9234_readb_idx(client, 0x0103);
|
|
+ sub_id = sii9234_readb_idx(client, 0x0102);
|
|
+ hdcp = sii9234_readb(client, 0x30);
|
|
+
|
|
+ if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 ||
|
|
+ sub_id < 0 || hdcp < 0) {
|
|
+ dev_err(dev, "failed to read chip's version\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ id = (dev_id << 8) | sub_id;
|
|
+
|
|
+ dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n",
|
|
+ id, family, rev);
|
|
+ dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp);
|
|
+ if (id != 0x9234) {
|
|
+ dev_err(dev, "not supported chip\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u8 data[][3] = {
|
|
+/* setup from driver created by doonsoo45.kim */
|
|
+ { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */
|
|
+ { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */
|
|
+ { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */
|
|
+ { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */
|
|
+ { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */
|
|
+ { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */
|
|
+ { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */
|
|
+ { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */
|
|
+ { 0x01, 0x92, 0x46 }, /* Force MHD mode */
|
|
+ { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */
|
|
+ { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */
|
|
+ { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */
|
|
+ { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */
|
|
+ { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */
|
|
+ { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */
|
|
+ { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */
|
|
+ { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */
|
|
+ { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */
|
|
+ { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */
|
|
+ { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */
|
|
+ { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */
|
|
+ { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */
|
|
+ { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */
|
|
+ { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */
|
|
+ { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */
|
|
+ { 0x03, 0x1a, 0x20 }, /* VCO Cal */
|
|
+ { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */
|
|
+ { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */
|
|
+ { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */
|
|
+ { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */
|
|
+ { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */
|
|
+ { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */
|
|
+ { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */
|
|
+ { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */
|
|
+ { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */
|
|
+ { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */
|
|
+ { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */
|
|
+ { 0x03, 0x4c, 0xa0 }, /* Manual zone control */
|
|
+ { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */
|
|
+};
|
|
+
|
|
+static int sii9234_set_internal(struct sii9234_context *ctx)
|
|
+{
|
|
+ struct i2c_client *client = ctx->client;
|
|
+ int i, ret;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(data); ++i) {
|
|
+ int addr = (data[i][0] << 8) | data[i][1];
|
|
+ ret = sii9234_writeb_idx(client, addr, data[i][2]);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sii9234_runtime_suspend(struct device *dev)
|
|
+{
|
|
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
|
+ struct sii9234_context *ctx = sd_to_context(sd);
|
|
+ struct i2c_client *client = ctx->client;
|
|
+
|
|
+ dev_info(dev, "suspend start\n");
|
|
+
|
|
+ sii9234_writeb_mask(client, 0x1e, 3, 3);
|
|
+ regulator_disable(ctx->power);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sii9234_runtime_resume(struct device *dev)
|
|
+{
|
|
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
|
+ struct sii9234_context *ctx = sd_to_context(sd);
|
|
+ struct i2c_client *client = ctx->client;
|
|
+ int ret;
|
|
+
|
|
+ dev_info(dev, "resume start\n");
|
|
+ regulator_enable(ctx->power);
|
|
+
|
|
+ ret = sii9234_reset(ctx);
|
|
+ if (ret)
|
|
+ goto fail;
|
|
+
|
|
+ /* enable tpi */
|
|
+ ret = sii9234_writeb_mask(client, 0x1e, 1, 0);
|
|
+ if (ret < 0)
|
|
+ goto fail;
|
|
+ ret = sii9234_set_internal(ctx);
|
|
+ if (ret < 0)
|
|
+ goto fail;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+fail:
|
|
+ dev_err(dev, "failed to resume\n");
|
|
+ regulator_disable(ctx->power);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct dev_pm_ops sii9234_pm_ops = {
|
|
+ .runtime_suspend = sii9234_runtime_suspend,
|
|
+ .runtime_resume = sii9234_runtime_resume,
|
|
+};
|
|
+
|
|
+static int sii9234_s_power(struct v4l2_subdev *sd, int on)
|
|
+{
|
|
+ struct sii9234_context *ctx = sd_to_context(sd);
|
|
+ int ret;
|
|
+
|
|
+ if (on)
|
|
+ ret = pm_runtime_get_sync(&ctx->client->dev);
|
|
+ else
|
|
+ ret = pm_runtime_put(&ctx->client->dev);
|
|
+ /* only values < 0 indicate errors */
|
|
+ return IS_ERR_VALUE(ret) ? ret : 0;
|
|
+}
|
|
+
|
|
+static int sii9234_s_stream(struct v4l2_subdev *sd, int enable)
|
|
+{
|
|
+ struct sii9234_context *ctx = sd_to_context(sd);
|
|
+
|
|
+ /* (dis/en)able TDMS output */
|
|
+ sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct v4l2_subdev_core_ops sii9234_core_ops = {
|
|
+ .s_power = sii9234_s_power,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_video_ops sii9234_video_ops = {
|
|
+ .s_stream = sii9234_s_stream,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_ops sii9234_ops = {
|
|
+ .core = &sii9234_core_ops,
|
|
+ .video = &sii9234_video_ops,
|
|
+};
|
|
+
|
|
+static int __devinit sii9234_probe(struct i2c_client *client,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ struct device *dev = &client->dev;
|
|
+ struct sii9234_platform_data *pdata = dev->platform_data;
|
|
+ struct sii9234_context *ctx;
|
|
+ int ret;
|
|
+
|
|
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
|
+ if (!ctx) {
|
|
+ dev_err(dev, "out of memory\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto fail;
|
|
+ }
|
|
+ ctx->client = client;
|
|
+
|
|
+ ctx->power = regulator_get(dev, "hdmi-en");
|
|
+ if (IS_ERR(ctx->power)) {
|
|
+ dev_err(dev, "failed to acquire regulator hdmi-en\n");
|
|
+ ret = PTR_ERR(ctx->power);
|
|
+ goto fail_ctx;
|
|
+ }
|
|
+
|
|
+ ctx->gpio_n_reset = pdata->gpio_n_reset;
|
|
+ ret = gpio_request(ctx->gpio_n_reset, "MHL_RST");
|
|
+ if (ret) {
|
|
+ dev_err(dev, "failed to acquire MHL_RST gpio\n");
|
|
+ goto fail_power;
|
|
+ }
|
|
+
|
|
+ v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops);
|
|
+
|
|
+ pm_runtime_enable(dev);
|
|
+
|
|
+ /* enable device */
|
|
+ ret = pm_runtime_get_sync(dev);
|
|
+ if (ret)
|
|
+ goto fail_pm;
|
|
+
|
|
+ /* verify chip version */
|
|
+ ret = sii9234_verify_version(client);
|
|
+ if (ret)
|
|
+ goto fail_pm_get;
|
|
+
|
|
+ /* stop processing */
|
|
+ pm_runtime_put(dev);
|
|
+
|
|
+ dev_info(dev, "probe successful\n");
|
|
+
|
|
+ return 0;
|
|
+
|
|
+fail_pm_get:
|
|
+ pm_runtime_put_sync(dev);
|
|
+
|
|
+fail_pm:
|
|
+ pm_runtime_disable(dev);
|
|
+ gpio_free(ctx->gpio_n_reset);
|
|
+
|
|
+fail_power:
|
|
+ regulator_put(ctx->power);
|
|
+
|
|
+fail_ctx:
|
|
+ kfree(ctx);
|
|
+
|
|
+fail:
|
|
+ dev_err(dev, "probe failed\n");
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int __devexit sii9234_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct device *dev = &client->dev;
|
|
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
|
+ struct sii9234_context *ctx = sd_to_context(sd);
|
|
+
|
|
+ pm_runtime_disable(dev);
|
|
+ gpio_free(ctx->gpio_n_reset);
|
|
+ regulator_put(ctx->power);
|
|
+ kfree(ctx);
|
|
+
|
|
+ dev_info(dev, "remove successful\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static const struct i2c_device_id sii9234_id[] = {
|
|
+ { "SII9234", 0 },
|
|
+ { },
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(i2c, sii9234_id);
|
|
+static struct i2c_driver sii9234_driver = {
|
|
+ .driver = {
|
|
+ .name = "sii9234",
|
|
+ .owner = THIS_MODULE,
|
|
+ .pm = &sii9234_pm_ops,
|
|
+ },
|
|
+ .probe = sii9234_probe,
|
|
+ .remove = __devexit_p(sii9234_remove),
|
|
+ .id_table = sii9234_id,
|
|
+};
|
|
+
|
|
+static int __init sii9234_init(void)
|
|
+{
|
|
+ return i2c_add_driver(&sii9234_driver);
|
|
+}
|
|
+module_init(sii9234_init);
|
|
+
|
|
+static void __exit sii9234_exit(void)
|
|
+{
|
|
+ i2c_del_driver(&sii9234_driver);
|
|
+}
|
|
+module_exit(sii9234_exit);
|
|
Index: linux-3.3.x86_64/include/media/sii9234.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/include/media/sii9234.h
|
|
@@ -0,0 +1,24 @@
|
|
+/*
|
|
+ * Driver header for SII9234 MHL converter chip.
|
|
+ *
|
|
+ * Copyright (c) 2011 Samsung Electronics, Co. Ltd
|
|
+ * Contact: Tomasz Stanislawski <t.stanislaws@samsung.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef SII9234_H
|
|
+#define SII9234_H
|
|
+
|
|
+/**
|
|
+ * @gpio_n_reset: GPIO driving nRESET pin
|
|
+ */
|
|
+
|
|
+struct sii9234_platform_data {
|
|
+ int gpio_n_reset;
|
|
+};
|
|
+
|
|
+#endif /* SII9234_H */
|
|
Index: linux-3.3.x86_64/include/media/s5p_hdmi.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/include/media/s5p_hdmi.h
|
|
@@ -0,0 +1,35 @@
|
|
+/*
|
|
+ * Driver header for S5P HDMI chip.
|
|
+ *
|
|
+ * Copyright (c) 2011 Samsung Electronics, Co. Ltd
|
|
+ * Contact: Tomasz Stanislawski <t.stanislaws@samsung.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef S5P_HDMI_H
|
|
+#define S5P_HDMI_H
|
|
+
|
|
+struct i2c_board_info;
|
|
+
|
|
+/**
|
|
+ * @hdmiphy_bus: controller id for HDMIPHY bus
|
|
+ * @hdmiphy_info: template for HDMIPHY I2C device
|
|
+ * @mhl_bus: controller id for MHL control bus
|
|
+ * @mhl_info: template for MHL I2C device
|
|
+ *
|
|
+ * NULL pointer for *_info fields indicates that
|
|
+ * the corresponding chip is not present
|
|
+ */
|
|
+struct s5p_hdmi_platform_data {
|
|
+ int hdmiphy_bus;
|
|
+ struct i2c_board_info *hdmiphy_info;
|
|
+ int mhl_bus;
|
|
+ struct i2c_board_info *mhl_info;
|
|
+};
|
|
+
|
|
+#endif /* S5P_HDMI_H */
|
|
+
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/Kconfig
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/Kconfig
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/Kconfig
|
|
@@ -425,6 +425,13 @@ config DVB_CXD2820R
|
|
help
|
|
Say Y when you want to support this frontend.
|
|
|
|
+config DVB_RTL2830
|
|
+ tristate "Realtek RTL2830 DVB-T"
|
|
+ depends on DVB_CORE && I2C
|
|
+ default m if DVB_FE_CUSTOMISE
|
|
+ help
|
|
+ Say Y when you want to support this frontend.
|
|
+
|
|
comment "DVB-C (cable) frontends"
|
|
depends on DVB_CORE
|
|
|
|
@@ -698,6 +705,19 @@ config DVB_IT913X_FE
|
|
A DVB-T tuner module.
|
|
Say Y when you want to support this frontend.
|
|
|
|
+config DVB_M88RS2000
|
|
+ tristate "M88RS2000 DVB-S demodulator and tuner"
|
|
+ depends on DVB_CORE && I2C
|
|
+ default m if DVB_FE_CUSTOMISE
|
|
+ help
|
|
+ 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
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/Makefile
|
|
@@ -2,8 +2,8 @@
|
|
# Makefile for the kernel DVB frontend device drivers.
|
|
#
|
|
|
|
-ccflags-y += -Idrivers/media/dvb/dvb-core/
|
|
-ccflags-y += -Idrivers/media/common/tuners/
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core/
|
|
+ccflags-y += -I$(srctree)/drivers/media/common/tuners/
|
|
|
|
stb0899-objs = stb0899_drv.o stb0899_algo.o
|
|
stv0900-objs = stv0900_core.o stv0900_sw.o
|
|
@@ -96,4 +96,7 @@ obj-$(CONFIG_DVB_TDA18271C2DD) += tda182
|
|
obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
|
|
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
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/rtl2830.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/rtl2830.c
|
|
@@ -0,0 +1,562 @@
|
|
+/*
|
|
+ * Realtek RTL2830 DVB-T demodulator driver
|
|
+ *
|
|
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+
|
|
+/*
|
|
+ * Driver implements own I2C-adapter for tuner I2C access. That's since chip
|
|
+ * have unusual I2C-gate control which closes gate automatically after each
|
|
+ * I2C transfer. Using own I2C adapter we can workaround that.
|
|
+ */
|
|
+
|
|
+#include "rtl2830_priv.h"
|
|
+
|
|
+int rtl2830_debug;
|
|
+module_param_named(debug, rtl2830_debug, int, 0644);
|
|
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
|
|
+
|
|
+/* write multiple hardware registers */
|
|
+static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
|
|
+{
|
|
+ int ret;
|
|
+ u8 buf[1+len];
|
|
+ struct i2c_msg msg[1] = {
|
|
+ {
|
|
+ .addr = priv->cfg.i2c_addr,
|
|
+ .flags = 0,
|
|
+ .len = 1+len,
|
|
+ .buf = buf,
|
|
+ }
|
|
+ };
|
|
+
|
|
+ buf[0] = reg;
|
|
+ memcpy(&buf[1], val, len);
|
|
+
|
|
+ ret = i2c_transfer(priv->i2c, msg, 1);
|
|
+ if (ret == 1) {
|
|
+ ret = 0;
|
|
+ } else {
|
|
+ warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
|
|
+ ret = -EREMOTEIO;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* read multiple hardware registers */
|
|
+static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
|
|
+{
|
|
+ int ret;
|
|
+ struct i2c_msg msg[2] = {
|
|
+ {
|
|
+ .addr = priv->cfg.i2c_addr,
|
|
+ .flags = 0,
|
|
+ .len = 1,
|
|
+ .buf = ®,
|
|
+ }, {
|
|
+ .addr = priv->cfg.i2c_addr,
|
|
+ .flags = I2C_M_RD,
|
|
+ .len = len,
|
|
+ .buf = val,
|
|
+ }
|
|
+ };
|
|
+
|
|
+ ret = i2c_transfer(priv->i2c, msg, 2);
|
|
+ if (ret == 2) {
|
|
+ ret = 0;
|
|
+ } else {
|
|
+ warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
|
|
+ ret = -EREMOTEIO;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* write multiple registers */
|
|
+static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
|
|
+{
|
|
+ int ret;
|
|
+ u8 reg2 = (reg >> 0) & 0xff;
|
|
+ u8 page = (reg >> 8) & 0xff;
|
|
+
|
|
+ /* switch bank if needed */
|
|
+ if (page != priv->page) {
|
|
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ priv->page = page;
|
|
+ }
|
|
+
|
|
+ return rtl2830_wr(priv, reg2, val, len);
|
|
+}
|
|
+
|
|
+/* read multiple registers */
|
|
+static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
|
|
+{
|
|
+ int ret;
|
|
+ u8 reg2 = (reg >> 0) & 0xff;
|
|
+ u8 page = (reg >> 8) & 0xff;
|
|
+
|
|
+ /* switch bank if needed */
|
|
+ if (page != priv->page) {
|
|
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ priv->page = page;
|
|
+ }
|
|
+
|
|
+ return rtl2830_rd(priv, reg2, val, len);
|
|
+}
|
|
+
|
|
+#if 0 /* currently not used */
|
|
+/* write single register */
|
|
+static int rtl2830_wr_reg(struct rtl2830_priv *priv, u16 reg, u8 val)
|
|
+{
|
|
+ return rtl2830_wr_regs(priv, reg, &val, 1);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* read single register */
|
|
+static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val)
|
|
+{
|
|
+ return rtl2830_rd_regs(priv, reg, val, 1);
|
|
+}
|
|
+
|
|
+/* write single register with mask */
|
|
+int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
|
|
+{
|
|
+ int ret;
|
|
+ u8 tmp;
|
|
+
|
|
+ /* no need for read if whole reg is written */
|
|
+ if (mask != 0xff) {
|
|
+ ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ val &= mask;
|
|
+ tmp &= ~mask;
|
|
+ val |= tmp;
|
|
+ }
|
|
+
|
|
+ return rtl2830_wr_regs(priv, reg, &val, 1);
|
|
+}
|
|
+
|
|
+/* read single register with mask */
|
|
+int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
|
|
+{
|
|
+ int ret, i;
|
|
+ u8 tmp;
|
|
+
|
|
+ ret = rtl2830_rd_regs(priv, 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 int rtl2830_init(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct rtl2830_priv *priv = fe->demodulator_priv;
|
|
+ int ret, i;
|
|
+ u64 num;
|
|
+ u8 buf[3], tmp;
|
|
+ u32 if_ctl;
|
|
+ struct rtl2830_reg_val_mask tab[] = {
|
|
+ { 0x00d, 0x01, 0x03 },
|
|
+ { 0x00d, 0x10, 0x10 },
|
|
+ { 0x104, 0x00, 0x1e },
|
|
+ { 0x105, 0x80, 0x80 },
|
|
+ { 0x110, 0x02, 0x03 },
|
|
+ { 0x110, 0x08, 0x0c },
|
|
+ { 0x17b, 0x00, 0x40 },
|
|
+ { 0x17d, 0x05, 0x0f },
|
|
+ { 0x17d, 0x50, 0xf0 },
|
|
+ { 0x18c, 0x08, 0x0f },
|
|
+ { 0x18d, 0x00, 0xc0 },
|
|
+ { 0x188, 0x05, 0x0f },
|
|
+ { 0x189, 0x00, 0xfc },
|
|
+ { 0x2d5, 0x02, 0x02 },
|
|
+ { 0x2f1, 0x02, 0x06 },
|
|
+ { 0x2f1, 0x20, 0xf8 },
|
|
+ { 0x16d, 0x00, 0x01 },
|
|
+ { 0x1a6, 0x00, 0x80 },
|
|
+ { 0x106, priv->cfg.vtop, 0x3f },
|
|
+ { 0x107, priv->cfg.krf, 0x3f },
|
|
+ { 0x112, 0x28, 0xff },
|
|
+ { 0x103, priv->cfg.agc_targ_val, 0xff },
|
|
+ { 0x00a, 0x02, 0x07 },
|
|
+ { 0x140, 0x0c, 0x3c },
|
|
+ { 0x140, 0x40, 0xc0 },
|
|
+ { 0x15b, 0x05, 0x07 },
|
|
+ { 0x15b, 0x28, 0x38 },
|
|
+ { 0x15c, 0x05, 0x07 },
|
|
+ { 0x15c, 0x28, 0x38 },
|
|
+ { 0x115, priv->cfg.spec_inv, 0x01 },
|
|
+ { 0x16f, 0x01, 0x07 },
|
|
+ { 0x170, 0x18, 0x38 },
|
|
+ { 0x172, 0x0f, 0x0f },
|
|
+ { 0x173, 0x08, 0x38 },
|
|
+ { 0x175, 0x01, 0x07 },
|
|
+ { 0x176, 0x00, 0xc0 },
|
|
+ };
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
|
|
+ ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val,
|
|
+ tab[i].mask);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = rtl2830_wr_regs(priv, 0x195,
|
|
+ "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ num = priv->cfg.if_dvbt % priv->cfg.xtal;
|
|
+ num *= 0x400000;
|
|
+ num = div_u64(num, priv->cfg.xtal);
|
|
+ num = -num;
|
|
+ if_ctl = num & 0x3fffff;
|
|
+ dbg("%s: if_ctl=%08x", __func__, if_ctl);
|
|
+
|
|
+ ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ buf[0] = tmp << 6;
|
|
+ buf[0] = (if_ctl >> 16) & 0x3f;
|
|
+ buf[1] = (if_ctl >> 8) & 0xff;
|
|
+ buf[2] = (if_ctl >> 0) & 0xff;
|
|
+
|
|
+ ret = rtl2830_wr_regs(priv, 0x119, buf, 3);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* TODO: spec init */
|
|
+
|
|
+ /* soft reset */
|
|
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ priv->sleeping = false;
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ dbg("%s: failed=%d", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rtl2830_sleep(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct rtl2830_priv *priv = fe->demodulator_priv;
|
|
+ priv->sleeping = true;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int rtl2830_get_tune_settings(struct dvb_frontend *fe,
|
|
+ struct dvb_frontend_tune_settings *s)
|
|
+{
|
|
+ s->min_delay_ms = 500;
|
|
+ s->step_size = fe->ops.info.frequency_stepsize * 2;
|
|
+ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rtl2830_set_frontend(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct rtl2830_priv *priv = fe->demodulator_priv;
|
|
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
+ int ret, i;
|
|
+ static u8 bw_params1[3][34] = {
|
|
+ {
|
|
+ 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41,
|
|
+ 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a,
|
|
+ 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82,
|
|
+ 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */
|
|
+ }, {
|
|
+ 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca,
|
|
+ 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca,
|
|
+ 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e,
|
|
+ 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */
|
|
+ }, {
|
|
+ 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0,
|
|
+ 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a,
|
|
+ 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f,
|
|
+ 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */
|
|
+ },
|
|
+ };
|
|
+ static u8 bw_params2[3][6] = {
|
|
+ {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30,}, /* 6 MHz */
|
|
+ {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98,}, /* 7 MHz */
|
|
+ {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64,}, /* 8 MHz */
|
|
+ };
|
|
+
|
|
+
|
|
+ dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__,
|
|
+ c->frequency, c->bandwidth_hz, c->inversion);
|
|
+
|
|
+ /* program tuner */
|
|
+ if (fe->ops.tuner_ops.set_params)
|
|
+ fe->ops.tuner_ops.set_params(fe);
|
|
+
|
|
+ switch (c->bandwidth_hz) {
|
|
+ case 6000000:
|
|
+ i = 0;
|
|
+ break;
|
|
+ case 7000000:
|
|
+ i = 1;
|
|
+ break;
|
|
+ case 8000000:
|
|
+ i = 2;
|
|
+ break;
|
|
+ default:
|
|
+ dbg("invalid bandwidth");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* 1/2 split I2C write */
|
|
+ ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* 2/2 split I2C write */
|
|
+ ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ return ret;
|
|
+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;
|
|
+ int ret;
|
|
+ u8 tmp;
|
|
+ *status = 0;
|
|
+
|
|
+ if (priv->sleeping)
|
|
+ return 0;
|
|
+
|
|
+ ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ if (tmp == 11) {
|
|
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
|
|
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
|
|
+ } else if (tmp == 10) {
|
|
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
|
|
+ FE_HAS_VITERBI;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ dbg("%s: failed=%d", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
|
|
+{
|
|
+ *snr = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
|
|
+{
|
|
+ *ber = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
|
|
+{
|
|
+ *ucblocks = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
|
|
+{
|
|
+ *strength = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct dvb_frontend_ops rtl2830_ops;
|
|
+
|
|
+static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter)
|
|
+{
|
|
+ return I2C_FUNC_I2C;
|
|
+}
|
|
+
|
|
+static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
|
|
+ struct i2c_msg msg[], int num)
|
|
+{
|
|
+ struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap);
|
|
+ int ret;
|
|
+
|
|
+ /* open i2c-gate */
|
|
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = i2c_transfer(priv->i2c, msg, num);
|
|
+ if (ret < 0)
|
|
+ warn("tuner i2c failed=%d", ret);
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ dbg("%s: failed=%d", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct i2c_algorithm rtl2830_tuner_i2c_algo = {
|
|
+ .master_xfer = rtl2830_tuner_i2c_xfer,
|
|
+ .functionality = rtl2830_tuner_i2c_func,
|
|
+};
|
|
+
|
|
+struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct rtl2830_priv *priv = fe->demodulator_priv;
|
|
+ return &priv->tuner_i2c_adapter;
|
|
+}
|
|
+EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter);
|
|
+
|
|
+static void rtl2830_release(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct rtl2830_priv *priv = fe->demodulator_priv;
|
|
+
|
|
+ i2c_del_adapter(&priv->tuner_i2c_adapter);
|
|
+ kfree(priv);
|
|
+}
|
|
+
|
|
+struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg,
|
|
+ struct i2c_adapter *i2c)
|
|
+{
|
|
+ struct rtl2830_priv *priv = NULL;
|
|
+ int ret = 0;
|
|
+ u8 tmp;
|
|
+
|
|
+ /* allocate memory for the internal state */
|
|
+ priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL);
|
|
+ if (priv == NULL)
|
|
+ goto err;
|
|
+
|
|
+ /* setup the priv */
|
|
+ priv->i2c = i2c;
|
|
+ memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config));
|
|
+
|
|
+ /* check if the demod is there */
|
|
+ ret = rtl2830_rd_reg(priv, 0x000, &tmp);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* create dvb_frontend */
|
|
+ memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops));
|
|
+ priv->fe.demodulator_priv = priv;
|
|
+
|
|
+ /* create tuner i2c adapter */
|
|
+ strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter",
|
|
+ sizeof(priv->tuner_i2c_adapter.name));
|
|
+ priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo;
|
|
+ priv->tuner_i2c_adapter.algo_data = NULL;
|
|
+ i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
|
|
+ if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
|
|
+ err("tuner I2C bus could not be initialized");
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ priv->sleeping = true;
|
|
+
|
|
+ return &priv->fe;
|
|
+err:
|
|
+ dbg("%s: failed=%d", __func__, ret);
|
|
+ kfree(priv);
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(rtl2830_attach);
|
|
+
|
|
+static struct dvb_frontend_ops rtl2830_ops = {
|
|
+ .delsys = { SYS_DVBT },
|
|
+ .info = {
|
|
+ .name = "Realtek RTL2830 (DVB-T)",
|
|
+ .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 = rtl2830_release,
|
|
+
|
|
+ .init = rtl2830_init,
|
|
+ .sleep = rtl2830_sleep,
|
|
+
|
|
+ .get_tune_settings = rtl2830_get_tune_settings,
|
|
+
|
|
+ .set_frontend = rtl2830_set_frontend,
|
|
+
|
|
+ .read_status = rtl2830_read_status,
|
|
+ .read_snr = rtl2830_read_snr,
|
|
+ .read_ber = rtl2830_read_ber,
|
|
+ .read_ucblocks = rtl2830_read_ucblocks,
|
|
+ .read_signal_strength = rtl2830_read_signal_strength,
|
|
+};
|
|
+
|
|
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
|
|
+MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
|
|
+MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/rtl2830.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/rtl2830.h
|
|
@@ -0,0 +1,97 @@
|
|
+/*
|
|
+ * Realtek RTL2830 DVB-T demodulator driver
|
|
+ *
|
|
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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 RTL2830_H
|
|
+#define RTL2830_H
|
|
+
|
|
+#include <linux/dvb/frontend.h>
|
|
+
|
|
+struct rtl2830_config {
|
|
+ /*
|
|
+ * Demodulator I2C address.
|
|
+ */
|
|
+ u8 i2c_addr;
|
|
+
|
|
+ /*
|
|
+ * Xtal frequency.
|
|
+ * Hz
|
|
+ * 4000000, 16000000, 25000000, 28800000
|
|
+ */
|
|
+ u32 xtal;
|
|
+
|
|
+ /*
|
|
+ * TS output mode.
|
|
+ */
|
|
+ u8 ts_mode;
|
|
+
|
|
+ /*
|
|
+ * Spectrum inversion.
|
|
+ */
|
|
+ bool spec_inv;
|
|
+
|
|
+ /*
|
|
+ * IFs for all used modes.
|
|
+ * Hz
|
|
+ * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
|
|
+ */
|
|
+ u32 if_dvbt;
|
|
+
|
|
+ /*
|
|
+ */
|
|
+ u8 vtop;
|
|
+
|
|
+ /*
|
|
+ */
|
|
+ u8 krf;
|
|
+
|
|
+ /*
|
|
+ */
|
|
+ u8 agc_targ_val;
|
|
+};
|
|
+
|
|
+#if defined(CONFIG_DVB_RTL2830) || \
|
|
+ (defined(CONFIG_DVB_RTL2830_MODULE) && defined(MODULE))
|
|
+extern struct dvb_frontend *rtl2830_attach(
|
|
+ const struct rtl2830_config *config,
|
|
+ struct i2c_adapter *i2c
|
|
+);
|
|
+
|
|
+extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
|
|
+ struct dvb_frontend *fe
|
|
+);
|
|
+#else
|
|
+static inline struct dvb_frontend *rtl2830_attach(
|
|
+ const struct rtl2830_config *config,
|
|
+ struct i2c_adapter *i2c
|
|
+)
|
|
+{
|
|
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
|
|
+ struct dvb_frontend *fe
|
|
+)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif /* RTL2830_H */
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/rtl2830_priv.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/rtl2830_priv.h
|
|
@@ -0,0 +1,57 @@
|
|
+/*
|
|
+ * Realtek RTL2830 DVB-T demodulator driver
|
|
+ *
|
|
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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 RTL2830_PRIV_H
|
|
+#define RTL2830_PRIV_H
|
|
+
|
|
+#include "dvb_frontend.h"
|
|
+#include "rtl2830.h"
|
|
+
|
|
+#define LOG_PREFIX "rtl2830"
|
|
+
|
|
+#undef dbg
|
|
+#define dbg(f, arg...) \
|
|
+ if (rtl2830_debug) \
|
|
+ printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
|
|
+#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 rtl2830_priv {
|
|
+ struct i2c_adapter *i2c;
|
|
+ struct dvb_frontend fe;
|
|
+ struct rtl2830_config cfg;
|
|
+ struct i2c_adapter tuner_i2c_adapter;
|
|
+
|
|
+ bool sleeping;
|
|
+
|
|
+ u8 page; /* active register page */
|
|
+};
|
|
+
|
|
+struct rtl2830_reg_val_mask {
|
|
+ u16 reg;
|
|
+ u8 val;
|
|
+ u8 mask;
|
|
+};
|
|
+
|
|
+#endif /* RTL2830_PRIV_H */
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/rtl28xxu.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/rtl28xxu.c
|
|
@@ -0,0 +1,982 @@
|
|
+/*
|
|
+ * Realtek RTL28xxU DVB USB driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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 "rtl28xxu.h"
|
|
+
|
|
+#include "rtl2830.h"
|
|
+
|
|
+#include "qt1010.h"
|
|
+#include "mt2060.h"
|
|
+#include "mxl5005s.h"
|
|
+
|
|
+/* debug */
|
|
+static int dvb_usb_rtl28xxu_debug;
|
|
+module_param_named(debug, dvb_usb_rtl28xxu_debug, int, 0644);
|
|
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
|
|
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
|
+
|
|
+static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
|
|
+{
|
|
+ int ret;
|
|
+ unsigned int pipe;
|
|
+ u8 requesttype;
|
|
+ u8 *buf;
|
|
+
|
|
+ buf = kmalloc(req->size, GFP_KERNEL);
|
|
+ if (!buf) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ if (req->index & CMD_WR_FLAG) {
|
|
+ /* write */
|
|
+ memcpy(buf, req->data, req->size);
|
|
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
|
|
+ pipe = usb_sndctrlpipe(d->udev, 0);
|
|
+ } else {
|
|
+ /* read */
|
|
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
|
|
+ pipe = usb_rcvctrlpipe(d->udev, 0);
|
|
+ }
|
|
+
|
|
+ ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value,
|
|
+ req->index, buf, req->size, 1000);
|
|
+ if (ret > 0)
|
|
+ ret = 0;
|
|
+
|
|
+ deb_dump(0, requesttype, req->value, req->index, buf, req->size,
|
|
+ deb_xfer);
|
|
+
|
|
+ /* read request, copy returned data to return buf */
|
|
+ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
|
|
+ memcpy(req->data, buf, req->size);
|
|
+
|
|
+ kfree(buf);
|
|
+
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rtl2831_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
|
|
+{
|
|
+ struct rtl28xxu_req req;
|
|
+
|
|
+ if (reg < 0x3000)
|
|
+ req.index = CMD_USB_WR;
|
|
+ else if (reg < 0x4000)
|
|
+ req.index = CMD_SYS_WR;
|
|
+ else
|
|
+ req.index = CMD_IR_WR;
|
|
+
|
|
+ req.value = reg;
|
|
+ req.size = len;
|
|
+ req.data = val;
|
|
+
|
|
+ return rtl28xxu_ctrl_msg(d, &req);
|
|
+}
|
|
+
|
|
+static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
|
|
+{
|
|
+ struct rtl28xxu_req req;
|
|
+
|
|
+ if (reg < 0x3000)
|
|
+ req.index = CMD_USB_RD;
|
|
+ else if (reg < 0x4000)
|
|
+ req.index = CMD_SYS_RD;
|
|
+ else
|
|
+ req.index = CMD_IR_RD;
|
|
+
|
|
+ req.value = reg;
|
|
+ req.size = len;
|
|
+ req.data = val;
|
|
+
|
|
+ return rtl28xxu_ctrl_msg(d, &req);
|
|
+}
|
|
+
|
|
+static int rtl2831_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
|
|
+{
|
|
+ return rtl2831_wr_regs(d, reg, &val, 1);
|
|
+}
|
|
+
|
|
+static int rtl2831_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
|
|
+{
|
|
+ return rtl2831_rd_regs(d, reg, val, 1);
|
|
+}
|
|
+
|
|
+/* I2C */
|
|
+static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
|
|
+ int num)
|
|
+{
|
|
+ int ret;
|
|
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
|
|
+ struct rtl28xxu_priv *priv = d->priv;
|
|
+ struct rtl28xxu_req req;
|
|
+
|
|
+ /*
|
|
+ * It is not known which are real I2C bus xfer limits, but testing
|
|
+ * with RTL2831U + MT2060 gives max RD 24 and max WR 22 bytes.
|
|
+ * TODO: find out RTL2832U lens
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * I2C adapter logic looks rather complicated due to fact it handles
|
|
+ * three different access methods. Those methods are;
|
|
+ * 1) integrated demod access
|
|
+ * 2) old I2C access
|
|
+ * 3) new I2C access
|
|
+ *
|
|
+ * Used method is selected in order 1, 2, 3. Method 3 can handle all
|
|
+ * requests but there is two reasons why not use it always;
|
|
+ * 1) It is most expensive, usually two USB messages are needed
|
|
+ * 2) At least RTL2831U does not support it
|
|
+ *
|
|
+ * Method 3 is needed in case of I2C write+read (typical register read)
|
|
+ * where write is more than one byte.
|
|
+ */
|
|
+
|
|
+ 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 > 24 || msg[1].len > 24) {
|
|
+ /* TODO: check msg[0].len max */
|
|
+ ret = -EOPNOTSUPP;
|
|
+ goto err_mutex_unlock;
|
|
+ } else if (msg[0].addr == 0x10) {
|
|
+ /* method 1 - integrated demod */
|
|
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
|
|
+ req.index = CMD_DEMOD_RD | priv->page;
|
|
+ req.size = msg[1].len;
|
|
+ req.data = &msg[1].buf[0];
|
|
+ ret = rtl28xxu_ctrl_msg(d, &req);
|
|
+ } else if (msg[0].len < 2) {
|
|
+ /* method 2 - old I2C */
|
|
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
|
|
+ req.index = CMD_I2C_RD;
|
|
+ req.size = msg[1].len;
|
|
+ req.data = &msg[1].buf[0];
|
|
+ ret = rtl28xxu_ctrl_msg(d, &req);
|
|
+ } else {
|
|
+ /* method 3 - new I2C */
|
|
+ req.value = (msg[0].addr << 1);
|
|
+ req.index = CMD_I2C_DA_WR;
|
|
+ req.size = msg[0].len;
|
|
+ req.data = msg[0].buf;
|
|
+ ret = rtl28xxu_ctrl_msg(d, &req);
|
|
+ if (ret)
|
|
+ goto err_mutex_unlock;
|
|
+
|
|
+ req.value = (msg[0].addr << 1);
|
|
+ req.index = CMD_I2C_DA_RD;
|
|
+ req.size = msg[1].len;
|
|
+ req.data = msg[1].buf;
|
|
+ ret = rtl28xxu_ctrl_msg(d, &req);
|
|
+ }
|
|
+ } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
|
|
+ if (msg[0].len > 22) {
|
|
+ /* TODO: check msg[0].len max */
|
|
+ ret = -EOPNOTSUPP;
|
|
+ goto err_mutex_unlock;
|
|
+ } else if (msg[0].addr == 0x10) {
|
|
+ /* method 1 - integrated demod */
|
|
+ if (msg[0].buf[0] == 0x00) {
|
|
+ /* save demod page for later demod access */
|
|
+ priv->page = msg[0].buf[1];
|
|
+ ret = 0;
|
|
+ } else {
|
|
+ req.value = (msg[0].buf[0] << 8) |
|
|
+ (msg[0].addr << 1);
|
|
+ req.index = CMD_DEMOD_WR | priv->page;
|
|
+ req.size = msg[0].len-1;
|
|
+ req.data = &msg[0].buf[1];
|
|
+ ret = rtl28xxu_ctrl_msg(d, &req);
|
|
+ }
|
|
+ } else if (msg[0].len < 23) {
|
|
+ /* method 2 - old I2C */
|
|
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
|
|
+ req.index = CMD_I2C_WR;
|
|
+ req.size = msg[0].len-1;
|
|
+ req.data = &msg[0].buf[1];
|
|
+ ret = rtl28xxu_ctrl_msg(d, &req);
|
|
+ } else {
|
|
+ /* method 3 - new I2C */
|
|
+ req.value = (msg[0].addr << 1);
|
|
+ req.index = CMD_I2C_DA_WR;
|
|
+ req.size = msg[0].len;
|
|
+ req.data = msg[0].buf;
|
|
+ ret = rtl28xxu_ctrl_msg(d, &req);
|
|
+ }
|
|
+ } else {
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+err_mutex_unlock:
|
|
+ mutex_unlock(&d->i2c_mutex);
|
|
+
|
|
+ return ret ? ret : num;
|
|
+}
|
|
+
|
|
+static u32 rtl28xxu_i2c_func(struct i2c_adapter *adapter)
|
|
+{
|
|
+ return I2C_FUNC_I2C;
|
|
+}
|
|
+
|
|
+static struct i2c_algorithm rtl28xxu_i2c_algo = {
|
|
+ .master_xfer = rtl28xxu_i2c_xfer,
|
|
+ .functionality = rtl28xxu_i2c_func,
|
|
+};
|
|
+
|
|
+static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
|
|
+ .i2c_addr = 0x10, /* 0x20 */
|
|
+ .xtal = 28800000,
|
|
+ .ts_mode = 0,
|
|
+ .spec_inv = 1,
|
|
+ .if_dvbt = 36150000,
|
|
+ .vtop = 0x20,
|
|
+ .krf = 0x04,
|
|
+ .agc_targ_val = 0x2d,
|
|
+
|
|
+};
|
|
+
|
|
+static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
|
|
+ .i2c_addr = 0x10, /* 0x20 */
|
|
+ .xtal = 28800000,
|
|
+ .ts_mode = 0,
|
|
+ .spec_inv = 1,
|
|
+ .if_dvbt = 36125000,
|
|
+ .vtop = 0x20,
|
|
+ .krf = 0x04,
|
|
+ .agc_targ_val = 0x2d,
|
|
+};
|
|
+
|
|
+static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
|
|
+ .i2c_addr = 0x10, /* 0x20 */
|
|
+ .xtal = 28800000,
|
|
+ .ts_mode = 0,
|
|
+ .spec_inv = 0,
|
|
+ .if_dvbt = 4570000,
|
|
+ .vtop = 0x3f,
|
|
+ .krf = 0x04,
|
|
+ .agc_targ_val = 0x3e,
|
|
+};
|
|
+
|
|
+static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ int ret;
|
|
+ struct rtl28xxu_priv *priv = adap->dev->priv;
|
|
+ u8 buf[1];
|
|
+ struct rtl2830_config *rtl2830_config;
|
|
+ /* open RTL2831U/RTL2830 I2C gate */
|
|
+ struct rtl28xxu_req req_gate = { 0x0120, 0x0011, 0x0001, "\x08" };
|
|
+ /* for MT2060 tuner probe */
|
|
+ struct rtl28xxu_req req_mt2060 = { 0x00c0, CMD_I2C_RD, 1, buf };
|
|
+ /* for QT1010 tuner probe */
|
|
+ struct rtl28xxu_req req_qt1010 = { 0x0fc4, CMD_I2C_RD, 1, buf };
|
|
+
|
|
+ deb_info("%s:\n", __func__);
|
|
+
|
|
+ /*
|
|
+ * RTL2831U GPIOs
|
|
+ * =========================================================
|
|
+ * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?)
|
|
+ * GPIO2 | LED | 0 off | 1 on |
|
|
+ * GPIO4 | tuner#1 | 0 on | 1 off | MT2060
|
|
+ */
|
|
+
|
|
+ /* GPIO direction */
|
|
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* enable as output GPIO0, GPIO2, GPIO4 */
|
|
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /*
|
|
+ * Probe used tuner. We need to know used tuner before demod attach
|
|
+ * since there is some demod params needed to set according to tuner.
|
|
+ */
|
|
+
|
|
+ /* open demod I2C gate */
|
|
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* check QT1010 ID(?) register; reg=0f val=2c */
|
|
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_qt1010);
|
|
+ if (ret == 0 && buf[0] == 0x2c) {
|
|
+ priv->tuner = TUNER_RTL2830_QT1010;
|
|
+ rtl2830_config = &rtl28xxu_rtl2830_qt1010_config;
|
|
+ deb_info("%s: QT1010\n", __func__);
|
|
+ goto found;
|
|
+ } else {
|
|
+ deb_info("%s: QT1010 probe failed=%d - %02x\n",
|
|
+ __func__, ret, buf[0]);
|
|
+ }
|
|
+
|
|
+ /* open demod I2C gate */
|
|
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* check MT2060 ID register; reg=00 val=63 */
|
|
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2060);
|
|
+ if (ret == 0 && buf[0] == 0x63) {
|
|
+ priv->tuner = TUNER_RTL2830_MT2060;
|
|
+ rtl2830_config = &rtl28xxu_rtl2830_mt2060_config;
|
|
+ deb_info("%s: MT2060\n", __func__);
|
|
+ goto found;
|
|
+ } else {
|
|
+ deb_info("%s: MT2060 probe failed=%d - %02x\n",
|
|
+ __func__, ret, buf[0]);
|
|
+ }
|
|
+
|
|
+ /* assume MXL5005S */
|
|
+ ret = 0;
|
|
+ priv->tuner = TUNER_RTL2830_MXL5005S;
|
|
+ rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config;
|
|
+ deb_info("%s: MXL5005S\n", __func__);
|
|
+ goto found;
|
|
+
|
|
+found:
|
|
+ /* attach demodulator */
|
|
+ adap->fe_adap[0].fe = dvb_attach(rtl2830_attach, rtl2830_config,
|
|
+ &adap->dev->i2c_adap);
|
|
+ if (adap->fe_adap[0].fe == NULL) {
|
|
+ ret = -ENODEV;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ int ret;
|
|
+ struct rtl28xxu_priv *priv = adap->dev->priv;
|
|
+ u8 buf[1];
|
|
+ /* open RTL2832U/RTL2832 I2C gate */
|
|
+ struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"};
|
|
+ /* close RTL2832U/RTL2832 I2C gate */
|
|
+ struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"};
|
|
+ /* for FC2580 tuner probe */
|
|
+ struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf};
|
|
+
|
|
+ deb_info("%s:\n", __func__);
|
|
+
|
|
+ /* GPIO direction */
|
|
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* enable as output GPIO0, GPIO2, GPIO4 */
|
|
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = rtl2831_wr_reg(adap->dev, SYS_DEMOD_CTL, 0xe8);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /*
|
|
+ * Probe used tuner. We need to know used tuner before demod attach
|
|
+ * since there is some demod params needed to set according to tuner.
|
|
+ */
|
|
+
|
|
+ /* open demod I2C gate */
|
|
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_open);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* check FC2580 ID register; reg=01 val=56 */
|
|
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc2580);
|
|
+ if (ret == 0 && buf[0] == 0x56) {
|
|
+ priv->tuner = TUNER_RTL2832_FC2580;
|
|
+ deb_info("%s: FC2580\n", __func__);
|
|
+ goto found;
|
|
+ } else {
|
|
+ deb_info("%s: FC2580 probe failed=%d - %02x\n",
|
|
+ __func__, ret, buf[0]);
|
|
+ }
|
|
+
|
|
+ /* close demod I2C gate */
|
|
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* tuner not found */
|
|
+ ret = -ENODEV;
|
|
+ goto err;
|
|
+
|
|
+found:
|
|
+ /* close demod I2C gate */
|
|
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* attach demodulator */
|
|
+ /* TODO: */
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct qt1010_config rtl28xxu_qt1010_config = {
|
|
+ .i2c_address = 0x62, /* 0xc4 */
|
|
+};
|
|
+
|
|
+static struct mt2060_config rtl28xxu_mt2060_config = {
|
|
+ .i2c_address = 0x60, /* 0xc0 */
|
|
+ .clock_out = 0,
|
|
+};
|
|
+
|
|
+static struct mxl5005s_config rtl28xxu_mxl5005s_config = {
|
|
+ .i2c_address = 0x63, /* 0xc6 */
|
|
+ .if_freq = IF_FREQ_4570000HZ,
|
|
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
|
|
+ .agc_mode = MXL_SINGLE_AGC,
|
|
+ .tracking_filter = MXL_TF_C_H,
|
|
+ .rssi_enable = MXL_RSSI_ENABLE,
|
|
+ .cap_select = MXL_CAP_SEL_ENABLE,
|
|
+ .div_out = MXL_DIV_OUT_4,
|
|
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
|
|
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
|
|
+ .top = MXL5005S_TOP_25P2,
|
|
+ .mod_mode = MXL_DIGITAL_MODE,
|
|
+ .if_mode = MXL_ZERO_IF,
|
|
+ .AgcMasterByte = 0x00,
|
|
+};
|
|
+
|
|
+static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ int ret;
|
|
+ struct rtl28xxu_priv *priv = adap->dev->priv;
|
|
+ struct i2c_adapter *rtl2830_tuner_i2c;
|
|
+ struct dvb_frontend *fe;
|
|
+
|
|
+ deb_info("%s:\n", __func__);
|
|
+
|
|
+ /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */
|
|
+ rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe_adap[0].fe);
|
|
+
|
|
+ switch (priv->tuner) {
|
|
+ case TUNER_RTL2830_QT1010:
|
|
+ fe = dvb_attach(qt1010_attach, adap->fe_adap[0].fe,
|
|
+ rtl2830_tuner_i2c, &rtl28xxu_qt1010_config);
|
|
+ break;
|
|
+ case TUNER_RTL2830_MT2060:
|
|
+ fe = dvb_attach(mt2060_attach, adap->fe_adap[0].fe,
|
|
+ rtl2830_tuner_i2c, &rtl28xxu_mt2060_config,
|
|
+ 1220);
|
|
+ break;
|
|
+ case TUNER_RTL2830_MXL5005S:
|
|
+ fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
|
|
+ rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config);
|
|
+ break;
|
|
+ default:
|
|
+ fe = NULL;
|
|
+ err("unknown tuner=%d", priv->tuner);
|
|
+ }
|
|
+
|
|
+ if (fe == NULL) {
|
|
+ ret = -ENODEV;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ int ret;
|
|
+ struct rtl28xxu_priv *priv = adap->dev->priv;
|
|
+ struct dvb_frontend *fe;
|
|
+
|
|
+ deb_info("%s:\n", __func__);
|
|
+
|
|
+ switch (priv->tuner) {
|
|
+ case TUNER_RTL2832_FC2580:
|
|
+ /* TODO: */
|
|
+ fe = NULL;
|
|
+ break;
|
|
+ default:
|
|
+ fe = NULL;
|
|
+ err("unknown tuner=%d", priv->tuner);
|
|
+ }
|
|
+
|
|
+ if (fe == NULL) {
|
|
+ ret = -ENODEV;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
|
|
+{
|
|
+ int ret;
|
|
+ u8 buf[2], gpio;
|
|
+
|
|
+ deb_info("%s: onoff=%d\n", __func__, onoff);
|
|
+
|
|
+ ret = rtl2831_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ if (onoff) {
|
|
+ buf[0] = 0x00;
|
|
+ buf[1] = 0x00;
|
|
+ gpio |= 0x04; /* LED on */
|
|
+ } else {
|
|
+ buf[0] = 0x10; /* stall EPA */
|
|
+ buf[1] = 0x02; /* reset EPA */
|
|
+ gpio &= (~0x04); /* LED off */
|
|
+ }
|
|
+
|
|
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = rtl2831_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
|
|
+{
|
|
+ int ret;
|
|
+ u8 gpio, sys0;
|
|
+
|
|
+ deb_info("%s: onoff=%d\n", __func__, onoff);
|
|
+
|
|
+ /* demod adc */
|
|
+ ret = rtl2831_rd_reg(d, SYS_SYS0, &sys0);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* tuner power, read GPIOs */
|
|
+ ret = rtl2831_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ deb_info("%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
|
|
+
|
|
+ if (onoff) {
|
|
+ gpio |= 0x01; /* GPIO0 = 1 */
|
|
+ gpio &= (~0x10); /* GPIO4 = 0 */
|
|
+ sys0 = sys0 & 0x0f;
|
|
+ sys0 |= 0xe0;
|
|
+ } else {
|
|
+ gpio &= (~0x01); /* GPIO0 = 0 */
|
|
+ gpio |= 0x10; /* GPIO4 = 1 */
|
|
+ sys0 = sys0 & (~0xc0);
|
|
+ }
|
|
+
|
|
+ deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
|
|
+
|
|
+ /* demod adc */
|
|
+ ret = rtl2831_wr_reg(d, SYS_SYS0, sys0);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* tuner power, write GPIOs */
|
|
+ ret = rtl2831_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rtl2831u_rc_query(struct dvb_usb_device *d)
|
|
+{
|
|
+ int ret, i;
|
|
+ struct rtl28xxu_priv *priv = d->priv;
|
|
+ u8 buf[5];
|
|
+ u32 rc_code;
|
|
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
|
|
+ { 0x3033, 0x80 },
|
|
+ { 0x3020, 0x43 },
|
|
+ { 0x3021, 0x16 },
|
|
+ { 0x3022, 0x16 },
|
|
+ { 0x3023, 0x5a },
|
|
+ { 0x3024, 0x2d },
|
|
+ { 0x3025, 0x16 },
|
|
+ { 0x3026, 0x01 },
|
|
+ { 0x3028, 0xb0 },
|
|
+ { 0x3029, 0x04 },
|
|
+ { 0x302c, 0x88 },
|
|
+ { 0x302e, 0x13 },
|
|
+ { 0x3030, 0xdf },
|
|
+ { 0x3031, 0x05 },
|
|
+ };
|
|
+
|
|
+ /* init remote controller */
|
|
+ if (!priv->rc_active) {
|
|
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
|
|
+ ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
|
|
+ rc_nec_tab[i].val);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+ priv->rc_active = true;
|
|
+ }
|
|
+
|
|
+ ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ if (buf[4] & 0x01) {
|
|
+ if (buf[2] == (u8) ~buf[3]) {
|
|
+ if (buf[0] == (u8) ~buf[1]) {
|
|
+ /* NEC standard (16 bit) */
|
|
+ rc_code = buf[0] << 8 | buf[2];
|
|
+ } else {
|
|
+ /* NEC extended (24 bit) */
|
|
+ rc_code = buf[0] << 16 |
|
|
+ buf[1] << 8 | buf[2];
|
|
+ }
|
|
+ } else {
|
|
+ /* NEC full (32 bit) */
|
|
+ rc_code = buf[0] << 24 | buf[1] << 16 |
|
|
+ buf[2] << 8 | buf[3];
|
|
+ }
|
|
+
|
|
+ rc_keydown(d->rc_dev, rc_code, 0);
|
|
+
|
|
+ ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* repeated intentionally to avoid extra keypress */
|
|
+ ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rtl2832u_rc_query(struct dvb_usb_device *d)
|
|
+{
|
|
+ int ret, i;
|
|
+ struct rtl28xxu_priv *priv = d->priv;
|
|
+ u8 buf[128];
|
|
+ int len;
|
|
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
|
|
+ { IR_RX_CTRL, 0x20 },
|
|
+ { IR_RX_BUF_CTRL, 0x80 },
|
|
+ { IR_RX_IF, 0xff },
|
|
+ { IR_RX_IE, 0xff },
|
|
+ { IR_MAX_DURATION0, 0xd0 },
|
|
+ { IR_MAX_DURATION1, 0x07 },
|
|
+ { IR_IDLE_LEN0, 0xc0 },
|
|
+ { IR_IDLE_LEN1, 0x00 },
|
|
+ { IR_GLITCH_LEN, 0x03 },
|
|
+ { IR_RX_CLK, 0x09 },
|
|
+ { IR_RX_CFG, 0x1c },
|
|
+ { IR_MAX_H_TOL_LEN, 0x1e },
|
|
+ { IR_MAX_L_TOL_LEN, 0x1e },
|
|
+ { IR_RX_CTRL, 0x80 },
|
|
+ };
|
|
+
|
|
+ /* init remote controller */
|
|
+ if (!priv->rc_active) {
|
|
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
|
|
+ ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
|
|
+ rc_nec_tab[i].val);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+ priv->rc_active = true;
|
|
+ }
|
|
+
|
|
+ ret = rtl2831_rd_reg(d, IR_RX_IF, &buf[0]);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ if (buf[0] != 0x83)
|
|
+ goto exit;
|
|
+
|
|
+ ret = rtl2831_rd_reg(d, IR_RX_BC, &buf[0]);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ len = buf[0];
|
|
+ ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len);
|
|
+
|
|
+ /* TODO: pass raw IR to Kernel IR decoder */
|
|
+
|
|
+ ret = rtl2831_wr_reg(d, IR_RX_IF, 0x03);
|
|
+ ret = rtl2831_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
|
|
+ ret = rtl2831_wr_reg(d, IR_RX_CTRL, 0x80);
|
|
+
|
|
+exit:
|
|
+ return ret;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+enum rtl28xxu_usb_table_entry {
|
|
+ RTL2831U_0BDA_2831,
|
|
+ RTL2831U_14AA_0160,
|
|
+ RTL2831U_14AA_0161,
|
|
+};
|
|
+
|
|
+static struct usb_device_id rtl28xxu_table[] = {
|
|
+ /* RTL2831U */
|
|
+ [RTL2831U_0BDA_2831] = {
|
|
+ USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U)},
|
|
+ [RTL2831U_14AA_0160] = {
|
|
+ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT)},
|
|
+ [RTL2831U_14AA_0161] = {
|
|
+ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2)},
|
|
+
|
|
+ /* RTL2832U */
|
|
+ {} /* terminating entry */
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(usb, rtl28xxu_table);
|
|
+
|
|
+static struct dvb_usb_device_properties rtl28xxu_properties[] = {
|
|
+ {
|
|
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
|
|
+
|
|
+ .usb_ctrl = DEVICE_SPECIFIC,
|
|
+ .no_reconnect = 1,
|
|
+
|
|
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
|
|
+
|
|
+ .num_adapters = 1,
|
|
+ .adapter = {
|
|
+ {
|
|
+ .num_frontends = 1,
|
|
+ .fe = {
|
|
+ {
|
|
+ .frontend_attach = rtl2831u_frontend_attach,
|
|
+ .tuner_attach = rtl2831u_tuner_attach,
|
|
+ .streaming_ctrl = rtl28xxu_streaming_ctrl,
|
|
+ .stream = {
|
|
+ .type = USB_BULK,
|
|
+ .count = 6,
|
|
+ .endpoint = 0x81,
|
|
+ .u = {
|
|
+ .bulk = {
|
|
+ .buffersize = 8*512,
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ },
|
|
+
|
|
+ .power_ctrl = rtl28xxu_power_ctrl,
|
|
+
|
|
+ .rc.core = {
|
|
+ .protocol = RC_TYPE_NEC,
|
|
+ .module_name = "rtl28xxu",
|
|
+ .rc_query = rtl2831u_rc_query,
|
|
+ .rc_interval = 400,
|
|
+ .allowed_protos = RC_TYPE_NEC,
|
|
+ .rc_codes = RC_MAP_EMPTY,
|
|
+ },
|
|
+
|
|
+ .i2c_algo = &rtl28xxu_i2c_algo,
|
|
+
|
|
+ .num_device_descs = 2,
|
|
+ .devices = {
|
|
+ {
|
|
+ .name = "Realtek RTL2831U reference design",
|
|
+ .warm_ids = {
|
|
+ &rtl28xxu_table[RTL2831U_0BDA_2831],
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .name = "Freecom USB2.0 DVB-T",
|
|
+ .warm_ids = {
|
|
+ &rtl28xxu_table[RTL2831U_14AA_0160],
|
|
+ &rtl28xxu_table[RTL2831U_14AA_0161],
|
|
+ },
|
|
+ },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
|
|
+
|
|
+ .usb_ctrl = DEVICE_SPECIFIC,
|
|
+ .no_reconnect = 1,
|
|
+
|
|
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
|
|
+
|
|
+ .num_adapters = 1,
|
|
+ .adapter = {
|
|
+ {
|
|
+ .num_frontends = 1,
|
|
+ .fe = {
|
|
+ {
|
|
+ .frontend_attach = rtl2832u_frontend_attach,
|
|
+ .tuner_attach = rtl2832u_tuner_attach,
|
|
+ .streaming_ctrl = rtl28xxu_streaming_ctrl,
|
|
+ .stream = {
|
|
+ .type = USB_BULK,
|
|
+ .count = 6,
|
|
+ .endpoint = 0x81,
|
|
+ .u = {
|
|
+ .bulk = {
|
|
+ .buffersize = 8*512,
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ },
|
|
+
|
|
+ .power_ctrl = rtl28xxu_power_ctrl,
|
|
+
|
|
+ .rc.core = {
|
|
+ .protocol = RC_TYPE_NEC,
|
|
+ .module_name = "rtl28xxu",
|
|
+ .rc_query = rtl2832u_rc_query,
|
|
+ .rc_interval = 400,
|
|
+ .allowed_protos = RC_TYPE_NEC,
|
|
+ .rc_codes = RC_MAP_EMPTY,
|
|
+ },
|
|
+
|
|
+ .i2c_algo = &rtl28xxu_i2c_algo,
|
|
+
|
|
+ .num_device_descs = 0, /* disabled as no support for RTL2832 */
|
|
+ .devices = {
|
|
+ {
|
|
+ .name = "Realtek RTL2832U reference design",
|
|
+ },
|
|
+ }
|
|
+ },
|
|
+
|
|
+};
|
|
+
|
|
+static int rtl28xxu_probe(struct usb_interface *intf,
|
|
+ const struct usb_device_id *id)
|
|
+{
|
|
+ int ret, i;
|
|
+ int properties_count = ARRAY_SIZE(rtl28xxu_properties);
|
|
+ struct dvb_usb_device *d;
|
|
+
|
|
+ deb_info("%s: interface=%d\n", __func__,
|
|
+ intf->cur_altsetting->desc.bInterfaceNumber);
|
|
+
|
|
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; i < properties_count; i++) {
|
|
+ ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i],
|
|
+ THIS_MODULE, &d, adapter_nr);
|
|
+ if (ret == 0 || ret != -ENODEV)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* init USB endpoints */
|
|
+ ret = rtl2831_wr_reg(d, USB_SYSCTL_0, 0x09);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = rtl2831_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = rtl2831_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ return ret;
|
|
+err:
|
|
+ deb_info("%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct usb_driver rtl28xxu_driver = {
|
|
+ .name = "dvb_usb_rtl28xxu",
|
|
+ .probe = rtl28xxu_probe,
|
|
+ .disconnect = dvb_usb_device_exit,
|
|
+ .id_table = rtl28xxu_table,
|
|
+};
|
|
+
|
|
+/* module stuff */
|
|
+static int __init rtl28xxu_module_init(void)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ deb_info("%s:\n", __func__);
|
|
+
|
|
+ ret = usb_register(&rtl28xxu_driver);
|
|
+ if (ret)
|
|
+ err("usb_register failed=%d", ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void __exit rtl28xxu_module_exit(void)
|
|
+{
|
|
+ deb_info("%s:\n", __func__);
|
|
+
|
|
+ /* deregister this driver from the USB subsystem */
|
|
+ usb_deregister(&rtl28xxu_driver);
|
|
+}
|
|
+
|
|
+module_init(rtl28xxu_module_init);
|
|
+module_exit(rtl28xxu_module_exit);
|
|
+
|
|
+MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver");
|
|
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
|
|
+MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/rtl28xxu.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/rtl28xxu.h
|
|
@@ -0,0 +1,264 @@
|
|
+/*
|
|
+ * Realtek RTL28xxU DVB USB driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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 RTL28XXU_H
|
|
+#define RTL28XXU_H
|
|
+
|
|
+#define DVB_USB_LOG_PREFIX "rtl28xxu"
|
|
+#include "dvb-usb.h"
|
|
+
|
|
+#define deb_info(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x01, args)
|
|
+#define deb_rc(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x02, args)
|
|
+#define deb_xfer(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x04, args)
|
|
+#define deb_reg(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x08, args)
|
|
+#define deb_i2c(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x10, args)
|
|
+#define deb_fw(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x20, args)
|
|
+
|
|
+#define deb_dump(r, t, v, i, b, l, func) { \
|
|
+ int loop_; \
|
|
+ func("%02x %02x %02x %02x %02x %02x %02x %02x", \
|
|
+ t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
|
|
+ if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
|
|
+ func(" >>> "); \
|
|
+ else \
|
|
+ func(" <<< "); \
|
|
+ for (loop_ = 0; loop_ < l; loop_++) \
|
|
+ func("%02x ", b[loop_]); \
|
|
+ func("\n");\
|
|
+}
|
|
+
|
|
+/*
|
|
+ * USB commands
|
|
+ * (usb_control_msg() index parameter)
|
|
+ */
|
|
+
|
|
+#define DEMOD 0x0000
|
|
+#define USB 0x0100
|
|
+#define SYS 0x0200
|
|
+#define I2C 0x0300
|
|
+#define I2C_DA 0x0600
|
|
+
|
|
+#define CMD_WR_FLAG 0x0010
|
|
+#define CMD_DEMOD_RD 0x0000
|
|
+#define CMD_DEMOD_WR 0x0010
|
|
+#define CMD_USB_RD 0x0100
|
|
+#define CMD_USB_WR 0x0110
|
|
+#define CMD_SYS_RD 0x0200
|
|
+#define CMD_IR_RD 0x0201
|
|
+#define CMD_IR_WR 0x0211
|
|
+#define CMD_SYS_WR 0x0210
|
|
+#define CMD_I2C_RD 0x0300
|
|
+#define CMD_I2C_WR 0x0310
|
|
+#define CMD_I2C_DA_RD 0x0600
|
|
+#define CMD_I2C_DA_WR 0x0610
|
|
+
|
|
+
|
|
+struct rtl28xxu_priv {
|
|
+ u8 chip_id;
|
|
+ u8 tuner;
|
|
+ u8 page; /* integrated demod active register page */
|
|
+ bool rc_active;
|
|
+};
|
|
+
|
|
+enum rtl28xxu_chip_id {
|
|
+ CHIP_ID_NONE,
|
|
+ CHIP_ID_RTL2831U,
|
|
+ CHIP_ID_RTL2832U,
|
|
+};
|
|
+
|
|
+enum rtl28xxu_tuner {
|
|
+ TUNER_NONE,
|
|
+
|
|
+ TUNER_RTL2830_QT1010,
|
|
+ TUNER_RTL2830_MT2060,
|
|
+ TUNER_RTL2830_MXL5005S,
|
|
+
|
|
+ TUNER_RTL2832_MT2266,
|
|
+ TUNER_RTL2832_FC2580,
|
|
+ TUNER_RTL2832_MT2063,
|
|
+ TUNER_RTL2832_MAX3543,
|
|
+ TUNER_RTL2832_TUA9001,
|
|
+ TUNER_RTL2832_MXL5007T,
|
|
+ TUNER_RTL2832_FC0012,
|
|
+ TUNER_RTL2832_E4000,
|
|
+ TUNER_RTL2832_TDA18272,
|
|
+ TUNER_RTL2832_FC0013,
|
|
+};
|
|
+
|
|
+struct rtl28xxu_req {
|
|
+ u16 value;
|
|
+ u16 index;
|
|
+ u16 size;
|
|
+ u8 *data;
|
|
+};
|
|
+
|
|
+struct rtl28xxu_reg_val {
|
|
+ u16 reg;
|
|
+ u8 val;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * memory map
|
|
+ *
|
|
+ * 0x0000 DEMOD : demodulator
|
|
+ * 0x2000 USB : SIE, USB endpoint, debug, DMA
|
|
+ * 0x3000 SYS : system
|
|
+ * 0xfc00 RC : remote controller (not RTL2831U)
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * USB registers
|
|
+ */
|
|
+/* SIE Control Registers */
|
|
+#define USB_SYSCTL 0x2000 /* USB system control */
|
|
+#define USB_SYSCTL_0 0x2000 /* USB system control */
|
|
+#define USB_SYSCTL_1 0x2001 /* USB system control */
|
|
+#define USB_SYSCTL_2 0x2002 /* USB system control */
|
|
+#define USB_SYSCTL_3 0x2003 /* USB system control */
|
|
+#define USB_IRQSTAT 0x2008 /* SIE interrupt status */
|
|
+#define USB_IRQEN 0x200C /* SIE interrupt enable */
|
|
+#define USB_CTRL 0x2010 /* USB control */
|
|
+#define USB_STAT 0x2014 /* USB status */
|
|
+#define USB_DEVADDR 0x2018 /* USB device address */
|
|
+#define USB_TEST 0x201C /* USB test mode */
|
|
+#define USB_FRAME_NUMBER 0x2020 /* frame number */
|
|
+#define USB_FIFO_ADDR 0x2028 /* address of SIE FIFO RAM */
|
|
+#define USB_FIFO_CMD 0x202A /* SIE FIFO RAM access command */
|
|
+#define USB_FIFO_DATA 0x2030 /* SIE FIFO RAM data */
|
|
+/* Endpoint Registers */
|
|
+#define EP0_SETUPA 0x20F8 /* EP 0 setup packet lower byte */
|
|
+#define EP0_SETUPB 0x20FC /* EP 0 setup packet higher byte */
|
|
+#define USB_EP0_CFG 0x2104 /* EP 0 configure */
|
|
+#define USB_EP0_CTL 0x2108 /* EP 0 control */
|
|
+#define USB_EP0_STAT 0x210C /* EP 0 status */
|
|
+#define USB_EP0_IRQSTAT 0x2110 /* EP 0 interrupt status */
|
|
+#define USB_EP0_IRQEN 0x2114 /* EP 0 interrupt enable */
|
|
+#define USB_EP0_MAXPKT 0x2118 /* EP 0 max packet size */
|
|
+#define USB_EP0_BC 0x2120 /* EP 0 FIFO byte counter */
|
|
+#define USB_EPA_CFG 0x2144 /* EP A configure */
|
|
+#define USB_EPA_CFG_0 0x2144 /* EP A configure */
|
|
+#define USB_EPA_CFG_1 0x2145 /* EP A configure */
|
|
+#define USB_EPA_CFG_2 0x2146 /* EP A configure */
|
|
+#define USB_EPA_CFG_3 0x2147 /* EP A configure */
|
|
+#define USB_EPA_CTL 0x2148 /* EP A control */
|
|
+#define USB_EPA_CTL_0 0x2148 /* EP A control */
|
|
+#define USB_EPA_CTL_1 0x2149 /* EP A control */
|
|
+#define USB_EPA_CTL_2 0x214A /* EP A control */
|
|
+#define USB_EPA_CTL_3 0x214B /* EP A control */
|
|
+#define USB_EPA_STAT 0x214C /* EP A status */
|
|
+#define USB_EPA_IRQSTAT 0x2150 /* EP A interrupt status */
|
|
+#define USB_EPA_IRQEN 0x2154 /* EP A interrupt enable */
|
|
+#define USB_EPA_MAXPKT 0x2158 /* EP A max packet size */
|
|
+#define USB_EPA_MAXPKT_0 0x2158 /* EP A max packet size */
|
|
+#define USB_EPA_MAXPKT_1 0x2159 /* EP A max packet size */
|
|
+#define USB_EPA_MAXPKT_2 0x215A /* EP A max packet size */
|
|
+#define USB_EPA_MAXPKT_3 0x215B /* EP A max packet size */
|
|
+#define USB_EPA_FIFO_CFG 0x2160 /* EP A FIFO configure */
|
|
+#define USB_EPA_FIFO_CFG_0 0x2160 /* EP A FIFO configure */
|
|
+#define USB_EPA_FIFO_CFG_1 0x2161 /* EP A FIFO configure */
|
|
+#define USB_EPA_FIFO_CFG_2 0x2162 /* EP A FIFO configure */
|
|
+#define USB_EPA_FIFO_CFG_3 0x2163 /* EP A FIFO configure */
|
|
+/* Debug Registers */
|
|
+#define USB_PHYTSTDIS 0x2F04 /* PHY test disable */
|
|
+#define USB_TOUT_VAL 0x2F08 /* USB time-out time */
|
|
+#define USB_VDRCTRL 0x2F10 /* UTMI vendor signal control */
|
|
+#define USB_VSTAIN 0x2F14 /* UTMI vendor signal status in */
|
|
+#define USB_VLOADM 0x2F18 /* UTMI load vendor signal status in */
|
|
+#define USB_VSTAOUT 0x2F1C /* UTMI vendor signal status out */
|
|
+#define USB_UTMI_TST 0x2F80 /* UTMI test */
|
|
+#define USB_UTMI_STATUS 0x2F84 /* UTMI status */
|
|
+#define USB_TSTCTL 0x2F88 /* test control */
|
|
+#define USB_TSTCTL2 0x2F8C /* test control 2 */
|
|
+#define USB_PID_FORCE 0x2F90 /* force PID */
|
|
+#define USB_PKTERR_CNT 0x2F94 /* packet error counter */
|
|
+#define USB_RXERR_CNT 0x2F98 /* RX error counter */
|
|
+#define USB_MEM_BIST 0x2F9C /* MEM BIST test */
|
|
+#define USB_SLBBIST 0x2FA0 /* self-loop-back BIST */
|
|
+#define USB_CNTTEST 0x2FA4 /* counter test */
|
|
+#define USB_PHYTST 0x2FC0 /* USB PHY test */
|
|
+#define USB_DBGIDX 0x2FF0 /* select individual block debug signal */
|
|
+#define USB_DBGMUX 0x2FF4 /* debug signal module mux */
|
|
+
|
|
+/*
|
|
+ * SYS registers
|
|
+ */
|
|
+/* demod control registers */
|
|
+#define SYS_SYS0 0x3000 /* include DEMOD_CTL, GPO, GPI, GPOE */
|
|
+#define SYS_DEMOD_CTL 0x3000 /* control register for DVB-T demodulator */
|
|
+/* GPIO registers */
|
|
+#define SYS_GPIO_OUT_VAL 0x3001 /* output value of GPIO */
|
|
+#define SYS_GPIO_IN_VAL 0x3002 /* input value of GPIO */
|
|
+#define SYS_GPIO_OUT_EN 0x3003 /* output enable of GPIO */
|
|
+#define SYS_SYS1 0x3004 /* include GPD, SYSINTE, SYSINTS, GP_CFG0 */
|
|
+#define SYS_GPIO_DIR 0x3004 /* direction control for GPIO */
|
|
+#define SYS_SYSINTE 0x3005 /* system interrupt enable */
|
|
+#define SYS_SYSINTS 0x3006 /* system interrupt status */
|
|
+#define SYS_GPIO_CFG0 0x3007 /* PAD configuration for GPIO0-GPIO3 */
|
|
+#define SYS_SYS2 0x3008 /* include GP_CFG1 and 3 reserved bytes */
|
|
+#define SYS_GPIO_CFG1 0x3008 /* PAD configuration for GPIO4 */
|
|
+#define SYS_DEMOD_CTL1 0x300B
|
|
+
|
|
+/* IrDA registers */
|
|
+#define SYS_IRRC_PSR 0x3020 /* IR protocol selection */
|
|
+#define SYS_IRRC_PER 0x3024 /* IR protocol extension */
|
|
+#define SYS_IRRC_SF 0x3028 /* IR sampling frequency */
|
|
+#define SYS_IRRC_DPIR 0x302C /* IR data package interval */
|
|
+#define SYS_IRRC_CR 0x3030 /* IR control */
|
|
+#define SYS_IRRC_RP 0x3034 /* IR read port */
|
|
+#define SYS_IRRC_SR 0x3038 /* IR status */
|
|
+/* I2C master registers */
|
|
+#define SYS_I2CCR 0x3040 /* I2C clock */
|
|
+#define SYS_I2CMCR 0x3044 /* I2C master control */
|
|
+#define SYS_I2CMSTR 0x3048 /* I2C master SCL timing */
|
|
+#define SYS_I2CMSR 0x304C /* I2C master status */
|
|
+#define SYS_I2CMFR 0x3050 /* I2C master FIFO */
|
|
+
|
|
+/*
|
|
+ * IR registers
|
|
+ */
|
|
+#define IR_RX_BUF 0xFC00
|
|
+#define IR_RX_IE 0xFD00
|
|
+#define IR_RX_IF 0xFD01
|
|
+#define IR_RX_CTRL 0xFD02
|
|
+#define IR_RX_CFG 0xFD03
|
|
+#define IR_MAX_DURATION0 0xFD04
|
|
+#define IR_MAX_DURATION1 0xFD05
|
|
+#define IR_IDLE_LEN0 0xFD06
|
|
+#define IR_IDLE_LEN1 0xFD07
|
|
+#define IR_GLITCH_LEN 0xFD08
|
|
+#define IR_RX_BUF_CTRL 0xFD09
|
|
+#define IR_RX_BUF_DATA 0xFD0A
|
|
+#define IR_RX_BC 0xFD0B
|
|
+#define IR_RX_CLK 0xFD0C
|
|
+#define IR_RX_C_COUNT_L 0xFD0D
|
|
+#define IR_RX_C_COUNT_H 0xFD0E
|
|
+#define IR_SUSPEND_CTRL 0xFD10
|
|
+#define IR_ERR_TOL_CTRL 0xFD11
|
|
+#define IR_UNIT_LEN 0xFD12
|
|
+#define IR_ERR_TOL_LEN 0xFD13
|
|
+#define IR_MAX_H_TOL_LEN 0xFD14
|
|
+#define IR_MAX_L_TOL_LEN 0xFD15
|
|
+#define IR_MASK_CTRL 0xFD16
|
|
+#define IR_MASK_DATA 0xFD17
|
|
+#define IR_RES_MASK_ADDR 0xFD18
|
|
+#define IR_RES_MASK_T_LEN 0xFD19
|
|
+
|
|
+#endif
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-jpeg/jpeg-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-jpeg/jpeg-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-jpeg/jpeg-core.c
|
|
@@ -32,10 +32,9 @@
|
|
|
|
static struct s5p_jpeg_fmt formats_enc[] = {
|
|
{
|
|
- .name = "YUV 4:2:0 planar, YCbCr",
|
|
- .fourcc = V4L2_PIX_FMT_YUV420,
|
|
- .depth = 12,
|
|
- .colplanes = 3,
|
|
+ .name = "JPEG JFIF",
|
|
+ .fourcc = V4L2_PIX_FMT_JPEG,
|
|
+ .colplanes = 1,
|
|
.types = MEM2MEM_CAPTURE,
|
|
},
|
|
{
|
|
@@ -43,7 +42,7 @@ static struct s5p_jpeg_fmt formats_enc[]
|
|
.fourcc = V4L2_PIX_FMT_YUYV,
|
|
.depth = 16,
|
|
.colplanes = 1,
|
|
- .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
|
|
+ .types = MEM2MEM_OUTPUT,
|
|
},
|
|
{
|
|
.name = "RGB565",
|
|
@@ -203,6 +202,16 @@ static const unsigned char hactblg0[162]
|
|
0xf9, 0xfa
|
|
};
|
|
|
|
+static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)
|
|
+{
|
|
+ return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler);
|
|
+}
|
|
+
|
|
+static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh)
|
|
+{
|
|
+ return container_of(fh, struct s5p_jpeg_ctx, fh);
|
|
+}
|
|
+
|
|
static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl,
|
|
unsigned long tab, int len)
|
|
{
|
|
@@ -269,6 +278,7 @@ static int queue_init(void *priv, struct
|
|
struct vb2_queue *dst_vq);
|
|
static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
|
|
__u32 pixelformat);
|
|
+static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx);
|
|
|
|
static int s5p_jpeg_open(struct file *file)
|
|
{
|
|
@@ -276,12 +286,18 @@ static int s5p_jpeg_open(struct file *fi
|
|
struct video_device *vfd = video_devdata(file);
|
|
struct s5p_jpeg_ctx *ctx;
|
|
struct s5p_jpeg_fmt *out_fmt;
|
|
+ int ret = 0;
|
|
|
|
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
|
|
if (!ctx)
|
|
return -ENOMEM;
|
|
|
|
- file->private_data = ctx;
|
|
+ v4l2_fh_init(&ctx->fh, vfd);
|
|
+ /* Use separate control handler per file handle */
|
|
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
|
|
+ file->private_data = &ctx->fh;
|
|
+ v4l2_fh_add(&ctx->fh);
|
|
+
|
|
ctx->jpeg = jpeg;
|
|
if (vfd == jpeg->vfd_encoder) {
|
|
ctx->mode = S5P_JPEG_ENCODE;
|
|
@@ -291,24 +307,35 @@ static int s5p_jpeg_open(struct file *fi
|
|
out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG);
|
|
}
|
|
|
|
+ ret = s5p_jpeg_controls_create(ctx);
|
|
+ if (ret < 0)
|
|
+ goto error;
|
|
+
|
|
ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init);
|
|
if (IS_ERR(ctx->m2m_ctx)) {
|
|
- int err = PTR_ERR(ctx->m2m_ctx);
|
|
- kfree(ctx);
|
|
- return err;
|
|
+ ret = PTR_ERR(ctx->m2m_ctx);
|
|
+ goto error;
|
|
}
|
|
|
|
ctx->out_q.fmt = out_fmt;
|
|
ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV);
|
|
-
|
|
return 0;
|
|
+
|
|
+error:
|
|
+ v4l2_fh_del(&ctx->fh);
|
|
+ v4l2_fh_exit(&ctx->fh);
|
|
+ kfree(ctx);
|
|
+ return ret;
|
|
}
|
|
|
|
static int s5p_jpeg_release(struct file *file)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = file->private_data;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
|
|
|
|
v4l2_m2m_ctx_release(ctx->m2m_ctx);
|
|
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
|
|
+ v4l2_fh_del(&ctx->fh);
|
|
+ v4l2_fh_exit(&ctx->fh);
|
|
kfree(ctx);
|
|
|
|
return 0;
|
|
@@ -317,14 +344,14 @@ static int s5p_jpeg_release(struct file
|
|
static unsigned int s5p_jpeg_poll(struct file *file,
|
|
struct poll_table_struct *wait)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = file->private_data;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
|
|
|
|
return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
|
|
}
|
|
|
|
static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = file->private_data;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
|
|
|
|
return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
|
|
}
|
|
@@ -448,7 +475,7 @@ static bool s5p_jpeg_parse_hdr(struct s5
|
|
static int s5p_jpeg_querycap(struct file *file, void *priv,
|
|
struct v4l2_capability *cap)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
if (ctx->mode == S5P_JPEG_ENCODE) {
|
|
strlcpy(cap->driver, S5P_JPEG_M2M_NAME " encoder",
|
|
@@ -497,9 +524,7 @@ static int enum_fmt(struct s5p_jpeg_fmt
|
|
static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
struct v4l2_fmtdesc *f)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx;
|
|
-
|
|
- ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
if (ctx->mode == S5P_JPEG_ENCODE)
|
|
return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
|
|
@@ -511,9 +536,7 @@ static int s5p_jpeg_enum_fmt_vid_cap(str
|
|
static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
|
|
struct v4l2_fmtdesc *f)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx;
|
|
-
|
|
- ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
if (ctx->mode == S5P_JPEG_ENCODE)
|
|
return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
|
|
@@ -538,7 +561,7 @@ static int s5p_jpeg_g_fmt(struct file *f
|
|
struct vb2_queue *vq;
|
|
struct s5p_jpeg_q_data *q_data = NULL;
|
|
struct v4l2_pix_format *pix = &f->fmt.pix;
|
|
- struct s5p_jpeg_ctx *ct = priv;
|
|
+ struct s5p_jpeg_ctx *ct = fh_to_ctx(priv);
|
|
|
|
vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
|
|
if (!vq)
|
|
@@ -659,8 +682,8 @@ static int vidioc_try_fmt(struct v4l2_fo
|
|
static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
|
|
struct v4l2_format *f)
|
|
{
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
struct s5p_jpeg_fmt *fmt;
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
|
|
fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
|
|
if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
|
|
@@ -676,8 +699,8 @@ static int s5p_jpeg_try_fmt_vid_cap(stru
|
|
static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv,
|
|
struct v4l2_format *f)
|
|
{
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
struct s5p_jpeg_fmt *fmt;
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
|
|
fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
|
|
if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
|
|
@@ -728,7 +751,7 @@ static int s5p_jpeg_s_fmt_vid_cap(struct
|
|
if (ret)
|
|
return ret;
|
|
|
|
- return s5p_jpeg_s_fmt(priv, f);
|
|
+ return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
|
|
}
|
|
|
|
static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv,
|
|
@@ -740,13 +763,13 @@ static int s5p_jpeg_s_fmt_vid_out(struct
|
|
if (ret)
|
|
return ret;
|
|
|
|
- return s5p_jpeg_s_fmt(priv, f);
|
|
+ return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
|
|
}
|
|
|
|
static int s5p_jpeg_reqbufs(struct file *file, void *priv,
|
|
struct v4l2_requestbuffers *reqbufs)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
|
|
}
|
|
@@ -754,14 +777,14 @@ static int s5p_jpeg_reqbufs(struct file
|
|
static int s5p_jpeg_querybuf(struct file *file, void *priv,
|
|
struct v4l2_buffer *buf)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
|
|
}
|
|
|
|
static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
|
|
}
|
|
@@ -769,7 +792,7 @@ static int s5p_jpeg_qbuf(struct file *fi
|
|
static int s5p_jpeg_dqbuf(struct file *file, void *priv,
|
|
struct v4l2_buffer *buf)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
|
|
}
|
|
@@ -777,7 +800,7 @@ static int s5p_jpeg_dqbuf(struct file *f
|
|
static int s5p_jpeg_streamon(struct file *file, void *priv,
|
|
enum v4l2_buf_type type)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
|
|
}
|
|
@@ -785,7 +808,7 @@ static int s5p_jpeg_streamon(struct file
|
|
static int s5p_jpeg_streamoff(struct file *file, void *priv,
|
|
enum v4l2_buf_type type)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
|
|
}
|
|
@@ -793,7 +816,7 @@ static int s5p_jpeg_streamoff(struct fil
|
|
int s5p_jpeg_g_selection(struct file *file, void *priv,
|
|
struct v4l2_selection *s)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
|
|
s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
@@ -822,33 +845,89 @@ int s5p_jpeg_g_selection(struct file *fi
|
|
return 0;
|
|
}
|
|
|
|
-static int s5p_jpeg_g_jpegcomp(struct file *file, void *priv,
|
|
- struct v4l2_jpegcompression *compr)
|
|
+/*
|
|
+ * V4L2 controls
|
|
+ */
|
|
+
|
|
+static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
|
|
+ struct s5p_jpeg *jpeg = ctx->jpeg;
|
|
+ unsigned long flags;
|
|
|
|
- if (ctx->mode == S5P_JPEG_DECODE)
|
|
- return -ENOTTY;
|
|
+ switch (ctrl->id) {
|
|
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
|
|
+ spin_lock_irqsave(&jpeg->slock, flags);
|
|
+
|
|
+ WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY);
|
|
+ if (ctx->subsampling > 2)
|
|
+ ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
|
|
+ else
|
|
+ ctrl->val = ctx->subsampling;
|
|
+ spin_unlock_irqrestore(&jpeg->slock, flags);
|
|
+ break;
|
|
+ }
|
|
|
|
- memset(compr, 0, sizeof(*compr));
|
|
- compr->quality = ctx->compr_quality;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&ctx->jpeg->slock, flags);
|
|
+
|
|
+ switch (ctrl->id) {
|
|
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
|
|
+ ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val;
|
|
+ break;
|
|
+ case V4L2_CID_JPEG_RESTART_INTERVAL:
|
|
+ ctx->restart_interval = ctrl->val;
|
|
+ break;
|
|
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
|
|
+ ctx->subsampling = ctrl->val;
|
|
+ break;
|
|
+ }
|
|
|
|
+ spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
|
|
return 0;
|
|
}
|
|
|
|
-static int s5p_jpeg_s_jpegcomp(struct file *file, void *priv,
|
|
- struct v4l2_jpegcompression *compr)
|
|
+static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = {
|
|
+ .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl,
|
|
+ .s_ctrl = s5p_jpeg_s_ctrl,
|
|
+};
|
|
+
|
|
+static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)
|
|
{
|
|
- struct s5p_jpeg_ctx *ctx = priv;
|
|
+ unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */
|
|
+ struct v4l2_ctrl *ctrl;
|
|
|
|
- if (ctx->mode == S5P_JPEG_DECODE)
|
|
- return -ENOTTY;
|
|
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
|
|
|
|
- compr->quality = clamp(compr->quality, S5P_JPEG_COMPR_QUAL_BEST,
|
|
- S5P_JPEG_COMPR_QUAL_WORST);
|
|
+ if (ctx->mode == S5P_JPEG_ENCODE) {
|
|
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
|
|
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
|
|
+ 0, 3, 1, 3);
|
|
+
|
|
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
|
|
+ V4L2_CID_JPEG_RESTART_INTERVAL,
|
|
+ 0, 3, 0xffff, 0);
|
|
+ mask = ~0x06; /* 422, 420 */
|
|
+ }
|
|
+
|
|
+ ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
|
|
+ V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
|
|
+ V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask,
|
|
+ V4L2_JPEG_CHROMA_SUBSAMPLING_422);
|
|
|
|
- ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - compr->quality;
|
|
+ if (ctx->ctrl_handler.error)
|
|
+ return ctx->ctrl_handler.error;
|
|
|
|
+ if (ctx->mode == S5P_JPEG_DECODE)
|
|
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
|
|
+ V4L2_CTRL_FLAG_READ_ONLY;
|
|
return 0;
|
|
}
|
|
|
|
@@ -877,9 +956,6 @@ static const struct v4l2_ioctl_ops s5p_j
|
|
.vidioc_streamoff = s5p_jpeg_streamoff,
|
|
|
|
.vidioc_g_selection = s5p_jpeg_g_selection,
|
|
-
|
|
- .vidioc_g_jpegcomp = s5p_jpeg_g_jpegcomp,
|
|
- .vidioc_s_jpegcomp = s5p_jpeg_s_jpegcomp,
|
|
};
|
|
|
|
/*
|
|
@@ -908,13 +984,8 @@ static void s5p_jpeg_device_run(void *pr
|
|
jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565);
|
|
else
|
|
jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422);
|
|
- if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV)
|
|
- jpeg_subsampling_mode(jpeg->regs,
|
|
- S5P_JPEG_SUBSAMPLING_422);
|
|
- else
|
|
- jpeg_subsampling_mode(jpeg->regs,
|
|
- S5P_JPEG_SUBSAMPLING_420);
|
|
- jpeg_dri(jpeg->regs, 0);
|
|
+ jpeg_subsampling_mode(jpeg->regs, ctx->subsampling);
|
|
+ jpeg_dri(jpeg->regs, ctx->restart_interval);
|
|
jpeg_x(jpeg->regs, ctx->out_q.w);
|
|
jpeg_y(jpeg->regs, ctx->out_q.h);
|
|
jpeg_imgadr(jpeg->regs, src_addr);
|
|
@@ -953,14 +1024,18 @@ static void s5p_jpeg_device_run(void *pr
|
|
jpeg_htbl_dc(jpeg->regs, 2);
|
|
jpeg_htbl_ac(jpeg->regs, 3);
|
|
jpeg_htbl_dc(jpeg->regs, 3);
|
|
- } else {
|
|
+ } else { /* S5P_JPEG_DECODE */
|
|
jpeg_rst_int_enable(jpeg->regs, true);
|
|
jpeg_data_num_int_enable(jpeg->regs, true);
|
|
jpeg_final_mcu_num_int_enable(jpeg->regs, true);
|
|
- jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422);
|
|
+ if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV)
|
|
+ jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422);
|
|
+ else
|
|
+ jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420);
|
|
jpeg_jpgadr(jpeg->regs, src_addr);
|
|
jpeg_imgadr(jpeg->regs, dst_addr);
|
|
}
|
|
+
|
|
jpeg_start(jpeg->regs);
|
|
}
|
|
|
|
@@ -1162,6 +1237,8 @@ static irqreturn_t s5p_jpeg_irq(int irq,
|
|
bool timer_elapsed = false;
|
|
bool op_completed = false;
|
|
|
|
+ spin_lock(&jpeg->slock);
|
|
+
|
|
curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
|
|
|
|
src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
|
|
@@ -1192,6 +1269,9 @@ static irqreturn_t s5p_jpeg_irq(int irq,
|
|
v4l2_m2m_buf_done(dst_buf, state);
|
|
v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx);
|
|
|
|
+ curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs);
|
|
+ spin_unlock(&jpeg->slock);
|
|
+
|
|
jpeg_clear_int(jpeg->regs);
|
|
|
|
return IRQ_HANDLED;
|
|
@@ -1215,6 +1295,7 @@ static int s5p_jpeg_probe(struct platfor
|
|
return -ENOMEM;
|
|
|
|
mutex_init(&jpeg->lock);
|
|
+ spin_lock_init(&jpeg->slock);
|
|
jpeg->dev = &pdev->dev;
|
|
|
|
/* memory-mapped registers */
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-jpeg/jpeg-core.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-jpeg/jpeg-core.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-jpeg/jpeg-core.h
|
|
@@ -14,6 +14,8 @@
|
|
#define JPEG_CORE_H_
|
|
|
|
#include <media/v4l2-device.h>
|
|
+#include <media/v4l2-fh.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
|
|
#define S5P_JPEG_M2M_NAME "s5p-jpeg"
|
|
|
|
@@ -47,6 +49,7 @@
|
|
/**
|
|
* struct s5p_jpeg - JPEG IP abstraction
|
|
* @lock: the mutex protecting this structure
|
|
+ * @slock: spinlock protecting the device contexts
|
|
* @v4l2_dev: v4l2 device for mem2mem mode
|
|
* @vfd_encoder: video device node for encoder mem2mem mode
|
|
* @vfd_decoder: video device node for decoder mem2mem mode
|
|
@@ -60,6 +63,7 @@
|
|
*/
|
|
struct s5p_jpeg {
|
|
struct mutex lock;
|
|
+ struct spinlock slock;
|
|
|
|
struct v4l2_device v4l2_dev;
|
|
struct video_device *vfd_encoder;
|
|
@@ -117,15 +121,20 @@ struct s5p_jpeg_q_data {
|
|
* @out_q: source (output) queue information
|
|
* @cap_fmt: destination (capture) queue queue information
|
|
* @hdr_parsed: set if header has been parsed during decompression
|
|
+ * @ctrl_handler: controls handler
|
|
*/
|
|
struct s5p_jpeg_ctx {
|
|
struct s5p_jpeg *jpeg;
|
|
unsigned int mode;
|
|
- unsigned int compr_quality;
|
|
+ unsigned short compr_quality;
|
|
+ unsigned short restart_interval;
|
|
+ unsigned short subsampling;
|
|
struct v4l2_m2m_ctx *m2m_ctx;
|
|
struct s5p_jpeg_q_data out_q;
|
|
struct s5p_jpeg_q_data cap_q;
|
|
+ struct v4l2_fh fh;
|
|
bool hdr_parsed;
|
|
+ struct v4l2_ctrl_handler ctrl_handler;
|
|
};
|
|
|
|
/**
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-jpeg/jpeg-hw.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-jpeg/jpeg-hw.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-jpeg/jpeg-hw.h
|
|
@@ -13,6 +13,7 @@
|
|
#define JPEG_HW_H_
|
|
|
|
#include <linux/io.h>
|
|
+#include <linux/videodev2.h>
|
|
|
|
#include "jpeg-hw.h"
|
|
#include "jpeg-regs.h"
|
|
@@ -25,8 +26,6 @@
|
|
#define S5P_JPEG_DECODE 1
|
|
#define S5P_JPEG_RAW_IN_565 0
|
|
#define S5P_JPEG_RAW_IN_422 1
|
|
-#define S5P_JPEG_SUBSAMPLING_422 0
|
|
-#define S5P_JPEG_SUBSAMPLING_420 1
|
|
#define S5P_JPEG_RAW_OUT_422 0
|
|
#define S5P_JPEG_RAW_OUT_420 1
|
|
|
|
@@ -91,21 +90,26 @@ static inline void jpeg_proc_mode(void _
|
|
writel(reg, regs + S5P_JPGMOD);
|
|
}
|
|
|
|
-static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned long mode)
|
|
+static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)
|
|
{
|
|
unsigned long reg, m;
|
|
|
|
- m = S5P_SUBSAMPLING_MODE_422;
|
|
- if (mode == S5P_JPEG_SUBSAMPLING_422)
|
|
- m = S5P_SUBSAMPLING_MODE_422;
|
|
- else if (mode == S5P_JPEG_SUBSAMPLING_420)
|
|
+ if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420)
|
|
m = S5P_SUBSAMPLING_MODE_420;
|
|
+ else
|
|
+ m = S5P_SUBSAMPLING_MODE_422;
|
|
+
|
|
reg = readl(regs + S5P_JPGMOD);
|
|
reg &= ~S5P_SUBSAMPLING_MODE_MASK;
|
|
reg |= m;
|
|
writel(reg, regs + S5P_JPGMOD);
|
|
}
|
|
|
|
+static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs)
|
|
+{
|
|
+ return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK;
|
|
+}
|
|
+
|
|
static inline void jpeg_dri(void __iomem *regs, unsigned int dri)
|
|
{
|
|
unsigned long reg;
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-fimc/fimc-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-fimc/fimc-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-fimc/fimc-core.c
|
|
@@ -1602,24 +1602,35 @@ static void fimc_clk_put(struct fimc_dev
|
|
{
|
|
int i;
|
|
for (i = 0; i < fimc->num_clocks; i++) {
|
|
- if (fimc->clock[i])
|
|
- clk_put(fimc->clock[i]);
|
|
+ if (IS_ERR_OR_NULL(fimc->clock[i]))
|
|
+ continue;
|
|
+ clk_unprepare(fimc->clock[i]);
|
|
+ clk_put(fimc->clock[i]);
|
|
+ fimc->clock[i] = NULL;
|
|
}
|
|
}
|
|
|
|
static int fimc_clk_get(struct fimc_dev *fimc)
|
|
{
|
|
- int i;
|
|
+ int i, ret;
|
|
+
|
|
for (i = 0; i < fimc->num_clocks; i++) {
|
|
fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);
|
|
- if (!IS_ERR_OR_NULL(fimc->clock[i]))
|
|
- continue;
|
|
- dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n",
|
|
- fimc_clocks[i]);
|
|
- return -ENXIO;
|
|
+ if (IS_ERR(fimc->clock[i]))
|
|
+ goto err;
|
|
+ ret = clk_prepare(fimc->clock[i]);
|
|
+ if (ret < 0) {
|
|
+ clk_put(fimc->clock[i]);
|
|
+ fimc->clock[i] = NULL;
|
|
+ goto err;
|
|
+ }
|
|
}
|
|
-
|
|
return 0;
|
|
+err:
|
|
+ fimc_clk_put(fimc);
|
|
+ dev_err(&fimc->pdev->dev, "failed to get clock: %s\n",
|
|
+ fimc_clocks[i]);
|
|
+ return -ENXIO;
|
|
}
|
|
|
|
static int fimc_m2m_suspend(struct fimc_dev *fimc)
|
|
@@ -1667,8 +1678,6 @@ static int fimc_probe(struct platform_de
|
|
struct s5p_platform_fimc *pdata;
|
|
int ret = 0;
|
|
|
|
- dev_dbg(&pdev->dev, "%s():\n", __func__);
|
|
-
|
|
drv_data = (struct samsung_fimc_driverdata *)
|
|
platform_get_device_id(pdev)->driver_data;
|
|
|
|
@@ -1678,7 +1687,7 @@ static int fimc_probe(struct platform_de
|
|
return -EINVAL;
|
|
}
|
|
|
|
- fimc = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL);
|
|
+ fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL);
|
|
if (!fimc)
|
|
return -ENOMEM;
|
|
|
|
@@ -1689,51 +1698,35 @@ static int fimc_probe(struct platform_de
|
|
pdata = pdev->dev.platform_data;
|
|
fimc->pdata = pdata;
|
|
|
|
-
|
|
init_waitqueue_head(&fimc->irq_queue);
|
|
spin_lock_init(&fimc->slock);
|
|
mutex_init(&fimc->lock);
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
- if (!res) {
|
|
- dev_err(&pdev->dev, "failed to find the registers\n");
|
|
- ret = -ENOENT;
|
|
- goto err_info;
|
|
- }
|
|
-
|
|
- fimc->regs_res = request_mem_region(res->start, resource_size(res),
|
|
- dev_name(&pdev->dev));
|
|
- if (!fimc->regs_res) {
|
|
- dev_err(&pdev->dev, "failed to obtain register region\n");
|
|
- ret = -ENOENT;
|
|
- goto err_info;
|
|
- }
|
|
-
|
|
- fimc->regs = ioremap(res->start, resource_size(res));
|
|
- if (!fimc->regs) {
|
|
- dev_err(&pdev->dev, "failed to map registers\n");
|
|
- ret = -ENXIO;
|
|
- goto err_req_region;
|
|
+ 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) {
|
|
- dev_err(&pdev->dev, "failed to get IRQ resource\n");
|
|
- ret = -ENXIO;
|
|
- goto err_regs_unmap;
|
|
+ if (res == NULL) {
|
|
+ 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)
|
|
- goto err_regs_unmap;
|
|
+ return ret;
|
|
clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
|
|
clk_enable(fimc->clock[CLK_BUS]);
|
|
|
|
platform_set_drvdata(pdev, fimc);
|
|
|
|
- ret = request_irq(fimc->irq, fimc_irq_handler, 0, pdev->name, fimc);
|
|
+ ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler,
|
|
+ 0, pdev->name, fimc);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
|
|
goto err_clk;
|
|
@@ -1742,7 +1735,7 @@ static int fimc_probe(struct platform_de
|
|
pm_runtime_enable(&pdev->dev);
|
|
ret = pm_runtime_get_sync(&pdev->dev);
|
|
if (ret < 0)
|
|
- goto err_irq;
|
|
+ goto err_clk;
|
|
/* Initialize contiguous memory allocator */
|
|
fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
|
|
if (IS_ERR(fimc->alloc_ctx)) {
|
|
@@ -1757,17 +1750,8 @@ static int fimc_probe(struct platform_de
|
|
|
|
err_pm:
|
|
pm_runtime_put(&pdev->dev);
|
|
-err_irq:
|
|
- free_irq(fimc->irq, fimc);
|
|
err_clk:
|
|
fimc_clk_put(fimc);
|
|
-err_regs_unmap:
|
|
- iounmap(fimc->regs);
|
|
-err_req_region:
|
|
- release_resource(fimc->regs_res);
|
|
- kfree(fimc->regs_res);
|
|
-err_info:
|
|
- kfree(fimc);
|
|
return ret;
|
|
}
|
|
|
|
@@ -1854,11 +1838,6 @@ static int __devexit fimc_remove(struct
|
|
|
|
clk_disable(fimc->clock[CLK_BUS]);
|
|
fimc_clk_put(fimc);
|
|
- free_irq(fimc->irq, fimc);
|
|
- iounmap(fimc->regs);
|
|
- release_resource(fimc->regs_res);
|
|
- kfree(fimc->regs_res);
|
|
- kfree(fimc);
|
|
|
|
dev_info(&pdev->dev, "driver unloaded\n");
|
|
return 0;
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-fimc/mipi-csis.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-fimc/mipi-csis.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-fimc/mipi-csis.c
|
|
@@ -1,8 +1,8 @@
|
|
/*
|
|
* Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
|
|
*
|
|
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
|
|
- * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
|
|
+ * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
|
|
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
|
|
*
|
|
* 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
|
|
@@ -100,7 +100,6 @@ enum {
|
|
* @pads: CSIS pads array
|
|
* @sd: v4l2_subdev associated with CSIS device instance
|
|
* @pdev: CSIS platform device
|
|
- * @regs_res: requested I/O register memory resource
|
|
* @regs: mmaped I/O registers memory
|
|
* @clock: CSIS clocks
|
|
* @irq: requested s5p-mipi-csis irq number
|
|
@@ -113,7 +112,6 @@ struct csis_state {
|
|
struct media_pad pads[CSIS_PADS_NUM];
|
|
struct v4l2_subdev sd;
|
|
struct platform_device *pdev;
|
|
- struct resource *regs_res;
|
|
void __iomem *regs;
|
|
struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
|
|
struct clk *clock[NUM_CSIS_CLOCKS];
|
|
@@ -258,26 +256,36 @@ static void s5pcsis_clk_put(struct csis_
|
|
{
|
|
int i;
|
|
|
|
- for (i = 0; i < NUM_CSIS_CLOCKS; i++)
|
|
- if (!IS_ERR_OR_NULL(state->clock[i]))
|
|
- clk_put(state->clock[i]);
|
|
+ for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
|
|
+ if (IS_ERR_OR_NULL(state->clock[i]))
|
|
+ continue;
|
|
+ clk_unprepare(state->clock[i]);
|
|
+ clk_put(state->clock[i]);
|
|
+ state->clock[i] = NULL;
|
|
+ }
|
|
}
|
|
|
|
static int s5pcsis_clk_get(struct csis_state *state)
|
|
{
|
|
struct device *dev = &state->pdev->dev;
|
|
- int i;
|
|
+ int i, ret;
|
|
|
|
for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
|
|
state->clock[i] = clk_get(dev, csi_clock_name[i]);
|
|
- if (IS_ERR(state->clock[i])) {
|
|
- s5pcsis_clk_put(state);
|
|
- dev_err(dev, "failed to get clock: %s\n",
|
|
- csi_clock_name[i]);
|
|
- return -ENXIO;
|
|
+ if (IS_ERR(state->clock[i]))
|
|
+ goto err;
|
|
+ ret = clk_prepare(state->clock[i]);
|
|
+ if (ret < 0) {
|
|
+ clk_put(state->clock[i]);
|
|
+ state->clock[i] = NULL;
|
|
+ goto err;
|
|
}
|
|
}
|
|
return 0;
|
|
+err:
|
|
+ s5pcsis_clk_put(state);
|
|
+ dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
|
|
+ return -ENXIO;
|
|
}
|
|
|
|
static int s5pcsis_s_power(struct v4l2_subdev *sd, int on)
|
|
@@ -480,12 +488,11 @@ static int __devinit s5pcsis_probe(struc
|
|
{
|
|
struct s5p_platform_mipi_csis *pdata;
|
|
struct resource *mem_res;
|
|
- struct resource *regs_res;
|
|
struct csis_state *state;
|
|
int ret = -ENOMEM;
|
|
int i;
|
|
|
|
- state = kzalloc(sizeof(*state), GFP_KERNEL);
|
|
+ state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
|
|
if (!state)
|
|
return -ENOMEM;
|
|
|
|
@@ -495,52 +502,27 @@ static int __devinit s5pcsis_probe(struc
|
|
pdata = pdev->dev.platform_data;
|
|
if (pdata == NULL || pdata->phy_enable == NULL) {
|
|
dev_err(&pdev->dev, "Platform data not fully specified\n");
|
|
- goto e_free;
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) ||
|
|
pdata->lanes > CSIS0_MAX_LANES) {
|
|
- ret = -EINVAL;
|
|
dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n",
|
|
pdata->lanes);
|
|
- goto e_free;
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
- if (!mem_res) {
|
|
- dev_err(&pdev->dev, "Failed to get IO memory region\n");
|
|
- goto e_free;
|
|
- }
|
|
-
|
|
- regs_res = request_mem_region(mem_res->start, resource_size(mem_res),
|
|
- pdev->name);
|
|
- if (!regs_res) {
|
|
- dev_err(&pdev->dev, "Failed to request IO memory region\n");
|
|
- goto e_free;
|
|
- }
|
|
- state->regs_res = regs_res;
|
|
-
|
|
- state->regs = ioremap(mem_res->start, resource_size(mem_res));
|
|
- if (!state->regs) {
|
|
- dev_err(&pdev->dev, "Failed to remap IO region\n");
|
|
- goto e_reqmem;
|
|
+ state->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
|
|
+ if (state->regs == NULL) {
|
|
+ dev_err(&pdev->dev, "Failed to request and remap io memory\n");
|
|
+ return -ENXIO;
|
|
}
|
|
|
|
- ret = s5pcsis_clk_get(state);
|
|
- if (ret)
|
|
- goto e_unmap;
|
|
-
|
|
- clk_enable(state->clock[CSIS_CLK_MUX]);
|
|
- if (pdata->clk_rate)
|
|
- clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
|
|
- else
|
|
- dev_WARN(&pdev->dev, "No clock frequency specified!\n");
|
|
-
|
|
state->irq = platform_get_irq(pdev, 0);
|
|
if (state->irq < 0) {
|
|
- ret = state->irq;
|
|
dev_err(&pdev->dev, "Failed to get irq\n");
|
|
- goto e_clkput;
|
|
+ return state->irq;
|
|
}
|
|
|
|
for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
|
|
@@ -549,12 +531,22 @@ static int __devinit s5pcsis_probe(struc
|
|
ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
|
|
state->supplies);
|
|
if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = s5pcsis_clk_get(state);
|
|
+ if (ret)
|
|
goto e_clkput;
|
|
|
|
- ret = request_irq(state->irq, s5pcsis_irq_handler, 0,
|
|
- dev_name(&pdev->dev), state);
|
|
+ clk_enable(state->clock[CSIS_CLK_MUX]);
|
|
+ if (pdata->clk_rate)
|
|
+ clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
|
|
+ else
|
|
+ dev_WARN(&pdev->dev, "No clock frequency specified!\n");
|
|
+
|
|
+ ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler,
|
|
+ 0, dev_name(&pdev->dev), state);
|
|
if (ret) {
|
|
- dev_err(&pdev->dev, "request_irq failed\n");
|
|
+ dev_err(&pdev->dev, "Interrupt request failed\n");
|
|
goto e_regput;
|
|
}
|
|
|
|
@@ -573,7 +565,7 @@ static int __devinit s5pcsis_probe(struc
|
|
ret = media_entity_init(&state->sd.entity,
|
|
CSIS_PADS_NUM, state->pads, 0);
|
|
if (ret < 0)
|
|
- goto e_irqfree;
|
|
+ goto e_clkput;
|
|
|
|
/* This allows to retrieve the platform device id by the host driver */
|
|
v4l2_set_subdevdata(&state->sd, pdev);
|
|
@@ -582,22 +574,13 @@ static int __devinit s5pcsis_probe(struc
|
|
platform_set_drvdata(pdev, &state->sd);
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
-
|
|
return 0;
|
|
|
|
-e_irqfree:
|
|
- free_irq(state->irq, state);
|
|
e_regput:
|
|
regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
|
|
e_clkput:
|
|
clk_disable(state->clock[CSIS_CLK_MUX]);
|
|
s5pcsis_clk_put(state);
|
|
-e_unmap:
|
|
- iounmap(state->regs);
|
|
-e_reqmem:
|
|
- release_mem_region(regs_res->start, resource_size(regs_res));
|
|
-e_free:
|
|
- kfree(state);
|
|
return ret;
|
|
}
|
|
|
|
@@ -699,21 +682,15 @@ static int __devexit s5pcsis_remove(stru
|
|
{
|
|
struct v4l2_subdev *sd = platform_get_drvdata(pdev);
|
|
struct csis_state *state = sd_to_csis_state(sd);
|
|
- struct resource *res = state->regs_res;
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
- s5pcsis_suspend(&pdev->dev);
|
|
+ s5pcsis_pm_suspend(&pdev->dev, false);
|
|
clk_disable(state->clock[CSIS_CLK_MUX]);
|
|
pm_runtime_set_suspended(&pdev->dev);
|
|
-
|
|
s5pcsis_clk_put(state);
|
|
regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
|
|
|
|
media_entity_cleanup(&state->sd.entity);
|
|
- free_irq(state->irq, state);
|
|
- iounmap(state->regs);
|
|
- release_mem_region(res->start, resource_size(res));
|
|
- kfree(state);
|
|
|
|
return 0;
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-fimc/fimc-core.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-fimc/fimc-core.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-fimc/fimc-core.h
|
|
@@ -434,7 +434,6 @@ struct fimc_ctx;
|
|
* @num_clocks: the number of clocks managed by this device instance
|
|
* @clock: clocks required for FIMC operation
|
|
* @regs: the mapped hardware registers
|
|
- * @regs_res: the resource claimed for IO registers
|
|
* @irq: FIMC interrupt number
|
|
* @irq_queue: interrupt handler waitqueue
|
|
* @v4l2_dev: root v4l2_device
|
|
@@ -454,7 +453,6 @@ struct fimc_dev {
|
|
u16 num_clocks;
|
|
struct clk *clock[MAX_FIMC_CLOCKS];
|
|
void __iomem *regs;
|
|
- struct resource *regs_res;
|
|
int irq;
|
|
wait_queue_head_t irq_queue;
|
|
struct v4l2_device *v4l2_dev;
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-fimc/fimc-capture.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-fimc/fimc-capture.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-fimc/fimc-capture.c
|
|
@@ -1019,52 +1019,117 @@ static int fimc_cap_dqbuf(struct file *f
|
|
return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK);
|
|
}
|
|
|
|
-static int fimc_cap_cropcap(struct file *file, void *fh,
|
|
- struct v4l2_cropcap *cr)
|
|
+static int fimc_cap_create_bufs(struct file *file, void *priv,
|
|
+ struct v4l2_create_buffers *create)
|
|
{
|
|
struct fimc_dev *fimc = video_drvdata(file);
|
|
- struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
|
|
|
|
- if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
|
|
- return -EINVAL;
|
|
+ return vb2_create_bufs(&fimc->vid_cap.vbq, create);
|
|
+}
|
|
|
|
- cr->bounds.left = 0;
|
|
- cr->bounds.top = 0;
|
|
- cr->bounds.width = f->o_width;
|
|
- cr->bounds.height = f->o_height;
|
|
- cr->defrect = cr->bounds;
|
|
+static int fimc_cap_prepare_buf(struct file *file, void *priv,
|
|
+ struct v4l2_buffer *b)
|
|
+{
|
|
+ struct fimc_dev *fimc = video_drvdata(file);
|
|
|
|
- return 0;
|
|
+ return vb2_prepare_buf(&fimc->vid_cap.vbq, b);
|
|
}
|
|
|
|
-static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
|
|
+static int fimc_cap_g_selection(struct file *file, void *fh,
|
|
+ struct v4l2_selection *s)
|
|
{
|
|
struct fimc_dev *fimc = video_drvdata(file);
|
|
- struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
|
|
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
|
|
+ struct fimc_frame *f = &ctx->s_frame;
|
|
|
|
- cr->c.left = f->offs_h;
|
|
- cr->c.top = f->offs_v;
|
|
- cr->c.width = f->width;
|
|
- cr->c.height = f->height;
|
|
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
|
|
+ return -EINVAL;
|
|
|
|
- return 0;
|
|
+ switch (s->target) {
|
|
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
|
|
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
|
|
+ f = &ctx->d_frame;
|
|
+ case V4L2_SEL_TGT_CROP_BOUNDS:
|
|
+ case V4L2_SEL_TGT_CROP_DEFAULT:
|
|
+ s->r.left = 0;
|
|
+ s->r.top = 0;
|
|
+ s->r.width = f->o_width;
|
|
+ s->r.height = f->o_height;
|
|
+ return 0;
|
|
+
|
|
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
|
|
+ f = &ctx->d_frame;
|
|
+ case V4L2_SEL_TGT_CROP_ACTIVE:
|
|
+ s->r.left = f->offs_h;
|
|
+ s->r.top = f->offs_v;
|
|
+ s->r.width = f->width;
|
|
+ s->r.height = f->height;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
|
|
+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_cap_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
|
|
+static int fimc_cap_s_selection(struct file *file, void *fh,
|
|
+ struct v4l2_selection *s)
|
|
{
|
|
struct fimc_dev *fimc = video_drvdata(file);
|
|
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
|
|
- struct fimc_frame *ff;
|
|
+ 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:
|
|
+ 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:
|
|
+ f = &ctx->s_frame;
|
|
+ pad = FIMC_SD_PAD_SINK;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
- fimc_capture_try_crop(ctx, &cr->c, FIMC_SD_PAD_SINK);
|
|
- ff = &ctx->s_frame;
|
|
+ fimc_capture_try_crop(ctx, &rect, pad);
|
|
|
|
+ if (s->flags & V4L2_SEL_FLAG_LE &&
|
|
+ !enclosed_rectangle(&rect, &s->r))
|
|
+ return -ERANGE;
|
|
+
|
|
+ if (s->flags & V4L2_SEL_FLAG_GE &&
|
|
+ !enclosed_rectangle(&s->r, &rect))
|
|
+ return -ERANGE;
|
|
+
|
|
+ s->r = rect;
|
|
spin_lock_irqsave(&fimc->slock, flags);
|
|
- set_frame_crop(ff, cr->c.left, cr->c.top, cr->c.width, cr->c.height);
|
|
- set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
|
|
+ set_frame_crop(f, s->r.left, s->r.top, s->r.width,
|
|
+ s->r.height);
|
|
spin_unlock_irqrestore(&fimc->slock, flags);
|
|
|
|
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
|
|
return 0;
|
|
}
|
|
|
|
@@ -1082,12 +1147,14 @@ static const struct v4l2_ioctl_ops fimc_
|
|
.vidioc_qbuf = fimc_cap_qbuf,
|
|
.vidioc_dqbuf = fimc_cap_dqbuf,
|
|
|
|
+ .vidioc_prepare_buf = fimc_cap_prepare_buf,
|
|
+ .vidioc_create_bufs = fimc_cap_create_bufs,
|
|
+
|
|
.vidioc_streamon = fimc_cap_streamon,
|
|
.vidioc_streamoff = fimc_cap_streamoff,
|
|
|
|
- .vidioc_g_crop = fimc_cap_g_crop,
|
|
- .vidioc_s_crop = fimc_cap_s_crop,
|
|
- .vidioc_cropcap = fimc_cap_cropcap,
|
|
+ .vidioc_g_selection = fimc_cap_g_selection,
|
|
+ .vidioc_s_selection = fimc_cap_s_selection,
|
|
|
|
.vidioc_enum_input = fimc_cap_enum_input,
|
|
.vidioc_s_input = fimc_cap_s_input,
|
|
Index: linux-3.3.x86_64/drivers/media/video/videobuf2-vmalloc.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/videobuf2-vmalloc.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/videobuf2-vmalloc.c
|
|
@@ -10,6 +10,7 @@
|
|
* the Free Software Foundation.
|
|
*/
|
|
|
|
+#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/sched.h>
|
|
@@ -22,6 +23,7 @@
|
|
struct vb2_vmalloc_buf {
|
|
void *vaddr;
|
|
struct page **pages;
|
|
+ struct vm_area_struct *vma;
|
|
int write;
|
|
unsigned long size;
|
|
unsigned int n_pages;
|
|
@@ -71,6 +73,8 @@ static void *vb2_vmalloc_get_userptr(voi
|
|
struct vb2_vmalloc_buf *buf;
|
|
unsigned long first, last;
|
|
int n_pages, offset;
|
|
+ struct vm_area_struct *vma;
|
|
+ dma_addr_t physp;
|
|
|
|
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
|
|
if (!buf)
|
|
@@ -80,23 +84,37 @@ static void *vb2_vmalloc_get_userptr(voi
|
|
offset = vaddr & ~PAGE_MASK;
|
|
buf->size = size;
|
|
|
|
- first = vaddr >> PAGE_SHIFT;
|
|
- last = (vaddr + size - 1) >> PAGE_SHIFT;
|
|
- buf->n_pages = last - first + 1;
|
|
- buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), GFP_KERNEL);
|
|
- if (!buf->pages)
|
|
- goto fail_pages_array_alloc;
|
|
-
|
|
- /* current->mm->mmap_sem is taken by videobuf2 core */
|
|
- n_pages = get_user_pages(current, current->mm, vaddr & PAGE_MASK,
|
|
- buf->n_pages, write, 1, /* force */
|
|
- buf->pages, NULL);
|
|
- if (n_pages != buf->n_pages)
|
|
- goto fail_get_user_pages;
|
|
-
|
|
- buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, PAGE_KERNEL);
|
|
- if (!buf->vaddr)
|
|
- goto fail_get_user_pages;
|
|
+
|
|
+ vma = find_vma(current->mm, vaddr);
|
|
+ if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
|
|
+ if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
|
|
+ goto fail_pages_array_alloc;
|
|
+ buf->vma = vma;
|
|
+ buf->vaddr = ioremap_nocache(physp, size);
|
|
+ if (!buf->vaddr)
|
|
+ goto fail_pages_array_alloc;
|
|
+ } else {
|
|
+ first = vaddr >> PAGE_SHIFT;
|
|
+ last = (vaddr + size - 1) >> PAGE_SHIFT;
|
|
+ buf->n_pages = last - first + 1;
|
|
+ buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
|
|
+ GFP_KERNEL);
|
|
+ if (!buf->pages)
|
|
+ goto fail_pages_array_alloc;
|
|
+
|
|
+ /* current->mm->mmap_sem is taken by videobuf2 core */
|
|
+ n_pages = get_user_pages(current, current->mm,
|
|
+ vaddr & PAGE_MASK, buf->n_pages,
|
|
+ write, 1, /* force */
|
|
+ buf->pages, NULL);
|
|
+ if (n_pages != buf->n_pages)
|
|
+ goto fail_get_user_pages;
|
|
+
|
|
+ buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
|
|
+ PAGE_KERNEL);
|
|
+ if (!buf->vaddr)
|
|
+ goto fail_get_user_pages;
|
|
+ }
|
|
|
|
buf->vaddr += offset;
|
|
return buf;
|
|
@@ -120,14 +138,20 @@ static void vb2_vmalloc_put_userptr(void
|
|
unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
|
|
unsigned int i;
|
|
|
|
- if (vaddr)
|
|
- vm_unmap_ram((void *)vaddr, buf->n_pages);
|
|
- for (i = 0; i < buf->n_pages; ++i) {
|
|
- if (buf->write)
|
|
- set_page_dirty_lock(buf->pages[i]);
|
|
- put_page(buf->pages[i]);
|
|
+ if (buf->pages) {
|
|
+ if (vaddr)
|
|
+ vm_unmap_ram((void *)vaddr, buf->n_pages);
|
|
+ for (i = 0; i < buf->n_pages; ++i) {
|
|
+ if (buf->write)
|
|
+ set_page_dirty_lock(buf->pages[i]);
|
|
+ put_page(buf->pages[i]);
|
|
+ }
|
|
+ kfree(buf->pages);
|
|
+ } else {
|
|
+ if (buf->vma)
|
|
+ vb2_put_vma(buf->vma);
|
|
+ iounmap(buf->vaddr);
|
|
}
|
|
- kfree(buf->pages);
|
|
kfree(buf);
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-g2d/g2d.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-g2d/g2d.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-g2d/g2d.c
|
|
@@ -178,6 +178,9 @@ static int g2d_s_ctrl(struct v4l2_ctrl *
|
|
{
|
|
struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx,
|
|
ctrl_handler);
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_COLORFX:
|
|
if (ctrl->val == V4L2_COLORFX_NEGATIVE)
|
|
@@ -185,10 +188,13 @@ static int g2d_s_ctrl(struct v4l2_ctrl *
|
|
else
|
|
ctx->rop = ROP4_COPY;
|
|
break;
|
|
- default:
|
|
- v4l2_err(&ctx->dev->v4l2_dev, "unknown control\n");
|
|
- return -EINVAL;
|
|
+
|
|
+ case V4L2_CID_HFLIP:
|
|
+ ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1);
|
|
+ break;
|
|
+
|
|
}
|
|
+ spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
|
|
return 0;
|
|
}
|
|
|
|
@@ -200,11 +206,13 @@ int g2d_setup_ctrls(struct g2d_ctx *ctx)
|
|
{
|
|
struct g2d_dev *dev = ctx->dev;
|
|
|
|
- v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1);
|
|
- if (ctx->ctrl_handler.error) {
|
|
- v4l2_err(&dev->v4l2_dev, "v4l2_ctrl_handler_init failed\n");
|
|
- return ctx->ctrl_handler.error;
|
|
- }
|
|
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
|
|
+
|
|
+ ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
|
|
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
|
|
+
|
|
+ ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
|
|
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
|
|
|
|
v4l2_ctrl_new_std_menu(
|
|
&ctx->ctrl_handler,
|
|
@@ -215,10 +223,14 @@ int g2d_setup_ctrls(struct g2d_ctx *ctx)
|
|
V4L2_COLORFX_NONE);
|
|
|
|
if (ctx->ctrl_handler.error) {
|
|
- v4l2_err(&dev->v4l2_dev, "v4l2_ctrl_handler_init failed\n");
|
|
- return ctx->ctrl_handler.error;
|
|
+ int err = ctx->ctrl_handler.error;
|
|
+ v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n");
|
|
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
|
|
+ return err;
|
|
}
|
|
|
|
+ v4l2_ctrl_cluster(2, &ctx->ctrl_hflip);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -547,6 +559,7 @@ static void device_run(void *prv)
|
|
struct g2d_ctx *ctx = prv;
|
|
struct g2d_dev *dev = ctx->dev;
|
|
struct vb2_buffer *src, *dst;
|
|
+ unsigned long flags;
|
|
u32 cmd = 0;
|
|
|
|
dev->curr = ctx;
|
|
@@ -557,6 +570,8 @@ static void device_run(void *prv)
|
|
clk_enable(dev->gate);
|
|
g2d_reset(dev);
|
|
|
|
+ spin_lock_irqsave(&dev->ctrl_lock, flags);
|
|
+
|
|
g2d_set_src_size(dev, &ctx->in);
|
|
g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0));
|
|
|
|
@@ -564,11 +579,15 @@ static void device_run(void *prv)
|
|
g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0));
|
|
|
|
g2d_set_rop4(dev, ctx->rop);
|
|
+ g2d_set_flip(dev, ctx->flip);
|
|
+
|
|
if (ctx->in.c_width != ctx->out.c_width ||
|
|
ctx->in.c_height != ctx->out.c_height)
|
|
cmd |= g2d_cmd_stretch(1);
|
|
g2d_set_cmd(dev, cmd);
|
|
g2d_start(dev);
|
|
+
|
|
+ spin_unlock_irqrestore(&dev->ctrl_lock, flags);
|
|
}
|
|
|
|
static irqreturn_t g2d_isr(int irq, void *prv)
|
|
@@ -658,7 +677,7 @@ static int g2d_probe(struct platform_dev
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
- spin_lock_init(&dev->irqlock);
|
|
+ spin_lock_init(&dev->ctrl_lock);
|
|
mutex_init(&dev->mutex);
|
|
atomic_set(&dev->num_inst, 0);
|
|
init_waitqueue_head(&dev->irq_queue);
|
|
@@ -693,18 +712,30 @@ static int g2d_probe(struct platform_dev
|
|
goto unmap_regs;
|
|
}
|
|
|
|
+ ret = clk_prepare(dev->clk);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "failed to prepare g2d clock\n");
|
|
+ goto put_clk;
|
|
+ }
|
|
+
|
|
dev->gate = clk_get(&pdev->dev, "fimg2d");
|
|
if (IS_ERR_OR_NULL(dev->gate)) {
|
|
dev_err(&pdev->dev, "failed to get g2d clock gate\n");
|
|
ret = -ENXIO;
|
|
- goto put_clk;
|
|
+ goto unprep_clk;
|
|
+ }
|
|
+
|
|
+ ret = clk_prepare(dev->gate);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "failed to prepare g2d clock gate\n");
|
|
+ goto put_clk_gate;
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
if (!res) {
|
|
dev_err(&pdev->dev, "failed to find IRQ\n");
|
|
ret = -ENXIO;
|
|
- goto put_clk_gate;
|
|
+ goto unprep_clk_gate;
|
|
}
|
|
|
|
dev->irq = res->start;
|
|
@@ -764,8 +795,12 @@ 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:
|
|
clk_put(dev->gate);
|
|
+unprep_clk:
|
|
+ clk_unprepare(dev->clk);
|
|
put_clk:
|
|
clk_put(dev->clk);
|
|
unmap_regs:
|
|
@@ -787,7 +822,9 @@ static int g2d_remove(struct platform_de
|
|
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);
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
|
|
@@ -41,15 +41,29 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *
|
|
pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME);
|
|
if (IS_ERR(pm->clock_gate)) {
|
|
mfc_err("Failed to get clock-gating control\n");
|
|
- ret = -ENOENT;
|
|
+ ret = PTR_ERR(pm->clock_gate);
|
|
goto err_g_ip_clk;
|
|
}
|
|
+
|
|
+ ret = clk_prepare(pm->clock_gate);
|
|
+ if (ret) {
|
|
+ mfc_err("Failed to preapre clock-gating control\n");
|
|
+ goto err_p_ip_clk;
|
|
+ }
|
|
+
|
|
pm->clock = clk_get(&dev->plat_dev->dev, MFC_CLKNAME);
|
|
if (IS_ERR(pm->clock)) {
|
|
mfc_err("Failed to get MFC clock\n");
|
|
- ret = -ENOENT;
|
|
+ ret = PTR_ERR(pm->clock);
|
|
goto err_g_ip_clk_2;
|
|
}
|
|
+
|
|
+ ret = clk_prepare(pm->clock);
|
|
+ if (ret) {
|
|
+ mfc_err("Failed to prepare MFC clock\n");
|
|
+ goto err_p_ip_clk_2;
|
|
+ }
|
|
+
|
|
atomic_set(&pm->power, 0);
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
pm->device = &dev->plat_dev->dev;
|
|
@@ -59,7 +73,11 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *
|
|
atomic_set(&clk_ref, 0);
|
|
#endif
|
|
return 0;
|
|
+err_p_ip_clk_2:
|
|
+ clk_put(pm->clock);
|
|
err_g_ip_clk_2:
|
|
+ clk_unprepare(pm->clock_gate);
|
|
+err_p_ip_clk:
|
|
clk_put(pm->clock_gate);
|
|
err_g_ip_clk:
|
|
return ret;
|
|
@@ -67,7 +85,9 @@ err_g_ip_clk:
|
|
|
|
void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
|
|
{
|
|
+ clk_unprepare(pm->clock_gate);
|
|
clk_put(pm->clock_gate);
|
|
+ clk_unprepare(pm->clock);
|
|
clk_put(pm->clock);
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
pm_runtime_disable(pm->device);
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-g2d/g2d-hw.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-g2d/g2d-hw.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-g2d/g2d-hw.c
|
|
@@ -77,6 +77,11 @@ void g2d_set_rop4(struct g2d_dev *d, u32
|
|
w(r, ROP4_REG);
|
|
}
|
|
|
|
+void g2d_set_flip(struct g2d_dev *d, u32 r)
|
|
+{
|
|
+ w(r, SRC_MSK_DIRECT_REG);
|
|
+}
|
|
+
|
|
u32 g2d_cmd_stretch(u32 e)
|
|
{
|
|
e &= 1;
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-g2d/g2d.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-g2d/g2d.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-g2d/g2d.h
|
|
@@ -20,7 +20,7 @@ struct g2d_dev {
|
|
struct v4l2_m2m_dev *m2m_dev;
|
|
struct video_device *vfd;
|
|
struct mutex mutex;
|
|
- spinlock_t irqlock;
|
|
+ spinlock_t ctrl_lock;
|
|
atomic_t num_inst;
|
|
struct vb2_alloc_ctx *alloc_ctx;
|
|
struct resource *res_regs;
|
|
@@ -57,8 +57,11 @@ struct g2d_ctx {
|
|
struct v4l2_m2m_ctx *m2m_ctx;
|
|
struct g2d_frame in;
|
|
struct g2d_frame out;
|
|
+ struct v4l2_ctrl *ctrl_hflip;
|
|
+ struct v4l2_ctrl *ctrl_vflip;
|
|
struct v4l2_ctrl_handler ctrl_handler;
|
|
u32 rop;
|
|
+ u32 flip;
|
|
};
|
|
|
|
struct g2d_fmt {
|
|
@@ -77,6 +80,7 @@ void g2d_set_dst_addr(struct g2d_dev *d,
|
|
void g2d_start(struct g2d_dev *d);
|
|
void g2d_clear_int(struct g2d_dev *d);
|
|
void g2d_set_rop4(struct g2d_dev *d, u32 r);
|
|
+void g2d_set_flip(struct g2d_dev *d, u32 r);
|
|
u32 g2d_cmd_stretch(u32 e);
|
|
void g2d_set_cmd(struct g2d_dev *d, u32 c);
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5k6aa.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5k6aa.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5k6aa.c
|
|
@@ -1582,8 +1582,8 @@ static int s5k6aa_probe(struct i2c_clien
|
|
s5k6aa->inv_vflip = pdata->vert_flip;
|
|
|
|
sd = &s5k6aa->sd;
|
|
- strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
|
|
v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
|
|
+ strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
|
|
|
|
sd->internal_ops = &s5k6aa_subdev_internal_ops;
|
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
@@ -1663,18 +1663,7 @@ static struct i2c_driver s5k6aa_i2c_driv
|
|
.id_table = s5k6aa_id,
|
|
};
|
|
|
|
-static int __init s5k6aa_init(void)
|
|
-{
|
|
- return i2c_add_driver(&s5k6aa_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit s5k6aa_exit(void)
|
|
-{
|
|
- i2c_del_driver(&s5k6aa_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(s5k6aa_init);
|
|
-module_exit(s5k6aa_exit);
|
|
+module_i2c_driver(s5k6aa_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
|
|
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/noon010pc30.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/noon010pc30.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/noon010pc30.c
|
|
@@ -725,8 +725,8 @@ static int noon010_probe(struct i2c_clie
|
|
|
|
mutex_init(&info->lock);
|
|
sd = &info->sd;
|
|
- strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
|
|
v4l2_i2c_subdev_init(sd, client, &noon010_ops);
|
|
+ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
|
|
|
|
sd->internal_ops = &noon010_subdev_internal_ops;
|
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
@@ -844,18 +844,7 @@ static struct i2c_driver noon010_i2c_dri
|
|
.id_table = noon010_id,
|
|
};
|
|
|
|
-static int __init noon010_init(void)
|
|
-{
|
|
- return i2c_add_driver(&noon010_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit noon010_exit(void)
|
|
-{
|
|
- i2c_del_driver(&noon010_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(noon010_init);
|
|
-module_exit(noon010_exit);
|
|
+module_i2c_driver(noon010_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver");
|
|
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/m5mols/m5mols_core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/m5mols/m5mols_core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/m5mols/m5mols_core.c
|
|
@@ -982,8 +982,8 @@ static int __devinit m5mols_probe(struct
|
|
}
|
|
|
|
sd = &info->sd;
|
|
- strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
|
|
v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
|
|
+ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
|
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
|
|
sd->internal_ops = &m5mols_subdev_internal_ops;
|
|
@@ -1057,18 +1057,7 @@ static struct i2c_driver m5mols_i2c_driv
|
|
.id_table = m5mols_id,
|
|
};
|
|
|
|
-static int __init m5mols_mod_init(void)
|
|
-{
|
|
- return i2c_add_driver(&m5mols_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit m5mols_mod_exit(void)
|
|
-{
|
|
- i2c_del_driver(&m5mols_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(m5mols_mod_init);
|
|
-module_exit(m5mols_mod_exit);
|
|
+module_i2c_driver(m5mols_i2c_driver);
|
|
|
|
MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
|
|
MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
|
|
Index: linux-3.3.x86_64/Documentation/video4linux/gspca.txt
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/Documentation/video4linux/gspca.txt
|
|
+++ linux-3.3.x86_64/Documentation/video4linux/gspca.txt
|
|
@@ -217,6 +217,7 @@ ov534_9 06f8:3003 Hercules Dualpix HD W
|
|
sonixj 06f8:3004 Hercules Classic Silver
|
|
sonixj 06f8:3008 Hercules Deluxe Optical Glass
|
|
pac7302 06f8:3009 Hercules Classic Link
|
|
+pac7302 06f8:301b Hercules Link
|
|
nw80x 0728:d001 AVerMedia Camguard
|
|
spca508 0733:0110 ViewQuest VQ110
|
|
spca501 0733:0401 Intel Create and Share
|
|
Index: linux-3.3.x86_64/drivers/media/video/gspca/pac7302.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/gspca/pac7302.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/gspca/pac7302.c
|
|
@@ -1,8 +1,8 @@
|
|
/*
|
|
- * Pixart PAC7302 library
|
|
- * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
|
|
+ * Pixart PAC7302 driver
|
|
*
|
|
- * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
|
|
+ * Copyright (C) 2008-2012 Jean-Francois Moine <http://moinejf.free.fr>
|
|
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
|
|
*
|
|
* Separated from Pixart PAC7311 library by Márton Németh
|
|
* Camera button input handling by Márton Németh <nm127@freemail.hu>
|
|
@@ -63,67 +63,61 @@
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
-#define MODULE_NAME "pac7302"
|
|
-
|
|
#include <linux/input.h>
|
|
#include <media/v4l2-chip-ident.h>
|
|
#include "gspca.h"
|
|
+/* Include pac common sof detection functions */
|
|
+#include "pac_common.h"
|
|
|
|
-MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
|
|
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
|
|
+ "Thomas Kaiser thomas@kaiser-linux.li");
|
|
MODULE_DESCRIPTION("Pixart PAC7302");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
+enum e_ctrl {
|
|
+ BRIGHTNESS,
|
|
+ CONTRAST,
|
|
+ COLORS,
|
|
+ WHITE_BALANCE,
|
|
+ RED_BALANCE,
|
|
+ BLUE_BALANCE,
|
|
+ GAIN,
|
|
+ AUTOGAIN,
|
|
+ EXPOSURE,
|
|
+ VFLIP,
|
|
+ HFLIP,
|
|
+ NCTRLS /* number of controls */
|
|
+};
|
|
+
|
|
/* specific webcam descriptor for pac7302 */
|
|
struct sd {
|
|
struct gspca_dev gspca_dev; /* !! must be the first item */
|
|
|
|
- unsigned char brightness;
|
|
- unsigned char contrast;
|
|
- unsigned char colors;
|
|
- unsigned char white_balance;
|
|
- unsigned char red_balance;
|
|
- unsigned char blue_balance;
|
|
- unsigned char gain;
|
|
- unsigned char autogain;
|
|
- unsigned short exposure;
|
|
- __u8 hflip;
|
|
- __u8 vflip;
|
|
+ struct gspca_ctrl ctrls[NCTRLS];
|
|
+
|
|
u8 flags;
|
|
#define FL_HFLIP 0x01 /* mirrored by default */
|
|
#define FL_VFLIP 0x02 /* vertical flipped by default */
|
|
|
|
u8 sof_read;
|
|
- u8 autogain_ignore_frames;
|
|
+ s8 autogain_ignore_frames;
|
|
|
|
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_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
|
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
|
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
|
|
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
|
|
-static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val);
|
|
-static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val);
|
|
-static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val);
|
|
-static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val);
|
|
-static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val);
|
|
-static int sd_getbluebalance(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 setbrightcont(struct gspca_dev *gspca_dev);
|
|
+static void setcolors(struct gspca_dev *gspca_dev);
|
|
+static void setwhitebalance(struct gspca_dev *gspca_dev);
|
|
+static void setredbalance(struct gspca_dev *gspca_dev);
|
|
+static void setbluebalance(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[] = {
|
|
- {
|
|
+[BRIGHTNESS] = {
|
|
{
|
|
.id = V4L2_CID_BRIGHTNESS,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -132,13 +126,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
#define BRIGHTNESS_MAX 0x20
|
|
.maximum = BRIGHTNESS_MAX,
|
|
.step = 1,
|
|
-#define BRIGHTNESS_DEF 0x10
|
|
- .default_value = BRIGHTNESS_DEF,
|
|
+ .default_value = 0x10,
|
|
},
|
|
- .set = sd_setbrightness,
|
|
- .get = sd_getbrightness,
|
|
+ .set_control = setbrightcont
|
|
},
|
|
- {
|
|
+[CONTRAST] = {
|
|
{
|
|
.id = V4L2_CID_CONTRAST,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -147,13 +139,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
#define CONTRAST_MAX 255
|
|
.maximum = CONTRAST_MAX,
|
|
.step = 1,
|
|
-#define CONTRAST_DEF 127
|
|
- .default_value = CONTRAST_DEF,
|
|
+ .default_value = 127,
|
|
},
|
|
- .set = sd_setcontrast,
|
|
- .get = sd_getcontrast,
|
|
+ .set_control = setbrightcont
|
|
},
|
|
- {
|
|
+[COLORS] = {
|
|
{
|
|
.id = V4L2_CID_SATURATION,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -162,13 +152,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
#define COLOR_MAX 255
|
|
.maximum = COLOR_MAX,
|
|
.step = 1,
|
|
-#define COLOR_DEF 127
|
|
- .default_value = COLOR_DEF,
|
|
+ .default_value = 127
|
|
},
|
|
- .set = sd_setcolors,
|
|
- .get = sd_getcolors,
|
|
+ .set_control = setcolors
|
|
},
|
|
- {
|
|
+[WHITE_BALANCE] = {
|
|
{
|
|
.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -176,13 +164,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 255,
|
|
.step = 1,
|
|
-#define WHITEBALANCE_DEF 4
|
|
- .default_value = WHITEBALANCE_DEF,
|
|
+ .default_value = 4,
|
|
},
|
|
- .set = sd_setwhitebalance,
|
|
- .get = sd_getwhitebalance,
|
|
+ .set_control = setwhitebalance
|
|
},
|
|
- {
|
|
+[RED_BALANCE] = {
|
|
{
|
|
.id = V4L2_CID_RED_BALANCE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -190,13 +176,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 3,
|
|
.step = 1,
|
|
-#define REDBALANCE_DEF 1
|
|
- .default_value = REDBALANCE_DEF,
|
|
+ .default_value = 1,
|
|
},
|
|
- .set = sd_setredbalance,
|
|
- .get = sd_getredbalance,
|
|
+ .set_control = setredbalance
|
|
},
|
|
- {
|
|
+[BLUE_BALANCE] = {
|
|
{
|
|
.id = V4L2_CID_BLUE_BALANCE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -204,29 +188,25 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 3,
|
|
.step = 1,
|
|
-#define BLUEBALANCE_DEF 1
|
|
- .default_value = BLUEBALANCE_DEF,
|
|
+ .default_value = 1,
|
|
},
|
|
- .set = sd_setbluebalance,
|
|
- .get = sd_getbluebalance,
|
|
+ .set_control = setbluebalance
|
|
},
|
|
- {
|
|
+[GAIN] = {
|
|
{
|
|
.id = V4L2_CID_GAIN,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.name = "Gain",
|
|
.minimum = 0,
|
|
-#define GAIN_MAX 255
|
|
- .maximum = GAIN_MAX,
|
|
+ .maximum = 255,
|
|
.step = 1,
|
|
#define GAIN_DEF 127
|
|
#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
|
|
.default_value = GAIN_DEF,
|
|
},
|
|
- .set = sd_setgain,
|
|
- .get = sd_getgain,
|
|
+ .set_control = setgain
|
|
},
|
|
- {
|
|
+[EXPOSURE] = {
|
|
{
|
|
.id = V4L2_CID_EXPOSURE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -238,10 +218,9 @@ static const struct ctrl sd_ctrls[] = {
|
|
#define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
|
|
.default_value = EXPOSURE_DEF,
|
|
},
|
|
- .set = sd_setexposure,
|
|
- .get = sd_getexposure,
|
|
+ .set_control = setexposure
|
|
},
|
|
- {
|
|
+[AUTOGAIN] = {
|
|
{
|
|
.id = V4L2_CID_AUTOGAIN,
|
|
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
@@ -252,10 +231,9 @@ static const struct ctrl sd_ctrls[] = {
|
|
#define AUTOGAIN_DEF 1
|
|
.default_value = AUTOGAIN_DEF,
|
|
},
|
|
- .set = sd_setautogain,
|
|
- .get = sd_getautogain,
|
|
+ .set_control = setautogain,
|
|
},
|
|
- {
|
|
+[HFLIP] = {
|
|
{
|
|
.id = V4L2_CID_HFLIP,
|
|
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
@@ -263,13 +241,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,
|
|
@@ -277,11 +253,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
|
|
},
|
|
};
|
|
|
|
@@ -290,21 +264,21 @@ static const struct v4l2_pix_format vga_
|
|
.bytesperline = 640,
|
|
.sizeimage = 640 * 480 * 3 / 8 + 590,
|
|
.colorspace = V4L2_COLORSPACE_JPEG,
|
|
- .priv = 0},
|
|
+ },
|
|
};
|
|
|
|
#define LOAD_PAGE3 255
|
|
#define END_OF_SEQUENCE 0
|
|
|
|
/* pac 7302 */
|
|
-static const __u8 init_7302[] = {
|
|
+static const u8 init_7302[] = {
|
|
/* index,value */
|
|
0xff, 0x01, /* page 1 */
|
|
0x78, 0x00, /* deactivate */
|
|
0xff, 0x01,
|
|
0x78, 0x40, /* led off */
|
|
};
|
|
-static const __u8 start_7302[] = {
|
|
+static const u8 start_7302[] = {
|
|
/* index, len, [value]* */
|
|
0xff, 1, 0x00, /* page 0 */
|
|
0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
|
|
@@ -319,7 +293,7 @@ static const __u8 start_7302[] = {
|
|
0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
|
|
0x00, 0x54, 0x11,
|
|
0x55, 1, 0x00,
|
|
- 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
|
|
+ 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
|
|
0x6b, 1, 0x00,
|
|
0x6e, 3, 0x08, 0x06, 0x00,
|
|
0x72, 3, 0x00, 0xff, 0x00,
|
|
@@ -370,7 +344,7 @@ static const __u8 start_7302[] = {
|
|
|
|
#define SKIP 0xaa
|
|
/* page 3 - the value SKIP says skip the index - see reg_w_page() */
|
|
-static const __u8 page3_7302[] = {
|
|
+static const u8 page3_7302[] = {
|
|
0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16,
|
|
0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
|
|
0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
@@ -394,7 +368,7 @@ static const __u8 page3_7302[] = {
|
|
};
|
|
|
|
static void reg_w_buf(struct gspca_dev *gspca_dev,
|
|
- __u8 index,
|
|
+ u8 index,
|
|
const u8 *buffer, int len)
|
|
{
|
|
int ret;
|
|
@@ -410,7 +384,7 @@ static void reg_w_buf(struct gspca_dev *
|
|
index, gspca_dev->usb_buf, len,
|
|
500);
|
|
if (ret < 0) {
|
|
- pr_err("reg_w_buf failed index 0x%02x, error %d\n",
|
|
+ pr_err("reg_w_buf failed i: %02x error %d\n",
|
|
index, ret);
|
|
gspca_dev->usb_err = ret;
|
|
}
|
|
@@ -418,8 +392,8 @@ static void reg_w_buf(struct gspca_dev *
|
|
|
|
|
|
static void reg_w(struct gspca_dev *gspca_dev,
|
|
- __u8 index,
|
|
- __u8 value)
|
|
+ u8 index,
|
|
+ u8 value)
|
|
{
|
|
int ret;
|
|
|
|
@@ -433,14 +407,14 @@ static void reg_w(struct gspca_dev *gspc
|
|
0, index, gspca_dev->usb_buf, 1,
|
|
500);
|
|
if (ret < 0) {
|
|
- pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
|
|
+ pr_err("reg_w() failed i: %02x v: %02x error %d\n",
|
|
index, value, ret);
|
|
gspca_dev->usb_err = ret;
|
|
}
|
|
}
|
|
|
|
static void reg_w_seq(struct gspca_dev *gspca_dev,
|
|
- const __u8 *seq, int len)
|
|
+ const u8 *seq, int len)
|
|
{
|
|
while (--len >= 0) {
|
|
reg_w(gspca_dev, seq[0], seq[1]);
|
|
@@ -450,7 +424,7 @@ static void reg_w_seq(struct gspca_dev *
|
|
|
|
/* load the beginning of a page */
|
|
static void reg_w_page(struct gspca_dev *gspca_dev,
|
|
- const __u8 *page, int len)
|
|
+ const u8 *page, int len)
|
|
{
|
|
int index;
|
|
int ret = 0;
|
|
@@ -468,7 +442,7 @@ static void reg_w_page(struct gspca_dev
|
|
0, index, gspca_dev->usb_buf, 1,
|
|
500);
|
|
if (ret < 0) {
|
|
- pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
|
|
+ pr_err("reg_w_page() failed i: %02x v: %02x error %d\n",
|
|
index, page[index], ret);
|
|
gspca_dev->usb_err = ret;
|
|
break;
|
|
@@ -478,8 +452,8 @@ static void reg_w_page(struct gspca_dev
|
|
|
|
/* output a variable sequence */
|
|
static void reg_w_var(struct gspca_dev *gspca_dev,
|
|
- const __u8 *seq,
|
|
- const __u8 *page3, unsigned int page3_len)
|
|
+ const u8 *seq,
|
|
+ const u8 *page3, unsigned int page3_len)
|
|
{
|
|
int index, len;
|
|
|
|
@@ -493,11 +467,13 @@ static void reg_w_var(struct gspca_dev *
|
|
reg_w_page(gspca_dev, page3, page3_len);
|
|
break;
|
|
default:
|
|
+#ifdef GSPCA_DEBUG
|
|
if (len > USB_BUF_SZ) {
|
|
PDEBUG(D_ERR|D_STREAM,
|
|
"Incorrect variable sequence");
|
|
return;
|
|
}
|
|
+#endif
|
|
while (len > 0) {
|
|
if (len < 8) {
|
|
reg_w_buf(gspca_dev,
|
|
@@ -524,21 +500,11 @@ static int sd_config(struct gspca_dev *g
|
|
|
|
cam = &gspca_dev->cam;
|
|
|
|
- PDEBUG(D_CONF, "Find Sensor PAC7302");
|
|
cam->cam_mode = vga_mode; /* only 640x480 */
|
|
cam->nmodes = ARRAY_SIZE(vga_mode);
|
|
|
|
- sd->brightness = BRIGHTNESS_DEF;
|
|
- sd->contrast = CONTRAST_DEF;
|
|
- sd->colors = COLOR_DEF;
|
|
- sd->white_balance = WHITEBALANCE_DEF;
|
|
- sd->red_balance = REDBALANCE_DEF;
|
|
- sd->blue_balance = BLUEBALANCE_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;
|
|
+
|
|
sd->flags = id->driver_info;
|
|
return 0;
|
|
}
|
|
@@ -548,19 +514,19 @@ static void setbrightcont(struct gspca_d
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
int i, v;
|
|
- static const __u8 max[10] =
|
|
+ static const u8 max[10] =
|
|
{0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
|
|
0xd4, 0xec};
|
|
- static const __u8 delta[10] =
|
|
+ static const u8 delta[10] =
|
|
{0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
|
|
0x11, 0x0b};
|
|
|
|
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
|
for (i = 0; i < 10; i++) {
|
|
v = max[i];
|
|
- v += (sd->brightness - BRIGHTNESS_MAX)
|
|
+ v += (sd->ctrls[BRIGHTNESS].val - BRIGHTNESS_MAX)
|
|
* 150 / BRIGHTNESS_MAX; /* 200 ? */
|
|
- v -= delta[i] * sd->contrast / CONTRAST_MAX;
|
|
+ v -= delta[i] * sd->ctrls[CONTRAST].val / CONTRAST_MAX;
|
|
if (v < 0)
|
|
v = 0;
|
|
else if (v > 0xff)
|
|
@@ -584,12 +550,11 @@ static void setcolors(struct gspca_dev *
|
|
reg_w(gspca_dev, 0x11, 0x01);
|
|
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
|
for (i = 0; i < 9; i++) {
|
|
- v = a[i] * sd->colors / COLOR_MAX + b[i];
|
|
+ v = a[i] * sd->ctrls[COLORS].val / COLOR_MAX + b[i];
|
|
reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
|
|
reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
|
|
}
|
|
reg_w(gspca_dev, 0xdc, 0x01);
|
|
- PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
|
|
}
|
|
|
|
static void setwhitebalance(struct gspca_dev *gspca_dev)
|
|
@@ -597,10 +562,9 @@ static void setwhitebalance(struct gspca
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
|
- reg_w(gspca_dev, 0xc6, sd->white_balance);
|
|
+ reg_w(gspca_dev, 0xc6, sd->ctrls[WHITE_BALANCE].val);
|
|
|
|
reg_w(gspca_dev, 0xdc, 0x01);
|
|
- PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance);
|
|
}
|
|
|
|
static void setredbalance(struct gspca_dev *gspca_dev)
|
|
@@ -608,10 +572,9 @@ static void setredbalance(struct gspca_d
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
|
- reg_w(gspca_dev, 0xc5, sd->red_balance);
|
|
+ reg_w(gspca_dev, 0xc5, sd->ctrls[RED_BALANCE].val);
|
|
|
|
reg_w(gspca_dev, 0xdc, 0x01);
|
|
- PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance);
|
|
}
|
|
|
|
static void setbluebalance(struct gspca_dev *gspca_dev)
|
|
@@ -619,10 +582,9 @@ static void setbluebalance(struct gspca_
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
|
- reg_w(gspca_dev, 0xc7, sd->blue_balance);
|
|
+ reg_w(gspca_dev, 0xc7, sd->ctrls[BLUE_BALANCE].val);
|
|
|
|
reg_w(gspca_dev, 0xdc, 0x01);
|
|
- PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance);
|
|
}
|
|
|
|
static void setgain(struct gspca_dev *gspca_dev)
|
|
@@ -630,7 +592,7 @@ static void setgain(struct gspca_dev *gs
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
|
|
- reg_w(gspca_dev, 0x10, sd->gain >> 3);
|
|
+ reg_w(gspca_dev, 0x10, sd->ctrls[GAIN].val >> 3);
|
|
|
|
/* load registers to sensor (Bit 0, auto clear) */
|
|
reg_w(gspca_dev, 0x11, 0x01);
|
|
@@ -639,13 +601,13 @@ static void setgain(struct gspca_dev *gs
|
|
static void setexposure(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- __u8 clockdiv;
|
|
- __u16 exposure;
|
|
+ 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. */
|
|
- clockdiv = (90 * sd->exposure + 1999) / 2000;
|
|
+ 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
|
|
@@ -664,7 +626,7 @@ static void setexposure(struct gspca_dev
|
|
|
|
/* frame exposure time in ms = 1000 * clockdiv / 90 ->
|
|
exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */
|
|
- exposure = (sd->exposure * 45 * 448) / (1000 * clockdiv);
|
|
+ exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv);
|
|
/* 0 = use full frametime, 448 = no exposure, reverse it */
|
|
exposure = 448 - exposure;
|
|
|
|
@@ -677,15 +639,35 @@ static void setexposure(struct gspca_dev
|
|
reg_w(gspca_dev, 0x11, 0x01);
|
|
}
|
|
|
|
+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. */
|
|
+ if (sd->ctrls[AUTOGAIN].val) {
|
|
+ sd->ctrls[EXPOSURE].val = EXPOSURE_DEF;
|
|
+ sd->ctrls[GAIN].val = GAIN_DEF;
|
|
+ sd->autogain_ignore_frames =
|
|
+ PAC_AUTOGAIN_IGNORE_FRAMES;
|
|
+ } else {
|
|
+ sd->autogain_ignore_frames = -1;
|
|
+ }
|
|
+ setexposure(gspca_dev);
|
|
+ setgain(gspca_dev);
|
|
+}
|
|
+
|
|
static void sethvflip(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
u8 data, hflip, vflip;
|
|
|
|
- hflip = sd->hflip;
|
|
+ hflip = sd->ctrls[HFLIP].val;
|
|
if (sd->flags & FL_HFLIP)
|
|
hflip = !hflip;
|
|
- vflip = sd->vflip;
|
|
+ vflip = sd->ctrls[VFLIP].val;
|
|
if (sd->flags & FL_VFLIP)
|
|
vflip = !vflip;
|
|
|
|
@@ -708,8 +690,6 @@ static int sd_start(struct gspca_dev *gs
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
- sd->sof_read = 0;
|
|
-
|
|
reg_w_var(gspca_dev, start_7302,
|
|
page3_7302, sizeof(page3_7302));
|
|
setbrightcont(gspca_dev);
|
|
@@ -717,15 +697,13 @@ static int sd_start(struct gspca_dev *gs
|
|
setwhitebalance(gspca_dev);
|
|
setredbalance(gspca_dev);
|
|
setbluebalance(gspca_dev);
|
|
- setgain(gspca_dev);
|
|
- setexposure(gspca_dev);
|
|
+ setautogain(gspca_dev);
|
|
sethvflip(gspca_dev);
|
|
|
|
/* only resolution 640x480 is supported for pac7302 */
|
|
|
|
sd->sof_read = 0;
|
|
- sd->autogain_ignore_frames = 0;
|
|
- atomic_set(&sd->avg_lum, -1);
|
|
+ atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val);
|
|
|
|
/* start stream */
|
|
reg_w(gspca_dev, 0xff, 0x01);
|
|
@@ -751,8 +729,10 @@ static void sd_stop0(struct gspca_dev *g
|
|
reg_w(gspca_dev, 0x78, 0x40);
|
|
}
|
|
|
|
-/* Include pac common sof detection functions */
|
|
-#include "pac_common.h"
|
|
+/* !! coarse_grained_expo_autogain is not used !! */
|
|
+#define exp_too_low_cnt flags
|
|
+#define exp_too_high_cnt sof_read
|
|
+#include "autogain_functions.h"
|
|
|
|
static void do_autogain(struct gspca_dev *gspca_dev)
|
|
{
|
|
@@ -761,65 +741,44 @@ static void do_autogain(struct gspca_dev
|
|
int desired_lum;
|
|
const int deadzone = 30;
|
|
|
|
- if (avg_lum == -1)
|
|
+ if (sd->autogain_ignore_frames < 0)
|
|
return;
|
|
|
|
- desired_lum = 270 + sd->brightness;
|
|
-
|
|
- if (sd->autogain_ignore_frames > 0)
|
|
+ if (sd->autogain_ignore_frames > 0) {
|
|
sd->autogain_ignore_frames--;
|
|
- else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
|
|
- deadzone, GAIN_KNEE, EXPOSURE_KNEE))
|
|
+ } else {
|
|
+ desired_lum = 270 + sd->ctrls[BRIGHTNESS].val;
|
|
+
|
|
+ auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
|
|
+ deadzone, GAIN_KNEE, EXPOSURE_KNEE);
|
|
sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
|
|
+ }
|
|
}
|
|
|
|
-/* JPEG header, part 1 */
|
|
-static const unsigned char pac_jpeg_header1[] = {
|
|
- 0xff, 0xd8, /* SOI: Start of Image */
|
|
-
|
|
- 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
|
|
- 0x00, 0x11, /* length = 17 bytes (including this length field) */
|
|
- 0x08 /* Precision: 8 */
|
|
- /* 2 bytes is placed here: number of image lines */
|
|
- /* 2 bytes is placed here: samples per line */
|
|
-};
|
|
-
|
|
-/* JPEG header, continued */
|
|
-static const unsigned char pac_jpeg_header2[] = {
|
|
- 0x03, /* Number of image components: 3 */
|
|
- 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
|
|
- 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
|
|
- 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
|
|
-
|
|
- 0xff, 0xda, /* SOS: Start Of Scan */
|
|
- 0x00, 0x0c, /* length = 12 bytes (including this length field) */
|
|
- 0x03, /* number of components: 3 */
|
|
- 0x01, 0x00, /* selector 1, table 0x00 */
|
|
- 0x02, 0x11, /* selector 2, table 0x11 */
|
|
- 0x03, 0x11, /* selector 3, table 0x11 */
|
|
- 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
|
|
- 0x00 /* Successive approximation: 0 */
|
|
+/* JPEG header */
|
|
+static const u8 jpeg_header[] = {
|
|
+ 0xff, 0xd8, /* SOI: Start of Image */
|
|
+
|
|
+ 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
|
|
+ 0x00, 0x11, /* length = 17 bytes (including this length field) */
|
|
+ 0x08, /* Precision: 8 */
|
|
+ 0x02, 0x80, /* height = 640 (image rotated) */
|
|
+ 0x01, 0xe0, /* width = 480 */
|
|
+ 0x03, /* Number of image components: 3 */
|
|
+ 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
|
|
+ 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
|
|
+ 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
|
|
+
|
|
+ 0xff, 0xda, /* SOS: Start Of Scan */
|
|
+ 0x00, 0x0c, /* length = 12 bytes (including this length field) */
|
|
+ 0x03, /* number of components: 3 */
|
|
+ 0x01, 0x00, /* selector 1, table 0x00 */
|
|
+ 0x02, 0x11, /* selector 2, table 0x11 */
|
|
+ 0x03, 0x11, /* selector 3, table 0x11 */
|
|
+ 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
|
|
+ 0x00 /* Successive approximation: 0 */
|
|
};
|
|
|
|
-static void pac_start_frame(struct gspca_dev *gspca_dev,
|
|
- __u16 lines, __u16 samples_per_line)
|
|
-{
|
|
- unsigned char tmpbuf[4];
|
|
-
|
|
- gspca_frame_add(gspca_dev, FIRST_PACKET,
|
|
- pac_jpeg_header1, sizeof(pac_jpeg_header1));
|
|
-
|
|
- tmpbuf[0] = lines >> 8;
|
|
- tmpbuf[1] = lines & 0xff;
|
|
- tmpbuf[2] = samples_per_line >> 8;
|
|
- tmpbuf[3] = samples_per_line & 0xff;
|
|
-
|
|
- gspca_frame_add(gspca_dev, INTER_PACKET,
|
|
- tmpbuf, sizeof(tmpbuf));
|
|
- gspca_frame_add(gspca_dev, INTER_PACKET,
|
|
- pac_jpeg_header2, sizeof(pac_jpeg_header2));
|
|
-}
|
|
-
|
|
/* this function is run at interrupt level */
|
|
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|
u8 *data, /* isoc packet */
|
|
@@ -827,7 +786,7 @@ static void sd_pkt_scan(struct gspca_dev
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
u8 *image;
|
|
- unsigned char *sof;
|
|
+ u8 *sof;
|
|
|
|
sof = pac_find_sof(&sd->sof_read, data, len);
|
|
if (sof) {
|
|
@@ -864,234 +823,21 @@ static void sd_pkt_scan(struct gspca_dev
|
|
n >= lum_offset)
|
|
atomic_set(&sd->avg_lum, data[-lum_offset] +
|
|
data[-lum_offset + 1]);
|
|
- else
|
|
- atomic_set(&sd->avg_lum, -1);
|
|
|
|
/* Start the new frame with the jpeg header */
|
|
/* The PAC7302 has the image rotated 90 degrees */
|
|
- pac_start_frame(gspca_dev,
|
|
- gspca_dev->width, gspca_dev->height);
|
|
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
|
|
+ jpeg_header, sizeof jpeg_header);
|
|
}
|
|
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
|
|
}
|
|
|
|
-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)
|
|
- setbrightcont(gspca_dev);
|
|
- return gspca_dev->usb_err;
|
|
-}
|
|
-
|
|
-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_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->contrast = val;
|
|
- if (gspca_dev->streaming)
|
|
- setbrightcont(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_setcolors(struct gspca_dev *gspca_dev, __s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->colors = val;
|
|
- if (gspca_dev->streaming)
|
|
- setcolors(gspca_dev);
|
|
- return gspca_dev->usb_err;
|
|
-}
|
|
-
|
|
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- *val = sd->colors;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->white_balance = val;
|
|
- if (gspca_dev->streaming)
|
|
- setwhitebalance(gspca_dev);
|
|
- return gspca_dev->usb_err;
|
|
-}
|
|
-
|
|
-static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- *val = sd->white_balance;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->red_balance = val;
|
|
- if (gspca_dev->streaming)
|
|
- setredbalance(gspca_dev);
|
|
- return gspca_dev->usb_err;
|
|
-}
|
|
-
|
|
-static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- *val = sd->red_balance;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->blue_balance = val;
|
|
- if (gspca_dev->streaming)
|
|
- setbluebalance(gspca_dev);
|
|
- return gspca_dev->usb_err;
|
|
-}
|
|
-
|
|
-static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- *val = sd->blue_balance;
|
|
- 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)
|
|
-{
|
|
- 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 (gspca_dev->streaming) {
|
|
- sd->autogain_ignore_frames =
|
|
- PAC_AUTOGAIN_IGNORE_FRAMES;
|
|
- setexposure(gspca_dev);
|
|
- 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;
|
|
-}
|
|
-
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
|
|
struct v4l2_dbg_register *reg)
|
|
{
|
|
- __u8 index;
|
|
- __u8 value;
|
|
+ u8 index;
|
|
+ u8 value;
|
|
|
|
/* reg->reg: bit0..15: reserved for register index (wIndex is 16bit
|
|
long on the USB bus)
|
|
@@ -1103,8 +849,8 @@ static int sd_dbg_s_register(struct gspc
|
|
) {
|
|
/* Currently writing to page 0 is only supported. */
|
|
/* reg_w() only supports 8bit index */
|
|
- index = reg->reg & 0x000000ff;
|
|
- value = reg->val & 0x000000ff;
|
|
+ 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
|
|
@@ -1165,7 +911,7 @@ static int sd_int_pkt_scan(struct gspca_
|
|
|
|
/* sub-driver description for pac7302 */
|
|
static const struct sd_desc sd_desc = {
|
|
- .name = MODULE_NAME,
|
|
+ .name = KBUILD_MODNAME,
|
|
.ctrls = sd_ctrls,
|
|
.nctrls = ARRAY_SIZE(sd_ctrls),
|
|
.config = sd_config,
|
|
@@ -1187,6 +933,7 @@ static const struct sd_desc sd_desc = {
|
|
/* -- module initialisation -- */
|
|
static const struct usb_device_id device_table[] = {
|
|
{USB_DEVICE(0x06f8, 0x3009)},
|
|
+ {USB_DEVICE(0x06f8, 0x301b)},
|
|
{USB_DEVICE(0x093a, 0x2620)},
|
|
{USB_DEVICE(0x093a, 0x2621)},
|
|
{USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP},
|
|
@@ -1211,7 +958,7 @@ static int sd_probe(struct usb_interface
|
|
}
|
|
|
|
static struct usb_driver sd_driver = {
|
|
- .name = MODULE_NAME,
|
|
+ .name = KBUILD_MODNAME,
|
|
.id_table = device_table,
|
|
.probe = sd_probe,
|
|
.disconnect = gspca_disconnect,
|
|
Index: linux-3.3.x86_64/drivers/media/video/gspca/sonixj.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/gspca/sonixj.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/gspca/sonixj.c
|
|
@@ -39,7 +39,9 @@ enum e_ctrl {
|
|
BLUE,
|
|
RED,
|
|
GAMMA,
|
|
+ EXPOSURE,
|
|
AUTOGAIN,
|
|
+ GAIN,
|
|
HFLIP,
|
|
VFLIP,
|
|
SHARPNESS,
|
|
@@ -131,7 +133,9 @@ static void setcontrast(struct gspca_dev
|
|
static void setcolors(struct gspca_dev *gspca_dev);
|
|
static void setredblue(struct gspca_dev *gspca_dev);
|
|
static void setgamma(struct gspca_dev *gspca_dev);
|
|
-static void setautogain(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 setgain(struct gspca_dev *gspca_dev);
|
|
static void sethvflip(struct gspca_dev *gspca_dev);
|
|
static void setsharpness(struct gspca_dev *gspca_dev);
|
|
static void setillum(struct gspca_dev *gspca_dev);
|
|
@@ -213,6 +217,18 @@ static const struct ctrl sd_ctrls[NCTRLS
|
|
},
|
|
.set_control = setgamma
|
|
},
|
|
+[EXPOSURE] = {
|
|
+ {
|
|
+ .id = V4L2_CID_EXPOSURE,
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
+ .name = "Exposure",
|
|
+ .minimum = 500,
|
|
+ .maximum = 1500,
|
|
+ .step = 1,
|
|
+ .default_value = 1024
|
|
+ },
|
|
+ .set_control = setexposure
|
|
+ },
|
|
[AUTOGAIN] = {
|
|
{
|
|
.id = V4L2_CID_AUTOGAIN,
|
|
@@ -223,7 +239,19 @@ static const struct ctrl sd_ctrls[NCTRLS
|
|
.step = 1,
|
|
.default_value = 1
|
|
},
|
|
- .set_control = setautogain
|
|
+ .set = sd_setautogain,
|
|
+ },
|
|
+[GAIN] = {
|
|
+ {
|
|
+ .id = V4L2_CID_GAIN,
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
+ .name = "Gain",
|
|
+ .minimum = 4,
|
|
+ .maximum = 49,
|
|
+ .step = 1,
|
|
+ .default_value = 15
|
|
+ },
|
|
+ .set_control = setgain
|
|
},
|
|
[HFLIP] = {
|
|
{
|
|
@@ -290,60 +318,87 @@ static const struct ctrl sd_ctrls[NCTRLS
|
|
|
|
/* table of the disabled controls */
|
|
static const __u32 ctrl_dis[] = {
|
|
-[SENSOR_ADCM1700] = (1 << AUTOGAIN) |
|
|
+[SENSOR_ADCM1700] = (1 << EXPOSURE) |
|
|
+ (1 << AUTOGAIN) |
|
|
+ (1 << GAIN) |
|
|
(1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_GC0307] = (1 << HFLIP) |
|
|
+[SENSOR_GC0307] = (1 << EXPOSURE) |
|
|
+ (1 << GAIN) |
|
|
+ (1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_HV7131R] = (1 << HFLIP) |
|
|
+[SENSOR_HV7131R] = (1 << EXPOSURE) |
|
|
+ (1 << GAIN) |
|
|
+ (1 << HFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_MI0360] = (1 << HFLIP) |
|
|
+[SENSOR_MI0360] = (1 << EXPOSURE) |
|
|
+ (1 << GAIN) |
|
|
+ (1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_MI0360B] = (1 << HFLIP) |
|
|
+[SENSOR_MI0360B] = (1 << EXPOSURE) |
|
|
+ (1 << GAIN) |
|
|
+ (1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_MO4000] = (1 << HFLIP) |
|
|
+[SENSOR_MO4000] = (1 << EXPOSURE) |
|
|
+ (1 << GAIN) |
|
|
+ (1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_MT9V111] = (1 << HFLIP) |
|
|
+[SENSOR_MT9V111] = (1 << EXPOSURE) |
|
|
+ (1 << GAIN) |
|
|
+ (1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_OM6802] = (1 << HFLIP) |
|
|
+[SENSOR_OM6802] = (1 << EXPOSURE) |
|
|
+ (1 << GAIN) |
|
|
+ (1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_OV7630] = (1 << HFLIP),
|
|
-
|
|
-[SENSOR_OV7648] = (1 << HFLIP),
|
|
-
|
|
-[SENSOR_OV7660] = (1 << AUTOGAIN) |
|
|
+[SENSOR_OV7630] = (1 << EXPOSURE) |
|
|
+ (1 << GAIN) |
|
|
+ (1 << HFLIP),
|
|
+
|
|
+[SENSOR_OV7648] = (1 << EXPOSURE) |
|
|
+ (1 << GAIN) |
|
|
+ (1 << HFLIP),
|
|
+
|
|
+[SENSOR_OV7660] = (1 << EXPOSURE) |
|
|
+ (1 << AUTOGAIN) |
|
|
+ (1 << GAIN) |
|
|
(1 << HFLIP) |
|
|
(1 << VFLIP),
|
|
|
|
-[SENSOR_PO1030] = (1 << AUTOGAIN) |
|
|
+[SENSOR_PO1030] = (1 << EXPOSURE) |
|
|
+ (1 << AUTOGAIN) |
|
|
+ (1 << GAIN) |
|
|
(1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_PO2030N] = (1 << AUTOGAIN) |
|
|
- (1 << FREQ),
|
|
+[SENSOR_PO2030N] = (1 << FREQ),
|
|
|
|
-[SENSOR_SOI768] = (1 << AUTOGAIN) |
|
|
+[SENSOR_SOI768] = (1 << EXPOSURE) |
|
|
+ (1 << AUTOGAIN) |
|
|
+ (1 << GAIN) |
|
|
(1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
|
|
-[SENSOR_SP80708] = (1 << AUTOGAIN) |
|
|
+[SENSOR_SP80708] = (1 << EXPOSURE) |
|
|
+ (1 << AUTOGAIN) |
|
|
+ (1 << GAIN) |
|
|
(1 << HFLIP) |
|
|
(1 << VFLIP) |
|
|
(1 << FREQ),
|
|
@@ -1242,14 +1297,6 @@ static const u8 po2030n_sensor_param1[][
|
|
{0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10},
|
|
{0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
|
|
{0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
|
|
- {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10},
|
|
- {0xc1, 0x6e, 0x16, 0x52, 0x40, 0x48, 0x00, 0x10},
|
|
-/*after start*/
|
|
- {0xa1, 0x6e, 0x15, 0x0f, 0x00, 0x00, 0x00, 0x10},
|
|
- {DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */
|
|
- {0xa1, 0x6e, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x10},
|
|
- {DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */
|
|
- {0xa1, 0x6e, 0x1b, 0x53, 0x00, 0x00, 0x00, 0x10},
|
|
{}
|
|
};
|
|
|
|
@@ -1858,7 +1905,7 @@ static int sd_init(struct gspca_dev *gsp
|
|
return gspca_dev->usb_err;
|
|
}
|
|
|
|
-static u32 setexposure(struct gspca_dev *gspca_dev,
|
|
+static u32 expo_adjust(struct gspca_dev *gspca_dev,
|
|
u32 expo)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
@@ -1982,28 +2029,28 @@ static void setbrightness(struct gspca_d
|
|
expo = 0x002dc6c0;
|
|
else if (expo < 0x02a0)
|
|
expo = 0x02a0;
|
|
- sd->exposure = setexposure(gspca_dev, expo);
|
|
+ sd->exposure = expo_adjust(gspca_dev, expo);
|
|
break;
|
|
case SENSOR_MI0360:
|
|
case SENSOR_MO4000:
|
|
expo = brightness << 4;
|
|
- sd->exposure = setexposure(gspca_dev, expo);
|
|
+ sd->exposure = expo_adjust(gspca_dev, expo);
|
|
break;
|
|
case SENSOR_MI0360B:
|
|
expo = brightness << 2;
|
|
- sd->exposure = setexposure(gspca_dev, expo);
|
|
+ sd->exposure = expo_adjust(gspca_dev, expo);
|
|
break;
|
|
case SENSOR_GC0307:
|
|
expo = brightness;
|
|
- sd->exposure = setexposure(gspca_dev, expo);
|
|
+ sd->exposure = expo_adjust(gspca_dev, expo);
|
|
return; /* don't set the Y offset */
|
|
case SENSOR_MT9V111:
|
|
expo = brightness << 2;
|
|
- sd->exposure = setexposure(gspca_dev, expo);
|
|
+ sd->exposure = expo_adjust(gspca_dev, expo);
|
|
return; /* don't set the Y offset */
|
|
case SENSOR_OM6802:
|
|
expo = brightness << 2;
|
|
- sd->exposure = setexposure(gspca_dev, expo);
|
|
+ sd->exposure = expo_adjust(gspca_dev, expo);
|
|
return; /* Y offset already set */
|
|
}
|
|
|
|
@@ -2112,6 +2159,23 @@ static void setgamma(struct gspca_dev *g
|
|
reg_w(gspca_dev, 0x20, gamma, sizeof gamma);
|
|
}
|
|
|
|
+static void setexposure(struct gspca_dev *gspca_dev)
|
|
+{
|
|
+ struct sd *sd = (struct sd *) gspca_dev;
|
|
+
|
|
+ if (sd->sensor == SENSOR_PO2030N) {
|
|
+ u8 rexpo[] = /* 1a: expo H, 1b: expo M */
|
|
+ {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10};
|
|
+
|
|
+ rexpo[3] = sd->ctrls[EXPOSURE].val >> 8;
|
|
+ i2c_w8(gspca_dev, rexpo);
|
|
+ msleep(6);
|
|
+ rexpo[2] = 0x1b;
|
|
+ rexpo[3] = sd->ctrls[EXPOSURE].val;
|
|
+ i2c_w8(gspca_dev, rexpo);
|
|
+ }
|
|
+}
|
|
+
|
|
static void setautogain(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
@@ -2139,6 +2203,19 @@ static void setautogain(struct gspca_dev
|
|
sd->ag_cnt = -1;
|
|
}
|
|
|
|
+static void setgain(struct gspca_dev *gspca_dev)
|
|
+{
|
|
+ struct sd *sd = (struct sd *) gspca_dev;
|
|
+
|
|
+ if (sd->sensor == SENSOR_PO2030N) {
|
|
+ u8 rgain[] = /* 15: gain */
|
|
+ {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15};
|
|
+
|
|
+ rgain[3] = sd->ctrls[GAIN].val;
|
|
+ i2c_w8(gspca_dev, rgain);
|
|
+ }
|
|
+}
|
|
+
|
|
static void sethvflip(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
@@ -2623,6 +2700,10 @@ static int sd_start(struct gspca_dev *gs
|
|
setcontrast(gspca_dev);
|
|
setcolors(gspca_dev);
|
|
setautogain(gspca_dev);
|
|
+ if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) {
|
|
+ setexposure(gspca_dev);
|
|
+ setgain(gspca_dev);
|
|
+ }
|
|
setfreq(gspca_dev);
|
|
|
|
sd->pktsz = sd->npkt = 0;
|
|
@@ -2719,6 +2800,12 @@ static void sd_stop0(struct gspca_dev *g
|
|
}
|
|
}
|
|
|
|
+/* !! coarse_grained_expo_autogain is not used !! */
|
|
+#define exp_too_low_cnt bridge
|
|
+#define exp_too_high_cnt sensor
|
|
+
|
|
+#include "autogain_functions.h"
|
|
+
|
|
static void do_autogain(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
@@ -2736,6 +2823,13 @@ static void do_autogain(struct gspca_dev
|
|
|
|
delta = atomic_read(&sd->avg_lum);
|
|
PDEBUG(D_FRAM, "mean lum %d", delta);
|
|
+
|
|
+ if (sd->sensor == SENSOR_PO2030N) {
|
|
+ auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta,
|
|
+ 15, 1024);
|
|
+ return;
|
|
+ }
|
|
+
|
|
if (delta < luma_mean - luma_delta ||
|
|
delta > luma_mean + luma_delta) {
|
|
switch (sd->sensor) {
|
|
@@ -2744,7 +2838,7 @@ static void do_autogain(struct gspca_dev
|
|
expotimes += (luma_mean - delta) >> 6;
|
|
if (expotimes < 0)
|
|
expotimes = 0;
|
|
- sd->exposure = setexposure(gspca_dev,
|
|
+ sd->exposure = expo_adjust(gspca_dev,
|
|
(unsigned int) expotimes);
|
|
break;
|
|
case SENSOR_HV7131R:
|
|
@@ -2752,7 +2846,7 @@ static void do_autogain(struct gspca_dev
|
|
expotimes += (luma_mean - delta) >> 4;
|
|
if (expotimes < 0)
|
|
expotimes = 0;
|
|
- sd->exposure = setexposure(gspca_dev,
|
|
+ sd->exposure = expo_adjust(gspca_dev,
|
|
(unsigned int) (expotimes << 8));
|
|
break;
|
|
case SENSOR_OM6802:
|
|
@@ -2761,7 +2855,7 @@ static void do_autogain(struct gspca_dev
|
|
expotimes += (luma_mean - delta) >> 2;
|
|
if (expotimes < 0)
|
|
expotimes = 0;
|
|
- sd->exposure = setexposure(gspca_dev,
|
|
+ sd->exposure = expo_adjust(gspca_dev,
|
|
(unsigned int) expotimes);
|
|
setredblue(gspca_dev);
|
|
break;
|
|
@@ -2773,7 +2867,7 @@ static void do_autogain(struct gspca_dev
|
|
expotimes += (luma_mean - delta) >> 6;
|
|
if (expotimes < 0)
|
|
expotimes = 0;
|
|
- sd->exposure = setexposure(gspca_dev,
|
|
+ sd->exposure = expo_adjust(gspca_dev,
|
|
(unsigned int) expotimes);
|
|
setredblue(gspca_dev);
|
|
break;
|
|
@@ -2948,16 +3042,18 @@ marker_found:
|
|
}
|
|
}
|
|
|
|
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
|
- struct v4l2_jpegcompression *jcomp)
|
|
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
- memset(jcomp, 0, sizeof *jcomp);
|
|
- jcomp->quality = sd->quality;
|
|
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
|
|
- | V4L2_JPEG_MARKER_DQT;
|
|
- return 0;
|
|
+ sd->ctrls[AUTOGAIN].val = val;
|
|
+ if (val)
|
|
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
|
|
+ else
|
|
+ gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN);
|
|
+ if (gspca_dev->streaming)
|
|
+ setautogain(gspca_dev);
|
|
+ return gspca_dev->usb_err;
|
|
}
|
|
|
|
static int sd_querymenu(struct gspca_dev *gspca_dev,
|
|
@@ -3012,7 +3108,6 @@ static const struct sd_desc sd_desc = {
|
|
.stop0 = sd_stop0,
|
|
.pkt_scan = sd_pkt_scan,
|
|
.dq_callback = do_autogain,
|
|
- .get_jcomp = sd_get_jcomp,
|
|
.querymenu = sd_querymenu,
|
|
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
|
.int_pkt_scan = sd_int_pkt_scan,
|
|
Index: linux-3.3.x86_64/drivers/media/video/gspca/zc3xx.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/gspca/zc3xx.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/gspca/zc3xx.c
|
|
@@ -1,7 +1,7 @@
|
|
/*
|
|
- * Z-Star/Vimicro zc301/zc302p/vc30x library
|
|
+ * Z-Star/Vimicro zc301/zc302p/vc30x driver
|
|
*
|
|
- * Copyright (C) 2009-2011 Jean-Francois Moine <http://moinejf.free.fr>
|
|
+ * Copyright (C) 2009-2012 Jean-Francois Moine <http://moinejf.free.fr>
|
|
* Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
@@ -21,8 +21,6 @@
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
-#define MODULE_NAME "zc3xx"
|
|
-
|
|
#include <linux/input.h>
|
|
#include "gspca.h"
|
|
#include "jpeg.h"
|
|
@@ -34,7 +32,7 @@ MODULE_LICENSE("GPL");
|
|
|
|
static int force_sensor = -1;
|
|
|
|
-#define QUANT_VAL 1 /* quantization table */
|
|
+#define REG08_DEF 3 /* default JPEG compression (70%) */
|
|
#include "zc3xx-reg.h"
|
|
|
|
/* controls */
|
|
@@ -46,6 +44,7 @@ enum e_ctrl {
|
|
AUTOGAIN,
|
|
LIGHTFREQ,
|
|
SHARPNESS,
|
|
+ QUALITY,
|
|
NCTRLS /* number of controls */
|
|
};
|
|
|
|
@@ -57,10 +56,10 @@ struct sd {
|
|
|
|
struct gspca_ctrl ctrls[NCTRLS];
|
|
|
|
- u8 quality; /* image quality */
|
|
-#define QUALITY_MIN 50
|
|
-#define QUALITY_MAX 80
|
|
-#define QUALITY_DEF 70
|
|
+ struct work_struct work;
|
|
+ struct workqueue_struct *work_thread;
|
|
+
|
|
+ u8 reg08; /* webcam compression quality */
|
|
|
|
u8 bridge;
|
|
u8 sensor; /* Type of image sensor chip */
|
|
@@ -101,6 +100,7 @@ static void setexposure(struct 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] = {
|
|
@@ -188,6 +188,18 @@ static const struct ctrl sd_ctrls[NCTRLS
|
|
},
|
|
.set_control = setsharpness
|
|
},
|
|
+[QUALITY] = {
|
|
+ {
|
|
+ .id = V4L2_CID_JPEG_COMPRESSION_QUALITY,
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
+ .name = "Compression Quality",
|
|
+ .minimum = 40,
|
|
+ .maximum = 70,
|
|
+ .step = 1,
|
|
+ .default_value = 70 /* updated in sd_init() */
|
|
+ },
|
|
+ .set = sd_setquality
|
|
+ },
|
|
};
|
|
|
|
static const struct v4l2_pix_format vga_mode[] = {
|
|
@@ -229,6 +241,9 @@ static const struct v4l2_pix_format sif_
|
|
.priv = 0},
|
|
};
|
|
|
|
+/* bridge reg08 -> JPEG quality conversion table */
|
|
+static u8 jpeg_qual[] = {40, 50, 60, 70, /*80*/};
|
|
+
|
|
/* usb exchanges */
|
|
struct usb_action {
|
|
u8 req;
|
|
@@ -3894,7 +3909,6 @@ static const struct usb_action pas106b_I
|
|
/* Gains */
|
|
{0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
|
|
{0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
|
|
- {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN},
|
|
{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
|
|
/* Auto correction */
|
|
{0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
|
|
@@ -5640,7 +5654,7 @@ static const struct usb_action gc0303_No
|
|
{}
|
|
};
|
|
|
|
-static u8 reg_r_i(struct gspca_dev *gspca_dev,
|
|
+static u8 reg_r(struct gspca_dev *gspca_dev,
|
|
u16 index)
|
|
{
|
|
int ret;
|
|
@@ -5655,24 +5669,14 @@ static u8 reg_r_i(struct gspca_dev *gspc
|
|
index, gspca_dev->usb_buf, 1,
|
|
500);
|
|
if (ret < 0) {
|
|
- pr_err("reg_r_i err %d\n", ret);
|
|
+ pr_err("reg_r err %d\n", ret);
|
|
gspca_dev->usb_err = ret;
|
|
return 0;
|
|
}
|
|
return gspca_dev->usb_buf[0];
|
|
}
|
|
|
|
-static u8 reg_r(struct gspca_dev *gspca_dev,
|
|
- u16 index)
|
|
-{
|
|
- u8 ret;
|
|
-
|
|
- ret = reg_r_i(gspca_dev, index);
|
|
- PDEBUG(D_USBI, "reg r [%04x] -> %02x", index, ret);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void reg_w_i(struct gspca_dev *gspca_dev,
|
|
+static void reg_w(struct gspca_dev *gspca_dev,
|
|
u8 value,
|
|
u16 index)
|
|
{
|
|
@@ -5692,14 +5696,6 @@ static void reg_w_i(struct gspca_dev *gs
|
|
}
|
|
}
|
|
|
|
-static void reg_w(struct gspca_dev *gspca_dev,
|
|
- u8 value,
|
|
- u16 index)
|
|
-{
|
|
- PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value);
|
|
- reg_w_i(gspca_dev, value, index);
|
|
-}
|
|
-
|
|
static u16 i2c_read(struct gspca_dev *gspca_dev,
|
|
u8 reg)
|
|
{
|
|
@@ -5708,16 +5704,14 @@ static u16 i2c_read(struct gspca_dev *gs
|
|
|
|
if (gspca_dev->usb_err < 0)
|
|
return 0;
|
|
- reg_w_i(gspca_dev, reg, 0x0092);
|
|
- reg_w_i(gspca_dev, 0x02, 0x0090); /* <- read command */
|
|
+ reg_w(gspca_dev, reg, 0x0092);
|
|
+ reg_w(gspca_dev, 0x02, 0x0090); /* <- read command */
|
|
msleep(20);
|
|
- retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
|
|
+ retbyte = reg_r(gspca_dev, 0x0091); /* read status */
|
|
if (retbyte != 0x00)
|
|
pr_err("i2c_r status error %02x\n", retbyte);
|
|
- retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */
|
|
- retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */
|
|
- PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)",
|
|
- reg, retval, retbyte);
|
|
+ retval = reg_r(gspca_dev, 0x0095); /* read Lowbyte */
|
|
+ retval |= reg_r(gspca_dev, 0x0096) << 8; /* read Hightbyte */
|
|
return retval;
|
|
}
|
|
|
|
@@ -5730,16 +5724,14 @@ static u8 i2c_write(struct gspca_dev *gs
|
|
|
|
if (gspca_dev->usb_err < 0)
|
|
return 0;
|
|
- reg_w_i(gspca_dev, reg, 0x92);
|
|
- reg_w_i(gspca_dev, valL, 0x93);
|
|
- reg_w_i(gspca_dev, valH, 0x94);
|
|
- reg_w_i(gspca_dev, 0x01, 0x90); /* <- write command */
|
|
+ reg_w(gspca_dev, reg, 0x92);
|
|
+ reg_w(gspca_dev, valL, 0x93);
|
|
+ reg_w(gspca_dev, valH, 0x94);
|
|
+ reg_w(gspca_dev, 0x01, 0x90); /* <- write command */
|
|
msleep(1);
|
|
- retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
|
|
+ retbyte = reg_r(gspca_dev, 0x0091); /* read status */
|
|
if (retbyte != 0x00)
|
|
pr_err("i2c_w status error %02x\n", retbyte);
|
|
- PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
|
|
- reg, valH, valL, retbyte);
|
|
return retbyte;
|
|
}
|
|
|
|
@@ -5906,6 +5898,8 @@ static void getexposure(struct 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)
|
|
| (i2c_read(gspca_dev, 0x26) << 1)
|
|
| (i2c_read(gspca_dev, 0x27) >> 7);
|
|
@@ -5916,6 +5910,8 @@ static void setexposure(struct gspca_dev
|
|
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);
|
|
@@ -5925,32 +5921,20 @@ static void setexposure(struct gspca_dev
|
|
static void setquality(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- u8 frxt;
|
|
+ s8 reg07;
|
|
|
|
+ reg07 = 0;
|
|
switch (sd->sensor) {
|
|
- case SENSOR_ADCM2700:
|
|
- case SENSOR_GC0305:
|
|
- case SENSOR_HV7131B:
|
|
- case SENSOR_HV7131R:
|
|
case SENSOR_OV7620:
|
|
+ reg07 = 0x30;
|
|
+ break;
|
|
+ case SENSOR_HV7131R:
|
|
case SENSOR_PAS202B:
|
|
- case SENSOR_PO2030:
|
|
- return;
|
|
+ return; /* done by work queue */
|
|
}
|
|
-/*fixme: is it really 0008 0007 0018 for all other sensors? */
|
|
- reg_w(gspca_dev, QUANT_VAL, 0x0008);
|
|
- frxt = 0x30;
|
|
- reg_w(gspca_dev, frxt, 0x0007);
|
|
-#if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2
|
|
- frxt = 0xff;
|
|
-#elif QUANT_VAL == 3
|
|
- frxt = 0xf0;
|
|
-#elif QUANT_VAL == 4
|
|
- frxt = 0xe0;
|
|
-#else
|
|
- frxt = 0x20;
|
|
-#endif
|
|
- reg_w(gspca_dev, frxt, 0x0018);
|
|
+ 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.
|
|
@@ -6084,6 +6068,115 @@ static void setautogain(struct 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 */
|
|
+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;
|
|
+
|
|
+ /* synchronize with the main driver and initialize the registers */
|
|
+ mutex_lock(&gspca_dev->usb_lock);
|
|
+ reg07 = 0; /* max */
|
|
+ reg_w(gspca_dev, reg07, 0x0007);
|
|
+ reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
|
|
+ mutex_unlock(&gspca_dev->usb_lock);
|
|
+
|
|
+ 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->present || !gspca_dev->streaming)
|
|
+ goto err;
|
|
+ reg11 = reg_r(gspca_dev, 0x0011);
|
|
+ if (gspca_dev->usb_err < 0
|
|
+ || !gspca_dev->present || !gspca_dev->streaming)
|
|
+ goto err;
|
|
+
|
|
+ change = reg11 & 0x01;
|
|
+ if (change) { /* overflow */
|
|
+ switch (reg07) {
|
|
+ case 0: /* max */
|
|
+ reg07 = sd->sensor == SENSOR_HV7131R
|
|
+ ? 0x30 : 0x32;
|
|
+ if (sd->reg08 != 0) {
|
|
+ change = 3;
|
|
+ sd->reg08--;
|
|
+ }
|
|
+ break;
|
|
+ case 0x32:
|
|
+ reg07 -= 4;
|
|
+ break;
|
|
+ default:
|
|
+ reg07 -= 2;
|
|
+ break;
|
|
+ case 2:
|
|
+ change = 0; /* already min */
|
|
+ break;
|
|
+ }
|
|
+ good = 0;
|
|
+ } else { /* no overflow */
|
|
+ if (reg07 != 0) { /* if not max */
|
|
+ good++;
|
|
+ if (good >= 10) {
|
|
+ good = 0;
|
|
+ change = 1;
|
|
+ reg07 += 2;
|
|
+ switch (reg07) {
|
|
+ case 0x30:
|
|
+ if (sd->sensor == SENSOR_PAS202B)
|
|
+ reg07 += 2;
|
|
+ break;
|
|
+ case 0x32:
|
|
+ case 0x34:
|
|
+ reg07 = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ } else { /* reg07 max */
|
|
+ if (sd->reg08 < sizeof jpeg_qual - 1) {
|
|
+ good++;
|
|
+ if (good > 10) {
|
|
+ sd->reg08++;
|
|
+ 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) {
|
|
+ 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];
|
|
+ jpeg_set_qual(sd->jpeg_hdr,
|
|
+ jpeg_qual[sd->reg08]);
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&gspca_dev->usb_lock);
|
|
+ }
|
|
+ return;
|
|
+err:
|
|
+ mutex_unlock(&gspca_dev->usb_lock);
|
|
+}
|
|
+
|
|
static void send_unknown(struct gspca_dev *gspca_dev, int sensor)
|
|
{
|
|
reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */
|
|
@@ -6411,7 +6504,9 @@ static int sd_config(struct gspca_dev *g
|
|
sd->sensor = id->driver_info;
|
|
|
|
gspca_dev->cam.ctrls = sd->ctrls;
|
|
- sd->quality = QUALITY_DEF;
|
|
+ sd->reg08 = REG08_DEF;
|
|
+
|
|
+ INIT_WORK(&sd->work, transfer_update);
|
|
|
|
return 0;
|
|
}
|
|
@@ -6464,6 +6559,27 @@ static int sd_init(struct gspca_dev *gsp
|
|
[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)
|
|
@@ -6528,7 +6644,6 @@ static int sd_init(struct gspca_dev *gsp
|
|
case 0x0e:
|
|
PDEBUG(D_PROBE, "Find Sensor PAS202B");
|
|
sd->sensor = SENSOR_PAS202B;
|
|
-/* sd->sharpness = 1; */
|
|
break;
|
|
case 0x0f:
|
|
PDEBUG(D_PROBE, "Find Sensor PAS106");
|
|
@@ -6616,13 +6731,21 @@ static int sd_init(struct gspca_dev *gsp
|
|
}
|
|
|
|
sd->ctrls[GAMMA].def = gamma[sd->sensor];
|
|
+ sd->reg08 = reg08_tb[sd->sensor];
|
|
+ sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08];
|
|
+ sd->ctrls[QUALITY].min = jpeg_qual[0];
|
|
+ sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1];
|
|
|
|
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;
|
|
@@ -6685,7 +6808,6 @@ static int sd_start(struct gspca_dev *gs
|
|
/* 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);
|
|
|
|
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
|
|
switch (sd->sensor) {
|
|
@@ -6761,10 +6883,9 @@ static int sd_start(struct gspca_dev *gs
|
|
reg_r(gspca_dev, 0x0180); /* from win */
|
|
reg_w(gspca_dev, 0x00, 0x0180);
|
|
break;
|
|
- default:
|
|
- setquality(gspca_dev);
|
|
- break;
|
|
}
|
|
+ setquality(gspca_dev);
|
|
+ jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]);
|
|
setlightfreq(gspca_dev);
|
|
|
|
switch (sd->sensor) {
|
|
@@ -6776,8 +6897,7 @@ static int sd_start(struct gspca_dev *gs
|
|
reg_w(gspca_dev, 0x40, 0x0117);
|
|
break;
|
|
case SENSOR_HV7131R:
|
|
- if (!sd->ctrls[AUTOGAIN].val)
|
|
- setexposure(gspca_dev);
|
|
+ setexposure(gspca_dev);
|
|
reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN);
|
|
break;
|
|
case SENSOR_GC0305:
|
|
@@ -6802,13 +6922,19 @@ static int sd_start(struct gspca_dev *gs
|
|
}
|
|
|
|
setautogain(gspca_dev);
|
|
- switch (sd->sensor) {
|
|
- case SENSOR_PO2030:
|
|
- msleep(50);
|
|
- reg_w(gspca_dev, 0x00, 0x0007); /* (from win traces) */
|
|
- reg_w(gspca_dev, 0x02, ZC3XX_R008_CLOCKSETTING);
|
|
- break;
|
|
+
|
|
+ /* 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;
|
|
+ }
|
|
}
|
|
+
|
|
return gspca_dev->usb_err;
|
|
}
|
|
|
|
@@ -6817,6 +6943,12 @@ static void sd_stop0(struct gspca_dev *g
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
+ if (sd->work_thread != NULL) {
|
|
+ mutex_unlock(&gspca_dev->usb_lock);
|
|
+ destroy_workqueue(sd->work_thread);
|
|
+ mutex_lock(&gspca_dev->usb_lock);
|
|
+ sd->work_thread = NULL;
|
|
+ }
|
|
if (!gspca_dev->present)
|
|
return;
|
|
send_unknown(gspca_dev, sd->sensor);
|
|
@@ -6893,19 +7025,33 @@ static int sd_querymenu(struct gspca_dev
|
|
return -EINVAL;
|
|
}
|
|
|
|
+static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val)
|
|
+{
|
|
+ struct sd *sd = (struct sd *) gspca_dev;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(jpeg_qual) - 1; i++) {
|
|
+ if (val <= jpeg_qual[i])
|
|
+ break;
|
|
+ }
|
|
+ if (i > 0
|
|
+ && i == sd->reg08
|
|
+ && val < jpeg_qual[sd->reg08])
|
|
+ i--;
|
|
+ sd->reg08 = i;
|
|
+ sd->ctrls[QUALITY].val = jpeg_qual[i];
|
|
+ if (gspca_dev->streaming)
|
|
+ jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
|
|
+ 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;
|
|
|
|
- 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);
|
|
+ sd_setquality(gspca_dev, jcomp->quality);
|
|
+ jcomp->quality = sd->ctrls[QUALITY].val;
|
|
return gspca_dev->usb_err;
|
|
}
|
|
|
|
@@ -6915,7 +7061,7 @@ static int sd_get_jcomp(struct gspca_dev
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
memset(jcomp, 0, sizeof *jcomp);
|
|
- jcomp->quality = sd->quality;
|
|
+ jcomp->quality = sd->ctrls[QUALITY].val;
|
|
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
|
|
| V4L2_JPEG_MARKER_DQT;
|
|
return 0;
|
|
@@ -6938,7 +7084,7 @@ static int sd_int_pkt_scan(struct gspca_
|
|
#endif
|
|
|
|
static const struct sd_desc sd_desc = {
|
|
- .name = MODULE_NAME,
|
|
+ .name = KBUILD_MODNAME,
|
|
.ctrls = sd_ctrls,
|
|
.nctrls = ARRAY_SIZE(sd_ctrls),
|
|
.config = sd_config,
|
|
@@ -7023,7 +7169,7 @@ static int sd_probe(struct usb_interface
|
|
|
|
/* USB driver */
|
|
static struct usb_driver sd_driver = {
|
|
- .name = MODULE_NAME,
|
|
+ .name = KBUILD_MODNAME,
|
|
.id_table = device_table,
|
|
.probe = sd_probe,
|
|
.disconnect = gspca_disconnect,
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/au8522_decoder.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/au8522_decoder.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/au8522_decoder.c
|
|
@@ -839,15 +839,4 @@ static struct i2c_driver au8522_driver =
|
|
.id_table = au8522_id,
|
|
};
|
|
|
|
-static __init int init_au8522(void)
|
|
-{
|
|
- return i2c_add_driver(&au8522_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_au8522(void)
|
|
-{
|
|
- i2c_del_driver(&au8522_driver);
|
|
-}
|
|
-
|
|
-module_init(init_au8522);
|
|
-module_exit(exit_au8522);
|
|
+module_i2c_driver(au8522_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-tea5764.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-tea5764.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-tea5764.c
|
|
@@ -575,21 +575,7 @@ static struct i2c_driver tea5764_i2c_dri
|
|
.id_table = tea5764_id,
|
|
};
|
|
|
|
-/* init the driver */
|
|
-static int __init tea5764_init(void)
|
|
-{
|
|
- int ret = i2c_add_driver(&tea5764_i2c_driver);
|
|
-
|
|
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": "
|
|
- DRIVER_DESC "\n");
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/* cleanup the driver */
|
|
-static void __exit tea5764_exit(void)
|
|
-{
|
|
- i2c_del_driver(&tea5764_i2c_driver);
|
|
-}
|
|
+module_i2c_driver(tea5764_i2c_driver);
|
|
|
|
MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
@@ -600,6 +586,3 @@ module_param(use_xtal, int, 0);
|
|
MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board");
|
|
module_param(radio_nr, int, 0);
|
|
MODULE_PARM_DESC(radio_nr, "video4linux device number to use");
|
|
-
|
|
-module_init(tea5764_init);
|
|
-module_exit(tea5764_exit);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/saa7706h.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/saa7706h.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/saa7706h.c
|
|
@@ -434,18 +434,7 @@ static struct i2c_driver saa7706h_driver
|
|
.id_table = saa7706h_id,
|
|
};
|
|
|
|
-static __init int saa7706h_init(void)
|
|
-{
|
|
- return i2c_add_driver(&saa7706h_driver);
|
|
-}
|
|
-
|
|
-static __exit void saa7706h_exit(void)
|
|
-{
|
|
- i2c_del_driver(&saa7706h_driver);
|
|
-}
|
|
-
|
|
-module_init(saa7706h_init);
|
|
-module_exit(saa7706h_exit);
|
|
+module_i2c_driver(saa7706h_driver);
|
|
|
|
MODULE_DESCRIPTION("SAA7706H Car Radio DSP driver");
|
|
MODULE_AUTHOR("Mocean Laboratories");
|
|
Index: linux-3.3.x86_64/drivers/media/radio/si470x/radio-si470x-i2c.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/si470x/radio-si470x-i2c.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/si470x/radio-si470x-i2c.c
|
|
@@ -539,33 +539,7 @@ static struct i2c_driver si470x_i2c_driv
|
|
.id_table = si470x_i2c_id,
|
|
};
|
|
|
|
-
|
|
-
|
|
-/**************************************************************************
|
|
- * Module Interface
|
|
- **************************************************************************/
|
|
-
|
|
-/*
|
|
- * si470x_i2c_init - module init
|
|
- */
|
|
-static int __init si470x_i2c_init(void)
|
|
-{
|
|
- printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
|
|
- return i2c_add_driver(&si470x_i2c_driver);
|
|
-}
|
|
-
|
|
-
|
|
-/*
|
|
- * si470x_i2c_exit - module exit
|
|
- */
|
|
-static void __exit si470x_i2c_exit(void)
|
|
-{
|
|
- i2c_del_driver(&si470x_i2c_driver);
|
|
-}
|
|
-
|
|
-
|
|
-module_init(si470x_i2c_init);
|
|
-module_exit(si470x_i2c_exit);
|
|
+module_i2c_driver(si470x_i2c_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/si4713-i2c.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/si4713-i2c.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/si4713-i2c.c
|
|
@@ -2106,17 +2106,4 @@ static struct i2c_driver si4713_i2c_driv
|
|
.id_table = si4713_id,
|
|
};
|
|
|
|
-/* Module Interface */
|
|
-static int __init si4713_module_init(void)
|
|
-{
|
|
- return i2c_add_driver(&si4713_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit si4713_module_exit(void)
|
|
-{
|
|
- i2c_del_driver(&si4713_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(si4713_module_init);
|
|
-module_exit(si4713_module_exit);
|
|
-
|
|
+module_i2c_driver(si4713_i2c_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/radio/tef6862.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/tef6862.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/tef6862.c
|
|
@@ -215,20 +215,8 @@ static struct i2c_driver tef6862_driver
|
|
.id_table = tef6862_id,
|
|
};
|
|
|
|
-static __init int tef6862_init(void)
|
|
-{
|
|
- return i2c_add_driver(&tef6862_driver);
|
|
-}
|
|
-
|
|
-static __exit void tef6862_exit(void)
|
|
-{
|
|
- i2c_del_driver(&tef6862_driver);
|
|
-}
|
|
-
|
|
-module_init(tef6862_init);
|
|
-module_exit(tef6862_exit);
|
|
+module_i2c_driver(tef6862_driver);
|
|
|
|
MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner");
|
|
MODULE_AUTHOR("Mocean Laboratories");
|
|
MODULE_LICENSE("GPL v2");
|
|
-
|
|
Index: linux-3.3.x86_64/drivers/media/video/adp1653.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/adp1653.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/adp1653.c
|
|
@@ -33,7 +33,6 @@
|
|
#include <linux/delay.h>
|
|
#include <linux/module.h>
|
|
#include <linux/i2c.h>
|
|
-#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/version.h>
|
|
#include <media/adp1653.h>
|
|
@@ -482,24 +481,7 @@ static struct i2c_driver adp1653_i2c_dri
|
|
.id_table = adp1653_id_table,
|
|
};
|
|
|
|
-static int __init adp1653_init(void)
|
|
-{
|
|
- int rval;
|
|
-
|
|
- rval = i2c_add_driver(&adp1653_i2c_driver);
|
|
- if (rval)
|
|
- printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__);
|
|
-
|
|
- return rval;
|
|
-}
|
|
-
|
|
-static void __exit adp1653_exit(void)
|
|
-{
|
|
- i2c_del_driver(&adp1653_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(adp1653_init);
|
|
-module_exit(adp1653_exit);
|
|
+module_i2c_driver(adp1653_i2c_driver);
|
|
|
|
MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
|
|
MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
|
|
Index: linux-3.3.x86_64/drivers/media/video/adv7170.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/adv7170.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/adv7170.c
|
|
@@ -407,15 +407,4 @@ static struct i2c_driver adv7170_driver
|
|
.id_table = adv7170_id,
|
|
};
|
|
|
|
-static __init int init_adv7170(void)
|
|
-{
|
|
- return i2c_add_driver(&adv7170_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_adv7170(void)
|
|
-{
|
|
- i2c_del_driver(&adv7170_driver);
|
|
-}
|
|
-
|
|
-module_init(init_adv7170);
|
|
-module_exit(exit_adv7170);
|
|
+module_i2c_driver(adv7170_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/adv7175.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/adv7175.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/adv7175.c
|
|
@@ -457,15 +457,4 @@ static struct i2c_driver adv7175_driver
|
|
.id_table = adv7175_id,
|
|
};
|
|
|
|
-static __init int init_adv7175(void)
|
|
-{
|
|
- return i2c_add_driver(&adv7175_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_adv7175(void)
|
|
-{
|
|
- i2c_del_driver(&adv7175_driver);
|
|
-}
|
|
-
|
|
-module_init(init_adv7175);
|
|
-module_exit(exit_adv7175);
|
|
+module_i2c_driver(adv7175_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/adv7180.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/adv7180.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/adv7180.c
|
|
@@ -444,20 +444,8 @@ static struct i2c_driver adv7180_driver
|
|
.id_table = adv7180_id,
|
|
};
|
|
|
|
-static __init int adv7180_init(void)
|
|
-{
|
|
- return i2c_add_driver(&adv7180_driver);
|
|
-}
|
|
-
|
|
-static __exit void adv7180_exit(void)
|
|
-{
|
|
- i2c_del_driver(&adv7180_driver);
|
|
-}
|
|
-
|
|
-module_init(adv7180_init);
|
|
-module_exit(adv7180_exit);
|
|
+module_i2c_driver(adv7180_driver);
|
|
|
|
MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
|
|
MODULE_AUTHOR("Mocean Laboratories");
|
|
MODULE_LICENSE("GPL v2");
|
|
-
|
|
Index: linux-3.3.x86_64/drivers/media/video/adv7343.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/adv7343.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/adv7343.c
|
|
@@ -475,15 +475,4 @@ static struct i2c_driver adv7343_driver
|
|
.id_table = adv7343_id,
|
|
};
|
|
|
|
-static __init int init_adv7343(void)
|
|
-{
|
|
- return i2c_add_driver(&adv7343_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_adv7343(void)
|
|
-{
|
|
- i2c_del_driver(&adv7343_driver);
|
|
-}
|
|
-
|
|
-module_init(init_adv7343);
|
|
-module_exit(exit_adv7343);
|
|
+module_i2c_driver(adv7343_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/ak881x.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ak881x.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ak881x.c
|
|
@@ -352,18 +352,7 @@ static struct i2c_driver ak881x_i2c_driv
|
|
.id_table = ak881x_id,
|
|
};
|
|
|
|
-static int __init ak881x_module_init(void)
|
|
-{
|
|
- return i2c_add_driver(&ak881x_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit ak881x_module_exit(void)
|
|
-{
|
|
- i2c_del_driver(&ak881x_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(ak881x_module_init);
|
|
-module_exit(ak881x_module_exit);
|
|
+module_i2c_driver(ak881x_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");
|
|
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/as3645a.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/as3645a.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/as3645a.c
|
|
@@ -881,24 +881,7 @@ static struct i2c_driver as3645a_i2c_dri
|
|
.id_table = as3645a_id_table,
|
|
};
|
|
|
|
-static int __init as3645a_init(void)
|
|
-{
|
|
- int rval;
|
|
-
|
|
- rval = i2c_add_driver(&as3645a_i2c_driver);
|
|
- if (rval)
|
|
- pr_err("%s: Failed to register the driver\n", AS3645A_NAME);
|
|
-
|
|
- return rval;
|
|
-}
|
|
-
|
|
-static void __exit as3645a_exit(void)
|
|
-{
|
|
- i2c_del_driver(&as3645a_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(as3645a_init);
|
|
-module_exit(as3645a_exit);
|
|
+module_i2c_driver(as3645a_i2c_driver);
|
|
|
|
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
|
|
MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones");
|
|
Index: linux-3.3.x86_64/drivers/media/video/bt819.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/bt819.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/bt819.c
|
|
@@ -514,15 +514,4 @@ static struct i2c_driver bt819_driver =
|
|
.id_table = bt819_id,
|
|
};
|
|
|
|
-static __init int init_bt819(void)
|
|
-{
|
|
- return i2c_add_driver(&bt819_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_bt819(void)
|
|
-{
|
|
- i2c_del_driver(&bt819_driver);
|
|
-}
|
|
-
|
|
-module_init(init_bt819);
|
|
-module_exit(exit_bt819);
|
|
+module_i2c_driver(bt819_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/bt856.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/bt856.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/bt856.c
|
|
@@ -270,15 +270,4 @@ static struct i2c_driver bt856_driver =
|
|
.id_table = bt856_id,
|
|
};
|
|
|
|
-static __init int init_bt856(void)
|
|
-{
|
|
- return i2c_add_driver(&bt856_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_bt856(void)
|
|
-{
|
|
- i2c_del_driver(&bt856_driver);
|
|
-}
|
|
-
|
|
-module_init(init_bt856);
|
|
-module_exit(exit_bt856);
|
|
+module_i2c_driver(bt856_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/bt866.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/bt866.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/bt866.c
|
|
@@ -240,15 +240,4 @@ static struct i2c_driver bt866_driver =
|
|
.id_table = bt866_id,
|
|
};
|
|
|
|
-static __init int init_bt866(void)
|
|
-{
|
|
- return i2c_add_driver(&bt866_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_bt866(void)
|
|
-{
|
|
- i2c_del_driver(&bt866_driver);
|
|
-}
|
|
-
|
|
-module_init(init_bt866);
|
|
-module_exit(exit_bt866);
|
|
+module_i2c_driver(bt866_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/cs5345.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cs5345.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cs5345.c
|
|
@@ -249,15 +249,4 @@ static struct i2c_driver cs5345_driver =
|
|
.id_table = cs5345_id,
|
|
};
|
|
|
|
-static __init int init_cs5345(void)
|
|
-{
|
|
- return i2c_add_driver(&cs5345_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_cs5345(void)
|
|
-{
|
|
- i2c_del_driver(&cs5345_driver);
|
|
-}
|
|
-
|
|
-module_init(init_cs5345);
|
|
-module_exit(exit_cs5345);
|
|
+module_i2c_driver(cs5345_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/cs53l32a.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cs53l32a.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cs53l32a.c
|
|
@@ -248,15 +248,4 @@ static struct i2c_driver cs53l32a_driver
|
|
.id_table = cs53l32a_id,
|
|
};
|
|
|
|
-static __init int init_cs53l32a(void)
|
|
-{
|
|
- return i2c_add_driver(&cs53l32a_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_cs53l32a(void)
|
|
-{
|
|
- i2c_del_driver(&cs53l32a_driver);
|
|
-}
|
|
-
|
|
-module_init(init_cs53l32a);
|
|
-module_exit(exit_cs53l32a);
|
|
+module_i2c_driver(cs53l32a_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/cx25840/cx25840-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cx25840/cx25840-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cx25840/cx25840-core.c
|
|
@@ -5301,15 +5301,4 @@ static struct i2c_driver cx25840_driver
|
|
.id_table = cx25840_id,
|
|
};
|
|
|
|
-static __init int init_cx25840(void)
|
|
-{
|
|
- return i2c_add_driver(&cx25840_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_cx25840(void)
|
|
-{
|
|
- i2c_del_driver(&cx25840_driver);
|
|
-}
|
|
-
|
|
-module_init(init_cx25840);
|
|
-module_exit(exit_cx25840);
|
|
+module_i2c_driver(cx25840_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/imx074.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/imx074.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/imx074.c
|
|
@@ -468,18 +468,7 @@ static struct i2c_driver imx074_i2c_driv
|
|
.id_table = imx074_id,
|
|
};
|
|
|
|
-static int __init imx074_mod_init(void)
|
|
-{
|
|
- return i2c_add_driver(&imx074_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit imx074_mod_exit(void)
|
|
-{
|
|
- i2c_del_driver(&imx074_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(imx074_mod_init);
|
|
-module_exit(imx074_mod_exit);
|
|
+module_i2c_driver(imx074_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Sony IMX074 Camera driver");
|
|
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/indycam.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/indycam.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/indycam.c
|
|
@@ -387,15 +387,4 @@ static struct i2c_driver indycam_driver
|
|
.id_table = indycam_id,
|
|
};
|
|
|
|
-static __init int init_indycam(void)
|
|
-{
|
|
- return i2c_add_driver(&indycam_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_indycam(void)
|
|
-{
|
|
- i2c_del_driver(&indycam_driver);
|
|
-}
|
|
-
|
|
-module_init(init_indycam);
|
|
-module_exit(exit_indycam);
|
|
+module_i2c_driver(indycam_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/ir-kbd-i2c.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ir-kbd-i2c.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ir-kbd-i2c.c
|
|
@@ -471,7 +471,7 @@ static const struct i2c_device_id ir_kbd
|
|
{ }
|
|
};
|
|
|
|
-static struct i2c_driver driver = {
|
|
+static struct i2c_driver ir_kbd_driver = {
|
|
.driver = {
|
|
.name = "ir-kbd-i2c",
|
|
},
|
|
@@ -480,21 +480,10 @@ static struct i2c_driver driver = {
|
|
.id_table = ir_kbd_id,
|
|
};
|
|
|
|
+module_i2c_driver(ir_kbd_driver);
|
|
+
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller");
|
|
MODULE_DESCRIPTION("input driver for i2c IR remote controls");
|
|
MODULE_LICENSE("GPL");
|
|
-
|
|
-static int __init ir_init(void)
|
|
-{
|
|
- return i2c_add_driver(&driver);
|
|
-}
|
|
-
|
|
-static void __exit ir_fini(void)
|
|
-{
|
|
- i2c_del_driver(&driver);
|
|
-}
|
|
-
|
|
-module_init(ir_init);
|
|
-module_exit(ir_fini);
|
|
Index: linux-3.3.x86_64/drivers/media/video/ks0127.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ks0127.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ks0127.c
|
|
@@ -721,15 +721,4 @@ static struct i2c_driver ks0127_driver =
|
|
.id_table = ks0127_id,
|
|
};
|
|
|
|
-static __init int init_ks0127(void)
|
|
-{
|
|
- return i2c_add_driver(&ks0127_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_ks0127(void)
|
|
-{
|
|
- i2c_del_driver(&ks0127_driver);
|
|
-}
|
|
-
|
|
-module_init(init_ks0127);
|
|
-module_exit(exit_ks0127);
|
|
+module_i2c_driver(ks0127_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/m52790.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/m52790.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/m52790.c
|
|
@@ -213,15 +213,4 @@ static struct i2c_driver m52790_driver =
|
|
.id_table = m52790_id,
|
|
};
|
|
|
|
-static __init int init_m52790(void)
|
|
-{
|
|
- return i2c_add_driver(&m52790_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_m52790(void)
|
|
-{
|
|
- i2c_del_driver(&m52790_driver);
|
|
-}
|
|
-
|
|
-module_init(init_m52790);
|
|
-module_exit(exit_m52790);
|
|
+module_i2c_driver(m52790_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/msp3400-driver.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/msp3400-driver.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/msp3400-driver.c
|
|
@@ -881,18 +881,7 @@ static struct i2c_driver msp_driver = {
|
|
.id_table = msp_id,
|
|
};
|
|
|
|
-static __init int init_msp(void)
|
|
-{
|
|
- return i2c_add_driver(&msp_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_msp(void)
|
|
-{
|
|
- i2c_del_driver(&msp_driver);
|
|
-}
|
|
-
|
|
-module_init(init_msp);
|
|
-module_exit(exit_msp);
|
|
+module_i2c_driver(msp_driver);
|
|
|
|
/*
|
|
* Overrides for Emacs so that we follow Linus's tabbing style.
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9m001.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mt9m001.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9m001.c
|
|
@@ -730,18 +730,7 @@ static struct i2c_driver mt9m001_i2c_dri
|
|
.id_table = mt9m001_id,
|
|
};
|
|
|
|
-static int __init mt9m001_mod_init(void)
|
|
-{
|
|
- return i2c_add_driver(&mt9m001_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit mt9m001_mod_exit(void)
|
|
-{
|
|
- i2c_del_driver(&mt9m001_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(mt9m001_mod_init);
|
|
-module_exit(mt9m001_mod_exit);
|
|
+module_i2c_driver(mt9m001_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Micron MT9M001 Camera driver");
|
|
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9m111.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mt9m111.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9m111.c
|
|
@@ -1008,18 +1008,7 @@ static struct i2c_driver mt9m111_i2c_dri
|
|
.id_table = mt9m111_id,
|
|
};
|
|
|
|
-static int __init mt9m111_mod_init(void)
|
|
-{
|
|
- return i2c_add_driver(&mt9m111_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit mt9m111_mod_exit(void)
|
|
-{
|
|
- i2c_del_driver(&mt9m111_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(mt9m111_mod_init);
|
|
-module_exit(mt9m111_mod_exit);
|
|
+module_i2c_driver(mt9m111_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
|
|
MODULE_AUTHOR("Robert Jarzmik");
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9p031.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mt9p031.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9p031.c
|
|
@@ -19,7 +19,6 @@
|
|
#include <linux/log2.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/slab.h>
|
|
-#include <media/v4l2-subdev.h>
|
|
#include <linux/videodev2.h>
|
|
|
|
#include <media/mt9p031.h>
|
|
@@ -28,6 +27,8 @@
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-subdev.h>
|
|
|
|
+#include "aptina-pll.h"
|
|
+
|
|
#define MT9P031_PIXEL_ARRAY_WIDTH 2752
|
|
#define MT9P031_PIXEL_ARRAY_HEIGHT 2004
|
|
|
|
@@ -98,14 +99,6 @@
|
|
#define MT9P031_TEST_PATTERN_RED 0xa2
|
|
#define MT9P031_TEST_PATTERN_BLUE 0xa3
|
|
|
|
-struct mt9p031_pll_divs {
|
|
- u32 ext_freq;
|
|
- u32 target_freq;
|
|
- u8 m;
|
|
- u8 n;
|
|
- u8 p1;
|
|
-};
|
|
-
|
|
struct mt9p031 {
|
|
struct v4l2_subdev subdev;
|
|
struct media_pad pad;
|
|
@@ -115,10 +108,8 @@ struct mt9p031 {
|
|
struct mt9p031_platform_data *pdata;
|
|
struct mutex power_lock; /* lock to protect power_count */
|
|
int power_count;
|
|
- u16 xskip;
|
|
- u16 yskip;
|
|
|
|
- const struct mt9p031_pll_divs *pll;
|
|
+ struct aptina_pll pll;
|
|
|
|
/* Registers cache */
|
|
u16 output_control;
|
|
@@ -186,33 +177,31 @@ static int mt9p031_reset(struct mt9p031
|
|
0);
|
|
}
|
|
|
|
-/*
|
|
- * This static table uses ext_freq and vdd_io values to select suitable
|
|
- * PLL dividers m, n and p1 which have been calculated as specifiec in p36
|
|
- * of Aptina's mt9p031 datasheet. New values should be added here.
|
|
- */
|
|
-static const struct mt9p031_pll_divs mt9p031_divs[] = {
|
|
- /* ext_freq target_freq m n p1 */
|
|
- {21000000, 48000000, 26, 2, 6}
|
|
-};
|
|
-
|
|
-static int mt9p031_pll_get_divs(struct mt9p031 *mt9p031)
|
|
+static int mt9p031_pll_setup(struct mt9p031 *mt9p031)
|
|
{
|
|
+ static const struct aptina_pll_limits limits = {
|
|
+ .ext_clock_min = 6000000,
|
|
+ .ext_clock_max = 27000000,
|
|
+ .int_clock_min = 2000000,
|
|
+ .int_clock_max = 13500000,
|
|
+ .out_clock_min = 180000000,
|
|
+ .out_clock_max = 360000000,
|
|
+ .pix_clock_max = 96000000,
|
|
+ .n_min = 1,
|
|
+ .n_max = 64,
|
|
+ .m_min = 16,
|
|
+ .m_max = 255,
|
|
+ .p1_min = 1,
|
|
+ .p1_max = 128,
|
|
+ };
|
|
+
|
|
struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
|
|
- int i;
|
|
+ struct mt9p031_platform_data *pdata = mt9p031->pdata;
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(mt9p031_divs); i++) {
|
|
- if (mt9p031_divs[i].ext_freq == mt9p031->pdata->ext_freq &&
|
|
- mt9p031_divs[i].target_freq == mt9p031->pdata->target_freq) {
|
|
- mt9p031->pll = &mt9p031_divs[i];
|
|
- return 0;
|
|
- }
|
|
- }
|
|
+ mt9p031->pll.ext_clock = pdata->ext_freq;
|
|
+ mt9p031->pll.pix_clock = pdata->target_freq;
|
|
|
|
- dev_err(&client->dev, "Couldn't find PLL dividers for ext_freq = %d, "
|
|
- "target_freq = %d\n", mt9p031->pdata->ext_freq,
|
|
- mt9p031->pdata->target_freq);
|
|
- return -EINVAL;
|
|
+ return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
|
|
}
|
|
|
|
static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
|
|
@@ -226,11 +215,11 @@ static int mt9p031_pll_enable(struct mt9
|
|
return ret;
|
|
|
|
ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
|
|
- (mt9p031->pll->m << 8) | (mt9p031->pll->n - 1));
|
|
+ (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1));
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
- ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll->p1 - 1);
|
|
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
@@ -785,8 +774,6 @@ static int mt9p031_open(struct v4l2_subd
|
|
format->field = V4L2_FIELD_NONE;
|
|
format->colorspace = V4L2_COLORSPACE_SRGB;
|
|
|
|
- mt9p031->xskip = 1;
|
|
- mt9p031->yskip = 1;
|
|
return mt9p031_set_power(subdev, 1);
|
|
}
|
|
|
|
@@ -905,7 +892,7 @@ static int mt9p031_probe(struct i2c_clie
|
|
mt9p031->format.field = V4L2_FIELD_NONE;
|
|
mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
|
|
|
|
- ret = mt9p031_pll_get_divs(mt9p031);
|
|
+ ret = mt9p031_pll_setup(mt9p031);
|
|
|
|
done:
|
|
if (ret < 0) {
|
|
@@ -945,18 +932,7 @@ static struct i2c_driver mt9p031_i2c_dri
|
|
.id_table = mt9p031_id,
|
|
};
|
|
|
|
-static int __init mt9p031_mod_init(void)
|
|
-{
|
|
- return i2c_add_driver(&mt9p031_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit mt9p031_mod_exit(void)
|
|
-{
|
|
- i2c_del_driver(&mt9p031_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(mt9p031_mod_init);
|
|
-module_exit(mt9p031_mod_exit);
|
|
+module_i2c_driver(mt9p031_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
|
|
MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9t001.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mt9t001.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9t001.c
|
|
@@ -817,18 +817,7 @@ static struct i2c_driver mt9t001_driver
|
|
.id_table = mt9t001_id,
|
|
};
|
|
|
|
-static int __init mt9t001_init(void)
|
|
-{
|
|
- return i2c_add_driver(&mt9t001_driver);
|
|
-}
|
|
-
|
|
-static void __exit mt9t001_exit(void)
|
|
-{
|
|
- i2c_del_driver(&mt9t001_driver);
|
|
-}
|
|
-
|
|
-module_init(mt9t001_init);
|
|
-module_exit(mt9t001_exit);
|
|
+module_i2c_driver(mt9t001_driver);
|
|
|
|
MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver");
|
|
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9t031.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mt9t031.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9t031.c
|
|
@@ -850,18 +850,7 @@ static struct i2c_driver mt9t031_i2c_dri
|
|
.id_table = mt9t031_id,
|
|
};
|
|
|
|
-static int __init mt9t031_mod_init(void)
|
|
-{
|
|
- return i2c_add_driver(&mt9t031_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit mt9t031_mod_exit(void)
|
|
-{
|
|
- i2c_del_driver(&mt9t031_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(mt9t031_mod_init);
|
|
-module_exit(mt9t031_mod_exit);
|
|
+module_i2c_driver(mt9t031_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
|
|
MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9t112.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mt9t112.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9t112.c
|
|
@@ -1117,21 +1117,7 @@ static struct i2c_driver mt9t112_i2c_dri
|
|
.id_table = mt9t112_id,
|
|
};
|
|
|
|
-/************************************************************************
|
|
- module function
|
|
-************************************************************************/
|
|
-static int __init mt9t112_module_init(void)
|
|
-{
|
|
- return i2c_add_driver(&mt9t112_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit mt9t112_module_exit(void)
|
|
-{
|
|
- i2c_del_driver(&mt9t112_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(mt9t112_module_init);
|
|
-module_exit(mt9t112_module_exit);
|
|
+module_i2c_driver(mt9t112_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("SoC Camera driver for mt9t112");
|
|
MODULE_AUTHOR("Kuninori Morimoto");
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9v011.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mt9v011.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9v011.c
|
|
@@ -709,15 +709,4 @@ static struct i2c_driver mt9v011_driver
|
|
.id_table = mt9v011_id,
|
|
};
|
|
|
|
-static __init int init_mt9v011(void)
|
|
-{
|
|
- return i2c_add_driver(&mt9v011_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_mt9v011(void)
|
|
-{
|
|
- i2c_del_driver(&mt9v011_driver);
|
|
-}
|
|
-
|
|
-module_init(init_mt9v011);
|
|
-module_exit(exit_mt9v011);
|
|
+module_i2c_driver(mt9v011_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9v022.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mt9v022.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9v022.c
|
|
@@ -872,18 +872,7 @@ static struct i2c_driver mt9v022_i2c_dri
|
|
.id_table = mt9v022_id,
|
|
};
|
|
|
|
-static int __init mt9v022_mod_init(void)
|
|
-{
|
|
- return i2c_add_driver(&mt9v022_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit mt9v022_mod_exit(void)
|
|
-{
|
|
- i2c_del_driver(&mt9v022_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(mt9v022_mod_init);
|
|
-module_exit(mt9v022_mod_exit);
|
|
+module_i2c_driver(mt9v022_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Micron MT9V022 Camera driver");
|
|
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9v032.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mt9v032.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9v032.c
|
|
@@ -756,18 +756,7 @@ static struct i2c_driver mt9v032_driver
|
|
.id_table = mt9v032_id,
|
|
};
|
|
|
|
-static int __init mt9v032_init(void)
|
|
-{
|
|
- return i2c_add_driver(&mt9v032_driver);
|
|
-}
|
|
-
|
|
-static void __exit mt9v032_exit(void)
|
|
-{
|
|
- i2c_del_driver(&mt9v032_driver);
|
|
-}
|
|
-
|
|
-module_init(mt9v032_init);
|
|
-module_exit(mt9v032_exit);
|
|
+module_i2c_driver(mt9v032_driver);
|
|
|
|
MODULE_DESCRIPTION("Aptina MT9V032 Camera driver");
|
|
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/ov2640.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ov2640.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ov2640.c
|
|
@@ -1103,21 +1103,7 @@ static struct i2c_driver ov2640_i2c_driv
|
|
.id_table = ov2640_id,
|
|
};
|
|
|
|
-/*
|
|
- * Module functions
|
|
- */
|
|
-static int __init ov2640_module_init(void)
|
|
-{
|
|
- return i2c_add_driver(&ov2640_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit ov2640_module_exit(void)
|
|
-{
|
|
- i2c_del_driver(&ov2640_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(ov2640_module_init);
|
|
-module_exit(ov2640_module_exit);
|
|
+module_i2c_driver(ov2640_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor");
|
|
MODULE_AUTHOR("Alberto Panizzo");
|
|
Index: linux-3.3.x86_64/drivers/media/video/ov5642.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ov5642.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ov5642.c
|
|
@@ -1068,18 +1068,7 @@ static struct i2c_driver ov5642_i2c_driv
|
|
.id_table = ov5642_id,
|
|
};
|
|
|
|
-static int __init ov5642_mod_init(void)
|
|
-{
|
|
- return i2c_add_driver(&ov5642_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit ov5642_mod_exit(void)
|
|
-{
|
|
- i2c_del_driver(&ov5642_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(ov5642_mod_init);
|
|
-module_exit(ov5642_mod_exit);
|
|
+module_i2c_driver(ov5642_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Omnivision OV5642 Camera driver");
|
|
MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/ov7670.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ov7670.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ov7670.c
|
|
@@ -1583,15 +1583,4 @@ static struct i2c_driver ov7670_driver =
|
|
.id_table = ov7670_id,
|
|
};
|
|
|
|
-static __init int init_ov7670(void)
|
|
-{
|
|
- return i2c_add_driver(&ov7670_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_ov7670(void)
|
|
-{
|
|
- i2c_del_driver(&ov7670_driver);
|
|
-}
|
|
-
|
|
-module_init(init_ov7670);
|
|
-module_exit(exit_ov7670);
|
|
+module_i2c_driver(ov7670_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/ov772x.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ov772x.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ov772x.c
|
|
@@ -1123,22 +1123,7 @@ static struct i2c_driver ov772x_i2c_driv
|
|
.id_table = ov772x_id,
|
|
};
|
|
|
|
-/*
|
|
- * module function
|
|
- */
|
|
-
|
|
-static int __init ov772x_module_init(void)
|
|
-{
|
|
- return i2c_add_driver(&ov772x_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit ov772x_module_exit(void)
|
|
-{
|
|
- i2c_del_driver(&ov772x_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(ov772x_module_init);
|
|
-module_exit(ov772x_module_exit);
|
|
+module_i2c_driver(ov772x_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("SoC Camera driver for ov772x");
|
|
MODULE_AUTHOR("Kuninori Morimoto");
|
|
Index: linux-3.3.x86_64/drivers/media/video/ov9640.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ov9640.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ov9640.c
|
|
@@ -738,18 +738,7 @@ static struct i2c_driver ov9640_i2c_driv
|
|
.id_table = ov9640_id,
|
|
};
|
|
|
|
-static int __init ov9640_module_init(void)
|
|
-{
|
|
- return i2c_add_driver(&ov9640_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit ov9640_module_exit(void)
|
|
-{
|
|
- i2c_del_driver(&ov9640_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(ov9640_module_init);
|
|
-module_exit(ov9640_module_exit);
|
|
+module_i2c_driver(ov9640_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx");
|
|
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/ov9740.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ov9740.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ov9740.c
|
|
@@ -998,18 +998,7 @@ static struct i2c_driver ov9740_i2c_driv
|
|
.id_table = ov9740_id,
|
|
};
|
|
|
|
-static int __init ov9740_module_init(void)
|
|
-{
|
|
- return i2c_add_driver(&ov9740_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit ov9740_module_exit(void)
|
|
-{
|
|
- i2c_del_driver(&ov9740_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(ov9740_module_init);
|
|
-module_exit(ov9740_module_exit);
|
|
+module_i2c_driver(ov9740_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740");
|
|
MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/rj54n1cb0c.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/rj54n1cb0c.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/rj54n1cb0c.c
|
|
@@ -1407,18 +1407,7 @@ static struct i2c_driver rj54n1_i2c_driv
|
|
.id_table = rj54n1_id,
|
|
};
|
|
|
|
-static int __init rj54n1_mod_init(void)
|
|
-{
|
|
- return i2c_add_driver(&rj54n1_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit rj54n1_mod_exit(void)
|
|
-{
|
|
- i2c_del_driver(&rj54n1_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(rj54n1_mod_init);
|
|
-module_exit(rj54n1_mod_exit);
|
|
+module_i2c_driver(rj54n1_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver");
|
|
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/s5p-tv/hdmiphy_drv.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s5p-tv/hdmiphy_drv.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s5p-tv/hdmiphy_drv.c
|
|
@@ -175,14 +175,4 @@ static struct i2c_driver hdmiphy_driver
|
|
.id_table = hdmiphy_id,
|
|
};
|
|
|
|
-static int __init hdmiphy_init(void)
|
|
-{
|
|
- return i2c_add_driver(&hdmiphy_driver);
|
|
-}
|
|
-module_init(hdmiphy_init);
|
|
-
|
|
-static void __exit hdmiphy_exit(void)
|
|
-{
|
|
- i2c_del_driver(&hdmiphy_driver);
|
|
-}
|
|
-module_exit(hdmiphy_exit);
|
|
+module_i2c_driver(hdmiphy_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa6588.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa6588.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa6588.c
|
|
@@ -539,15 +539,4 @@ static struct i2c_driver saa6588_driver
|
|
.id_table = saa6588_id,
|
|
};
|
|
|
|
-static __init int init_saa6588(void)
|
|
-{
|
|
- return i2c_add_driver(&saa6588_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_saa6588(void)
|
|
-{
|
|
- i2c_del_driver(&saa6588_driver);
|
|
-}
|
|
-
|
|
-module_init(init_saa6588);
|
|
-module_exit(exit_saa6588);
|
|
+module_i2c_driver(saa6588_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7110.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7110.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7110.c
|
|
@@ -491,15 +491,4 @@ static struct i2c_driver saa7110_driver
|
|
.id_table = saa7110_id,
|
|
};
|
|
|
|
-static __init int init_saa7110(void)
|
|
-{
|
|
- return i2c_add_driver(&saa7110_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_saa7110(void)
|
|
-{
|
|
- i2c_del_driver(&saa7110_driver);
|
|
-}
|
|
-
|
|
-module_init(init_saa7110);
|
|
-module_exit(exit_saa7110);
|
|
+module_i2c_driver(saa7110_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7115.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7115.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7115.c
|
|
@@ -1724,15 +1724,4 @@ static struct i2c_driver saa711x_driver
|
|
.id_table = saa711x_id,
|
|
};
|
|
|
|
-static __init int init_saa711x(void)
|
|
-{
|
|
- return i2c_add_driver(&saa711x_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_saa711x(void)
|
|
-{
|
|
- i2c_del_driver(&saa711x_driver);
|
|
-}
|
|
-
|
|
-module_init(init_saa711x);
|
|
-module_exit(exit_saa711x);
|
|
+module_i2c_driver(saa711x_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7127.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7127.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7127.c
|
|
@@ -852,15 +852,4 @@ static struct i2c_driver saa7127_driver
|
|
.id_table = saa7127_id,
|
|
};
|
|
|
|
-static __init int init_saa7127(void)
|
|
-{
|
|
- return i2c_add_driver(&saa7127_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_saa7127(void)
|
|
-{
|
|
- i2c_del_driver(&saa7127_driver);
|
|
-}
|
|
-
|
|
-module_init(init_saa7127);
|
|
-module_exit(exit_saa7127);
|
|
+module_i2c_driver(saa7127_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7134/saa6752hs.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7134/saa6752hs.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7134/saa6752hs.c
|
|
@@ -1001,18 +1001,7 @@ static struct i2c_driver saa6752hs_drive
|
|
.id_table = saa6752hs_id,
|
|
};
|
|
|
|
-static __init int init_saa6752hs(void)
|
|
-{
|
|
- return i2c_add_driver(&saa6752hs_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_saa6752hs(void)
|
|
-{
|
|
- i2c_del_driver(&saa6752hs_driver);
|
|
-}
|
|
-
|
|
-module_init(init_saa6752hs);
|
|
-module_exit(exit_saa6752hs);
|
|
+module_i2c_driver(saa6752hs_driver);
|
|
|
|
/*
|
|
* Overrides for Emacs so that we follow Linus's tabbing style.
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa717x.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa717x.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa717x.c
|
|
@@ -1375,15 +1375,4 @@ static struct i2c_driver saa717x_driver
|
|
.id_table = saa717x_id,
|
|
};
|
|
|
|
-static __init int init_saa717x(void)
|
|
-{
|
|
- return i2c_add_driver(&saa717x_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_saa717x(void)
|
|
-{
|
|
- i2c_del_driver(&saa717x_driver);
|
|
-}
|
|
-
|
|
-module_init(init_saa717x);
|
|
-module_exit(exit_saa717x);
|
|
+module_i2c_driver(saa717x_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7185.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7185.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7185.c
|
|
@@ -374,15 +374,4 @@ static struct i2c_driver saa7185_driver
|
|
.id_table = saa7185_id,
|
|
};
|
|
|
|
-static __init int init_saa7185(void)
|
|
-{
|
|
- return i2c_add_driver(&saa7185_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_saa7185(void)
|
|
-{
|
|
- i2c_del_driver(&saa7185_driver);
|
|
-}
|
|
-
|
|
-module_init(init_saa7185);
|
|
-module_exit(exit_saa7185);
|
|
+module_i2c_driver(saa7185_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7191.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7191.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7191.c
|
|
@@ -656,15 +656,4 @@ static struct i2c_driver saa7191_driver
|
|
.id_table = saa7191_id,
|
|
};
|
|
|
|
-static __init int init_saa7191(void)
|
|
-{
|
|
- return i2c_add_driver(&saa7191_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_saa7191(void)
|
|
-{
|
|
- i2c_del_driver(&saa7191_driver);
|
|
-}
|
|
-
|
|
-module_init(init_saa7191);
|
|
-module_exit(exit_saa7191);
|
|
+module_i2c_driver(saa7191_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/sr030pc30.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/sr030pc30.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/sr030pc30.c
|
|
@@ -864,18 +864,7 @@ static struct i2c_driver sr030pc30_i2c_d
|
|
.id_table = sr030pc30_id,
|
|
};
|
|
|
|
-static int __init sr030pc30_init(void)
|
|
-{
|
|
- return i2c_add_driver(&sr030pc30_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit sr030pc30_exit(void)
|
|
-{
|
|
- i2c_del_driver(&sr030pc30_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(sr030pc30_init);
|
|
-module_exit(sr030pc30_exit);
|
|
+module_i2c_driver(sr030pc30_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver");
|
|
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/tda7432.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tda7432.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tda7432.c
|
|
@@ -482,15 +482,4 @@ static struct i2c_driver tda7432_driver
|
|
.id_table = tda7432_id,
|
|
};
|
|
|
|
-static __init int init_tda7432(void)
|
|
-{
|
|
- return i2c_add_driver(&tda7432_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_tda7432(void)
|
|
-{
|
|
- i2c_del_driver(&tda7432_driver);
|
|
-}
|
|
-
|
|
-module_init(init_tda7432);
|
|
-module_exit(exit_tda7432);
|
|
+module_i2c_driver(tda7432_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/tda9840.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tda9840.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tda9840.c
|
|
@@ -208,15 +208,4 @@ static struct i2c_driver tda9840_driver
|
|
.id_table = tda9840_id,
|
|
};
|
|
|
|
-static __init int init_tda9840(void)
|
|
-{
|
|
- return i2c_add_driver(&tda9840_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_tda9840(void)
|
|
-{
|
|
- i2c_del_driver(&tda9840_driver);
|
|
-}
|
|
-
|
|
-module_init(init_tda9840);
|
|
-module_exit(exit_tda9840);
|
|
+module_i2c_driver(tda9840_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/tea6415c.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tea6415c.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tea6415c.c
|
|
@@ -184,15 +184,4 @@ static struct i2c_driver tea6415c_driver
|
|
.id_table = tea6415c_id,
|
|
};
|
|
|
|
-static __init int init_tea6415c(void)
|
|
-{
|
|
- return i2c_add_driver(&tea6415c_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_tea6415c(void)
|
|
-{
|
|
- i2c_del_driver(&tea6415c_driver);
|
|
-}
|
|
-
|
|
-module_init(init_tea6415c);
|
|
-module_exit(exit_tea6415c);
|
|
+module_i2c_driver(tea6415c_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/tea6420.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tea6420.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tea6420.c
|
|
@@ -166,15 +166,4 @@ static struct i2c_driver tea6420_driver
|
|
.id_table = tea6420_id,
|
|
};
|
|
|
|
-static __init int init_tea6420(void)
|
|
-{
|
|
- return i2c_add_driver(&tea6420_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_tea6420(void)
|
|
-{
|
|
- i2c_del_driver(&tea6420_driver);
|
|
-}
|
|
-
|
|
-module_init(init_tea6420);
|
|
-module_exit(exit_tea6420);
|
|
+module_i2c_driver(tea6420_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/ths7303.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ths7303.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ths7303.c
|
|
@@ -137,16 +137,4 @@ static struct i2c_driver ths7303_driver
|
|
.id_table = ths7303_id,
|
|
};
|
|
|
|
-static int __init ths7303_init(void)
|
|
-{
|
|
- return i2c_add_driver(&ths7303_driver);
|
|
-}
|
|
-
|
|
-static void __exit ths7303_exit(void)
|
|
-{
|
|
- i2c_del_driver(&ths7303_driver);
|
|
-}
|
|
-
|
|
-module_init(ths7303_init);
|
|
-module_exit(ths7303_exit);
|
|
-
|
|
+module_i2c_driver(ths7303_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/tlv320aic23b.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tlv320aic23b.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tlv320aic23b.c
|
|
@@ -227,15 +227,4 @@ static struct i2c_driver tlv320aic23b_dr
|
|
.id_table = tlv320aic23b_id,
|
|
};
|
|
|
|
-static __init int init_tlv320aic23b(void)
|
|
-{
|
|
- return i2c_add_driver(&tlv320aic23b_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_tlv320aic23b(void)
|
|
-{
|
|
- i2c_del_driver(&tlv320aic23b_driver);
|
|
-}
|
|
-
|
|
-module_init(init_tlv320aic23b);
|
|
-module_exit(exit_tlv320aic23b);
|
|
+module_i2c_driver(tlv320aic23b_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/tvaudio.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tvaudio.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tvaudio.c
|
|
@@ -2078,15 +2078,4 @@ static struct i2c_driver tvaudio_driver
|
|
.id_table = tvaudio_id,
|
|
};
|
|
|
|
-static __init int init_tvaudio(void)
|
|
-{
|
|
- return i2c_add_driver(&tvaudio_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_tvaudio(void)
|
|
-{
|
|
- i2c_del_driver(&tvaudio_driver);
|
|
-}
|
|
-
|
|
-module_init(init_tvaudio);
|
|
-module_exit(exit_tvaudio);
|
|
+module_i2c_driver(tvaudio_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/tvp514x.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tvp514x.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tvp514x.c
|
|
@@ -1163,15 +1163,4 @@ static struct i2c_driver tvp514x_driver
|
|
.id_table = tvp514x_id,
|
|
};
|
|
|
|
-static int __init tvp514x_init(void)
|
|
-{
|
|
- return i2c_add_driver(&tvp514x_driver);
|
|
-}
|
|
-
|
|
-static void __exit tvp514x_exit(void)
|
|
-{
|
|
- i2c_del_driver(&tvp514x_driver);
|
|
-}
|
|
-
|
|
-module_init(tvp514x_init);
|
|
-module_exit(tvp514x_exit);
|
|
+module_i2c_driver(tvp514x_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/tvp5150.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tvp5150.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tvp5150.c
|
|
@@ -17,6 +17,13 @@
|
|
|
|
#include "tvp5150_reg.h"
|
|
|
|
+#define TVP5150_H_MAX 720
|
|
+#define TVP5150_V_MAX_525_60 480
|
|
+#define TVP5150_V_MAX_OTHERS 576
|
|
+#define TVP5150_MAX_CROP_LEFT 511
|
|
+#define TVP5150_MAX_CROP_TOP 127
|
|
+#define TVP5150_CROP_SHIFT 2
|
|
+
|
|
MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver");
|
|
MODULE_AUTHOR("Mauro Carvalho Chehab");
|
|
MODULE_LICENSE("GPL");
|
|
@@ -29,6 +36,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-
|
|
struct tvp5150 {
|
|
struct v4l2_subdev sd;
|
|
struct v4l2_ctrl_handler hdl;
|
|
+ struct v4l2_rect rect;
|
|
|
|
v4l2_std_id norm; /* Current set standard */
|
|
u32 input;
|
|
@@ -732,6 +740,13 @@ static int tvp5150_s_std(struct v4l2_sub
|
|
if (decoder->norm == std)
|
|
return 0;
|
|
|
|
+ /* Change cropping height limits */
|
|
+ if (std & V4L2_STD_525_60)
|
|
+ decoder->rect.height = TVP5150_V_MAX_525_60;
|
|
+ else
|
|
+ decoder->rect.height = TVP5150_V_MAX_OTHERS;
|
|
+
|
|
+
|
|
return tvp5150_set_std(sd, std);
|
|
}
|
|
|
|
@@ -828,11 +843,8 @@ static int tvp5150_mbus_fmt(struct v4l2_
|
|
else
|
|
std = decoder->norm;
|
|
|
|
- f->width = 720;
|
|
- if (std & V4L2_STD_525_60)
|
|
- f->height = 480;
|
|
- else
|
|
- f->height = 576;
|
|
+ f->width = decoder->rect.width;
|
|
+ f->height = decoder->rect.height;
|
|
|
|
f->code = V4L2_MBUS_FMT_YUYV8_2X8;
|
|
f->field = V4L2_FIELD_SEQ_TB;
|
|
@@ -843,6 +855,99 @@ static int tvp5150_mbus_fmt(struct v4l2_
|
|
return 0;
|
|
}
|
|
|
|
+static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
|
+{
|
|
+ struct v4l2_rect rect = a->c;
|
|
+ struct tvp5150 *decoder = to_tvp5150(sd);
|
|
+ v4l2_std_id std;
|
|
+ int hmax;
|
|
+
|
|
+ v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n",
|
|
+ __func__, rect.left, rect.top, rect.width, rect.height);
|
|
+
|
|
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* tvp5150 has some special limits */
|
|
+ rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
|
|
+ rect.width = clamp(rect.width,
|
|
+ TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
|
|
+ TVP5150_H_MAX - rect.left);
|
|
+ rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
|
|
+
|
|
+ /* Calculate height based on current standard */
|
|
+ if (decoder->norm == V4L2_STD_ALL)
|
|
+ std = tvp5150_read_std(sd);
|
|
+ else
|
|
+ std = decoder->norm;
|
|
+
|
|
+ if (std & V4L2_STD_525_60)
|
|
+ hmax = TVP5150_V_MAX_525_60;
|
|
+ else
|
|
+ hmax = TVP5150_V_MAX_OTHERS;
|
|
+
|
|
+ rect.height = clamp(rect.height,
|
|
+ hmax - TVP5150_MAX_CROP_TOP - rect.top,
|
|
+ hmax - rect.top);
|
|
+
|
|
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);
|
|
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP,
|
|
+ rect.top + rect.height - hmax);
|
|
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB,
|
|
+ rect.left >> TVP5150_CROP_SHIFT);
|
|
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB,
|
|
+ rect.left | (1 << TVP5150_CROP_SHIFT));
|
|
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB,
|
|
+ (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
|
|
+ TVP5150_CROP_SHIFT);
|
|
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB,
|
|
+ rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
|
|
+
|
|
+ decoder->rect = rect;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
|
+{
|
|
+ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
|
|
+
|
|
+ a->c = decoder->rect;
|
|
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
|
|
+{
|
|
+ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
|
|
+ v4l2_std_id std;
|
|
+
|
|
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ a->bounds.left = 0;
|
|
+ a->bounds.top = 0;
|
|
+ a->bounds.width = TVP5150_H_MAX;
|
|
+
|
|
+ /* Calculate height based on current standard */
|
|
+ if (decoder->norm == V4L2_STD_ALL)
|
|
+ std = tvp5150_read_std(sd);
|
|
+ else
|
|
+ std = decoder->norm;
|
|
+
|
|
+ if (std & V4L2_STD_525_60)
|
|
+ a->bounds.height = TVP5150_V_MAX_525_60;
|
|
+ else
|
|
+ a->bounds.height = TVP5150_V_MAX_OTHERS;
|
|
+
|
|
+ a->defrect = a->bounds;
|
|
+ a->pixelaspect.numerator = 1;
|
|
+ a->pixelaspect.denominator = 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/****************************************************************************
|
|
I2C Command
|
|
****************************************************************************/
|
|
@@ -998,6 +1103,10 @@ static const struct v4l2_subdev_video_op
|
|
.enum_mbus_fmt = tvp5150_enum_mbus_fmt,
|
|
.s_mbus_fmt = tvp5150_mbus_fmt,
|
|
.try_mbus_fmt = tvp5150_mbus_fmt,
|
|
+ .g_mbus_fmt = tvp5150_mbus_fmt,
|
|
+ .s_crop = tvp5150_s_crop,
|
|
+ .g_crop = tvp5150_g_crop,
|
|
+ .cropcap = tvp5150_cropcap,
|
|
};
|
|
|
|
static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
|
|
@@ -1083,6 +1192,15 @@ static int tvp5150_probe(struct i2c_clie
|
|
}
|
|
v4l2_ctrl_handler_setup(&core->hdl);
|
|
|
|
+ /* Default is no cropping */
|
|
+ core->rect.top = 0;
|
|
+ if (tvp5150_read_std(sd) & V4L2_STD_525_60)
|
|
+ core->rect.height = TVP5150_V_MAX_525_60;
|
|
+ else
|
|
+ core->rect.height = TVP5150_V_MAX_OTHERS;
|
|
+ core->rect.left = 0;
|
|
+ core->rect.width = TVP5150_H_MAX;
|
|
+
|
|
if (debug > 1)
|
|
tvp5150_log_status(sd);
|
|
return 0;
|
|
@@ -1121,15 +1239,4 @@ static struct i2c_driver tvp5150_driver
|
|
.id_table = tvp5150_id,
|
|
};
|
|
|
|
-static __init int init_tvp5150(void)
|
|
-{
|
|
- return i2c_add_driver(&tvp5150_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_tvp5150(void)
|
|
-{
|
|
- i2c_del_driver(&tvp5150_driver);
|
|
-}
|
|
-
|
|
-module_init(init_tvp5150);
|
|
-module_exit(exit_tvp5150);
|
|
+module_i2c_driver(tvp5150_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/tvp7002.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tvp7002.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tvp7002.c
|
|
@@ -1069,27 +1069,4 @@ static struct i2c_driver tvp7002_driver
|
|
.id_table = tvp7002_id,
|
|
};
|
|
|
|
-/*
|
|
- * tvp7002_init - Initialize driver via I2C interface
|
|
- *
|
|
- * Register the TVP7002 driver.
|
|
- * Return 0 on success or error code on failure.
|
|
- */
|
|
-static int __init tvp7002_init(void)
|
|
-{
|
|
- return i2c_add_driver(&tvp7002_driver);
|
|
-}
|
|
-
|
|
-/*
|
|
- * tvp7002_exit - Remove driver via I2C interface
|
|
- *
|
|
- * Unregister the TVP7002 driver.
|
|
- * Returns nothing.
|
|
- */
|
|
-static void __exit tvp7002_exit(void)
|
|
-{
|
|
- i2c_del_driver(&tvp7002_driver);
|
|
-}
|
|
-
|
|
-module_init(tvp7002_init);
|
|
-module_exit(tvp7002_exit);
|
|
+module_i2c_driver(tvp7002_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/tw9910.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/tw9910.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/tw9910.c
|
|
@@ -951,21 +951,7 @@ static struct i2c_driver tw9910_i2c_driv
|
|
.id_table = tw9910_id,
|
|
};
|
|
|
|
-/*
|
|
- * module function
|
|
- */
|
|
-static int __init tw9910_module_init(void)
|
|
-{
|
|
- return i2c_add_driver(&tw9910_i2c_driver);
|
|
-}
|
|
-
|
|
-static void __exit tw9910_module_exit(void)
|
|
-{
|
|
- i2c_del_driver(&tw9910_i2c_driver);
|
|
-}
|
|
-
|
|
-module_init(tw9910_module_init);
|
|
-module_exit(tw9910_module_exit);
|
|
+module_i2c_driver(tw9910_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("SoC Camera driver for tw9910");
|
|
MODULE_AUTHOR("Kuninori Morimoto");
|
|
Index: linux-3.3.x86_64/drivers/media/video/upd64031a.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/upd64031a.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/upd64031a.c
|
|
@@ -271,15 +271,4 @@ static struct i2c_driver upd64031a_drive
|
|
.id_table = upd64031a_id,
|
|
};
|
|
|
|
-static __init int init_upd64031a(void)
|
|
-{
|
|
- return i2c_add_driver(&upd64031a_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_upd64031a(void)
|
|
-{
|
|
- i2c_del_driver(&upd64031a_driver);
|
|
-}
|
|
-
|
|
-module_init(init_upd64031a);
|
|
-module_exit(exit_upd64031a);
|
|
+module_i2c_driver(upd64031a_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/upd64083.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/upd64083.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/upd64083.c
|
|
@@ -243,15 +243,4 @@ static struct i2c_driver upd64083_driver
|
|
.id_table = upd64083_id,
|
|
};
|
|
|
|
-static __init int init_upd64083(void)
|
|
-{
|
|
- return i2c_add_driver(&upd64083_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_upd64083(void)
|
|
-{
|
|
- i2c_del_driver(&upd64083_driver);
|
|
-}
|
|
-
|
|
-module_init(init_upd64083);
|
|
-module_exit(exit_upd64083);
|
|
+module_i2c_driver(upd64083_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/vp27smpx.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/vp27smpx.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/vp27smpx.c
|
|
@@ -208,15 +208,4 @@ static struct i2c_driver vp27smpx_driver
|
|
.id_table = vp27smpx_id,
|
|
};
|
|
|
|
-static __init int init_vp27smpx(void)
|
|
-{
|
|
- return i2c_add_driver(&vp27smpx_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_vp27smpx(void)
|
|
-{
|
|
- i2c_del_driver(&vp27smpx_driver);
|
|
-}
|
|
-
|
|
-module_init(init_vp27smpx);
|
|
-module_exit(exit_vp27smpx);
|
|
+module_i2c_driver(vp27smpx_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/vpx3220.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/vpx3220.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/vpx3220.c
|
|
@@ -588,15 +588,4 @@ static struct i2c_driver vpx3220_driver
|
|
.id_table = vpx3220_id,
|
|
};
|
|
|
|
-static __init int init_vpx3220(void)
|
|
-{
|
|
- return i2c_add_driver(&vpx3220_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_vpx3220(void)
|
|
-{
|
|
- i2c_del_driver(&vpx3220_driver);
|
|
-}
|
|
-
|
|
-module_init(init_vpx3220);
|
|
-module_exit(exit_vpx3220);
|
|
+module_i2c_driver(vpx3220_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/wm8739.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/wm8739.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/wm8739.c
|
|
@@ -291,15 +291,4 @@ static struct i2c_driver wm8739_driver =
|
|
.id_table = wm8739_id,
|
|
};
|
|
|
|
-static __init int init_wm8739(void)
|
|
-{
|
|
- return i2c_add_driver(&wm8739_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_wm8739(void)
|
|
-{
|
|
- i2c_del_driver(&wm8739_driver);
|
|
-}
|
|
-
|
|
-module_init(init_wm8739);
|
|
-module_exit(exit_wm8739);
|
|
+module_i2c_driver(wm8739_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/video/wm8775.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/wm8775.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/wm8775.c
|
|
@@ -339,15 +339,4 @@ static struct i2c_driver wm8775_driver =
|
|
.id_table = wm8775_id,
|
|
};
|
|
|
|
-static __init int init_wm8775(void)
|
|
-{
|
|
- return i2c_add_driver(&wm8775_driver);
|
|
-}
|
|
-
|
|
-static __exit void exit_wm8775(void)
|
|
-{
|
|
- i2c_del_driver(&wm8775_driver);
|
|
-}
|
|
-
|
|
-module_init(init_wm8775);
|
|
-module_exit(exit_wm8775);
|
|
+module_i2c_driver(wm8775_driver);
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/it913x-fe-priv.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/it913x-fe-priv.h
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/it913x-fe-priv.h
|
|
@@ -201,6 +201,11 @@ fe_modulation_t fe_con[] = {
|
|
QAM_64,
|
|
};
|
|
|
|
+enum {
|
|
+ PRIORITY_HIGH = 0, /* High-priority stream */
|
|
+ PRIORITY_LOW, /* Low-priority stream */
|
|
+};
|
|
+
|
|
/* Standard demodulator functions */
|
|
static struct it913xset set_solo_fe[] = {
|
|
{PRO_LINK, GPIOH5_EN, {0x01}, 0x01},
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/it913x-fe.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/it913x-fe.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/it913x-fe.c
|
|
@@ -57,6 +57,7 @@ struct it913x_fe_state {
|
|
u32 frequency;
|
|
fe_modulation_t constellation;
|
|
fe_transmit_mode_t transmission_mode;
|
|
+ u8 priority;
|
|
u32 crystalFrequency;
|
|
u32 adcFrequency;
|
|
u8 tuner_type;
|
|
@@ -500,19 +501,87 @@ static int it913x_fe_read_status(struct
|
|
return 0;
|
|
}
|
|
|
|
+/* FEC values based on fe_code_rate_t non supported values 0*/
|
|
+int it913x_qpsk_pval[] = {0, -93, -91, -90, 0, -89, -88};
|
|
+int it913x_16qam_pval[] = {0, -87, -85, -84, 0, -83, -82};
|
|
+int it913x_64qam_pval[] = {0, -82, -80, -78, 0, -77, -76};
|
|
+
|
|
+static int it913x_get_signal_strength(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
|
|
+ struct it913x_fe_state *state = fe->demodulator_priv;
|
|
+ u8 code_rate;
|
|
+ int ret, temp;
|
|
+ u8 lna_gain_os;
|
|
+
|
|
+ ret = it913x_read_reg_u8(state, VAR_P_INBAND);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ /* VHF/UHF gain offset */
|
|
+ if (state->frequency < 300000000)
|
|
+ lna_gain_os = 7;
|
|
+ else
|
|
+ lna_gain_os = 14;
|
|
+
|
|
+ temp = (ret - 100) - lna_gain_os;
|
|
+
|
|
+ if (state->priority == PRIORITY_HIGH)
|
|
+ code_rate = p->code_rate_HP;
|
|
+ else
|
|
+ code_rate = p->code_rate_LP;
|
|
+
|
|
+ if (code_rate >= ARRAY_SIZE(it913x_qpsk_pval))
|
|
+ return -EINVAL;
|
|
+
|
|
+ deb_info("Reg VAR_P_INBAND:%d Calc Offset Value:%d", ret, temp);
|
|
+
|
|
+ /* Apply FEC offset values*/
|
|
+ switch (p->modulation) {
|
|
+ case QPSK:
|
|
+ temp -= it913x_qpsk_pval[code_rate];
|
|
+ break;
|
|
+ case QAM_16:
|
|
+ temp -= it913x_16qam_pval[code_rate];
|
|
+ break;
|
|
+ case QAM_64:
|
|
+ temp -= it913x_64qam_pval[code_rate];
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (temp < -15)
|
|
+ ret = 0;
|
|
+ else if ((-15 <= temp) && (temp < 0))
|
|
+ ret = (2 * (temp + 15)) / 3;
|
|
+ else if ((0 <= temp) && (temp < 20))
|
|
+ ret = 4 * temp + 10;
|
|
+ else if ((20 <= temp) && (temp < 35))
|
|
+ ret = (2 * (temp - 20)) / 3 + 90;
|
|
+ else if (temp >= 35)
|
|
+ ret = 100;
|
|
+
|
|
+ deb_info("Signal Strength :%d", ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static int it913x_fe_read_signal_strength(struct dvb_frontend *fe,
|
|
u16 *strength)
|
|
{
|
|
struct it913x_fe_state *state = fe->demodulator_priv;
|
|
- int ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
|
|
- /*SIGNAL_LEVEL always returns 100%! so using FE_HAS_SIGNAL as switch*/
|
|
- if (state->it913x_status & FE_HAS_SIGNAL)
|
|
- ret = (ret * 0xff) / 0x64;
|
|
- else
|
|
- ret = 0x0;
|
|
- ret |= ret << 0x8;
|
|
- *strength = ret;
|
|
- return 0;
|
|
+ int ret = 0;
|
|
+ if (state->config->read_slevel) {
|
|
+ if (state->it913x_status & FE_HAS_SIGNAL)
|
|
+ ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
|
|
+ } else
|
|
+ ret = it913x_get_signal_strength(fe);
|
|
+
|
|
+ if (ret >= 0)
|
|
+ *strength = (u16)((u32)ret * 0xffff / 0x64);
|
|
+
|
|
+ return (ret < 0) ? -ENODEV : 0;
|
|
}
|
|
|
|
static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
|
|
@@ -606,6 +675,8 @@ static int it913x_fe_get_frontend(struct
|
|
if (reg[2] < 4)
|
|
p->hierarchy = fe_hi[reg[2]];
|
|
|
|
+ state->priority = reg[5];
|
|
+
|
|
p->code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE;
|
|
p->code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE;
|
|
|
|
@@ -972,5 +1043,5 @@ static struct dvb_frontend_ops it913x_fe
|
|
|
|
MODULE_DESCRIPTION("it913x Frontend and it9137 tuner");
|
|
MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
|
|
-MODULE_VERSION("1.13");
|
|
+MODULE_VERSION("1.15");
|
|
MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/it913x-fe.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/it913x-fe.h
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/it913x-fe.h
|
|
@@ -34,6 +34,8 @@ struct ite_config {
|
|
u8 tuner_id_1;
|
|
u8 dual_mode;
|
|
u8 adf;
|
|
+ /* option to read SIGNAL_LEVEL */
|
|
+ u8 read_slevel;
|
|
};
|
|
|
|
#if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \
|
|
@@ -168,6 +170,8 @@ static inline struct dvb_frontend *it913
|
|
#define EST_SIGNAL_LEVEL 0x004a
|
|
#define FREE_BAND 0x004b
|
|
#define SUSPEND_FLAG 0x004c
|
|
+#define VAR_P_INBAND 0x00f7
|
|
+
|
|
/* Build in tuner types */
|
|
#define IT9137 0x38
|
|
#define IT9135_38 0x38
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/cx22702.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/cx22702.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/cx22702.c
|
|
@@ -502,10 +502,26 @@ static int cx22702_read_signal_strength(
|
|
u16 *signal_strength)
|
|
{
|
|
struct cx22702_state *state = fe->demodulator_priv;
|
|
+ u8 reg23;
|
|
|
|
- u16 rs_ber;
|
|
- rs_ber = cx22702_readreg(state, 0x23);
|
|
- *signal_strength = (rs_ber << 8) | rs_ber;
|
|
+ /*
|
|
+ * Experience suggests that the strength signal register works as
|
|
+ * follows:
|
|
+ * - In the absence of signal, value is 0xff.
|
|
+ * - In the presence of a weak signal, bit 7 is set, not sure what
|
|
+ * the lower 7 bits mean.
|
|
+ * - In the presence of a strong signal, the register holds a 7-bit
|
|
+ * value (bit 7 is cleared), with greater values standing for
|
|
+ * weaker signals.
|
|
+ */
|
|
+ reg23 = cx22702_readreg(state, 0x23);
|
|
+ if (reg23 & 0x80) {
|
|
+ *signal_strength = 0;
|
|
+ } else {
|
|
+ reg23 = ~reg23 & 0x7f;
|
|
+ /* Scale to 16 bit */
|
|
+ *signal_strength = (reg23 << 9) | (reg23 << 2) | (reg23 >> 5);
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-cards.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/em28xx/em28xx-cards.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-cards.c
|
|
@@ -353,6 +353,44 @@ static struct em28xx_reg_seq hauppauge_9
|
|
};
|
|
#endif
|
|
|
|
+/* 1b80:e425 MaxMedia UB425-TC
|
|
+ * GPIO_6 - demod reset, 0=active
|
|
+ * GPIO_7 - LED, 0=active
|
|
+ */
|
|
+static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
|
|
+ {EM2874_R80_GPIO, 0x83, 0xff, 100},
|
|
+ {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */
|
|
+ {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */
|
|
+ {-1, -1, -1, -1},
|
|
+};
|
|
+
|
|
+/* 2304:0242 PCTV QuatroStick (510e)
|
|
+ * GPIO_2: decoder reset, 0=active
|
|
+ * GPIO_4: decoder suspend, 0=active
|
|
+ * GPIO_6: demod reset, 0=active
|
|
+ * GPIO_7: LED, 1=active
|
|
+ */
|
|
+static struct em28xx_reg_seq pctv_510e[] = {
|
|
+ {EM2874_R80_GPIO, 0x10, 0xff, 100},
|
|
+ {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
|
|
+ {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
|
|
+ { -1, -1, -1, -1},
|
|
+};
|
|
+
|
|
+/* 2013:0251 PCTV QuatroStick nano (520e)
|
|
+ * GPIO_2: decoder reset, 0=active
|
|
+ * GPIO_4: decoder suspend, 0=active
|
|
+ * GPIO_6: demod reset, 0=active
|
|
+ * GPIO_7: LED, 1=active
|
|
+ */
|
|
+static struct em28xx_reg_seq pctv_520e[] = {
|
|
+ {EM2874_R80_GPIO, 0x10, 0xff, 100},
|
|
+ {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
|
|
+ {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
|
|
+ {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
|
|
+ { -1, -1, -1, -1},
|
|
+};
|
|
+
|
|
/*
|
|
* Board definitions
|
|
*/
|
|
@@ -1908,6 +1946,41 @@ struct em28xx_board em28xx_boards[] = {
|
|
.amux = EM28XX_AMUX_LINE_IN,
|
|
} },
|
|
},
|
|
+ /* 1b80:e425 MaxMedia UB425-TC
|
|
+ * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */
|
|
+ [EM2874_BOARD_MAXMEDIA_UB425_TC] = {
|
|
+ .name = "MaxMedia UB425-TC",
|
|
+ .tuner_type = TUNER_ABSENT,
|
|
+ .tuner_gpio = maxmedia_ub425_tc,
|
|
+ .has_dvb = 1,
|
|
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
|
|
+ EM28XX_I2C_CLK_WAIT_ENABLE |
|
|
+ EM28XX_I2C_FREQ_400_KHZ,
|
|
+ },
|
|
+ /* 2304:0242 PCTV QuatroStick (510e)
|
|
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
|
|
+ [EM2884_BOARD_PCTV_510E] = {
|
|
+ .name = "PCTV QuatroStick (510e)",
|
|
+ .tuner_type = TUNER_ABSENT,
|
|
+ .tuner_gpio = pctv_510e,
|
|
+ .has_dvb = 1,
|
|
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
|
|
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
|
|
+ EM28XX_I2C_CLK_WAIT_ENABLE |
|
|
+ EM28XX_I2C_FREQ_400_KHZ,
|
|
+ },
|
|
+ /* 2013:0251 PCTV QuatroStick nano (520e)
|
|
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
|
|
+ [EM2884_BOARD_PCTV_520E] = {
|
|
+ .name = "PCTV QuatroStick nano (520e)",
|
|
+ .tuner_type = TUNER_ABSENT,
|
|
+ .tuner_gpio = pctv_520e,
|
|
+ .has_dvb = 1,
|
|
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
|
|
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
|
|
+ EM28XX_I2C_CLK_WAIT_ENABLE |
|
|
+ EM28XX_I2C_FREQ_400_KHZ,
|
|
+ },
|
|
};
|
|
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
|
|
|
|
@@ -2059,6 +2132,12 @@ struct usb_device_id em28xx_id_table[] =
|
|
.driver_info = EM2860_BOARD_HT_VIDBOX_NW03 },
|
|
{ USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */
|
|
.driver_info = EM2860_BOARD_EASYCAP },
|
|
+ { USB_DEVICE(0x1b80, 0xe425),
|
|
+ .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC },
|
|
+ { USB_DEVICE(0x2304, 0x0242),
|
|
+ .driver_info = EM2884_BOARD_PCTV_510E },
|
|
+ { USB_DEVICE(0x2013, 0x0251),
|
|
+ .driver_info = EM2884_BOARD_PCTV_520E },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(usb, em28xx_id_table);
|
|
@@ -3122,7 +3201,6 @@ static int em28xx_usb_probe(struct usb_i
|
|
int i, nr;
|
|
const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
|
|
char *speed;
|
|
- char descr[255] = "";
|
|
|
|
udev = usb_get_dev(interface_to_usbdev(interface));
|
|
|
|
@@ -3227,21 +3305,11 @@ static int em28xx_usb_probe(struct usb_i
|
|
speed = "unknown";
|
|
}
|
|
|
|
- if (udev->manufacturer)
|
|
- strlcpy(descr, udev->manufacturer, sizeof(descr));
|
|
-
|
|
- if (udev->product) {
|
|
- if (*descr)
|
|
- strlcat(descr, " ", sizeof(descr));
|
|
- strlcat(descr, udev->product, sizeof(descr));
|
|
- }
|
|
-
|
|
- if (*descr)
|
|
- strlcat(descr, " ", sizeof(descr));
|
|
-
|
|
printk(KERN_INFO DRIVER_NAME
|
|
- ": New device %s@ %s Mbps (%04x:%04x, interface %d, class %d)\n",
|
|
- descr,
|
|
+ ": New device %s %s @ %s Mbps "
|
|
+ "(%04x:%04x, interface %d, class %d)\n",
|
|
+ udev->manufacturer ? udev->manufacturer : "",
|
|
+ udev->product ? udev->product : "",
|
|
speed,
|
|
le16_to_cpu(udev->descriptor.idVendor),
|
|
le16_to_cpu(udev->descriptor.idProduct),
|
|
@@ -3307,6 +3375,17 @@ static int em28xx_usb_probe(struct usb_i
|
|
goto unlock_and_free;
|
|
}
|
|
|
|
+ if (has_dvb) {
|
|
+ /* pre-allocate DVB isoc transfer buffers */
|
|
+ retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE,
|
|
+ EM28XX_DVB_MAX_PACKETS,
|
|
+ EM28XX_DVB_NUM_BUFS,
|
|
+ dev->dvb_max_pkt_size);
|
|
+ if (retval) {
|
|
+ goto unlock_and_free;
|
|
+ }
|
|
+ }
|
|
+
|
|
request_modules(dev);
|
|
|
|
/* Should be the last thing to do, to avoid newer udev's to
|
|
@@ -3379,7 +3458,7 @@ static void em28xx_usb_disconnect(struct
|
|
video_device_node_name(dev->vdev));
|
|
|
|
dev->state |= DEV_MISCONFIGURED;
|
|
- em28xx_uninit_isoc(dev);
|
|
+ em28xx_uninit_isoc(dev, dev->mode);
|
|
dev->state |= DEV_DISCONNECTED;
|
|
wake_up_interruptible(&dev->wait_frame);
|
|
wake_up_interruptible(&dev->wait_stream);
|
|
@@ -3388,6 +3467,9 @@ static void em28xx_usb_disconnect(struct
|
|
em28xx_release_resources(dev);
|
|
}
|
|
|
|
+ /* free DVB isoc buffers */
|
|
+ em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE);
|
|
+
|
|
mutex_unlock(&dev->lock);
|
|
|
|
em28xx_close_extension(dev);
|
|
Index: linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/em28xx/em28xx-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-core.c
|
|
@@ -666,6 +666,7 @@ int em28xx_capture_start(struct em28xx *
|
|
|
|
return rc;
|
|
}
|
|
+EXPORT_SYMBOL_GPL(em28xx_capture_start);
|
|
|
|
int em28xx_vbi_supported(struct em28xx *dev)
|
|
{
|
|
@@ -961,146 +962,192 @@ static void em28xx_irq_callback(struct u
|
|
/*
|
|
* Stop and Deallocate URBs
|
|
*/
|
|
-void em28xx_uninit_isoc(struct em28xx *dev)
|
|
+void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode)
|
|
{
|
|
struct urb *urb;
|
|
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
|
|
int i;
|
|
|
|
- em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n");
|
|
+ em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode);
|
|
+
|
|
+ if (mode == EM28XX_DIGITAL_MODE)
|
|
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
|
|
+ else
|
|
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
|
|
|
|
dev->isoc_ctl.nfields = -1;
|
|
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
|
|
- urb = dev->isoc_ctl.urb[i];
|
|
+ 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);
|
|
|
|
- if (dev->isoc_ctl.transfer_buffer[i]) {
|
|
+ if (isoc_bufs->transfer_buffer[i]) {
|
|
usb_free_coherent(dev->udev,
|
|
urb->transfer_buffer_length,
|
|
- dev->isoc_ctl.transfer_buffer[i],
|
|
+ isoc_bufs->transfer_buffer[i],
|
|
urb->transfer_dma);
|
|
}
|
|
usb_free_urb(urb);
|
|
- dev->isoc_ctl.urb[i] = NULL;
|
|
+ isoc_bufs->urb[i] = NULL;
|
|
}
|
|
- dev->isoc_ctl.transfer_buffer[i] = NULL;
|
|
+ isoc_bufs->transfer_buffer[i] = NULL;
|
|
}
|
|
|
|
- kfree(dev->isoc_ctl.urb);
|
|
- kfree(dev->isoc_ctl.transfer_buffer);
|
|
+ kfree(isoc_bufs->urb);
|
|
+ kfree(isoc_bufs->transfer_buffer);
|
|
|
|
- dev->isoc_ctl.urb = NULL;
|
|
- dev->isoc_ctl.transfer_buffer = NULL;
|
|
- dev->isoc_ctl.num_bufs = 0;
|
|
+ isoc_bufs->urb = NULL;
|
|
+ isoc_bufs->transfer_buffer = NULL;
|
|
+ isoc_bufs->num_bufs = 0;
|
|
|
|
em28xx_capture_start(dev, 0);
|
|
}
|
|
EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
|
|
|
|
/*
|
|
- * Allocate URBs and start IRQ
|
|
+ * Allocate URBs
|
|
*/
|
|
-int em28xx_init_isoc(struct em28xx *dev, int max_packets,
|
|
- int num_bufs, int max_pkt_size,
|
|
- int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
|
|
+int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
|
|
+ int max_packets, int num_bufs, int max_pkt_size)
|
|
{
|
|
- struct em28xx_dmaqueue *dma_q = &dev->vidq;
|
|
- struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
|
|
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
|
|
int i;
|
|
int sb_size, pipe;
|
|
struct urb *urb;
|
|
int j, k;
|
|
- int rc;
|
|
|
|
- em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n");
|
|
+ em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode);
|
|
+
|
|
+ if (mode == EM28XX_DIGITAL_MODE)
|
|
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
|
|
+ else
|
|
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
|
|
|
|
/* De-allocates all pending stuff */
|
|
- em28xx_uninit_isoc(dev);
|
|
+ em28xx_uninit_isoc(dev, mode);
|
|
|
|
- dev->isoc_ctl.isoc_copy = isoc_copy;
|
|
- dev->isoc_ctl.num_bufs = num_bufs;
|
|
+ isoc_bufs->num_bufs = num_bufs;
|
|
|
|
- dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
|
|
- if (!dev->isoc_ctl.urb) {
|
|
+ isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
|
|
+ if (!isoc_bufs->urb) {
|
|
em28xx_errdev("cannot alloc memory for usb buffers\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
- dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
|
|
- GFP_KERNEL);
|
|
- if (!dev->isoc_ctl.transfer_buffer) {
|
|
+ isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
|
|
+ GFP_KERNEL);
|
|
+ if (!isoc_bufs->transfer_buffer) {
|
|
em28xx_errdev("cannot allocate memory for usb transfer\n");
|
|
- kfree(dev->isoc_ctl.urb);
|
|
+ kfree(isoc_bufs->urb);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
- dev->isoc_ctl.max_pkt_size = max_pkt_size;
|
|
+ isoc_bufs->max_pkt_size = max_pkt_size;
|
|
+ isoc_bufs->num_packets = max_packets;
|
|
dev->isoc_ctl.vid_buf = NULL;
|
|
dev->isoc_ctl.vbi_buf = NULL;
|
|
|
|
- sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
|
|
+ sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size;
|
|
|
|
/* allocate urbs and transfer buffers */
|
|
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
|
|
- urb = usb_alloc_urb(max_packets, GFP_KERNEL);
|
|
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
|
|
+ urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL);
|
|
if (!urb) {
|
|
em28xx_err("cannot alloc isoc_ctl.urb %i\n", i);
|
|
- em28xx_uninit_isoc(dev);
|
|
+ em28xx_uninit_isoc(dev, mode);
|
|
return -ENOMEM;
|
|
}
|
|
- dev->isoc_ctl.urb[i] = urb;
|
|
+ isoc_bufs->urb[i] = urb;
|
|
|
|
- dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev,
|
|
+ isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev,
|
|
sb_size, GFP_KERNEL, &urb->transfer_dma);
|
|
- if (!dev->isoc_ctl.transfer_buffer[i]) {
|
|
+ if (!isoc_bufs->transfer_buffer[i]) {
|
|
em28xx_err("unable to allocate %i bytes for transfer"
|
|
" buffer %i%s\n",
|
|
sb_size, i,
|
|
in_interrupt() ? " while in int" : "");
|
|
- em28xx_uninit_isoc(dev);
|
|
+ em28xx_uninit_isoc(dev, mode);
|
|
return -ENOMEM;
|
|
}
|
|
- memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
|
|
+ memset(isoc_bufs->transfer_buffer[i], 0, sb_size);
|
|
|
|
/* FIXME: this is a hack - should be
|
|
'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK'
|
|
should also be using 'desc.bInterval'
|
|
*/
|
|
pipe = usb_rcvisocpipe(dev->udev,
|
|
- dev->mode == EM28XX_ANALOG_MODE ?
|
|
+ mode == EM28XX_ANALOG_MODE ?
|
|
EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL);
|
|
|
|
usb_fill_int_urb(urb, dev->udev, pipe,
|
|
- dev->isoc_ctl.transfer_buffer[i], sb_size,
|
|
+ isoc_bufs->transfer_buffer[i], sb_size,
|
|
em28xx_irq_callback, dev, 1);
|
|
|
|
- urb->number_of_packets = max_packets;
|
|
+ urb->number_of_packets = isoc_bufs->num_packets;
|
|
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
|
|
|
|
k = 0;
|
|
- for (j = 0; j < max_packets; j++) {
|
|
+ for (j = 0; j < isoc_bufs->num_packets; j++) {
|
|
urb->iso_frame_desc[j].offset = k;
|
|
urb->iso_frame_desc[j].length =
|
|
- dev->isoc_ctl.max_pkt_size;
|
|
- k += dev->isoc_ctl.max_pkt_size;
|
|
+ isoc_bufs->max_pkt_size;
|
|
+ k += isoc_bufs->max_pkt_size;
|
|
}
|
|
}
|
|
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(em28xx_alloc_isoc);
|
|
+
|
|
+/*
|
|
+ * Allocate URBs and start IRQ
|
|
+ */
|
|
+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))
|
|
+{
|
|
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
|
|
+ struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
|
|
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
|
|
+ int i;
|
|
+ int rc;
|
|
+ int alloc;
|
|
+
|
|
+ em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode);
|
|
+
|
|
+ dev->isoc_ctl.isoc_copy = isoc_copy;
|
|
+
|
|
+ if (mode == EM28XX_DIGITAL_MODE) {
|
|
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
|
|
+ /* no need to free/alloc isoc buffers in digital mode */
|
|
+ alloc = 0;
|
|
+ } else {
|
|
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
|
|
+ alloc = 1;
|
|
+ }
|
|
+
|
|
+ if (alloc) {
|
|
+ rc = em28xx_alloc_isoc(dev, mode, max_packets,
|
|
+ num_bufs, max_pkt_size);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
init_waitqueue_head(&dma_q->wq);
|
|
init_waitqueue_head(&vbi_dma_q->wq);
|
|
|
|
em28xx_capture_start(dev, 1);
|
|
|
|
/* submit urbs and enables IRQ */
|
|
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
|
|
- rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
|
|
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
|
|
+ rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC);
|
|
if (rc) {
|
|
em28xx_err("submit of urb %i failed (error=%i)\n", i,
|
|
rc);
|
|
- em28xx_uninit_isoc(dev);
|
|
+ em28xx_uninit_isoc(dev, mode);
|
|
return rc;
|
|
}
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-dvb.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/em28xx/em28xx-dvb.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-dvb.c
|
|
@@ -61,9 +61,6 @@ if (debug >= level) \
|
|
printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
|
|
} while (0)
|
|
|
|
-#define EM28XX_DVB_NUM_BUFS 5
|
|
-#define EM28XX_DVB_MAX_PACKETS 64
|
|
-
|
|
struct em28xx_dvb {
|
|
struct dvb_frontend *fe[2];
|
|
|
|
@@ -172,20 +169,21 @@ static int em28xx_start_streaming(struct
|
|
max_dvb_packet_size = dev->dvb_max_pkt_size;
|
|
if (max_dvb_packet_size < 0)
|
|
return max_dvb_packet_size;
|
|
- dprintk(1, "Using %d buffers each with %d bytes\n",
|
|
+ dprintk(1, "Using %d buffers each with %d x %d bytes\n",
|
|
EM28XX_DVB_NUM_BUFS,
|
|
+ EM28XX_DVB_MAX_PACKETS,
|
|
max_dvb_packet_size);
|
|
|
|
- return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS,
|
|
- EM28XX_DVB_NUM_BUFS, max_dvb_packet_size,
|
|
- em28xx_dvb_isoc_copy);
|
|
+ return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE,
|
|
+ EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS,
|
|
+ max_dvb_packet_size, em28xx_dvb_isoc_copy);
|
|
}
|
|
|
|
static int em28xx_stop_streaming(struct em28xx_dvb *dvb)
|
|
{
|
|
struct em28xx *dev = dvb->adapter.priv;
|
|
|
|
- em28xx_uninit_isoc(dev);
|
|
+ em28xx_capture_start(dev, 0);
|
|
|
|
em28xx_set_mode(dev, EM28XX_SUSPEND);
|
|
|
|
@@ -327,6 +325,19 @@ struct drxk_config hauppauge_930c_drxk =
|
|
.chunk_size = 56,
|
|
};
|
|
|
|
+struct drxk_config maxmedia_ub425_tc_drxk = {
|
|
+ .adr = 0x29,
|
|
+ .single_master = 1,
|
|
+ .no_i2c_bridge = 1,
|
|
+};
|
|
+
|
|
+struct drxk_config pctv_520e_drxk = {
|
|
+ .adr = 0x29,
|
|
+ .single_master = 1,
|
|
+ .microcode_name = "dvb-demod-drxk-pctv.fw",
|
|
+ .chunk_size = 58,
|
|
+};
|
|
+
|
|
static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
|
|
{
|
|
struct em28xx_dvb *dvb = fe->sec_priv;
|
|
@@ -460,6 +471,33 @@ static void terratec_h5_init(struct em28
|
|
em28xx_gpio_set(dev, terratec_h5_end);
|
|
};
|
|
|
|
+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.
|
|
+ */
|
|
+ int i;
|
|
+ struct {
|
|
+ unsigned char r[4];
|
|
+ int len;
|
|
+ } regs[] = {
|
|
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
|
|
+ {{ 0x01, 0x02 }, 2},
|
|
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
|
|
+ {{ 0x01, 0x00 }, 2},
|
|
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
|
|
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
|
|
+ {{ 0x01, 0x00 }, 2},
|
|
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
|
|
+ };
|
|
+
|
|
+ dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
|
|
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
|
|
+};
|
|
+
|
|
static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe)
|
|
{
|
|
/* Values extracted from a USB trace of the Terratec Windows driver */
|
|
@@ -938,6 +976,48 @@ static int em28xx_dvb_init(struct em28xx
|
|
dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap,
|
|
&em28xx_a8293_config);
|
|
break;
|
|
+ case EM2874_BOARD_MAXMEDIA_UB425_TC:
|
|
+ /* attach demodulator */
|
|
+ dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk,
|
|
+ &dev->i2c_adap);
|
|
+
|
|
+ if (dvb->fe[0]) {
|
|
+ /* disable I2C-gate */
|
|
+ dvb->fe[0]->ops.i2c_gate_ctrl = NULL;
|
|
+
|
|
+ /* attach tuner */
|
|
+ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0],
|
|
+ &dev->i2c_adap, 0x60)) {
|
|
+ dvb_frontend_detach(dvb->fe[0]);
|
|
+ result = -EINVAL;
|
|
+ goto out_free;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* TODO: we need drx-3913k firmware in order to support DVB-T */
|
|
+ em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \
|
|
+ "driver version\n");
|
|
+
|
|
+ break;
|
|
+ case EM2884_BOARD_PCTV_510E:
|
|
+ case EM2884_BOARD_PCTV_520E:
|
|
+ pctv_520e_init(dev);
|
|
+
|
|
+ /* attach demodulator */
|
|
+ dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk,
|
|
+ &dev->i2c_adap);
|
|
+
|
|
+ if (dvb->fe[0]) {
|
|
+ /* attach tuner */
|
|
+ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
|
|
+ &dev->i2c_adap,
|
|
+ &em28xx_cxd2820r_tda18271_config)) {
|
|
+ dvb_frontend_detach(dvb->fe[0]);
|
|
+ result = -EINVAL;
|
|
+ goto out_free;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
default:
|
|
em28xx_errdev("/2: The frontend of your DVB/ATSC card"
|
|
" isn't supported yet\n");
|
|
Index: linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-video.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/em28xx/em28xx-video.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-video.c
|
|
@@ -760,17 +760,19 @@ buffer_prepare(struct videobuf_queue *vq
|
|
goto fail;
|
|
}
|
|
|
|
- if (!dev->isoc_ctl.num_bufs)
|
|
+ if (!dev->isoc_ctl.analog_bufs.num_bufs)
|
|
urb_init = 1;
|
|
|
|
if (urb_init) {
|
|
if (em28xx_vbi_supported(dev) == 1)
|
|
- rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
|
|
+ rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
|
|
+ EM28XX_NUM_PACKETS,
|
|
EM28XX_NUM_BUFS,
|
|
dev->max_pkt_size,
|
|
em28xx_isoc_copy_vbi);
|
|
else
|
|
- rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
|
|
+ rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
|
|
+ EM28XX_NUM_PACKETS,
|
|
EM28XX_NUM_BUFS,
|
|
dev->max_pkt_size,
|
|
em28xx_isoc_copy);
|
|
@@ -2267,7 +2269,7 @@ static int em28xx_v4l2_close(struct file
|
|
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
|
|
|
|
/* do this before setting alternate! */
|
|
- em28xx_uninit_isoc(dev);
|
|
+ em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE);
|
|
em28xx_set_mode(dev, EM28XX_SUSPEND);
|
|
|
|
/* set alternate 0 */
|
|
Index: linux-3.3.x86_64/drivers/media/video/em28xx/em28xx.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/em28xx/em28xx.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/em28xx/em28xx.h
|
|
@@ -125,6 +125,9 @@
|
|
#define EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C 81
|
|
#define EM2884_BOARD_CINERGY_HTC_STICK 82
|
|
#define EM2860_BOARD_HT_VIDBOX_NW03 83
|
|
+#define EM2874_BOARD_MAXMEDIA_UB425_TC 84
|
|
+#define EM2884_BOARD_PCTV_510E 85
|
|
+#define EM2884_BOARD_PCTV_520E 86
|
|
|
|
/* Limits minimum and default number of buffers */
|
|
#define EM28XX_MIN_BUF 4
|
|
@@ -151,12 +154,14 @@
|
|
|
|
/* number of buffers for isoc transfers */
|
|
#define EM28XX_NUM_BUFS 5
|
|
+#define EM28XX_DVB_NUM_BUFS 5
|
|
|
|
/* number of packets for each buffer
|
|
windows requests only 64 packets .. so we better do the same
|
|
this is what I found out for all alternate numbers there!
|
|
*/
|
|
#define EM28XX_NUM_PACKETS 64
|
|
+#define EM28XX_DVB_MAX_PACKETS 64
|
|
|
|
#define EM28XX_INTERLACED_DEFAULT 1
|
|
|
|
@@ -197,10 +202,13 @@ enum em28xx_mode {
|
|
|
|
struct em28xx;
|
|
|
|
-struct em28xx_usb_isoc_ctl {
|
|
+struct em28xx_usb_isoc_bufs {
|
|
/* max packet size of isoc transaction */
|
|
int max_pkt_size;
|
|
|
|
+ /* number of packets in each buffer */
|
|
+ int num_packets;
|
|
+
|
|
/* number of allocated urbs */
|
|
int num_bufs;
|
|
|
|
@@ -209,6 +217,14 @@ struct em28xx_usb_isoc_ctl {
|
|
|
|
/* transfer buffers for isoc transfer */
|
|
char **transfer_buffer;
|
|
+};
|
|
+
|
|
+struct em28xx_usb_isoc_ctl {
|
|
+ /* isoc transfer buffers for analog mode */
|
|
+ struct em28xx_usb_isoc_bufs analog_bufs;
|
|
+
|
|
+ /* isoc transfer buffers for digital mode */
|
|
+ struct em28xx_usb_isoc_bufs digital_bufs;
|
|
|
|
/* Last buffer command and region */
|
|
u8 cmd;
|
|
@@ -600,9 +616,6 @@ struct em28xx {
|
|
unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
|
|
int dvb_alt; /* alternate for DVB */
|
|
unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */
|
|
- struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */
|
|
- char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc
|
|
- transfer */
|
|
char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
|
|
|
|
/* helper funcs that call usb_control_msg */
|
|
@@ -676,10 +689,12 @@ int em28xx_vbi_supported(struct em28xx *
|
|
int em28xx_set_outfmt(struct em28xx *dev);
|
|
int em28xx_resolution_set(struct em28xx *dev);
|
|
int em28xx_set_alternate(struct em28xx *dev);
|
|
-int em28xx_init_isoc(struct em28xx *dev, int max_packets,
|
|
- int num_bufs, int max_pkt_size,
|
|
+int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
|
|
+ int max_packets, int num_bufs, int max_pkt_size);
|
|
+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);
|
|
+void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode);
|
|
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);
|
|
Index: linux-3.3.x86_64/drivers/media/video/mx2_camera.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/mx2_camera.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/mx2_camera.c
|
|
@@ -3,6 +3,7 @@
|
|
*
|
|
* Copyright (C) 2008, Sascha Hauer, Pengutronix
|
|
* Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
|
|
+ * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
|
|
*
|
|
* 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
|
|
@@ -18,6 +19,7 @@
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/fs.h>
|
|
+#include <linux/gcd.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
@@ -30,17 +32,14 @@
|
|
|
|
#include <media/v4l2-common.h>
|
|
#include <media/v4l2-dev.h>
|
|
-#include <media/videobuf-core.h>
|
|
-#include <media/videobuf-dma-contig.h>
|
|
+#include <media/videobuf2-core.h>
|
|
+#include <media/videobuf2-dma-contig.h>
|
|
#include <media/soc_camera.h>
|
|
#include <media/soc_mediabus.h>
|
|
|
|
#include <linux/videodev2.h>
|
|
|
|
#include <mach/mx2_cam.h>
|
|
-#ifdef CONFIG_MACH_MX27
|
|
-#include <mach/dma-mx1-mx2.h>
|
|
-#endif
|
|
#include <mach/hardware.h>
|
|
|
|
#include <asm/dma.h>
|
|
@@ -206,10 +205,23 @@
|
|
#define PRP_INTR_LBOVF (1 << 7)
|
|
#define PRP_INTR_CH2OVF (1 << 8)
|
|
|
|
-#define mx27_camera_emma(pcdev) (cpu_is_mx27() && pcdev->use_emma)
|
|
+/* Resizing registers */
|
|
+#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24)
|
|
+#define PRP_RZ_VALID_BILINEAR (1 << 31)
|
|
|
|
#define MAX_VIDEO_MEM 16
|
|
|
|
+#define RESIZE_NUM_MIN 1
|
|
+#define RESIZE_NUM_MAX 20
|
|
+#define BC_COEF 3
|
|
+#define SZ_COEF (1 << BC_COEF)
|
|
+
|
|
+#define RESIZE_DIR_H 0
|
|
+#define RESIZE_DIR_V 1
|
|
+
|
|
+#define RESIZE_ALGO_BILINEAR 0
|
|
+#define RESIZE_ALGO_AVERAGING 1
|
|
+
|
|
struct mx2_prp_cfg {
|
|
int channel;
|
|
u32 in_fmt;
|
|
@@ -219,6 +231,13 @@ struct mx2_prp_cfg {
|
|
u32 irq_flags;
|
|
};
|
|
|
|
+/* prp resizing parameters */
|
|
+struct emma_prp_resize {
|
|
+ int algo; /* type of algorithm used */
|
|
+ int len; /* number of coefficients */
|
|
+ unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */
|
|
+};
|
|
+
|
|
/* prp configuration for a client-host fmt pair */
|
|
struct mx2_fmt_cfg {
|
|
enum v4l2_mbus_pixelcode in_fmt;
|
|
@@ -226,6 +245,26 @@ struct mx2_fmt_cfg {
|
|
struct mx2_prp_cfg cfg;
|
|
};
|
|
|
|
+enum mx2_buffer_state {
|
|
+ MX2_STATE_QUEUED,
|
|
+ MX2_STATE_ACTIVE,
|
|
+ MX2_STATE_DONE,
|
|
+};
|
|
+
|
|
+struct mx2_buf_internal {
|
|
+ struct list_head queue;
|
|
+ int bufnum;
|
|
+ bool discard;
|
|
+};
|
|
+
|
|
+/* buffer for one video frame */
|
|
+struct mx2_buffer {
|
|
+ /* common v4l buffer stuff -- must be first */
|
|
+ struct vb2_buffer vb;
|
|
+ enum mx2_buffer_state state;
|
|
+ struct mx2_buf_internal internal;
|
|
+};
|
|
+
|
|
struct mx2_camera_dev {
|
|
struct device *dev;
|
|
struct soc_camera_host soc_host;
|
|
@@ -242,6 +281,7 @@ struct mx2_camera_dev {
|
|
|
|
struct list_head capture;
|
|
struct list_head active_bufs;
|
|
+ struct list_head discard;
|
|
|
|
spinlock_t lock;
|
|
|
|
@@ -250,26 +290,23 @@ struct mx2_camera_dev {
|
|
struct mx2_buffer *fb1_active;
|
|
struct mx2_buffer *fb2_active;
|
|
|
|
- int use_emma;
|
|
-
|
|
u32 csicr1;
|
|
|
|
+ struct mx2_buf_internal buf_discard[2];
|
|
void *discard_buffer;
|
|
dma_addr_t discard_buffer_dma;
|
|
size_t discard_size;
|
|
struct mx2_fmt_cfg *emma_prp;
|
|
+ struct emma_prp_resize resizing[2];
|
|
+ unsigned int s_width, s_height;
|
|
u32 frame_count;
|
|
+ struct vb2_alloc_ctx *alloc_ctx;
|
|
};
|
|
|
|
-/* buffer for one video frame */
|
|
-struct mx2_buffer {
|
|
- /* common v4l buffer stuff -- must be first */
|
|
- struct videobuf_buffer vb;
|
|
-
|
|
- enum v4l2_mbus_pixelcode code;
|
|
-
|
|
- int bufnum;
|
|
-};
|
|
+static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
|
|
+{
|
|
+ return container_of(int_buf, struct mx2_buffer, internal);
|
|
+}
|
|
|
|
static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
|
|
/*
|
|
@@ -324,13 +361,36 @@ static struct mx2_fmt_cfg *mx27_emma_prp
|
|
return &mx27_emma_prp_table[0];
|
|
};
|
|
|
|
+static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
|
|
+ unsigned long phys, int bufnum)
|
|
+{
|
|
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
|
|
+
|
|
+ if (prp->cfg.channel == 1) {
|
|
+ writel(phys, pcdev->base_emma +
|
|
+ PRP_DEST_RGB1_PTR + 4 * bufnum);
|
|
+ } else {
|
|
+ writel(phys, pcdev->base_emma +
|
|
+ PRP_DEST_Y_PTR - 0x14 * bufnum);
|
|
+ if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
|
|
+ u32 imgsize = pcdev->icd->user_height *
|
|
+ pcdev->icd->user_width;
|
|
+
|
|
+ writel(phys + imgsize, pcdev->base_emma +
|
|
+ PRP_DEST_CB_PTR - 0x14 * bufnum);
|
|
+ writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
|
|
+ PRP_DEST_CR_PTR - 0x14 * bufnum);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
|
|
{
|
|
unsigned long flags;
|
|
|
|
clk_disable(pcdev->clk_csi);
|
|
writel(0, pcdev->base_csi + CSICR1);
|
|
- if (mx27_camera_emma(pcdev)) {
|
|
+ if (cpu_is_mx27()) {
|
|
writel(0, pcdev->base_emma + PRP_CNTL);
|
|
} else if (cpu_is_mx25()) {
|
|
spin_lock_irqsave(&pcdev->lock, flags);
|
|
@@ -362,7 +422,7 @@ static int mx2_camera_add_device(struct
|
|
|
|
csicr1 = CSICR1_MCLKEN;
|
|
|
|
- if (mx27_camera_emma(pcdev)) {
|
|
+ if (cpu_is_mx27()) {
|
|
csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC |
|
|
CSICR1_RXFF_LEVEL(0);
|
|
} else if (cpu_is_mx27())
|
|
@@ -392,56 +452,13 @@ static void mx2_camera_remove_device(str
|
|
|
|
mx2_camera_deactivate(pcdev);
|
|
|
|
- if (pcdev->discard_buffer) {
|
|
- dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size,
|
|
- pcdev->discard_buffer,
|
|
- pcdev->discard_buffer_dma);
|
|
- pcdev->discard_buffer = NULL;
|
|
- }
|
|
-
|
|
pcdev->icd = NULL;
|
|
}
|
|
|
|
-#ifdef CONFIG_MACH_MX27
|
|
-static void mx27_camera_dma_enable(struct mx2_camera_dev *pcdev)
|
|
-{
|
|
- u32 tmp;
|
|
-
|
|
- imx_dma_enable(pcdev->dma);
|
|
-
|
|
- tmp = readl(pcdev->base_csi + CSICR1);
|
|
- tmp |= CSICR1_RF_OR_INTEN;
|
|
- writel(tmp, pcdev->base_csi + CSICR1);
|
|
-}
|
|
-
|
|
-static irqreturn_t mx27_camera_irq(int irq_csi, void *data)
|
|
-{
|
|
- struct mx2_camera_dev *pcdev = data;
|
|
- u32 status = readl(pcdev->base_csi + CSISR);
|
|
-
|
|
- if (status & CSISR_SOF_INT && pcdev->active) {
|
|
- u32 tmp;
|
|
-
|
|
- tmp = readl(pcdev->base_csi + CSICR1);
|
|
- writel(tmp | CSICR1_CLR_RXFIFO, pcdev->base_csi + CSICR1);
|
|
- mx27_camera_dma_enable(pcdev);
|
|
- }
|
|
-
|
|
- writel(CSISR_SOF_INT | CSISR_RFF_OR_INT, pcdev->base_csi + CSISR);
|
|
-
|
|
- return IRQ_HANDLED;
|
|
-}
|
|
-#else
|
|
-static irqreturn_t mx27_camera_irq(int irq_csi, void *data)
|
|
-{
|
|
- return IRQ_NONE;
|
|
-}
|
|
-#endif /* CONFIG_MACH_MX27 */
|
|
-
|
|
static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb,
|
|
int state)
|
|
{
|
|
- struct videobuf_buffer *vb;
|
|
+ struct vb2_buffer *vb;
|
|
struct mx2_buffer *buf;
|
|
struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active :
|
|
&pcdev->fb2_active;
|
|
@@ -454,25 +471,24 @@ static void mx25_camera_frame_done(struc
|
|
goto out;
|
|
|
|
vb = &(*fb_active)->vb;
|
|
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
|
|
- vb, vb->baddr, vb->bsize);
|
|
-
|
|
- vb->state = state;
|
|
- do_gettimeofday(&vb->ts);
|
|
- vb->field_count++;
|
|
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
|
|
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
|
|
|
|
- wake_up(&vb->done);
|
|
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
|
|
+ vb->v4l2_buf.sequence++;
|
|
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
|
|
|
|
if (list_empty(&pcdev->capture)) {
|
|
buf = NULL;
|
|
writel(0, pcdev->base_csi + fb_reg);
|
|
} else {
|
|
- buf = list_entry(pcdev->capture.next, struct mx2_buffer,
|
|
- vb.queue);
|
|
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
|
|
+ internal.queue);
|
|
vb = &buf->vb;
|
|
- list_del(&vb->queue);
|
|
- vb->state = VIDEOBUF_ACTIVE;
|
|
- writel(videobuf_to_dma_contig(vb), pcdev->base_csi + fb_reg);
|
|
+ list_del(&buf->internal.queue);
|
|
+ buf->state = MX2_STATE_ACTIVE;
|
|
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
|
|
+ pcdev->base_csi + fb_reg);
|
|
}
|
|
|
|
*fb_active = buf;
|
|
@@ -487,9 +503,9 @@ static irqreturn_t mx25_camera_irq(int i
|
|
u32 status = readl(pcdev->base_csi + CSISR);
|
|
|
|
if (status & CSISR_DMA_TSF_FB1_INT)
|
|
- mx25_camera_frame_done(pcdev, 1, VIDEOBUF_DONE);
|
|
+ mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE);
|
|
else if (status & CSISR_DMA_TSF_FB2_INT)
|
|
- mx25_camera_frame_done(pcdev, 2, VIDEOBUF_DONE);
|
|
+ mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE);
|
|
|
|
/* FIXME: handle CSISR_RFF_OR_INT */
|
|
|
|
@@ -501,59 +517,50 @@ static irqreturn_t mx25_camera_irq(int i
|
|
/*
|
|
* Videobuf operations
|
|
*/
|
|
-static int mx2_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
|
|
- unsigned int *size)
|
|
+static int mx2_videobuf_setup(struct vb2_queue *vq,
|
|
+ const struct v4l2_format *fmt,
|
|
+ unsigned int *count, unsigned int *num_planes,
|
|
+ unsigned int sizes[], void *alloc_ctxs[])
|
|
{
|
|
- struct soc_camera_device *icd = vq->priv_data;
|
|
+ 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, *size);
|
|
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
|
|
+
|
|
+ /* TODO: support for VIDIOC_CREATE_BUFS not ready */
|
|
+ if (fmt != NULL)
|
|
+ return -ENOTTY;
|
|
|
|
if (bytes_per_line < 0)
|
|
return bytes_per_line;
|
|
|
|
- *size = bytes_per_line * icd->user_height;
|
|
+ alloc_ctxs[0] = pcdev->alloc_ctx;
|
|
+
|
|
+ sizes[0] = bytes_per_line * icd->user_height;
|
|
|
|
if (0 == *count)
|
|
*count = 32;
|
|
- if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
|
|
- *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
|
|
+ if (!*num_planes &&
|
|
+ sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
|
|
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
|
|
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf)
|
|
-{
|
|
- struct soc_camera_device *icd = vq->priv_data;
|
|
- struct videobuf_buffer *vb = &buf->vb;
|
|
+ *num_planes = 1;
|
|
|
|
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
|
|
- vb, vb->baddr, vb->bsize);
|
|
-
|
|
- /*
|
|
- * This waits until this buffer is out of danger, i.e., until it is no
|
|
- * longer in state VIDEOBUF_QUEUED or VIDEOBUF_ACTIVE
|
|
- */
|
|
- videobuf_waiton(vq, vb, 0, 0);
|
|
-
|
|
- videobuf_dma_contig_free(vq, vb);
|
|
- dev_dbg(icd->parent, "%s freed\n", __func__);
|
|
-
|
|
- vb->state = VIDEOBUF_NEEDS_INIT;
|
|
+ return 0;
|
|
}
|
|
|
|
-static int mx2_videobuf_prepare(struct videobuf_queue *vq,
|
|
- struct videobuf_buffer *vb, enum v4l2_field field)
|
|
+static int mx2_videobuf_prepare(struct vb2_buffer *vb)
|
|
{
|
|
- struct soc_camera_device *icd = vq->priv_data;
|
|
- struct mx2_buffer *buf = container_of(vb, struct mx2_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%08lx %d\n", __func__,
|
|
- vb, vb->baddr, vb->bsize);
|
|
+ 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;
|
|
@@ -563,99 +570,58 @@ static int mx2_videobuf_prepare(struct v
|
|
* This can be useful if you want to see if we actually fill
|
|
* the buffer with something
|
|
*/
|
|
- memset((void *)vb->baddr, 0xaa, vb->bsize);
|
|
+ memset((void *)vb2_plane_vaddr(vb, 0),
|
|
+ 0xaa, vb2_get_plane_payload(vb, 0));
|
|
#endif
|
|
|
|
- if (buf->code != icd->current_fmt->code ||
|
|
- vb->width != icd->user_width ||
|
|
- vb->height != icd->user_height ||
|
|
- vb->field != field) {
|
|
- buf->code = icd->current_fmt->code;
|
|
- vb->width = icd->user_width;
|
|
- vb->height = icd->user_height;
|
|
- vb->field = field;
|
|
- vb->state = VIDEOBUF_NEEDS_INIT;
|
|
- }
|
|
-
|
|
- vb->size = bytes_per_line * vb->height;
|
|
- if (vb->baddr && vb->bsize < vb->size) {
|
|
+ vb2_set_plane_payload(vb, 0, bytes_per_line * icd->user_height);
|
|
+ if (vb2_plane_vaddr(vb, 0) &&
|
|
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
|
|
- ret = videobuf_iolock(vq, vb, NULL);
|
|
- if (ret)
|
|
- goto fail;
|
|
-
|
|
- vb->state = VIDEOBUF_PREPARED;
|
|
- }
|
|
-
|
|
return 0;
|
|
|
|
-fail:
|
|
- free_buffer(vq, buf);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
-static void mx2_videobuf_queue(struct videobuf_queue *vq,
|
|
- struct videobuf_buffer *vb)
|
|
+static void mx2_videobuf_queue(struct vb2_buffer *vb)
|
|
{
|
|
- struct soc_camera_device *icd = vq->priv_data;
|
|
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
|
|
struct soc_camera_host *ici =
|
|
to_soc_camera_host(icd->parent);
|
|
struct mx2_camera_dev *pcdev = ici->priv;
|
|
struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
|
|
unsigned long flags;
|
|
|
|
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
|
|
- vb, vb->baddr, vb->bsize);
|
|
+ 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));
|
|
|
|
spin_lock_irqsave(&pcdev->lock, flags);
|
|
|
|
- vb->state = VIDEOBUF_QUEUED;
|
|
- list_add_tail(&vb->queue, &pcdev->capture);
|
|
-
|
|
- if (mx27_camera_emma(pcdev)) {
|
|
- goto out;
|
|
-#ifdef CONFIG_MACH_MX27
|
|
- } else if (cpu_is_mx27()) {
|
|
- int ret;
|
|
-
|
|
- if (pcdev->active == NULL) {
|
|
- ret = imx_dma_setup_single(pcdev->dma,
|
|
- videobuf_to_dma_contig(vb), vb->size,
|
|
- (u32)pcdev->base_dma + 0x10,
|
|
- DMA_MODE_READ);
|
|
- if (ret) {
|
|
- vb->state = VIDEOBUF_ERROR;
|
|
- wake_up(&vb->done);
|
|
- goto out;
|
|
- }
|
|
+ buf->state = MX2_STATE_QUEUED;
|
|
+ list_add_tail(&buf->internal.queue, &pcdev->capture);
|
|
|
|
- vb->state = VIDEOBUF_ACTIVE;
|
|
- pcdev->active = buf;
|
|
- }
|
|
-#endif
|
|
- } else { /* cpu_is_mx25() */
|
|
+ if (cpu_is_mx25()) {
|
|
u32 csicr3, dma_inten = 0;
|
|
|
|
if (pcdev->fb1_active == NULL) {
|
|
- writel(videobuf_to_dma_contig(vb),
|
|
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
|
|
pcdev->base_csi + CSIDMASA_FB1);
|
|
pcdev->fb1_active = buf;
|
|
dma_inten = CSICR1_FB1_DMA_INTEN;
|
|
} else if (pcdev->fb2_active == NULL) {
|
|
- writel(videobuf_to_dma_contig(vb),
|
|
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
|
|
pcdev->base_csi + CSIDMASA_FB2);
|
|
pcdev->fb2_active = buf;
|
|
dma_inten = CSICR1_FB2_DMA_INTEN;
|
|
}
|
|
|
|
if (dma_inten) {
|
|
- list_del(&vb->queue);
|
|
- vb->state = VIDEOBUF_ACTIVE;
|
|
+ list_del(&buf->internal.queue);
|
|
+ buf->state = MX2_STATE_ACTIVE;
|
|
|
|
csicr3 = readl(pcdev->base_csi + CSICR3);
|
|
|
|
@@ -674,36 +640,31 @@ static void mx2_videobuf_queue(struct vi
|
|
}
|
|
}
|
|
|
|
-out:
|
|
spin_unlock_irqrestore(&pcdev->lock, flags);
|
|
}
|
|
|
|
-static void mx2_videobuf_release(struct videobuf_queue *vq,
|
|
- struct videobuf_buffer *vb)
|
|
+static void mx2_videobuf_release(struct vb2_buffer *vb)
|
|
{
|
|
- struct soc_camera_device *icd = vq->priv_data;
|
|
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
|
|
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
|
struct mx2_camera_dev *pcdev = ici->priv;
|
|
struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
|
|
unsigned long flags;
|
|
|
|
#ifdef DEBUG
|
|
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
|
|
- vb, vb->baddr, vb->bsize);
|
|
+ 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));
|
|
|
|
- switch (vb->state) {
|
|
- case VIDEOBUF_ACTIVE:
|
|
+ switch (buf->state) {
|
|
+ case MX2_STATE_ACTIVE:
|
|
dev_info(icd->parent, "%s (active)\n", __func__);
|
|
break;
|
|
- case VIDEOBUF_QUEUED:
|
|
+ case MX2_STATE_QUEUED:
|
|
dev_info(icd->parent, "%s (queued)\n", __func__);
|
|
break;
|
|
- case VIDEOBUF_PREPARED:
|
|
- dev_info(icd->parent, "%s (prepared)\n", __func__);
|
|
- break;
|
|
default:
|
|
dev_info(icd->parent, "%s (unknown) %d\n", __func__,
|
|
- vb->state);
|
|
+ buf->state);
|
|
break;
|
|
}
|
|
#endif
|
|
@@ -717,11 +678,9 @@ static void mx2_videobuf_release(struct
|
|
* state. This requires a specific handling for each of the these DMA
|
|
* types.
|
|
*/
|
|
+
|
|
spin_lock_irqsave(&pcdev->lock, flags);
|
|
- if (vb->state == VIDEOBUF_QUEUED) {
|
|
- list_del(&vb->queue);
|
|
- vb->state = VIDEOBUF_ERROR;
|
|
- } else if (cpu_is_mx25() && vb->state == VIDEOBUF_ACTIVE) {
|
|
+ if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
|
|
if (pcdev->fb1_active == buf) {
|
|
pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
|
|
writel(0, pcdev->base_csi + CSIDMASA_FB1);
|
|
@@ -732,30 +691,260 @@ static void mx2_videobuf_release(struct
|
|
pcdev->fb2_active = NULL;
|
|
}
|
|
writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
|
|
- vb->state = VIDEOBUF_ERROR;
|
|
}
|
|
spin_unlock_irqrestore(&pcdev->lock, flags);
|
|
+}
|
|
+
|
|
+static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
|
|
+ int bytesperline)
|
|
+{
|
|
+ struct soc_camera_host *ici =
|
|
+ to_soc_camera_host(icd->parent);
|
|
+ struct mx2_camera_dev *pcdev = ici->priv;
|
|
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
|
|
+
|
|
+ writel((pcdev->s_width << 16) | pcdev->s_height,
|
|
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
|
|
+ writel(prp->cfg.src_pixel,
|
|
+ pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
|
|
+ if (prp->cfg.channel == 1) {
|
|
+ writel((icd->user_width << 16) | icd->user_height,
|
|
+ pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
|
|
+ writel(bytesperline,
|
|
+ pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
|
|
+ writel(prp->cfg.ch1_pixel,
|
|
+ pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
|
|
+ } else { /* channel 2 */
|
|
+ writel((icd->user_width << 16) | icd->user_height,
|
|
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
|
|
+ }
|
|
+
|
|
+ /* Enable interrupts */
|
|
+ writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
|
|
+}
|
|
+
|
|
+static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
|
|
+{
|
|
+ int dir;
|
|
+
|
|
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
|
|
+ unsigned char *s = pcdev->resizing[dir].s;
|
|
+ int len = pcdev->resizing[dir].len;
|
|
+ unsigned int coeff[2] = {0, 0};
|
|
+ unsigned int valid = 0;
|
|
+ int i;
|
|
+
|
|
+ if (len == 0)
|
|
+ continue;
|
|
+
|
|
+ for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
|
|
+ int j;
|
|
+
|
|
+ j = i > 9 ? 1 : 0;
|
|
+ coeff[j] = (coeff[j] << BC_COEF) |
|
|
+ (s[i] & (SZ_COEF - 1));
|
|
+
|
|
+ if (i == 5 || i == 15)
|
|
+ coeff[j] <<= 1;
|
|
+
|
|
+ valid = (valid << 1) | (s[i] >> BC_COEF);
|
|
+ }
|
|
+
|
|
+ valid |= PRP_RZ_VALID_TBL_LEN(len);
|
|
+
|
|
+ if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
|
|
+ valid |= PRP_RZ_VALID_BILINEAR;
|
|
+
|
|
+ if (pcdev->emma_prp->cfg.channel == 1) {
|
|
+ if (dir == RESIZE_DIR_H) {
|
|
+ writel(coeff[0], pcdev->base_emma +
|
|
+ PRP_CH1_RZ_HORI_COEF1);
|
|
+ writel(coeff[1], pcdev->base_emma +
|
|
+ PRP_CH1_RZ_HORI_COEF2);
|
|
+ writel(valid, pcdev->base_emma +
|
|
+ PRP_CH1_RZ_HORI_VALID);
|
|
+ } else {
|
|
+ writel(coeff[0], pcdev->base_emma +
|
|
+ PRP_CH1_RZ_VERT_COEF1);
|
|
+ writel(coeff[1], pcdev->base_emma +
|
|
+ PRP_CH1_RZ_VERT_COEF2);
|
|
+ writel(valid, pcdev->base_emma +
|
|
+ PRP_CH1_RZ_VERT_VALID);
|
|
+ }
|
|
+ } else {
|
|
+ if (dir == RESIZE_DIR_H) {
|
|
+ writel(coeff[0], pcdev->base_emma +
|
|
+ PRP_CH2_RZ_HORI_COEF1);
|
|
+ writel(coeff[1], pcdev->base_emma +
|
|
+ PRP_CH2_RZ_HORI_COEF2);
|
|
+ writel(valid, pcdev->base_emma +
|
|
+ PRP_CH2_RZ_HORI_VALID);
|
|
+ } else {
|
|
+ writel(coeff[0], pcdev->base_emma +
|
|
+ PRP_CH2_RZ_VERT_COEF1);
|
|
+ writel(coeff[1], pcdev->base_emma +
|
|
+ PRP_CH2_RZ_VERT_COEF2);
|
|
+ writel(valid, pcdev->base_emma +
|
|
+ PRP_CH2_RZ_VERT_VALID);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
|
|
+{
|
|
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
|
|
+ struct soc_camera_host *ici =
|
|
+ to_soc_camera_host(icd->parent);
|
|
+ struct mx2_camera_dev *pcdev = ici->priv;
|
|
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
|
|
+ struct vb2_buffer *vb;
|
|
+ struct mx2_buffer *buf;
|
|
+ unsigned long phys;
|
|
+ int bytesperline;
|
|
+
|
|
+ if (cpu_is_mx27()) {
|
|
+ unsigned long flags;
|
|
+ if (count < 2)
|
|
+ return -EINVAL;
|
|
+
|
|
+ spin_lock_irqsave(&pcdev->lock, flags);
|
|
+
|
|
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
|
|
+ internal.queue);
|
|
+ buf->internal.bufnum = 0;
|
|
+ vb = &buf->vb;
|
|
+ buf->state = MX2_STATE_ACTIVE;
|
|
+
|
|
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
|
|
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
|
|
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
|
|
+
|
|
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
|
|
+ internal.queue);
|
|
+ buf->internal.bufnum = 1;
|
|
+ vb = &buf->vb;
|
|
+ buf->state = MX2_STATE_ACTIVE;
|
|
+
|
|
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
|
|
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
|
|
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
|
|
+
|
|
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
|
|
+ icd->current_fmt->host_fmt);
|
|
+ if (bytesperline < 0)
|
|
+ return bytesperline;
|
|
+
|
|
+ /*
|
|
+ * I didn't manage to properly enable/disable the prp
|
|
+ * on a per frame basis during running transfers,
|
|
+ * thus we allocate a buffer here and use it to
|
|
+ * discard frames when no buffer is available.
|
|
+ * Feel free to work on this ;)
|
|
+ */
|
|
+ pcdev->discard_size = icd->user_height * bytesperline;
|
|
+ pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
|
|
+ pcdev->discard_size, &pcdev->discard_buffer_dma,
|
|
+ GFP_KERNEL);
|
|
+ if (!pcdev->discard_buffer)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ pcdev->buf_discard[0].discard = true;
|
|
+ list_add_tail(&pcdev->buf_discard[0].queue,
|
|
+ &pcdev->discard);
|
|
+
|
|
+ pcdev->buf_discard[1].discard = true;
|
|
+ list_add_tail(&pcdev->buf_discard[1].queue,
|
|
+ &pcdev->discard);
|
|
+
|
|
+ mx2_prp_resize_commit(pcdev);
|
|
+
|
|
+ mx27_camera_emma_buf_init(icd, bytesperline);
|
|
+
|
|
+ if (prp->cfg.channel == 1) {
|
|
+ writel(PRP_CNTL_CH1EN |
|
|
+ PRP_CNTL_CSIEN |
|
|
+ prp->cfg.in_fmt |
|
|
+ prp->cfg.out_fmt |
|
|
+ PRP_CNTL_CH1_LEN |
|
|
+ PRP_CNTL_CH1BYP |
|
|
+ PRP_CNTL_CH1_TSKIP(0) |
|
|
+ PRP_CNTL_IN_TSKIP(0),
|
|
+ pcdev->base_emma + PRP_CNTL);
|
|
+ } else {
|
|
+ writel(PRP_CNTL_CH2EN |
|
|
+ PRP_CNTL_CSIEN |
|
|
+ prp->cfg.in_fmt |
|
|
+ prp->cfg.out_fmt |
|
|
+ PRP_CNTL_CH2_LEN |
|
|
+ PRP_CNTL_CH2_TSKIP(0) |
|
|
+ PRP_CNTL_IN_TSKIP(0),
|
|
+ pcdev->base_emma + PRP_CNTL);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&pcdev->lock, flags);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mx2_stop_streaming(struct vb2_queue *q)
|
|
+{
|
|
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
|
|
+ struct soc_camera_host *ici =
|
|
+ to_soc_camera_host(icd->parent);
|
|
+ struct mx2_camera_dev *pcdev = ici->priv;
|
|
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
|
|
+ unsigned long flags;
|
|
+ void *b;
|
|
+ u32 cntl;
|
|
+
|
|
+ if (cpu_is_mx27()) {
|
|
+ spin_lock_irqsave(&pcdev->lock, flags);
|
|
+
|
|
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
|
|
+ if (prp->cfg.channel == 1) {
|
|
+ writel(cntl & ~PRP_CNTL_CH1EN,
|
|
+ pcdev->base_emma + PRP_CNTL);
|
|
+ } else {
|
|
+ writel(cntl & ~PRP_CNTL_CH2EN,
|
|
+ pcdev->base_emma + PRP_CNTL);
|
|
+ }
|
|
+ INIT_LIST_HEAD(&pcdev->capture);
|
|
+ INIT_LIST_HEAD(&pcdev->active_bufs);
|
|
+ INIT_LIST_HEAD(&pcdev->discard);
|
|
+
|
|
+ b = pcdev->discard_buffer;
|
|
+ pcdev->discard_buffer = NULL;
|
|
+
|
|
+ spin_unlock_irqrestore(&pcdev->lock, flags);
|
|
+
|
|
+ dma_free_coherent(ici->v4l2_dev.dev,
|
|
+ pcdev->discard_size, b, pcdev->discard_buffer_dma);
|
|
+ }
|
|
|
|
- free_buffer(vq, buf);
|
|
+ return 0;
|
|
}
|
|
|
|
-static struct videobuf_queue_ops mx2_videobuf_ops = {
|
|
- .buf_setup = mx2_videobuf_setup,
|
|
- .buf_prepare = mx2_videobuf_prepare,
|
|
- .buf_queue = mx2_videobuf_queue,
|
|
- .buf_release = mx2_videobuf_release,
|
|
+static struct vb2_ops mx2_videobuf_ops = {
|
|
+ .queue_setup = mx2_videobuf_setup,
|
|
+ .buf_prepare = mx2_videobuf_prepare,
|
|
+ .buf_queue = mx2_videobuf_queue,
|
|
+ .buf_cleanup = mx2_videobuf_release,
|
|
+ .start_streaming = mx2_start_streaming,
|
|
+ .stop_streaming = mx2_stop_streaming,
|
|
};
|
|
|
|
-static void mx2_camera_init_videobuf(struct videobuf_queue *q,
|
|
+static int mx2_camera_init_videobuf(struct vb2_queue *q,
|
|
struct soc_camera_device *icd)
|
|
{
|
|
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
|
- struct mx2_camera_dev *pcdev = ici->priv;
|
|
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
|
|
+ q->drv_priv = icd;
|
|
+ q->ops = &mx2_videobuf_ops;
|
|
+ q->mem_ops = &vb2_dma_contig_memops;
|
|
+ q->buf_struct_size = sizeof(struct mx2_buffer);
|
|
|
|
- videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev,
|
|
- &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
|
- V4L2_FIELD_NONE, sizeof(struct mx2_buffer),
|
|
- icd, &icd->video_lock);
|
|
+ return vb2_queue_init(q);
|
|
}
|
|
|
|
#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
|
|
@@ -785,82 +974,6 @@ static int mx27_camera_emma_prp_reset(st
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
-static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
|
|
- int bytesperline)
|
|
-{
|
|
- struct soc_camera_host *ici =
|
|
- to_soc_camera_host(icd->parent);
|
|
- struct mx2_camera_dev *pcdev = ici->priv;
|
|
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
|
|
- u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width;
|
|
-
|
|
- if (prp->cfg.channel == 1) {
|
|
- writel(pcdev->discard_buffer_dma,
|
|
- pcdev->base_emma + PRP_DEST_RGB1_PTR);
|
|
- writel(pcdev->discard_buffer_dma,
|
|
- pcdev->base_emma + PRP_DEST_RGB2_PTR);
|
|
-
|
|
- writel(PRP_CNTL_CH1EN |
|
|
- PRP_CNTL_CSIEN |
|
|
- prp->cfg.in_fmt |
|
|
- prp->cfg.out_fmt |
|
|
- PRP_CNTL_CH1_LEN |
|
|
- PRP_CNTL_CH1BYP |
|
|
- PRP_CNTL_CH1_TSKIP(0) |
|
|
- PRP_CNTL_IN_TSKIP(0),
|
|
- pcdev->base_emma + PRP_CNTL);
|
|
-
|
|
- writel((icd->user_width << 16) | icd->user_height,
|
|
- pcdev->base_emma + PRP_SRC_FRAME_SIZE);
|
|
- writel((icd->user_width << 16) | icd->user_height,
|
|
- pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
|
|
- writel(bytesperline,
|
|
- pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
|
|
- writel(prp->cfg.src_pixel,
|
|
- pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
|
|
- writel(prp->cfg.ch1_pixel,
|
|
- pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
|
|
- } else { /* channel 2 */
|
|
- writel(pcdev->discard_buffer_dma,
|
|
- pcdev->base_emma + PRP_DEST_Y_PTR);
|
|
- writel(pcdev->discard_buffer_dma,
|
|
- pcdev->base_emma + PRP_SOURCE_Y_PTR);
|
|
-
|
|
- if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) {
|
|
- writel(pcdev->discard_buffer_dma + imgsize,
|
|
- pcdev->base_emma + PRP_DEST_CB_PTR);
|
|
- writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4),
|
|
- pcdev->base_emma + PRP_DEST_CR_PTR);
|
|
- writel(pcdev->discard_buffer_dma + imgsize,
|
|
- pcdev->base_emma + PRP_SOURCE_CB_PTR);
|
|
- writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4),
|
|
- pcdev->base_emma + PRP_SOURCE_CR_PTR);
|
|
- }
|
|
-
|
|
- writel(PRP_CNTL_CH2EN |
|
|
- PRP_CNTL_CSIEN |
|
|
- prp->cfg.in_fmt |
|
|
- prp->cfg.out_fmt |
|
|
- PRP_CNTL_CH2_LEN |
|
|
- PRP_CNTL_CH2_TSKIP(0) |
|
|
- PRP_CNTL_IN_TSKIP(0),
|
|
- pcdev->base_emma + PRP_CNTL);
|
|
-
|
|
- writel((icd->user_width << 16) | icd->user_height,
|
|
- pcdev->base_emma + PRP_SRC_FRAME_SIZE);
|
|
-
|
|
- writel((icd->user_width << 16) | icd->user_height,
|
|
- pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
|
|
-
|
|
- writel(prp->cfg.src_pixel,
|
|
- pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
|
|
-
|
|
- }
|
|
-
|
|
- /* Enable interrupts */
|
|
- writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
|
|
-}
|
|
-
|
|
static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
|
|
{
|
|
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
|
@@ -939,31 +1052,10 @@ static int mx2_camera_set_bus_param(stru
|
|
if (bytesperline < 0)
|
|
return bytesperline;
|
|
|
|
- if (mx27_camera_emma(pcdev)) {
|
|
+ if (cpu_is_mx27()) {
|
|
ret = mx27_camera_emma_prp_reset(pcdev);
|
|
if (ret)
|
|
return ret;
|
|
-
|
|
- if (pcdev->discard_buffer)
|
|
- dma_free_coherent(ici->v4l2_dev.dev,
|
|
- pcdev->discard_size, pcdev->discard_buffer,
|
|
- pcdev->discard_buffer_dma);
|
|
-
|
|
- /*
|
|
- * I didn't manage to properly enable/disable the prp
|
|
- * on a per frame basis during running transfers,
|
|
- * thus we allocate a buffer here and use it to
|
|
- * discard frames when no buffer is available.
|
|
- * Feel free to work on this ;)
|
|
- */
|
|
- pcdev->discard_size = icd->user_height * bytesperline;
|
|
- pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
|
|
- pcdev->discard_size, &pcdev->discard_buffer_dma,
|
|
- GFP_KERNEL);
|
|
- if (!pcdev->discard_buffer)
|
|
- return -ENOMEM;
|
|
-
|
|
- mx27_camera_emma_buf_init(icd, bytesperline);
|
|
} else if (cpu_is_mx25()) {
|
|
writel((bytesperline * icd->user_height) >> 2,
|
|
pcdev->base_csi + CSIRXCNT);
|
|
@@ -1052,6 +1144,123 @@ static int mx2_camera_get_formats(struct
|
|
return formats;
|
|
}
|
|
|
|
+static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
|
|
+ struct v4l2_mbus_framefmt *mf_in,
|
|
+ struct v4l2_pix_format *pix_out, bool apply)
|
|
+{
|
|
+ int num, den;
|
|
+ unsigned long m;
|
|
+ int i, dir;
|
|
+
|
|
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
|
|
+ struct emma_prp_resize tmprsz;
|
|
+ unsigned char *s = tmprsz.s;
|
|
+ int len = 0;
|
|
+ int in, out;
|
|
+
|
|
+ if (dir == RESIZE_DIR_H) {
|
|
+ in = mf_in->width;
|
|
+ out = pix_out->width;
|
|
+ } else {
|
|
+ in = mf_in->height;
|
|
+ out = pix_out->height;
|
|
+ }
|
|
+
|
|
+ if (in < out)
|
|
+ return -EINVAL;
|
|
+ else if (in == out)
|
|
+ continue;
|
|
+
|
|
+ /* Calculate ratio */
|
|
+ m = gcd(in, out);
|
|
+ num = in / m;
|
|
+ den = out / m;
|
|
+ if (num > RESIZE_NUM_MAX)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if ((num >= 2 * den) && (den == 1) &&
|
|
+ (num < 9) && (!(num & 0x01))) {
|
|
+ int sum = 0;
|
|
+ int j;
|
|
+
|
|
+ /* Average scaling for >= 2:1 ratios */
|
|
+ /* Support can be added for num >=9 and odd values */
|
|
+
|
|
+ tmprsz.algo = RESIZE_ALGO_AVERAGING;
|
|
+ len = num;
|
|
+
|
|
+ for (i = 0; i < (len / 2); i++)
|
|
+ s[i] = 8;
|
|
+
|
|
+ do {
|
|
+ for (i = 0; i < (len / 2); i++) {
|
|
+ s[i] = s[i] >> 1;
|
|
+ sum = 0;
|
|
+ for (j = 0; j < (len / 2); j++)
|
|
+ sum += s[j];
|
|
+ if (sum == 4)
|
|
+ break;
|
|
+ }
|
|
+ } while (sum != 4);
|
|
+
|
|
+ for (i = (len / 2); i < len; i++)
|
|
+ s[i] = s[len - i - 1];
|
|
+
|
|
+ s[len - 1] |= SZ_COEF;
|
|
+ } else {
|
|
+ /* bilinear scaling for < 2:1 ratios */
|
|
+ int v; /* overflow counter */
|
|
+ int coeff, nxt; /* table output */
|
|
+ int in_pos_inc = 2 * den;
|
|
+ int out_pos = num;
|
|
+ int out_pos_inc = 2 * num;
|
|
+ int init_carry = num - den;
|
|
+ int carry = init_carry;
|
|
+
|
|
+ tmprsz.algo = RESIZE_ALGO_BILINEAR;
|
|
+ v = den + in_pos_inc;
|
|
+ do {
|
|
+ coeff = v - out_pos;
|
|
+ out_pos += out_pos_inc;
|
|
+ carry += out_pos_inc;
|
|
+ for (nxt = 0; v < out_pos; nxt++) {
|
|
+ v += in_pos_inc;
|
|
+ carry -= in_pos_inc;
|
|
+ }
|
|
+
|
|
+ if (len > RESIZE_NUM_MAX)
|
|
+ return -EINVAL;
|
|
+
|
|
+ coeff = ((coeff << BC_COEF) +
|
|
+ (in_pos_inc >> 1)) / in_pos_inc;
|
|
+
|
|
+ if (coeff >= (SZ_COEF - 1))
|
|
+ coeff--;
|
|
+
|
|
+ coeff |= SZ_COEF;
|
|
+ s[len] = (unsigned char)coeff;
|
|
+ len++;
|
|
+
|
|
+ for (i = 1; i < nxt; i++) {
|
|
+ if (len >= RESIZE_NUM_MAX)
|
|
+ return -EINVAL;
|
|
+ s[len] = 0;
|
|
+ len++;
|
|
+ }
|
|
+ } while (carry != init_carry);
|
|
+ }
|
|
+ tmprsz.len = len;
|
|
+ if (dir == RESIZE_DIR_H)
|
|
+ mf_in->width = pix_out->width;
|
|
+ else
|
|
+ mf_in->height = pix_out->height;
|
|
+
|
|
+ if (apply)
|
|
+ memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int mx2_camera_set_fmt(struct soc_camera_device *icd,
|
|
struct v4l2_format *f)
|
|
{
|
|
@@ -1063,6 +1272,9 @@ static int mx2_camera_set_fmt(struct soc
|
|
struct v4l2_mbus_framefmt mf;
|
|
int ret;
|
|
|
|
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
|
|
+ __func__, pix->width, pix->height);
|
|
+
|
|
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
|
|
if (!xlate) {
|
|
dev_warn(icd->parent, "Format %x not found\n",
|
|
@@ -1080,6 +1292,22 @@ static int mx2_camera_set_fmt(struct soc
|
|
if (ret < 0 && ret != -ENOIOCTLCMD)
|
|
return ret;
|
|
|
|
+ /* Store width and height returned by the sensor for resizing */
|
|
+ pcdev->s_width = mf.width;
|
|
+ pcdev->s_height = mf.height;
|
|
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
|
|
+ __func__, pcdev->s_width, pcdev->s_height);
|
|
+
|
|
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
|
|
+ xlate->host_fmt->fourcc);
|
|
+
|
|
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
|
|
+ if ((mf.width != pix->width || mf.height != pix->height) &&
|
|
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
|
|
+ if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0)
|
|
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
|
|
+ }
|
|
+
|
|
if (mf.code != xlate->code)
|
|
return -EINVAL;
|
|
|
|
@@ -1089,9 +1317,8 @@ static int mx2_camera_set_fmt(struct soc
|
|
pix->colorspace = mf.colorspace;
|
|
icd->current_fmt = xlate;
|
|
|
|
- if (mx27_camera_emma(pcdev))
|
|
- pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
|
|
- xlate->host_fmt->fourcc);
|
|
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
|
|
+ __func__, pix->width, pix->height);
|
|
|
|
return 0;
|
|
}
|
|
@@ -1104,9 +1331,14 @@ static int mx2_camera_try_fmt(struct soc
|
|
struct v4l2_pix_format *pix = &f->fmt.pix;
|
|
struct v4l2_mbus_framefmt mf;
|
|
__u32 pixfmt = pix->pixelformat;
|
|
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
|
+ struct mx2_camera_dev *pcdev = ici->priv;
|
|
unsigned int width_limit;
|
|
int ret;
|
|
|
|
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
|
|
+ __func__, pix->width, pix->height);
|
|
+
|
|
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
|
|
if (pixfmt && !xlate) {
|
|
dev_warn(icd->parent, "Format %x not found\n", pixfmt);
|
|
@@ -1156,6 +1388,20 @@ static int mx2_camera_try_fmt(struct soc
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
|
|
+ __func__, pcdev->s_width, pcdev->s_height);
|
|
+
|
|
+ /* If the sensor does not support image size try PrP resizing */
|
|
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
|
|
+ xlate->host_fmt->fourcc);
|
|
+
|
|
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
|
|
+ if ((mf.width != pix->width || mf.height != pix->height) &&
|
|
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
|
|
+ if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0)
|
|
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
|
|
+ }
|
|
+
|
|
if (mf.field == V4L2_FIELD_ANY)
|
|
mf.field = V4L2_FIELD_NONE;
|
|
/*
|
|
@@ -1174,6 +1420,9 @@ static int mx2_camera_try_fmt(struct soc
|
|
pix->field = mf.field;
|
|
pix->colorspace = mf.colorspace;
|
|
|
|
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
|
|
+ __func__, pix->width, pix->height);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1187,136 +1436,11 @@ static int mx2_camera_querycap(struct so
|
|
return 0;
|
|
}
|
|
|
|
-static int mx2_camera_reqbufs(struct soc_camera_device *icd,
|
|
- struct v4l2_requestbuffers *p)
|
|
-{
|
|
- int i;
|
|
-
|
|
- for (i = 0; i < p->count; i++) {
|
|
- struct mx2_buffer *buf = container_of(icd->vb_vidq.bufs[i],
|
|
- struct mx2_buffer, vb);
|
|
- INIT_LIST_HEAD(&buf->vb.queue);
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-#ifdef CONFIG_MACH_MX27
|
|
-static void mx27_camera_frame_done(struct mx2_camera_dev *pcdev, int state)
|
|
-{
|
|
- struct videobuf_buffer *vb;
|
|
- struct mx2_buffer *buf;
|
|
- unsigned long flags;
|
|
- int ret;
|
|
-
|
|
- spin_lock_irqsave(&pcdev->lock, flags);
|
|
-
|
|
- if (!pcdev->active) {
|
|
- dev_err(pcdev->dev, "%s called with no active buffer!\n",
|
|
- __func__);
|
|
- goto out;
|
|
- }
|
|
-
|
|
- vb = &pcdev->active->vb;
|
|
- buf = container_of(vb, struct mx2_buffer, vb);
|
|
- WARN_ON(list_empty(&vb->queue));
|
|
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
|
|
- vb, vb->baddr, vb->bsize);
|
|
-
|
|
- /* _init is used to debug races, see comment in pxa_camera_reqbufs() */
|
|
- list_del_init(&vb->queue);
|
|
- vb->state = state;
|
|
- do_gettimeofday(&vb->ts);
|
|
- vb->field_count++;
|
|
-
|
|
- wake_up(&vb->done);
|
|
-
|
|
- if (list_empty(&pcdev->capture)) {
|
|
- pcdev->active = NULL;
|
|
- goto out;
|
|
- }
|
|
-
|
|
- pcdev->active = list_entry(pcdev->capture.next,
|
|
- struct mx2_buffer, vb.queue);
|
|
-
|
|
- vb = &pcdev->active->vb;
|
|
- vb->state = VIDEOBUF_ACTIVE;
|
|
-
|
|
- ret = imx_dma_setup_single(pcdev->dma, videobuf_to_dma_contig(vb),
|
|
- vb->size, (u32)pcdev->base_dma + 0x10, DMA_MODE_READ);
|
|
-
|
|
- if (ret) {
|
|
- vb->state = VIDEOBUF_ERROR;
|
|
- pcdev->active = NULL;
|
|
- wake_up(&vb->done);
|
|
- }
|
|
-
|
|
-out:
|
|
- spin_unlock_irqrestore(&pcdev->lock, flags);
|
|
-}
|
|
-
|
|
-static void mx27_camera_dma_err_callback(int channel, void *data, int err)
|
|
-{
|
|
- struct mx2_camera_dev *pcdev = data;
|
|
-
|
|
- mx27_camera_frame_done(pcdev, VIDEOBUF_ERROR);
|
|
-}
|
|
-
|
|
-static void mx27_camera_dma_callback(int channel, void *data)
|
|
-{
|
|
- struct mx2_camera_dev *pcdev = data;
|
|
-
|
|
- mx27_camera_frame_done(pcdev, VIDEOBUF_DONE);
|
|
-}
|
|
-
|
|
-#define DMA_REQ_CSI_RX 31 /* FIXME: Add this to a resource */
|
|
-
|
|
-static int __devinit mx27_camera_dma_init(struct platform_device *pdev,
|
|
- struct mx2_camera_dev *pcdev)
|
|
-{
|
|
- int err;
|
|
-
|
|
- pcdev->dma = imx_dma_request_by_prio("CSI RX DMA", DMA_PRIO_HIGH);
|
|
- if (pcdev->dma < 0) {
|
|
- dev_err(&pdev->dev, "%s failed to request DMA channel\n",
|
|
- __func__);
|
|
- return pcdev->dma;
|
|
- }
|
|
-
|
|
- err = imx_dma_setup_handlers(pcdev->dma, mx27_camera_dma_callback,
|
|
- mx27_camera_dma_err_callback, pcdev);
|
|
- if (err) {
|
|
- dev_err(&pdev->dev, "%s failed to set DMA callback\n",
|
|
- __func__);
|
|
- goto err_out;
|
|
- }
|
|
-
|
|
- err = imx_dma_config_channel(pcdev->dma,
|
|
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO,
|
|
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
|
|
- DMA_REQ_CSI_RX, 1);
|
|
- if (err) {
|
|
- dev_err(&pdev->dev, "%s failed to config DMA channel\n",
|
|
- __func__);
|
|
- goto err_out;
|
|
- }
|
|
-
|
|
- imx_dma_config_burstlen(pcdev->dma, 64);
|
|
-
|
|
- return 0;
|
|
-
|
|
-err_out:
|
|
- imx_dma_free(pcdev->dma);
|
|
-
|
|
- return err;
|
|
-}
|
|
-#endif /* CONFIG_MACH_MX27 */
|
|
-
|
|
static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
|
|
{
|
|
struct soc_camera_device *icd = file->private_data;
|
|
|
|
- return videobuf_poll_stream(file, &icd->vb_vidq, pt);
|
|
+ return vb2_poll(&icd->vb2_vidq, file, pt);
|
|
}
|
|
|
|
static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
|
|
@@ -1327,144 +1451,148 @@ static struct soc_camera_host_ops mx2_so
|
|
.set_crop = mx2_camera_set_crop,
|
|
.get_formats = mx2_camera_get_formats,
|
|
.try_fmt = mx2_camera_try_fmt,
|
|
- .init_videobuf = mx2_camera_init_videobuf,
|
|
- .reqbufs = mx2_camera_reqbufs,
|
|
+ .init_videobuf2 = mx2_camera_init_videobuf,
|
|
.poll = mx2_camera_poll,
|
|
.querycap = mx2_camera_querycap,
|
|
.set_bus_param = mx2_camera_set_bus_param,
|
|
};
|
|
|
|
static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
|
|
- int bufnum, int state)
|
|
+ int bufnum, bool err)
|
|
{
|
|
- u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width;
|
|
+#ifdef DEBUG
|
|
struct mx2_fmt_cfg *prp = pcdev->emma_prp;
|
|
+#endif
|
|
+ struct mx2_buf_internal *ibuf;
|
|
struct mx2_buffer *buf;
|
|
- struct videobuf_buffer *vb;
|
|
+ struct vb2_buffer *vb;
|
|
unsigned long phys;
|
|
|
|
- if (!list_empty(&pcdev->active_bufs)) {
|
|
- buf = list_entry(pcdev->active_bufs.next,
|
|
- struct mx2_buffer, vb.queue);
|
|
+ ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
|
|
+ queue);
|
|
|
|
- BUG_ON(buf->bufnum != bufnum);
|
|
+ BUG_ON(ibuf->bufnum != bufnum);
|
|
+
|
|
+ if (ibuf->discard) {
|
|
+ /*
|
|
+ * Discard buffer must not be returned to user space.
|
|
+ * Just return it to the discard queue.
|
|
+ */
|
|
+ list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
|
|
+ } else {
|
|
+ buf = mx2_ibuf_to_buf(ibuf);
|
|
|
|
vb = &buf->vb;
|
|
#ifdef DEBUG
|
|
- phys = videobuf_to_dma_contig(vb);
|
|
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
|
|
if (prp->cfg.channel == 1) {
|
|
if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
|
|
4 * bufnum) != phys) {
|
|
- dev_err(pcdev->dev, "%p != %p\n", phys,
|
|
- readl(pcdev->base_emma +
|
|
- PRP_DEST_RGB1_PTR +
|
|
- 4 * bufnum));
|
|
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
|
|
+ readl(pcdev->base_emma +
|
|
+ PRP_DEST_RGB1_PTR + 4 * bufnum));
|
|
}
|
|
} else {
|
|
if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
|
|
0x14 * bufnum) != phys) {
|
|
- dev_err(pcdev->dev, "%p != %p\n", phys,
|
|
- readl(pcdev->base_emma +
|
|
- PRP_DEST_Y_PTR -
|
|
- 0x14 * bufnum));
|
|
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
|
|
+ readl(pcdev->base_emma +
|
|
+ PRP_DEST_Y_PTR - 0x14 * bufnum));
|
|
}
|
|
}
|
|
#endif
|
|
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb,
|
|
- vb->baddr, vb->bsize);
|
|
-
|
|
- list_del(&vb->queue);
|
|
- vb->state = state;
|
|
- do_gettimeofday(&vb->ts);
|
|
- vb->field_count = pcdev->frame_count * 2;
|
|
- pcdev->frame_count++;
|
|
-
|
|
- wake_up(&vb->done);
|
|
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
|
|
+ vb2_plane_vaddr(vb, 0),
|
|
+ vb2_get_plane_payload(vb, 0));
|
|
+
|
|
+ list_del_init(&buf->internal.queue);
|
|
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
|
|
+ vb->v4l2_buf.sequence = pcdev->frame_count;
|
|
+ if (err)
|
|
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
|
|
+ else
|
|
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
|
|
}
|
|
|
|
+ pcdev->frame_count++;
|
|
+
|
|
if (list_empty(&pcdev->capture)) {
|
|
- if (prp->cfg.channel == 1) {
|
|
- writel(pcdev->discard_buffer_dma, pcdev->base_emma +
|
|
- PRP_DEST_RGB1_PTR + 4 * bufnum);
|
|
- } else {
|
|
- writel(pcdev->discard_buffer_dma, pcdev->base_emma +
|
|
- PRP_DEST_Y_PTR -
|
|
- 0x14 * bufnum);
|
|
- if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
|
|
- writel(pcdev->discard_buffer_dma + imgsize,
|
|
- pcdev->base_emma + PRP_DEST_CB_PTR -
|
|
- 0x14 * bufnum);
|
|
- writel(pcdev->discard_buffer_dma +
|
|
- ((5 * imgsize) / 4), pcdev->base_emma +
|
|
- PRP_DEST_CR_PTR - 0x14 * bufnum);
|
|
- }
|
|
+ if (list_empty(&pcdev->discard)) {
|
|
+ dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
|
|
+ __func__);
|
|
+ return;
|
|
}
|
|
+
|
|
+ ibuf = list_first_entry(&pcdev->discard,
|
|
+ struct mx2_buf_internal, queue);
|
|
+ ibuf->bufnum = bufnum;
|
|
+
|
|
+ list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
|
|
+ mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
|
|
return;
|
|
}
|
|
|
|
- buf = list_entry(pcdev->capture.next,
|
|
- struct mx2_buffer, vb.queue);
|
|
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
|
|
+ internal.queue);
|
|
|
|
- buf->bufnum = !bufnum;
|
|
+ buf->internal.bufnum = bufnum;
|
|
|
|
list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
|
|
|
|
vb = &buf->vb;
|
|
- vb->state = VIDEOBUF_ACTIVE;
|
|
+ buf->state = MX2_STATE_ACTIVE;
|
|
|
|
- phys = videobuf_to_dma_contig(vb);
|
|
- if (prp->cfg.channel == 1) {
|
|
- writel(phys, pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum);
|
|
- } else {
|
|
- writel(phys, pcdev->base_emma +
|
|
- PRP_DEST_Y_PTR - 0x14 * bufnum);
|
|
- if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) {
|
|
- writel(phys + imgsize, pcdev->base_emma +
|
|
- PRP_DEST_CB_PTR - 0x14 * bufnum);
|
|
- writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
|
|
- PRP_DEST_CR_PTR - 0x14 * bufnum);
|
|
- }
|
|
- }
|
|
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
|
|
+ mx27_update_emma_buf(pcdev, phys, bufnum);
|
|
}
|
|
|
|
static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
|
|
{
|
|
struct mx2_camera_dev *pcdev = data;
|
|
unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
|
|
- struct mx2_buffer *buf;
|
|
+ struct mx2_buf_internal *ibuf;
|
|
+
|
|
+ spin_lock(&pcdev->lock);
|
|
+
|
|
+ if (list_empty(&pcdev->active_bufs)) {
|
|
+ dev_warn(pcdev->dev, "%s: called while active list is empty\n",
|
|
+ __func__);
|
|
+
|
|
+ if (!status) {
|
|
+ spin_unlock(&pcdev->lock);
|
|
+ return IRQ_NONE;
|
|
+ }
|
|
+ }
|
|
|
|
if (status & (1 << 7)) { /* overflow */
|
|
- u32 cntl;
|
|
- /*
|
|
- * We only disable channel 1 here since this is the only
|
|
- * enabled channel
|
|
- *
|
|
- * FIXME: the correct DMA overflow handling should be resetting
|
|
- * the buffer, returning an error frame, and continuing with
|
|
- * the next one.
|
|
- */
|
|
- cntl = readl(pcdev->base_emma + PRP_CNTL);
|
|
+ u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
|
|
writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
|
|
pcdev->base_emma + PRP_CNTL);
|
|
writel(cntl, pcdev->base_emma + PRP_CNTL);
|
|
- }
|
|
- if ((((status & (3 << 5)) == (3 << 5)) ||
|
|
- ((status & (3 << 3)) == (3 << 3)))
|
|
- && !list_empty(&pcdev->active_bufs)) {
|
|
+
|
|
+ ibuf = list_first_entry(&pcdev->active_bufs,
|
|
+ struct mx2_buf_internal, queue);
|
|
+ mx27_camera_frame_done_emma(pcdev,
|
|
+ ibuf->bufnum, true);
|
|
+
|
|
+ status &= ~(1 << 7);
|
|
+ } else if (((status & (3 << 5)) == (3 << 5)) ||
|
|
+ ((status & (3 << 3)) == (3 << 3))) {
|
|
/*
|
|
* Both buffers have triggered, process the one we're expecting
|
|
* to first
|
|
*/
|
|
- buf = list_entry(pcdev->active_bufs.next,
|
|
- struct mx2_buffer, vb.queue);
|
|
- mx27_camera_frame_done_emma(pcdev, buf->bufnum, VIDEOBUF_DONE);
|
|
- status &= ~(1 << (6 - buf->bufnum)); /* mark processed */
|
|
- }
|
|
- if ((status & (1 << 6)) || (status & (1 << 4)))
|
|
- mx27_camera_frame_done_emma(pcdev, 0, VIDEOBUF_DONE);
|
|
- if ((status & (1 << 5)) || (status & (1 << 3)))
|
|
- mx27_camera_frame_done_emma(pcdev, 1, VIDEOBUF_DONE);
|
|
+ ibuf = list_first_entry(&pcdev->active_bufs,
|
|
+ struct mx2_buf_internal, queue);
|
|
+ mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
|
|
+ status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
|
|
+ } else if ((status & (1 << 6)) || (status & (1 << 4))) {
|
|
+ mx27_camera_frame_done_emma(pcdev, 0, false);
|
|
+ } else if ((status & (1 << 5)) || (status & (1 << 3))) {
|
|
+ mx27_camera_frame_done_emma(pcdev, 1, false);
|
|
+ }
|
|
|
|
+ spin_unlock(&pcdev->lock);
|
|
writel(status, pcdev->base_emma + PRP_INTRSTATUS);
|
|
|
|
return IRQ_HANDLED;
|
|
@@ -1527,8 +1655,6 @@ static int __devinit mx2_camera_probe(st
|
|
struct resource *res_csi, *res_emma;
|
|
void __iomem *base_csi;
|
|
int irq_csi, irq_emma;
|
|
- irq_handler_t mx2_cam_irq_handler = cpu_is_mx25() ? mx25_camera_irq
|
|
- : mx27_camera_irq;
|
|
int err = 0;
|
|
|
|
dev_dbg(&pdev->dev, "initialising\n");
|
|
@@ -1550,22 +1676,11 @@ static int __devinit mx2_camera_probe(st
|
|
|
|
pcdev->clk_csi = clk_get(&pdev->dev, NULL);
|
|
if (IS_ERR(pcdev->clk_csi)) {
|
|
+ dev_err(&pdev->dev, "Could not get csi clock\n");
|
|
err = PTR_ERR(pcdev->clk_csi);
|
|
goto exit_kfree;
|
|
}
|
|
|
|
- dev_dbg(&pdev->dev, "Camera clock frequency: %ld\n",
|
|
- clk_get_rate(pcdev->clk_csi));
|
|
-
|
|
- /* Initialize DMA */
|
|
-#ifdef CONFIG_MACH_MX27
|
|
- if (cpu_is_mx27()) {
|
|
- err = mx27_camera_dma_init(pdev, pcdev);
|
|
- if (err)
|
|
- goto exit_clk_put;
|
|
- }
|
|
-#endif /* CONFIG_MACH_MX27 */
|
|
-
|
|
pcdev->res_csi = res_csi;
|
|
pcdev->pdata = pdev->dev.platform_data;
|
|
if (pcdev->pdata) {
|
|
@@ -1585,6 +1700,7 @@ static int __devinit mx2_camera_probe(st
|
|
|
|
INIT_LIST_HEAD(&pcdev->capture);
|
|
INIT_LIST_HEAD(&pcdev->active_bufs);
|
|
+ INIT_LIST_HEAD(&pcdev->discard);
|
|
spin_lock_init(&pcdev->lock);
|
|
|
|
/*
|
|
@@ -1606,11 +1722,13 @@ static int __devinit mx2_camera_probe(st
|
|
pcdev->base_dma = res_csi->start;
|
|
pcdev->dev = &pdev->dev;
|
|
|
|
- err = request_irq(pcdev->irq_csi, mx2_cam_irq_handler, 0,
|
|
- MX2_CAM_DRV_NAME, pcdev);
|
|
- if (err) {
|
|
- dev_err(pcdev->dev, "Camera interrupt register failed \n");
|
|
- goto exit_iounmap;
|
|
+ if (cpu_is_mx25()) {
|
|
+ err = request_irq(pcdev->irq_csi, mx25_camera_irq, 0,
|
|
+ MX2_CAM_DRV_NAME, pcdev);
|
|
+ if (err) {
|
|
+ dev_err(pcdev->dev, "Camera interrupt register failed \n");
|
|
+ goto exit_iounmap;
|
|
+ }
|
|
}
|
|
|
|
if (cpu_is_mx27()) {
|
|
@@ -1618,14 +1736,15 @@ static int __devinit mx2_camera_probe(st
|
|
res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
irq_emma = platform_get_irq(pdev, 1);
|
|
|
|
- if (res_emma && irq_emma >= 0) {
|
|
- dev_info(&pdev->dev, "Using EMMA\n");
|
|
- pcdev->use_emma = 1;
|
|
- pcdev->res_emma = res_emma;
|
|
- pcdev->irq_emma = irq_emma;
|
|
- if (mx27_camera_emma_init(pcdev))
|
|
- goto exit_free_irq;
|
|
+ if (!res_emma || !irq_emma) {
|
|
+ dev_err(&pdev->dev, "no EMMA resources\n");
|
|
+ goto exit_free_irq;
|
|
}
|
|
+
|
|
+ pcdev->res_emma = res_emma;
|
|
+ pcdev->irq_emma = irq_emma;
|
|
+ if (mx27_camera_emma_init(pcdev))
|
|
+ goto exit_free_irq;
|
|
}
|
|
|
|
pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME,
|
|
@@ -1633,6 +1752,12 @@ static int __devinit mx2_camera_probe(st
|
|
pcdev->soc_host.priv = pcdev;
|
|
pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
|
|
pcdev->soc_host.nr = pdev->id;
|
|
+
|
|
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
|
|
+ if (IS_ERR(pcdev->alloc_ctx)) {
|
|
+ err = PTR_ERR(pcdev->alloc_ctx);
|
|
+ goto eallocctx;
|
|
+ }
|
|
err = soc_camera_host_register(&pcdev->soc_host);
|
|
if (err)
|
|
goto exit_free_emma;
|
|
@@ -1643,26 +1768,24 @@ static int __devinit mx2_camera_probe(st
|
|
return 0;
|
|
|
|
exit_free_emma:
|
|
- if (mx27_camera_emma(pcdev)) {
|
|
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
|
|
+eallocctx:
|
|
+ if (cpu_is_mx27()) {
|
|
free_irq(pcdev->irq_emma, pcdev);
|
|
clk_disable(pcdev->clk_emma);
|
|
clk_put(pcdev->clk_emma);
|
|
iounmap(pcdev->base_emma);
|
|
- release_mem_region(res_emma->start, resource_size(res_emma));
|
|
+ release_mem_region(pcdev->res_emma->start, resource_size(pcdev->res_emma));
|
|
}
|
|
exit_free_irq:
|
|
- free_irq(pcdev->irq_csi, pcdev);
|
|
+ if (cpu_is_mx25())
|
|
+ free_irq(pcdev->irq_csi, pcdev);
|
|
exit_iounmap:
|
|
iounmap(base_csi);
|
|
exit_release:
|
|
release_mem_region(res_csi->start, resource_size(res_csi));
|
|
exit_dma_free:
|
|
-#ifdef CONFIG_MACH_MX27
|
|
- if (cpu_is_mx27())
|
|
- imx_dma_free(pcdev->dma);
|
|
-exit_clk_put:
|
|
clk_put(pcdev->clk_csi);
|
|
-#endif /* CONFIG_MACH_MX27 */
|
|
exit_kfree:
|
|
kfree(pcdev);
|
|
exit:
|
|
@@ -1677,19 +1800,18 @@ static int __devexit mx2_camera_remove(s
|
|
struct resource *res;
|
|
|
|
clk_put(pcdev->clk_csi);
|
|
-#ifdef CONFIG_MACH_MX27
|
|
+ if (cpu_is_mx25())
|
|
+ free_irq(pcdev->irq_csi, pcdev);
|
|
if (cpu_is_mx27())
|
|
- imx_dma_free(pcdev->dma);
|
|
-#endif /* CONFIG_MACH_MX27 */
|
|
- free_irq(pcdev->irq_csi, pcdev);
|
|
- if (mx27_camera_emma(pcdev))
|
|
free_irq(pcdev->irq_emma, pcdev);
|
|
|
|
soc_camera_host_unregister(&pcdev->soc_host);
|
|
|
|
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
|
|
+
|
|
iounmap(pcdev->base_csi);
|
|
|
|
- if (mx27_camera_emma(pcdev)) {
|
|
+ if (cpu_is_mx27()) {
|
|
clk_disable(pcdev->clk_emma);
|
|
clk_put(pcdev->clk_emma);
|
|
iounmap(pcdev->base_emma);
|
|
Index: linux-3.3.x86_64/drivers/media/rc/rc-core-priv.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/rc-core-priv.h
|
|
+++ linux-3.3.x86_64/drivers/media/rc/rc-core-priv.h
|
|
@@ -35,7 +35,7 @@ struct ir_raw_event_ctrl {
|
|
struct list_head list; /* to keep track of raw clients */
|
|
struct task_struct *thread;
|
|
spinlock_t lock;
|
|
- struct kfifo kfifo; /* fifo for the pulse/space durations */
|
|
+ struct kfifo_rec_ptr_1 kfifo; /* fifo for the pulse/space durations */
|
|
ktime_t last_event; /* when last event occurred */
|
|
enum raw_event_type last_type; /* last event type */
|
|
struct rc_dev *dev; /* pointer to the parent rc_dev */
|
|
Index: linux-3.3.x86_64/drivers/media/video/s2255drv.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/s2255drv.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/s2255drv.c
|
|
@@ -134,7 +134,7 @@
|
|
|
|
/* usb config commands */
|
|
#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de)
|
|
-#define CMD_2255 cpu_to_le32(0xc2255000)
|
|
+#define CMD_2255 0xc2255000
|
|
#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10))
|
|
#define CMD_START cpu_to_le32((CMD_2255 | 0x20))
|
|
#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30))
|
|
@@ -852,15 +852,13 @@ static int vidioc_querycap(struct file *
|
|
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
struct v4l2_fmtdesc *f)
|
|
{
|
|
- int index = 0;
|
|
- if (f)
|
|
- index = f->index;
|
|
+ int index = f->index;
|
|
|
|
if (index >= ARRAY_SIZE(formats))
|
|
return -EINVAL;
|
|
- if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
|
|
- (formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
|
|
- return -EINVAL;
|
|
+ if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
|
|
+ (formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
|
|
+ return -EINVAL;
|
|
dprintk(4, "name %s\n", formats[index].name);
|
|
strlcpy(f->description, formats[index].name, sizeof(f->description));
|
|
f->pixelformat = formats[index].fourcc;
|
|
@@ -2027,7 +2025,7 @@ static int save_frame(struct s2255_dev *
|
|
pdata[1]);
|
|
offset = jj + PREFIX_SIZE;
|
|
bframe = 1;
|
|
- cc = pdword[1];
|
|
+ cc = le32_to_cpu(pdword[1]);
|
|
if (cc >= MAX_CHANNELS) {
|
|
printk(KERN_ERR
|
|
"bad channel\n");
|
|
@@ -2036,22 +2034,22 @@ static int save_frame(struct s2255_dev *
|
|
/* reverse it */
|
|
dev->cc = G_chnmap[cc];
|
|
channel = &dev->channel[dev->cc];
|
|
- payload = pdword[3];
|
|
+ payload = le32_to_cpu(pdword[3]);
|
|
if (payload > channel->req_image_size) {
|
|
channel->bad_payload++;
|
|
/* discard the bad frame */
|
|
return -EINVAL;
|
|
}
|
|
channel->pkt_size = payload;
|
|
- channel->jpg_size = pdword[4];
|
|
+ channel->jpg_size = le32_to_cpu(pdword[4]);
|
|
break;
|
|
case S2255_MARKER_RESPONSE:
|
|
|
|
pdata += DEF_USB_BLOCK;
|
|
jj += DEF_USB_BLOCK;
|
|
- if (pdword[1] >= MAX_CHANNELS)
|
|
+ if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
|
|
break;
|
|
- cc = G_chnmap[pdword[1]];
|
|
+ cc = G_chnmap[le32_to_cpu(pdword[1])];
|
|
if (cc >= MAX_CHANNELS)
|
|
break;
|
|
channel = &dev->channel[cc];
|
|
@@ -2074,11 +2072,11 @@ static int save_frame(struct s2255_dev *
|
|
wake_up(&dev->fw_data->wait_fw);
|
|
break;
|
|
case S2255_RESPONSE_STATUS:
|
|
- channel->vidstatus = pdword[3];
|
|
+ channel->vidstatus = le32_to_cpu(pdword[3]);
|
|
channel->vidstatus_ready = 1;
|
|
wake_up(&channel->wait_vidstatus);
|
|
dprintk(5, "got vidstatus %x chan %d\n",
|
|
- pdword[3], cc);
|
|
+ le32_to_cpu(pdword[3]), cc);
|
|
break;
|
|
default:
|
|
printk(KERN_INFO "s2255 unknown resp\n");
|
|
@@ -2605,10 +2603,11 @@ static int s2255_probe(struct usb_interf
|
|
__le32 *pRel;
|
|
pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
|
|
printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel);
|
|
- dev->dsp_fw_ver = *pRel;
|
|
- if (*pRel < S2255_CUR_DSP_FWVER)
|
|
+ dev->dsp_fw_ver = le32_to_cpu(*pRel);
|
|
+ if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
|
|
printk(KERN_INFO "s2255: f2255usb.bin out of date.\n");
|
|
- if (dev->pid == 0x2257 && *pRel < S2255_MIN_DSP_COLORFILTER)
|
|
+ if (dev->pid == 0x2257 &&
|
|
+ dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
|
|
printk(KERN_WARNING "s2255: 2257 requires firmware %d"
|
|
" or above.\n", S2255_MIN_DSP_COLORFILTER);
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/common/tuners/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/Makefile
|
|
@@ -28,6 +28,8 @@ obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc
|
|
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 += -Idrivers/media/dvb/dvb-core
|
|
-ccflags-y += -Idrivers/media/dvb/frontends
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7134/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7134/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7134/Makefile
|
|
@@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7
|
|
|
|
obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.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
|
|
+ccflags-y += -I$(srctree)/drivers/media/video
|
|
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7164/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7164/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7164/Makefile
|
|
@@ -4,9 +4,9 @@ saa7164-objs := saa7164-cards.o saa7164-
|
|
|
|
obj-$(CONFIG_VIDEO_SAA7164) += saa7164.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
|
|
+ccflags-y += -I$(srctree)/drivers/media/video
|
|
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
|
|
|
|
ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/Makefile
|
|
@@ -7,8 +7,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o
|
|
obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
|
|
obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.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
|
|
+ccflags-y += -I$(srctree)/drivers/media/video
|
|
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
|
|
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/gspca/gl860/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/gspca/gl860/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/video/gspca/gl860/Makefile
|
|
@@ -6,5 +6,5 @@ gspca_gl860-objs := gl860.o \
|
|
gl860-ov9655.o \
|
|
gl860-mi2020.o
|
|
|
|
-ccflags-y += -Idrivers/media/video/gspca
|
|
+ccflags-y += -I$(srctree)/drivers/media/video/gspca
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/gspca/m5602/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/gspca/m5602/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/video/gspca/m5602/Makefile
|
|
@@ -8,4 +8,4 @@ gspca_m5602-objs := m5602_core.o \
|
|
m5602_s5k83a.o \
|
|
m5602_s5k4aa.o
|
|
|
|
-ccflags-y += -Idrivers/media/video/gspca
|
|
+ccflags-y += -I$(srctree)/drivers/media/video/gspca
|
|
Index: linux-3.3.x86_64/drivers/media/video/gspca/stv06xx/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/gspca/stv06xx/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/video/gspca/stv06xx/Makefile
|
|
@@ -6,5 +6,5 @@ gspca_stv06xx-objs := stv06xx.o \
|
|
stv06xx_pb0100.o \
|
|
stv06xx_st6422.o
|
|
|
|
-ccflags-y += -Idrivers/media/video/gspca
|
|
+ccflags-y += -I$(srctree)/drivers/media/video/gspca
|
|
|
|
Index: linux-3.3.x86_64/Documentation/dvb/cards.txt
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/Documentation/dvb/cards.txt
|
|
+++ linux-3.3.x86_64/Documentation/dvb/cards.txt
|
|
@@ -119,4 +119,5 @@ o Cards based on the Phillips saa7134 PC
|
|
- Compro Videomate DVB-T300
|
|
- Compro Videomate DVB-T200
|
|
- AVerMedia AVerTVHD MCE A180
|
|
+ - KWorld PC150-U ATSC Hybrid
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/rc/keymaps/rc-kworld-pc150u.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/rc/keymaps/rc-kworld-pc150u.c
|
|
@@ -0,0 +1,102 @@
|
|
+/* kworld-pc150u.c - Keytable for kworld_pc150u Remote Controller
|
|
+ *
|
|
+ * keymap imported from ir-keymaps.c
|
|
+ *
|
|
+ * Copyright (c) 2010 by Kyle Strickland
|
|
+ * (based on kworld-plus-tv-analog.c by
|
|
+ * Mauro Carvalho Chehab <mchehab@redhat.com>)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <media/rc-map.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+/* Kworld PC150-U
|
|
+ Kyle Strickland <kyle@kyle.strickland.name>
|
|
+ */
|
|
+
|
|
+static struct rc_map_table kworld_pc150u[] = {
|
|
+ { 0x0c, KEY_MEDIA }, /* Kworld key */
|
|
+ { 0x16, KEY_EJECTCLOSECD }, /* -> ) */
|
|
+ { 0x1d, KEY_POWER2 },
|
|
+
|
|
+ { 0x00, KEY_1 },
|
|
+ { 0x01, KEY_2 },
|
|
+ { 0x02, KEY_3 },
|
|
+ { 0x03, KEY_4 },
|
|
+ { 0x04, KEY_5 },
|
|
+ { 0x05, KEY_6 },
|
|
+ { 0x06, KEY_7 },
|
|
+ { 0x07, KEY_8 },
|
|
+ { 0x08, KEY_9 },
|
|
+ { 0x0a, KEY_0 },
|
|
+
|
|
+ { 0x09, KEY_AGAIN },
|
|
+ { 0x14, KEY_MUTE },
|
|
+
|
|
+ { 0x1e, KEY_LAST },
|
|
+ { 0x17, KEY_ZOOM },
|
|
+ { 0x1f, KEY_HOMEPAGE },
|
|
+ { 0x0e, KEY_ESC },
|
|
+
|
|
+ { 0x20, KEY_UP },
|
|
+ { 0x21, KEY_DOWN },
|
|
+ { 0x42, KEY_LEFT },
|
|
+ { 0x43, KEY_RIGHT },
|
|
+ { 0x0b, KEY_ENTER },
|
|
+
|
|
+ { 0x10, KEY_CHANNELUP },
|
|
+ { 0x11, KEY_CHANNELDOWN },
|
|
+
|
|
+ { 0x13, KEY_VOLUMEUP },
|
|
+ { 0x12, KEY_VOLUMEDOWN },
|
|
+
|
|
+ { 0x19, KEY_TIME}, /* Timeshift */
|
|
+ { 0x1a, KEY_STOP},
|
|
+ { 0x1b, KEY_RECORD},
|
|
+ { 0x4b, KEY_EMAIL},
|
|
+
|
|
+ { 0x40, KEY_REWIND},
|
|
+ { 0x44, KEY_PLAYPAUSE},
|
|
+ { 0x41, KEY_FORWARD},
|
|
+ { 0x22, KEY_TEXT},
|
|
+
|
|
+ { 0x15, KEY_AUDIO}, /* ((*)) */
|
|
+ { 0x0f, KEY_MODE}, /* display ratio */
|
|
+ { 0x1c, KEY_SYSRQ}, /* snapshot */
|
|
+ { 0x4a, KEY_SLEEP}, /* sleep timer */
|
|
+
|
|
+ { 0x48, KEY_SOUND}, /* switch theater mode */
|
|
+ { 0x49, KEY_BLUE}, /* A */
|
|
+ { 0x18, KEY_RED}, /* B */
|
|
+ { 0x23, KEY_GREEN}, /* C */
|
|
+};
|
|
+
|
|
+static struct rc_map_list kworld_pc150u_map = {
|
|
+ .map = {
|
|
+ .scan = kworld_pc150u,
|
|
+ .size = ARRAY_SIZE(kworld_pc150u),
|
|
+ .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
|
|
+ .name = RC_MAP_KWORLD_PC150U,
|
|
+ }
|
|
+};
|
|
+
|
|
+static int __init init_rc_map_kworld_pc150u(void)
|
|
+{
|
|
+ return rc_map_register(&kworld_pc150u_map);
|
|
+}
|
|
+
|
|
+static void __exit exit_rc_map_kworld_pc150u(void)
|
|
+{
|
|
+ rc_map_unregister(&kworld_pc150u_map);
|
|
+}
|
|
+
|
|
+module_init(init_rc_map_kworld_pc150u)
|
|
+module_exit(exit_rc_map_kworld_pc150u)
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_AUTHOR("Kyle Strickland <kyle@kyle.strickland.name>");
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7134/saa7134-cards.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7134/saa7134-cards.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7134/saa7134-cards.c
|
|
@@ -33,6 +33,7 @@
|
|
#include "tea5767.h"
|
|
#include "tda18271.h"
|
|
#include "xc5000.h"
|
|
+#include "s5h1411.h"
|
|
|
|
/* commly used strings */
|
|
static char name_mute[] = "mute";
|
|
@@ -5712,6 +5713,36 @@ struct saa7134_board saa7134_boards[] =
|
|
.amux = LINE1,
|
|
} },
|
|
},
|
|
+ [SAA7134_BOARD_KWORLD_PC150U] = {
|
|
+ .name = "Kworld PC150-U",
|
|
+ .audio_clock = 0x00187de7,
|
|
+ .tuner_type = TUNER_PHILIPS_TDA8290,
|
|
+ .radio_type = UNSET,
|
|
+ .tuner_addr = ADDR_UNSET,
|
|
+ .radio_addr = ADDR_UNSET,
|
|
+ .mpeg = SAA7134_MPEG_DVB,
|
|
+ .gpiomask = 1 << 21,
|
|
+ .ts_type = SAA7134_MPEG_TS_PARALLEL,
|
|
+ .inputs = { {
|
|
+ .name = name_tv,
|
|
+ .vmux = 1,
|
|
+ .amux = TV,
|
|
+ .tv = 1,
|
|
+ }, {
|
|
+ .name = name_comp,
|
|
+ .vmux = 3,
|
|
+ .amux = LINE1,
|
|
+ }, {
|
|
+ .name = name_svideo,
|
|
+ .vmux = 8,
|
|
+ .amux = LINE2,
|
|
+ } },
|
|
+ .radio = {
|
|
+ .name = name_radio,
|
|
+ .amux = TV,
|
|
+ .gpio = 0x0000000,
|
|
+ },
|
|
+ },
|
|
|
|
};
|
|
|
|
@@ -6306,6 +6337,12 @@ struct pci_device_id saa7134_pci_tbl[] =
|
|
.driver_data = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */
|
|
},{
|
|
.vendor = PCI_VENDOR_ID_PHILIPS,
|
|
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
|
|
+ .subvendor = 0x17de,
|
|
+ .subdevice = 0xa134,
|
|
+ .driver_data = SAA7134_BOARD_KWORLD_PC150U,
|
|
+ }, {
|
|
+ .vendor = PCI_VENDOR_ID_PHILIPS,
|
|
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
|
|
.subvendor = 0x1461,
|
|
.subdevice = 0x7360,
|
|
@@ -7134,6 +7171,23 @@ static inline int saa7134_kworld_sbtvd_t
|
|
return 0;
|
|
}
|
|
|
|
+static int saa7134_kworld_pc150u_toggle_agc(struct saa7134_dev *dev,
|
|
+ enum tda18271_mode mode)
|
|
+{
|
|
+ switch (mode) {
|
|
+ case TDA18271_ANALOG:
|
|
+ saa7134_set_gpio(dev, 18, 0);
|
|
+ break;
|
|
+ case TDA18271_DIGITAL:
|
|
+ saa7134_set_gpio(dev, 18, 1);
|
|
+ msleep(30);
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev,
|
|
int command, int arg)
|
|
{
|
|
@@ -7150,6 +7204,9 @@ static int saa7134_tda8290_18271_callbac
|
|
case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
|
|
ret = saa7134_kworld_sbtvd_toggle_agc(dev, arg);
|
|
break;
|
|
+ case SAA7134_BOARD_KWORLD_PC150U:
|
|
+ ret = saa7134_kworld_pc150u_toggle_agc(dev, arg);
|
|
+ break;
|
|
default:
|
|
break;
|
|
}
|
|
@@ -7171,6 +7228,7 @@ static int saa7134_tda8290_callback(stru
|
|
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
|
|
case SAA7134_BOARD_AVERMEDIA_M733A:
|
|
case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
|
|
+ case SAA7134_BOARD_KWORLD_PC150U:
|
|
case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
|
|
/* tda8290 + tda18271 */
|
|
ret = saa7134_tda8290_18271_callback(dev, command, arg);
|
|
@@ -7452,6 +7510,7 @@ int saa7134_board_init1(struct saa7134_d
|
|
case SAA7134_BOARD_BEHOLD_X7:
|
|
case SAA7134_BOARD_BEHOLD_H7:
|
|
case SAA7134_BOARD_BEHOLD_A7:
|
|
+ case SAA7134_BOARD_KWORLD_PC150U:
|
|
dev->has_remote = SAA7134_REMOTE_I2C;
|
|
break;
|
|
case SAA7134_BOARD_AVERMEDIA_A169_B:
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7134/saa7134-dvb.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7134/saa7134-dvb.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7134/saa7134-dvb.c
|
|
@@ -61,6 +61,7 @@
|
|
#include "zl10036.h"
|
|
#include "zl10039.h"
|
|
#include "mt312.h"
|
|
+#include "s5h1411.h"
|
|
|
|
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
|
|
MODULE_LICENSE("GPL");
|
|
@@ -1158,6 +1159,33 @@ static struct tda18271_config prohdtv_pr
|
|
.output_opt = TDA18271_OUTPUT_LT_OFF,
|
|
};
|
|
|
|
+static struct tda18271_std_map kworld_tda18271_std_map = {
|
|
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3,
|
|
+ .if_lvl = 6, .rfagc_top = 0x37 },
|
|
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
|
|
+ .if_lvl = 6, .rfagc_top = 0x37 },
|
|
+};
|
|
+
|
|
+static struct tda18271_config kworld_pc150u_tda18271_config = {
|
|
+ .std_map = &kworld_tda18271_std_map,
|
|
+ .gate = TDA18271_GATE_ANALOG,
|
|
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
|
|
+ .config = 3, /* Use tuner callback for AGC */
|
|
+ .rf_cal_on_startup = 1
|
|
+};
|
|
+
|
|
+static struct s5h1411_config kworld_s5h1411_config = {
|
|
+ .output_mode = S5H1411_PARALLEL_OUTPUT,
|
|
+ .gpio = S5H1411_GPIO_OFF,
|
|
+ .qam_if = S5H1411_IF_4000,
|
|
+ .vsb_if = S5H1411_IF_3250,
|
|
+ .inversion = S5H1411_INVERSION_ON,
|
|
+ .status_mode = S5H1411_DEMODLOCKING,
|
|
+ .mpeg_timing =
|
|
+ S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
|
|
+};
|
|
+
|
|
+
|
|
/* ==================================================================
|
|
* Core code
|
|
*/
|
|
@@ -1438,6 +1466,22 @@ static int dvb_init(struct saa7134_dev *
|
|
&dev->i2c_adap, 0x61,
|
|
TUNER_PHILIPS_TUV1236D);
|
|
break;
|
|
+ case SAA7134_BOARD_KWORLD_PC150U:
|
|
+ saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */
|
|
+ saa7134_tuner_callback(dev, 0,
|
|
+ TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
|
|
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
|
|
+ &kworld_s5h1411_config,
|
|
+ &dev->i2c_adap);
|
|
+ if (fe0->dvb.frontend != NULL) {
|
|
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
|
|
+ &dev->i2c_adap, 0x4b,
|
|
+ &tda829x_no_probe);
|
|
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
|
|
+ 0x60, &dev->i2c_adap,
|
|
+ &kworld_pc150u_tda18271_config);
|
|
+ }
|
|
+ break;
|
|
case SAA7134_BOARD_FLYDVBS_LR300:
|
|
fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
|
|
&dev->i2c_adap);
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7134/saa7134-i2c.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7134/saa7134-i2c.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7134/saa7134-i2c.c
|
|
@@ -254,7 +254,9 @@ static int saa7134_i2c_xfer(struct i2c_a
|
|
addr = msgs[i].addr << 1;
|
|
if (msgs[i].flags & I2C_M_RD)
|
|
addr |= 1;
|
|
- if (i > 0 && msgs[i].flags & I2C_M_RD && msgs[i].addr != 0x40) {
|
|
+ if (i > 0 && msgs[i].flags &
|
|
+ I2C_M_RD && msgs[i].addr != 0x40 &&
|
|
+ msgs[i].addr != 0x19) {
|
|
/* workaround for a saa7134 i2c bug
|
|
* needed to talk to the mt352 demux
|
|
* thanks to pinnacle for the hint */
|
|
@@ -279,6 +281,16 @@ static int saa7134_i2c_xfer(struct i2c_a
|
|
d1printk("%02x", rc);
|
|
msgs[i].buf[byte] = rc;
|
|
}
|
|
+ /* discard mysterious extra byte when reading
|
|
+ from Samsung S5H1411. i2c bus gets error
|
|
+ if we do not. */
|
|
+ if (0x19 == msgs[i].addr) {
|
|
+ d1printk(" ?");
|
|
+ rc = i2c_recv_byte(dev);
|
|
+ if (rc < 0)
|
|
+ goto err;
|
|
+ d1printk("%02x", rc);
|
|
+ }
|
|
} else {
|
|
/* write bytes */
|
|
d2printk("write bytes\n");
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7134/saa7134-input.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7134/saa7134-input.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7134/saa7134-input.c
|
|
@@ -210,6 +210,54 @@ static int get_key_msi_tvanywhere_plus(s
|
|
return 1;
|
|
}
|
|
|
|
+/* copied and modified from get_key_msi_tvanywhere_plus() */
|
|
+static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key,
|
|
+ u32 *ir_raw)
|
|
+{
|
|
+ unsigned char b;
|
|
+ unsigned int gpio;
|
|
+
|
|
+ /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
|
|
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
|
|
+ if (dev == NULL) {
|
|
+ i2cdprintk("get_key_kworld_pc150u: "
|
|
+ "ir->c->adapter->algo_data is NULL!\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
|
|
+
|
|
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
|
|
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
|
|
+
|
|
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
|
|
+
|
|
+ /* GPIO&0x100 is pulsed low when a button is pressed. Don't do
|
|
+ I2C receive if gpio&0x100 is not low. */
|
|
+
|
|
+ if (gpio & 0x100)
|
|
+ return 0; /* No button press */
|
|
+
|
|
+ /* GPIO says there is a button press. Get it. */
|
|
+
|
|
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
|
|
+ i2cdprintk("read error\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ /* No button press */
|
|
+
|
|
+ if (b == 0xff)
|
|
+ return 0;
|
|
+
|
|
+ /* Button pressed */
|
|
+
|
|
+ dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b);
|
|
+ *ir_key = b;
|
|
+ *ir_raw = b;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
|
|
{
|
|
unsigned char b;
|
|
@@ -894,6 +942,21 @@ void saa7134_probe_i2c_ir(struct saa7134
|
|
info.addr = 0x30;
|
|
/* MSI TV@nywhere Plus controller doesn't seem to
|
|
respond to probes unless we read something from
|
|
+ an existing device. Weird...
|
|
+ REVISIT: might no longer be needed */
|
|
+ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
|
|
+ dprintk("probe 0x%02x @ %s: %s\n",
|
|
+ msg_msi.addr, dev->i2c_adap.name,
|
|
+ (1 == rc) ? "yes" : "no");
|
|
+ break;
|
|
+ case SAA7134_BOARD_KWORLD_PC150U:
|
|
+ /* copied and modified from MSI TV@nywhere Plus */
|
|
+ dev->init_data.name = "Kworld PC150-U";
|
|
+ dev->init_data.get_key = get_key_kworld_pc150u;
|
|
+ dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U;
|
|
+ info.addr = 0x30;
|
|
+ /* MSI TV@nywhere Plus controller doesn't seem to
|
|
+ respond to probes unless we read something from
|
|
an existing device. Weird...
|
|
REVISIT: might no longer be needed */
|
|
rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
|
|
Index: linux-3.3.x86_64/drivers/media/video/saa7134/saa7134.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/saa7134/saa7134.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/saa7134/saa7134.h
|
|
@@ -126,8 +126,8 @@ struct saa7134_card_ir {
|
|
unsigned users;
|
|
|
|
u32 polling;
|
|
- u32 last_gpio;
|
|
- u32 mask_keycode, mask_keydown, mask_keyup;
|
|
+ u32 last_gpio;
|
|
+ u32 mask_keycode, mask_keydown, mask_keyup;
|
|
|
|
bool running;
|
|
bool active;
|
|
@@ -331,6 +331,7 @@ struct saa7134_card_ir {
|
|
#define SAA7134_BOARD_BEHOLD_501 186
|
|
#define SAA7134_BOARD_BEHOLD_503FM 187
|
|
#define SAA7134_BOARD_SENSORAY811_911 188
|
|
+#define SAA7134_BOARD_KWORLD_PC150U 189
|
|
|
|
#define SAA7134_MAXBOARDS 32
|
|
#define SAA7134_INPUT_MAX 8
|
|
Index: linux-3.3.x86_64/drivers/media/video/cx18/cx18-driver.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cx18/cx18-driver.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cx18/cx18-driver.c
|
|
@@ -38,7 +38,7 @@
|
|
#include "cx18-ioctl.h"
|
|
#include "cx18-controls.h"
|
|
#include "tuner-xc2028.h"
|
|
-
|
|
+#include <linux/dma-mapping.h>
|
|
#include <media/tveeprom.h>
|
|
|
|
/* If you have already X v4l cards, then set this to X. This way
|
|
@@ -75,7 +75,7 @@ static int radio[CX18_MAX_CARDS] = { -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1 };
|
|
static unsigned cardtype_c = 1;
|
|
static unsigned tuner_c = 1;
|
|
-static bool radio_c = 1;
|
|
+static unsigned radio_c = 1;
|
|
static char pal[] = "--";
|
|
static char secam[] = "--";
|
|
static char ntsc[] = "-";
|
|
@@ -110,7 +110,7 @@ static int retry_mmio = 1;
|
|
int cx18_debug;
|
|
|
|
module_param_array(tuner, int, &tuner_c, 0644);
|
|
-module_param_array(radio, bool, &radio_c, 0644);
|
|
+module_param_array(radio, int, &radio_c, 0644);
|
|
module_param_array(cardtype, int, &cardtype_c, 0644);
|
|
module_param_string(pal, pal, sizeof(pal), 0644);
|
|
module_param_string(secam, secam, sizeof(secam), 0644);
|
|
@@ -812,7 +812,7 @@ static int cx18_setup_pci(struct cx18 *c
|
|
CX18_ERR("Can't enable device %d!\n", cx->instance);
|
|
return -EIO;
|
|
}
|
|
- if (pci_set_dma_mask(pci_dev, 0xffffffff)) {
|
|
+ if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
|
|
CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
|
|
return -EIO;
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/rc/Kconfig
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/Kconfig
|
|
+++ linux-3.3.x86_64/drivers/media/rc/Kconfig
|
|
@@ -266,4 +266,13 @@ config RC_LOOPBACK
|
|
To compile this driver as a module, choose M here: the module will
|
|
be called rc_loopback.
|
|
|
|
+config IR_GPIO_CIR
|
|
+ tristate "GPIO IR remote control"
|
|
+ depends on RC_CORE
|
|
+ ---help---
|
|
+ Say Y if you want to use GPIO based IR Receiver.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the module will
|
|
+ be called gpio-ir-recv.
|
|
+
|
|
endif #RC_CORE
|
|
Index: linux-3.3.x86_64/drivers/media/rc/Makefile
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/Makefile
|
|
+++ linux-3.3.x86_64/drivers/media/rc/Makefile
|
|
@@ -26,3 +26,4 @@ obj-$(CONFIG_IR_REDRAT3) += redrat3.o
|
|
obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
|
|
obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
|
|
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
|
|
+obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
|
|
Index: linux-3.3.x86_64/drivers/media/rc/gpio-ir-recv.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/rc/gpio-ir-recv.c
|
|
@@ -0,0 +1,205 @@
|
|
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 and
|
|
+ * only 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.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/irq.h>
|
|
+#include <media/rc-core.h>
|
|
+#include <media/gpio-ir-recv.h>
|
|
+
|
|
+#define GPIO_IR_DRIVER_NAME "gpio-rc-recv"
|
|
+#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
|
|
+
|
|
+struct gpio_rc_dev {
|
|
+ struct rc_dev *rcdev;
|
|
+ int gpio_nr;
|
|
+ bool active_low;
|
|
+};
|
|
+
|
|
+static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
|
|
+{
|
|
+ struct gpio_rc_dev *gpio_dev = dev_id;
|
|
+ int gval;
|
|
+ int rc = 0;
|
|
+ enum raw_event_type type = IR_SPACE;
|
|
+
|
|
+ gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
|
|
+
|
|
+ if (gval < 0)
|
|
+ goto err_get_value;
|
|
+
|
|
+ if (gpio_dev->active_low)
|
|
+ gval = !gval;
|
|
+
|
|
+ if (gval == 1)
|
|
+ type = IR_PULSE;
|
|
+
|
|
+ rc = ir_raw_event_store_edge(gpio_dev->rcdev, type);
|
|
+ if (rc < 0)
|
|
+ goto err_get_value;
|
|
+
|
|
+ ir_raw_event_handle(gpio_dev->rcdev);
|
|
+
|
|
+err_get_value:
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct gpio_rc_dev *gpio_dev;
|
|
+ struct rc_dev *rcdev;
|
|
+ const struct gpio_ir_recv_platform_data *pdata =
|
|
+ pdev->dev.platform_data;
|
|
+ int rc;
|
|
+
|
|
+ if (!pdata)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (pdata->gpio_nr < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ gpio_dev = kzalloc(sizeof(struct gpio_rc_dev), GFP_KERNEL);
|
|
+ if (!gpio_dev)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ rcdev = rc_allocate_device();
|
|
+ if (!rcdev) {
|
|
+ rc = -ENOMEM;
|
|
+ goto err_allocate_device;
|
|
+ }
|
|
+
|
|
+ rcdev->driver_type = RC_DRIVER_IR_RAW;
|
|
+ rcdev->allowed_protos = RC_TYPE_ALL;
|
|
+ rcdev->input_name = GPIO_IR_DEVICE_NAME;
|
|
+ rcdev->input_id.bustype = BUS_HOST;
|
|
+ rcdev->driver_name = GPIO_IR_DRIVER_NAME;
|
|
+ rcdev->map_name = RC_MAP_EMPTY;
|
|
+
|
|
+ gpio_dev->rcdev = rcdev;
|
|
+ gpio_dev->gpio_nr = pdata->gpio_nr;
|
|
+ gpio_dev->active_low = pdata->active_low;
|
|
+
|
|
+ rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
|
|
+ if (rc < 0)
|
|
+ goto err_gpio_request;
|
|
+ rc = gpio_direction_input(pdata->gpio_nr);
|
|
+ if (rc < 0)
|
|
+ goto err_gpio_direction_input;
|
|
+
|
|
+ rc = rc_register_device(rcdev);
|
|
+ if (rc < 0) {
|
|
+ dev_err(&pdev->dev, "failed to register rc device\n");
|
|
+ goto err_register_rc_device;
|
|
+ }
|
|
+
|
|
+ platform_set_drvdata(pdev, gpio_dev);
|
|
+
|
|
+ rc = request_any_context_irq(gpio_to_irq(pdata->gpio_nr),
|
|
+ gpio_ir_recv_irq,
|
|
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
|
+ "gpio-ir-recv-irq", gpio_dev);
|
|
+ if (rc < 0)
|
|
+ goto err_request_irq;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_request_irq:
|
|
+ platform_set_drvdata(pdev, NULL);
|
|
+ rc_unregister_device(rcdev);
|
|
+err_register_rc_device:
|
|
+err_gpio_direction_input:
|
|
+ gpio_free(pdata->gpio_nr);
|
|
+err_gpio_request:
|
|
+ rc_free_device(rcdev);
|
|
+ rcdev = NULL;
|
|
+err_allocate_device:
|
|
+ kfree(gpio_dev);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int __devexit gpio_ir_recv_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
|
|
+
|
|
+ free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
|
|
+ platform_set_drvdata(pdev, NULL);
|
|
+ rc_unregister_device(gpio_dev->rcdev);
|
|
+ gpio_free(gpio_dev->gpio_nr);
|
|
+ rc_free_device(gpio_dev->rcdev);
|
|
+ kfree(gpio_dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+static int gpio_ir_recv_suspend(struct device *dev)
|
|
+{
|
|
+ struct platform_device *pdev = to_platform_device(dev);
|
|
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
|
|
+
|
|
+ if (device_may_wakeup(dev))
|
|
+ enable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
|
|
+ else
|
|
+ disable_irq(gpio_to_irq(gpio_dev->gpio_nr));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_ir_recv_resume(struct device *dev)
|
|
+{
|
|
+ struct platform_device *pdev = to_platform_device(dev);
|
|
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
|
|
+
|
|
+ if (device_may_wakeup(dev))
|
|
+ disable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
|
|
+ else
|
|
+ enable_irq(gpio_to_irq(gpio_dev->gpio_nr));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct dev_pm_ops gpio_ir_recv_pm_ops = {
|
|
+ .suspend = gpio_ir_recv_suspend,
|
|
+ .resume = gpio_ir_recv_resume,
|
|
+};
|
|
+#endif
|
|
+
|
|
+static struct platform_driver gpio_ir_recv_driver = {
|
|
+ .probe = gpio_ir_recv_probe,
|
|
+ .remove = __devexit_p(gpio_ir_recv_remove),
|
|
+ .driver = {
|
|
+ .name = GPIO_IR_DRIVER_NAME,
|
|
+ .owner = THIS_MODULE,
|
|
+#ifdef CONFIG_PM
|
|
+ .pm = &gpio_ir_recv_pm_ops,
|
|
+#endif
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init gpio_ir_recv_init(void)
|
|
+{
|
|
+ return platform_driver_register(&gpio_ir_recv_driver);
|
|
+}
|
|
+module_init(gpio_ir_recv_init);
|
|
+
|
|
+static void __exit gpio_ir_recv_exit(void)
|
|
+{
|
|
+ platform_driver_unregister(&gpio_ir_recv_driver);
|
|
+}
|
|
+module_exit(gpio_ir_recv_exit);
|
|
+
|
|
+MODULE_DESCRIPTION("GPIO IR Receiver driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
Index: linux-3.3.x86_64/include/media/gpio-ir-recv.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/include/media/gpio-ir-recv.h
|
|
@@ -0,0 +1,22 @@
|
|
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 and
|
|
+ * only 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.
|
|
+ */
|
|
+
|
|
+#ifndef __GPIO_IR_RECV_H__
|
|
+#define __GPIO_IR_RECV_H__
|
|
+
|
|
+struct gpio_ir_recv_platform_data {
|
|
+ int gpio_nr;
|
|
+ bool active_low;
|
|
+};
|
|
+
|
|
+#endif /* __GPIO_IR_RECV_H__ */
|
|
+
|
|
Index: linux-3.3.x86_64/drivers/media/rc/ir-sony-decoder.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/ir-sony-decoder.c
|
|
+++ linux-3.3.x86_64/drivers/media/rc/ir-sony-decoder.c
|
|
@@ -130,7 +130,7 @@ static int ir_sony_decode(struct rc_dev
|
|
case 15:
|
|
device = bitrev8((data->bits >> 0) & 0xFF);
|
|
subdevice = 0;
|
|
- function = bitrev8((data->bits >> 7) & 0xFD);
|
|
+ function = bitrev8((data->bits >> 7) & 0xFE);
|
|
break;
|
|
case 20:
|
|
device = bitrev8((data->bits >> 5) & 0xF8);
|
|
Index: linux-3.3.x86_64/drivers/media/video/cx25821/cx25821-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cx25821/cx25821-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cx25821/cx25821-core.c
|
|
@@ -1474,8 +1474,13 @@ static DEFINE_PCI_DEVICE_TABLE(cx25821_p
|
|
.device = 0x8210,
|
|
.subvendor = 0x14f1,
|
|
.subdevice = 0x0920,
|
|
- },
|
|
- {
|
|
+ }, {
|
|
+ /* CX25821 No Brand */
|
|
+ .vendor = 0x14f1,
|
|
+ .device = 0x8210,
|
|
+ .subvendor = 0x0000,
|
|
+ .subdevice = 0x0000,
|
|
+ }, {
|
|
/* --- end of list --- */
|
|
}
|
|
};
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/dib0090.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/dib0090.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/dib0090.c
|
|
@@ -519,7 +519,7 @@ static int dib0090_fw_identify(struct dv
|
|
return 0;
|
|
|
|
identification_error:
|
|
- return -EIO;;
|
|
+ return -EIO;
|
|
}
|
|
|
|
static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg)
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/stb0899_drv.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/stb0899_drv.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/stb0899_drv.c
|
|
@@ -67,7 +67,7 @@ static const struct stb0899_tab stb0899_
|
|
* Crude linear extrapolation below -84.8dBm and above -8.0dBm.
|
|
*/
|
|
static const struct stb0899_tab stb0899_dvbsrf_tab[] = {
|
|
- { -950, -128 },
|
|
+ { -750, -128 },
|
|
{ -748, -94 },
|
|
{ -745, -92 },
|
|
{ -735, -90 },
|
|
@@ -131,7 +131,7 @@ static const struct stb0899_tab stb0899_
|
|
{ -730, 13645 },
|
|
{ -750, 13909 },
|
|
{ -766, 14153 },
|
|
- { -999, 16383 }
|
|
+ { -950, 16383 }
|
|
};
|
|
|
|
/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/
|
|
@@ -964,6 +964,7 @@ static int stb0899_read_signal_strength(
|
|
|
|
int val;
|
|
u32 reg;
|
|
+ *strength = 0;
|
|
switch (state->delsys) {
|
|
case SYS_DVBS:
|
|
case SYS_DSS:
|
|
@@ -983,11 +984,11 @@ static int stb0899_read_signal_strength(
|
|
break;
|
|
case SYS_DVBS2:
|
|
if (internal->lock) {
|
|
- reg = STB0899_READ_S2REG(STB0899_DEMOD, IF_AGC_GAIN);
|
|
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_GAIN);
|
|
val = STB0899_GETFIELD(IF_AGC_GAIN, reg);
|
|
|
|
*strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val);
|
|
- *strength += 750;
|
|
+ *strength += 950;
|
|
dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm",
|
|
val & 0x3fff, *strength);
|
|
}
|
|
@@ -1009,6 +1010,7 @@ static int stb0899_read_snr(struct dvb_f
|
|
u8 buf[2];
|
|
u32 reg;
|
|
|
|
+ *snr = 0;
|
|
reg = stb0899_read_reg(state, STB0899_VSTATUS);
|
|
switch (state->delsys) {
|
|
case SYS_DVBS:
|
|
@@ -1071,7 +1073,7 @@ static int stb0899_read_status(struct dv
|
|
reg = stb0899_read_reg(state, STB0899_VSTATUS);
|
|
if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
|
|
dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK");
|
|
- *status |= FE_HAS_CARRIER | FE_HAS_LOCK;
|
|
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
|
|
|
|
reg = stb0899_read_reg(state, STB0899_PLPARM);
|
|
if (STB0899_GETFIELD(VITCURPUN, reg)) {
|
|
Index: linux-3.3.x86_64/include/sound/tea575x-tuner.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/sound/tea575x-tuner.h
|
|
+++ linux-3.3.x86_64/include/sound/tea575x-tuner.h
|
|
@@ -25,6 +25,7 @@
|
|
#include <linux/videodev2.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-dev.h>
|
|
+#include <media/v4l2-device.h>
|
|
|
|
#define TEA575X_FMIF 10700
|
|
|
|
@@ -42,13 +43,16 @@ struct snd_tea575x_ops {
|
|
};
|
|
|
|
struct snd_tea575x {
|
|
+ struct v4l2_device *v4l2_dev;
|
|
struct video_device vd; /* video device */
|
|
+ int radio_nr; /* radio_nr */
|
|
bool tea5759; /* 5759 chip is present */
|
|
+ bool cannot_read_data; /* Device cannot read the data pin */
|
|
bool mute; /* Device is muted? */
|
|
bool stereo; /* receiving stereo */
|
|
bool tuned; /* tuned to a station */
|
|
unsigned int val; /* hw value */
|
|
- unsigned long freq; /* frequency */
|
|
+ u32 freq; /* frequency */
|
|
struct mutex mutex;
|
|
struct snd_tea575x_ops *ops;
|
|
void *private_data;
|
|
Index: linux-3.3.x86_64/sound/i2c/other/tea575x-tuner.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/sound/i2c/other/tea575x-tuner.c
|
|
+++ linux-3.3.x86_64/sound/i2c/other/tea575x-tuner.c
|
|
@@ -25,21 +25,20 @@
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
-#include <linux/version.h>
|
|
+#include <linux/sched.h>
|
|
+#include <media/v4l2-device.h>
|
|
#include <media/v4l2-dev.h>
|
|
+#include <media/v4l2-fh.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-event.h>
|
|
#include <sound/tea575x-tuner.h>
|
|
|
|
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
|
MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
-static int radio_nr = -1;
|
|
-module_param(radio_nr, int, 0);
|
|
-
|
|
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
|
|
-#define FREQ_LO (50UL * 16000)
|
|
-#define FREQ_HI (150UL * 16000)
|
|
+#define FREQ_LO (76U * 16000)
|
|
+#define FREQ_HI (108U * 16000)
|
|
|
|
/*
|
|
* definitions
|
|
@@ -121,28 +120,10 @@ static unsigned int snd_tea575x_read(str
|
|
return data;
|
|
}
|
|
|
|
-static void snd_tea575x_get_freq(struct snd_tea575x *tea)
|
|
-{
|
|
- unsigned long freq;
|
|
-
|
|
- freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK;
|
|
- /* freq *= 12.5 */
|
|
- freq *= 125;
|
|
- freq /= 10;
|
|
- /* crystal fixup */
|
|
- if (tea->tea5759)
|
|
- freq += TEA575X_FMIF;
|
|
- else
|
|
- freq -= TEA575X_FMIF;
|
|
-
|
|
- tea->freq = freq * 16; /* from kHz */
|
|
-}
|
|
-
|
|
static void snd_tea575x_set_freq(struct snd_tea575x *tea)
|
|
{
|
|
- unsigned long freq;
|
|
+ u32 freq = tea->freq;
|
|
|
|
- freq = clamp(tea->freq, FREQ_LO, FREQ_HI);
|
|
freq /= 16; /* to kHz */
|
|
/* crystal fixup */
|
|
if (tea->tea5759)
|
|
@@ -167,12 +148,14 @@ static int vidioc_querycap(struct file *
|
|
{
|
|
struct snd_tea575x *tea = video_drvdata(file);
|
|
|
|
- strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
|
|
+ strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
|
|
strlcpy(v->card, tea->card, sizeof(v->card));
|
|
strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
|
|
strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
|
|
- v->version = RADIO_VERSION;
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
+ v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
+ if (!tea->cannot_read_data)
|
|
+ v->device_caps |= V4L2_CAP_HW_FREQ_SEEK;
|
|
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
|
|
return 0;
|
|
}
|
|
|
|
@@ -191,18 +174,24 @@ static int vidioc_g_tuner(struct file *f
|
|
v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
|
|
v->rangelow = FREQ_LO;
|
|
v->rangehigh = FREQ_HI;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
|
|
- v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
|
|
+ v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
|
|
+ v->audmode = (tea->val & TEA575X_BIT_MONO) ?
|
|
+ V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
|
|
v->signal = tea->tuned ? 0xffff : 0;
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_s_tuner(struct file *file, void *priv,
|
|
struct v4l2_tuner *v)
|
|
{
|
|
- if (v->index > 0)
|
|
+ struct snd_tea575x *tea = video_drvdata(file);
|
|
+
|
|
+ if (v->index)
|
|
return -EINVAL;
|
|
+ tea->val &= ~TEA575X_BIT_MONO;
|
|
+ if (v->audmode == V4L2_TUNER_MODE_MONO)
|
|
+ tea->val |= TEA575X_BIT_MONO;
|
|
+ snd_tea575x_write(tea, tea->val);
|
|
return 0;
|
|
}
|
|
|
|
@@ -214,7 +203,6 @@ static int vidioc_g_frequency(struct fil
|
|
if (f->tuner != 0)
|
|
return -EINVAL;
|
|
f->type = V4L2_TUNER_RADIO;
|
|
- snd_tea575x_get_freq(tea);
|
|
f->frequency = tea->freq;
|
|
return 0;
|
|
}
|
|
@@ -227,32 +215,47 @@ static int vidioc_s_frequency(struct fil
|
|
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
return -EINVAL;
|
|
|
|
- if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
|
|
- return -EINVAL;
|
|
-
|
|
- tea->freq = f->frequency;
|
|
-
|
|
+ tea->val &= ~TEA575X_BIT_SEARCH;
|
|
+ tea->freq = clamp(f->frequency, FREQ_LO, FREQ_HI);
|
|
snd_tea575x_set_freq(tea);
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
-static int vidioc_g_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
+static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
|
|
+ struct v4l2_hw_freq_seek *a)
|
|
{
|
|
- if (a->index > 1)
|
|
- return -EINVAL;
|
|
-
|
|
- strcpy(a->name, "Radio");
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
- return 0;
|
|
-}
|
|
+ struct snd_tea575x *tea = video_drvdata(file);
|
|
|
|
-static int vidioc_s_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- if (a->index != 0)
|
|
+ if (tea->cannot_read_data)
|
|
+ return -ENOTTY;
|
|
+ if (a->tuner || a->wrap_around)
|
|
return -EINVAL;
|
|
+ tea->val |= TEA575X_BIT_SEARCH;
|
|
+ tea->val &= ~TEA575X_BIT_UPDOWN;
|
|
+ if (a->seek_upward)
|
|
+ tea->val |= TEA575X_BIT_UPDOWN;
|
|
+ snd_tea575x_write(tea, tea->val);
|
|
+ for (;;) {
|
|
+ unsigned val = snd_tea575x_read(tea);
|
|
+
|
|
+ if (!(val & TEA575X_BIT_SEARCH)) {
|
|
+ /* Found a frequency */
|
|
+ val &= TEA575X_BIT_FREQ_MASK;
|
|
+ val = (val * 10) / 125;
|
|
+ if (tea->tea5759)
|
|
+ val += TEA575X_FMIF;
|
|
+ else
|
|
+ val -= TEA575X_FMIF;
|
|
+ tea->freq = clamp(val * 16, FREQ_LO, FREQ_HI);
|
|
+ return 0;
|
|
+ }
|
|
+ if (schedule_timeout_interruptible(msecs_to_jiffies(10))) {
|
|
+ /* some signal arrived, stop search */
|
|
+ tea->val &= ~TEA575X_BIT_SEARCH;
|
|
+ snd_tea575x_write(tea, tea->val);
|
|
+ return -ERESTARTSYS;
|
|
+ }
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -273,23 +276,27 @@ static int tea575x_s_ctrl(struct v4l2_ct
|
|
static const struct v4l2_file_operations tea575x_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 tea575x_ioctl_ops = {
|
|
.vidioc_querycap = vidioc_querycap,
|
|
.vidioc_g_tuner = vidioc_g_tuner,
|
|
.vidioc_s_tuner = vidioc_s_tuner,
|
|
- .vidioc_g_audio = vidioc_g_audio,
|
|
- .vidioc_s_audio = vidioc_s_audio,
|
|
.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,
|
|
};
|
|
|
|
-static struct video_device tea575x_radio = {
|
|
- .name = "tea575x-tuner",
|
|
+static const struct video_device tea575x_radio = {
|
|
.fops = &tea575x_fops,
|
|
.ioctl_ops = &tea575x_ioctl_ops,
|
|
- .release = video_device_release_empty,
|
|
+ .release = video_device_release_empty,
|
|
};
|
|
|
|
static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
|
|
@@ -303,11 +310,15 @@ int snd_tea575x_init(struct snd_tea575x
|
|
{
|
|
int retval;
|
|
|
|
- tea->mute = 1;
|
|
+ tea->mute = true;
|
|
|
|
- snd_tea575x_write(tea, 0x55AA);
|
|
- if (snd_tea575x_read(tea) != 0x55AA)
|
|
- return -ENODEV;
|
|
+ /* Not all devices can or know how to read the data back.
|
|
+ Such devices can set cannot_read_data to true. */
|
|
+ if (!tea->cannot_read_data) {
|
|
+ snd_tea575x_write(tea, 0x55AA);
|
|
+ if (snd_tea575x_read(tea) != 0x55AA)
|
|
+ return -ENODEV;
|
|
+ }
|
|
|
|
tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
|
|
tea->freq = 90500 * 16; /* 90.5Mhz default */
|
|
@@ -316,14 +327,17 @@ int snd_tea575x_init(struct snd_tea575x
|
|
tea->vd = tea575x_radio;
|
|
video_set_drvdata(&tea->vd, tea);
|
|
mutex_init(&tea->mutex);
|
|
+ strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
|
|
tea->vd.lock = &tea->mutex;
|
|
+ tea->vd.v4l2_dev = tea->v4l2_dev;
|
|
+ tea->vd.ctrl_handler = &tea->ctrl_handler;
|
|
+ set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
|
|
|
|
v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
|
|
- tea->vd.ctrl_handler = &tea->ctrl_handler;
|
|
v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
|
|
retval = tea->ctrl_handler.error;
|
|
if (retval) {
|
|
- printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
|
|
+ v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
|
|
v4l2_ctrl_handler_free(&tea->ctrl_handler);
|
|
return retval;
|
|
}
|
|
@@ -338,9 +352,9 @@ int snd_tea575x_init(struct snd_tea575x
|
|
|
|
v4l2_ctrl_handler_setup(&tea->ctrl_handler);
|
|
|
|
- retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
|
|
+ retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr);
|
|
if (retval) {
|
|
- printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
|
|
+ v4l2_err(tea->v4l2_dev, "can't register video device!\n");
|
|
v4l2_ctrl_handler_free(&tea->ctrl_handler);
|
|
return retval;
|
|
}
|
|
Index: linux-3.3.x86_64/sound/pci/es1968.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/sound/pci/es1968.c
|
|
+++ linux-3.3.x86_64/sound/pci/es1968.c
|
|
@@ -142,6 +142,7 @@ static int enable_mpu[SNDRV_CARDS] = {[0
|
|
#ifdef SUPPORT_JOYSTICK
|
|
static bool joystick[SNDRV_CARDS];
|
|
#endif
|
|
+static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
|
|
|
|
module_param_array(index, int, NULL, 0444);
|
|
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
|
|
@@ -165,6 +166,9 @@ MODULE_PARM_DESC(enable_mpu, "Enable MPU
|
|
module_param_array(joystick, bool, NULL, 0444);
|
|
MODULE_PARM_DESC(joystick, "Enable joystick.");
|
|
#endif
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|
+
|
|
|
|
|
|
#define NR_APUS 64
|
|
@@ -558,6 +562,7 @@ struct es1968 {
|
|
struct work_struct hwvol_work;
|
|
|
|
#ifdef CONFIG_SND_ES1968_RADIO
|
|
+ struct v4l2_device v4l2_dev;
|
|
struct snd_tea575x tea;
|
|
#endif
|
|
};
|
|
@@ -2613,6 +2618,7 @@ static int snd_es1968_free(struct es1968
|
|
|
|
#ifdef CONFIG_SND_ES1968_RADIO
|
|
snd_tea575x_exit(&chip->tea);
|
|
+ v4l2_device_unregister(&chip->v4l2_dev);
|
|
#endif
|
|
|
|
if (chip->irq >= 0)
|
|
@@ -2655,6 +2661,7 @@ static int __devinit snd_es1968_create(s
|
|
int capt_streams,
|
|
int chip_type,
|
|
int do_pm,
|
|
+ int radio_nr,
|
|
struct es1968 **chip_ret)
|
|
{
|
|
static struct snd_device_ops ops = {
|
|
@@ -2751,7 +2758,14 @@ static int __devinit snd_es1968_create(s
|
|
snd_card_set_dev(card, &pci->dev);
|
|
|
|
#ifdef CONFIG_SND_ES1968_RADIO
|
|
+ err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
|
|
+ if (err < 0) {
|
|
+ snd_es1968_free(chip);
|
|
+ return err;
|
|
+ }
|
|
+ chip->tea.v4l2_dev = &chip->v4l2_dev;
|
|
chip->tea.private_data = chip;
|
|
+ chip->tea.radio_nr = radio_nr;
|
|
chip->tea.ops = &snd_es1968_tea_ops;
|
|
strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card));
|
|
sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
|
|
@@ -2797,6 +2811,7 @@ static int __devinit snd_es1968_probe(st
|
|
pcm_substreams_c[dev],
|
|
pci_id->driver_data,
|
|
use_pm[dev],
|
|
+ radio_nr[dev],
|
|
&chip)) < 0) {
|
|
snd_card_free(card);
|
|
return err;
|
|
Index: linux-3.3.x86_64/sound/pci/fm801.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/sound/pci/fm801.c
|
|
+++ linux-3.3.x86_64/sound/pci/fm801.c
|
|
@@ -58,6 +58,7 @@ static bool enable[SNDRV_CARDS] = SNDRV_
|
|
* High 16-bits are video (radio) device number + 1
|
|
*/
|
|
static int tea575x_tuner[SNDRV_CARDS];
|
|
+static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
|
|
|
|
module_param_array(index, int, NULL, 0444);
|
|
MODULE_PARM_DESC(index, "Index value for the FM801 soundcard.");
|
|
@@ -67,6 +68,9 @@ module_param_array(enable, bool, NULL, 0
|
|
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
|
|
module_param_array(tea575x_tuner, int, NULL, 0444);
|
|
MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only).");
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|
+
|
|
|
|
#define TUNER_DISABLED (1<<3)
|
|
#define TUNER_ONLY (1<<4)
|
|
@@ -197,6 +201,7 @@ struct fm801 {
|
|
struct snd_info_entry *proc_entry;
|
|
|
|
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
|
+ struct v4l2_device v4l2_dev;
|
|
struct snd_tea575x tea;
|
|
#endif
|
|
|
|
@@ -1154,8 +1159,10 @@ static int snd_fm801_free(struct fm801 *
|
|
|
|
__end_hw:
|
|
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
|
- if (!(chip->tea575x_tuner & TUNER_DISABLED))
|
|
+ if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
|
|
snd_tea575x_exit(&chip->tea);
|
|
+ v4l2_device_unregister(&chip->v4l2_dev);
|
|
+ }
|
|
#endif
|
|
if (chip->irq >= 0)
|
|
free_irq(chip->irq, chip);
|
|
@@ -1175,6 +1182,7 @@ static int snd_fm801_dev_free(struct snd
|
|
static int __devinit snd_fm801_create(struct snd_card *card,
|
|
struct pci_dev * pci,
|
|
int tea575x_tuner,
|
|
+ int radio_nr,
|
|
struct fm801 ** rchip)
|
|
{
|
|
struct fm801 *chip;
|
|
@@ -1234,6 +1242,13 @@ static int __devinit snd_fm801_create(st
|
|
snd_card_set_dev(card, &pci->dev);
|
|
|
|
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
|
+ err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
|
|
+ if (err < 0) {
|
|
+ snd_fm801_free(chip);
|
|
+ return err;
|
|
+ }
|
|
+ chip->tea.v4l2_dev = &chip->v4l2_dev;
|
|
+ chip->tea.radio_nr = radio_nr;
|
|
chip->tea.private_data = chip;
|
|
chip->tea.ops = &snd_fm801_tea_ops;
|
|
sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
|
|
@@ -1241,6 +1256,7 @@ static int __devinit snd_fm801_create(st
|
|
(tea575x_tuner & TUNER_TYPE_MASK) < 4) {
|
|
if (snd_tea575x_init(&chip->tea)) {
|
|
snd_printk(KERN_ERR "TEA575x radio not found\n");
|
|
+ snd_fm801_free(chip);
|
|
return -ENODEV;
|
|
}
|
|
} else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0) {
|
|
@@ -1287,7 +1303,7 @@ static int __devinit snd_card_fm801_prob
|
|
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
|
|
if (err < 0)
|
|
return err;
|
|
- if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) {
|
|
+ if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], radio_nr[dev], &chip)) < 0) {
|
|
snd_card_free(card);
|
|
return err;
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/radio/radio-maxiradio.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/radio/radio-maxiradio.c
|
|
+++ linux-3.3.x86_64/drivers/media/radio/radio-maxiradio.c
|
|
@@ -42,67 +42,37 @@
|
|
#include <linux/videodev2.h>
|
|
#include <linux/io.h>
|
|
#include <linux/slab.h>
|
|
+#include <sound/tea575x-tuner.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
-
|
|
-#define DRIVER_VERSION "0.7.8"
|
|
-
|
|
+#include <media/v4l2-fh.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-event.h>
|
|
|
|
MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
|
|
-MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
|
|
+MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000.");
|
|
MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION(DRIVER_VERSION);
|
|
+MODULE_VERSION("1.0.0");
|
|
|
|
static int radio_nr = -1;
|
|
-module_param(radio_nr, int, 0);
|
|
-
|
|
-static int debug;
|
|
-
|
|
-module_param(debug, int, 0644);
|
|
-MODULE_PARM_DESC(debug, "activates debug info");
|
|
-
|
|
-#define dprintk(dev, num, fmt, arg...) \
|
|
- v4l2_dbg(num, debug, &dev->v4l2_dev, fmt, ## arg)
|
|
-
|
|
-#ifndef PCI_VENDOR_ID_GUILLEMOT
|
|
-#define PCI_VENDOR_ID_GUILLEMOT 0x5046
|
|
-#endif
|
|
-
|
|
-#ifndef PCI_DEVICE_ID_GUILLEMOT
|
|
-#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
|
|
-#endif
|
|
-
|
|
+module_param(radio_nr, int, 0644);
|
|
+MODULE_PARM_DESC(radio_nr, "Radio device number");
|
|
|
|
/* TEA5757 pin mappings */
|
|
static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16;
|
|
|
|
-#define FREQ_LO (87 * 16000)
|
|
-#define FREQ_HI (108 * 16000)
|
|
-
|
|
-#define FREQ_IF 171200 /* 10.7*16000 */
|
|
-#define FREQ_STEP 200 /* 12.5*16 */
|
|
-
|
|
-/* (x==fmhz*16*1000) -> bits */
|
|
-#define FREQ2BITS(x) \
|
|
- ((((unsigned int)(x) + FREQ_IF + (FREQ_STEP << 1)) / (FREQ_STEP << 2)) << 2)
|
|
-
|
|
-#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
|
|
+static atomic_t maxiradio_instance = ATOMIC_INIT(0);
|
|
|
|
+#define PCI_VENDOR_ID_GUILLEMOT 0x5046
|
|
+#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
|
|
|
|
struct maxiradio
|
|
{
|
|
+ struct snd_tea575x tea;
|
|
struct v4l2_device v4l2_dev;
|
|
- struct video_device vdev;
|
|
struct pci_dev *pdev;
|
|
|
|
u16 io; /* base of radio io */
|
|
- u16 muted; /* VIDEO_AUDIO_MUTE */
|
|
- u16 stereo; /* VIDEO_TUNER_STEREO_ON */
|
|
- u16 tuned; /* signal strength (0 or 0xffff) */
|
|
-
|
|
- unsigned long freq;
|
|
-
|
|
- struct mutex lock;
|
|
};
|
|
|
|
static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev)
|
|
@@ -110,259 +80,41 @@ static inline struct maxiradio *to_maxir
|
|
return container_of(v4l2_dev, struct maxiradio, v4l2_dev);
|
|
}
|
|
|
|
-static void outbit(unsigned long bit, u16 io)
|
|
-{
|
|
- int val = power | wren | (bit ? data : 0);
|
|
-
|
|
- outb(val, io);
|
|
- udelay(4);
|
|
- outb(val | clk, io);
|
|
- udelay(4);
|
|
- outb(val, io);
|
|
- udelay(4);
|
|
-}
|
|
-
|
|
-static void turn_power(struct maxiradio *dev, int p)
|
|
-{
|
|
- if (p != 0) {
|
|
- dprintk(dev, 1, "Radio powered on\n");
|
|
- outb(power, dev->io);
|
|
- } else {
|
|
- dprintk(dev, 1, "Radio powered off\n");
|
|
- outb(0, dev->io);
|
|
- }
|
|
-}
|
|
-
|
|
-static void set_freq(struct maxiradio *dev, u32 freq)
|
|
-{
|
|
- unsigned long int si;
|
|
- int bl;
|
|
- int io = dev->io;
|
|
- int val = FREQ2BITS(freq);
|
|
-
|
|
- /* TEA5757 shift register bits (see pdf) */
|
|
-
|
|
- outbit(0, io); /* 24 search */
|
|
- outbit(1, io); /* 23 search up/down */
|
|
-
|
|
- outbit(0, io); /* 22 stereo/mono */
|
|
-
|
|
- outbit(0, io); /* 21 band */
|
|
- outbit(0, io); /* 20 band (only 00=FM works I think) */
|
|
-
|
|
- outbit(0, io); /* 19 port ? */
|
|
- outbit(0, io); /* 18 port ? */
|
|
-
|
|
- outbit(0, io); /* 17 search level */
|
|
- outbit(0, io); /* 16 search level */
|
|
-
|
|
- si = 0x8000;
|
|
- for (bl = 1; bl <= 16; bl++) {
|
|
- outbit(val & si, io);
|
|
- si >>= 1;
|
|
- }
|
|
-
|
|
- dprintk(dev, 1, "Radio freq set to %d.%02d MHz\n",
|
|
- freq / 16000,
|
|
- freq % 16000 * 100 / 16000);
|
|
-
|
|
- turn_power(dev, 1);
|
|
-}
|
|
-
|
|
-static int get_stereo(u16 io)
|
|
-{
|
|
- outb(power,io);
|
|
- udelay(4);
|
|
-
|
|
- return !(inb(io) & mo_st);
|
|
-}
|
|
-
|
|
-static int get_tune(u16 io)
|
|
-{
|
|
- outb(power+clk,io);
|
|
- udelay(4);
|
|
-
|
|
- return !(inb(io) & mo_st);
|
|
-}
|
|
-
|
|
-
|
|
-static int vidioc_querycap(struct file *file, void *priv,
|
|
- struct v4l2_capability *v)
|
|
-{
|
|
- struct maxiradio *dev = video_drvdata(file);
|
|
-
|
|
- strlcpy(v->driver, "radio-maxiradio", sizeof(v->driver));
|
|
- strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof(v->card));
|
|
- snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev));
|
|
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
+static void maxiradio_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
|
|
{
|
|
- struct maxiradio *dev = video_drvdata(file);
|
|
+ struct maxiradio *dev = tea->private_data;
|
|
+ u8 bits = 0;
|
|
|
|
- if (v->index > 0)
|
|
- return -EINVAL;
|
|
+ bits |= (pins & TEA575X_DATA) ? data : 0;
|
|
+ bits |= (pins & TEA575X_CLK) ? clk : 0;
|
|
+ bits |= (pins & TEA575X_WREN) ? wren : 0;
|
|
+ bits |= power;
|
|
|
|
- mutex_lock(&dev->lock);
|
|
- strlcpy(v->name, "FM", sizeof(v->name));
|
|
- v->type = V4L2_TUNER_RADIO;
|
|
- v->rangelow = FREQ_LO;
|
|
- v->rangehigh = FREQ_HI;
|
|
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
|
|
- v->capability = V4L2_TUNER_CAP_LOW;
|
|
- if (get_stereo(dev->io))
|
|
- v->audmode = V4L2_TUNER_MODE_STEREO;
|
|
- else
|
|
- v->audmode = V4L2_TUNER_MODE_MONO;
|
|
- v->signal = 0xffff * get_tune(dev->io);
|
|
- mutex_unlock(&dev->lock);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_tuner(struct file *file, void *priv,
|
|
- struct v4l2_tuner *v)
|
|
-{
|
|
- return v->index ? -EINVAL : 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_g_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- a->index = 0;
|
|
- strlcpy(a->name, "Radio", sizeof(a->name));
|
|
- a->capability = V4L2_AUDCAP_STEREO;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-
|
|
-static int vidioc_s_audio(struct file *file, void *priv,
|
|
- struct v4l2_audio *a)
|
|
-{
|
|
- return a->index ? -EINVAL : 0;
|
|
-}
|
|
-
|
|
-static int vidioc_s_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct maxiradio *dev = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
- return -EINVAL;
|
|
- if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) {
|
|
- dprintk(dev, 1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n",
|
|
- f->frequency / 16000,
|
|
- f->frequency % 16000 * 100 / 16000,
|
|
- FREQ_LO / 16000, FREQ_HI / 16000);
|
|
-
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- mutex_lock(&dev->lock);
|
|
- dev->freq = f->frequency;
|
|
- set_freq(dev, dev->freq);
|
|
- msleep(125);
|
|
- mutex_unlock(&dev->lock);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int vidioc_g_frequency(struct file *file, void *priv,
|
|
- struct v4l2_frequency *f)
|
|
-{
|
|
- struct maxiradio *dev = video_drvdata(file);
|
|
-
|
|
- if (f->tuner != 0)
|
|
- return -EINVAL;
|
|
- f->type = V4L2_TUNER_RADIO;
|
|
- f->frequency = dev->freq;
|
|
-
|
|
- dprintk(dev, 4, "radio freq is %d.%02d MHz",
|
|
- f->frequency / 16000,
|
|
- f->frequency % 16000 * 100 / 16000);
|
|
-
|
|
- 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;
|
|
+ outb(bits, dev->io);
|
|
}
|
|
|
|
-static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
+/* Note: this card cannot read out the data of the shift registers,
|
|
+ only the mono/stereo pin works. */
|
|
+static u8 maxiradio_tea575x_get_pins(struct snd_tea575x *tea)
|
|
{
|
|
- struct maxiradio *dev = video_drvdata(file);
|
|
+ struct maxiradio *dev = tea->private_data;
|
|
+ u8 bits = inb(dev->io);
|
|
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- ctrl->value = dev->muted;
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return -EINVAL;
|
|
+ return ((bits & data) ? TEA575X_DATA : 0) |
|
|
+ ((bits & mo_st) ? TEA575X_MOST : 0);
|
|
}
|
|
|
|
-static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
- struct v4l2_control *ctrl)
|
|
+static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output)
|
|
{
|
|
- struct maxiradio *dev = video_drvdata(file);
|
|
-
|
|
- switch (ctrl->id) {
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
- mutex_lock(&dev->lock);
|
|
- dev->muted = ctrl->value;
|
|
- if (dev->muted)
|
|
- turn_power(dev, 0);
|
|
- else
|
|
- set_freq(dev, dev->freq);
|
|
- mutex_unlock(&dev->lock);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return -EINVAL;
|
|
}
|
|
|
|
-static const struct v4l2_file_operations maxiradio_fops = {
|
|
- .owner = THIS_MODULE,
|
|
- .unlocked_ioctl = video_ioctl2,
|
|
-};
|
|
-
|
|
-static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = {
|
|
- .vidioc_querycap = vidioc_querycap,
|
|
- .vidioc_g_tuner = vidioc_g_tuner,
|
|
- .vidioc_s_tuner = vidioc_s_tuner,
|
|
- .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_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,
|
|
+static struct snd_tea575x_ops maxiradio_tea_ops = {
|
|
+ .set_pins = maxiradio_tea575x_set_pins,
|
|
+ .get_pins = maxiradio_tea575x_get_pins,
|
|
+ .set_direction = maxiradio_tea575x_set_direction,
|
|
};
|
|
|
|
-static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
+static int __devinit maxiradio_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
{
|
|
struct maxiradio *dev;
|
|
struct v4l2_device *v4l2_dev;
|
|
@@ -375,63 +127,60 @@ static int __devinit maxiradio_init_one(
|
|
}
|
|
|
|
v4l2_dev = &dev->v4l2_dev;
|
|
- mutex_init(&dev->lock);
|
|
- dev->pdev = pdev;
|
|
- dev->muted = 1;
|
|
- dev->freq = FREQ_LO;
|
|
-
|
|
- strlcpy(v4l2_dev->name, "maxiradio", sizeof(v4l2_dev->name));
|
|
+ v4l2_device_set_name(v4l2_dev, "maxiradio", &maxiradio_instance);
|
|
|
|
retval = v4l2_device_register(&pdev->dev, v4l2_dev);
|
|
if (retval < 0) {
|
|
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
goto errfr;
|
|
}
|
|
+ dev->tea.private_data = dev;
|
|
+ dev->tea.ops = &maxiradio_tea_ops;
|
|
+ /* The data pin cannot be read. This may be a hardware limitation, or
|
|
+ we just don't know how to read it. */
|
|
+ dev->tea.cannot_read_data = true;
|
|
+ dev->tea.v4l2_dev = v4l2_dev;
|
|
+ dev->tea.radio_nr = radio_nr;
|
|
+ strlcpy(dev->tea.card, "Maxi Radio FM2000", sizeof(dev->tea.card));
|
|
+ snprintf(dev->tea.bus_info, sizeof(dev->tea.bus_info),
|
|
+ "PCI:%s", pci_name(pdev));
|
|
+
|
|
+ retval = -ENODEV;
|
|
|
|
if (!request_region(pci_resource_start(pdev, 0),
|
|
- pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
|
|
- v4l2_err(v4l2_dev, "can't reserve I/O ports\n");
|
|
- goto err_out;
|
|
+ pci_resource_len(pdev, 0), v4l2_dev->name)) {
|
|
+ dev_err(&pdev->dev, "can't reserve I/O ports\n");
|
|
+ goto err_hdl;
|
|
}
|
|
|
|
if (pci_enable_device(pdev))
|
|
goto err_out_free_region;
|
|
|
|
dev->io = pci_resource_start(pdev, 0);
|
|
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
|
|
- dev->vdev.v4l2_dev = v4l2_dev;
|
|
- dev->vdev.fops = &maxiradio_fops;
|
|
- dev->vdev.ioctl_ops = &maxiradio_ioctl_ops;
|
|
- dev->vdev.release = video_device_release_empty;
|
|
- video_set_drvdata(&dev->vdev, dev);
|
|
-
|
|
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
|
- v4l2_err(v4l2_dev, "can't register device!");
|
|
+ if (snd_tea575x_init(&dev->tea)) {
|
|
+ printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n");
|
|
goto err_out_free_region;
|
|
}
|
|
-
|
|
- v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n");
|
|
-
|
|
- v4l2_info(v4l2_dev, "found Guillemot MAXI Radio device (io = 0x%x)\n",
|
|
- dev->io);
|
|
return 0;
|
|
|
|
err_out_free_region:
|
|
release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
|
|
-err_out:
|
|
+err_hdl:
|
|
v4l2_device_unregister(v4l2_dev);
|
|
errfr:
|
|
kfree(dev);
|
|
- return -ENODEV;
|
|
+ return retval;
|
|
}
|
|
|
|
-static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
|
|
+static void __devexit maxiradio_remove(struct pci_dev *pdev)
|
|
{
|
|
struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
|
|
struct maxiradio *dev = to_maxiradio(v4l2_dev);
|
|
|
|
- video_unregister_device(&dev->vdev);
|
|
- v4l2_device_unregister(&dev->v4l2_dev);
|
|
+ snd_tea575x_exit(&dev->tea);
|
|
+ /* Turn off power */
|
|
+ outb(0, dev->io);
|
|
+ v4l2_device_unregister(v4l2_dev);
|
|
release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
|
|
}
|
|
|
|
@@ -446,19 +195,19 @@ MODULE_DEVICE_TABLE(pci, maxiradio_pci_t
|
|
static struct pci_driver maxiradio_driver = {
|
|
.name = "radio-maxiradio",
|
|
.id_table = maxiradio_pci_tbl,
|
|
- .probe = maxiradio_init_one,
|
|
- .remove = __devexit_p(maxiradio_remove_one),
|
|
+ .probe = maxiradio_probe,
|
|
+ .remove = __devexit_p(maxiradio_remove),
|
|
};
|
|
|
|
-static int __init maxiradio_radio_init(void)
|
|
+static int __init maxiradio_init(void)
|
|
{
|
|
return pci_register_driver(&maxiradio_driver);
|
|
}
|
|
|
|
-static void __exit maxiradio_radio_exit(void)
|
|
+static void __exit maxiradio_exit(void)
|
|
{
|
|
pci_unregister_driver(&maxiradio_driver);
|
|
}
|
|
|
|
-module_init(maxiradio_radio_init);
|
|
-module_exit(maxiradio_radio_exit);
|
|
+module_init(maxiradio_init);
|
|
+module_exit(maxiradio_exit);
|
|
Index: linux-3.3.x86_64/include/media/v4l2-dev.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/media/v4l2-dev.h
|
|
+++ linux-3.3.x86_64/include/media/v4l2-dev.h
|
|
@@ -62,6 +62,9 @@ struct v4l2_file_operations {
|
|
unsigned int (*poll) (struct file *, struct poll_table_struct *);
|
|
long (*ioctl) (struct file *, unsigned int, unsigned long);
|
|
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
|
|
+#ifdef CONFIG_COMPAT
|
|
+ long (*compat_ioctl32) (struct file *, unsigned int, unsigned long);
|
|
+#endif
|
|
unsigned long (*get_unmapped_area) (struct file *, unsigned long,
|
|
unsigned long, unsigned long, unsigned long);
|
|
int (*mmap) (struct file *, struct vm_area_struct *);
|
|
Index: linux-3.3.x86_64/drivers/media/video/uvc/uvc_v4l2.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/uvc/uvc_v4l2.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/uvc/uvc_v4l2.c
|
|
@@ -11,6 +11,7 @@
|
|
*
|
|
*/
|
|
|
|
+#include <linux/compat.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/version.h>
|
|
#include <linux/list.h>
|
|
@@ -1012,7 +1013,7 @@ static long uvc_v4l2_do_ioctl(struct fil
|
|
|
|
default:
|
|
uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd);
|
|
- return -EINVAL;
|
|
+ return -ENOTTY;
|
|
}
|
|
|
|
return ret;
|
|
@@ -1030,6 +1031,207 @@ static long uvc_v4l2_ioctl(struct file *
|
|
return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
|
|
}
|
|
|
|
+#ifdef CONFIG_COMPAT
|
|
+struct uvc_xu_control_mapping32 {
|
|
+ __u32 id;
|
|
+ __u8 name[32];
|
|
+ __u8 entity[16];
|
|
+ __u8 selector;
|
|
+
|
|
+ __u8 size;
|
|
+ __u8 offset;
|
|
+ __u32 v4l2_type;
|
|
+ __u32 data_type;
|
|
+
|
|
+ compat_caddr_t menu_info;
|
|
+ __u32 menu_count;
|
|
+
|
|
+ __u32 reserved[4];
|
|
+};
|
|
+
|
|
+static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp,
|
|
+ const struct uvc_xu_control_mapping32 __user *up)
|
|
+{
|
|
+ struct uvc_menu_info __user *umenus;
|
|
+ struct uvc_menu_info __user *kmenus;
|
|
+ compat_caddr_t p;
|
|
+
|
|
+ if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
|
|
+ __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) ||
|
|
+ __get_user(kp->menu_count, &up->menu_count))
|
|
+ return -EFAULT;
|
|
+
|
|
+ memset(kp->reserved, 0, sizeof(kp->reserved));
|
|
+
|
|
+ if (kp->menu_count == 0) {
|
|
+ kp->menu_info = NULL;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (__get_user(p, &up->menu_info))
|
|
+ return -EFAULT;
|
|
+ umenus = compat_ptr(p);
|
|
+ if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus));
|
|
+ if (kmenus == NULL)
|
|
+ return -EFAULT;
|
|
+ kp->menu_info = kmenus;
|
|
+
|
|
+ if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
|
|
+ struct uvc_xu_control_mapping32 __user *up)
|
|
+{
|
|
+ struct uvc_menu_info __user *umenus;
|
|
+ struct uvc_menu_info __user *kmenus = kp->menu_info;
|
|
+ compat_caddr_t p;
|
|
+
|
|
+ if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
|
|
+ __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) ||
|
|
+ __put_user(kp->menu_count, &up->menu_count))
|
|
+ return -EFAULT;
|
|
+
|
|
+ __clear_user(up->reserved, sizeof(up->reserved));
|
|
+
|
|
+ if (kp->menu_count == 0)
|
|
+ return 0;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct uvc_xu_control_query32 {
|
|
+ __u8 unit;
|
|
+ __u8 selector;
|
|
+ __u8 query;
|
|
+ __u16 size;
|
|
+ compat_caddr_t data;
|
|
+};
|
|
+
|
|
+static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp,
|
|
+ const struct uvc_xu_control_query32 __user *up)
|
|
+{
|
|
+ u8 __user *udata;
|
|
+ u8 __user *kdata;
|
|
+ compat_caddr_t p;
|
|
+
|
|
+ if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
|
|
+ __copy_from_user(kp, up, offsetof(typeof(*up), data)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ if (kp->size == 0) {
|
|
+ kp->data = NULL;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (__get_user(p, &up->data))
|
|
+ return -EFAULT;
|
|
+ udata = compat_ptr(p);
|
|
+ if (!access_ok(VERIFY_READ, udata, kp->size))
|
|
+ return -EFAULT;
|
|
+
|
|
+ kdata = compat_alloc_user_space(kp->size);
|
|
+ if (kdata == NULL)
|
|
+ return -EFAULT;
|
|
+ kp->data = kdata;
|
|
+
|
|
+ if (copy_in_user(kdata, udata, kp->size))
|
|
+ return -EFAULT;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
|
|
+ struct uvc_xu_control_query32 __user *up)
|
|
+{
|
|
+ u8 __user *udata;
|
|
+ u8 __user *kdata = kp->data;
|
|
+ compat_caddr_t p;
|
|
+
|
|
+ if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
|
|
+ __copy_to_user(up, kp, offsetof(typeof(*up), data)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ if (kp->size == 0)
|
|
+ return 0;
|
|
+
|
|
+ if (get_user(p, &up->data))
|
|
+ return -EFAULT;
|
|
+ udata = compat_ptr(p);
|
|
+ if (!access_ok(VERIFY_READ, udata, kp->size))
|
|
+ return -EFAULT;
|
|
+
|
|
+ if (copy_in_user(udata, kdata, kp->size))
|
|
+ return -EFAULT;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
|
|
+#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32)
|
|
+
|
|
+static long uvc_v4l2_compat_ioctl32(struct file *file,
|
|
+ unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ union {
|
|
+ struct uvc_xu_control_mapping xmap;
|
|
+ struct uvc_xu_control_query xqry;
|
|
+ } karg;
|
|
+ void __user *up = compat_ptr(arg);
|
|
+ mm_segment_t old_fs;
|
|
+ long ret;
|
|
+
|
|
+ switch (cmd) {
|
|
+ case UVCIOC_CTRL_MAP32:
|
|
+ cmd = UVCIOC_CTRL_MAP;
|
|
+ ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
|
|
+ break;
|
|
+
|
|
+ case UVCIOC_CTRL_QUERY32:
|
|
+ cmd = UVCIOC_CTRL_QUERY;
|
|
+ ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ return -ENOIOCTLCMD;
|
|
+ }
|
|
+
|
|
+ old_fs = get_fs();
|
|
+ set_fs(KERNEL_DS);
|
|
+ ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg);
|
|
+ set_fs(old_fs);
|
|
+
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ switch (cmd) {
|
|
+ case UVCIOC_CTRL_MAP:
|
|
+ ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
|
|
+ break;
|
|
+
|
|
+ case UVCIOC_CTRL_QUERY:
|
|
+ ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#endif
|
|
+
|
|
static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
@@ -1076,6 +1278,9 @@ const struct v4l2_file_operations uvc_fo
|
|
.open = uvc_v4l2_open,
|
|
.release = uvc_v4l2_release,
|
|
.unlocked_ioctl = uvc_v4l2_ioctl,
|
|
+#ifdef CONFIG_COMPAT
|
|
+ .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
|
|
+#endif
|
|
.read = uvc_v4l2_read,
|
|
.mmap = uvc_v4l2_mmap,
|
|
.poll = uvc_v4l2_poll,
|
|
Index: linux-3.3.x86_64/drivers/media/video/uvc/uvc_queue.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/uvc/uvc_queue.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/uvc/uvc_queue.c
|
|
@@ -126,7 +126,7 @@ void uvc_queue_init(struct uvc_video_que
|
|
int drop_corrupted)
|
|
{
|
|
queue->queue.type = type;
|
|
- queue->queue.io_modes = VB2_MMAP;
|
|
+ queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
|
|
queue->queue.drv_priv = queue;
|
|
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
|
|
queue->queue.ops = &uvc_queue_qops;
|
|
Index: linux-3.3.x86_64/drivers/media/video/uvc/uvc_driver.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/uvc/uvc_driver.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/uvc/uvc_driver.c
|
|
@@ -23,6 +23,7 @@
|
|
* codec can't handle MJPEG data.
|
|
*/
|
|
|
|
+#include <linux/atomic.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/list.h>
|
|
#include <linux/module.h>
|
|
@@ -32,7 +33,6 @@
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/version.h>
|
|
-#include <asm/atomic.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
#include <media/v4l2-common.h>
|
|
@@ -2139,6 +2139,15 @@ static struct usb_device_id uvc_ids[] =
|
|
.bInterfaceSubClass = 1,
|
|
.bInterfaceProtocol = 0,
|
|
.driver_info = UVC_QUIRK_PROBE_MINMAX },
|
|
+ /* Dell XPS m1530 */
|
|
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
|
+ | USB_DEVICE_ID_MATCH_INT_INFO,
|
|
+ .idVendor = 0x05a9,
|
|
+ .idProduct = 0x2640,
|
|
+ .bInterfaceClass = USB_CLASS_VIDEO,
|
|
+ .bInterfaceSubClass = 1,
|
|
+ .bInterfaceProtocol = 0,
|
|
+ .driver_info = UVC_QUIRK_PROBE_DEF },
|
|
/* Apple Built-In iSight */
|
|
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
|
| USB_DEVICE_ID_MATCH_INT_INFO,
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/stv0288.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/stv0288.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/stv0288.c
|
|
@@ -506,7 +506,7 @@ static int stv0288_set_frontend(struct d
|
|
tda[1] = (unsigned char)tm;
|
|
stv0288_writeregI(state, 0x2b, tda[1]);
|
|
stv0288_writeregI(state, 0x2c, tda[2]);
|
|
- udelay(30);
|
|
+ msleep(30);
|
|
}
|
|
state->tuner_frequency = c->frequency;
|
|
state->fec_inner = FEC_AUTO;
|
|
Index: linux-3.3.x86_64/drivers/media/video/cx231xx/cx231xx-cards.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cx231xx/cx231xx-cards.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cx231xx/cx231xx-cards.c
|
|
@@ -861,7 +861,6 @@ void cx231xx_release_resources(struct cx
|
|
kfree(dev->sliced_cc_mode.alt_max_pkt_size);
|
|
kfree(dev->ts1_mode.alt_max_pkt_size);
|
|
kfree(dev);
|
|
- dev = NULL;
|
|
}
|
|
|
|
/*
|
|
Index: linux-3.3.x86_64/drivers/media/video/cx231xx/cx231xx-video.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/cx231xx/cx231xx-video.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/cx231xx/cx231xx-video.c
|
|
@@ -2319,8 +2319,7 @@ static int cx231xx_v4l2_close(struct fil
|
|
if (dev->state & DEV_DISCONNECTED) {
|
|
if (atomic_read(&dev->devlist_count) > 0) {
|
|
cx231xx_release_resources(dev);
|
|
- kfree(dev);
|
|
- dev = NULL;
|
|
+ fh->dev = NULL;
|
|
return 0;
|
|
}
|
|
return 0;
|
|
@@ -2350,8 +2349,7 @@ static int cx231xx_v4l2_close(struct fil
|
|
free the remaining resources */
|
|
if (dev->state & DEV_DISCONNECTED) {
|
|
cx231xx_release_resources(dev);
|
|
- kfree(dev);
|
|
- dev = NULL;
|
|
+ fh->dev = NULL;
|
|
return 0;
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/dib9000.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/dib9000.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/dib9000.c
|
|
@@ -33,7 +33,7 @@ struct i2c_device {
|
|
|
|
/* lock */
|
|
#define DIB_LOCK struct mutex
|
|
-#define DibAcquireLock(lock) do { if (mutex_lock_interruptible(lock) < 0) dprintk("could not get the lock"); } while (0)
|
|
+#define DibAcquireLock(lock) mutex_lock_interruptible(lock)
|
|
#define DibReleaseLock(lock) mutex_unlock(lock)
|
|
#define DibInitLock(lock) mutex_init(lock)
|
|
#define DibFreeLock(lock)
|
|
@@ -446,7 +446,10 @@ static int dib9000_risc_mem_read(struct
|
|
if (!state->platform.risc.fw_is_running)
|
|
return -EIO;
|
|
|
|
- DibAcquireLock(&state->platform.risc.mem_lock);
|
|
+ if (DibAcquireLock(&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);
|
|
@@ -459,7 +462,10 @@ static int dib9000_risc_mem_write(struct
|
|
if (!state->platform.risc.fw_is_running)
|
|
return -EIO;
|
|
|
|
- DibAcquireLock(&state->platform.risc.mem_lock);
|
|
+ if (DibAcquireLock(&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);
|
|
@@ -531,7 +537,10 @@ static int dib9000_mbx_send_attr(struct
|
|
if (!state->platform.risc.fw_is_running)
|
|
return -EINVAL;
|
|
|
|
- DibAcquireLock(&state->platform.risc.mbx_if_lock);
|
|
+ if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
tmp = MAX_MAILBOX_TRY;
|
|
do {
|
|
size = dib9000_read_word_attr(state, 1043, attr) & 0xff;
|
|
@@ -593,7 +602,10 @@ static u8 dib9000_mbx_read(struct dib900
|
|
if (!state->platform.risc.fw_is_running)
|
|
return 0;
|
|
|
|
- DibAcquireLock(&state->platform.risc.mbx_if_lock);
|
|
+ if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return 0;
|
|
+ }
|
|
if (risc_id == 1)
|
|
mc_base = 16;
|
|
else
|
|
@@ -701,7 +713,10 @@ static int dib9000_mbx_process(struct di
|
|
if (!state->platform.risc.fw_is_running)
|
|
return -1;
|
|
|
|
- DibAcquireLock(&state->platform.risc.mbx_lock);
|
|
+ if (DibAcquireLock(&state->platform.risc.mbx_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -1;
|
|
+ }
|
|
|
|
if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */
|
|
ret = dib9000_mbx_fetch_to_cache(state, attr);
|
|
@@ -1178,7 +1193,10 @@ static int dib9000_fw_get_channel(struct
|
|
struct dibDVBTChannel *ch;
|
|
int ret = 0;
|
|
|
|
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
|
|
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
|
|
ret = -EIO;
|
|
goto error;
|
|
@@ -1660,7 +1678,10 @@ static int dib9000_fw_component_bus_xfer
|
|
p[12] = 0;
|
|
}
|
|
|
|
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
|
|
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return 0;
|
|
+ }
|
|
|
|
dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p);
|
|
|
|
@@ -1768,7 +1789,10 @@ int dib9000_fw_pid_filter_ctrl(struct dv
|
|
return 0;
|
|
}
|
|
|
|
- DibAcquireLock(&state->demod_lock);
|
|
+ if (DibAcquireLock(&state->demod_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
|
|
val = dib9000_read_word(state, 294 + 1) & 0xffef;
|
|
val |= (onoff & 0x1) << 4;
|
|
@@ -1800,7 +1824,10 @@ int dib9000_fw_pid_filter(struct dvb_fro
|
|
return 0;
|
|
}
|
|
|
|
- DibAcquireLock(&state->demod_lock);
|
|
+ if (DibAcquireLock(&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);
|
|
@@ -1848,7 +1875,10 @@ static int dib9000_sleep(struct dvb_fron
|
|
u8 index_frontend;
|
|
int ret = 0;
|
|
|
|
- DibAcquireLock(&state->demod_lock);
|
|
+ if (DibAcquireLock(&state->demod_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
|
|
ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
|
|
if (ret < 0)
|
|
@@ -1874,8 +1904,12 @@ static int dib9000_get_frontend(struct d
|
|
fe_status_t stat;
|
|
int ret = 0;
|
|
|
|
- if (state->get_frontend_internal == 0)
|
|
- DibAcquireLock(&state->demod_lock);
|
|
+ if (state->get_frontend_internal == 0) {
|
|
+ if (DibAcquireLock(&state->demod_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
+ }
|
|
|
|
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
|
|
state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
|
|
@@ -1978,7 +2012,10 @@ static int dib9000_set_frontend(struct d
|
|
}
|
|
|
|
state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */
|
|
- DibAcquireLock(&state->demod_lock);
|
|
+ if (DibAcquireLock(&state->demod_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return 0;
|
|
+ }
|
|
|
|
fe->dtv_property_cache.delivery_system = SYS_DVBT;
|
|
|
|
@@ -2138,7 +2175,10 @@ static int dib9000_read_status(struct dv
|
|
u8 index_frontend;
|
|
u16 lock = 0, lock_slave = 0;
|
|
|
|
- DibAcquireLock(&state->demod_lock);
|
|
+ if (DibAcquireLock(&state->demod_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
|
|
lock_slave |= dib9000_read_lock(state->fe[index_frontend]);
|
|
|
|
@@ -2168,8 +2208,15 @@ static int dib9000_read_ber(struct dvb_f
|
|
u16 *c;
|
|
int ret = 0;
|
|
|
|
- DibAcquireLock(&state->demod_lock);
|
|
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
|
|
+ if (DibAcquireLock(&state->demod_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
+ if (DibAcquireLock(&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);
|
|
ret = -EIO;
|
|
@@ -2196,7 +2243,10 @@ static int dib9000_read_signal_strength(
|
|
u16 val;
|
|
int ret = 0;
|
|
|
|
- DibAcquireLock(&state->demod_lock);
|
|
+ if (DibAcquireLock(&state->demod_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
*strength = 0;
|
|
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
|
|
state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
|
|
@@ -2206,8 +2256,13 @@ static int dib9000_read_signal_strength(
|
|
*strength += val;
|
|
}
|
|
|
|
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
|
|
+ if (DibAcquireLock(&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);
|
|
ret = -EIO;
|
|
goto error;
|
|
}
|
|
@@ -2232,9 +2287,14 @@ static u32 dib9000_get_snr(struct dvb_fr
|
|
u32 n, s, exp;
|
|
u16 val;
|
|
|
|
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
|
|
- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
|
|
- return -EIO;
|
|
+ if (DibAcquireLock(&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);
|
|
+ return 0;
|
|
+ }
|
|
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
|
|
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
|
|
|
|
@@ -2266,7 +2326,10 @@ static int dib9000_read_snr(struct dvb_f
|
|
u8 index_frontend;
|
|
u32 snr_master;
|
|
|
|
- DibAcquireLock(&state->demod_lock);
|
|
+ if (DibAcquireLock(&state->demod_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
snr_master = dib9000_get_snr(fe);
|
|
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
|
|
snr_master += dib9000_get_snr(state->fe[index_frontend]);
|
|
@@ -2288,9 +2351,17 @@ static int dib9000_read_unc_blocks(struc
|
|
u16 *c = (u16 *)state->i2c_read_buffer;
|
|
int ret = 0;
|
|
|
|
- DibAcquireLock(&state->demod_lock);
|
|
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
|
|
+ if (DibAcquireLock(&state->demod_lock) < 0) {
|
|
+ dprintk("could not get the lock");
|
|
+ return -EINTR;
|
|
+ }
|
|
+ if (DibAcquireLock(&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);
|
|
ret = -EIO;
|
|
goto error;
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/drxd_hard.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/drxd_hard.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/drxd_hard.c
|
|
@@ -101,9 +101,9 @@ struct SCfgAgc {
|
|
|
|
struct SNoiseCal {
|
|
int cpOpt;
|
|
- u16 cpNexpOfs;
|
|
- u16 tdCal2k;
|
|
- u16 tdCal8k;
|
|
+ short cpNexpOfs;
|
|
+ short tdCal2k;
|
|
+ short tdCal8k;
|
|
};
|
|
|
|
enum app_env {
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/m88rs2000.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/m88rs2000.c
|
|
@@ -0,0 +1,904 @@
|
|
+/*
|
|
+ Driver for M88RS2000 demodulator and tuner
|
|
+
|
|
+ Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
|
|
+ Beta Driver
|
|
+
|
|
+ Include various calculation code from DS3000 driver.
|
|
+ Copyright (C) 2009 Konstantin Dimitrov.
|
|
+
|
|
+ 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 <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/jiffies.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+
|
|
+#include "dvb_frontend.h"
|
|
+#include "m88rs2000.h"
|
|
+
|
|
+struct m88rs2000_state {
|
|
+ struct i2c_adapter *i2c;
|
|
+ const struct m88rs2000_config *config;
|
|
+ struct dvb_frontend frontend;
|
|
+ u8 no_lock_count;
|
|
+ u32 tuner_frequency;
|
|
+ u32 symbol_rate;
|
|
+ fe_code_rate_t fec_inner;
|
|
+ u8 tuner_level;
|
|
+ int errmode;
|
|
+};
|
|
+
|
|
+static int m88rs2000_debug;
|
|
+
|
|
+module_param_named(debug, m88rs2000_debug, int, 0644);
|
|
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
|
|
+
|
|
+#define dprintk(level, args...) do { \
|
|
+ if (level & m88rs2000_debug) \
|
|
+ printk(KERN_DEBUG "m88rs2000-fe: " args); \
|
|
+} while (0)
|
|
+
|
|
+#define deb_info(args...) dprintk(0x01, args)
|
|
+#define info(format, arg...) \
|
|
+ printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg)
|
|
+
|
|
+static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner,
|
|
+ u8 reg, u8 data)
|
|
+{
|
|
+ int ret;
|
|
+ u8 addr = (tuner == 0) ? state->config->tuner_addr :
|
|
+ state->config->demod_addr;
|
|
+ u8 buf[] = { reg, data };
|
|
+ struct i2c_msg msg = {
|
|
+ .addr = addr,
|
|
+ .flags = 0,
|
|
+ .buf = buf,
|
|
+ .len = 2
|
|
+ };
|
|
+
|
|
+ ret = i2c_transfer(state->i2c, &msg, 1);
|
|
+
|
|
+ if (ret != 1)
|
|
+ deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
|
|
+ "ret == %i)\n", __func__, reg, data, ret);
|
|
+
|
|
+ return (ret != 1) ? -EREMOTEIO : 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_demod_write(struct m88rs2000_state *state, u8 reg, u8 data)
|
|
+{
|
|
+ return m88rs2000_writereg(state, 1, reg, data);
|
|
+}
|
|
+
|
|
+static int m88rs2000_tuner_write(struct m88rs2000_state *state, u8 reg, u8 data)
|
|
+{
|
|
+ m88rs2000_demod_write(state, 0x81, 0x84);
|
|
+ udelay(10);
|
|
+ return m88rs2000_writereg(state, 0, reg, data);
|
|
+
|
|
+}
|
|
+
|
|
+static int m88rs2000_write(struct dvb_frontend *fe, const u8 buf[], int len)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+
|
|
+ if (len != 2)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return m88rs2000_writereg(state, 1, buf[0], buf[1]);
|
|
+}
|
|
+
|
|
+static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg)
|
|
+{
|
|
+ int ret;
|
|
+ u8 b0[] = { reg };
|
|
+ u8 b1[] = { 0 };
|
|
+ u8 addr = (tuner == 0) ? state->config->tuner_addr :
|
|
+ state->config->demod_addr;
|
|
+ struct i2c_msg msg[] = {
|
|
+ {
|
|
+ .addr = addr,
|
|
+ .flags = 0,
|
|
+ .buf = b0,
|
|
+ .len = 1
|
|
+ }, {
|
|
+ .addr = addr,
|
|
+ .flags = I2C_M_RD,
|
|
+ .buf = b1,
|
|
+ .len = 1
|
|
+ }
|
|
+ };
|
|
+
|
|
+ ret = i2c_transfer(state->i2c, msg, 2);
|
|
+
|
|
+ if (ret != 2)
|
|
+ deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n",
|
|
+ __func__, reg, ret);
|
|
+
|
|
+ return b1[0];
|
|
+}
|
|
+
|
|
+static u8 m88rs2000_demod_read(struct m88rs2000_state *state, u8 reg)
|
|
+{
|
|
+ return m88rs2000_readreg(state, 1, reg);
|
|
+}
|
|
+
|
|
+static u8 m88rs2000_tuner_read(struct m88rs2000_state *state, u8 reg)
|
|
+{
|
|
+ m88rs2000_demod_write(state, 0x81, 0x85);
|
|
+ udelay(10);
|
|
+ return m88rs2000_readreg(state, 0, reg);
|
|
+}
|
|
+
|
|
+static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ int ret;
|
|
+ u32 temp;
|
|
+ u8 b[3];
|
|
+
|
|
+ if ((srate < 1000000) || (srate > 45000000))
|
|
+ return -EINVAL;
|
|
+
|
|
+ temp = srate / 1000;
|
|
+ temp *= 11831;
|
|
+ temp /= 68;
|
|
+ temp -= 3;
|
|
+
|
|
+ b[0] = (u8) (temp >> 16) & 0xff;
|
|
+ b[1] = (u8) (temp >> 8) & 0xff;
|
|
+ b[2] = (u8) temp & 0xff;
|
|
+ ret = m88rs2000_demod_write(state, 0x93, b[2]);
|
|
+ ret |= m88rs2000_demod_write(state, 0x94, b[1]);
|
|
+ ret |= m88rs2000_demod_write(state, 0x95, b[0]);
|
|
+
|
|
+ deb_info("m88rs2000: m88rs2000_set_symbolrate\n");
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe,
|
|
+ struct dvb_diseqc_master_cmd *m)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+
|
|
+ int i;
|
|
+ u8 reg;
|
|
+ deb_info("%s\n", __func__);
|
|
+ m88rs2000_demod_write(state, 0x9a, 0x30);
|
|
+ reg = m88rs2000_demod_read(state, 0xb2);
|
|
+ reg &= 0x3f;
|
|
+ m88rs2000_demod_write(state, 0xb2, reg);
|
|
+ for (i = 0; i < m->msg_len; i++)
|
|
+ m88rs2000_demod_write(state, 0xb3 + i, m->msg[i]);
|
|
+
|
|
+ reg = m88rs2000_demod_read(state, 0xb1);
|
|
+ reg &= 0x87;
|
|
+ reg |= ((m->msg_len - 1) << 3) | 0x07;
|
|
+ reg &= 0x7f;
|
|
+ m88rs2000_demod_write(state, 0xb1, reg);
|
|
+
|
|
+ for (i = 0; i < 15; i++) {
|
|
+ if ((m88rs2000_demod_read(state, 0xb1) & 0x40) == 0x0)
|
|
+ break;
|
|
+ msleep(20);
|
|
+ }
|
|
+
|
|
+ reg = m88rs2000_demod_read(state, 0xb1);
|
|
+ if ((reg & 0x40) > 0x0) {
|
|
+ reg &= 0x7f;
|
|
+ reg |= 0x40;
|
|
+ m88rs2000_demod_write(state, 0xb1, reg);
|
|
+ }
|
|
+
|
|
+ reg = m88rs2000_demod_read(state, 0xb2);
|
|
+ reg &= 0x3f;
|
|
+ reg |= 0x80;
|
|
+ m88rs2000_demod_write(state, 0xb2, reg);
|
|
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
|
|
+
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe,
|
|
+ fe_sec_mini_cmd_t burst)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ u8 reg0, reg1;
|
|
+ deb_info("%s\n", __func__);
|
|
+ m88rs2000_demod_write(state, 0x9a, 0x30);
|
|
+ msleep(50);
|
|
+ reg0 = m88rs2000_demod_read(state, 0xb1);
|
|
+ reg1 = m88rs2000_demod_read(state, 0xb2);
|
|
+ /* TODO complete this section */
|
|
+ m88rs2000_demod_write(state, 0xb2, reg1);
|
|
+ m88rs2000_demod_write(state, 0xb1, reg0);
|
|
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ u8 reg0, reg1;
|
|
+ m88rs2000_demod_write(state, 0x9a, 0x30);
|
|
+ reg0 = m88rs2000_demod_read(state, 0xb1);
|
|
+ reg1 = m88rs2000_demod_read(state, 0xb2);
|
|
+
|
|
+ reg1 &= 0x3f;
|
|
+
|
|
+ switch (tone) {
|
|
+ case SEC_TONE_ON:
|
|
+ reg0 |= 0x4;
|
|
+ reg0 &= 0xbc;
|
|
+ break;
|
|
+ case SEC_TONE_OFF:
|
|
+ reg1 |= 0x80;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ m88rs2000_demod_write(state, 0xb2, reg1);
|
|
+ m88rs2000_demod_write(state, 0xb1, reg0);
|
|
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct inittab {
|
|
+ u8 cmd;
|
|
+ u8 reg;
|
|
+ u8 val;
|
|
+};
|
|
+
|
|
+struct inittab m88rs2000_setup[] = {
|
|
+ {DEMOD_WRITE, 0x9a, 0x30},
|
|
+ {DEMOD_WRITE, 0x00, 0x01},
|
|
+ {WRITE_DELAY, 0x19, 0x00},
|
|
+ {DEMOD_WRITE, 0x00, 0x00},
|
|
+ {DEMOD_WRITE, 0x9a, 0xb0},
|
|
+ {DEMOD_WRITE, 0x81, 0xc1},
|
|
+ {TUNER_WRITE, 0x42, 0x73},
|
|
+ {TUNER_WRITE, 0x05, 0x07},
|
|
+ {TUNER_WRITE, 0x20, 0x27},
|
|
+ {TUNER_WRITE, 0x07, 0x02},
|
|
+ {TUNER_WRITE, 0x11, 0xff},
|
|
+ {TUNER_WRITE, 0x60, 0xf9},
|
|
+ {TUNER_WRITE, 0x08, 0x01},
|
|
+ {TUNER_WRITE, 0x00, 0x41},
|
|
+ {DEMOD_WRITE, 0x81, 0x81},
|
|
+ {DEMOD_WRITE, 0x86, 0xc6},
|
|
+ {DEMOD_WRITE, 0x9a, 0x30},
|
|
+ {DEMOD_WRITE, 0xf0, 0x22},
|
|
+ {DEMOD_WRITE, 0xf1, 0xbf},
|
|
+ {DEMOD_WRITE, 0xb0, 0x45},
|
|
+ {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/
|
|
+ {DEMOD_WRITE, 0x9a, 0xb0},
|
|
+ {0xff, 0xaa, 0xff}
|
|
+};
|
|
+
|
|
+struct inittab m88rs2000_shutdown[] = {
|
|
+ {DEMOD_WRITE, 0x9a, 0x30},
|
|
+ {DEMOD_WRITE, 0xb0, 0x00},
|
|
+ {DEMOD_WRITE, 0xf1, 0x89},
|
|
+ {DEMOD_WRITE, 0x00, 0x01},
|
|
+ {DEMOD_WRITE, 0x9a, 0xb0},
|
|
+ {TUNER_WRITE, 0x00, 0x40},
|
|
+ {DEMOD_WRITE, 0x81, 0x81},
|
|
+ {0xff, 0xaa, 0xff}
|
|
+};
|
|
+
|
|
+struct inittab tuner_reset[] = {
|
|
+ {TUNER_WRITE, 0x42, 0x73},
|
|
+ {TUNER_WRITE, 0x05, 0x07},
|
|
+ {TUNER_WRITE, 0x20, 0x27},
|
|
+ {TUNER_WRITE, 0x07, 0x02},
|
|
+ {TUNER_WRITE, 0x11, 0xff},
|
|
+ {TUNER_WRITE, 0x60, 0xf9},
|
|
+ {TUNER_WRITE, 0x08, 0x01},
|
|
+ {TUNER_WRITE, 0x00, 0x41},
|
|
+ {0xff, 0xaa, 0xff}
|
|
+};
|
|
+
|
|
+struct inittab fe_reset[] = {
|
|
+ {DEMOD_WRITE, 0x00, 0x01},
|
|
+ {DEMOD_WRITE, 0xf1, 0xbf},
|
|
+ {DEMOD_WRITE, 0x00, 0x01},
|
|
+ {DEMOD_WRITE, 0x20, 0x81},
|
|
+ {DEMOD_WRITE, 0x21, 0x80},
|
|
+ {DEMOD_WRITE, 0x10, 0x33},
|
|
+ {DEMOD_WRITE, 0x11, 0x44},
|
|
+ {DEMOD_WRITE, 0x12, 0x07},
|
|
+ {DEMOD_WRITE, 0x18, 0x20},
|
|
+ {DEMOD_WRITE, 0x28, 0x04},
|
|
+ {DEMOD_WRITE, 0x29, 0x8e},
|
|
+ {DEMOD_WRITE, 0x3b, 0xff},
|
|
+ {DEMOD_WRITE, 0x32, 0x10},
|
|
+ {DEMOD_WRITE, 0x33, 0x02},
|
|
+ {DEMOD_WRITE, 0x34, 0x30},
|
|
+ {DEMOD_WRITE, 0x35, 0xff},
|
|
+ {DEMOD_WRITE, 0x38, 0x50},
|
|
+ {DEMOD_WRITE, 0x39, 0x68},
|
|
+ {DEMOD_WRITE, 0x3c, 0x7f},
|
|
+ {DEMOD_WRITE, 0x3d, 0x0f},
|
|
+ {DEMOD_WRITE, 0x45, 0x20},
|
|
+ {DEMOD_WRITE, 0x46, 0x24},
|
|
+ {DEMOD_WRITE, 0x47, 0x7c},
|
|
+ {DEMOD_WRITE, 0x48, 0x16},
|
|
+ {DEMOD_WRITE, 0x49, 0x04},
|
|
+ {DEMOD_WRITE, 0x4a, 0x01},
|
|
+ {DEMOD_WRITE, 0x4b, 0x78},
|
|
+ {DEMOD_WRITE, 0X4d, 0xd2},
|
|
+ {DEMOD_WRITE, 0x4e, 0x6d},
|
|
+ {DEMOD_WRITE, 0x50, 0x30},
|
|
+ {DEMOD_WRITE, 0x51, 0x30},
|
|
+ {DEMOD_WRITE, 0x54, 0x7b},
|
|
+ {DEMOD_WRITE, 0x56, 0x09},
|
|
+ {DEMOD_WRITE, 0x58, 0x59},
|
|
+ {DEMOD_WRITE, 0x59, 0x37},
|
|
+ {DEMOD_WRITE, 0x63, 0xfa},
|
|
+ {0xff, 0xaa, 0xff}
|
|
+};
|
|
+
|
|
+struct inittab fe_trigger[] = {
|
|
+ {DEMOD_WRITE, 0x97, 0x04},
|
|
+ {DEMOD_WRITE, 0x99, 0x77},
|
|
+ {DEMOD_WRITE, 0x9b, 0x64},
|
|
+ {DEMOD_WRITE, 0x9e, 0x00},
|
|
+ {DEMOD_WRITE, 0x9f, 0xf8},
|
|
+ {DEMOD_WRITE, 0xa0, 0x20},
|
|
+ {DEMOD_WRITE, 0xa1, 0xe0},
|
|
+ {DEMOD_WRITE, 0xa3, 0x38},
|
|
+ {DEMOD_WRITE, 0x98, 0xff},
|
|
+ {DEMOD_WRITE, 0xc0, 0x0f},
|
|
+ {DEMOD_WRITE, 0x89, 0x01},
|
|
+ {DEMOD_WRITE, 0x00, 0x00},
|
|
+ {WRITE_DELAY, 0x0a, 0x00},
|
|
+ {DEMOD_WRITE, 0x00, 0x01},
|
|
+ {DEMOD_WRITE, 0x00, 0x00},
|
|
+ {DEMOD_WRITE, 0x9a, 0xb0},
|
|
+ {0xff, 0xaa, 0xff}
|
|
+};
|
|
+
|
|
+static int m88rs2000_tab_set(struct m88rs2000_state *state,
|
|
+ struct inittab *tab)
|
|
+{
|
|
+ int ret = 0;
|
|
+ u8 i;
|
|
+ if (tab == NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+ for (i = 0; i < 255; i++) {
|
|
+ switch (tab[i].cmd) {
|
|
+ case 0x01:
|
|
+ ret = m88rs2000_demod_write(state, tab[i].reg,
|
|
+ tab[i].val);
|
|
+ break;
|
|
+ case 0x02:
|
|
+ ret = m88rs2000_tuner_write(state, tab[i].reg,
|
|
+ tab[i].val);
|
|
+ break;
|
|
+ case 0x10:
|
|
+ if (tab[i].reg > 0)
|
|
+ mdelay(tab[i].reg);
|
|
+ break;
|
|
+ case 0xff:
|
|
+ if (tab[i].reg == 0xaa && tab[i].val == 0xff)
|
|
+ return 0;
|
|
+ case 0x00:
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (ret < 0)
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+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" : "??");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_startup(struct m88rs2000_state *state)
|
|
+{
|
|
+ int ret = 0;
|
|
+ u8 reg;
|
|
+
|
|
+ reg = m88rs2000_tuner_read(state, 0x00);
|
|
+ if ((reg & 0x40) == 0)
|
|
+ ret = -ENODEV;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int m88rs2000_init(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ int ret;
|
|
+
|
|
+ deb_info("m88rs2000: init chip\n");
|
|
+ /* Setup frontend from shutdown/cold */
|
|
+ ret = m88rs2000_tab_set(state, m88rs2000_setup);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int m88rs2000_sleep(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ int ret;
|
|
+ /* Shutdown the frondend */
|
|
+ ret = m88rs2000_tab_set(state, m88rs2000_shutdown);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ u8 reg = m88rs2000_demod_read(state, 0x8c);
|
|
+
|
|
+ *status = 0;
|
|
+
|
|
+ if ((reg & 0x7) == 0x7) {
|
|
+ *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
|
|
+ | FE_HAS_LOCK;
|
|
+ if (state->config->set_ts_params)
|
|
+ state->config->set_ts_params(fe, CALL_IS_READ);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Extact code for these unknown but lmedm04 driver uses interupt callbacks */
|
|
+
|
|
+static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber)
|
|
+{
|
|
+ deb_info("m88rs2000_read_ber %d\n", *ber);
|
|
+ *ber = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_read_signal_strength(struct dvb_frontend *fe,
|
|
+ u16 *strength)
|
|
+{
|
|
+ *strength = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
|
|
+{
|
|
+ deb_info("m88rs2000_read_snr %d\n", *snr);
|
|
+ *snr = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
|
|
+{
|
|
+ deb_info("m88rs2000_read_ber %d\n", *ucblocks);
|
|
+ *ucblocks = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_tuner_gate_ctrl(struct m88rs2000_state *state, u8 offset)
|
|
+{
|
|
+ int ret;
|
|
+ ret = m88rs2000_tuner_write(state, 0x51, 0x1f - offset);
|
|
+ ret |= m88rs2000_tuner_write(state, 0x51, 0x1f);
|
|
+ ret |= m88rs2000_tuner_write(state, 0x50, offset);
|
|
+ ret |= m88rs2000_tuner_write(state, 0x50, 0x00);
|
|
+ msleep(20);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int m88rs2000_set_tuner_rf(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ int reg;
|
|
+ reg = m88rs2000_tuner_read(state, 0x3d);
|
|
+ reg &= 0x7f;
|
|
+ if (reg < 0x16)
|
|
+ reg = 0xa1;
|
|
+ else if (reg == 0x16)
|
|
+ reg = 0x99;
|
|
+ else
|
|
+ reg = 0xf9;
|
|
+
|
|
+ m88rs2000_tuner_write(state, 0x60, reg);
|
|
+ reg = m88rs2000_tuner_gate_ctrl(state, 0x08);
|
|
+
|
|
+ if (fe->ops.i2c_gate_ctrl)
|
|
+ fe->ops.i2c_gate_ctrl(fe, 0);
|
|
+ return reg;
|
|
+}
|
|
+
|
|
+static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset)
|
|
+{
|
|
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ int ret;
|
|
+ u32 frequency = c->frequency;
|
|
+ s32 offset_khz;
|
|
+ s32 tmp;
|
|
+ u32 symbol_rate = (c->symbol_rate / 1000);
|
|
+ u32 f3db, gdiv28;
|
|
+ u16 value, ndiv, lpf_coeff;
|
|
+ u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf;
|
|
+ u8 lo = 0x01, div4 = 0x0;
|
|
+
|
|
+ /* Reset Tuner */
|
|
+ ret = m88rs2000_tab_set(state, tuner_reset);
|
|
+
|
|
+ /* Calculate frequency divider */
|
|
+ if (frequency < 1060000) {
|
|
+ lo |= 0x10;
|
|
+ div4 = 0x1;
|
|
+ ndiv = (frequency * 14 * 4) / FE_CRYSTAL_KHZ;
|
|
+ } else
|
|
+ ndiv = (frequency * 14 * 2) / FE_CRYSTAL_KHZ;
|
|
+ ndiv = ndiv + ndiv % 2;
|
|
+ ndiv = ndiv - 1024;
|
|
+
|
|
+ ret = m88rs2000_tuner_write(state, 0x10, 0x80 | lo);
|
|
+
|
|
+ /* Set frequency divider */
|
|
+ ret |= m88rs2000_tuner_write(state, 0x01, (ndiv >> 8) & 0xf);
|
|
+ ret |= m88rs2000_tuner_write(state, 0x02, ndiv & 0xff);
|
|
+
|
|
+ ret |= m88rs2000_tuner_write(state, 0x03, 0x06);
|
|
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x10);
|
|
+ if (ret < 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /* Tuner Frequency Range */
|
|
+ ret = m88rs2000_tuner_write(state, 0x10, lo);
|
|
+
|
|
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x08);
|
|
+
|
|
+ /* Tuner RF */
|
|
+ ret |= m88rs2000_set_tuner_rf(fe);
|
|
+
|
|
+ gdiv28 = (FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000;
|
|
+ ret |= m88rs2000_tuner_write(state, 0x04, gdiv28 & 0xff);
|
|
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
|
|
+ if (ret < 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ value = m88rs2000_tuner_read(state, 0x26);
|
|
+
|
|
+ f3db = (symbol_rate * 135) / 200 + 2000;
|
|
+ f3db += FREQ_OFFSET_LOW_SYM_RATE;
|
|
+ if (f3db < 7000)
|
|
+ f3db = 7000;
|
|
+ if (f3db > 40000)
|
|
+ f3db = 40000;
|
|
+
|
|
+ gdiv28 = gdiv28 * 207 / (value * 2 + 151);
|
|
+ mlpf_max = gdiv28 * 135 / 100;
|
|
+ mlpf_min = gdiv28 * 78 / 100;
|
|
+ if (mlpf_max > 63)
|
|
+ mlpf_max = 63;
|
|
+
|
|
+ lpf_coeff = 2766;
|
|
+
|
|
+ nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
|
|
+ (FE_CRYSTAL_KHZ / 1000) + 1) / 2;
|
|
+ if (nlpf > 23)
|
|
+ nlpf = 23;
|
|
+ if (nlpf < 1)
|
|
+ nlpf = 1;
|
|
+
|
|
+ lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
|
|
+ * lpf_coeff * 2 / f3db + 1) / 2;
|
|
+
|
|
+ if (lpf_mxdiv < mlpf_min) {
|
|
+ nlpf++;
|
|
+ lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
|
|
+ * lpf_coeff * 2 / f3db + 1) / 2;
|
|
+ }
|
|
+
|
|
+ if (lpf_mxdiv > mlpf_max)
|
|
+ lpf_mxdiv = mlpf_max;
|
|
+
|
|
+ ret = m88rs2000_tuner_write(state, 0x04, lpf_mxdiv);
|
|
+ ret |= m88rs2000_tuner_write(state, 0x06, nlpf);
|
|
+
|
|
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
|
|
+
|
|
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x01);
|
|
+
|
|
+ msleep(80);
|
|
+ /* calculate offset assuming 96000kHz*/
|
|
+ offset_khz = (ndiv - ndiv % 2 + 1024) * FE_CRYSTAL_KHZ
|
|
+ / 14 / (div4 + 1) / 2;
|
|
+
|
|
+ offset_khz -= frequency;
|
|
+
|
|
+ tmp = offset_khz;
|
|
+ tmp *= 65536;
|
|
+
|
|
+ tmp = (2 * tmp + 96000) / (2 * 96000);
|
|
+ if (tmp < 0)
|
|
+ tmp += 65536;
|
|
+
|
|
+ *offset = tmp & 0xffff;
|
|
+
|
|
+ if (fe->ops.i2c_gate_ctrl)
|
|
+ fe->ops.i2c_gate_ctrl(fe, 0);
|
|
+
|
|
+ return (ret < 0) ? -EINVAL : 0;
|
|
+}
|
|
+
|
|
+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 */
|
|
+/* case FEC_1_2:
|
|
+ fec_set = 0x88;
|
|
+ break;
|
|
+ case FEC_2_3:
|
|
+ fec_set = 0x68;
|
|
+ break;
|
|
+ case FEC_3_4:
|
|
+ fec_set = 0x48;
|
|
+ break;
|
|
+ case FEC_5_6:
|
|
+ fec_set = 0x28;
|
|
+ break;
|
|
+ case FEC_7_8:
|
|
+ fec_set = 0x18;
|
|
+ break; */
|
|
+ case FEC_AUTO:
|
|
+ default:
|
|
+ fec_set = 0x08;
|
|
+ }
|
|
+ ret = m88rs2000_demod_write(state, 0x76, fec_set);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state)
|
|
+{
|
|
+ u8 reg;
|
|
+ m88rs2000_demod_write(state, 0x9a, 0x30);
|
|
+ reg = m88rs2000_demod_read(state, 0x76);
|
|
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
|
|
+
|
|
+ switch (reg) {
|
|
+ case 0x88:
|
|
+ return FEC_1_2;
|
|
+ case 0x68:
|
|
+ return FEC_2_3;
|
|
+ case 0x48:
|
|
+ return FEC_3_4;
|
|
+ case 0x28:
|
|
+ return FEC_5_6;
|
|
+ case 0x18:
|
|
+ return FEC_7_8;
|
|
+ case 0x08:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return FEC_AUTO;
|
|
+}
|
|
+
|
|
+static int m88rs2000_set_frontend(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
+ fe_status_t status;
|
|
+ int i, ret;
|
|
+ u16 offset = 0;
|
|
+ u8 reg;
|
|
+
|
|
+ state->no_lock_count = 0;
|
|
+
|
|
+ if (c->delivery_system != SYS_DVBS) {
|
|
+ deb_info("%s: unsupported delivery "
|
|
+ "system selected (%d)\n",
|
|
+ __func__, c->delivery_system);
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ /* Set Tuner */
|
|
+ ret = m88rs2000_set_tuner(fe, &offset);
|
|
+ if (ret < 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = m88rs2000_demod_write(state, 0x9a, 0x30);
|
|
+ /* Unknown usually 0xc6 sometimes 0xc1 */
|
|
+ reg = m88rs2000_demod_read(state, 0x86);
|
|
+ ret |= m88rs2000_demod_write(state, 0x86, reg);
|
|
+ /* Offset lower nibble always 0 */
|
|
+ ret |= m88rs2000_demod_write(state, 0x9c, (offset >> 8));
|
|
+ ret |= m88rs2000_demod_write(state, 0x9d, offset & 0xf0);
|
|
+
|
|
+
|
|
+ /* Reset Demod */
|
|
+ ret = m88rs2000_tab_set(state, fe_reset);
|
|
+ if (ret < 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /* Unknown */
|
|
+ reg = m88rs2000_demod_read(state, 0x70);
|
|
+ ret = m88rs2000_demod_write(state, 0x70, reg);
|
|
+
|
|
+ /* Set FEC */
|
|
+ ret |= m88rs2000_set_fec(state, c->fec_inner);
|
|
+ ret |= m88rs2000_demod_write(state, 0x85, 0x1);
|
|
+ ret |= m88rs2000_demod_write(state, 0x8a, 0xbf);
|
|
+ ret |= m88rs2000_demod_write(state, 0x8d, 0x1e);
|
|
+ ret |= m88rs2000_demod_write(state, 0x90, 0xf1);
|
|
+ ret |= m88rs2000_demod_write(state, 0x91, 0x08);
|
|
+
|
|
+ if (ret < 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /* Set Symbol Rate */
|
|
+ ret = m88rs2000_set_symbolrate(fe, c->symbol_rate);
|
|
+ if (ret < 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /* Set up Demod */
|
|
+ ret = m88rs2000_tab_set(state, fe_trigger);
|
|
+ if (ret < 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ for (i = 0; i < 25; i++) {
|
|
+ u8 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) {
|
|
+ reg = m88rs2000_demod_read(state, 0x70);
|
|
+ reg ^= 0x4;
|
|
+ m88rs2000_demod_write(state, 0x70, reg);
|
|
+ state->no_lock_count = 0;
|
|
+ }
|
|
+ if (state->no_lock_count == 20)
|
|
+ m88rs2000_set_tuner_rf(fe);
|
|
+ msleep(20);
|
|
+ }
|
|
+
|
|
+ if (status & FE_HAS_LOCK) {
|
|
+ state->fec_inner = m88rs2000_get_fec(state);
|
|
+ /* Uknown suspect SNR level */
|
|
+ reg = m88rs2000_demod_read(state, 0x65);
|
|
+ }
|
|
+
|
|
+ state->tuner_frequency = c->frequency;
|
|
+ state->symbol_rate = c->symbol_rate;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_get_frontend(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ c->fec_inner = state->fec_inner;
|
|
+ c->frequency = state->tuner_frequency;
|
|
+ c->symbol_rate = state->symbol_rate;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+
|
|
+ if (enable)
|
|
+ m88rs2000_demod_write(state, 0x81, 0x84);
|
|
+ else
|
|
+ m88rs2000_demod_write(state, 0x81, 0x81);
|
|
+ udelay(10);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void m88rs2000_release(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct m88rs2000_state *state = fe->demodulator_priv;
|
|
+ kfree(state);
|
|
+}
|
|
+
|
|
+static struct dvb_frontend_ops m88rs2000_ops = {
|
|
+ .delsys = { SYS_DVBS },
|
|
+ .info = {
|
|
+ .name = "M88RS2000 DVB-S",
|
|
+ .frequency_min = 950000,
|
|
+ .frequency_max = 2150000,
|
|
+ .frequency_stepsize = 1000, /* kHz for QPSK frontends */
|
|
+ .frequency_tolerance = 5000,
|
|
+ .symbol_rate_min = 1000000,
|
|
+ .symbol_rate_max = 45000000,
|
|
+ .symbol_rate_tolerance = 500, /* ppm */
|
|
+ .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_QPSK |
|
|
+ FE_CAN_FEC_AUTO
|
|
+ },
|
|
+
|
|
+ .release = m88rs2000_release,
|
|
+ .init = m88rs2000_init,
|
|
+ .sleep = m88rs2000_sleep,
|
|
+ .write = m88rs2000_write,
|
|
+ .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl,
|
|
+ .read_status = m88rs2000_read_status,
|
|
+ .read_ber = m88rs2000_read_ber,
|
|
+ .read_signal_strength = m88rs2000_read_signal_strength,
|
|
+ .read_snr = m88rs2000_read_snr,
|
|
+ .read_ucblocks = m88rs2000_read_ucblocks,
|
|
+ .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg,
|
|
+ .diseqc_send_burst = m88rs2000_send_diseqc_burst,
|
|
+ .set_tone = m88rs2000_set_tone,
|
|
+ .set_voltage = m88rs2000_set_voltage,
|
|
+
|
|
+ .set_frontend = m88rs2000_set_frontend,
|
|
+ .get_frontend = m88rs2000_get_frontend,
|
|
+};
|
|
+
|
|
+struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
|
|
+ struct i2c_adapter *i2c)
|
|
+{
|
|
+ struct m88rs2000_state *state = NULL;
|
|
+
|
|
+ /* allocate memory for the internal state */
|
|
+ state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL);
|
|
+ if (state == NULL)
|
|
+ goto error;
|
|
+
|
|
+ /* setup the state */
|
|
+ state->config = config;
|
|
+ state->i2c = i2c;
|
|
+ state->tuner_frequency = 0;
|
|
+ state->symbol_rate = 0;
|
|
+ state->fec_inner = 0;
|
|
+
|
|
+ if (m88rs2000_startup(state) < 0)
|
|
+ goto error;
|
|
+
|
|
+ /* create dvb_frontend */
|
|
+ memcpy(&state->frontend.ops, &m88rs2000_ops,
|
|
+ sizeof(struct dvb_frontend_ops));
|
|
+ state->frontend.demodulator_priv = state;
|
|
+ return &state->frontend;
|
|
+
|
|
+error:
|
|
+ kfree(state);
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(m88rs2000_attach);
|
|
+
|
|
+MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver");
|
|
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_VERSION("1.13");
|
|
+
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/m88rs2000.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/m88rs2000.h
|
|
@@ -0,0 +1,66 @@
|
|
+/*
|
|
+ Driver for M88RS2000 demodulator
|
|
+
|
|
+ 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 M88RS2000_H
|
|
+#define M88RS2000_H
|
|
+
|
|
+#include <linux/dvb/frontend.h>
|
|
+#include "dvb_frontend.h"
|
|
+
|
|
+struct m88rs2000_config {
|
|
+ /* Demodulator i2c address */
|
|
+ u8 demod_addr;
|
|
+ /* Tuner address */
|
|
+ u8 tuner_addr;
|
|
+
|
|
+ u8 *inittab;
|
|
+
|
|
+ /* minimum delay before retuning */
|
|
+ int min_delay_ms;
|
|
+
|
|
+ int (*set_ts_params)(struct dvb_frontend *, int);
|
|
+};
|
|
+
|
|
+enum {
|
|
+ CALL_IS_SET_FRONTEND = 0x0,
|
|
+ CALL_IS_READ,
|
|
+};
|
|
+
|
|
+#if defined(CONFIG_DVB_M88RS2000) || (defined(CONFIG_DVB_M88RS2000_MODULE) && \
|
|
+ defined(MODULE))
|
|
+extern struct dvb_frontend *m88rs2000_attach(
|
|
+ const struct m88rs2000_config *config, struct i2c_adapter *i2c);
|
|
+#else
|
|
+static inline struct dvb_frontend *m88rs2000_attach(
|
|
+ const struct m88rs2000_config *config, struct i2c_adapter *i2c)
|
|
+{
|
|
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
|
+ return NULL;
|
|
+}
|
|
+#endif /* CONFIG_DVB_M88RS2000 */
|
|
+
|
|
+#define FE_CRYSTAL_KHZ 27000
|
|
+#define FREQ_OFFSET_LOW_SYM_RATE 3000
|
|
+
|
|
+enum {
|
|
+ DEMOD_WRITE = 0x1,
|
|
+ TUNER_WRITE,
|
|
+ WRITE_DELAY = 0x10,
|
|
+};
|
|
+#endif /* M88RS2000_H */
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/lmedm04.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/lmedm04.h
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/lmedm04.h
|
|
@@ -41,6 +41,7 @@
|
|
#define LME_ST_ON_W {0x06, 0x00}
|
|
#define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0}
|
|
#define LME_ZERO_PID {0x03, 0x06, 0x00, 0x00, 0x01, 0x00, 0x20, 0x9c}
|
|
+#define LME_ALL_PIDS {0x03, 0x06, 0x00, 0xff, 0x01, 0x1f, 0x20, 0x81}
|
|
|
|
/* LNB Voltage
|
|
* 07 XX XX
|
|
Index: linux-3.3.x86_64/drivers/media/video/adv7183.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/adv7183.c
|
|
@@ -0,0 +1,699 @@
|
|
+/*
|
|
+ * adv7183.c Analog Devices ADV7183 video decoder driver
|
|
+ *
|
|
+ * Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/delay.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/videodev2.h>
|
|
+
|
|
+#include <media/adv7183.h>
|
|
+#include <media/v4l2-chip-ident.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-device.h>
|
|
+
|
|
+#include "adv7183_regs.h"
|
|
+
|
|
+struct adv7183 {
|
|
+ struct v4l2_subdev sd;
|
|
+ struct v4l2_ctrl_handler hdl;
|
|
+
|
|
+ v4l2_std_id std; /* Current set standard */
|
|
+ u32 input;
|
|
+ u32 output;
|
|
+ unsigned reset_pin;
|
|
+ unsigned oe_pin;
|
|
+ struct v4l2_mbus_framefmt fmt;
|
|
+};
|
|
+
|
|
+/* EXAMPLES USING 27 MHz CLOCK
|
|
+ * Mode 1 CVBS Input (Composite Video on AIN5)
|
|
+ * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8.
|
|
+ */
|
|
+static const unsigned char adv7183_init_regs[] = {
|
|
+ ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */
|
|
+ ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */
|
|
+ ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */
|
|
+ ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */
|
|
+ ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */
|
|
+ /* ADI recommended programming sequence */
|
|
+ ADV7183_ADI_CTRL, 0x80,
|
|
+ ADV7183_CTI_DNR_CTRL_4, 0x20,
|
|
+ 0x52, 0x18,
|
|
+ 0x58, 0xED,
|
|
+ 0x77, 0xC5,
|
|
+ 0x7C, 0x93,
|
|
+ 0x7D, 0x00,
|
|
+ 0xD0, 0x48,
|
|
+ 0xD5, 0xA0,
|
|
+ 0xD7, 0xEA,
|
|
+ ADV7183_SD_SATURATION_CR, 0x3E,
|
|
+ ADV7183_PAL_V_END, 0x3E,
|
|
+ ADV7183_PAL_F_TOGGLE, 0x0F,
|
|
+ ADV7183_ADI_CTRL, 0x00,
|
|
+};
|
|
+
|
|
+static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd)
|
|
+{
|
|
+ return container_of(sd, struct adv7183, sd);
|
|
+}
|
|
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ return &container_of(ctrl->handler, struct adv7183, hdl)->sd;
|
|
+}
|
|
+
|
|
+static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+
|
|
+ return i2c_smbus_read_byte_data(client, reg);
|
|
+}
|
|
+
|
|
+static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg,
|
|
+ unsigned char value)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+
|
|
+ return i2c_smbus_write_byte_data(client, reg, value);
|
|
+}
|
|
+
|
|
+static int adv7183_writeregs(struct v4l2_subdev *sd,
|
|
+ const unsigned char *regs, unsigned int num)
|
|
+{
|
|
+ unsigned char reg, data;
|
|
+ unsigned int cnt = 0;
|
|
+
|
|
+ if (num & 0x1) {
|
|
+ v4l2_err(sd, "invalid regs array\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ while (cnt < num) {
|
|
+ reg = *regs++;
|
|
+ data = *regs++;
|
|
+ cnt += 2;
|
|
+
|
|
+ adv7183_write(sd, reg, data);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_log_status(struct v4l2_subdev *sd)
|
|
+{
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+
|
|
+ v4l2_info(sd, "adv7183: Input control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_IN_CTRL));
|
|
+ v4l2_info(sd, "adv7183: Video selection = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_VD_SEL));
|
|
+ v4l2_info(sd, "adv7183: Output control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_OUT_CTRL));
|
|
+ v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_EXT_OUT_CTRL));
|
|
+ v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_AUTO_DET_EN));
|
|
+ v4l2_info(sd, "adv7183: Contrast = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_CONTRAST));
|
|
+ v4l2_info(sd, "adv7183: Brightness = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_BRIGHTNESS));
|
|
+ v4l2_info(sd, "adv7183: Hue = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_HUE));
|
|
+ v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_DEF_Y));
|
|
+ v4l2_info(sd, "adv7183: Default value C = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_DEF_C));
|
|
+ v4l2_info(sd, "adv7183: ADI control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_ADI_CTRL));
|
|
+ v4l2_info(sd, "adv7183: Power Management = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_POW_MANAGE));
|
|
+ v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_STATUS_1),
|
|
+ adv7183_read(sd, ADV7183_STATUS_2),
|
|
+ adv7183_read(sd, ADV7183_STATUS_3));
|
|
+ v4l2_info(sd, "adv7183: Ident = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_IDENT));
|
|
+ v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL));
|
|
+ v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1));
|
|
+ v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL),
|
|
+ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2));
|
|
+ v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_COMB_FILT_CTRL));
|
|
+ v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_ADI_CTRL_2));
|
|
+ v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_PIX_DELAY_CTRL));
|
|
+ v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_MISC_GAIN_CTRL));
|
|
+ v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_AGC_MODE_CTRL));
|
|
+ v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1),
|
|
+ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2));
|
|
+ v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1),
|
|
+ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2));
|
|
+ v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),
|
|
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),
|
|
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3));
|
|
+ v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_1),
|
|
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_2),
|
|
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_3));
|
|
+ v4l2_info(sd, "adv7183: Polarity = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_POLARITY));
|
|
+ v4l2_info(sd, "adv7183: ADC control = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_ADC_CTRL));
|
|
+ v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_SD_OFFSET_CB),
|
|
+ adv7183_read(sd, ADV7183_SD_OFFSET_CR));
|
|
+ v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_SD_SATURATION_CB),
|
|
+ adv7183_read(sd, ADV7183_SD_SATURATION_CR));
|
|
+ v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n",
|
|
+ adv7183_read(sd, ADV7183_DRIVE_STR));
|
|
+ v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
|
|
+{
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+
|
|
+ *std = decoder->std;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
|
|
+{
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+ int reg;
|
|
+
|
|
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
|
|
+ if (std == V4L2_STD_PAL_60)
|
|
+ reg |= 0x60;
|
|
+ else if (std == V4L2_STD_NTSC_443)
|
|
+ reg |= 0x70;
|
|
+ else if (std == V4L2_STD_PAL_N)
|
|
+ reg |= 0x90;
|
|
+ else if (std == V4L2_STD_PAL_M)
|
|
+ reg |= 0xA0;
|
|
+ else if (std == V4L2_STD_PAL_Nc)
|
|
+ reg |= 0xC0;
|
|
+ else if (std & V4L2_STD_PAL)
|
|
+ reg |= 0x80;
|
|
+ else if (std & V4L2_STD_NTSC)
|
|
+ reg |= 0x50;
|
|
+ else if (std & V4L2_STD_SECAM)
|
|
+ reg |= 0xE0;
|
|
+ else
|
|
+ return -EINVAL;
|
|
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
|
|
+
|
|
+ decoder->std = std;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_reset(struct v4l2_subdev *sd, u32 val)
|
|
+{
|
|
+ int reg;
|
|
+
|
|
+ reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80;
|
|
+ adv7183_write(sd, ADV7183_POW_MANAGE, reg);
|
|
+ /* wait 5ms before any further i2c writes are performed */
|
|
+ usleep_range(5000, 10000);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_s_routing(struct v4l2_subdev *sd,
|
|
+ u32 input, u32 output, u32 config)
|
|
+{
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+ int reg;
|
|
+
|
|
+ if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (input != decoder->input) {
|
|
+ decoder->input = input;
|
|
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0;
|
|
+ switch (input) {
|
|
+ case ADV7183_COMPOSITE1:
|
|
+ reg |= 0x1;
|
|
+ break;
|
|
+ case ADV7183_COMPOSITE2:
|
|
+ reg |= 0x2;
|
|
+ break;
|
|
+ case ADV7183_COMPOSITE3:
|
|
+ reg |= 0x3;
|
|
+ break;
|
|
+ case ADV7183_COMPOSITE4:
|
|
+ reg |= 0x4;
|
|
+ break;
|
|
+ case ADV7183_COMPOSITE5:
|
|
+ reg |= 0x5;
|
|
+ break;
|
|
+ case ADV7183_COMPOSITE6:
|
|
+ reg |= 0xB;
|
|
+ break;
|
|
+ case ADV7183_COMPOSITE7:
|
|
+ reg |= 0xC;
|
|
+ break;
|
|
+ case ADV7183_COMPOSITE8:
|
|
+ reg |= 0xD;
|
|
+ break;
|
|
+ case ADV7183_COMPOSITE9:
|
|
+ reg |= 0xE;
|
|
+ break;
|
|
+ case ADV7183_COMPOSITE10:
|
|
+ reg |= 0xF;
|
|
+ break;
|
|
+ case ADV7183_SVIDEO0:
|
|
+ reg |= 0x6;
|
|
+ break;
|
|
+ case ADV7183_SVIDEO1:
|
|
+ reg |= 0x7;
|
|
+ break;
|
|
+ case ADV7183_SVIDEO2:
|
|
+ reg |= 0x8;
|
|
+ break;
|
|
+ case ADV7183_COMPONENT0:
|
|
+ reg |= 0x9;
|
|
+ break;
|
|
+ case ADV7183_COMPONENT1:
|
|
+ reg |= 0xA;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
|
|
+ }
|
|
+
|
|
+ if (output != decoder->output) {
|
|
+ decoder->output = output;
|
|
+ reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0;
|
|
+ switch (output) {
|
|
+ case ADV7183_16BIT_OUT:
|
|
+ reg |= 0x9;
|
|
+ break;
|
|
+ default:
|
|
+ reg |= 0xC;
|
|
+ break;
|
|
+ }
|
|
+ adv7183_write(sd, ADV7183_OUT_CTRL, reg);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ struct v4l2_subdev *sd = to_sd(ctrl);
|
|
+ int val = ctrl->val;
|
|
+
|
|
+ switch (ctrl->id) {
|
|
+ case V4L2_CID_BRIGHTNESS:
|
|
+ if (val < 0)
|
|
+ val = 127 - val;
|
|
+ adv7183_write(sd, ADV7183_BRIGHTNESS, val);
|
|
+ break;
|
|
+ case V4L2_CID_CONTRAST:
|
|
+ adv7183_write(sd, ADV7183_CONTRAST, val);
|
|
+ break;
|
|
+ case V4L2_CID_SATURATION:
|
|
+ adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8);
|
|
+ adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF));
|
|
+ break;
|
|
+ case V4L2_CID_HUE:
|
|
+ adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8);
|
|
+ adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF));
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
|
|
+{
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+ int reg;
|
|
+
|
|
+ /* enable autodetection block */
|
|
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
|
|
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
|
|
+
|
|
+ /* wait autodetection switch */
|
|
+ mdelay(10);
|
|
+
|
|
+ /* get autodetection result */
|
|
+ reg = adv7183_read(sd, ADV7183_STATUS_1);
|
|
+ switch ((reg >> 0x4) & 0x7) {
|
|
+ case 0:
|
|
+ *std = V4L2_STD_NTSC;
|
|
+ break;
|
|
+ case 1:
|
|
+ *std = V4L2_STD_NTSC_443;
|
|
+ break;
|
|
+ case 2:
|
|
+ *std = V4L2_STD_PAL_M;
|
|
+ break;
|
|
+ case 3:
|
|
+ *std = V4L2_STD_PAL_60;
|
|
+ break;
|
|
+ case 4:
|
|
+ *std = V4L2_STD_PAL;
|
|
+ break;
|
|
+ case 5:
|
|
+ *std = V4L2_STD_SECAM;
|
|
+ break;
|
|
+ case 6:
|
|
+ *std = V4L2_STD_PAL_Nc;
|
|
+ break;
|
|
+ case 7:
|
|
+ *std = V4L2_STD_SECAM;
|
|
+ break;
|
|
+ default:
|
|
+ *std = V4L2_STD_UNKNOWN;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* after std detection, write back user set std */
|
|
+ adv7183_s_std(sd, decoder->std);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status)
|
|
+{
|
|
+ int reg;
|
|
+
|
|
+ *status = V4L2_IN_ST_NO_SIGNAL;
|
|
+ reg = adv7183_read(sd, ADV7183_STATUS_1);
|
|
+ if (reg < 0)
|
|
+ return reg;
|
|
+ if (reg & 0x1)
|
|
+ *status = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
|
|
+ enum v4l2_mbus_pixelcode *code)
|
|
+{
|
|
+ if (index > 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ *code = V4L2_MBUS_FMT_UYVY8_2X8;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd,
|
|
+ struct v4l2_mbus_framefmt *fmt)
|
|
+{
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+
|
|
+ fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
|
|
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
+ if (decoder->std & V4L2_STD_525_60) {
|
|
+ fmt->field = V4L2_FIELD_SEQ_TB;
|
|
+ fmt->width = 720;
|
|
+ fmt->height = 480;
|
|
+ } else {
|
|
+ fmt->field = V4L2_FIELD_SEQ_BT;
|
|
+ fmt->width = 720;
|
|
+ fmt->height = 576;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd,
|
|
+ struct v4l2_mbus_framefmt *fmt)
|
|
+{
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+
|
|
+ adv7183_try_mbus_fmt(sd, fmt);
|
|
+ decoder->fmt = *fmt;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd,
|
|
+ struct v4l2_mbus_framefmt *fmt)
|
|
+{
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+
|
|
+ *fmt = decoder->fmt;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_s_stream(struct v4l2_subdev *sd, int enable)
|
|
+{
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+
|
|
+ if (enable)
|
|
+ gpio_direction_output(decoder->oe_pin, 0);
|
|
+ else
|
|
+ gpio_direction_output(decoder->oe_pin, 1);
|
|
+ udelay(1);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_g_chip_ident(struct v4l2_subdev *sd,
|
|
+ struct v4l2_dbg_chip_ident *chip)
|
|
+{
|
|
+ int rev;
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+
|
|
+ /* 0x11 for adv7183, 0x13 for adv7183b */
|
|
+ rev = adv7183_read(sd, ADV7183_IDENT);
|
|
+
|
|
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+
|
|
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
|
|
+ return -EINVAL;
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
+ return -EPERM;
|
|
+ reg->val = adv7183_read(sd, reg->reg & 0xff);
|
|
+ reg->size = 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+
|
|
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
|
|
+ return -EINVAL;
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
+ return -EPERM;
|
|
+ adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff);
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
|
|
+ .s_ctrl = adv7183_s_ctrl,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_core_ops adv7183_core_ops = {
|
|
+ .log_status = adv7183_log_status,
|
|
+ .g_std = adv7183_g_std,
|
|
+ .s_std = adv7183_s_std,
|
|
+ .reset = adv7183_reset,
|
|
+ .g_chip_ident = adv7183_g_chip_ident,
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+ .g_register = adv7183_g_register,
|
|
+ .s_register = adv7183_s_register,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_video_ops adv7183_video_ops = {
|
|
+ .s_routing = adv7183_s_routing,
|
|
+ .querystd = adv7183_querystd,
|
|
+ .g_input_status = adv7183_g_input_status,
|
|
+ .enum_mbus_fmt = adv7183_enum_mbus_fmt,
|
|
+ .try_mbus_fmt = adv7183_try_mbus_fmt,
|
|
+ .s_mbus_fmt = adv7183_s_mbus_fmt,
|
|
+ .g_mbus_fmt = adv7183_g_mbus_fmt,
|
|
+ .s_stream = adv7183_s_stream,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_ops adv7183_ops = {
|
|
+ .core = &adv7183_core_ops,
|
|
+ .video = &adv7183_video_ops,
|
|
+};
|
|
+
|
|
+static int adv7183_probe(struct i2c_client *client,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ struct adv7183 *decoder;
|
|
+ struct v4l2_subdev *sd;
|
|
+ struct v4l2_ctrl_handler *hdl;
|
|
+ int ret;
|
|
+ struct v4l2_mbus_framefmt fmt;
|
|
+ const unsigned *pin_array;
|
|
+
|
|
+ /* 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);
|
|
+
|
|
+ pin_array = client->dev.platform_data;
|
|
+ if (pin_array == NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+ decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL);
|
|
+ if (decoder == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ decoder->reset_pin = pin_array[0];
|
|
+ decoder->oe_pin = pin_array[1];
|
|
+
|
|
+ if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) {
|
|
+ v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin);
|
|
+ ret = -EBUSY;
|
|
+ goto err_free_decoder;
|
|
+ }
|
|
+
|
|
+ if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) {
|
|
+ v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin);
|
|
+ ret = -EBUSY;
|
|
+ goto err_free_reset;
|
|
+ }
|
|
+
|
|
+ sd = &decoder->sd;
|
|
+ v4l2_i2c_subdev_init(sd, client, &adv7183_ops);
|
|
+
|
|
+ hdl = &decoder->hdl;
|
|
+ v4l2_ctrl_handler_init(hdl, 4);
|
|
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
|
|
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
|
|
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
|
|
+ V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80);
|
|
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
|
|
+ V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080);
|
|
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
|
|
+ V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080);
|
|
+ /* hook the control handler into the driver */
|
|
+ sd->ctrl_handler = hdl;
|
|
+ if (hdl->error) {
|
|
+ ret = hdl->error;
|
|
+
|
|
+ v4l2_ctrl_handler_free(hdl);
|
|
+ goto err_free_oe;
|
|
+ }
|
|
+
|
|
+ /* v4l2 doesn't support an autodetect standard, pick PAL as default */
|
|
+ decoder->std = V4L2_STD_PAL;
|
|
+ decoder->input = ADV7183_COMPOSITE4;
|
|
+ decoder->output = ADV7183_8BIT_OUT;
|
|
+
|
|
+ gpio_direction_output(decoder->oe_pin, 1);
|
|
+ /* reset chip */
|
|
+ gpio_direction_output(decoder->reset_pin, 0);
|
|
+ /* reset pulse width at least 5ms */
|
|
+ mdelay(10);
|
|
+ gpio_direction_output(decoder->reset_pin, 1);
|
|
+ /* wait 5ms before any further i2c writes are performed */
|
|
+ mdelay(5);
|
|
+
|
|
+ adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs));
|
|
+ adv7183_s_std(sd, decoder->std);
|
|
+ fmt.width = 720;
|
|
+ fmt.height = 576;
|
|
+ adv7183_s_mbus_fmt(sd, &fmt);
|
|
+
|
|
+ /* initialize the hardware to the default control values */
|
|
+ ret = v4l2_ctrl_handler_setup(hdl);
|
|
+ if (ret) {
|
|
+ v4l2_ctrl_handler_free(hdl);
|
|
+ goto err_free_oe;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+err_free_oe:
|
|
+ gpio_free(decoder->oe_pin);
|
|
+err_free_reset:
|
|
+ gpio_free(decoder->reset_pin);
|
|
+err_free_decoder:
|
|
+ kfree(decoder);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int adv7183_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
|
+ struct adv7183 *decoder = to_adv7183(sd);
|
|
+
|
|
+ v4l2_device_unregister_subdev(sd);
|
|
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
|
|
+ gpio_free(decoder->oe_pin);
|
|
+ gpio_free(decoder->reset_pin);
|
|
+ kfree(decoder);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct i2c_device_id adv7183_id[] = {
|
|
+ {"adv7183", 0},
|
|
+ {},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(i2c, adv7183_id);
|
|
+
|
|
+static struct i2c_driver adv7183_driver = {
|
|
+ .driver = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .name = "adv7183",
|
|
+ },
|
|
+ .probe = adv7183_probe,
|
|
+ .remove = __devexit_p(adv7183_remove),
|
|
+ .id_table = adv7183_id,
|
|
+};
|
|
+
|
|
+static __init int adv7183_init(void)
|
|
+{
|
|
+ return i2c_add_driver(&adv7183_driver);
|
|
+}
|
|
+
|
|
+static __exit void adv7183_exit(void)
|
|
+{
|
|
+ i2c_del_driver(&adv7183_driver);
|
|
+}
|
|
+
|
|
+module_init(adv7183_init);
|
|
+module_exit(adv7183_exit);
|
|
+
|
|
+MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver");
|
|
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
|
|
+MODULE_LICENSE("GPL v2");
|
|
Index: linux-3.3.x86_64/drivers/media/video/adv7183_regs.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/adv7183_regs.h
|
|
@@ -0,0 +1,107 @@
|
|
+/*
|
|
+ * adv7183 - Analog Devices ADV7183 video decoder registers
|
|
+ *
|
|
+ * Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#ifndef _ADV7183_REGS_H_
|
|
+#define _ADV7183_REGS_H_
|
|
+
|
|
+#define ADV7183_IN_CTRL 0x00 /* Input control */
|
|
+#define ADV7183_VD_SEL 0x01 /* Video selection */
|
|
+#define ADV7183_OUT_CTRL 0x03 /* Output control */
|
|
+#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */
|
|
+#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */
|
|
+#define ADV7183_CONTRAST 0x08 /* Contrast */
|
|
+#define ADV7183_BRIGHTNESS 0x0A /* Brightness */
|
|
+#define ADV7183_HUE 0x0B /* Hue */
|
|
+#define ADV7183_DEF_Y 0x0C /* Default value Y */
|
|
+#define ADV7183_DEF_C 0x0D /* Default value C */
|
|
+#define ADV7183_ADI_CTRL 0x0E /* ADI control */
|
|
+#define ADV7183_POW_MANAGE 0x0F /* Power Management */
|
|
+#define ADV7183_STATUS_1 0x10 /* Status 1 */
|
|
+#define ADV7183_IDENT 0x11 /* Ident */
|
|
+#define ADV7183_STATUS_2 0x12 /* Status 2 */
|
|
+#define ADV7183_STATUS_3 0x13 /* Status 3 */
|
|
+#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */
|
|
+#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */
|
|
+#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */
|
|
+#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */
|
|
+#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */
|
|
+#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */
|
|
+#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */
|
|
+#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */
|
|
+#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */
|
|
+#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */
|
|
+#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */
|
|
+#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */
|
|
+#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */
|
|
+#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */
|
|
+#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */
|
|
+#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */
|
|
+#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */
|
|
+#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */
|
|
+#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */
|
|
+#define ADV7183_POLARITY 0x37 /* Polarity */
|
|
+#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */
|
|
+#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */
|
|
+#define ADV7183_ADC_CTRL 0x3A /* ADC control */
|
|
+#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */
|
|
+#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */
|
|
+#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */
|
|
+#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */
|
|
+#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */
|
|
+#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */
|
|
+#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */
|
|
+#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */
|
|
+#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */
|
|
+#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */
|
|
+#define ADV7183_LOCK_CNT 0x51 /* Lock count */
|
|
+#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */
|
|
+#define ADV7183_VBI_INFO 0x90 /* VBI info */
|
|
+#define ADV7183_WSS_1 0x91 /* WSS 1 */
|
|
+#define ADV7183_WSS_2 0x92 /* WSS 2 */
|
|
+#define ADV7183_EDTV_1 0x93 /* EDTV 1 */
|
|
+#define ADV7183_EDTV_2 0x94 /* EDTV 2 */
|
|
+#define ADV7183_EDTV_3 0x95 /* EDTV 3 */
|
|
+#define ADV7183_CGMS_1 0x96 /* CGMS 1 */
|
|
+#define ADV7183_CGMS_2 0x97 /* CGMS 2 */
|
|
+#define ADV7183_CGMS_3 0x98 /* CGMS 3 */
|
|
+#define ADV7183_CCAP_1 0x99 /* CCAP 1 */
|
|
+#define ADV7183_CCAP_2 0x9A /* CCAP 2 */
|
|
+#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */
|
|
+#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */
|
|
+#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */
|
|
+#define ADV7183_CRC_EN 0xB2 /* CRC enable */
|
|
+#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */
|
|
+#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */
|
|
+#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */
|
|
+#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */
|
|
+#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */
|
|
+#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */
|
|
+#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */
|
|
+#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */
|
|
+#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */
|
|
+#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */
|
|
+#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */
|
|
+#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */
|
|
+#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */
|
|
+#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */
|
|
+#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */
|
|
+#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */
|
|
+#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */
|
|
+
|
|
+#endif
|
|
Index: linux-3.3.x86_64/include/media/adv7183.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/include/media/adv7183.h
|
|
@@ -0,0 +1,47 @@
|
|
+/*
|
|
+ * adv7183.h - definition for adv7183 inputs and outputs
|
|
+ *
|
|
+ * Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#ifndef _ADV7183_H_
|
|
+#define _ADV7183_H_
|
|
+
|
|
+/* ADV7183 HW inputs */
|
|
+#define ADV7183_COMPOSITE0 0 /* CVBS in on AIN1 */
|
|
+#define ADV7183_COMPOSITE1 1 /* CVBS in on AIN2 */
|
|
+#define ADV7183_COMPOSITE2 2 /* CVBS in on AIN3 */
|
|
+#define ADV7183_COMPOSITE3 3 /* CVBS in on AIN4 */
|
|
+#define ADV7183_COMPOSITE4 4 /* CVBS in on AIN5 */
|
|
+#define ADV7183_COMPOSITE5 5 /* CVBS in on AIN6 */
|
|
+#define ADV7183_COMPOSITE6 6 /* CVBS in on AIN7 */
|
|
+#define ADV7183_COMPOSITE7 7 /* CVBS in on AIN8 */
|
|
+#define ADV7183_COMPOSITE8 8 /* CVBS in on AIN9 */
|
|
+#define ADV7183_COMPOSITE9 9 /* CVBS in on AIN10 */
|
|
+#define ADV7183_COMPOSITE10 10 /* CVBS in on AIN11 */
|
|
+
|
|
+#define ADV7183_SVIDEO0 11 /* Y on AIN1, C on AIN4 */
|
|
+#define ADV7183_SVIDEO1 12 /* Y on AIN2, C on AIN5 */
|
|
+#define ADV7183_SVIDEO2 13 /* Y on AIN3, C on AIN6 */
|
|
+
|
|
+#define ADV7183_COMPONENT0 14 /* Y on AIN1, Pr on AIN4, Pb on AIN5 */
|
|
+#define ADV7183_COMPONENT1 15 /* Y on AIN2, Pr on AIN3, Pb on AIN6 */
|
|
+
|
|
+/* ADV7183 HW outputs */
|
|
+#define ADV7183_8BIT_OUT 0
|
|
+#define ADV7183_16BIT_OUT 1
|
|
+
|
|
+#endif
|
|
Index: linux-3.3.x86_64/include/media/v4l2-chip-ident.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/media/v4l2-chip-ident.h
|
|
+++ linux-3.3.x86_64/include/media/v4l2-chip-ident.h
|
|
@@ -143,6 +143,9 @@ enum {
|
|
/* module saa6588: just ident 6588 */
|
|
V4L2_IDENT_SAA6588 = 6588,
|
|
|
|
+ /* module vs6624: just ident 6624 */
|
|
+ V4L2_IDENT_VS6624 = 6624,
|
|
+
|
|
/* module saa6752hs: reserved range 6750-6759 */
|
|
V4L2_IDENT_SAA6752HS = 6752,
|
|
V4L2_IDENT_SAA6752HS_AC3 = 6753,
|
|
@@ -162,6 +165,9 @@ enum {
|
|
/* module adv7180: just ident 7180 */
|
|
V4L2_IDENT_ADV7180 = 7180,
|
|
|
|
+ /* module adv7183: just ident 7183 */
|
|
+ V4L2_IDENT_ADV7183 = 7183,
|
|
+
|
|
/* module saa7185: just ident 7185 */
|
|
V4L2_IDENT_SAA7185 = 7185,
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/vs6624.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/vs6624.c
|
|
@@ -0,0 +1,928 @@
|
|
+/*
|
|
+ * vs6624.c ST VS6624 CMOS image sensor driver
|
|
+ *
|
|
+ * Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/delay.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/videodev2.h>
|
|
+
|
|
+#include <media/v4l2-chip-ident.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-device.h>
|
|
+#include <media/v4l2-mediabus.h>
|
|
+
|
|
+#include "vs6624_regs.h"
|
|
+
|
|
+#define VGA_WIDTH 640
|
|
+#define VGA_HEIGHT 480
|
|
+#define QVGA_WIDTH 320
|
|
+#define QVGA_HEIGHT 240
|
|
+#define QQVGA_WIDTH 160
|
|
+#define QQVGA_HEIGHT 120
|
|
+#define CIF_WIDTH 352
|
|
+#define CIF_HEIGHT 288
|
|
+#define QCIF_WIDTH 176
|
|
+#define QCIF_HEIGHT 144
|
|
+#define QQCIF_WIDTH 88
|
|
+#define QQCIF_HEIGHT 72
|
|
+
|
|
+#define MAX_FRAME_RATE 30
|
|
+
|
|
+struct vs6624 {
|
|
+ struct v4l2_subdev sd;
|
|
+ struct v4l2_ctrl_handler hdl;
|
|
+ struct v4l2_fract frame_rate;
|
|
+ struct v4l2_mbus_framefmt fmt;
|
|
+ unsigned ce_pin;
|
|
+};
|
|
+
|
|
+static const struct vs6624_format {
|
|
+ enum v4l2_mbus_pixelcode mbus_code;
|
|
+ enum v4l2_colorspace colorspace;
|
|
+} vs6624_formats[] = {
|
|
+ {
|
|
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
|
|
+ .colorspace = V4L2_COLORSPACE_JPEG,
|
|
+ },
|
|
+ {
|
|
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
|
|
+ .colorspace = V4L2_COLORSPACE_JPEG,
|
|
+ },
|
|
+ {
|
|
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
|
|
+ .colorspace = V4L2_COLORSPACE_SRGB,
|
|
+ },
|
|
+};
|
|
+
|
|
+static struct v4l2_mbus_framefmt vs6624_default_fmt = {
|
|
+ .width = VGA_WIDTH,
|
|
+ .height = VGA_HEIGHT,
|
|
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
|
|
+ .field = V4L2_FIELD_NONE,
|
|
+ .colorspace = V4L2_COLORSPACE_JPEG,
|
|
+};
|
|
+
|
|
+static const u16 vs6624_p1[] = {
|
|
+ 0x8104, 0x03,
|
|
+ 0x8105, 0x01,
|
|
+ 0xc900, 0x03,
|
|
+ 0xc904, 0x47,
|
|
+ 0xc905, 0x10,
|
|
+ 0xc906, 0x80,
|
|
+ 0xc907, 0x3a,
|
|
+ 0x903a, 0x02,
|
|
+ 0x903b, 0x47,
|
|
+ 0x903c, 0x15,
|
|
+ 0xc908, 0x31,
|
|
+ 0xc909, 0xdc,
|
|
+ 0xc90a, 0x80,
|
|
+ 0xc90b, 0x44,
|
|
+ 0x9044, 0x02,
|
|
+ 0x9045, 0x31,
|
|
+ 0x9046, 0xe2,
|
|
+ 0xc90c, 0x07,
|
|
+ 0xc90d, 0xe0,
|
|
+ 0xc90e, 0x80,
|
|
+ 0xc90f, 0x47,
|
|
+ 0x9047, 0x90,
|
|
+ 0x9048, 0x83,
|
|
+ 0x9049, 0x81,
|
|
+ 0x904a, 0xe0,
|
|
+ 0x904b, 0x60,
|
|
+ 0x904c, 0x08,
|
|
+ 0x904d, 0x90,
|
|
+ 0x904e, 0xc0,
|
|
+ 0x904f, 0x43,
|
|
+ 0x9050, 0x74,
|
|
+ 0x9051, 0x01,
|
|
+ 0x9052, 0xf0,
|
|
+ 0x9053, 0x80,
|
|
+ 0x9054, 0x05,
|
|
+ 0x9055, 0xE4,
|
|
+ 0x9056, 0x90,
|
|
+ 0x9057, 0xc0,
|
|
+ 0x9058, 0x43,
|
|
+ 0x9059, 0xf0,
|
|
+ 0x905a, 0x02,
|
|
+ 0x905b, 0x07,
|
|
+ 0x905c, 0xec,
|
|
+ 0xc910, 0x5d,
|
|
+ 0xc911, 0xca,
|
|
+ 0xc912, 0x80,
|
|
+ 0xc913, 0x5d,
|
|
+ 0x905d, 0xa3,
|
|
+ 0x905e, 0x04,
|
|
+ 0x905f, 0xf0,
|
|
+ 0x9060, 0xa3,
|
|
+ 0x9061, 0x04,
|
|
+ 0x9062, 0xf0,
|
|
+ 0x9063, 0x22,
|
|
+ 0xc914, 0x72,
|
|
+ 0xc915, 0x92,
|
|
+ 0xc916, 0x80,
|
|
+ 0xc917, 0x64,
|
|
+ 0x9064, 0x74,
|
|
+ 0x9065, 0x01,
|
|
+ 0x9066, 0x02,
|
|
+ 0x9067, 0x72,
|
|
+ 0x9068, 0x95,
|
|
+ 0xc918, 0x47,
|
|
+ 0xc919, 0xf2,
|
|
+ 0xc91a, 0x81,
|
|
+ 0xc91b, 0x69,
|
|
+ 0x9169, 0x74,
|
|
+ 0x916a, 0x02,
|
|
+ 0x916b, 0xf0,
|
|
+ 0x916c, 0xec,
|
|
+ 0x916d, 0xb4,
|
|
+ 0x916e, 0x10,
|
|
+ 0x916f, 0x0a,
|
|
+ 0x9170, 0x90,
|
|
+ 0x9171, 0x80,
|
|
+ 0x9172, 0x16,
|
|
+ 0x9173, 0xe0,
|
|
+ 0x9174, 0x70,
|
|
+ 0x9175, 0x04,
|
|
+ 0x9176, 0x90,
|
|
+ 0x9177, 0xd3,
|
|
+ 0x9178, 0xc4,
|
|
+ 0x9179, 0xf0,
|
|
+ 0x917a, 0x22,
|
|
+ 0xc91c, 0x0a,
|
|
+ 0xc91d, 0xbe,
|
|
+ 0xc91e, 0x80,
|
|
+ 0xc91f, 0x73,
|
|
+ 0x9073, 0xfc,
|
|
+ 0x9074, 0xa3,
|
|
+ 0x9075, 0xe0,
|
|
+ 0x9076, 0xf5,
|
|
+ 0x9077, 0x82,
|
|
+ 0x9078, 0x8c,
|
|
+ 0x9079, 0x83,
|
|
+ 0x907a, 0xa3,
|
|
+ 0x907b, 0xa3,
|
|
+ 0x907c, 0xe0,
|
|
+ 0x907d, 0xfc,
|
|
+ 0x907e, 0xa3,
|
|
+ 0x907f, 0xe0,
|
|
+ 0x9080, 0xc3,
|
|
+ 0x9081, 0x9f,
|
|
+ 0x9082, 0xff,
|
|
+ 0x9083, 0xec,
|
|
+ 0x9084, 0x9e,
|
|
+ 0x9085, 0xfe,
|
|
+ 0x9086, 0x02,
|
|
+ 0x9087, 0x0a,
|
|
+ 0x9088, 0xea,
|
|
+ 0xc920, 0x47,
|
|
+ 0xc921, 0x38,
|
|
+ 0xc922, 0x80,
|
|
+ 0xc923, 0x89,
|
|
+ 0x9089, 0xec,
|
|
+ 0x908a, 0xd3,
|
|
+ 0x908b, 0x94,
|
|
+ 0x908c, 0x20,
|
|
+ 0x908d, 0x40,
|
|
+ 0x908e, 0x01,
|
|
+ 0x908f, 0x1c,
|
|
+ 0x9090, 0x90,
|
|
+ 0x9091, 0xd3,
|
|
+ 0x9092, 0xd4,
|
|
+ 0x9093, 0xec,
|
|
+ 0x9094, 0xf0,
|
|
+ 0x9095, 0x02,
|
|
+ 0x9096, 0x47,
|
|
+ 0x9097, 0x3d,
|
|
+ 0xc924, 0x45,
|
|
+ 0xc925, 0xca,
|
|
+ 0xc926, 0x80,
|
|
+ 0xc927, 0x98,
|
|
+ 0x9098, 0x12,
|
|
+ 0x9099, 0x77,
|
|
+ 0x909a, 0xd6,
|
|
+ 0x909b, 0x02,
|
|
+ 0x909c, 0x45,
|
|
+ 0x909d, 0xcd,
|
|
+ 0xc928, 0x20,
|
|
+ 0xc929, 0xd5,
|
|
+ 0xc92a, 0x80,
|
|
+ 0xc92b, 0x9e,
|
|
+ 0x909e, 0x90,
|
|
+ 0x909f, 0x82,
|
|
+ 0x90a0, 0x18,
|
|
+ 0x90a1, 0xe0,
|
|
+ 0x90a2, 0xb4,
|
|
+ 0x90a3, 0x03,
|
|
+ 0x90a4, 0x0e,
|
|
+ 0x90a5, 0x90,
|
|
+ 0x90a6, 0x83,
|
|
+ 0x90a7, 0xbf,
|
|
+ 0x90a8, 0xe0,
|
|
+ 0x90a9, 0x60,
|
|
+ 0x90aa, 0x08,
|
|
+ 0x90ab, 0x90,
|
|
+ 0x90ac, 0x81,
|
|
+ 0x90ad, 0xfc,
|
|
+ 0x90ae, 0xe0,
|
|
+ 0x90af, 0xff,
|
|
+ 0x90b0, 0xc3,
|
|
+ 0x90b1, 0x13,
|
|
+ 0x90b2, 0xf0,
|
|
+ 0x90b3, 0x90,
|
|
+ 0x90b4, 0x81,
|
|
+ 0x90b5, 0xfc,
|
|
+ 0x90b6, 0xe0,
|
|
+ 0x90b7, 0xff,
|
|
+ 0x90b8, 0x02,
|
|
+ 0x90b9, 0x20,
|
|
+ 0x90ba, 0xda,
|
|
+ 0xc92c, 0x70,
|
|
+ 0xc92d, 0xbc,
|
|
+ 0xc92e, 0x80,
|
|
+ 0xc92f, 0xbb,
|
|
+ 0x90bb, 0x90,
|
|
+ 0x90bc, 0x82,
|
|
+ 0x90bd, 0x18,
|
|
+ 0x90be, 0xe0,
|
|
+ 0x90bf, 0xb4,
|
|
+ 0x90c0, 0x03,
|
|
+ 0x90c1, 0x06,
|
|
+ 0x90c2, 0x90,
|
|
+ 0x90c3, 0xc1,
|
|
+ 0x90c4, 0x06,
|
|
+ 0x90c5, 0x74,
|
|
+ 0x90c6, 0x05,
|
|
+ 0x90c7, 0xf0,
|
|
+ 0x90c8, 0x90,
|
|
+ 0x90c9, 0xd3,
|
|
+ 0x90ca, 0xa0,
|
|
+ 0x90cb, 0x02,
|
|
+ 0x90cc, 0x70,
|
|
+ 0x90cd, 0xbf,
|
|
+ 0xc930, 0x72,
|
|
+ 0xc931, 0x21,
|
|
+ 0xc932, 0x81,
|
|
+ 0xc933, 0x3b,
|
|
+ 0x913b, 0x7d,
|
|
+ 0x913c, 0x02,
|
|
+ 0x913d, 0x7f,
|
|
+ 0x913e, 0x7b,
|
|
+ 0x913f, 0x02,
|
|
+ 0x9140, 0x72,
|
|
+ 0x9141, 0x25,
|
|
+ 0xc934, 0x28,
|
|
+ 0xc935, 0xae,
|
|
+ 0xc936, 0x80,
|
|
+ 0xc937, 0xd2,
|
|
+ 0x90d2, 0xf0,
|
|
+ 0x90d3, 0x90,
|
|
+ 0x90d4, 0xd2,
|
|
+ 0x90d5, 0x0a,
|
|
+ 0x90d6, 0x02,
|
|
+ 0x90d7, 0x28,
|
|
+ 0x90d8, 0xb4,
|
|
+ 0xc938, 0x28,
|
|
+ 0xc939, 0xb1,
|
|
+ 0xc93a, 0x80,
|
|
+ 0xc93b, 0xd9,
|
|
+ 0x90d9, 0x90,
|
|
+ 0x90da, 0x83,
|
|
+ 0x90db, 0xba,
|
|
+ 0x90dc, 0xe0,
|
|
+ 0x90dd, 0xff,
|
|
+ 0x90de, 0x90,
|
|
+ 0x90df, 0xd2,
|
|
+ 0x90e0, 0x08,
|
|
+ 0x90e1, 0xe0,
|
|
+ 0x90e2, 0xe4,
|
|
+ 0x90e3, 0xef,
|
|
+ 0x90e4, 0xf0,
|
|
+ 0x90e5, 0xa3,
|
|
+ 0x90e6, 0xe0,
|
|
+ 0x90e7, 0x74,
|
|
+ 0x90e8, 0xff,
|
|
+ 0x90e9, 0xf0,
|
|
+ 0x90ea, 0x90,
|
|
+ 0x90eb, 0xd2,
|
|
+ 0x90ec, 0x0a,
|
|
+ 0x90ed, 0x02,
|
|
+ 0x90ee, 0x28,
|
|
+ 0x90ef, 0xb4,
|
|
+ 0xc93c, 0x29,
|
|
+ 0xc93d, 0x79,
|
|
+ 0xc93e, 0x80,
|
|
+ 0xc93f, 0xf0,
|
|
+ 0x90f0, 0xf0,
|
|
+ 0x90f1, 0x90,
|
|
+ 0x90f2, 0xd2,
|
|
+ 0x90f3, 0x0e,
|
|
+ 0x90f4, 0x02,
|
|
+ 0x90f5, 0x29,
|
|
+ 0x90f6, 0x7f,
|
|
+ 0xc940, 0x29,
|
|
+ 0xc941, 0x7c,
|
|
+ 0xc942, 0x80,
|
|
+ 0xc943, 0xf7,
|
|
+ 0x90f7, 0x90,
|
|
+ 0x90f8, 0x83,
|
|
+ 0x90f9, 0xba,
|
|
+ 0x90fa, 0xe0,
|
|
+ 0x90fb, 0xff,
|
|
+ 0x90fc, 0x90,
|
|
+ 0x90fd, 0xd2,
|
|
+ 0x90fe, 0x0c,
|
|
+ 0x90ff, 0xe0,
|
|
+ 0x9100, 0xe4,
|
|
+ 0x9101, 0xef,
|
|
+ 0x9102, 0xf0,
|
|
+ 0x9103, 0xa3,
|
|
+ 0x9104, 0xe0,
|
|
+ 0x9105, 0x74,
|
|
+ 0x9106, 0xff,
|
|
+ 0x9107, 0xf0,
|
|
+ 0x9108, 0x90,
|
|
+ 0x9109, 0xd2,
|
|
+ 0x910a, 0x0e,
|
|
+ 0x910b, 0x02,
|
|
+ 0x910c, 0x29,
|
|
+ 0x910d, 0x7f,
|
|
+ 0xc944, 0x2a,
|
|
+ 0xc945, 0x42,
|
|
+ 0xc946, 0x81,
|
|
+ 0xc947, 0x0e,
|
|
+ 0x910e, 0xf0,
|
|
+ 0x910f, 0x90,
|
|
+ 0x9110, 0xd2,
|
|
+ 0x9111, 0x12,
|
|
+ 0x9112, 0x02,
|
|
+ 0x9113, 0x2a,
|
|
+ 0x9114, 0x48,
|
|
+ 0xc948, 0x2a,
|
|
+ 0xc949, 0x45,
|
|
+ 0xc94a, 0x81,
|
|
+ 0xc94b, 0x15,
|
|
+ 0x9115, 0x90,
|
|
+ 0x9116, 0x83,
|
|
+ 0x9117, 0xba,
|
|
+ 0x9118, 0xe0,
|
|
+ 0x9119, 0xff,
|
|
+ 0x911a, 0x90,
|
|
+ 0x911b, 0xd2,
|
|
+ 0x911c, 0x10,
|
|
+ 0x911d, 0xe0,
|
|
+ 0x911e, 0xe4,
|
|
+ 0x911f, 0xef,
|
|
+ 0x9120, 0xf0,
|
|
+ 0x9121, 0xa3,
|
|
+ 0x9122, 0xe0,
|
|
+ 0x9123, 0x74,
|
|
+ 0x9124, 0xff,
|
|
+ 0x9125, 0xf0,
|
|
+ 0x9126, 0x90,
|
|
+ 0x9127, 0xd2,
|
|
+ 0x9128, 0x12,
|
|
+ 0x9129, 0x02,
|
|
+ 0x912a, 0x2a,
|
|
+ 0x912b, 0x48,
|
|
+ 0xc900, 0x01,
|
|
+ 0x0000, 0x00,
|
|
+};
|
|
+
|
|
+static const u16 vs6624_p2[] = {
|
|
+ 0x806f, 0x01,
|
|
+ 0x058c, 0x01,
|
|
+ 0x0000, 0x00,
|
|
+};
|
|
+
|
|
+static const u16 vs6624_run_setup[] = {
|
|
+ 0x1d18, 0x00, /* Enableconstrainedwhitebalance */
|
|
+ VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */
|
|
+ VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */
|
|
+ VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */
|
|
+ VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */
|
|
+ VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */
|
|
+ VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */
|
|
+ VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */
|
|
+ VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */
|
|
+ VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */
|
|
+ VS6624_NORA_USAGE, 0x04, /* Nora usage */
|
|
+ VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */
|
|
+ VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */
|
|
+ VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */
|
|
+ VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */
|
|
+ VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */
|
|
+ VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */
|
|
+ VS6624_F2B_DISABLE, 0x00, /* Disable */
|
|
+ 0x1d8a, 0x30, /* MAXWeightHigh */
|
|
+ 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */
|
|
+ 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */
|
|
+ 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */
|
|
+ 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */
|
|
+ 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */
|
|
+ 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */
|
|
+ 0x1e08, 0x06, /* MAXWeightLow */
|
|
+ 0x1e0a, 0x0a, /* MAXWeightHigh */
|
|
+ 0x1601, 0x3a, /* Red A MSB */
|
|
+ 0x1602, 0x14, /* Red A LSB */
|
|
+ 0x1605, 0x3b, /* Blue A MSB */
|
|
+ 0x1606, 0x85, /* BLue A LSB */
|
|
+ 0x1609, 0x3b, /* RED B MSB */
|
|
+ 0x160a, 0x85, /* RED B LSB */
|
|
+ 0x160d, 0x3a, /* Blue B MSB */
|
|
+ 0x160e, 0x14, /* Blue B LSB */
|
|
+ 0x1611, 0x30, /* Max Distance from Locus MSB */
|
|
+ 0x1612, 0x8f, /* Max Distance from Locus MSB */
|
|
+ 0x1614, 0x01, /* Enable constrainer */
|
|
+ 0x0000, 0x00,
|
|
+};
|
|
+
|
|
+static const u16 vs6624_default[] = {
|
|
+ VS6624_CONTRAST0, 0x84,
|
|
+ VS6624_SATURATION0, 0x75,
|
|
+ VS6624_GAMMA0, 0x11,
|
|
+ VS6624_CONTRAST1, 0x84,
|
|
+ VS6624_SATURATION1, 0x75,
|
|
+ VS6624_GAMMA1, 0x11,
|
|
+ VS6624_MAN_RG, 0x80,
|
|
+ VS6624_MAN_GG, 0x80,
|
|
+ VS6624_MAN_BG, 0x80,
|
|
+ VS6624_WB_MODE, 0x1,
|
|
+ VS6624_EXPO_COMPENSATION, 0xfe,
|
|
+ VS6624_EXPO_METER, 0x0,
|
|
+ VS6624_LIGHT_FREQ, 0x64,
|
|
+ VS6624_PEAK_GAIN, 0xe,
|
|
+ VS6624_PEAK_LOW_THR, 0x28,
|
|
+ VS6624_HMIRROR0, 0x0,
|
|
+ VS6624_VFLIP0, 0x0,
|
|
+ VS6624_ZOOM_HSTEP0_MSB, 0x0,
|
|
+ VS6624_ZOOM_HSTEP0_LSB, 0x1,
|
|
+ VS6624_ZOOM_VSTEP0_MSB, 0x0,
|
|
+ VS6624_ZOOM_VSTEP0_LSB, 0x1,
|
|
+ VS6624_PAN_HSTEP0_MSB, 0x0,
|
|
+ VS6624_PAN_HSTEP0_LSB, 0xf,
|
|
+ VS6624_PAN_VSTEP0_MSB, 0x0,
|
|
+ VS6624_PAN_VSTEP0_LSB, 0xf,
|
|
+ VS6624_SENSOR_MODE, 0x1,
|
|
+ VS6624_SYNC_CODE_SETUP, 0x21,
|
|
+ VS6624_DISABLE_FR_DAMPER, 0x0,
|
|
+ VS6624_FR_DEN, 0x1,
|
|
+ VS6624_FR_NUM_LSB, 0xf,
|
|
+ VS6624_INIT_PIPE_SETUP, 0x0,
|
|
+ VS6624_IMG_FMT0, 0x0,
|
|
+ VS6624_YUV_SETUP, 0x1,
|
|
+ VS6624_IMAGE_SIZE0, 0x2,
|
|
+ 0x0000, 0x00,
|
|
+};
|
|
+
|
|
+static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd)
|
|
+{
|
|
+ return container_of(sd, struct vs6624, sd);
|
|
+}
|
|
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ return &container_of(ctrl->handler, struct vs6624, hdl)->sd;
|
|
+}
|
|
+
|
|
+static int vs6624_read(struct v4l2_subdev *sd, u16 index)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+ u8 buf[2];
|
|
+
|
|
+ buf[0] = index >> 8;
|
|
+ buf[1] = index;
|
|
+ i2c_master_send(client, buf, 2);
|
|
+ i2c_master_recv(client, buf, 1);
|
|
+
|
|
+ return buf[0];
|
|
+}
|
|
+
|
|
+static int vs6624_write(struct v4l2_subdev *sd, u16 index,
|
|
+ u8 value)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+ u8 buf[3];
|
|
+
|
|
+ buf[0] = index >> 8;
|
|
+ buf[1] = index;
|
|
+ buf[2] = value;
|
|
+
|
|
+ return i2c_master_send(client, buf, 3);
|
|
+}
|
|
+
|
|
+static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs)
|
|
+{
|
|
+ u16 reg;
|
|
+ u8 data;
|
|
+
|
|
+ while (*regs != 0x00) {
|
|
+ reg = *regs++;
|
|
+ data = *regs++;
|
|
+
|
|
+ vs6624_write(sd, reg, data);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ struct v4l2_subdev *sd = to_sd(ctrl);
|
|
+
|
|
+ switch (ctrl->id) {
|
|
+ case V4L2_CID_CONTRAST:
|
|
+ vs6624_write(sd, VS6624_CONTRAST0, ctrl->val);
|
|
+ break;
|
|
+ case V4L2_CID_SATURATION:
|
|
+ vs6624_write(sd, VS6624_SATURATION0, ctrl->val);
|
|
+ break;
|
|
+ case V4L2_CID_HFLIP:
|
|
+ vs6624_write(sd, VS6624_HMIRROR0, ctrl->val);
|
|
+ break;
|
|
+ case V4L2_CID_VFLIP:
|
|
+ vs6624_write(sd, VS6624_VFLIP0, ctrl->val);
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
|
|
+ enum v4l2_mbus_pixelcode *code)
|
|
+{
|
|
+ if (index >= ARRAY_SIZE(vs6624_formats))
|
|
+ return -EINVAL;
|
|
+
|
|
+ *code = vs6624_formats[index].mbus_code;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd,
|
|
+ struct v4l2_mbus_framefmt *fmt)
|
|
+{
|
|
+ int index;
|
|
+
|
|
+ for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++)
|
|
+ if (vs6624_formats[index].mbus_code == fmt->code)
|
|
+ break;
|
|
+ if (index >= ARRAY_SIZE(vs6624_formats)) {
|
|
+ /* default to first format */
|
|
+ index = 0;
|
|
+ fmt->code = vs6624_formats[0].mbus_code;
|
|
+ }
|
|
+
|
|
+ /* sensor mode is VGA */
|
|
+ if (fmt->width > VGA_WIDTH)
|
|
+ fmt->width = VGA_WIDTH;
|
|
+ if (fmt->height > VGA_HEIGHT)
|
|
+ fmt->height = VGA_HEIGHT;
|
|
+ fmt->width = fmt->width & (~3);
|
|
+ fmt->height = fmt->height & (~3);
|
|
+ fmt->field = V4L2_FIELD_NONE;
|
|
+ fmt->colorspace = vs6624_formats[index].colorspace;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd,
|
|
+ struct v4l2_mbus_framefmt *fmt)
|
|
+{
|
|
+ struct vs6624 *sensor = to_vs6624(sd);
|
|
+ int ret;
|
|
+
|
|
+ ret = vs6624_try_mbus_fmt(sd, fmt);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* set image format */
|
|
+ switch (fmt->code) {
|
|
+ case V4L2_MBUS_FMT_UYVY8_2X8:
|
|
+ vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
|
|
+ vs6624_write(sd, VS6624_YUV_SETUP, 0x1);
|
|
+ break;
|
|
+ case V4L2_MBUS_FMT_YUYV8_2X8:
|
|
+ vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
|
|
+ vs6624_write(sd, VS6624_YUV_SETUP, 0x3);
|
|
+ break;
|
|
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
|
|
+ vs6624_write(sd, VS6624_IMG_FMT0, 0x4);
|
|
+ vs6624_write(sd, VS6624_RGB_SETUP, 0x0);
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* set image size */
|
|
+ if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT))
|
|
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2);
|
|
+ else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT))
|
|
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4);
|
|
+ else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT))
|
|
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6);
|
|
+ else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT))
|
|
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3);
|
|
+ else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT))
|
|
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5);
|
|
+ else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT))
|
|
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7);
|
|
+ else {
|
|
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8);
|
|
+ vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8);
|
|
+ vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF);
|
|
+ vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8);
|
|
+ vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF);
|
|
+ vs6624_write(sd, VS6624_CROP_CTRL0, 0x1);
|
|
+ }
|
|
+
|
|
+ sensor->fmt = *fmt;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd,
|
|
+ struct v4l2_mbus_framefmt *fmt)
|
|
+{
|
|
+ struct vs6624 *sensor = to_vs6624(sd);
|
|
+
|
|
+ *fmt = sensor->fmt;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
|
|
+{
|
|
+ struct vs6624 *sensor = to_vs6624(sd);
|
|
+ struct v4l2_captureparm *cp = &parms->parm.capture;
|
|
+
|
|
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ memset(cp, 0, sizeof(*cp));
|
|
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
|
|
+ cp->timeperframe.numerator = sensor->frame_rate.denominator;
|
|
+ cp->timeperframe.denominator = sensor->frame_rate.numerator;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
|
|
+{
|
|
+ struct vs6624 *sensor = to_vs6624(sd);
|
|
+ struct v4l2_captureparm *cp = &parms->parm.capture;
|
|
+ struct v4l2_fract *tpf = &cp->timeperframe;
|
|
+
|
|
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
+ return -EINVAL;
|
|
+ if (cp->extendedmode != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (tpf->numerator == 0 || tpf->denominator == 0
|
|
+ || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) {
|
|
+ /* reset to max frame rate */
|
|
+ tpf->numerator = 1;
|
|
+ tpf->denominator = MAX_FRAME_RATE;
|
|
+ }
|
|
+ sensor->frame_rate.numerator = tpf->denominator;
|
|
+ sensor->frame_rate.denominator = tpf->numerator;
|
|
+ vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
|
|
+ vs6624_write(sd, VS6624_FR_NUM_MSB,
|
|
+ sensor->frame_rate.numerator >> 8);
|
|
+ vs6624_write(sd, VS6624_FR_NUM_LSB,
|
|
+ sensor->frame_rate.numerator & 0xFF);
|
|
+ vs6624_write(sd, VS6624_FR_DEN,
|
|
+ sensor->frame_rate.denominator & 0xFF);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_s_stream(struct v4l2_subdev *sd, int enable)
|
|
+{
|
|
+ if (enable)
|
|
+ vs6624_write(sd, VS6624_USER_CMD, 0x2);
|
|
+ else
|
|
+ vs6624_write(sd, VS6624_USER_CMD, 0x4);
|
|
+ udelay(100);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_g_chip_ident(struct v4l2_subdev *sd,
|
|
+ struct v4l2_dbg_chip_ident *chip)
|
|
+{
|
|
+ int rev;
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+
|
|
+ rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8)
|
|
+ | vs6624_read(sd, VS6624_FW_VSN_MINOR);
|
|
+
|
|
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+
|
|
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
|
|
+ return -EINVAL;
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
+ return -EPERM;
|
|
+ reg->val = vs6624_read(sd, reg->reg & 0xffff);
|
|
+ reg->size = 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
+
|
|
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
|
|
+ return -EINVAL;
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
+ return -EPERM;
|
|
+ vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff);
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static const struct v4l2_ctrl_ops vs6624_ctrl_ops = {
|
|
+ .s_ctrl = vs6624_s_ctrl,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_core_ops vs6624_core_ops = {
|
|
+ .g_chip_ident = vs6624_g_chip_ident,
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+ .g_register = vs6624_g_register,
|
|
+ .s_register = vs6624_s_register,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_video_ops vs6624_video_ops = {
|
|
+ .enum_mbus_fmt = vs6624_enum_mbus_fmt,
|
|
+ .try_mbus_fmt = vs6624_try_mbus_fmt,
|
|
+ .s_mbus_fmt = vs6624_s_mbus_fmt,
|
|
+ .g_mbus_fmt = vs6624_g_mbus_fmt,
|
|
+ .s_parm = vs6624_s_parm,
|
|
+ .g_parm = vs6624_g_parm,
|
|
+ .s_stream = vs6624_s_stream,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_ops vs6624_ops = {
|
|
+ .core = &vs6624_core_ops,
|
|
+ .video = &vs6624_video_ops,
|
|
+};
|
|
+
|
|
+static int __devinit vs6624_probe(struct i2c_client *client,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ struct vs6624 *sensor;
|
|
+ struct v4l2_subdev *sd;
|
|
+ struct v4l2_ctrl_handler *hdl;
|
|
+ const unsigned *ce;
|
|
+ int ret;
|
|
+
|
|
+ /* Check if the adapter supports the needed features */
|
|
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
|
+ return -EIO;
|
|
+
|
|
+ ce = client->dev.platform_data;
|
|
+ if (ce == NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = gpio_request(*ce, "VS6624 Chip Enable");
|
|
+ if (ret) {
|
|
+ v4l_err(client, "failed to request GPIO %d\n", *ce);
|
|
+ return ret;
|
|
+ }
|
|
+ gpio_direction_output(*ce, 1);
|
|
+ /* wait 100ms before any further i2c writes are performed */
|
|
+ mdelay(100);
|
|
+
|
|
+ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
|
|
+ if (sensor == NULL) {
|
|
+ gpio_free(*ce);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ sd = &sensor->sd;
|
|
+ v4l2_i2c_subdev_init(sd, client, &vs6624_ops);
|
|
+
|
|
+ vs6624_writeregs(sd, vs6624_p1);
|
|
+ vs6624_write(sd, VS6624_MICRO_EN, 0x2);
|
|
+ vs6624_write(sd, VS6624_DIO_EN, 0x1);
|
|
+ mdelay(10);
|
|
+ vs6624_writeregs(sd, vs6624_p2);
|
|
+
|
|
+ vs6624_writeregs(sd, vs6624_default);
|
|
+ vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF);
|
|
+ vs6624_writeregs(sd, vs6624_run_setup);
|
|
+
|
|
+ /* set frame rate */
|
|
+ sensor->frame_rate.numerator = MAX_FRAME_RATE;
|
|
+ sensor->frame_rate.denominator = 1;
|
|
+ vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
|
|
+ vs6624_write(sd, VS6624_FR_NUM_MSB,
|
|
+ sensor->frame_rate.numerator >> 8);
|
|
+ vs6624_write(sd, VS6624_FR_NUM_LSB,
|
|
+ sensor->frame_rate.numerator & 0xFF);
|
|
+ vs6624_write(sd, VS6624_FR_DEN,
|
|
+ sensor->frame_rate.denominator & 0xFF);
|
|
+
|
|
+ sensor->fmt = vs6624_default_fmt;
|
|
+ sensor->ce_pin = *ce;
|
|
+
|
|
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
|
|
+ client->addr << 1, client->adapter->name);
|
|
+
|
|
+ hdl = &sensor->hdl;
|
|
+ v4l2_ctrl_handler_init(hdl, 4);
|
|
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
|
|
+ V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87);
|
|
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
|
|
+ V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78);
|
|
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
|
|
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
|
|
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
|
|
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
|
|
+ /* hook the control handler into the driver */
|
|
+ sd->ctrl_handler = hdl;
|
|
+ if (hdl->error) {
|
|
+ int err = hdl->error;
|
|
+
|
|
+ v4l2_ctrl_handler_free(hdl);
|
|
+ kfree(sensor);
|
|
+ gpio_free(*ce);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ /* initialize the hardware to the default control values */
|
|
+ ret = v4l2_ctrl_handler_setup(hdl);
|
|
+ if (ret) {
|
|
+ v4l2_ctrl_handler_free(hdl);
|
|
+ kfree(sensor);
|
|
+ gpio_free(*ce);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int __devexit vs6624_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
|
+ struct vs6624 *sensor = to_vs6624(sd);
|
|
+
|
|
+ v4l2_device_unregister_subdev(sd);
|
|
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
|
|
+ gpio_free(sensor->ce_pin);
|
|
+ kfree(sensor);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct i2c_device_id vs6624_id[] = {
|
|
+ {"vs6624", 0},
|
|
+ {},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(i2c, vs6624_id);
|
|
+
|
|
+static struct i2c_driver vs6624_driver = {
|
|
+ .driver = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .name = "vs6624",
|
|
+ },
|
|
+ .probe = vs6624_probe,
|
|
+ .remove = __devexit_p(vs6624_remove),
|
|
+ .id_table = vs6624_id,
|
|
+};
|
|
+
|
|
+static __init int vs6624_init(void)
|
|
+{
|
|
+ return i2c_add_driver(&vs6624_driver);
|
|
+}
|
|
+
|
|
+static __exit void vs6624_exit(void)
|
|
+{
|
|
+ i2c_del_driver(&vs6624_driver);
|
|
+}
|
|
+
|
|
+module_init(vs6624_init);
|
|
+module_exit(vs6624_exit);
|
|
+
|
|
+MODULE_DESCRIPTION("VS6624 sensor driver");
|
|
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
|
|
+MODULE_LICENSE("GPL v2");
|
|
Index: linux-3.3.x86_64/drivers/media/video/vs6624_regs.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/vs6624_regs.h
|
|
@@ -0,0 +1,337 @@
|
|
+/*
|
|
+ * vs6624 - ST VS6624 CMOS image sensor registers
|
|
+ *
|
|
+ * Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#ifndef _VS6624_REGS_H_
|
|
+#define _VS6624_REGS_H_
|
|
+
|
|
+/* low level control registers */
|
|
+#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */
|
|
+#define VS6624_DIO_EN 0xC044 /* enable digital I/O */
|
|
+/* device parameters */
|
|
+#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */
|
|
+#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */
|
|
+#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */
|
|
+#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */
|
|
+#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */
|
|
+#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */
|
|
+/* host interface manager control */
|
|
+#define VS6624_USER_CMD 0x0180 /* user level control of operating states */
|
|
+/* host interface manager status */
|
|
+#define VS6624_STATE 0x0202 /* current state of the mode manager */
|
|
+/* run mode control */
|
|
+#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */
|
|
+/* mode setup */
|
|
+#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */
|
|
+#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */
|
|
+/* pipe setup bank0 */
|
|
+#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */
|
|
+#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */
|
|
+#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */
|
|
+#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */
|
|
+#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */
|
|
+#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */
|
|
+#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */
|
|
+#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */
|
|
+#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */
|
|
+#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */
|
|
+#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */
|
|
+#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */
|
|
+#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */
|
|
+#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */
|
|
+#define VS6624_PAN_CTRL0 0x039C /* control pan operation */
|
|
+#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */
|
|
+#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */
|
|
+#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */
|
|
+#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */
|
|
+#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */
|
|
+#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */
|
|
+#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */
|
|
+#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */
|
|
+#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */
|
|
+#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */
|
|
+#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */
|
|
+#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */
|
|
+#define VS6624_SATURATION0 0x03B6 /* saturation control for output */
|
|
+#define VS6624_GAMMA0 0x03B8 /* gamma settings */
|
|
+#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */
|
|
+#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */
|
|
+#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */
|
|
+/* pipe setup bank1 */
|
|
+#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */
|
|
+#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */
|
|
+#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */
|
|
+#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */
|
|
+#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */
|
|
+#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */
|
|
+#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */
|
|
+#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */
|
|
+#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */
|
|
+#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */
|
|
+#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */
|
|
+#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */
|
|
+#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */
|
|
+#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */
|
|
+#define VS6624_PAN_CTRL1 0x041C /* control pan operation */
|
|
+#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */
|
|
+#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */
|
|
+#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */
|
|
+#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */
|
|
+#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */
|
|
+#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */
|
|
+#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */
|
|
+#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */
|
|
+#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */
|
|
+#define VS6624_IMG_FMT1 0x0430 /* select required output image format */
|
|
+#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */
|
|
+#define VS6624_CONTRAST1 0x0434 /* contrast control for output */
|
|
+#define VS6624_SATURATION1 0x0436 /* saturation control for output */
|
|
+#define VS6624_GAMMA1 0x0438 /* gamma settings */
|
|
+#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */
|
|
+#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */
|
|
+#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */
|
|
+/* view live control */
|
|
+#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */
|
|
+#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */
|
|
+/* view live status */
|
|
+#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */
|
|
+/* power management */
|
|
+#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */
|
|
+/* video timing parameter host inputs */
|
|
+#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */
|
|
+#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */
|
|
+#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */
|
|
+/* video timing control */
|
|
+#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */
|
|
+/* frame dimension parameter host inputs */
|
|
+#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */
|
|
+#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */
|
|
+/* static frame rate control */
|
|
+#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */
|
|
+#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */
|
|
+#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */
|
|
+/* automatic frame rate control */
|
|
+#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */
|
|
+#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */
|
|
+#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */
|
|
+/* exposure controls */
|
|
+#define VS6624_EXPO_MODE 0x1180 /* exposure mode */
|
|
+#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */
|
|
+#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */
|
|
+#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */
|
|
+#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */
|
|
+#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */
|
|
+#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */
|
|
+#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */
|
|
+#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */
|
|
+#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */
|
|
+#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */
|
|
+#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */
|
|
+#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */
|
|
+#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */
|
|
+#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */
|
|
+#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */
|
|
+#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */
|
|
+#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */
|
|
+#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */
|
|
+#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */
|
|
+#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */
|
|
+#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */
|
|
+#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */
|
|
+#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */
|
|
+#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */
|
|
+#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */
|
|
+#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */
|
|
+#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */
|
|
+#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */
|
|
+/* white balance control */
|
|
+#define VS6624_WB_MODE 0x1480 /* set white balance mode */
|
|
+#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */
|
|
+#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */
|
|
+#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */
|
|
+#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */
|
|
+#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */
|
|
+#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */
|
|
+#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */
|
|
+#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */
|
|
+#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */
|
|
+/* sensor setup */
|
|
+#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */
|
|
+/* image stability */
|
|
+#define VS6624_STABLE_WB 0x1900 /* white balance stable */
|
|
+#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */
|
|
+#define VS6624_STABLE 0x1906 /* system stable */
|
|
+/* flash control */
|
|
+#define VS6624_FLASH_MODE 0x1A80 /* flash mode */
|
|
+#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */
|
|
+#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */
|
|
+/* flash status */
|
|
+#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */
|
|
+#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */
|
|
+/* scythe filter controls */
|
|
+#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */
|
|
+/* jack filter controls */
|
|
+#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */
|
|
+/* demosaic control */
|
|
+#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */
|
|
+/* color matrix dampers */
|
|
+#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */
|
|
+#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */
|
|
+#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */
|
|
+#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */
|
|
+#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */
|
|
+#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */
|
|
+#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */
|
|
+/* peaking control */
|
|
+#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */
|
|
+#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */
|
|
+#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */
|
|
+#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */
|
|
+#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */
|
|
+#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */
|
|
+#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */
|
|
+#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */
|
|
+#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */
|
|
+#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */
|
|
+#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */
|
|
+#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */
|
|
+#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */
|
|
+#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */
|
|
+#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */
|
|
+#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */
|
|
+#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */
|
|
+/* pipe 0 RGB to YUV matrix manual control */
|
|
+#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */
|
|
+#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */
|
|
+#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */
|
|
+#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */
|
|
+#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */
|
|
+#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */
|
|
+#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */
|
|
+#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */
|
|
+#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */
|
|
+#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */
|
|
+#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */
|
|
+#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */
|
|
+#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */
|
|
+#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */
|
|
+#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */
|
|
+#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */
|
|
+#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */
|
|
+#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */
|
|
+#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */
|
|
+#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */
|
|
+#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */
|
|
+#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */
|
|
+#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */
|
|
+#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */
|
|
+#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */
|
|
+/* pipe 1 RGB to YUV matrix manual control */
|
|
+#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */
|
|
+#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */
|
|
+#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */
|
|
+#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */
|
|
+#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */
|
|
+#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */
|
|
+#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */
|
|
+#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */
|
|
+#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */
|
|
+#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */
|
|
+#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */
|
|
+#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */
|
|
+#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */
|
|
+#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */
|
|
+#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */
|
|
+#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */
|
|
+#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */
|
|
+#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */
|
|
+#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */
|
|
+#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */
|
|
+#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */
|
|
+#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */
|
|
+#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */
|
|
+#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */
|
|
+#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */
|
|
+/* pipe 0 gamma manual control */
|
|
+#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */
|
|
+#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */
|
|
+#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */
|
|
+#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */
|
|
+#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */
|
|
+#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */
|
|
+#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */
|
|
+/* pipe 1 gamma manual control */
|
|
+#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */
|
|
+#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */
|
|
+#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */
|
|
+#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */
|
|
+#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */
|
|
+#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */
|
|
+#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */
|
|
+/* fade to black */
|
|
+#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */
|
|
+#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */
|
|
+#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */
|
|
+#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */
|
|
+#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */
|
|
+#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */
|
|
+#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */
|
|
+#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */
|
|
+#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */
|
|
+/* output formatter control */
|
|
+#define VS6624_CODE_CK_EN 0x2580 /* code check enable */
|
|
+#define VS6624_BLANK_FMT 0x2582 /* blank format */
|
|
+#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */
|
|
+#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */
|
|
+#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */
|
|
+#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */
|
|
+#define VS6624_PCLK_EN 0x258C /* PCLK enable */
|
|
+#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */
|
|
+#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */
|
|
+#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */
|
|
+#define VS6624_RGB_SETUP 0x2594 /* RGB setup */
|
|
+#define VS6624_YUV_SETUP 0x2596 /* YUV setup */
|
|
+#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */
|
|
+#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */
|
|
+#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */
|
|
+#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */
|
|
+#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */
|
|
+#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */
|
|
+#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */
|
|
+#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */
|
|
+#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */
|
|
+#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */
|
|
+#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */
|
|
+#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */
|
|
+#define VS6624_OUT_IF 0x25B0 /* output interface */
|
|
+#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */
|
|
+/* NoRA controls */
|
|
+#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */
|
|
+#define VS6624_NORA_USAGE 0x2602 /* usage */
|
|
+#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */
|
|
+#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */
|
|
+#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */
|
|
+#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */
|
|
+#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */
|
|
+#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */
|
|
+#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */
|
|
+#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */
|
|
+#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */
|
|
+#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */
|
|
+
|
|
+#endif
|
|
Index: linux-3.3.x86_64/drivers/media/video/blackfin/Kconfig
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/blackfin/Kconfig
|
|
@@ -0,0 +1,10 @@
|
|
+config VIDEO_BLACKFIN_CAPTURE
|
|
+ tristate "Blackfin Video Capture Driver"
|
|
+ depends on VIDEO_V4L2 && BLACKFIN && I2C
|
|
+ select VIDEOBUF2_DMA_CONTIG
|
|
+ help
|
|
+ V4L2 bridge driver for Blackfin video capture device.
|
|
+ Choose PPI or EPPI as its interface.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called bfin_video_capture.
|
|
Index: linux-3.3.x86_64/drivers/media/video/blackfin/Makefile
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/blackfin/Makefile
|
|
@@ -0,0 +1,2 @@
|
|
+bfin_video_capture-objs := bfin_capture.o ppi.o
|
|
+obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o
|
|
Index: linux-3.3.x86_64/drivers/media/video/blackfin/bfin_capture.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/blackfin/bfin_capture.c
|
|
@@ -0,0 +1,1059 @@
|
|
+/*
|
|
+ * Analog Devices video capture driver
|
|
+ *
|
|
+ * Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/completion.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/time.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+#include <media/v4l2-chip-ident.h>
|
|
+#include <media/v4l2-common.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-device.h>
|
|
+#include <media/v4l2-ioctl.h>
|
|
+#include <media/videobuf2-dma-contig.h>
|
|
+
|
|
+#include <asm/dma.h>
|
|
+
|
|
+#include <media/blackfin/bfin_capture.h>
|
|
+#include <media/blackfin/ppi.h>
|
|
+
|
|
+#define CAPTURE_DRV_NAME "bfin_capture"
|
|
+#define BCAP_MIN_NUM_BUF 2
|
|
+
|
|
+struct bcap_format {
|
|
+ char *desc;
|
|
+ u32 pixelformat;
|
|
+ enum v4l2_mbus_pixelcode mbus_code;
|
|
+ int bpp; /* bits per pixel */
|
|
+};
|
|
+
|
|
+struct bcap_buffer {
|
|
+ struct vb2_buffer vb;
|
|
+ struct list_head list;
|
|
+};
|
|
+
|
|
+struct bcap_device {
|
|
+ /* capture device instance */
|
|
+ struct v4l2_device v4l2_dev;
|
|
+ /* v4l2 control handler */
|
|
+ struct v4l2_ctrl_handler ctrl_handler;
|
|
+ /* device node data */
|
|
+ struct video_device *video_dev;
|
|
+ /* sub device instance */
|
|
+ struct v4l2_subdev *sd;
|
|
+ /* capture config */
|
|
+ struct bfin_capture_config *cfg;
|
|
+ /* ppi interface */
|
|
+ struct ppi_if *ppi;
|
|
+ /* current input */
|
|
+ unsigned int cur_input;
|
|
+ /* current selected standard */
|
|
+ v4l2_std_id std;
|
|
+ /* used to store pixel format */
|
|
+ struct v4l2_pix_format fmt;
|
|
+ /* bits per pixel*/
|
|
+ int bpp;
|
|
+ /* used to store sensor supported format */
|
|
+ struct bcap_format *sensor_formats;
|
|
+ /* number of sensor formats array */
|
|
+ int num_sensor_formats;
|
|
+ /* pointing to current video buffer */
|
|
+ struct bcap_buffer *cur_frm;
|
|
+ /* pointing to next video buffer */
|
|
+ struct bcap_buffer *next_frm;
|
|
+ /* buffer queue used in videobuf2 */
|
|
+ struct vb2_queue buffer_queue;
|
|
+ /* allocator-specific contexts for each plane */
|
|
+ struct vb2_alloc_ctx *alloc_ctx;
|
|
+ /* queue of filled frames */
|
|
+ struct list_head dma_queue;
|
|
+ /* used in videobuf2 callback */
|
|
+ spinlock_t lock;
|
|
+ /* used to access capture device */
|
|
+ struct mutex mutex;
|
|
+ /* used to wait ppi to complete one transfer */
|
|
+ struct completion comp;
|
|
+ /* prepare to stop */
|
|
+ bool stop;
|
|
+};
|
|
+
|
|
+struct bcap_fh {
|
|
+ struct v4l2_fh fh;
|
|
+ /* indicates whether this file handle is doing IO */
|
|
+ bool io_allowed;
|
|
+};
|
|
+
|
|
+static const struct bcap_format bcap_formats[] = {
|
|
+ {
|
|
+ .desc = "YCbCr 4:2:2 Interleaved UYVY",
|
|
+ .pixelformat = V4L2_PIX_FMT_UYVY,
|
|
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
|
|
+ .bpp = 16,
|
|
+ },
|
|
+ {
|
|
+ .desc = "YCbCr 4:2:2 Interleaved YUYV",
|
|
+ .pixelformat = V4L2_PIX_FMT_YUYV,
|
|
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
|
|
+ .bpp = 16,
|
|
+ },
|
|
+ {
|
|
+ .desc = "RGB 565",
|
|
+ .pixelformat = V4L2_PIX_FMT_RGB565,
|
|
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
|
|
+ .bpp = 16,
|
|
+ },
|
|
+ {
|
|
+ .desc = "RGB 444",
|
|
+ .pixelformat = V4L2_PIX_FMT_RGB444,
|
|
+ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
|
|
+ .bpp = 16,
|
|
+ },
|
|
+
|
|
+};
|
|
+#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
|
|
+
|
|
+static irqreturn_t bcap_isr(int irq, void *dev_id);
|
|
+
|
|
+static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb)
|
|
+{
|
|
+ return container_of(vb, struct bcap_buffer, vb);
|
|
+}
|
|
+
|
|
+static int bcap_init_sensor_formats(struct bcap_device *bcap_dev)
|
|
+{
|
|
+ enum v4l2_mbus_pixelcode code;
|
|
+ struct bcap_format *sf;
|
|
+ unsigned int num_formats = 0;
|
|
+ int i, j;
|
|
+
|
|
+ while (!v4l2_subdev_call(bcap_dev->sd, video,
|
|
+ enum_mbus_fmt, num_formats, &code))
|
|
+ num_formats++;
|
|
+ if (!num_formats)
|
|
+ return -ENXIO;
|
|
+
|
|
+ sf = kzalloc(num_formats * sizeof(*sf), GFP_KERNEL);
|
|
+ if (!sf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for (i = 0; i < num_formats; i++) {
|
|
+ v4l2_subdev_call(bcap_dev->sd, video,
|
|
+ enum_mbus_fmt, i, &code);
|
|
+ for (j = 0; j < BCAP_MAX_FMTS; j++)
|
|
+ if (code == bcap_formats[j].mbus_code)
|
|
+ break;
|
|
+ if (j == BCAP_MAX_FMTS) {
|
|
+ /* we don't allow this sensor working with our bridge */
|
|
+ kfree(sf);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ sf[i] = bcap_formats[j];
|
|
+ }
|
|
+ bcap_dev->sensor_formats = sf;
|
|
+ bcap_dev->num_sensor_formats = num_formats;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void bcap_free_sensor_formats(struct bcap_device *bcap_dev)
|
|
+{
|
|
+ bcap_dev->num_sensor_formats = 0;
|
|
+ kfree(bcap_dev->sensor_formats);
|
|
+ bcap_dev->sensor_formats = NULL;
|
|
+}
|
|
+
|
|
+static int bcap_open(struct file *file)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct video_device *vfd = bcap_dev->video_dev;
|
|
+ struct bcap_fh *bcap_fh;
|
|
+
|
|
+ if (!bcap_dev->sd) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL);
|
|
+ if (!bcap_fh) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "unable to allocate memory for file handle object\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ v4l2_fh_init(&bcap_fh->fh, vfd);
|
|
+
|
|
+ /* store pointer to v4l2_fh in private_data member of file */
|
|
+ file->private_data = &bcap_fh->fh;
|
|
+ v4l2_fh_add(&bcap_fh->fh);
|
|
+ bcap_fh->io_allowed = false;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_release(struct file *file)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct v4l2_fh *fh = file->private_data;
|
|
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
|
|
+
|
|
+ /* if this instance is doing IO */
|
|
+ if (bcap_fh->io_allowed)
|
|
+ vb2_queue_release(&bcap_dev->buffer_queue);
|
|
+
|
|
+ file->private_data = NULL;
|
|
+ v4l2_fh_del(&bcap_fh->fh);
|
|
+ v4l2_fh_exit(&bcap_fh->fh);
|
|
+ kfree(bcap_fh);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_mmap(struct file *file, struct vm_area_struct *vma)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ return vb2_mmap(&bcap_dev->buffer_queue, vma);
|
|
+}
|
|
+
|
|
+#ifndef CONFIG_MMU
|
|
+static unsigned long bcap_get_unmapped_area(struct file *file,
|
|
+ unsigned long addr,
|
|
+ unsigned long len,
|
|
+ unsigned long pgoff,
|
|
+ unsigned long flags)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ return vb2_get_unmapped_area(&bcap_dev->buffer_queue,
|
|
+ addr,
|
|
+ len,
|
|
+ pgoff,
|
|
+ flags);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static unsigned int bcap_poll(struct file *file, poll_table *wait)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ return vb2_poll(&bcap_dev->buffer_queue, file, wait);
|
|
+}
|
|
+
|
|
+static int bcap_queue_setup(struct vb2_queue *vq,
|
|
+ const struct v4l2_format *fmt,
|
|
+ unsigned int *nbuffers, unsigned int *nplanes,
|
|
+ unsigned int sizes[], void *alloc_ctxs[])
|
|
+{
|
|
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
|
|
+
|
|
+ if (*nbuffers < BCAP_MIN_NUM_BUF)
|
|
+ *nbuffers = BCAP_MIN_NUM_BUF;
|
|
+
|
|
+ *nplanes = 1;
|
|
+ sizes[0] = bcap_dev->fmt.sizeimage;
|
|
+ alloc_ctxs[0] = bcap_dev->alloc_ctx;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_buffer_init(struct vb2_buffer *vb)
|
|
+{
|
|
+ struct bcap_buffer *buf = to_bcap_vb(vb);
|
|
+
|
|
+ INIT_LIST_HEAD(&buf->list);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_buffer_prepare(struct vb2_buffer *vb)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
|
|
+ struct bcap_buffer *buf = to_bcap_vb(vb);
|
|
+ unsigned long size;
|
|
+
|
|
+ size = bcap_dev->fmt.sizeimage;
|
|
+ if (vb2_plane_size(vb, 0) < size) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
|
|
+ vb2_plane_size(vb, 0), size);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ vb2_set_plane_payload(&buf->vb, 0, size);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void bcap_buffer_queue(struct vb2_buffer *vb)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
|
|
+ struct bcap_buffer *buf = to_bcap_vb(vb);
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&bcap_dev->lock, flags);
|
|
+ list_add_tail(&buf->list, &bcap_dev->dma_queue);
|
|
+ spin_unlock_irqrestore(&bcap_dev->lock, flags);
|
|
+}
|
|
+
|
|
+static void bcap_buffer_cleanup(struct vb2_buffer *vb)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
|
|
+ struct bcap_buffer *buf = to_bcap_vb(vb);
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&bcap_dev->lock, flags);
|
|
+ list_del_init(&buf->list);
|
|
+ spin_unlock_irqrestore(&bcap_dev->lock, flags);
|
|
+}
|
|
+
|
|
+static void bcap_lock(struct vb2_queue *vq)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
|
|
+ mutex_lock(&bcap_dev->mutex);
|
|
+}
|
|
+
|
|
+static void bcap_unlock(struct vb2_queue *vq)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
|
|
+ mutex_unlock(&bcap_dev->mutex);
|
|
+}
|
|
+
|
|
+static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
|
|
+ struct ppi_if *ppi = bcap_dev->ppi;
|
|
+ struct ppi_params params;
|
|
+ int ret;
|
|
+
|
|
+ /* enable streamon on the sub device */
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
|
|
+ if (ret && (ret != -ENOIOCTLCMD)) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* set ppi params */
|
|
+ params.width = bcap_dev->fmt.width;
|
|
+ params.height = bcap_dev->fmt.height;
|
|
+ params.bpp = bcap_dev->bpp;
|
|
+ params.ppi_control = bcap_dev->cfg->ppi_control;
|
|
+ params.int_mask = bcap_dev->cfg->int_mask;
|
|
+ params.blank_clocks = bcap_dev->cfg->blank_clocks;
|
|
+ ret = ppi->ops->set_params(ppi, ¶ms);
|
|
+ if (ret < 0) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "Error in setting ppi params\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* attach ppi DMA irq handler */
|
|
+ ret = ppi->ops->attach_irq(ppi, bcap_isr);
|
|
+ if (ret < 0) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "Error in attaching interrupt handler\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ INIT_COMPLETION(bcap_dev->comp);
|
|
+ bcap_dev->stop = false;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_stop_streaming(struct vb2_queue *vq)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
|
|
+ struct ppi_if *ppi = bcap_dev->ppi;
|
|
+ int ret;
|
|
+
|
|
+ if (!vb2_is_streaming(vq))
|
|
+ return 0;
|
|
+
|
|
+ bcap_dev->stop = true;
|
|
+ wait_for_completion(&bcap_dev->comp);
|
|
+ ppi->ops->stop(ppi);
|
|
+ ppi->ops->detach_irq(ppi);
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0);
|
|
+ if (ret && (ret != -ENOIOCTLCMD))
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "stream off failed in subdev\n");
|
|
+
|
|
+ /* release all active buffers */
|
|
+ while (!list_empty(&bcap_dev->dma_queue)) {
|
|
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
|
|
+ struct bcap_buffer, list);
|
|
+ list_del(&bcap_dev->next_frm->list);
|
|
+ vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct vb2_ops bcap_video_qops = {
|
|
+ .queue_setup = bcap_queue_setup,
|
|
+ .buf_init = bcap_buffer_init,
|
|
+ .buf_prepare = bcap_buffer_prepare,
|
|
+ .buf_cleanup = bcap_buffer_cleanup,
|
|
+ .buf_queue = bcap_buffer_queue,
|
|
+ .wait_prepare = bcap_unlock,
|
|
+ .wait_finish = bcap_lock,
|
|
+ .start_streaming = bcap_start_streaming,
|
|
+ .stop_streaming = bcap_stop_streaming,
|
|
+};
|
|
+
|
|
+static int bcap_reqbufs(struct file *file, void *priv,
|
|
+ struct v4l2_requestbuffers *req_buf)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct vb2_queue *vq = &bcap_dev->buffer_queue;
|
|
+ struct v4l2_fh *fh = file->private_data;
|
|
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
|
|
+
|
|
+ if (vb2_is_busy(vq))
|
|
+ return -EBUSY;
|
|
+
|
|
+ bcap_fh->io_allowed = true;
|
|
+
|
|
+ return vb2_reqbufs(vq, req_buf);
|
|
+}
|
|
+
|
|
+static int bcap_querybuf(struct file *file, void *priv,
|
|
+ struct v4l2_buffer *buf)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ return vb2_querybuf(&bcap_dev->buffer_queue, buf);
|
|
+}
|
|
+
|
|
+static int bcap_qbuf(struct file *file, void *priv,
|
|
+ struct v4l2_buffer *buf)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct v4l2_fh *fh = file->private_data;
|
|
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
|
|
+
|
|
+ if (!bcap_fh->io_allowed)
|
|
+ return -EBUSY;
|
|
+
|
|
+ return vb2_qbuf(&bcap_dev->buffer_queue, buf);
|
|
+}
|
|
+
|
|
+static int bcap_dqbuf(struct file *file, void *priv,
|
|
+ struct v4l2_buffer *buf)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct v4l2_fh *fh = file->private_data;
|
|
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
|
|
+
|
|
+ if (!bcap_fh->io_allowed)
|
|
+ return -EBUSY;
|
|
+
|
|
+ return vb2_dqbuf(&bcap_dev->buffer_queue,
|
|
+ buf, file->f_flags & O_NONBLOCK);
|
|
+}
|
|
+
|
|
+static irqreturn_t bcap_isr(int irq, void *dev_id)
|
|
+{
|
|
+ struct ppi_if *ppi = dev_id;
|
|
+ struct bcap_device *bcap_dev = ppi->priv;
|
|
+ struct timeval timevalue;
|
|
+ struct vb2_buffer *vb = &bcap_dev->cur_frm->vb;
|
|
+ dma_addr_t addr;
|
|
+
|
|
+ spin_lock(&bcap_dev->lock);
|
|
+
|
|
+ if (bcap_dev->cur_frm != bcap_dev->next_frm) {
|
|
+ do_gettimeofday(&timevalue);
|
|
+ vb->v4l2_buf.timestamp = timevalue;
|
|
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
|
|
+ bcap_dev->cur_frm = bcap_dev->next_frm;
|
|
+ }
|
|
+
|
|
+ ppi->ops->stop(ppi);
|
|
+
|
|
+ if (bcap_dev->stop) {
|
|
+ complete(&bcap_dev->comp);
|
|
+ } else {
|
|
+ if (!list_empty(&bcap_dev->dma_queue)) {
|
|
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
|
|
+ struct bcap_buffer, list);
|
|
+ list_del(&bcap_dev->next_frm->list);
|
|
+ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0);
|
|
+ ppi->ops->update_addr(ppi, (unsigned long)addr);
|
|
+ }
|
|
+ ppi->ops->start(ppi);
|
|
+ }
|
|
+
|
|
+ spin_unlock(&bcap_dev->lock);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int bcap_streamon(struct file *file, void *priv,
|
|
+ enum v4l2_buf_type buf_type)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct bcap_fh *fh = file->private_data;
|
|
+ struct ppi_if *ppi = bcap_dev->ppi;
|
|
+ dma_addr_t addr;
|
|
+ int ret;
|
|
+
|
|
+ if (!fh->io_allowed)
|
|
+ return -EBUSY;
|
|
+
|
|
+ /* call streamon to start streaming in videobuf */
|
|
+ ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* if dma queue is empty, return error */
|
|
+ if (list_empty(&bcap_dev->dma_queue)) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n");
|
|
+ ret = -EINVAL;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ /* get the next frame from the dma queue */
|
|
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
|
|
+ struct bcap_buffer, list);
|
|
+ bcap_dev->cur_frm = bcap_dev->next_frm;
|
|
+ /* remove buffer from the dma queue */
|
|
+ list_del(&bcap_dev->cur_frm->list);
|
|
+ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0);
|
|
+ /* update DMA address */
|
|
+ ppi->ops->update_addr(ppi, (unsigned long)addr);
|
|
+ /* enable ppi */
|
|
+ ppi->ops->start(ppi);
|
|
+
|
|
+ return 0;
|
|
+err:
|
|
+ vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int bcap_streamoff(struct file *file, void *priv,
|
|
+ enum v4l2_buf_type buf_type)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct bcap_fh *fh = file->private_data;
|
|
+
|
|
+ if (!fh->io_allowed)
|
|
+ return -EBUSY;
|
|
+
|
|
+ return vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
|
|
+}
|
|
+
|
|
+static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ return v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
|
|
+}
|
|
+
|
|
+static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ *std = bcap_dev->std;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ int ret;
|
|
+
|
|
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
|
|
+ return -EBUSY;
|
|
+
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ bcap_dev->std = *std;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_enum_input(struct file *file, void *priv,
|
|
+ struct v4l2_input *input)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct bfin_capture_config *config = bcap_dev->cfg;
|
|
+ int ret;
|
|
+ u32 status;
|
|
+
|
|
+ if (input->index >= config->num_inputs)
|
|
+ return -EINVAL;
|
|
+
|
|
+ *input = config->inputs[input->index];
|
|
+ /* get input status */
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status);
|
|
+ if (!ret)
|
|
+ input->status = status;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_g_input(struct file *file, void *priv, unsigned int *index)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ *index = bcap_dev->cur_input;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_s_input(struct file *file, void *priv, unsigned int index)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct bfin_capture_config *config = bcap_dev->cfg;
|
|
+ struct bcap_route *route;
|
|
+ int ret;
|
|
+
|
|
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
|
|
+ return -EBUSY;
|
|
+
|
|
+ if (index >= config->num_inputs)
|
|
+ return -EINVAL;
|
|
+
|
|
+ route = &config->routes[index];
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
|
|
+ route->input, route->output, 0);
|
|
+ if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
|
|
+ return ret;
|
|
+ }
|
|
+ bcap_dev->cur_input = index;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_try_format(struct bcap_device *bcap,
|
|
+ struct v4l2_pix_format *pixfmt,
|
|
+ enum v4l2_mbus_pixelcode *mbus_code,
|
|
+ int *bpp)
|
|
+{
|
|
+ struct bcap_format *sf = bcap->sensor_formats;
|
|
+ struct bcap_format *fmt = NULL;
|
|
+ struct v4l2_mbus_framefmt mbus_fmt;
|
|
+ int ret, i;
|
|
+
|
|
+ for (i = 0; i < bcap->num_sensor_formats; i++) {
|
|
+ fmt = &sf[i];
|
|
+ if (pixfmt->pixelformat == fmt->pixelformat)
|
|
+ break;
|
|
+ }
|
|
+ if (i == bcap->num_sensor_formats)
|
|
+ fmt = &sf[0];
|
|
+
|
|
+ if (mbus_code)
|
|
+ *mbus_code = fmt->mbus_code;
|
|
+ if (bpp)
|
|
+ *bpp = fmt->bpp;
|
|
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
|
|
+ ret = v4l2_subdev_call(bcap->sd, video,
|
|
+ try_mbus_fmt, &mbus_fmt);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ v4l2_fill_pix_format(pixfmt, &mbus_fmt);
|
|
+ pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
|
|
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_fmtdesc *fmt)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct bcap_format *sf = bcap_dev->sensor_formats;
|
|
+
|
|
+ if (fmt->index >= bcap_dev->num_sensor_formats)
|
|
+ return -EINVAL;
|
|
+
|
|
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
+ strlcpy(fmt->description,
|
|
+ sf[fmt->index].desc,
|
|
+ sizeof(fmt->description));
|
|
+ fmt->pixelformat = sf[fmt->index].pixelformat;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *fmt)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
|
|
+
|
|
+ return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
|
|
+}
|
|
+
|
|
+static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *fmt)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ fmt->fmt.pix = bcap_dev->fmt;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *fmt)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ struct v4l2_mbus_framefmt mbus_fmt;
|
|
+ enum v4l2_mbus_pixelcode mbus_code;
|
|
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
|
|
+ int ret, bpp;
|
|
+
|
|
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
|
|
+ return -EBUSY;
|
|
+
|
|
+ /* see if format works */
|
|
+ ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ bcap_dev->fmt = *pixfmt;
|
|
+ bcap_dev->bpp = bpp;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_querycap(struct file *file, void *priv,
|
|
+ struct v4l2_capability *cap)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
|
|
+ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
|
|
+ strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
|
|
+ strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bcap_g_parm(struct file *file, void *fh,
|
|
+ struct v4l2_streamparm *a)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
+ return -EINVAL;
|
|
+ return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a);
|
|
+}
|
|
+
|
|
+static int bcap_s_parm(struct file *file, void *fh,
|
|
+ struct v4l2_streamparm *a)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
+ return -EINVAL;
|
|
+ return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
|
|
+}
|
|
+
|
|
+static int bcap_g_chip_ident(struct file *file, void *priv,
|
|
+ struct v4l2_dbg_chip_ident *chip)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ chip->ident = V4L2_IDENT_NONE;
|
|
+ chip->revision = 0;
|
|
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
|
|
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return v4l2_subdev_call(bcap_dev->sd, core,
|
|
+ g_chip_ident, chip);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+static int bcap_dbg_g_register(struct file *file, void *priv,
|
|
+ struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ return v4l2_subdev_call(bcap_dev->sd, core,
|
|
+ g_register, reg);
|
|
+}
|
|
+
|
|
+static int bcap_dbg_s_register(struct file *file, void *priv,
|
|
+ struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+
|
|
+ return v4l2_subdev_call(bcap_dev->sd, core,
|
|
+ s_register, reg);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int bcap_log_status(struct file *file, void *priv)
|
|
+{
|
|
+ struct bcap_device *bcap_dev = video_drvdata(file);
|
|
+ /* status for sub devices */
|
|
+ v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
|
|
+ .vidioc_querycap = bcap_querycap,
|
|
+ .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap,
|
|
+ .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap,
|
|
+ .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap,
|
|
+ .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap,
|
|
+ .vidioc_enum_input = bcap_enum_input,
|
|
+ .vidioc_g_input = bcap_g_input,
|
|
+ .vidioc_s_input = bcap_s_input,
|
|
+ .vidioc_querystd = bcap_querystd,
|
|
+ .vidioc_s_std = bcap_s_std,
|
|
+ .vidioc_g_std = bcap_g_std,
|
|
+ .vidioc_reqbufs = bcap_reqbufs,
|
|
+ .vidioc_querybuf = bcap_querybuf,
|
|
+ .vidioc_qbuf = bcap_qbuf,
|
|
+ .vidioc_dqbuf = bcap_dqbuf,
|
|
+ .vidioc_streamon = bcap_streamon,
|
|
+ .vidioc_streamoff = bcap_streamoff,
|
|
+ .vidioc_g_parm = bcap_g_parm,
|
|
+ .vidioc_s_parm = bcap_s_parm,
|
|
+ .vidioc_g_chip_ident = bcap_g_chip_ident,
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+ .vidioc_g_register = bcap_dbg_g_register,
|
|
+ .vidioc_s_register = bcap_dbg_s_register,
|
|
+#endif
|
|
+ .vidioc_log_status = bcap_log_status,
|
|
+};
|
|
+
|
|
+static struct v4l2_file_operations bcap_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = bcap_open,
|
|
+ .release = bcap_release,
|
|
+ .unlocked_ioctl = video_ioctl2,
|
|
+ .mmap = bcap_mmap,
|
|
+#ifndef CONFIG_MMU
|
|
+ .get_unmapped_area = bcap_get_unmapped_area,
|
|
+#endif
|
|
+ .poll = bcap_poll
|
|
+};
|
|
+
|
|
+static int __devinit bcap_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct bcap_device *bcap_dev;
|
|
+ struct video_device *vfd;
|
|
+ struct i2c_adapter *i2c_adap;
|
|
+ struct bfin_capture_config *config;
|
|
+ struct vb2_queue *q;
|
|
+ int ret;
|
|
+
|
|
+ config = pdev->dev.platform_data;
|
|
+ if (!config) {
|
|
+ v4l2_err(pdev->dev.driver, "Unable to get board config\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL);
|
|
+ if (!bcap_dev) {
|
|
+ v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ bcap_dev->cfg = config;
|
|
+
|
|
+ bcap_dev->ppi = ppi_create_instance(config->ppi_info);
|
|
+ if (!bcap_dev->ppi) {
|
|
+ v4l2_err(pdev->dev.driver, "Unable to create ppi\n");
|
|
+ ret = -ENODEV;
|
|
+ goto err_free_dev;
|
|
+ }
|
|
+ bcap_dev->ppi->priv = bcap_dev;
|
|
+
|
|
+ bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
|
|
+ if (IS_ERR(bcap_dev->alloc_ctx)) {
|
|
+ ret = PTR_ERR(bcap_dev->alloc_ctx);
|
|
+ goto err_free_ppi;
|
|
+ }
|
|
+
|
|
+ vfd = video_device_alloc();
|
|
+ if (!vfd) {
|
|
+ ret = -ENOMEM;
|
|
+ v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
|
|
+ goto err_cleanup_ctx;
|
|
+ }
|
|
+
|
|
+ /* initialize field of video device */
|
|
+ vfd->release = video_device_release;
|
|
+ vfd->fops = &bcap_fops;
|
|
+ vfd->ioctl_ops = &bcap_ioctl_ops;
|
|
+ vfd->tvnorms = 0;
|
|
+ vfd->v4l2_dev = &bcap_dev->v4l2_dev;
|
|
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
|
|
+ strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
|
|
+ bcap_dev->video_dev = vfd;
|
|
+
|
|
+ ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
|
|
+ if (ret) {
|
|
+ v4l2_err(pdev->dev.driver,
|
|
+ "Unable to register v4l2 device\n");
|
|
+ goto err_release_vdev;
|
|
+ }
|
|
+ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
|
|
+
|
|
+ bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler;
|
|
+ ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0);
|
|
+ if (ret) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "Unable to init control handler\n");
|
|
+ goto err_unreg_v4l2;
|
|
+ }
|
|
+
|
|
+ spin_lock_init(&bcap_dev->lock);
|
|
+ /* initialize queue */
|
|
+ q = &bcap_dev->buffer_queue;
|
|
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
+ q->io_modes = VB2_MMAP;
|
|
+ q->drv_priv = bcap_dev;
|
|
+ q->buf_struct_size = sizeof(struct bcap_buffer);
|
|
+ q->ops = &bcap_video_qops;
|
|
+ q->mem_ops = &vb2_dma_contig_memops;
|
|
+
|
|
+ vb2_queue_init(q);
|
|
+
|
|
+ mutex_init(&bcap_dev->mutex);
|
|
+ init_completion(&bcap_dev->comp);
|
|
+
|
|
+ /* init video dma queues */
|
|
+ INIT_LIST_HEAD(&bcap_dev->dma_queue);
|
|
+
|
|
+ vfd->lock = &bcap_dev->mutex;
|
|
+
|
|
+ /* register video device */
|
|
+ ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
|
|
+ if (ret) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "Unable to register video device\n");
|
|
+ goto err_free_handler;
|
|
+ }
|
|
+ video_set_drvdata(bcap_dev->video_dev, bcap_dev);
|
|
+ v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
|
|
+ video_device_node_name(vfd));
|
|
+
|
|
+ /* load up the subdevice */
|
|
+ i2c_adap = i2c_get_adapter(config->i2c_adapter_id);
|
|
+ if (!i2c_adap) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "Unable to find i2c adapter\n");
|
|
+ goto err_unreg_vdev;
|
|
+
|
|
+ }
|
|
+ bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev,
|
|
+ i2c_adap,
|
|
+ &config->board_info,
|
|
+ NULL);
|
|
+ if (bcap_dev->sd) {
|
|
+ int i;
|
|
+ /* update tvnorms from the sub devices */
|
|
+ for (i = 0; i < config->num_inputs; i++)
|
|
+ vfd->tvnorms |= config->inputs[i].std;
|
|
+ } else {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "Unable to register sub device\n");
|
|
+ goto err_unreg_vdev;
|
|
+ }
|
|
+
|
|
+ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
|
|
+
|
|
+ /* now we can probe the default state */
|
|
+ if (vfd->tvnorms) {
|
|
+ v4l2_std_id std;
|
|
+ ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std);
|
|
+ if (ret) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "Unable to get std\n");
|
|
+ goto err_unreg_vdev;
|
|
+ }
|
|
+ bcap_dev->std = std;
|
|
+ }
|
|
+ ret = bcap_init_sensor_formats(bcap_dev);
|
|
+ if (ret) {
|
|
+ v4l2_err(&bcap_dev->v4l2_dev,
|
|
+ "Unable to create sensor formats table\n");
|
|
+ goto err_unreg_vdev;
|
|
+ }
|
|
+ return 0;
|
|
+err_unreg_vdev:
|
|
+ video_unregister_device(bcap_dev->video_dev);
|
|
+ bcap_dev->video_dev = NULL;
|
|
+err_free_handler:
|
|
+ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
|
|
+err_unreg_v4l2:
|
|
+ v4l2_device_unregister(&bcap_dev->v4l2_dev);
|
|
+err_release_vdev:
|
|
+ if (bcap_dev->video_dev)
|
|
+ video_device_release(bcap_dev->video_dev);
|
|
+err_cleanup_ctx:
|
|
+ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
|
|
+err_free_ppi:
|
|
+ ppi_delete_instance(bcap_dev->ppi);
|
|
+err_free_dev:
|
|
+ kfree(bcap_dev);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int __devexit bcap_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
|
|
+ struct bcap_device *bcap_dev = container_of(v4l2_dev,
|
|
+ struct bcap_device, v4l2_dev);
|
|
+
|
|
+ bcap_free_sensor_formats(bcap_dev);
|
|
+ video_unregister_device(bcap_dev->video_dev);
|
|
+ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
|
|
+ v4l2_device_unregister(v4l2_dev);
|
|
+ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
|
|
+ ppi_delete_instance(bcap_dev->ppi);
|
|
+ kfree(bcap_dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct platform_driver bcap_driver = {
|
|
+ .driver = {
|
|
+ .name = CAPTURE_DRV_NAME,
|
|
+ .owner = THIS_MODULE,
|
|
+ },
|
|
+ .probe = bcap_probe,
|
|
+ .remove = __devexit_p(bcap_remove),
|
|
+};
|
|
+
|
|
+static __init int bcap_init(void)
|
|
+{
|
|
+ return platform_driver_register(&bcap_driver);
|
|
+}
|
|
+
|
|
+static __exit void bcap_exit(void)
|
|
+{
|
|
+ platform_driver_unregister(&bcap_driver);
|
|
+}
|
|
+
|
|
+module_init(bcap_init);
|
|
+module_exit(bcap_exit);
|
|
+
|
|
+MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
|
|
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
|
|
+MODULE_LICENSE("GPL v2");
|
|
Index: linux-3.3.x86_64/drivers/media/video/blackfin/ppi.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/blackfin/ppi.c
|
|
@@ -0,0 +1,271 @@
|
|
+/*
|
|
+ * ppi.c Analog Devices Parallel Peripheral Interface driver
|
|
+ *
|
|
+ * Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#include <asm/bfin_ppi.h>
|
|
+#include <asm/blackfin.h>
|
|
+#include <asm/cacheflush.h>
|
|
+#include <asm/dma.h>
|
|
+#include <asm/portmux.h>
|
|
+
|
|
+#include <media/blackfin/ppi.h>
|
|
+
|
|
+static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler);
|
|
+static void ppi_detach_irq(struct ppi_if *ppi);
|
|
+static int ppi_start(struct ppi_if *ppi);
|
|
+static int ppi_stop(struct ppi_if *ppi);
|
|
+static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params);
|
|
+static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr);
|
|
+
|
|
+static const struct ppi_ops ppi_ops = {
|
|
+ .attach_irq = ppi_attach_irq,
|
|
+ .detach_irq = ppi_detach_irq,
|
|
+ .start = ppi_start,
|
|
+ .stop = ppi_stop,
|
|
+ .set_params = ppi_set_params,
|
|
+ .update_addr = ppi_update_addr,
|
|
+};
|
|
+
|
|
+static irqreturn_t ppi_irq_err(int irq, void *dev_id)
|
|
+{
|
|
+ struct ppi_if *ppi = dev_id;
|
|
+ const struct ppi_info *info = ppi->info;
|
|
+
|
|
+ switch (info->type) {
|
|
+ case PPI_TYPE_PPI:
|
|
+ {
|
|
+ struct bfin_ppi_regs *reg = info->base;
|
|
+ unsigned short status;
|
|
+
|
|
+ /* register on bf561 is cleared when read
|
|
+ * others are W1C
|
|
+ */
|
|
+ status = bfin_read16(®->status);
|
|
+ bfin_write16(®->status, 0xff00);
|
|
+ break;
|
|
+ }
|
|
+ case PPI_TYPE_EPPI:
|
|
+ {
|
|
+ struct bfin_eppi_regs *reg = info->base;
|
|
+ bfin_write16(®->status, 0xffff);
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler)
|
|
+{
|
|
+ const struct ppi_info *info = ppi->info;
|
|
+ int ret;
|
|
+
|
|
+ ret = request_dma(info->dma_ch, "PPI_DMA");
|
|
+
|
|
+ if (ret) {
|
|
+ pr_err("Unable to allocate DMA channel for PPI\n");
|
|
+ return ret;
|
|
+ }
|
|
+ set_dma_callback(info->dma_ch, handler, ppi);
|
|
+
|
|
+ if (ppi->err_int) {
|
|
+ ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi);
|
|
+ if (ret) {
|
|
+ pr_err("Unable to allocate IRQ for PPI\n");
|
|
+ free_dma(info->dma_ch);
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void ppi_detach_irq(struct ppi_if *ppi)
|
|
+{
|
|
+ const struct ppi_info *info = ppi->info;
|
|
+
|
|
+ if (ppi->err_int)
|
|
+ free_irq(info->irq_err, ppi);
|
|
+ free_dma(info->dma_ch);
|
|
+}
|
|
+
|
|
+static int ppi_start(struct ppi_if *ppi)
|
|
+{
|
|
+ const struct ppi_info *info = ppi->info;
|
|
+
|
|
+ /* enable DMA */
|
|
+ enable_dma(info->dma_ch);
|
|
+
|
|
+ /* enable PPI */
|
|
+ ppi->ppi_control |= PORT_EN;
|
|
+ switch (info->type) {
|
|
+ case PPI_TYPE_PPI:
|
|
+ {
|
|
+ struct bfin_ppi_regs *reg = info->base;
|
|
+ bfin_write16(®->control, ppi->ppi_control);
|
|
+ break;
|
|
+ }
|
|
+ case PPI_TYPE_EPPI:
|
|
+ {
|
|
+ struct bfin_eppi_regs *reg = info->base;
|
|
+ bfin_write32(®->control, ppi->ppi_control);
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ SSYNC();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ppi_stop(struct ppi_if *ppi)
|
|
+{
|
|
+ const struct ppi_info *info = ppi->info;
|
|
+
|
|
+ /* disable PPI */
|
|
+ ppi->ppi_control &= ~PORT_EN;
|
|
+ switch (info->type) {
|
|
+ case PPI_TYPE_PPI:
|
|
+ {
|
|
+ struct bfin_ppi_regs *reg = info->base;
|
|
+ bfin_write16(®->control, ppi->ppi_control);
|
|
+ break;
|
|
+ }
|
|
+ case PPI_TYPE_EPPI:
|
|
+ {
|
|
+ struct bfin_eppi_regs *reg = info->base;
|
|
+ bfin_write32(®->control, ppi->ppi_control);
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* disable DMA */
|
|
+ clear_dma_irqstat(info->dma_ch);
|
|
+ disable_dma(info->dma_ch);
|
|
+
|
|
+ SSYNC();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
|
|
+{
|
|
+ const struct ppi_info *info = ppi->info;
|
|
+ int dma32 = 0;
|
|
+ int dma_config, bytes_per_line, lines_per_frame;
|
|
+
|
|
+ bytes_per_line = params->width * params->bpp / 8;
|
|
+ lines_per_frame = params->height;
|
|
+ if (params->int_mask == 0xFFFFFFFF)
|
|
+ ppi->err_int = false;
|
|
+ else
|
|
+ ppi->err_int = true;
|
|
+
|
|
+ dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN);
|
|
+ ppi->ppi_control = params->ppi_control & ~PORT_EN;
|
|
+ switch (info->type) {
|
|
+ case PPI_TYPE_PPI:
|
|
+ {
|
|
+ struct bfin_ppi_regs *reg = info->base;
|
|
+
|
|
+ if (params->ppi_control & DMA32)
|
|
+ dma32 = 1;
|
|
+
|
|
+ bfin_write16(®->control, ppi->ppi_control);
|
|
+ bfin_write16(®->count, bytes_per_line - 1);
|
|
+ bfin_write16(®->frame, lines_per_frame);
|
|
+ break;
|
|
+ }
|
|
+ case PPI_TYPE_EPPI:
|
|
+ {
|
|
+ struct bfin_eppi_regs *reg = info->base;
|
|
+
|
|
+ if ((params->ppi_control & PACK_EN)
|
|
+ || (params->ppi_control & 0x38000) > DLEN_16)
|
|
+ dma32 = 1;
|
|
+
|
|
+ bfin_write32(®->control, ppi->ppi_control);
|
|
+ bfin_write16(®->line, bytes_per_line + params->blank_clocks);
|
|
+ bfin_write16(®->frame, lines_per_frame);
|
|
+ bfin_write16(®->hdelay, 0);
|
|
+ bfin_write16(®->vdelay, 0);
|
|
+ bfin_write16(®->hcount, bytes_per_line);
|
|
+ bfin_write16(®->vcount, lines_per_frame);
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (dma32) {
|
|
+ dma_config |= WDSIZE_32;
|
|
+ set_dma_x_count(info->dma_ch, bytes_per_line >> 2);
|
|
+ set_dma_x_modify(info->dma_ch, 4);
|
|
+ set_dma_y_modify(info->dma_ch, 4);
|
|
+ } else {
|
|
+ dma_config |= WDSIZE_16;
|
|
+ set_dma_x_count(info->dma_ch, bytes_per_line >> 1);
|
|
+ set_dma_x_modify(info->dma_ch, 2);
|
|
+ set_dma_y_modify(info->dma_ch, 2);
|
|
+ }
|
|
+ set_dma_y_count(info->dma_ch, lines_per_frame);
|
|
+ set_dma_config(info->dma_ch, dma_config);
|
|
+
|
|
+ SSYNC();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr)
|
|
+{
|
|
+ set_dma_start_addr(ppi->info->dma_ch, addr);
|
|
+}
|
|
+
|
|
+struct ppi_if *ppi_create_instance(const struct ppi_info *info)
|
|
+{
|
|
+ struct ppi_if *ppi;
|
|
+
|
|
+ if (!info || !info->pin_req)
|
|
+ return NULL;
|
|
+
|
|
+ if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) {
|
|
+ pr_err("request peripheral failed\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
|
|
+ if (!ppi) {
|
|
+ peripheral_free_list(info->pin_req);
|
|
+ pr_err("unable to allocate memory for ppi handle\n");
|
|
+ return NULL;
|
|
+ }
|
|
+ ppi->ops = &ppi_ops;
|
|
+ ppi->info = info;
|
|
+
|
|
+ pr_info("ppi probe success\n");
|
|
+ return ppi;
|
|
+}
|
|
+
|
|
+void ppi_delete_instance(struct ppi_if *ppi)
|
|
+{
|
|
+ peripheral_free_list(ppi->info->pin_req);
|
|
+ kfree(ppi);
|
|
+}
|
|
Index: linux-3.3.x86_64/include/media/blackfin/bfin_capture.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/include/media/blackfin/bfin_capture.h
|
|
@@ -0,0 +1,37 @@
|
|
+#ifndef _BFIN_CAPTURE_H_
|
|
+#define _BFIN_CAPTURE_H_
|
|
+
|
|
+#include <linux/i2c.h>
|
|
+
|
|
+struct v4l2_input;
|
|
+struct ppi_info;
|
|
+
|
|
+struct bcap_route {
|
|
+ u32 input;
|
|
+ u32 output;
|
|
+};
|
|
+
|
|
+struct bfin_capture_config {
|
|
+ /* card name */
|
|
+ char *card_name;
|
|
+ /* inputs available at the sub device */
|
|
+ struct v4l2_input *inputs;
|
|
+ /* number of inputs supported */
|
|
+ int num_inputs;
|
|
+ /* routing information for each input */
|
|
+ struct bcap_route *routes;
|
|
+ /* i2c bus adapter no */
|
|
+ int i2c_adapter_id;
|
|
+ /* i2c subdevice board info */
|
|
+ struct i2c_board_info board_info;
|
|
+ /* ppi board info */
|
|
+ const struct ppi_info *ppi_info;
|
|
+ /* ppi control */
|
|
+ unsigned long ppi_control;
|
|
+ /* ppi interrupt mask */
|
|
+ u32 int_mask;
|
|
+ /* horizontal blanking clocks */
|
|
+ int blank_clocks;
|
|
+};
|
|
+
|
|
+#endif
|
|
Index: linux-3.3.x86_64/include/media/blackfin/ppi.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/include/media/blackfin/ppi.h
|
|
@@ -0,0 +1,74 @@
|
|
+/*
|
|
+ * Analog Devices PPI header file
|
|
+ *
|
|
+ * Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#ifndef _PPI_H_
|
|
+#define _PPI_H_
|
|
+
|
|
+#include <linux/interrupt.h>
|
|
+
|
|
+#ifdef EPPI_EN
|
|
+#define PORT_EN EPPI_EN
|
|
+#define DMA32 0
|
|
+#define PACK_EN PACKEN
|
|
+#endif
|
|
+
|
|
+struct ppi_if;
|
|
+
|
|
+struct ppi_params {
|
|
+ int width;
|
|
+ int height;
|
|
+ int bpp;
|
|
+ unsigned long ppi_control;
|
|
+ u32 int_mask;
|
|
+ int blank_clocks;
|
|
+};
|
|
+
|
|
+struct ppi_ops {
|
|
+ int (*attach_irq)(struct ppi_if *ppi, irq_handler_t handler);
|
|
+ void (*detach_irq)(struct ppi_if *ppi);
|
|
+ int (*start)(struct ppi_if *ppi);
|
|
+ int (*stop)(struct ppi_if *ppi);
|
|
+ int (*set_params)(struct ppi_if *ppi, struct ppi_params *params);
|
|
+ void (*update_addr)(struct ppi_if *ppi, unsigned long addr);
|
|
+};
|
|
+
|
|
+enum ppi_type {
|
|
+ PPI_TYPE_PPI,
|
|
+ PPI_TYPE_EPPI,
|
|
+};
|
|
+
|
|
+struct ppi_info {
|
|
+ enum ppi_type type;
|
|
+ int dma_ch;
|
|
+ int irq_err;
|
|
+ void __iomem *base;
|
|
+ const unsigned short *pin_req;
|
|
+};
|
|
+
|
|
+struct ppi_if {
|
|
+ unsigned long ppi_control;
|
|
+ const struct ppi_ops *ops;
|
|
+ const struct ppi_info *info;
|
|
+ bool err_int;
|
|
+ void *priv;
|
|
+};
|
|
+
|
|
+struct ppi_if *ppi_create_instance(const struct ppi_info *info);
|
|
+void ppi_delete_instance(struct ppi_if *ppi);
|
|
+#endif
|
|
Index: linux-3.3.x86_64/Documentation/DocBook/media/v4l/selection-api.xml
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/Documentation/DocBook/media/v4l/selection-api.xml
|
|
+++ linux-3.3.x86_64/Documentation/DocBook/media/v4l/selection-api.xml
|
|
@@ -52,6 +52,10 @@ cropping and composing rectangles have t
|
|
</textobject>
|
|
</mediaobject>
|
|
</figure>
|
|
+
|
|
+For complete list of the available selection targets see table <xref
|
|
+linkend="v4l2-sel-target"/>
|
|
+
|
|
</section>
|
|
|
|
<section>
|
|
@@ -186,7 +190,7 @@ V4L2_SEL_TGT_COMPOSE_ACTIVE </constant>
|
|
|
|
<section>
|
|
|
|
- <title>Scaling control.</title>
|
|
+ <title>Scaling control</title>
|
|
|
|
<para>An application can detect if scaling is performed by comparing the width
|
|
and the height of rectangles obtained using <constant> V4L2_SEL_TGT_CROP_ACTIVE
|
|
@@ -200,7 +204,7 @@ the scaling ratios using these values.</
|
|
|
|
<section>
|
|
|
|
- <title>Comparison with old cropping API.</title>
|
|
+ <title>Comparison with old cropping API</title>
|
|
|
|
<para>The selection API was introduced to cope with deficiencies of previous
|
|
<link linkend="crop"> API </link>, that was designed to control simple capture
|
|
Index: linux-3.3.x86_64/Documentation/DocBook/media/v4l/vidioc-g-selection.xml
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/Documentation/DocBook/media/v4l/vidioc-g-selection.xml
|
|
+++ linux-3.3.x86_64/Documentation/DocBook/media/v4l/vidioc-g-selection.xml
|
|
@@ -58,43 +58,43 @@
|
|
|
|
<para>The ioctls are used to query and configure selection rectangles.</para>
|
|
|
|
-<para> To query the cropping (composing) rectangle set <structfield>
|
|
-&v4l2-selection;::type </structfield> to the respective buffer type. Do not
|
|
-use multiplanar buffers. Use <constant> V4L2_BUF_TYPE_VIDEO_CAPTURE
|
|
+<para> To query the cropping (composing) rectangle set &v4l2-selection;
|
|
+<structfield> type </structfield> field to the respective buffer type.
|
|
+Do not use multiplanar buffers. Use <constant> V4L2_BUF_TYPE_VIDEO_CAPTURE
|
|
</constant> instead of <constant> V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
|
|
</constant>. Use <constant> V4L2_BUF_TYPE_VIDEO_OUTPUT </constant> instead of
|
|
<constant> V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE </constant>. The next step is
|
|
-setting <structfield> &v4l2-selection;::target </structfield> to value
|
|
-<constant> V4L2_SEL_TGT_CROP_ACTIVE </constant> (<constant>
|
|
+setting the value of &v4l2-selection; <structfield>target</structfield> field
|
|
+to <constant> V4L2_SEL_TGT_CROP_ACTIVE </constant> (<constant>
|
|
V4L2_SEL_TGT_COMPOSE_ACTIVE </constant>). Please refer to table <xref
|
|
linkend="v4l2-sel-target" /> or <xref linkend="selection-api" /> for additional
|
|
-targets. Fields <structfield> &v4l2-selection;::flags </structfield> and
|
|
-<structfield> &v4l2-selection;::reserved </structfield> are ignored and they
|
|
-must be filled with zeros. The driver fills the rest of the structure or
|
|
+targets. The <structfield>flags</structfield> and <structfield>reserved
|
|
+</structfield> fields of &v4l2-selection; are ignored and they must be filled
|
|
+with zeros. The driver fills the rest of the structure or
|
|
returns &EINVAL; if incorrect buffer type or target was used. If cropping
|
|
(composing) is not supported then the active rectangle is not mutable and it is
|
|
-always equal to the bounds rectangle. Finally, structure <structfield>
|
|
-&v4l2-selection;::r </structfield> is filled with the current cropping
|
|
+always equal to the bounds rectangle. Finally, the &v4l2-rect;
|
|
+<structfield>r</structfield> rectangle is filled with the current cropping
|
|
(composing) coordinates. The coordinates are expressed in driver-dependent
|
|
units. The only exception are rectangles for images in raw formats, whose
|
|
coordinates are always expressed in pixels. </para>
|
|
|
|
-<para> To change the cropping (composing) rectangle set <structfield>
|
|
-&v4l2-selection;::type </structfield> to the respective buffer type. Do not
|
|
+<para> To change the cropping (composing) rectangle set the &v4l2-selection;
|
|
+<structfield>type</structfield> field to the respective buffer type. Do not
|
|
use multiplanar buffers. Use <constant> V4L2_BUF_TYPE_VIDEO_CAPTURE
|
|
</constant> instead of <constant> V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
|
|
</constant>. Use <constant> V4L2_BUF_TYPE_VIDEO_OUTPUT </constant> instead of
|
|
<constant> V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE </constant>. The next step is
|
|
-setting <structfield> &v4l2-selection;::target </structfield> to value
|
|
-<constant> V4L2_SEL_TGT_CROP_ACTIVE </constant> (<constant>
|
|
+setting the value of &v4l2-selection; <structfield>target</structfield> to
|
|
+<constant>V4L2_SEL_TGT_CROP_ACTIVE</constant> (<constant>
|
|
V4L2_SEL_TGT_COMPOSE_ACTIVE </constant>). Please refer to table <xref
|
|
linkend="v4l2-sel-target" /> or <xref linkend="selection-api" /> for additional
|
|
-targets. Set desired active area into the field <structfield>
|
|
-&v4l2-selection;::r </structfield>. Field <structfield>
|
|
-&v4l2-selection;::reserved </structfield> is ignored and must be filled with
|
|
-zeros. The driver may adjust the rectangle coordinates. An application may
|
|
-introduce constraints to control rounding behaviour. Set the field
|
|
-<structfield> &v4l2-selection;::flags </structfield> to one of values:
|
|
+targets. The &v4l2-rect; <structfield>r</structfield> rectangle need to be
|
|
+set to the desired active area. Field &v4l2-selection; <structfield> reserved
|
|
+</structfield> is ignored and must be filled with zeros. The driver may adjust
|
|
+coordinates of the requested rectangle. An application may
|
|
+introduce constraints to control rounding behaviour. The &v4l2-selection;
|
|
+<structfield>flags</structfield> field must be set to one of the following:
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
@@ -129,7 +129,7 @@ and vertical offset and sizes are chosen
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
- <para>Satisfy constraints from <structfield>&v4l2-selection;::flags</structfield>.</para>
|
|
+ <para>Satisfy constraints from &v4l2-selection; <structfield>flags</structfield>.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>Adjust width, height, left, and top to hardware limits and alignments.</para>
|
|
@@ -145,7 +145,7 @@ and vertical offset and sizes are chosen
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
-On success the field <structfield> &v4l2-selection;::r </structfield> contains
|
|
+On success the &v4l2-rect; <structfield>r</structfield> field contains
|
|
the adjusted rectangle. When the parameters are unsuitable the application may
|
|
modify the cropping (composing) or image parameters and repeat the cycle until
|
|
satisfactory parameters have been negotiated. If constraints flags have to be
|
|
@@ -162,38 +162,38 @@ exist no rectangle </emphasis> that sati
|
|
<tbody valign="top">
|
|
<row>
|
|
<entry><constant>V4L2_SEL_TGT_CROP_ACTIVE</constant></entry>
|
|
- <entry>0</entry>
|
|
- <entry>area that is currently cropped by hardware</entry>
|
|
+ <entry>0x0000</entry>
|
|
+ <entry>The area that is currently cropped by hardware.</entry>
|
|
</row>
|
|
<row>
|
|
<entry><constant>V4L2_SEL_TGT_CROP_DEFAULT</constant></entry>
|
|
- <entry>1</entry>
|
|
- <entry>suggested cropping rectangle that covers the "whole picture"</entry>
|
|
+ <entry>0x0001</entry>
|
|
+ <entry>Suggested cropping rectangle that covers the "whole picture".</entry>
|
|
</row>
|
|
<row>
|
|
<entry><constant>V4L2_SEL_TGT_CROP_BOUNDS</constant></entry>
|
|
- <entry>2</entry>
|
|
- <entry>limits for the cropping rectangle</entry>
|
|
+ <entry>0x0002</entry>
|
|
+ <entry>Limits for the cropping rectangle.</entry>
|
|
</row>
|
|
<row>
|
|
<entry><constant>V4L2_SEL_TGT_COMPOSE_ACTIVE</constant></entry>
|
|
- <entry>256</entry>
|
|
- <entry>area to which data are composed by hardware</entry>
|
|
+ <entry>0x0100</entry>
|
|
+ <entry>The area to which data is composed by hardware.</entry>
|
|
</row>
|
|
<row>
|
|
<entry><constant>V4L2_SEL_TGT_COMPOSE_DEFAULT</constant></entry>
|
|
- <entry>257</entry>
|
|
- <entry>suggested composing rectangle that covers the "whole picture"</entry>
|
|
+ <entry>0x0101</entry>
|
|
+ <entry>Suggested composing rectangle that covers the "whole picture".</entry>
|
|
</row>
|
|
<row>
|
|
<entry><constant>V4L2_SEL_TGT_COMPOSE_BOUNDS</constant></entry>
|
|
- <entry>258</entry>
|
|
- <entry>limits for the composing rectangle</entry>
|
|
+ <entry>0x0102</entry>
|
|
+ <entry>Limits for the composing rectangle.</entry>
|
|
</row>
|
|
<row>
|
|
<entry><constant>V4L2_SEL_TGT_COMPOSE_PADDED</constant></entry>
|
|
- <entry>259</entry>
|
|
- <entry>the active area and all padding pixels that are inserted or modified by the hardware</entry>
|
|
+ <entry>0x0103</entry>
|
|
+ <entry>The active area and all padding pixels that are inserted or modified by hardware.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
@@ -209,12 +209,14 @@ exist no rectangle </emphasis> that sati
|
|
<row>
|
|
<entry><constant>V4L2_SEL_FLAG_GE</constant></entry>
|
|
<entry>0x00000001</entry>
|
|
- <entry>indicate that adjusted rectangle must contain a rectangle from <structfield>&v4l2-selection;::r</structfield></entry>
|
|
+ <entry>Indicates that the adjusted rectangle must contain the original
|
|
+ &v4l2-selection; <structfield>r</structfield> rectangle.</entry>
|
|
</row>
|
|
<row>
|
|
<entry><constant>V4L2_SEL_FLAG_LE</constant></entry>
|
|
<entry>0x00000002</entry>
|
|
- <entry>indicate that adjusted rectangle must be inside a rectangle from <structfield>&v4l2-selection;::r</structfield></entry>
|
|
+ <entry>Indicates that the adjusted rectangle must be inside the original
|
|
+ &v4l2-rect; <structfield>r</structfield> rectangle.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
@@ -245,27 +247,29 @@ exist no rectangle </emphasis> that sati
|
|
<row>
|
|
<entry>__u32</entry>
|
|
<entry><structfield>type</structfield></entry>
|
|
- <entry>Type of the buffer (from &v4l2-buf-type;)</entry>
|
|
+ <entry>Type of the buffer (from &v4l2-buf-type;).</entry>
|
|
</row>
|
|
<row>
|
|
<entry>__u32</entry>
|
|
<entry><structfield>target</structfield></entry>
|
|
- <entry>used to select between <link linkend="v4l2-sel-target"> cropping and composing rectangles </link></entry>
|
|
+ <entry>Used to select between <link linkend="v4l2-sel-target"> cropping
|
|
+ and composing rectangles</link>.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>__u32</entry>
|
|
<entry><structfield>flags</structfield></entry>
|
|
- <entry>control over coordinates adjustments, refer to <link linkend="v4l2-sel-flags">selection flags</link></entry>
|
|
+ <entry>Flags controlling the selection rectangle adjustments, refer to
|
|
+ <link linkend="v4l2-sel-flags">selection flags</link>.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>&v4l2-rect;</entry>
|
|
<entry><structfield>r</structfield></entry>
|
|
- <entry>selection rectangle</entry>
|
|
+ <entry>The selection rectangle.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>__u32</entry>
|
|
<entry><structfield>reserved[9]</structfield></entry>
|
|
- <entry>Reserved fields for future use</entry>
|
|
+ <entry>Reserved fields for future use.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
@@ -278,24 +282,24 @@ exist no rectangle </emphasis> that sati
|
|
<varlistentry>
|
|
<term><errorcode>EINVAL</errorcode></term>
|
|
<listitem>
|
|
- <para>The buffer <structfield> &v4l2-selection;::type </structfield>
|
|
-or <structfield> &v4l2-selection;::target </structfield> is not supported, or
|
|
-the <structfield> &v4l2-selection;::flags </structfield> are invalid.</para>
|
|
+ <para>Given buffer type <structfield>type</structfield> or
|
|
+the selection target <structfield>target</structfield> is not supported,
|
|
+or the <structfield>flags</structfield> argument is not valid.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><errorcode>ERANGE</errorcode></term>
|
|
<listitem>
|
|
- <para>it is not possible to adjust a rectangle <structfield>
|
|
-&v4l2-selection;::r </structfield> that satisfies all contraints from
|
|
-<structfield> &v4l2-selection;::flags </structfield>.</para>
|
|
+ <para>It is not possible to adjust &v4l2-rect; <structfield>
|
|
+r</structfield> rectangle to satisfy all contraints given in the
|
|
+<structfield>flags</structfield> argument.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><errorcode>EBUSY</errorcode></term>
|
|
<listitem>
|
|
- <para>it is not possible to apply change of selection rectangle at the moment.
|
|
-Usually because streaming is in progress.</para>
|
|
+ <para>It is not possible to apply change of the selection rectangle
|
|
+at the moment. Usually because streaming is in progress.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
Index: linux-3.3.x86_64/drivers/media/video/w9966.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/w9966.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/w9966.c
|
|
@@ -129,9 +129,9 @@ MODULE_LICENSE("GPL");
|
|
MODULE_VERSION("0.33.1");
|
|
|
|
#ifdef MODULE
|
|
-static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
|
|
+static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
|
|
#else
|
|
-static const char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
|
|
+static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
|
|
#endif
|
|
module_param_array(pardev, charp, NULL, 0);
|
|
MODULE_PARM_DESC(pardev, "pardev: where to search for\n"
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/pt1/pt1.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/pt1/pt1.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/pt1/pt1.c
|
|
@@ -28,6 +28,7 @@
|
|
#include <linux/pci.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/freezer.h>
|
|
+#include <linux/ratelimit.h>
|
|
|
|
#include "dvbdev.h"
|
|
#include "dvb_demux.h"
|
|
@@ -77,6 +78,8 @@ struct pt1 {
|
|
struct pt1_adapter *adaps[PT1_NR_ADAPS];
|
|
struct pt1_table *tables;
|
|
struct task_struct *kthread;
|
|
+ int table_index;
|
|
+ int buf_index;
|
|
|
|
struct mutex lock;
|
|
int power;
|
|
@@ -90,12 +93,12 @@ struct pt1_adapter {
|
|
u8 *buf;
|
|
int upacket_count;
|
|
int packet_count;
|
|
+ int st_count;
|
|
|
|
struct dvb_adapter adap;
|
|
struct dvb_demux demux;
|
|
int users;
|
|
struct dmxdev dmxdev;
|
|
- struct dvb_net net;
|
|
struct dvb_frontend *fe;
|
|
int (*orig_set_voltage)(struct dvb_frontend *fe,
|
|
fe_sec_voltage_t voltage);
|
|
@@ -119,7 +122,7 @@ static u32 pt1_read_reg(struct pt1 *pt1,
|
|
return readl(pt1->regs + reg * 4);
|
|
}
|
|
|
|
-static int pt1_nr_tables = 64;
|
|
+static int pt1_nr_tables = 8;
|
|
module_param_named(nr_tables, pt1_nr_tables, int, 0);
|
|
|
|
static void pt1_increment_table_count(struct pt1 *pt1)
|
|
@@ -264,6 +267,7 @@ static int pt1_filter(struct pt1 *pt1, s
|
|
struct pt1_adapter *adap;
|
|
int offset;
|
|
u8 *buf;
|
|
+ int sc;
|
|
|
|
if (!page->upackets[PT1_NR_UPACKETS - 1])
|
|
return 0;
|
|
@@ -280,6 +284,16 @@ static int pt1_filter(struct pt1 *pt1, s
|
|
else if (!adap->upacket_count)
|
|
continue;
|
|
|
|
+ if (upacket >> 24 & 1)
|
|
+ printk_ratelimited(KERN_INFO "earth-pt1: device "
|
|
+ "buffer overflowing. table[%d] buf[%d]\n",
|
|
+ pt1->table_index, pt1->buf_index);
|
|
+ sc = upacket >> 26 & 0x7;
|
|
+ if (adap->st_count != -1 && sc != ((adap->st_count + 1) & 0x7))
|
|
+ printk_ratelimited(KERN_INFO "earth-pt1: data loss"
|
|
+ " in streamID(adapter)[%d]\n", index);
|
|
+ adap->st_count = sc;
|
|
+
|
|
buf = adap->buf;
|
|
offset = adap->packet_count * 188 + adap->upacket_count * 3;
|
|
buf[offset] = upacket >> 16;
|
|
@@ -303,30 +317,25 @@ static int pt1_filter(struct pt1 *pt1, s
|
|
static int pt1_thread(void *data)
|
|
{
|
|
struct pt1 *pt1;
|
|
- int table_index;
|
|
- int buf_index;
|
|
struct pt1_buffer_page *page;
|
|
|
|
pt1 = data;
|
|
set_freezable();
|
|
|
|
- table_index = 0;
|
|
- buf_index = 0;
|
|
-
|
|
while (!kthread_should_stop()) {
|
|
try_to_freeze();
|
|
|
|
- page = pt1->tables[table_index].bufs[buf_index].page;
|
|
+ page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page;
|
|
if (!pt1_filter(pt1, page)) {
|
|
schedule_timeout_interruptible((HZ + 999) / 1000);
|
|
continue;
|
|
}
|
|
|
|
- if (++buf_index >= PT1_NR_BUFS) {
|
|
+ if (++pt1->buf_index >= PT1_NR_BUFS) {
|
|
pt1_increment_table_count(pt1);
|
|
- buf_index = 0;
|
|
- if (++table_index >= pt1_nr_tables)
|
|
- table_index = 0;
|
|
+ pt1->buf_index = 0;
|
|
+ if (++pt1->table_index >= pt1_nr_tables)
|
|
+ pt1->table_index = 0;
|
|
}
|
|
}
|
|
|
|
@@ -477,21 +486,60 @@ err:
|
|
return ret;
|
|
}
|
|
|
|
+static int pt1_start_polling(struct pt1 *pt1)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ mutex_lock(&pt1->lock);
|
|
+ if (!pt1->kthread) {
|
|
+ pt1->kthread = kthread_run(pt1_thread, pt1, "earth-pt1");
|
|
+ if (IS_ERR(pt1->kthread)) {
|
|
+ ret = PTR_ERR(pt1->kthread);
|
|
+ pt1->kthread = NULL;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&pt1->lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static int pt1_start_feed(struct dvb_demux_feed *feed)
|
|
{
|
|
struct pt1_adapter *adap;
|
|
adap = container_of(feed->demux, struct pt1_adapter, demux);
|
|
- if (!adap->users++)
|
|
+ if (!adap->users++) {
|
|
+ int ret;
|
|
+
|
|
+ ret = pt1_start_polling(adap->pt1);
|
|
+ if (ret)
|
|
+ return ret;
|
|
pt1_set_stream(adap->pt1, adap->index, 1);
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
+static void pt1_stop_polling(struct pt1 *pt1)
|
|
+{
|
|
+ int i, count;
|
|
+
|
|
+ mutex_lock(&pt1->lock);
|
|
+ for (i = 0, count = 0; i < PT1_NR_ADAPS; i++)
|
|
+ count += pt1->adaps[i]->users;
|
|
+
|
|
+ if (count == 0 && pt1->kthread) {
|
|
+ kthread_stop(pt1->kthread);
|
|
+ pt1->kthread = NULL;
|
|
+ }
|
|
+ mutex_unlock(&pt1->lock);
|
|
+}
|
|
+
|
|
static int pt1_stop_feed(struct dvb_demux_feed *feed)
|
|
{
|
|
struct pt1_adapter *adap;
|
|
adap = container_of(feed->demux, struct pt1_adapter, demux);
|
|
- if (!--adap->users)
|
|
+ if (!--adap->users) {
|
|
pt1_set_stream(adap->pt1, adap->index, 0);
|
|
+ pt1_stop_polling(adap->pt1);
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -575,7 +623,6 @@ static int pt1_wakeup(struct dvb_fronten
|
|
|
|
static void pt1_free_adapter(struct pt1_adapter *adap)
|
|
{
|
|
- dvb_net_release(&adap->net);
|
|
adap->demux.dmx.close(&adap->demux.dmx);
|
|
dvb_dmxdev_release(&adap->dmxdev);
|
|
dvb_dmx_release(&adap->demux);
|
|
@@ -616,6 +663,7 @@ pt1_alloc_adapter(struct pt1 *pt1)
|
|
adap->buf = buf;
|
|
adap->upacket_count = 0;
|
|
adap->packet_count = 0;
|
|
+ adap->st_count = -1;
|
|
|
|
dvb_adap = &adap->adap;
|
|
dvb_adap->priv = adap;
|
|
@@ -644,8 +692,6 @@ pt1_alloc_adapter(struct pt1 *pt1)
|
|
if (ret < 0)
|
|
goto err_dmx_release;
|
|
|
|
- dvb_net_init(dvb_adap, &adap->net, &demux->dmx);
|
|
-
|
|
return adap;
|
|
|
|
err_dmx_release:
|
|
@@ -1020,7 +1066,8 @@ static void __devexit pt1_remove(struct
|
|
pt1 = pci_get_drvdata(pdev);
|
|
regs = pt1->regs;
|
|
|
|
- kthread_stop(pt1->kthread);
|
|
+ if (pt1->kthread)
|
|
+ kthread_stop(pt1->kthread);
|
|
pt1_cleanup_tables(pt1);
|
|
pt1_cleanup_frontends(pt1);
|
|
pt1_disable_ram(pt1);
|
|
@@ -1043,7 +1090,6 @@ pt1_probe(struct pci_dev *pdev, const st
|
|
void __iomem *regs;
|
|
struct pt1 *pt1;
|
|
struct i2c_adapter *i2c_adap;
|
|
- struct task_struct *kthread;
|
|
|
|
ret = pci_enable_device(pdev);
|
|
if (ret < 0)
|
|
@@ -1139,17 +1185,8 @@ pt1_probe(struct pci_dev *pdev, const st
|
|
if (ret < 0)
|
|
goto err_pt1_cleanup_frontends;
|
|
|
|
- kthread = kthread_run(pt1_thread, pt1, "pt1");
|
|
- if (IS_ERR(kthread)) {
|
|
- ret = PTR_ERR(kthread);
|
|
- goto err_pt1_cleanup_tables;
|
|
- }
|
|
-
|
|
- pt1->kthread = kthread;
|
|
return 0;
|
|
|
|
-err_pt1_cleanup_tables:
|
|
- pt1_cleanup_tables(pt1);
|
|
err_pt1_cleanup_frontends:
|
|
pt1_cleanup_frontends(pt1);
|
|
err_pt1_disable_ram:
|
|
Index: linux-3.3.x86_64/drivers/media/video/aptina-pll.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/aptina-pll.c
|
|
@@ -0,0 +1,174 @@
|
|
+/*
|
|
+ * Aptina Sensor PLL Configuration
|
|
+ *
|
|
+ * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
|
+ *
|
|
+ * 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 <linux/device.h>
|
|
+#include <linux/gcd.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/lcm.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+#include "aptina-pll.h"
|
|
+
|
|
+int aptina_pll_calculate(struct device *dev,
|
|
+ const struct aptina_pll_limits *limits,
|
|
+ struct aptina_pll *pll)
|
|
+{
|
|
+ unsigned int mf_min;
|
|
+ unsigned int mf_max;
|
|
+ unsigned int p1_min;
|
|
+ unsigned int p1_max;
|
|
+ unsigned int p1;
|
|
+ unsigned int div;
|
|
+
|
|
+ dev_dbg(dev, "PLL: ext clock %u pix clock %u\n",
|
|
+ pll->ext_clock, pll->pix_clock);
|
|
+
|
|
+ if (pll->ext_clock < limits->ext_clock_min ||
|
|
+ pll->ext_clock > limits->ext_clock_max) {
|
|
+ dev_err(dev, "pll: invalid external clock frequency.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) {
|
|
+ dev_err(dev, "pll: invalid pixel clock frequency.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Compute the multiplier M and combined N*P1 divisor. */
|
|
+ div = gcd(pll->pix_clock, pll->ext_clock);
|
|
+ pll->m = pll->pix_clock / div;
|
|
+ div = pll->ext_clock / div;
|
|
+
|
|
+ /* We now have the smallest M and N*P1 values that will result in the
|
|
+ * desired pixel clock frequency, but they might be out of the valid
|
|
+ * range. Compute the factor by which we should multiply them given the
|
|
+ * following constraints:
|
|
+ *
|
|
+ * - minimum/maximum multiplier
|
|
+ * - minimum/maximum multiplier output clock frequency assuming the
|
|
+ * minimum/maximum N value
|
|
+ * - minimum/maximum combined N*P1 divisor
|
|
+ */
|
|
+ mf_min = DIV_ROUND_UP(limits->m_min, pll->m);
|
|
+ mf_min = max(mf_min, limits->out_clock_min /
|
|
+ (pll->ext_clock / limits->n_min * pll->m));
|
|
+ mf_min = max(mf_min, limits->n_min * limits->p1_min / div);
|
|
+ mf_max = limits->m_max / pll->m;
|
|
+ mf_max = min(mf_max, limits->out_clock_max /
|
|
+ (pll->ext_clock / limits->n_max * pll->m));
|
|
+ mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div));
|
|
+
|
|
+ dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max);
|
|
+ if (mf_min > mf_max) {
|
|
+ dev_err(dev, "pll: no valid combined N*P1 divisor.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * We're looking for the highest acceptable P1 value for which a
|
|
+ * multiplier factor MF exists that fulfills the following conditions:
|
|
+ *
|
|
+ * 1. p1 is in the [p1_min, p1_max] range given by the limits and is
|
|
+ * even
|
|
+ * 2. mf is in the [mf_min, mf_max] range computed above
|
|
+ * 3. div * mf is a multiple of p1, in order to compute
|
|
+ * n = div * mf / p1
|
|
+ * m = pll->m * mf
|
|
+ * 4. the internal clock frequency, given by ext_clock / n, is in the
|
|
+ * [int_clock_min, int_clock_max] range given by the limits
|
|
+ * 5. the output clock frequency, given by ext_clock / n * m, is in the
|
|
+ * [out_clock_min, out_clock_max] range given by the limits
|
|
+ *
|
|
+ * The first naive approach is to iterate over all p1 values acceptable
|
|
+ * according to (1) and all mf values acceptable according to (2), and
|
|
+ * stop at the first combination that fulfills (3), (4) and (5). This
|
|
+ * has a O(n^2) complexity.
|
|
+ *
|
|
+ * Instead of iterating over all mf values in the [mf_min, mf_max] range
|
|
+ * we can compute the mf increment between two acceptable values
|
|
+ * according to (3) with
|
|
+ *
|
|
+ * mf_inc = p1 / gcd(div, p1) (6)
|
|
+ *
|
|
+ * and round the minimum up to the nearest multiple of mf_inc. This will
|
|
+ * restrict the number of mf values to be checked.
|
|
+ *
|
|
+ * Furthermore, conditions (4) and (5) only restrict the range of
|
|
+ * acceptable p1 and mf values by modifying the minimum and maximum
|
|
+ * limits. (5) can be expressed as
|
|
+ *
|
|
+ * ext_clock / (div * mf / p1) * m * mf >= out_clock_min
|
|
+ * ext_clock / (div * mf / p1) * m * mf <= out_clock_max
|
|
+ *
|
|
+ * or
|
|
+ *
|
|
+ * p1 >= out_clock_min * div / (ext_clock * m) (7)
|
|
+ * p1 <= out_clock_max * div / (ext_clock * m)
|
|
+ *
|
|
+ * Similarly, (4) can be expressed as
|
|
+ *
|
|
+ * mf >= ext_clock * p1 / (int_clock_max * div) (8)
|
|
+ * mf <= ext_clock * p1 / (int_clock_min * div)
|
|
+ *
|
|
+ * We can thus iterate over the restricted p1 range defined by the
|
|
+ * combination of (1) and (7), and then compute the restricted mf range
|
|
+ * defined by the combination of (2), (6) and (8). If the resulting mf
|
|
+ * range is not empty, any value in the mf range is acceptable. We thus
|
|
+ * select the mf lwoer bound and the corresponding p1 value.
|
|
+ */
|
|
+ if (limits->p1_min == 0) {
|
|
+ dev_err(dev, "pll: P1 minimum value must be >0.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div,
|
|
+ pll->ext_clock * pll->m));
|
|
+ p1_max = min(limits->p1_max, limits->out_clock_max * div /
|
|
+ (pll->ext_clock * pll->m));
|
|
+
|
|
+ for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) {
|
|
+ unsigned int mf_inc = p1 / gcd(div, p1);
|
|
+ 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_high = min(mf_max, pll->ext_clock * p1 /
|
|
+ (limits->int_clock_min * div));
|
|
+
|
|
+ if (mf_low > mf_high)
|
|
+ continue;
|
|
+
|
|
+ pll->n = div * mf_low / p1;
|
|
+ pll->m *= mf_low;
|
|
+ pll->p1 = p1;
|
|
+ dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ dev_err(dev, "pll: no valid N and P1 divisors found.\n");
|
|
+ return -EINVAL;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(aptina_pll_calculate);
|
|
+
|
|
+MODULE_DESCRIPTION("Aptina PLL Helpers");
|
|
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
|
|
+MODULE_LICENSE("GPL v2");
|
|
Index: linux-3.3.x86_64/drivers/media/video/aptina-pll.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/aptina-pll.h
|
|
@@ -0,0 +1,56 @@
|
|
+/*
|
|
+ * Aptina Sensor PLL Configuration
|
|
+ *
|
|
+ * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
|
+ *
|
|
+ * 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 __APTINA_PLL_H
|
|
+#define __APTINA_PLL_H
|
|
+
|
|
+struct aptina_pll {
|
|
+ unsigned int ext_clock;
|
|
+ unsigned int pix_clock;
|
|
+
|
|
+ unsigned int n;
|
|
+ unsigned int m;
|
|
+ unsigned int p1;
|
|
+};
|
|
+
|
|
+struct aptina_pll_limits {
|
|
+ unsigned int ext_clock_min;
|
|
+ unsigned int ext_clock_max;
|
|
+ unsigned int int_clock_min;
|
|
+ unsigned int int_clock_max;
|
|
+ unsigned int out_clock_min;
|
|
+ unsigned int out_clock_max;
|
|
+ unsigned int pix_clock_max;
|
|
+
|
|
+ unsigned int n_min;
|
|
+ unsigned int n_max;
|
|
+ unsigned int m_min;
|
|
+ unsigned int m_max;
|
|
+ unsigned int p1_min;
|
|
+ unsigned int p1_max;
|
|
+};
|
|
+
|
|
+struct device;
|
|
+
|
|
+int aptina_pll_calculate(struct device *dev,
|
|
+ const struct aptina_pll_limits *limits,
|
|
+ struct aptina_pll *pll);
|
|
+
|
|
+#endif /* __APTINA_PLL_H */
|
|
Index: linux-3.3.x86_64/drivers/media/video/mt9m032.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/video/mt9m032.c
|
|
@@ -0,0 +1,868 @@
|
|
+/*
|
|
+ * Driver for MT9M032 CMOS Image Sensor from Micron
|
|
+ *
|
|
+ * Copyright (C) 2010-2011 Lund Engineering
|
|
+ * Contact: Gil Lund <gwlund@lundeng.com>
|
|
+ * Author: Martin Hostettler <martin@neutronstar.dyndns.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License
|
|
+ * version 2 as published by the Free Software Foundation.
|
|
+ *
|
|
+ * 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 <linux/delay.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/math64.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/v4l2-mediabus.h>
|
|
+
|
|
+#include <media/media-entity.h>
|
|
+#include <media/mt9m032.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-device.h>
|
|
+#include <media/v4l2-subdev.h>
|
|
+
|
|
+#include "aptina-pll.h"
|
|
+
|
|
+/*
|
|
+ * width and height include active boundary and black parts
|
|
+ *
|
|
+ * column 0- 15 active boundary
|
|
+ * column 16-1455 image
|
|
+ * column 1456-1471 active boundary
|
|
+ * column 1472-1599 black
|
|
+ *
|
|
+ * row 0- 51 black
|
|
+ * row 53- 59 active boundary
|
|
+ * row 60-1139 image
|
|
+ * row 1140-1147 active boundary
|
|
+ * row 1148-1151 black
|
|
+ */
|
|
+
|
|
+#define MT9M032_PIXEL_ARRAY_WIDTH 1600
|
|
+#define MT9M032_PIXEL_ARRAY_HEIGHT 1152
|
|
+
|
|
+#define MT9M032_CHIP_VERSION 0x00
|
|
+#define MT9M032_CHIP_VERSION_VALUE 0x1402
|
|
+#define MT9M032_ROW_START 0x01
|
|
+#define MT9M032_ROW_START_MIN 0
|
|
+#define MT9M032_ROW_START_MAX 1152
|
|
+#define MT9M032_ROW_START_DEF 60
|
|
+#define MT9M032_COLUMN_START 0x02
|
|
+#define MT9M032_COLUMN_START_MIN 0
|
|
+#define MT9M032_COLUMN_START_MAX 1600
|
|
+#define MT9M032_COLUMN_START_DEF 16
|
|
+#define MT9M032_ROW_SIZE 0x03
|
|
+#define MT9M032_ROW_SIZE_MIN 32
|
|
+#define MT9M032_ROW_SIZE_MAX 1152
|
|
+#define MT9M032_ROW_SIZE_DEF 1080
|
|
+#define MT9M032_COLUMN_SIZE 0x04
|
|
+#define MT9M032_COLUMN_SIZE_MIN 32
|
|
+#define MT9M032_COLUMN_SIZE_MAX 1600
|
|
+#define MT9M032_COLUMN_SIZE_DEF 1440
|
|
+#define MT9M032_HBLANK 0x05
|
|
+#define MT9M032_VBLANK 0x06
|
|
+#define MT9M032_VBLANK_MAX 0x7ff
|
|
+#define MT9M032_SHUTTER_WIDTH_HIGH 0x08
|
|
+#define MT9M032_SHUTTER_WIDTH_LOW 0x09
|
|
+#define MT9M032_SHUTTER_WIDTH_MIN 1
|
|
+#define MT9M032_SHUTTER_WIDTH_MAX 1048575
|
|
+#define MT9M032_SHUTTER_WIDTH_DEF 1943
|
|
+#define MT9M032_PIX_CLK_CTRL 0x0a
|
|
+#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000
|
|
+#define MT9M032_RESTART 0x0b
|
|
+#define MT9M032_RESET 0x0d
|
|
+#define MT9M032_PLL_CONFIG1 0x11
|
|
+#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f
|
|
+#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8
|
|
+#define MT9M032_READ_MODE1 0x1e
|
|
+#define MT9M032_READ_MODE2 0x20
|
|
+#define MT9M032_READ_MODE2_VFLIP_SHIFT 15
|
|
+#define MT9M032_READ_MODE2_HFLIP_SHIFT 14
|
|
+#define MT9M032_READ_MODE2_ROW_BLC 0x40
|
|
+#define MT9M032_GAIN_GREEN1 0x2b
|
|
+#define MT9M032_GAIN_BLUE 0x2c
|
|
+#define MT9M032_GAIN_RED 0x2d
|
|
+#define MT9M032_GAIN_GREEN2 0x2e
|
|
+
|
|
+/* write only */
|
|
+#define MT9M032_GAIN_ALL 0x35
|
|
+#define MT9M032_GAIN_DIGITAL_MASK 0x7f
|
|
+#define MT9M032_GAIN_DIGITAL_SHIFT 8
|
|
+#define MT9M032_GAIN_AMUL_SHIFT 6
|
|
+#define MT9M032_GAIN_ANALOG_MASK 0x3f
|
|
+#define MT9M032_FORMATTER1 0x9e
|
|
+#define MT9M032_FORMATTER2 0x9f
|
|
+#define MT9M032_FORMATTER2_DOUT_EN 0x1000
|
|
+#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000
|
|
+
|
|
+/*
|
|
+ * The available MT9M032 datasheet is missing documentation for register 0x10
|
|
+ * MT9P031 seems to be close enough, so use constants from that datasheet for
|
|
+ * now.
|
|
+ * But keep the name MT9P031 to remind us, that this isn't really confirmed
|
|
+ * for this sensor.
|
|
+ */
|
|
+#define MT9P031_PLL_CONTROL 0x10
|
|
+#define MT9P031_PLL_CONTROL_PWROFF 0x0050
|
|
+#define MT9P031_PLL_CONTROL_PWRON 0x0051
|
|
+#define MT9P031_PLL_CONTROL_USEPLL 0x0052
|
|
+#define MT9P031_PLL_CONFIG2 0x11
|
|
+#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f
|
|
+
|
|
+struct mt9m032 {
|
|
+ struct v4l2_subdev subdev;
|
|
+ struct media_pad pad;
|
|
+ struct mt9m032_platform_data *pdata;
|
|
+
|
|
+ unsigned int pix_clock;
|
|
+
|
|
+ struct v4l2_ctrl_handler ctrls;
|
|
+ struct {
|
|
+ struct v4l2_ctrl *hflip;
|
|
+ struct v4l2_ctrl *vflip;
|
|
+ };
|
|
+
|
|
+ struct mutex lock; /* Protects streaming, format, interval and crop */
|
|
+
|
|
+ bool streaming;
|
|
+
|
|
+ struct v4l2_mbus_framefmt format;
|
|
+ struct v4l2_rect crop;
|
|
+ struct v4l2_fract frame_interval;
|
|
+};
|
|
+
|
|
+#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev)
|
|
+#define to_dev(sensor) \
|
|
+ (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev)
|
|
+
|
|
+static int mt9m032_read(struct i2c_client *client, u8 reg)
|
|
+{
|
|
+ return i2c_smbus_read_word_swapped(client, reg);
|
|
+}
|
|
+
|
|
+static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data)
|
|
+{
|
|
+ return i2c_smbus_write_word_swapped(client, reg, data);
|
|
+}
|
|
+
|
|
+static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width)
|
|
+{
|
|
+ unsigned int effective_width;
|
|
+ u32 ns;
|
|
+
|
|
+ effective_width = width + 716; /* empirical value */
|
|
+ ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock);
|
|
+ dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns);
|
|
+ return ns;
|
|
+}
|
|
+
|
|
+static int mt9m032_update_timing(struct mt9m032 *sensor,
|
|
+ struct v4l2_fract *interval)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
|
|
+ struct v4l2_rect *crop = &sensor->crop;
|
|
+ unsigned int min_vblank;
|
|
+ unsigned int vblank;
|
|
+ u32 row_time;
|
|
+
|
|
+ if (!interval)
|
|
+ interval = &sensor->frame_interval;
|
|
+
|
|
+ row_time = mt9m032_row_time(sensor, crop->width);
|
|
+
|
|
+ vblank = div_u64(1000000000ULL * interval->numerator,
|
|
+ (u64)row_time * interval->denominator)
|
|
+ - crop->height;
|
|
+
|
|
+ if (vblank > MT9M032_VBLANK_MAX) {
|
|
+ /* hardware limits to 11 bit values */
|
|
+ interval->denominator = 1000;
|
|
+ interval->numerator =
|
|
+ div_u64((crop->height + MT9M032_VBLANK_MAX) *
|
|
+ (u64)row_time * interval->denominator,
|
|
+ 1000000000ULL);
|
|
+ vblank = div_u64(1000000000ULL * interval->numerator,
|
|
+ (u64)row_time * interval->denominator)
|
|
+ - crop->height;
|
|
+ }
|
|
+ /* enforce minimal 1.6ms blanking time. */
|
|
+ min_vblank = 1600000 / row_time;
|
|
+ vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX);
|
|
+
|
|
+ return mt9m032_write(client, MT9M032_VBLANK, vblank);
|
|
+}
|
|
+
|
|
+static int mt9m032_update_geom_timing(struct mt9m032 *sensor)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
|
|
+ int ret;
|
|
+
|
|
+ ret = mt9m032_write(client, MT9M032_COLUMN_SIZE,
|
|
+ sensor->crop.width - 1);
|
|
+ if (!ret)
|
|
+ ret = mt9m032_write(client, MT9M032_ROW_SIZE,
|
|
+ sensor->crop.height - 1);
|
|
+ if (!ret)
|
|
+ ret = mt9m032_write(client, MT9M032_COLUMN_START,
|
|
+ sensor->crop.left);
|
|
+ if (!ret)
|
|
+ ret = mt9m032_write(client, MT9M032_ROW_START,
|
|
+ sensor->crop.top);
|
|
+ if (!ret)
|
|
+ ret = mt9m032_update_timing(sensor, NULL);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int update_formatter2(struct mt9m032 *sensor, bool streaming)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
|
|
+ u16 reg_val = MT9M032_FORMATTER2_DOUT_EN
|
|
+ | 0x0070; /* parts reserved! */
|
|
+ /* possibly for changing to 14-bit mode */
|
|
+
|
|
+ if (streaming)
|
|
+ reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */
|
|
+
|
|
+ return mt9m032_write(client, MT9M032_FORMATTER2, reg_val);
|
|
+}
|
|
+
|
|
+static int mt9m032_setup_pll(struct mt9m032 *sensor)
|
|
+{
|
|
+ static const struct aptina_pll_limits limits = {
|
|
+ .ext_clock_min = 8000000,
|
|
+ .ext_clock_max = 16500000,
|
|
+ .int_clock_min = 2000000,
|
|
+ .int_clock_max = 24000000,
|
|
+ .out_clock_min = 322000000,
|
|
+ .out_clock_max = 693000000,
|
|
+ .pix_clock_max = 99000000,
|
|
+ .n_min = 1,
|
|
+ .n_max = 64,
|
|
+ .m_min = 16,
|
|
+ .m_max = 255,
|
|
+ .p1_min = 1,
|
|
+ .p1_max = 128,
|
|
+ };
|
|
+
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
|
|
+ struct mt9m032_platform_data *pdata = sensor->pdata;
|
|
+ struct aptina_pll pll;
|
|
+ int ret;
|
|
+
|
|
+ pll.ext_clock = pdata->ext_clock;
|
|
+ pll.pix_clock = pdata->pix_clock;
|
|
+
|
|
+ ret = aptina_pll_calculate(&client->dev, &limits, &pll);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ sensor->pix_clock = pdata->pix_clock;
|
|
+
|
|
+ ret = mt9m032_write(client, MT9M032_PLL_CONFIG1,
|
|
+ (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT)
|
|
+ | (pll.p1 - 1));
|
|
+ if (!ret)
|
|
+ ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1);
|
|
+ if (!ret)
|
|
+ ret = mt9m032_write(client, MT9P031_PLL_CONTROL,
|
|
+ MT9P031_PLL_CONTROL_PWRON |
|
|
+ MT9P031_PLL_CONTROL_USEPLL);
|
|
+ if (!ret) /* more reserved, Continuous, Master Mode */
|
|
+ ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006);
|
|
+ if (!ret) /* Set 14-bit mode, select 7 divider */
|
|
+ ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* -----------------------------------------------------------------------------
|
|
+ * Subdev pad operations
|
|
+ */
|
|
+
|
|
+static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev,
|
|
+ struct v4l2_subdev_fh *fh,
|
|
+ struct v4l2_subdev_mbus_code_enum *code)
|
|
+{
|
|
+ if (code->index != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ code->code = V4L2_MBUS_FMT_Y8_1X8;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev,
|
|
+ struct v4l2_subdev_fh *fh,
|
|
+ struct v4l2_subdev_frame_size_enum *fse)
|
|
+{
|
|
+ if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8)
|
|
+ return -EINVAL;
|
|
+
|
|
+ fse->min_width = MT9M032_COLUMN_SIZE_DEF;
|
|
+ fse->max_width = MT9M032_COLUMN_SIZE_DEF;
|
|
+ fse->min_height = MT9M032_ROW_SIZE_DEF;
|
|
+ fse->max_height = MT9M032_ROW_SIZE_DEF;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * __mt9m032_get_pad_crop() - get crop rect
|
|
+ * @sensor: pointer to the sensor struct
|
|
+ * @fh: file handle for getting the try crop rect from
|
|
+ * @which: select try or active crop rect
|
|
+ *
|
|
+ * Returns a pointer the current active or fh relative try crop rect
|
|
+ */
|
|
+static struct v4l2_rect *
|
|
+__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
|
|
+ enum v4l2_subdev_format_whence which)
|
|
+{
|
|
+ switch (which) {
|
|
+ case V4L2_SUBDEV_FORMAT_TRY:
|
|
+ return v4l2_subdev_get_try_crop(fh, 0);
|
|
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
|
|
+ return &sensor->crop;
|
|
+ default:
|
|
+ return NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * __mt9m032_get_pad_format() - get format
|
|
+ * @sensor: pointer to the sensor struct
|
|
+ * @fh: file handle for getting the try format from
|
|
+ * @which: select try or active format
|
|
+ *
|
|
+ * Returns a pointer the current active or fh relative try format
|
|
+ */
|
|
+static struct v4l2_mbus_framefmt *
|
|
+__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
|
|
+ enum v4l2_subdev_format_whence which)
|
|
+{
|
|
+ switch (which) {
|
|
+ case V4L2_SUBDEV_FORMAT_TRY:
|
|
+ return v4l2_subdev_get_try_format(fh, 0);
|
|
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
|
|
+ return &sensor->format;
|
|
+ default:
|
|
+ return NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int mt9m032_get_pad_format(struct v4l2_subdev *subdev,
|
|
+ struct v4l2_subdev_fh *fh,
|
|
+ struct v4l2_subdev_format *fmt)
|
|
+{
|
|
+ struct mt9m032 *sensor = to_mt9m032(subdev);
|
|
+
|
|
+ mutex_lock(&sensor->lock);
|
|
+ fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
|
|
+ mutex_unlock(&sensor->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mt9m032_set_pad_format(struct v4l2_subdev *subdev,
|
|
+ struct v4l2_subdev_fh *fh,
|
|
+ struct v4l2_subdev_format *fmt)
|
|
+{
|
|
+ struct mt9m032 *sensor = to_mt9m032(subdev);
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&sensor->lock);
|
|
+
|
|
+ if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
|
|
+ ret = -EBUSY;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* Scaling is not supported, the format is thus fixed. */
|
|
+ ret = mt9m032_get_pad_format(subdev, fh, fmt);
|
|
+
|
|
+done:
|
|
+ mutex_lock(&sensor->lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev,
|
|
+ struct v4l2_subdev_fh *fh,
|
|
+ struct v4l2_subdev_crop *crop)
|
|
+{
|
|
+ struct mt9m032 *sensor = to_mt9m032(subdev);
|
|
+
|
|
+ mutex_lock(&sensor->lock);
|
|
+ crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which);
|
|
+ mutex_unlock(&sensor->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev,
|
|
+ struct v4l2_subdev_fh *fh,
|
|
+ struct v4l2_subdev_crop *crop)
|
|
+{
|
|
+ struct mt9m032 *sensor = to_mt9m032(subdev);
|
|
+ struct v4l2_mbus_framefmt *format;
|
|
+ struct v4l2_rect *__crop;
|
|
+ struct v4l2_rect rect;
|
|
+ int ret = 0;
|
|
+
|
|
+ mutex_lock(&sensor->lock);
|
|
+
|
|
+ if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
|
|
+ ret = -EBUSY;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
|
|
+ * pixels to ensure a GRBG Bayer pattern.
|
|
+ */
|
|
+ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN,
|
|
+ MT9M032_COLUMN_START_MAX);
|
|
+ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN,
|
|
+ MT9M032_ROW_START_MAX);
|
|
+ rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN,
|
|
+ MT9M032_COLUMN_SIZE_MAX);
|
|
+ rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN,
|
|
+ MT9M032_ROW_SIZE_MAX);
|
|
+
|
|
+ rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left);
|
|
+ rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top);
|
|
+
|
|
+ __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which);
|
|
+
|
|
+ if (rect.width != __crop->width || rect.height != __crop->height) {
|
|
+ /* Reset the output image size if the crop rectangle size has
|
|
+ * been modified.
|
|
+ */
|
|
+ format = __mt9m032_get_pad_format(sensor, fh, crop->which);
|
|
+ format->width = rect.width;
|
|
+ format->height = rect.height;
|
|
+ }
|
|
+
|
|
+ *__crop = rect;
|
|
+ crop->rect = rect;
|
|
+
|
|
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
|
|
+ ret = mt9m032_update_geom_timing(sensor);
|
|
+
|
|
+done:
|
|
+ mutex_unlock(&sensor->lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev,
|
|
+ struct v4l2_subdev_frame_interval *fi)
|
|
+{
|
|
+ struct mt9m032 *sensor = to_mt9m032(subdev);
|
|
+
|
|
+ mutex_lock(&sensor->lock);
|
|
+ memset(fi, 0, sizeof(*fi));
|
|
+ fi->interval = sensor->frame_interval;
|
|
+ mutex_unlock(&sensor->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev,
|
|
+ struct v4l2_subdev_frame_interval *fi)
|
|
+{
|
|
+ struct mt9m032 *sensor = to_mt9m032(subdev);
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&sensor->lock);
|
|
+
|
|
+ if (sensor->streaming) {
|
|
+ ret = -EBUSY;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* Avoid divisions by 0. */
|
|
+ if (fi->interval.denominator == 0)
|
|
+ fi->interval.denominator = 1;
|
|
+
|
|
+ ret = mt9m032_update_timing(sensor, &fi->interval);
|
|
+ if (!ret)
|
|
+ sensor->frame_interval = fi->interval;
|
|
+
|
|
+done:
|
|
+ mutex_unlock(&sensor->lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming)
|
|
+{
|
|
+ struct mt9m032 *sensor = to_mt9m032(subdev);
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&sensor->lock);
|
|
+ ret = update_formatter2(sensor, streaming);
|
|
+ if (!ret)
|
|
+ sensor->streaming = streaming;
|
|
+ mutex_unlock(&sensor->lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* -----------------------------------------------------------------------------
|
|
+ * V4L2 subdev core operations
|
|
+ */
|
|
+
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+static int mt9m032_g_register(struct v4l2_subdev *sd,
|
|
+ struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct mt9m032 *sensor = to_mt9m032(sd);
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
|
|
+ int val;
|
|
+
|
|
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
|
|
+ return -EINVAL;
|
|
+ if (reg->match.addr != client->addr)
|
|
+ return -ENODEV;
|
|
+
|
|
+ val = mt9m032_read(client, reg->reg);
|
|
+ if (val < 0)
|
|
+ return -EIO;
|
|
+
|
|
+ reg->size = 2;
|
|
+ reg->val = val;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mt9m032_s_register(struct v4l2_subdev *sd,
|
|
+ struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct mt9m032 *sensor = to_mt9m032(sd);
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
|
|
+
|
|
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (reg->match.addr != client->addr)
|
|
+ return -ENODEV;
|
|
+
|
|
+ return mt9m032_write(client, reg->reg, reg->val);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* -----------------------------------------------------------------------------
|
|
+ * V4L2 subdev control operations
|
|
+ */
|
|
+
|
|
+static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
|
|
+ int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT)
|
|
+ | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT)
|
|
+ | MT9M032_READ_MODE2_ROW_BLC
|
|
+ | 0x0007;
|
|
+
|
|
+ return mt9m032_write(client, MT9M032_READ_MODE2, reg_val);
|
|
+}
|
|
+
|
|
+static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val)
|
|
+{
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
|
|
+ int digital_gain_val; /* in 1/8th (0..127) */
|
|
+ int analog_mul; /* 0 or 1 */
|
|
+ int analog_gain_val; /* in 1/16th. (0..63) */
|
|
+ u16 reg_val;
|
|
+
|
|
+ digital_gain_val = 51; /* from setup example */
|
|
+
|
|
+ if (val < 63) {
|
|
+ analog_mul = 0;
|
|
+ analog_gain_val = val;
|
|
+ } else {
|
|
+ analog_mul = 1;
|
|
+ analog_gain_val = val / 2;
|
|
+ }
|
|
+
|
|
+ /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */
|
|
+ /* overall_gain = a_gain * (1 + digital_gain_val / 8) */
|
|
+
|
|
+ reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK)
|
|
+ << MT9M032_GAIN_DIGITAL_SHIFT)
|
|
+ | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT)
|
|
+ | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK);
|
|
+
|
|
+ return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val);
|
|
+}
|
|
+
|
|
+static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) {
|
|
+ /* round because of multiplier used for values >= 63 */
|
|
+ ctrl->val &= ~1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ struct mt9m032 *sensor =
|
|
+ container_of(ctrl->handler, struct mt9m032, ctrls);
|
|
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
|
|
+ int ret;
|
|
+
|
|
+ switch (ctrl->id) {
|
|
+ case V4L2_CID_GAIN:
|
|
+ return mt9m032_set_gain(sensor, ctrl->val);
|
|
+
|
|
+ case V4L2_CID_HFLIP:
|
|
+ /* case V4L2_CID_VFLIP: -- In the same cluster */
|
|
+ return update_read_mode2(sensor, sensor->vflip->val,
|
|
+ sensor->hflip->val);
|
|
+
|
|
+ case V4L2_CID_EXPOSURE:
|
|
+ ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH,
|
|
+ (ctrl->val >> 16) & 0xffff);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW,
|
|
+ ctrl->val & 0xffff);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct v4l2_ctrl_ops mt9m032_ctrl_ops = {
|
|
+ .s_ctrl = mt9m032_set_ctrl,
|
|
+ .try_ctrl = mt9m032_try_ctrl,
|
|
+};
|
|
+
|
|
+/* -------------------------------------------------------------------------- */
|
|
+
|
|
+static const struct v4l2_subdev_core_ops mt9m032_core_ops = {
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+ .g_register = mt9m032_g_register,
|
|
+ .s_register = mt9m032_s_register,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_video_ops mt9m032_video_ops = {
|
|
+ .s_stream = mt9m032_s_stream,
|
|
+ .g_frame_interval = mt9m032_get_frame_interval,
|
|
+ .s_frame_interval = mt9m032_set_frame_interval,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = {
|
|
+ .enum_mbus_code = mt9m032_enum_mbus_code,
|
|
+ .enum_frame_size = mt9m032_enum_frame_size,
|
|
+ .get_fmt = mt9m032_get_pad_format,
|
|
+ .set_fmt = mt9m032_set_pad_format,
|
|
+ .set_crop = mt9m032_set_pad_crop,
|
|
+ .get_crop = mt9m032_get_pad_crop,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_ops mt9m032_ops = {
|
|
+ .core = &mt9m032_core_ops,
|
|
+ .video = &mt9m032_video_ops,
|
|
+ .pad = &mt9m032_pad_ops,
|
|
+};
|
|
+
|
|
+/* -----------------------------------------------------------------------------
|
|
+ * Driver initialization and probing
|
|
+ */
|
|
+
|
|
+static int mt9m032_probe(struct i2c_client *client,
|
|
+ const struct i2c_device_id *devid)
|
|
+{
|
|
+ struct i2c_adapter *adapter = client->adapter;
|
|
+ struct mt9m032 *sensor;
|
|
+ int chip_version;
|
|
+ int ret;
|
|
+
|
|
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
|
|
+ dev_warn(&client->dev,
|
|
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if (!client->dev.platform_data)
|
|
+ return -ENODEV;
|
|
+
|
|
+ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
|
|
+ if (sensor == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mutex_init(&sensor->lock);
|
|
+
|
|
+ sensor->pdata = client->dev.platform_data;
|
|
+
|
|
+ v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops);
|
|
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
+
|
|
+ chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION);
|
|
+ if (chip_version != MT9M032_CHIP_VERSION_VALUE) {
|
|
+ dev_err(&client->dev, "MT9M032 not detected, wrong version "
|
|
+ "0x%04x\n", chip_version);
|
|
+ ret = -ENODEV;
|
|
+ goto error_sensor;
|
|
+ }
|
|
+
|
|
+ dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n",
|
|
+ client->addr);
|
|
+
|
|
+ sensor->frame_interval.numerator = 1;
|
|
+ sensor->frame_interval.denominator = 30;
|
|
+
|
|
+ sensor->crop.left = MT9M032_COLUMN_START_DEF;
|
|
+ sensor->crop.top = MT9M032_ROW_START_DEF;
|
|
+ sensor->crop.width = MT9M032_COLUMN_SIZE_DEF;
|
|
+ sensor->crop.height = MT9M032_ROW_SIZE_DEF;
|
|
+
|
|
+ sensor->format.width = sensor->crop.width;
|
|
+ sensor->format.height = sensor->crop.height;
|
|
+ sensor->format.code = V4L2_MBUS_FMT_Y8_1X8;
|
|
+ sensor->format.field = V4L2_FIELD_NONE;
|
|
+ sensor->format.colorspace = V4L2_COLORSPACE_SRGB;
|
|
+
|
|
+ v4l2_ctrl_handler_init(&sensor->ctrls, 4);
|
|
+
|
|
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
|
|
+ V4L2_CID_GAIN, 0, 127, 1, 64);
|
|
+
|
|
+ sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls,
|
|
+ &mt9m032_ctrl_ops,
|
|
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
|
|
+ sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls,
|
|
+ &mt9m032_ctrl_ops,
|
|
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
|
|
+
|
|
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
|
|
+ V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN,
|
|
+ MT9M032_SHUTTER_WIDTH_MAX, 1,
|
|
+ MT9M032_SHUTTER_WIDTH_DEF);
|
|
+
|
|
+ if (sensor->ctrls.error) {
|
|
+ ret = sensor->ctrls.error;
|
|
+ dev_err(&client->dev, "control initialization error %d\n", ret);
|
|
+ goto error_ctrl;
|
|
+ }
|
|
+
|
|
+ v4l2_ctrl_cluster(2, &sensor->hflip);
|
|
+
|
|
+ sensor->subdev.ctrl_handler = &sensor->ctrls;
|
|
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
|
|
+ ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0);
|
|
+ if (ret < 0)
|
|
+ goto error_ctrl;
|
|
+
|
|
+ ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+ mt9m032_write(client, MT9M032_RESET, 0); /* reset off */
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+
|
|
+ ret = mt9m032_setup_pll(sensor);
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+ usleep_range(10000, 11000);
|
|
+
|
|
+ ret = v4l2_ctrl_handler_setup(&sensor->ctrls);
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+
|
|
+ /* SIZE */
|
|
+ ret = mt9m032_update_geom_timing(sensor);
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+
|
|
+ ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+ ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+ ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+ ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+ if (sensor->pdata->invert_pixclock) {
|
|
+ ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL,
|
|
+ MT9M032_PIX_CLK_CTRL_INV_PIXCLK);
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+ }
|
|
+
|
|
+ ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+ msleep(100);
|
|
+ ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+ msleep(100);
|
|
+ ret = update_formatter2(sensor, false);
|
|
+ if (ret < 0)
|
|
+ goto error_entity;
|
|
+
|
|
+ return ret;
|
|
+
|
|
+error_entity:
|
|
+ media_entity_cleanup(&sensor->subdev.entity);
|
|
+error_ctrl:
|
|
+ v4l2_ctrl_handler_free(&sensor->ctrls);
|
|
+error_sensor:
|
|
+ mutex_destroy(&sensor->lock);
|
|
+ kfree(sensor);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+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_ctrl_handler_free(&sensor->ctrls);
|
|
+ media_entity_cleanup(&sensor->subdev.entity);
|
|
+ mutex_destroy(&sensor->lock);
|
|
+ kfree(sensor);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct i2c_device_id mt9m032_id_table[] = {
|
|
+ { MT9M032_NAME, 0 },
|
|
+ { }
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(i2c, mt9m032_id_table);
|
|
+
|
|
+static struct i2c_driver mt9m032_i2c_driver = {
|
|
+ .driver = {
|
|
+ .name = MT9M032_NAME,
|
|
+ },
|
|
+ .probe = mt9m032_probe,
|
|
+ .remove = mt9m032_remove,
|
|
+ .id_table = mt9m032_id_table,
|
|
+};
|
|
+
|
|
+module_i2c_driver(mt9m032_i2c_driver);
|
|
+
|
|
+MODULE_AUTHOR("Martin Hostettler <martin@neutronstar.dyndns.org>");
|
|
+MODULE_DESCRIPTION("MT9M032 camera sensor driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
Index: linux-3.3.x86_64/include/media/mt9m032.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/include/media/mt9m032.h
|
|
@@ -0,0 +1,36 @@
|
|
+/*
|
|
+ * Driver for MT9M032 CMOS Image Sensor from Micron
|
|
+ *
|
|
+ * Copyright (C) 2010-2011 Lund Engineering
|
|
+ * Contact: Gil Lund <gwlund@lundeng.com>
|
|
+ * Author: Martin Hostettler <martin@neutronstar.dyndns.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License
|
|
+ * version 2 as published by the Free Software Foundation.
|
|
+ *
|
|
+ * 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 MT9M032_H
|
|
+#define MT9M032_H
|
|
+
|
|
+#define MT9M032_NAME "mt9m032"
|
|
+#define MT9M032_I2C_ADDR (0xb8 >> 1)
|
|
+
|
|
+struct mt9m032_platform_data {
|
|
+ u32 ext_clock;
|
|
+ u32 pix_clock;
|
|
+ bool invert_pixclock;
|
|
+
|
|
+};
|
|
+#endif /* MT9M032_H */
|
|
Index: linux-3.3.x86_64/drivers/media/video/v4l2-dev.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/v4l2-dev.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/v4l2-dev.c
|
|
@@ -788,7 +788,7 @@ static void __exit videodev_exit(void)
|
|
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
|
|
}
|
|
|
|
-module_init(videodev_init)
|
|
+subsys_initcall(videodev_init);
|
|
module_exit(videodev_exit)
|
|
|
|
MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
|
|
Index: linux-3.3.x86_64/drivers/media/media-devnode.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/media-devnode.c
|
|
+++ linux-3.3.x86_64/drivers/media/media-devnode.c
|
|
@@ -312,7 +312,7 @@ static void __exit media_devnode_exit(vo
|
|
unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
|
|
}
|
|
|
|
-module_init(media_devnode_init)
|
|
+subsys_initcall(media_devnode_init);
|
|
module_exit(media_devnode_exit)
|
|
|
|
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
|
|
Index: linux-3.3.x86_64/drivers/media/rc/mceusb.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/mceusb.c
|
|
+++ linux-3.3.x86_64/drivers/media/rc/mceusb.c
|
|
@@ -361,6 +361,8 @@ static struct usb_device_id mceusb_dev_t
|
|
{ USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
|
|
/* Formosa Industrial Computing */
|
|
{ USB_DEVICE(VENDOR_FORMOSA, 0xe03e) },
|
|
+ /* Formosa Industrial Computing */
|
|
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) },
|
|
/* Fintek eHome Infrared Transceiver (HP branded) */
|
|
{ USB_DEVICE(VENDOR_FINTEK, 0x5168) },
|
|
/* Fintek eHome Infrared Transceiver */
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/au8522_dig.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/frontends/au8522_dig.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/au8522_dig.c
|
|
@@ -604,6 +604,11 @@ static int au8522_set_frontend(struct dv
|
|
|
|
au8522_enable_modulation(fe, c->modulation);
|
|
|
|
+ /* Allow the tuner to settle */
|
|
+ msleep(100);
|
|
+
|
|
+ au8522_enable_modulation(fe, c->modulation);
|
|
+
|
|
state->current_frequency = c->frequency;
|
|
|
|
return 0;
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/dib0700_core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/dib0700_core.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/dib0700_core.c
|
|
@@ -677,11 +677,9 @@ static void dib0700_rc_urb_completion(st
|
|
u8 toggle;
|
|
|
|
deb_info("%s()\n", __func__);
|
|
- if (d == NULL)
|
|
- return;
|
|
-
|
|
if (d->rc_dev == NULL) {
|
|
/* This will occur if disable_rc_polling=1 */
|
|
+ kfree(purb->transfer_buffer);
|
|
usb_free_urb(purb);
|
|
return;
|
|
}
|
|
@@ -690,6 +688,7 @@ static void dib0700_rc_urb_completion(st
|
|
|
|
if (purb->status < 0) {
|
|
deb_info("discontinuing polling\n");
|
|
+ kfree(purb->transfer_buffer);
|
|
usb_free_urb(purb);
|
|
return;
|
|
}
|
|
@@ -784,8 +783,11 @@ int dib0700_rc_setup(struct dvb_usb_devi
|
|
dib0700_rc_urb_completion, d);
|
|
|
|
ret = usb_submit_urb(purb, GFP_ATOMIC);
|
|
- if (ret)
|
|
+ if (ret) {
|
|
err("rc submit urb failed\n");
|
|
+ kfree(purb->transfer_buffer);
|
|
+ usb_free_urb(purb);
|
|
+ }
|
|
|
|
return ret;
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/af9015.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/af9015.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/af9015.c
|
|
@@ -1164,6 +1164,41 @@ static int af9015_af9013_sleep(struct dv
|
|
return ret;
|
|
}
|
|
|
|
+/* override tuner callbacks for resource locking */
|
|
+static int af9015_tuner_init(struct dvb_frontend *fe)
|
|
+{
|
|
+ int ret;
|
|
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
|
|
+ struct af9015_state *priv = adap->dev->priv;
|
|
+
|
|
+ if (mutex_lock_interruptible(&adap->dev->usb_mutex))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ ret = priv->tuner_init[adap->id](fe);
|
|
+
|
|
+ mutex_unlock(&adap->dev->usb_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* override tuner callbacks for resource locking */
|
|
+static int af9015_tuner_sleep(struct dvb_frontend *fe)
|
|
+{
|
|
+ int ret;
|
|
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
|
|
+ struct af9015_state *priv = adap->dev->priv;
|
|
+
|
|
+ if (mutex_lock_interruptible(&adap->dev->usb_mutex))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ ret = priv->tuner_sleep[adap->id](fe);
|
|
+
|
|
+ mutex_unlock(&adap->dev->usb_mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
|
|
{
|
|
int ret;
|
|
@@ -1283,6 +1318,7 @@ static struct mxl5007t_config af9015_mxl
|
|
static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
|
|
{
|
|
int ret;
|
|
+ struct af9015_state *state = adap->dev->priv;
|
|
deb_info("%s:\n", __func__);
|
|
|
|
switch (af9015_af9013_config[adap->id].tuner) {
|
|
@@ -1340,6 +1376,19 @@ static int af9015_tuner_attach(struct dv
|
|
err("Unknown tuner id:%d",
|
|
af9015_af9013_config[adap->id].tuner);
|
|
}
|
|
+
|
|
+ if (adap->fe_adap[0].fe->ops.tuner_ops.init) {
|
|
+ state->tuner_init[adap->id] =
|
|
+ adap->fe_adap[0].fe->ops.tuner_ops.init;
|
|
+ adap->fe_adap[0].fe->ops.tuner_ops.init = af9015_tuner_init;
|
|
+ }
|
|
+
|
|
+ if (adap->fe_adap[0].fe->ops.tuner_ops.sleep) {
|
|
+ state->tuner_sleep[adap->id] =
|
|
+ adap->fe_adap[0].fe->ops.tuner_ops.sleep;
|
|
+ adap->fe_adap[0].fe->ops.tuner_ops.sleep = af9015_tuner_sleep;
|
|
+ }
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/af9015.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/af9015.h
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/af9015.h
|
|
@@ -108,6 +108,8 @@ struct af9015_state {
|
|
int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status);
|
|
int (*init[2]) (struct dvb_frontend *fe);
|
|
int (*sleep[2]) (struct dvb_frontend *fe);
|
|
+ int (*tuner_init[2]) (struct dvb_frontend *fe);
|
|
+ int (*tuner_sleep[2]) (struct dvb_frontend *fe);
|
|
};
|
|
|
|
struct af9015_config {
|
|
Index: linux-3.3.x86_64/drivers/media/rc/rc-main.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/rc/rc-main.c
|
|
+++ linux-3.3.x86_64/drivers/media/rc/rc-main.c
|
|
@@ -1029,6 +1029,7 @@ EXPORT_SYMBOL_GPL(rc_free_device);
|
|
|
|
int rc_register_device(struct rc_dev *dev)
|
|
{
|
|
+ static bool raw_init = false; /* raw decoders loaded? */
|
|
static atomic_t devno = ATOMIC_INIT(0);
|
|
struct rc_map *rc_map;
|
|
const char *path;
|
|
@@ -1103,6 +1104,12 @@ int rc_register_device(struct rc_dev *de
|
|
kfree(path);
|
|
|
|
if (dev->driver_type == RC_DRIVER_IR_RAW) {
|
|
+ /* Load raw decoders, if they aren't already */
|
|
+ if (!raw_init) {
|
|
+ IR_dprintk(1, "Loading raw decoders\n");
|
|
+ ir_raw_init();
|
|
+ raw_init = true;
|
|
+ }
|
|
rc = ir_raw_event_register(dev);
|
|
if (rc < 0)
|
|
goto out_input;
|
|
@@ -1176,8 +1183,6 @@ static int __init rc_core_init(void)
|
|
return rc;
|
|
}
|
|
|
|
- /* Initialize/load the decoders/keymap code that will be used */
|
|
- ir_raw_init();
|
|
rc_map_register(&empty_map);
|
|
|
|
return 0;
|
|
Index: linux-3.3.x86_64/drivers/media/video/soc_camera.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/soc_camera.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/soc_camera.c
|
|
@@ -526,10 +526,6 @@ static int soc_camera_open(struct file *
|
|
},
|
|
};
|
|
|
|
- ret = soc_camera_power_on(icd, icl);
|
|
- if (ret < 0)
|
|
- goto epower;
|
|
-
|
|
/* The camera could have been already on, try to reset */
|
|
if (icl->reset)
|
|
icl->reset(icd->pdev);
|
|
@@ -540,6 +536,10 @@ static int soc_camera_open(struct file *
|
|
goto eiciadd;
|
|
}
|
|
|
|
+ ret = soc_camera_power_on(icd, icl);
|
|
+ if (ret < 0)
|
|
+ goto epower;
|
|
+
|
|
pm_runtime_enable(&icd->vdev->dev);
|
|
ret = pm_runtime_resume(&icd->vdev->dev);
|
|
if (ret < 0 && ret != -ENOSYS)
|
|
@@ -578,10 +578,10 @@ einitvb:
|
|
esfmt:
|
|
pm_runtime_disable(&icd->vdev->dev);
|
|
eresume:
|
|
- ici->ops->remove(icd);
|
|
-eiciadd:
|
|
soc_camera_power_off(icd, icl);
|
|
epower:
|
|
+ ici->ops->remove(icd);
|
|
+eiciadd:
|
|
icd->use_count--;
|
|
module_put(ici->ops->owner);
|
|
|
|
@@ -1050,6 +1050,14 @@ static int soc_camera_probe(struct soc_c
|
|
if (ret < 0)
|
|
goto ereg;
|
|
|
|
+ /* The camera could have been already on, try to reset */
|
|
+ if (icl->reset)
|
|
+ icl->reset(icd->pdev);
|
|
+
|
|
+ ret = ici->ops->add(icd);
|
|
+ if (ret < 0)
|
|
+ goto eadd;
|
|
+
|
|
/*
|
|
* This will not yet call v4l2_subdev_core_ops::s_power(1), because the
|
|
* subdevice has not been initialised yet. We'll have to call it once
|
|
@@ -1060,14 +1068,6 @@ static int soc_camera_probe(struct soc_c
|
|
if (ret < 0)
|
|
goto epower;
|
|
|
|
- /* The camera could have been already on, try to reset */
|
|
- if (icl->reset)
|
|
- icl->reset(icd->pdev);
|
|
-
|
|
- ret = ici->ops->add(icd);
|
|
- if (ret < 0)
|
|
- goto eadd;
|
|
-
|
|
/* Must have icd->vdev before registering the device */
|
|
ret = video_dev_create(icd);
|
|
if (ret < 0)
|
|
@@ -1165,10 +1165,10 @@ eadddev:
|
|
video_device_release(icd->vdev);
|
|
icd->vdev = NULL;
|
|
evdc:
|
|
- ici->ops->remove(icd);
|
|
-eadd:
|
|
soc_camera_power_off(icd, icl);
|
|
epower:
|
|
+ ici->ops->remove(icd);
|
|
+eadd:
|
|
regulator_bulk_free(icl->num_regulators, icl->regulators);
|
|
ereg:
|
|
v4l2_ctrl_handler_free(&icd->ctrl_handler);
|
|
Index: linux-3.3.x86_64/drivers/media/video/sh_mobile_ceu_camera.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/sh_mobile_ceu_camera.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/sh_mobile_ceu_camera.c
|
|
@@ -112,6 +112,10 @@ struct sh_mobile_ceu_dev {
|
|
|
|
u32 cflcr;
|
|
|
|
+ /* static max sizes either from platform data or default */
|
|
+ int max_width;
|
|
+ int max_height;
|
|
+
|
|
enum v4l2_field field;
|
|
int sequence;
|
|
|
|
@@ -1081,7 +1085,15 @@ static int sh_mobile_ceu_get_formats(str
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
- while ((mf.width > 2560 || mf.height > 1920) && shift < 4) {
|
|
+ /*
|
|
+ * All currently existing CEU implementations support 2560x1920
|
|
+ * or larger frames. If the sensor is proposing too big a frame,
|
|
+ * don't bother with possibly supportred by the CEU larger
|
|
+ * sizes, just try VGA multiples. If needed, this can be
|
|
+ * adjusted in the future.
|
|
+ */
|
|
+ while ((mf.width > pcdev->max_width ||
|
|
+ mf.height > pcdev->max_height) && shift < 4) {
|
|
/* Try 2560x1920, 1280x960, 640x480, 320x240 */
|
|
mf.width = 2560 >> shift;
|
|
mf.height = 1920 >> shift;
|
|
@@ -1377,6 +1389,8 @@ static int client_s_crop(struct soc_came
|
|
static int client_s_fmt(struct soc_camera_device *icd,
|
|
struct v4l2_mbus_framefmt *mf, bool ceu_can_scale)
|
|
{
|
|
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
|
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
|
struct sh_mobile_ceu_cam *cam = icd->host_priv;
|
|
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
|
struct device *dev = icd->parent;
|
|
@@ -1410,8 +1424,8 @@ static int client_s_fmt(struct soc_camer
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
- max_width = min(cap.bounds.width, 2560);
|
|
- max_height = min(cap.bounds.height, 1920);
|
|
+ max_width = min(cap.bounds.width, pcdev->max_width);
|
|
+ max_height = min(cap.bounds.height, pcdev->max_height);
|
|
|
|
/* Camera set a format, but geometry is not precise, try to improve */
|
|
tmp_w = mf->width;
|
|
@@ -1551,7 +1565,7 @@ static int sh_mobile_ceu_set_crop(struct
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
- if (mf.width > 2560 || mf.height > 1920)
|
|
+ if (mf.width > pcdev->max_width || mf.height > pcdev->max_height)
|
|
return -EINVAL;
|
|
|
|
/* 4. Calculate camera scales */
|
|
@@ -1834,6 +1848,8 @@ static int sh_mobile_ceu_set_fmt(struct
|
|
static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
|
|
struct v4l2_format *f)
|
|
{
|
|
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
|
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
|
const struct soc_camera_format_xlate *xlate;
|
|
struct v4l2_pix_format *pix = &f->fmt.pix;
|
|
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
|
@@ -1854,8 +1870,8 @@ static int sh_mobile_ceu_try_fmt(struct
|
|
/* FIXME: calculate using depth and bus width */
|
|
|
|
/* CFSZR requires height and width to be 4-pixel aligned */
|
|
- v4l_bound_align_image(&pix->width, 2, 2560, 2,
|
|
- &pix->height, 4, 1920, 2, 0);
|
|
+ v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2,
|
|
+ &pix->height, 4, pcdev->max_height, 2, 0);
|
|
|
|
width = pix->width;
|
|
height = pix->height;
|
|
@@ -1890,8 +1906,8 @@ static int sh_mobile_ceu_try_fmt(struct
|
|
* requested a bigger rectangle, it will not return a
|
|
* smaller one.
|
|
*/
|
|
- mf.width = 2560;
|
|
- mf.height = 1920;
|
|
+ mf.width = pcdev->max_width;
|
|
+ mf.height = pcdev->max_height;
|
|
ret = v4l2_device_call_until_err(sd->v4l2_dev,
|
|
soc_camera_grp_id(icd), video,
|
|
try_mbus_fmt, &mf);
|
|
@@ -2082,6 +2098,9 @@ static int __devinit sh_mobile_ceu_probe
|
|
goto exit_kfree;
|
|
}
|
|
|
|
+ pcdev->max_width = pcdev->pdata->max_width ? : 2560;
|
|
+ pcdev->max_height = pcdev->pdata->max_height ? : 1920;
|
|
+
|
|
base = ioremap_nocache(res->start, resource_size(res));
|
|
if (!base) {
|
|
err = -ENXIO;
|
|
Index: linux-3.3.x86_64/include/media/sh_mobile_ceu.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/include/media/sh_mobile_ceu.h
|
|
+++ linux-3.3.x86_64/include/media/sh_mobile_ceu.h
|
|
@@ -18,6 +18,8 @@ struct sh_mobile_ceu_companion {
|
|
|
|
struct sh_mobile_ceu_info {
|
|
unsigned long flags;
|
|
+ int max_width;
|
|
+ int max_height;
|
|
struct sh_mobile_ceu_companion *csi2;
|
|
};
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/pxa_camera.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/pxa_camera.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/pxa_camera.c
|
|
@@ -921,12 +921,12 @@ static void pxa_camera_activate(struct p
|
|
/* "Safe default" - 13MHz */
|
|
recalculate_fifo_timeout(pcdev, 13000000);
|
|
|
|
- clk_enable(pcdev->clk);
|
|
+ clk_prepare_enable(pcdev->clk);
|
|
}
|
|
|
|
static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
|
|
{
|
|
- clk_disable(pcdev->clk);
|
|
+ clk_disable_unprepare(pcdev->clk);
|
|
}
|
|
|
|
static irqreturn_t pxa_camera_irq(int irq, void *data)
|
|
Index: linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-i2c.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/em28xx/em28xx-i2c.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/em28xx/em28xx-i2c.c
|
|
@@ -41,14 +41,6 @@ static unsigned int i2c_debug;
|
|
module_param(i2c_debug, int, 0644);
|
|
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
|
|
|
|
-
|
|
-#define dprintk1(lvl, fmt, args...) \
|
|
-do { \
|
|
- if (i2c_debug >= lvl) { \
|
|
- printk(fmt, ##args); \
|
|
- } \
|
|
-} while (0)
|
|
-
|
|
#define dprintk2(lvl, fmt, args...) \
|
|
do { \
|
|
if (i2c_debug >= lvl) { \
|
|
Index: linux-3.3.x86_64/drivers/media/video/marvell-ccic/mcam-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/marvell-ccic/mcam-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/marvell-ccic/mcam-core.c
|
|
@@ -509,11 +509,17 @@ static void mcam_sg_next_buffer(struct m
|
|
|
|
buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
|
|
list_del_init(&buf->queue);
|
|
+ /*
|
|
+ * Very Bad Not Good Things happen if you don't clear
|
|
+ * C1_DESC_ENA before making any descriptor changes.
|
|
+ */
|
|
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA);
|
|
mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa);
|
|
mcam_reg_write(cam, REG_DESC_LEN_Y,
|
|
buf->dma_desc_nent*sizeof(struct mcam_dma_desc));
|
|
mcam_reg_write(cam, REG_DESC_LEN_U, 0);
|
|
mcam_reg_write(cam, REG_DESC_LEN_V, 0);
|
|
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
|
|
cam->vb_bufs[0] = buf;
|
|
}
|
|
|
|
@@ -533,7 +539,6 @@ static void mcam_ctlr_dma_sg(struct mcam
|
|
|
|
mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_3WORD);
|
|
mcam_sg_next_buffer(cam);
|
|
- mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
|
|
cam->nbufs = 3;
|
|
}
|
|
|
|
@@ -556,17 +561,16 @@ static void mcam_dma_sg_done(struct mcam
|
|
struct mcam_vb_buffer *buf = cam->vb_bufs[0];
|
|
|
|
/*
|
|
- * Very Bad Not Good Things happen if you don't clear
|
|
- * C1_DESC_ENA before making any descriptor changes.
|
|
+ * If we're no longer supposed to be streaming, don't do anything.
|
|
*/
|
|
- mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA);
|
|
+ if (cam->state != S_STREAMING)
|
|
+ return;
|
|
/*
|
|
* If we have another buffer available, put it in and
|
|
* restart the engine.
|
|
*/
|
|
if (!list_empty(&cam->buffers)) {
|
|
mcam_sg_next_buffer(cam);
|
|
- mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
|
|
mcam_ctlr_start(cam);
|
|
/*
|
|
* Otherwise set CF_SG_RESTART and the controller will
|
|
@@ -737,7 +741,14 @@ static void mcam_ctlr_stop_dma(struct mc
|
|
mcam_ctlr_stop(cam);
|
|
cam->state = S_IDLE;
|
|
spin_unlock_irqrestore(&cam->dev_lock, flags);
|
|
- msleep(40);
|
|
+ /*
|
|
+ * This is a brutally long sleep, but experience shows that
|
|
+ * it can take the controller a while to get the message that
|
|
+ * it needs to stop grabbing frames. In particular, we can
|
|
+ * sometimes (on mmp) get a frame at the end WITHOUT the
|
|
+ * start-of-frame indication.
|
|
+ */
|
|
+ msleep(150);
|
|
if (test_bit(CF_DMA_ACTIVE, &cam->flags))
|
|
cam_err(cam, "Timeout waiting for DMA to end\n");
|
|
/* This would be bad news - what now? */
|
|
@@ -880,6 +891,7 @@ static int mcam_read_setup(struct mcam_c
|
|
* Turn it loose.
|
|
*/
|
|
spin_lock_irqsave(&cam->dev_lock, flags);
|
|
+ clear_bit(CF_DMA_ACTIVE, &cam->flags);
|
|
mcam_reset_buffers(cam);
|
|
mcam_ctlr_irq_enable(cam);
|
|
cam->state = S_STREAMING;
|
|
@@ -922,7 +934,7 @@ static void mcam_vb_buf_queue(struct vb2
|
|
spin_lock_irqsave(&cam->dev_lock, flags);
|
|
start = (cam->state == S_BUFWAIT) && !list_empty(&cam->buffers);
|
|
list_add(&mvb->queue, &cam->buffers);
|
|
- if (test_bit(CF_SG_RESTART, &cam->flags))
|
|
+ if (cam->state == S_STREAMING && test_bit(CF_SG_RESTART, &cam->flags))
|
|
mcam_sg_restart(cam);
|
|
spin_unlock_irqrestore(&cam->dev_lock, flags);
|
|
if (start)
|
|
@@ -1555,15 +1567,12 @@ static int mcam_v4l_release(struct file
|
|
{
|
|
struct mcam_camera *cam = filp->private_data;
|
|
|
|
- cam_err(cam, "Release, %d frames, %d singles, %d delivered\n", frames,
|
|
+ cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", frames,
|
|
singles, delivered);
|
|
mutex_lock(&cam->s_mutex);
|
|
(cam->users)--;
|
|
- if (filp == cam->owner) {
|
|
- mcam_ctlr_stop_dma(cam);
|
|
- cam->owner = NULL;
|
|
- }
|
|
if (cam->users == 0) {
|
|
+ mcam_ctlr_stop_dma(cam);
|
|
mcam_cleanup_vb2(cam);
|
|
mcam_ctlr_power_down(cam);
|
|
if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
|
|
@@ -1688,6 +1697,8 @@ int mccic_irq(struct mcam_camera *cam, u
|
|
if (irqs & (IRQ_EOF0 << frame)) {
|
|
mcam_frame_complete(cam, frame);
|
|
handled = 1;
|
|
+ if (cam->buffer_mode == B_DMA_sg)
|
|
+ break;
|
|
}
|
|
/*
|
|
* If a frame starts, note that we have DMA active. This
|
|
Index: linux-3.3.x86_64/drivers/media/video/marvell-ccic/mcam-core.h
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/marvell-ccic/mcam-core.h
|
|
+++ linux-3.3.x86_64/drivers/media/video/marvell-ccic/mcam-core.h
|
|
@@ -107,7 +107,6 @@ struct mcam_camera {
|
|
enum mcam_state state;
|
|
unsigned long flags; /* Buffer status, mainly (dev_lock) */
|
|
int users; /* How many open FDs */
|
|
- struct file *owner; /* Who has data access (v4l2) */
|
|
|
|
/*
|
|
* Subsystem structures.
|
|
Index: linux-3.3.x86_64/drivers/media/video/marvell-ccic/mmp-driver.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/marvell-ccic/mmp-driver.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/marvell-ccic/mmp-driver.c
|
|
@@ -106,6 +106,13 @@ static struct mmp_camera *mmpcam_find_de
|
|
/*
|
|
* Power control.
|
|
*/
|
|
+static void mmpcam_power_up_ctlr(struct mmp_camera *cam)
|
|
+{
|
|
+ iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR);
|
|
+ iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR);
|
|
+ mdelay(1);
|
|
+}
|
|
+
|
|
static void mmpcam_power_up(struct mcam_camera *mcam)
|
|
{
|
|
struct mmp_camera *cam = mcam_to_cam(mcam);
|
|
@@ -113,9 +120,7 @@ static void mmpcam_power_up(struct mcam_
|
|
/*
|
|
* Turn on power and clocks to the controller.
|
|
*/
|
|
- iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR);
|
|
- iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR);
|
|
- mdelay(1);
|
|
+ mmpcam_power_up_ctlr(cam);
|
|
/*
|
|
* Provide power to the sensor.
|
|
*/
|
|
@@ -335,7 +340,7 @@ static int mmpcam_resume(struct platform
|
|
* touch a register even if nothing was active before; trust
|
|
* me, it's better this way.
|
|
*/
|
|
- mmpcam_power_up(&cam->mcam);
|
|
+ mmpcam_power_up_ctlr(cam);
|
|
return mccic_resume(&cam->mcam);
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/mxl111sf.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/dvb/dvb-usb/mxl111sf.c
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/mxl111sf.c
|
|
@@ -340,7 +340,6 @@ static int mxl111sf_ep6_streaming_ctrl(s
|
|
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);
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/gspca/ov534_9.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/gspca/ov534_9.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/gspca/ov534_9.c
|
|
@@ -1107,16 +1107,34 @@ static void setbrightness(struct gspca_d
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
u8 val;
|
|
+ s8 sval;
|
|
|
|
if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
|
|
return;
|
|
- val = sd->ctrls[BRIGHTNESS].val;
|
|
- if (val < 8)
|
|
- val = 15 - val; /* f .. 8 */
|
|
- else
|
|
- val = val - 8; /* 0 .. 7 */
|
|
- sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */
|
|
- 0x0f | (val << 4));
|
|
+ if (sd->sensor == SENSOR_OV562x) {
|
|
+ sval = sd->ctrls[BRIGHTNESS].val;
|
|
+ val = 0x76;
|
|
+ val += sval;
|
|
+ sccb_write(gspca_dev, 0x24, val);
|
|
+ val = 0x6a;
|
|
+ val += sval;
|
|
+ sccb_write(gspca_dev, 0x25, val);
|
|
+ if (sval < -40)
|
|
+ val = 0x71;
|
|
+ else if (sval < 20)
|
|
+ val = 0x94;
|
|
+ else
|
|
+ val = 0xe6;
|
|
+ sccb_write(gspca_dev, 0x26, val);
|
|
+ } else {
|
|
+ val = sd->ctrls[BRIGHTNESS].val;
|
|
+ if (val < 8)
|
|
+ val = 15 - val; /* f .. 8 */
|
|
+ else
|
|
+ val = val - 8; /* 0 .. 7 */
|
|
+ sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */
|
|
+ 0x0f | (val << 4));
|
|
+ }
|
|
}
|
|
|
|
static void setcontrast(struct gspca_dev *gspca_dev)
|
|
@@ -1339,7 +1357,16 @@ static int sd_init(struct gspca_dev *gsp
|
|
reg_w(gspca_dev, 0x56, 0x17);
|
|
} else if ((sensor_id & 0xfff0) == 0x5620) {
|
|
sd->sensor = SENSOR_OV562x;
|
|
-
|
|
+ gspca_dev->ctrl_dis = (1 << CONTRAST) |
|
|
+ (1 << AUTOGAIN) |
|
|
+ (1 << EXPOSURE) |
|
|
+ (1 << SHARPNESS) |
|
|
+ (1 << SATUR) |
|
|
+ (1 << LIGHTFREQ);
|
|
+
|
|
+ sd->ctrls[BRIGHTNESS].min = -90;
|
|
+ sd->ctrls[BRIGHTNESS].max = 90;
|
|
+ sd->ctrls[BRIGHTNESS].def = 0;
|
|
gspca_dev->cam.cam_mode = ov562x_mode;
|
|
gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode);
|
|
|
|
@@ -1360,8 +1387,12 @@ static int sd_start(struct gspca_dev *gs
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
- if (sd->sensor == SENSOR_OV971x || sd->sensor == SENSOR_OV562x)
|
|
+ if (sd->sensor == SENSOR_OV971x)
|
|
return gspca_dev->usb_err;
|
|
+ else if (sd->sensor == SENSOR_OV562x) {
|
|
+ setbrightness(gspca_dev);
|
|
+ return gspca_dev->usb_err;
|
|
+ }
|
|
switch (gspca_dev->curr_mode) {
|
|
case QVGA_MODE: /* 320x240 */
|
|
sccb_w_array(gspca_dev, ov965x_start_1_vga,
|
|
Index: linux-3.3.x86_64/drivers/media/video/gspca/sn9c20x.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/gspca/sn9c20x.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/gspca/sn9c20x.c
|
|
@@ -1,5 +1,7 @@
|
|
/*
|
|
* Sonix sn9c201 sn9c202 library
|
|
+ *
|
|
+ * Copyright (C) 2012 Jean-Francois Moine <http://moinejf.free.fr>
|
|
* Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com>
|
|
* Copyright (C) 2009 Brian Johnson <brijohn@gmail.com>
|
|
*
|
|
@@ -33,8 +35,6 @@ MODULE_AUTHOR("Brian Johnson <brijohn@gm
|
|
MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
-#define MODULE_NAME "sn9c20x"
|
|
-
|
|
/*
|
|
* Pixel format private data
|
|
*/
|
|
@@ -66,10 +66,37 @@ 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 work_struct work;
|
|
+ struct workqueue_struct *work_thread;
|
|
+
|
|
+ u32 pktsz; /* (used by pkt_scan) */
|
|
+ u16 npkt;
|
|
+ s8 nchg;
|
|
+ u8 fmt; /* (used for JPEG QTAB update */
|
|
+
|
|
#define MIN_AVG_LUM 80
|
|
#define MAX_AVG_LUM 130
|
|
atomic_t avg_lum;
|
|
@@ -77,31 +104,18 @@ struct sd {
|
|
u8 older_step;
|
|
u8 exposure_step;
|
|
|
|
- u8 brightness;
|
|
- u8 contrast;
|
|
- u8 saturation;
|
|
- s16 hue;
|
|
- u8 gamma;
|
|
- u8 red;
|
|
- u8 blue;
|
|
-
|
|
- u8 hflip;
|
|
- u8 vflip;
|
|
- u8 gain;
|
|
- u16 exposure;
|
|
- u8 auto_exposure;
|
|
-
|
|
u8 i2c_addr;
|
|
u8 sensor;
|
|
u8 hstart;
|
|
u8 vstart;
|
|
|
|
u8 jpeg_hdr[JPEG_HDR_SZ];
|
|
- u8 quality;
|
|
|
|
u8 flags;
|
|
};
|
|
|
|
+static void qual_upd(struct work_struct *work);
|
|
+
|
|
struct i2c_reg_u8 {
|
|
u8 reg;
|
|
u8 val;
|
|
@@ -112,31 +126,6 @@ struct i2c_reg_u16 {
|
|
u16 val;
|
|
};
|
|
|
|
-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_setcontrast(struct gspca_dev *gspca_dev, s32 val);
|
|
-static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val);
|
|
-static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val);
|
|
-static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val);
|
|
-static int sd_sethue(struct gspca_dev *gspca_dev, s32 val);
|
|
-static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val);
|
|
-static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val);
|
|
-static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val);
|
|
-static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val);
|
|
-static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val);
|
|
-static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val);
|
|
-static int sd_getbluebalance(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_sethflip(struct gspca_dev *gspca_dev, s32 val);
|
|
-static int sd_gethflip(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 int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val);
|
|
-static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val);
|
|
-
|
|
static const struct dmi_system_id flip_dmi_table[] = {
|
|
{
|
|
.ident = "MSI MS-1034",
|
|
@@ -177,9 +166,16 @@ static const struct dmi_system_id flip_d
|
|
{}
|
|
};
|
|
|
|
-static const struct ctrl sd_ctrls[] = {
|
|
- {
|
|
-#define BRIGHTNESS_IDX 0
|
|
+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,
|
|
@@ -187,14 +183,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 0xff,
|
|
.step = 1,
|
|
-#define BRIGHTNESS_DEFAULT 0x7f
|
|
- .default_value = BRIGHTNESS_DEFAULT,
|
|
+ .default_value = 0x7f
|
|
},
|
|
- .set = sd_setbrightness,
|
|
- .get = sd_getbrightness,
|
|
+ .set_control = set_cmatrix
|
|
},
|
|
- {
|
|
-#define CONTRAST_IDX 1
|
|
+[CONTRAST] = {
|
|
{
|
|
.id = V4L2_CID_CONTRAST,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -202,14 +195,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 0xff,
|
|
.step = 1,
|
|
-#define CONTRAST_DEFAULT 0x7f
|
|
- .default_value = CONTRAST_DEFAULT,
|
|
+ .default_value = 0x7f
|
|
},
|
|
- .set = sd_setcontrast,
|
|
- .get = sd_getcontrast,
|
|
+ .set_control = set_cmatrix
|
|
},
|
|
- {
|
|
-#define SATURATION_IDX 2
|
|
+[SATURATION] = {
|
|
{
|
|
.id = V4L2_CID_SATURATION,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -217,14 +207,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 0xff,
|
|
.step = 1,
|
|
-#define SATURATION_DEFAULT 0x7f
|
|
- .default_value = SATURATION_DEFAULT,
|
|
+ .default_value = 0x7f
|
|
},
|
|
- .set = sd_setsaturation,
|
|
- .get = sd_getsaturation,
|
|
+ .set_control = set_cmatrix
|
|
},
|
|
- {
|
|
-#define HUE_IDX 3
|
|
+[HUE] = {
|
|
{
|
|
.id = V4L2_CID_HUE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -232,14 +219,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = -180,
|
|
.maximum = 180,
|
|
.step = 1,
|
|
-#define HUE_DEFAULT 0
|
|
- .default_value = HUE_DEFAULT,
|
|
+ .default_value = 0
|
|
},
|
|
- .set = sd_sethue,
|
|
- .get = sd_gethue,
|
|
+ .set_control = set_cmatrix
|
|
},
|
|
- {
|
|
-#define GAMMA_IDX 4
|
|
+[GAMMA] = {
|
|
{
|
|
.id = V4L2_CID_GAMMA,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -247,14 +231,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 0xff,
|
|
.step = 1,
|
|
-#define GAMMA_DEFAULT 0x10
|
|
- .default_value = GAMMA_DEFAULT,
|
|
+ .default_value = 0x10
|
|
},
|
|
- .set = sd_setgamma,
|
|
- .get = sd_getgamma,
|
|
+ .set_control = set_gamma
|
|
},
|
|
- {
|
|
-#define BLUE_IDX 5
|
|
+[BLUE] = {
|
|
{
|
|
.id = V4L2_CID_BLUE_BALANCE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -262,14 +243,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 0x7f,
|
|
.step = 1,
|
|
-#define BLUE_DEFAULT 0x28
|
|
- .default_value = BLUE_DEFAULT,
|
|
+ .default_value = 0x28
|
|
},
|
|
- .set = sd_setbluebalance,
|
|
- .get = sd_getbluebalance,
|
|
+ .set_control = set_redblue
|
|
},
|
|
- {
|
|
-#define RED_IDX 6
|
|
+[RED] = {
|
|
{
|
|
.id = V4L2_CID_RED_BALANCE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -277,14 +255,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 0x7f,
|
|
.step = 1,
|
|
-#define RED_DEFAULT 0x28
|
|
- .default_value = RED_DEFAULT,
|
|
+ .default_value = 0x28
|
|
},
|
|
- .set = sd_setredbalance,
|
|
- .get = sd_getredbalance,
|
|
+ .set_control = set_redblue
|
|
},
|
|
- {
|
|
-#define HFLIP_IDX 7
|
|
+[HFLIP] = {
|
|
{
|
|
.id = V4L2_CID_HFLIP,
|
|
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
@@ -292,14 +267,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 1,
|
|
.step = 1,
|
|
-#define HFLIP_DEFAULT 0
|
|
- .default_value = HFLIP_DEFAULT,
|
|
+ .default_value = 0,
|
|
},
|
|
- .set = sd_sethflip,
|
|
- .get = sd_gethflip,
|
|
+ .set_control = set_hvflip
|
|
},
|
|
- {
|
|
-#define VFLIP_IDX 8
|
|
+[VFLIP] = {
|
|
{
|
|
.id = V4L2_CID_VFLIP,
|
|
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
@@ -307,14 +279,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 1,
|
|
.step = 1,
|
|
-#define VFLIP_DEFAULT 0
|
|
- .default_value = VFLIP_DEFAULT,
|
|
+ .default_value = 0,
|
|
},
|
|
- .set = sd_setvflip,
|
|
- .get = sd_getvflip,
|
|
+ .set_control = set_hvflip
|
|
},
|
|
- {
|
|
-#define EXPOSURE_IDX 9
|
|
+[EXPOSURE] = {
|
|
{
|
|
.id = V4L2_CID_EXPOSURE,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -322,14 +291,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 0x1780,
|
|
.step = 1,
|
|
-#define EXPOSURE_DEFAULT 0x33
|
|
- .default_value = EXPOSURE_DEFAULT,
|
|
+ .default_value = 0x33,
|
|
},
|
|
- .set = sd_setexposure,
|
|
- .get = sd_getexposure,
|
|
+ .set_control = set_exposure
|
|
},
|
|
- {
|
|
-#define GAIN_IDX 10
|
|
+[GAIN] = {
|
|
{
|
|
.id = V4L2_CID_GAIN,
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
@@ -337,14 +303,11 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 28,
|
|
.step = 1,
|
|
-#define GAIN_DEFAULT 0x00
|
|
- .default_value = GAIN_DEFAULT,
|
|
+ .default_value = 0,
|
|
},
|
|
- .set = sd_setgain,
|
|
- .get = sd_getgain,
|
|
+ .set_control = set_gain
|
|
},
|
|
- {
|
|
-#define AUTOGAIN_IDX 11
|
|
+[AUTOGAIN] = {
|
|
{
|
|
.id = V4L2_CID_AUTOGAIN,
|
|
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
@@ -352,11 +315,23 @@ static const struct ctrl sd_ctrls[] = {
|
|
.minimum = 0,
|
|
.maximum = 1,
|
|
.step = 1,
|
|
-#define AUTO_EXPOSURE_DEFAULT 1
|
|
- .default_value = AUTO_EXPOSURE_DEFAULT,
|
|
+ .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 = sd_setautoexposure,
|
|
- .get = sd_getautoexposure,
|
|
+ .set_control = set_quality
|
|
},
|
|
};
|
|
|
|
@@ -876,7 +851,7 @@ static u8 hv7131r_gain[] = {
|
|
};
|
|
|
|
static struct i2c_reg_u8 soi968_init[] = {
|
|
- {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f},
|
|
+ {0x0c, 0x00}, {0x0f, 0x1f},
|
|
{0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00},
|
|
{0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c},
|
|
{0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff},
|
|
@@ -902,7 +877,7 @@ static struct i2c_reg_u8 ov7660_init[] =
|
|
};
|
|
|
|
static struct i2c_reg_u8 ov7670_init[] = {
|
|
- {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
|
|
+ {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
|
|
{0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00},
|
|
{0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0},
|
|
{0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00},
|
|
@@ -959,7 +934,7 @@ static struct i2c_reg_u8 ov7670_init[] =
|
|
};
|
|
|
|
static struct i2c_reg_u8 ov9650_init[] = {
|
|
- {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78},
|
|
+ {0x00, 0x00}, {0x01, 0x78},
|
|
{0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03},
|
|
{0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00},
|
|
{0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00},
|
|
@@ -989,7 +964,7 @@ static struct i2c_reg_u8 ov9650_init[] =
|
|
};
|
|
|
|
static struct i2c_reg_u8 ov9655_init[] = {
|
|
- {0x12, 0x80}, {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba},
|
|
+ {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba},
|
|
{0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08},
|
|
{0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d},
|
|
{0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57},
|
|
@@ -1112,10 +1087,13 @@ static struct i2c_reg_u8 hv7131r_init[]
|
|
{0x23, 0x09}, {0x01, 0x08},
|
|
};
|
|
|
|
-static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
|
|
+static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
|
|
{
|
|
struct usb_device *dev = gspca_dev->dev;
|
|
int result;
|
|
+
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ return;
|
|
result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
0x00,
|
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
|
@@ -1125,17 +1103,19 @@ static int reg_r(struct gspca_dev *gspca
|
|
length,
|
|
500);
|
|
if (unlikely(result < 0 || result != length)) {
|
|
- pr_err("Read register failed 0x%02X\n", reg);
|
|
- return -EIO;
|
|
+ pr_err("Read register %02x failed %d\n", reg, result);
|
|
+ gspca_dev->usb_err = result;
|
|
}
|
|
- return 0;
|
|
}
|
|
|
|
-static int reg_w(struct gspca_dev *gspca_dev, u16 reg,
|
|
+static void reg_w(struct gspca_dev *gspca_dev, u16 reg,
|
|
const u8 *buffer, int length)
|
|
{
|
|
struct usb_device *dev = gspca_dev->dev;
|
|
int result;
|
|
+
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ return;
|
|
memcpy(gspca_dev->usb_buf, buffer, length);
|
|
result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
0x08,
|
|
@@ -1146,38 +1126,41 @@ static int reg_w(struct gspca_dev *gspca
|
|
length,
|
|
500);
|
|
if (unlikely(result < 0 || result != length)) {
|
|
- pr_err("Write register failed index 0x%02X\n", reg);
|
|
- return -EIO;
|
|
+ pr_err("Write register %02x failed %d\n", reg, result);
|
|
+ gspca_dev->usb_err = result;
|
|
}
|
|
- return 0;
|
|
}
|
|
|
|
-static int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value)
|
|
+static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value)
|
|
{
|
|
- u8 data[1] = {value};
|
|
- return reg_w(gspca_dev, reg, data, 1);
|
|
+ reg_w(gspca_dev, reg, &value, 1);
|
|
}
|
|
|
|
-static int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer)
|
|
+static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer)
|
|
{
|
|
int i;
|
|
+
|
|
reg_w(gspca_dev, 0x10c0, buffer, 8);
|
|
for (i = 0; i < 5; i++) {
|
|
reg_r(gspca_dev, 0x10c0, 1);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ return;
|
|
if (gspca_dev->usb_buf[0] & 0x04) {
|
|
- if (gspca_dev->usb_buf[0] & 0x08)
|
|
- return -EIO;
|
|
- return 0;
|
|
+ if (gspca_dev->usb_buf[0] & 0x08) {
|
|
+ pr_err("i2c_w error\n");
|
|
+ gspca_dev->usb_err = -EIO;
|
|
+ }
|
|
+ return;
|
|
}
|
|
- msleep(1);
|
|
+ msleep(10);
|
|
}
|
|
- return -EIO;
|
|
+ pr_err("i2c_w reg %02x no response\n", buffer[2]);
|
|
+/* gspca_dev->usb_err = -EIO; fixme: may occur */
|
|
}
|
|
|
|
-static int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
|
|
+static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
u8 row[8];
|
|
|
|
/*
|
|
@@ -1193,10 +1176,19 @@ static int i2c_w1(struct gspca_dev *gspc
|
|
row[6] = 0x00;
|
|
row[7] = 0x10;
|
|
|
|
- return i2c_w(gspca_dev, row);
|
|
+ i2c_w(gspca_dev, row);
|
|
+}
|
|
+
|
|
+static void i2c_w1_buf(struct gspca_dev *gspca_dev,
|
|
+ struct i2c_reg_u8 *buf, int sz)
|
|
+{
|
|
+ while (--sz >= 0) {
|
|
+ i2c_w1(gspca_dev, buf->reg, buf->val);
|
|
+ buf++;
|
|
+ }
|
|
}
|
|
|
|
-static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
|
|
+static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
u8 row[8];
|
|
@@ -1208,16 +1200,25 @@ static int i2c_w2(struct gspca_dev *gspc
|
|
row[0] = 0x81 | (3 << 4);
|
|
row[1] = sd->i2c_addr;
|
|
row[2] = reg;
|
|
- row[3] = (val >> 8) & 0xff;
|
|
- row[4] = val & 0xff;
|
|
+ row[3] = val >> 8;
|
|
+ row[4] = val;
|
|
row[5] = 0x00;
|
|
row[6] = 0x00;
|
|
row[7] = 0x10;
|
|
|
|
- return i2c_w(gspca_dev, row);
|
|
+ i2c_w(gspca_dev, row);
|
|
}
|
|
|
|
-static int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
|
|
+static void i2c_w2_buf(struct gspca_dev *gspca_dev,
|
|
+ struct i2c_reg_u16 *buf, int sz)
|
|
+{
|
|
+ while (--sz >= 0) {
|
|
+ i2c_w2(gspca_dev, buf->reg, buf->val);
|
|
+ buf++;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
u8 row[8];
|
|
@@ -1230,19 +1231,15 @@ static int i2c_r1(struct gspca_dev *gspc
|
|
row[5] = 0;
|
|
row[6] = 0;
|
|
row[7] = 0x10;
|
|
- if (i2c_w(gspca_dev, row) < 0)
|
|
- return -EIO;
|
|
+ i2c_w(gspca_dev, row);
|
|
row[0] = 0x81 | (1 << 4) | 0x02;
|
|
row[2] = 0;
|
|
- if (i2c_w(gspca_dev, row) < 0)
|
|
- return -EIO;
|
|
- if (reg_r(gspca_dev, 0x10c2, 5) < 0)
|
|
- return -EIO;
|
|
+ i2c_w(gspca_dev, row);
|
|
+ reg_r(gspca_dev, 0x10c2, 5);
|
|
*val = gspca_dev->usb_buf[4];
|
|
- return 0;
|
|
}
|
|
|
|
-static int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
|
|
+static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
u8 row[8];
|
|
@@ -1255,233 +1252,204 @@ static int i2c_r2(struct gspca_dev *gspc
|
|
row[5] = 0;
|
|
row[6] = 0;
|
|
row[7] = 0x10;
|
|
- if (i2c_w(gspca_dev, row) < 0)
|
|
- return -EIO;
|
|
+ i2c_w(gspca_dev, row);
|
|
row[0] = 0x81 | (2 << 4) | 0x02;
|
|
row[2] = 0;
|
|
- if (i2c_w(gspca_dev, row) < 0)
|
|
- return -EIO;
|
|
- if (reg_r(gspca_dev, 0x10c2, 5) < 0)
|
|
- return -EIO;
|
|
+ i2c_w(gspca_dev, row);
|
|
+ reg_r(gspca_dev, 0x10c2, 5);
|
|
*val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
|
|
- return 0;
|
|
}
|
|
|
|
-static int ov9650_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void ov9650_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
- int i;
|
|
u16 id;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
- if (i2c_r2(gspca_dev, 0x1c, &id) < 0)
|
|
- return -EINVAL;
|
|
+ i2c_r2(gspca_dev, 0x1c, &id);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ return;
|
|
|
|
if (id != 0x7fa2) {
|
|
pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id);
|
|
- return -ENODEV;
|
|
+ gspca_dev->usb_err = -ENODEV;
|
|
+ return;
|
|
}
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) {
|
|
- if (i2c_w1(gspca_dev, ov9650_init[i].reg,
|
|
- ov9650_init[i].val) < 0) {
|
|
- pr_err("OV9650 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
- }
|
|
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
|
|
+ msleep(200);
|
|
+ i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init));
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ pr_err("OV9650 sensor initialization failed\n");
|
|
sd->hstart = 1;
|
|
sd->vstart = 7;
|
|
- return 0;
|
|
}
|
|
|
|
-static int ov9655_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void ov9655_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
- int i;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) {
|
|
- if (i2c_w1(gspca_dev, ov9655_init[i].reg,
|
|
- ov9655_init[i].val) < 0) {
|
|
- pr_err("OV9655 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
- }
|
|
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
|
|
+ msleep(200);
|
|
+ i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init));
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ pr_err("OV9655 sensor initialization failed\n");
|
|
+
|
|
/* disable hflip and vflip */
|
|
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
|
|
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
|
|
sd->hstart = 1;
|
|
sd->vstart = 2;
|
|
- return 0;
|
|
}
|
|
|
|
-static int soi968_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void soi968_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
- int i;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(soi968_init); i++) {
|
|
- if (i2c_w1(gspca_dev, soi968_init[i].reg,
|
|
- soi968_init[i].val) < 0) {
|
|
- pr_err("SOI968 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
- }
|
|
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
|
|
+ msleep(200);
|
|
+ i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init));
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ pr_err("SOI968 sensor initialization failed\n");
|
|
+
|
|
/* disable hflip and vflip */
|
|
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX)
|
|
- | (1 << EXPOSURE_IDX);
|
|
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP)
|
|
+ | (1 << EXPOSURE);
|
|
sd->hstart = 60;
|
|
sd->vstart = 11;
|
|
- return 0;
|
|
}
|
|
|
|
-static int ov7660_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void ov7660_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
- int i;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) {
|
|
- if (i2c_w1(gspca_dev, ov7660_init[i].reg,
|
|
- ov7660_init[i].val) < 0) {
|
|
- pr_err("OV7660 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
- }
|
|
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
|
|
+ msleep(200);
|
|
+ i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init));
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ pr_err("OV7660 sensor initialization failed\n");
|
|
sd->hstart = 3;
|
|
sd->vstart = 3;
|
|
- return 0;
|
|
}
|
|
|
|
-static int ov7670_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void ov7670_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
- int i;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) {
|
|
- if (i2c_w1(gspca_dev, ov7670_init[i].reg,
|
|
- ov7670_init[i].val) < 0) {
|
|
- pr_err("OV7670 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
- }
|
|
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
|
|
+ msleep(200);
|
|
+ i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init));
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ pr_err("OV7670 sensor initialization failed\n");
|
|
+
|
|
/* disable hflip and vflip */
|
|
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
|
|
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
|
|
sd->hstart = 0;
|
|
sd->vstart = 1;
|
|
- return 0;
|
|
}
|
|
|
|
-static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void mt9v_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- int i;
|
|
u16 value;
|
|
- int ret;
|
|
|
|
sd->i2c_addr = 0x5d;
|
|
- ret = i2c_r2(gspca_dev, 0xff, &value);
|
|
- if ((ret == 0) && (value == 0x8243)) {
|
|
- for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) {
|
|
- if (i2c_w2(gspca_dev, mt9v011_init[i].reg,
|
|
- mt9v011_init[i].val) < 0) {
|
|
- pr_err("MT9V011 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
+ i2c_r2(gspca_dev, 0xff, &value);
|
|
+ if (gspca_dev->usb_err >= 0
|
|
+ && value == 0x8243) {
|
|
+ i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init));
|
|
+ if (gspca_dev->usb_err < 0) {
|
|
+ pr_err("MT9V011 sensor initialization failed\n");
|
|
+ return;
|
|
}
|
|
sd->hstart = 2;
|
|
sd->vstart = 2;
|
|
sd->sensor = SENSOR_MT9V011;
|
|
pr_info("MT9V011 sensor detected\n");
|
|
- return 0;
|
|
+ return;
|
|
}
|
|
|
|
+ gspca_dev->usb_err = 0;
|
|
sd->i2c_addr = 0x5c;
|
|
i2c_w2(gspca_dev, 0x01, 0x0004);
|
|
- ret = i2c_r2(gspca_dev, 0xff, &value);
|
|
- if ((ret == 0) && (value == 0x823a)) {
|
|
- for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) {
|
|
- if (i2c_w2(gspca_dev, mt9v111_init[i].reg,
|
|
- mt9v111_init[i].val) < 0) {
|
|
- pr_err("MT9V111 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
+ i2c_r2(gspca_dev, 0xff, &value);
|
|
+ if (gspca_dev->usb_err >= 0
|
|
+ && value == 0x823a) {
|
|
+ i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init));
|
|
+ if (gspca_dev->usb_err < 0) {
|
|
+ pr_err("MT9V111 sensor initialization failed\n");
|
|
+ return;
|
|
}
|
|
- gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX)
|
|
- | (1 << AUTOGAIN_IDX)
|
|
- | (1 << GAIN_IDX);
|
|
+ gspca_dev->ctrl_dis = (1 << EXPOSURE)
|
|
+ | (1 << AUTOGAIN)
|
|
+ | (1 << GAIN);
|
|
sd->hstart = 2;
|
|
sd->vstart = 2;
|
|
sd->sensor = SENSOR_MT9V111;
|
|
pr_info("MT9V111 sensor detected\n");
|
|
- return 0;
|
|
+ return;
|
|
}
|
|
|
|
+ gspca_dev->usb_err = 0;
|
|
sd->i2c_addr = 0x5d;
|
|
- ret = i2c_w2(gspca_dev, 0xf0, 0x0000);
|
|
- if (ret < 0) {
|
|
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
|
|
+ if (gspca_dev->usb_err < 0) {
|
|
+ gspca_dev->usb_err = 0;
|
|
sd->i2c_addr = 0x48;
|
|
i2c_w2(gspca_dev, 0xf0, 0x0000);
|
|
}
|
|
- ret = i2c_r2(gspca_dev, 0x00, &value);
|
|
- if ((ret == 0) && (value == 0x1229)) {
|
|
- for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) {
|
|
- if (i2c_w2(gspca_dev, mt9v112_init[i].reg,
|
|
- mt9v112_init[i].val) < 0) {
|
|
- pr_err("MT9V112 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
+ i2c_r2(gspca_dev, 0x00, &value);
|
|
+ if (gspca_dev->usb_err >= 0
|
|
+ && value == 0x1229) {
|
|
+ i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init));
|
|
+ if (gspca_dev->usb_err < 0) {
|
|
+ pr_err("MT9V112 sensor initialization failed\n");
|
|
+ return;
|
|
}
|
|
sd->hstart = 6;
|
|
sd->vstart = 2;
|
|
sd->sensor = SENSOR_MT9V112;
|
|
pr_info("MT9V112 sensor detected\n");
|
|
- return 0;
|
|
+ return;
|
|
}
|
|
|
|
- return -ENODEV;
|
|
+ gspca_dev->usb_err = -ENODEV;
|
|
}
|
|
|
|
-static int mt9m112_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void mt9m112_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- int i;
|
|
- for (i = 0; i < ARRAY_SIZE(mt9m112_init); i++) {
|
|
- if (i2c_w2(gspca_dev, mt9m112_init[i].reg,
|
|
- mt9m112_init[i].val) < 0) {
|
|
- pr_err("MT9M112 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
- }
|
|
- gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)
|
|
- | (1 << GAIN_IDX);
|
|
+
|
|
+ i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init));
|
|
+ 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;
|
|
- return 0;
|
|
}
|
|
|
|
-static int mt9m111_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void mt9m111_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- int i;
|
|
- for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) {
|
|
- if (i2c_w2(gspca_dev, mt9m111_init[i].reg,
|
|
- mt9m111_init[i].val) < 0) {
|
|
- pr_err("MT9M111 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
- }
|
|
- gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)
|
|
- | (1 << GAIN_IDX);
|
|
+
|
|
+ i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init));
|
|
+ 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;
|
|
- return 0;
|
|
}
|
|
|
|
-static int mt9m001_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void mt9m001_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- int i;
|
|
u16 id;
|
|
|
|
- if (i2c_r2(gspca_dev, 0x00, &id) < 0)
|
|
- return -EINVAL;
|
|
+ i2c_r2(gspca_dev, 0x00, &id);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ return;
|
|
|
|
/* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
|
|
switch (id) {
|
|
@@ -1494,85 +1462,78 @@ static int mt9m001_init_sensor(struct gs
|
|
break;
|
|
default:
|
|
pr_err("No MT9M001 chip detected, ID = %x\n\n", id);
|
|
- return -ENODEV;
|
|
+ gspca_dev->usb_err = -ENODEV;
|
|
+ return;
|
|
}
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) {
|
|
- if (i2c_w2(gspca_dev, mt9m001_init[i].reg,
|
|
- mt9m001_init[i].val) < 0) {
|
|
- pr_err("MT9M001 sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
- }
|
|
+ i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init));
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ pr_err("MT9M001 sensor initialization failed\n");
|
|
+
|
|
/* disable hflip and vflip */
|
|
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
|
|
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
|
|
sd->hstart = 1;
|
|
sd->vstart = 1;
|
|
- return 0;
|
|
}
|
|
|
|
-static int hv7131r_init_sensor(struct gspca_dev *gspca_dev)
|
|
+static void hv7131r_init_sensor(struct gspca_dev *gspca_dev)
|
|
{
|
|
- int i;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) {
|
|
- if (i2c_w1(gspca_dev, hv7131r_init[i].reg,
|
|
- hv7131r_init[i].val) < 0) {
|
|
- pr_err("HV7131R Sensor initialization failed\n");
|
|
- return -ENODEV;
|
|
- }
|
|
- }
|
|
+ i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init));
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ pr_err("HV7131R Sensor initialization failed\n");
|
|
+
|
|
sd->hstart = 0;
|
|
sd->vstart = 1;
|
|
- return 0;
|
|
}
|
|
|
|
-static int set_cmatrix(struct gspca_dev *gspca_dev)
|
|
+static void set_cmatrix(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- s32 hue_coord, hue_index = 180 + sd->hue;
|
|
+ int satur;
|
|
+ s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val;
|
|
u8 cmatrix[21];
|
|
|
|
memset(cmatrix, 0, sizeof cmatrix);
|
|
- cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26;
|
|
+ cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26;
|
|
cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
|
|
cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
|
|
- cmatrix[18] = sd->brightness - 0x80;
|
|
+ cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80;
|
|
|
|
- hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8;
|
|
+ satur = sd->ctrls[SATURATION].val;
|
|
+ hue_coord = (hsv_red_x[hue_index] * satur) >> 8;
|
|
cmatrix[6] = hue_coord;
|
|
cmatrix[7] = (hue_coord >> 8) & 0x0f;
|
|
|
|
- hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8;
|
|
+ hue_coord = (hsv_red_y[hue_index] * satur) >> 8;
|
|
cmatrix[8] = hue_coord;
|
|
cmatrix[9] = (hue_coord >> 8) & 0x0f;
|
|
|
|
- hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8;
|
|
+ hue_coord = (hsv_green_x[hue_index] * satur) >> 8;
|
|
cmatrix[10] = hue_coord;
|
|
cmatrix[11] = (hue_coord >> 8) & 0x0f;
|
|
|
|
- hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8;
|
|
+ hue_coord = (hsv_green_y[hue_index] * satur) >> 8;
|
|
cmatrix[12] = hue_coord;
|
|
cmatrix[13] = (hue_coord >> 8) & 0x0f;
|
|
|
|
- hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8;
|
|
+ hue_coord = (hsv_blue_x[hue_index] * satur) >> 8;
|
|
cmatrix[14] = hue_coord;
|
|
cmatrix[15] = (hue_coord >> 8) & 0x0f;
|
|
|
|
- hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8;
|
|
+ hue_coord = (hsv_blue_y[hue_index] * satur) >> 8;
|
|
cmatrix[16] = hue_coord;
|
|
cmatrix[17] = (hue_coord >> 8) & 0x0f;
|
|
|
|
- return reg_w(gspca_dev, 0x10e1, cmatrix, 21);
|
|
+ reg_w(gspca_dev, 0x10e1, cmatrix, 21);
|
|
}
|
|
|
|
-static int set_gamma(struct gspca_dev *gspca_dev)
|
|
+static void set_gamma(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
u8 gamma[17];
|
|
- u8 gval = sd->gamma * 0xb8 / 0x100;
|
|
-
|
|
+ u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100;
|
|
|
|
gamma[0] = 0x0a;
|
|
gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8);
|
|
@@ -1592,29 +1553,29 @@ static int set_gamma(struct gspca_dev *g
|
|
gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8);
|
|
gamma[16] = 0xf5;
|
|
|
|
- return reg_w(gspca_dev, 0x1190, gamma, 17);
|
|
+ reg_w(gspca_dev, 0x1190, gamma, 17);
|
|
}
|
|
|
|
-static int set_redblue(struct gspca_dev *gspca_dev)
|
|
+static void set_redblue(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- reg_w1(gspca_dev, 0x118c, sd->red);
|
|
- reg_w1(gspca_dev, 0x118f, sd->blue);
|
|
- return 0;
|
|
+
|
|
+ reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val);
|
|
+ reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val);
|
|
}
|
|
|
|
-static int set_hvflip(struct gspca_dev *gspca_dev)
|
|
+static void set_hvflip(struct gspca_dev *gspca_dev)
|
|
{
|
|
u8 value, tslb, hflip, vflip;
|
|
u16 value2;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) {
|
|
- hflip = !sd->hflip;
|
|
- vflip = !sd->vflip;
|
|
+ hflip = !sd->ctrls[HFLIP].val;
|
|
+ vflip = !sd->ctrls[VFLIP].val;
|
|
} else {
|
|
- hflip = sd->hflip;
|
|
- vflip = sd->vflip;
|
|
+ hflip = sd->ctrls[HFLIP].val;
|
|
+ vflip = sd->ctrls[VFLIP].val;
|
|
}
|
|
|
|
switch (sd->sensor) {
|
|
@@ -1625,8 +1586,9 @@ static int set_hvflip(struct gspca_dev *
|
|
if (vflip) {
|
|
value |= 0x10;
|
|
sd->vstart = 2;
|
|
- } else
|
|
+ } else {
|
|
sd->vstart = 3;
|
|
+ }
|
|
reg_w1(gspca_dev, 0x1182, sd->vstart);
|
|
i2c_w1(gspca_dev, 0x1e, value);
|
|
break;
|
|
@@ -1674,13 +1636,15 @@ static int set_hvflip(struct gspca_dev *
|
|
i2c_w1(gspca_dev, 0x01, value);
|
|
break;
|
|
}
|
|
- return 0;
|
|
}
|
|
|
|
-static int set_exposure(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};
|
|
+ int expo;
|
|
+
|
|
+ expo = sd->ctrls[EXPOSURE].val;
|
|
switch (sd->sensor) {
|
|
case SENSOR_OV7660:
|
|
case SENSOR_OV7670:
|
|
@@ -1688,35 +1652,37 @@ static int set_exposure(struct gspca_dev
|
|
case SENSOR_OV9650:
|
|
exp[0] |= (3 << 4);
|
|
exp[2] = 0x2d;
|
|
- exp[3] = sd->exposure & 0xff;
|
|
- exp[4] = sd->exposure >> 8;
|
|
+ exp[3] = expo;
|
|
+ exp[4] = expo >> 8;
|
|
break;
|
|
case SENSOR_MT9M001:
|
|
case SENSOR_MT9V112:
|
|
case SENSOR_MT9V011:
|
|
exp[0] |= (3 << 4);
|
|
exp[2] = 0x09;
|
|
- exp[3] = sd->exposure >> 8;
|
|
- exp[4] = sd->exposure & 0xff;
|
|
+ exp[3] = expo >> 8;
|
|
+ exp[4] = expo;
|
|
break;
|
|
case SENSOR_HV7131R:
|
|
exp[0] |= (4 << 4);
|
|
exp[2] = 0x25;
|
|
- exp[3] = (sd->exposure >> 5) & 0xff;
|
|
- exp[4] = (sd->exposure << 3) & 0xff;
|
|
+ exp[3] = expo >> 5;
|
|
+ exp[4] = expo << 3;
|
|
exp[5] = 0;
|
|
break;
|
|
default:
|
|
- return 0;
|
|
+ return;
|
|
}
|
|
i2c_w(gspca_dev, exp);
|
|
- return 0;
|
|
}
|
|
|
|
-static int set_gain(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};
|
|
+ int g;
|
|
+
|
|
+ g = sd->ctrls[GAIN].val;
|
|
switch (sd->sensor) {
|
|
case SENSOR_OV7660:
|
|
case SENSOR_OV7670:
|
|
@@ -1724,238 +1690,50 @@ static int set_gain(struct gspca_dev *gs
|
|
case SENSOR_OV9655:
|
|
case SENSOR_OV9650:
|
|
gain[0] |= (2 << 4);
|
|
- gain[3] = ov_gain[sd->gain];
|
|
+ gain[3] = ov_gain[g];
|
|
break;
|
|
case SENSOR_MT9V011:
|
|
gain[0] |= (3 << 4);
|
|
gain[2] = 0x35;
|
|
- gain[3] = micron1_gain[sd->gain] >> 8;
|
|
- gain[4] = micron1_gain[sd->gain] & 0xff;
|
|
+ gain[3] = micron1_gain[g] >> 8;
|
|
+ gain[4] = micron1_gain[g];
|
|
break;
|
|
case SENSOR_MT9V112:
|
|
gain[0] |= (3 << 4);
|
|
gain[2] = 0x2f;
|
|
- gain[3] = micron1_gain[sd->gain] >> 8;
|
|
- gain[4] = micron1_gain[sd->gain] & 0xff;
|
|
+ gain[3] = micron1_gain[g] >> 8;
|
|
+ gain[4] = micron1_gain[g];
|
|
break;
|
|
case SENSOR_MT9M001:
|
|
gain[0] |= (3 << 4);
|
|
gain[2] = 0x2f;
|
|
- gain[3] = micron2_gain[sd->gain] >> 8;
|
|
- gain[4] = micron2_gain[sd->gain] & 0xff;
|
|
+ gain[3] = micron2_gain[g] >> 8;
|
|
+ gain[4] = micron2_gain[g];
|
|
break;
|
|
case SENSOR_HV7131R:
|
|
gain[0] |= (2 << 4);
|
|
gain[2] = 0x30;
|
|
- gain[3] = hv7131r_gain[sd->gain];
|
|
+ gain[3] = hv7131r_gain[g];
|
|
break;
|
|
default:
|
|
- return 0;
|
|
+ return;
|
|
}
|
|
i2c_w(gspca_dev, gain);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-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)
|
|
- return set_cmatrix(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_setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->contrast = val;
|
|
- if (gspca_dev->streaming)
|
|
- return set_cmatrix(gspca_dev);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-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_setsaturation(struct gspca_dev *gspca_dev, s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->saturation = val;
|
|
- if (gspca_dev->streaming)
|
|
- return set_cmatrix(gspca_dev);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
- *val = sd->saturation;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_sethue(struct gspca_dev *gspca_dev, s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->hue = val;
|
|
- if (gspca_dev->streaming)
|
|
- return set_cmatrix(gspca_dev);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
- *val = sd->hue;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->gamma = val;
|
|
- if (gspca_dev->streaming)
|
|
- return set_gamma(gspca_dev);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
- *val = sd->gamma;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->red = val;
|
|
- if (gspca_dev->streaming)
|
|
- return set_redblue(gspca_dev);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
- *val = sd->red;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
-
|
|
- sd->blue = val;
|
|
- if (gspca_dev->streaming)
|
|
- return set_redblue(gspca_dev);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
- *val = sd->blue;
|
|
- 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)
|
|
- return set_hvflip(gspca_dev);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-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)
|
|
- return set_hvflip(gspca_dev);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
- *val = sd->vflip;
|
|
- 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)
|
|
- return set_exposure(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)
|
|
- return set_gain(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_setautoexposure(struct gspca_dev *gspca_dev, s32 val)
|
|
+static void set_quality(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- sd->auto_exposure = val;
|
|
- return 0;
|
|
-}
|
|
|
|
-static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val)
|
|
-{
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
- *val = sd->auto_exposure;
|
|
- return 0;
|
|
+ jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].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);
|
|
+ reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
|
|
+ reg_w1(gspca_dev, 0x1061, 0x03); /* restart transfer */
|
|
+ reg_w1(gspca_dev, 0x10e0, sd->fmt);
|
|
+ sd->fmt ^= 0x0c; /* invert QTAB use + write */
|
|
+ reg_w1(gspca_dev, 0x10e0, sd->fmt);
|
|
}
|
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
@@ -1963,28 +1741,26 @@ static int sd_dbg_g_register(struct gspc
|
|
struct v4l2_dbg_register *reg)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
+
|
|
switch (reg->match.type) {
|
|
case V4L2_CHIP_MATCH_HOST:
|
|
if (reg->match.addr != 0)
|
|
return -EINVAL;
|
|
if (reg->reg < 0x1000 || reg->reg > 0x11ff)
|
|
return -EINVAL;
|
|
- if (reg_r(gspca_dev, reg->reg, 1) < 0)
|
|
- return -EINVAL;
|
|
+ reg_r(gspca_dev, reg->reg, 1);
|
|
reg->val = gspca_dev->usb_buf[0];
|
|
- return 0;
|
|
+ return gspca_dev->usb_err;
|
|
case V4L2_CHIP_MATCH_I2C_ADDR:
|
|
if (reg->match.addr != sd->i2c_addr)
|
|
return -EINVAL;
|
|
if (sd->sensor >= SENSOR_MT9V011 &&
|
|
sd->sensor <= SENSOR_MT9M112) {
|
|
- if (i2c_r2(gspca_dev, reg->reg, (u16 *)®->val) < 0)
|
|
- return -EINVAL;
|
|
+ i2c_r2(gspca_dev, reg->reg, (u16 *) ®->val);
|
|
} else {
|
|
- if (i2c_r1(gspca_dev, reg->reg, (u8 *)®->val) < 0)
|
|
- return -EINVAL;
|
|
+ i2c_r1(gspca_dev, reg->reg, (u8 *) ®->val);
|
|
}
|
|
- return 0;
|
|
+ return gspca_dev->usb_err;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
@@ -1993,27 +1769,25 @@ static int sd_dbg_s_register(struct gspc
|
|
struct v4l2_dbg_register *reg)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
+
|
|
switch (reg->match.type) {
|
|
case V4L2_CHIP_MATCH_HOST:
|
|
if (reg->match.addr != 0)
|
|
return -EINVAL;
|
|
if (reg->reg < 0x1000 || reg->reg > 0x11ff)
|
|
return -EINVAL;
|
|
- if (reg_w1(gspca_dev, reg->reg, reg->val) < 0)
|
|
- return -EINVAL;
|
|
- return 0;
|
|
+ reg_w1(gspca_dev, reg->reg, reg->val);
|
|
+ return gspca_dev->usb_err;
|
|
case V4L2_CHIP_MATCH_I2C_ADDR:
|
|
if (reg->match.addr != sd->i2c_addr)
|
|
return -EINVAL;
|
|
if (sd->sensor >= SENSOR_MT9V011 &&
|
|
sd->sensor <= SENSOR_MT9M112) {
|
|
- if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0)
|
|
- return -EINVAL;
|
|
+ i2c_w2(gspca_dev, reg->reg, reg->val);
|
|
} else {
|
|
- if (i2c_w1(gspca_dev, reg->reg, reg->val) < 0)
|
|
- return -EINVAL;
|
|
+ i2c_w1(gspca_dev, reg->reg, reg->val);
|
|
}
|
|
- return 0;
|
|
+ return gspca_dev->usb_err;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
@@ -2050,9 +1824,9 @@ static int sd_config(struct gspca_dev *g
|
|
cam = &gspca_dev->cam;
|
|
cam->needs_full_bandwidth = 1;
|
|
|
|
- sd->sensor = (id->driver_info >> 8) & 0xff;
|
|
- sd->i2c_addr = id->driver_info & 0xff;
|
|
- sd->flags = (id->driver_info >> 16) & 0xff;
|
|
+ sd->sensor = id->driver_info >> 8;
|
|
+ sd->i2c_addr = id->driver_info;
|
|
+ sd->flags = id->driver_info >> 16;
|
|
|
|
switch (sd->sensor) {
|
|
case SENSOR_MT9M112:
|
|
@@ -2076,21 +1850,9 @@ static int sd_config(struct gspca_dev *g
|
|
sd->older_step = 0;
|
|
sd->exposure_step = 16;
|
|
|
|
- sd->brightness = BRIGHTNESS_DEFAULT;
|
|
- sd->contrast = CONTRAST_DEFAULT;
|
|
- sd->saturation = SATURATION_DEFAULT;
|
|
- sd->hue = HUE_DEFAULT;
|
|
- sd->gamma = GAMMA_DEFAULT;
|
|
- sd->red = RED_DEFAULT;
|
|
- sd->blue = BLUE_DEFAULT;
|
|
-
|
|
- sd->hflip = HFLIP_DEFAULT;
|
|
- sd->vflip = VFLIP_DEFAULT;
|
|
- sd->exposure = EXPOSURE_DEFAULT;
|
|
- sd->gain = GAIN_DEFAULT;
|
|
- sd->auto_exposure = AUTO_EXPOSURE_DEFAULT;
|
|
+ gspca_dev->cam.ctrls = sd->ctrls;
|
|
|
|
- sd->quality = 95;
|
|
+ INIT_WORK(&sd->work, qual_upd);
|
|
|
|
return 0;
|
|
}
|
|
@@ -2105,9 +1867,10 @@ static int sd_init(struct gspca_dev *gsp
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
|
|
value = bridge_init[i][1];
|
|
- if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) {
|
|
+ reg_w(gspca_dev, bridge_init[i][0], &value, 1);
|
|
+ if (gspca_dev->usb_err < 0) {
|
|
pr_err("Device initialization failed\n");
|
|
- return -ENODEV;
|
|
+ return gspca_dev->usb_err;
|
|
}
|
|
}
|
|
|
|
@@ -2116,72 +1879,85 @@ static int sd_init(struct gspca_dev *gsp
|
|
else
|
|
reg_w1(gspca_dev, 0x1006, 0x20);
|
|
|
|
- if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) {
|
|
+ reg_w(gspca_dev, 0x10c0, i2c_init, 9);
|
|
+ if (gspca_dev->usb_err < 0) {
|
|
pr_err("Device initialization failed\n");
|
|
- return -ENODEV;
|
|
+ return gspca_dev->usb_err;
|
|
}
|
|
|
|
switch (sd->sensor) {
|
|
case SENSOR_OV9650:
|
|
- if (ov9650_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ ov9650_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
pr_info("OV9650 sensor detected\n");
|
|
break;
|
|
case SENSOR_OV9655:
|
|
- if (ov9655_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ ov9655_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
pr_info("OV9655 sensor detected\n");
|
|
break;
|
|
case SENSOR_SOI968:
|
|
- if (soi968_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ soi968_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
pr_info("SOI968 sensor detected\n");
|
|
break;
|
|
case SENSOR_OV7660:
|
|
- if (ov7660_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ ov7660_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
pr_info("OV7660 sensor detected\n");
|
|
break;
|
|
case SENSOR_OV7670:
|
|
- if (ov7670_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ ov7670_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
pr_info("OV7670 sensor detected\n");
|
|
break;
|
|
case SENSOR_MT9VPRB:
|
|
- if (mt9v_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ mt9v_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
+ pr_info("MT9VPRB sensor detected\n");
|
|
break;
|
|
case SENSOR_MT9M111:
|
|
- if (mt9m111_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ mt9m111_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
pr_info("MT9M111 sensor detected\n");
|
|
break;
|
|
case SENSOR_MT9M112:
|
|
- if (mt9m112_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ mt9m112_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
pr_info("MT9M112 sensor detected\n");
|
|
break;
|
|
case SENSOR_MT9M001:
|
|
- if (mt9m001_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ mt9m001_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
break;
|
|
case SENSOR_HV7131R:
|
|
- if (hv7131r_init_sensor(gspca_dev) < 0)
|
|
- return -ENODEV;
|
|
+ hv7131r_init_sensor(gspca_dev);
|
|
+ if (gspca_dev->usb_err < 0)
|
|
+ break;
|
|
pr_info("HV7131R sensor detected\n");
|
|
break;
|
|
default:
|
|
- pr_info("Unsupported Sensor\n");
|
|
- return -ENODEV;
|
|
+ pr_err("Unsupported sensor\n");
|
|
+ gspca_dev->usb_err = -ENODEV;
|
|
}
|
|
|
|
- return 0;
|
|
+ return gspca_dev->usb_err;
|
|
}
|
|
|
|
static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
u8 value;
|
|
+
|
|
switch (sd->sensor) {
|
|
case SENSOR_SOI968:
|
|
if (mode & MODE_SXGA) {
|
|
@@ -2264,6 +2040,7 @@ static int sd_isoc_init(struct gspca_dev
|
|
break;
|
|
default: /* >= 640x480 */
|
|
gspca_dev->alt = 9;
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
@@ -2290,14 +2067,15 @@ static int sd_start(struct gspca_dev *gs
|
|
|
|
jpeg_define(sd->jpeg_hdr, height, width,
|
|
0x21);
|
|
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
|
+ jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
|
|
|
|
if (mode & MODE_RAW)
|
|
fmt = 0x2d;
|
|
else if (mode & MODE_JPEG)
|
|
- fmt = 0x2c;
|
|
+ fmt = 0x24;
|
|
else
|
|
fmt = 0x2f; /* YUV 420 */
|
|
+ sd->fmt = fmt;
|
|
|
|
switch (mode & SCALE_MASK) {
|
|
case SCALE_1280x1024:
|
|
@@ -2334,18 +2112,37 @@ static int sd_start(struct gspca_dev *gs
|
|
set_hvflip(gspca_dev);
|
|
|
|
reg_w1(gspca_dev, 0x1007, 0x20);
|
|
+ reg_w1(gspca_dev, 0x1061, 0x03);
|
|
|
|
- reg_r(gspca_dev, 0x1061, 1);
|
|
- reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02);
|
|
- return 0;
|
|
+ /* if JPEG, prepare the compression quality update */
|
|
+ if (mode & MODE_JPEG) {
|
|
+ sd->pktsz = sd->npkt = 0;
|
|
+ sd->nchg = 0;
|
|
+ sd->work_thread =
|
|
+ create_singlethread_workqueue(KBUILD_MODNAME);
|
|
+ }
|
|
+
|
|
+ return gspca_dev->usb_err;
|
|
}
|
|
|
|
static void sd_stopN(struct gspca_dev *gspca_dev)
|
|
{
|
|
reg_w1(gspca_dev, 0x1007, 0x00);
|
|
+ reg_w1(gspca_dev, 0x1061, 0x01);
|
|
+}
|
|
|
|
- reg_r(gspca_dev, 0x1061, 1);
|
|
- reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02);
|
|
+/* called on streamoff with alt==0 and on disconnect */
|
|
+/* the usb_lock is held at entry - restore on exit */
|
|
+static void sd_stop0(struct gspca_dev *gspca_dev)
|
|
+{
|
|
+ struct sd *sd = (struct sd *) gspca_dev;
|
|
+
|
|
+ if (sd->work_thread != NULL) {
|
|
+ mutex_unlock(&gspca_dev->usb_lock);
|
|
+ destroy_workqueue(sd->work_thread);
|
|
+ mutex_lock(&gspca_dev->usb_lock);
|
|
+ sd->work_thread = NULL;
|
|
+ }
|
|
}
|
|
|
|
static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
|
|
@@ -2359,15 +2156,15 @@ static void do_autoexposure(struct gspca
|
|
* and exposure steps
|
|
*/
|
|
if (avg_lum < MIN_AVG_LUM) {
|
|
- if (sd->exposure > 0x1770)
|
|
+ if (sd->ctrls[EXPOSURE].val > 0x1770)
|
|
return;
|
|
|
|
- new_exp = sd->exposure + sd->exposure_step;
|
|
+ new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step;
|
|
if (new_exp > 0x1770)
|
|
new_exp = 0x1770;
|
|
if (new_exp < 0x10)
|
|
new_exp = 0x10;
|
|
- sd->exposure = new_exp;
|
|
+ sd->ctrls[EXPOSURE].val = new_exp;
|
|
set_exposure(gspca_dev);
|
|
|
|
sd->older_step = sd->old_step;
|
|
@@ -2379,14 +2176,14 @@ static void do_autoexposure(struct gspca
|
|
sd->exposure_step += 2;
|
|
}
|
|
if (avg_lum > MAX_AVG_LUM) {
|
|
- if (sd->exposure < 0x10)
|
|
+ if (sd->ctrls[EXPOSURE].val < 0x10)
|
|
return;
|
|
- new_exp = sd->exposure - sd->exposure_step;
|
|
+ new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step;
|
|
if (new_exp > 0x1700)
|
|
new_exp = 0x1770;
|
|
if (new_exp < 0x10)
|
|
new_exp = 0x10;
|
|
- sd->exposure = new_exp;
|
|
+ sd->ctrls[EXPOSURE].val = new_exp;
|
|
set_exposure(gspca_dev);
|
|
sd->older_step = sd->old_step;
|
|
sd->old_step = 0;
|
|
@@ -2403,14 +2200,14 @@ static void do_autogain(struct gspca_dev
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
if (avg_lum < MIN_AVG_LUM) {
|
|
- if (sd->gain + 1 <= 28) {
|
|
- sd->gain++;
|
|
+ if (sd->ctrls[GAIN].val + 1 <= 28) {
|
|
+ sd->ctrls[GAIN].val++;
|
|
set_gain(gspca_dev);
|
|
}
|
|
}
|
|
if (avg_lum > MAX_AVG_LUM) {
|
|
- if (sd->gain > 0) {
|
|
- sd->gain--;
|
|
+ if (sd->ctrls[GAIN].val > 0) {
|
|
+ sd->ctrls[GAIN].val--;
|
|
set_gain(gspca_dev);
|
|
}
|
|
}
|
|
@@ -2421,7 +2218,7 @@ static void sd_dqcallback(struct gspca_d
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
int avg_lum;
|
|
|
|
- if (!sd->auto_exposure)
|
|
+ if (!sd->ctrls[AUTOGAIN].val)
|
|
return;
|
|
|
|
avg_lum = atomic_read(&sd->avg_lum);
|
|
@@ -2431,33 +2228,92 @@ static void sd_dqcallback(struct gspca_d
|
|
do_autoexposure(gspca_dev, avg_lum);
|
|
}
|
|
|
|
+/* JPEG quality update */
|
|
+/* This function is executed from a work queue. */
|
|
+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;
|
|
+
|
|
+ mutex_lock(&gspca_dev->usb_lock);
|
|
+ PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val);
|
|
+ set_quality(gspca_dev);
|
|
+ mutex_unlock(&gspca_dev->usb_lock);
|
|
+}
|
|
+
|
|
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
|
static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
|
|
u8 *data, /* interrupt packet */
|
|
int len) /* interrupt packet length */
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- int ret = -EINVAL;
|
|
+
|
|
if (!(sd->flags & HAS_NO_BUTTON) && len == 1) {
|
|
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
|
|
- input_sync(gspca_dev->input_dev);
|
|
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
|
|
- input_sync(gspca_dev->input_dev);
|
|
- ret = 0;
|
|
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
|
|
+ input_sync(gspca_dev->input_dev);
|
|
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
|
|
+ input_sync(gspca_dev->input_dev);
|
|
+ return 0;
|
|
}
|
|
- return ret;
|
|
+ return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
+/* check the JPEG compression */
|
|
+static void transfer_check(struct gspca_dev *gspca_dev,
|
|
+ u8 *data)
|
|
+{
|
|
+ struct sd *sd = (struct sd *) gspca_dev;
|
|
+ int new_qual, r;
|
|
+
|
|
+ new_qual = 0;
|
|
+
|
|
+ /* if USB error, discard the frame and decrease the quality */
|
|
+ if (data[6] & 0x08) { /* USB FIFO full */
|
|
+ gspca_dev->last_packet_type = DISCARD_PACKET;
|
|
+ new_qual = -5;
|
|
+ } else {
|
|
+
|
|
+ /* else, compute the filling rate and a new JPEG quality */
|
|
+ r = (sd->pktsz * 100) /
|
|
+ (sd->npkt *
|
|
+ gspca_dev->urb[0]->iso_frame_desc[0].length);
|
|
+ if (r >= 85)
|
|
+ new_qual = -3;
|
|
+ else if (r < 75)
|
|
+ new_qual = 2;
|
|
+ }
|
|
+ if (new_qual != 0) {
|
|
+ sd->nchg += new_qual;
|
|
+ if (sd->nchg < -6 || sd->nchg >= 12) {
|
|
+ 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;
|
|
+ queue_work(sd->work_thread, &sd->work);
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ sd->nchg = 0;
|
|
+ }
|
|
+ sd->pktsz = sd->npkt = 0;
|
|
+}
|
|
+
|
|
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|
u8 *data, /* isoc packet */
|
|
int len) /* iso packet length */
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
- int avg_lum;
|
|
+ int avg_lum, is_jpeg;
|
|
static u8 frame_header[] =
|
|
{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
|
|
- if (len == 64 && memcmp(data, frame_header, 6) == 0) {
|
|
+
|
|
+ is_jpeg = (sd->fmt & 0x03) == 0;
|
|
+ if (len >= 64 && memcmp(data, frame_header, 6) == 0) {
|
|
avg_lum = ((data[35] >> 2) & 3) |
|
|
(data[20] << 2) |
|
|
(data[19] << 10);
|
|
@@ -2484,12 +2340,18 @@ static void sd_pkt_scan(struct gspca_dev
|
|
(data[33] << 10);
|
|
avg_lum >>= 9;
|
|
atomic_set(&sd->avg_lum, avg_lum);
|
|
+
|
|
+ if (is_jpeg)
|
|
+ transfer_check(gspca_dev, data);
|
|
+
|
|
gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
|
|
- return;
|
|
+ len -= 64;
|
|
+ if (len == 0)
|
|
+ return;
|
|
+ data += 64;
|
|
}
|
|
if (gspca_dev->last_packet_type == LAST_PACKET) {
|
|
- if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv
|
|
- & MODE_JPEG) {
|
|
+ if (is_jpeg) {
|
|
gspca_frame_add(gspca_dev, FIRST_PACKET,
|
|
sd->jpeg_hdr, JPEG_HDR_SZ);
|
|
gspca_frame_add(gspca_dev, INTER_PACKET,
|
|
@@ -2499,13 +2361,18 @@ static void sd_pkt_scan(struct gspca_dev
|
|
data, len);
|
|
}
|
|
} else {
|
|
+ /* if JPEG, count the packets and their size */
|
|
+ if (is_jpeg) {
|
|
+ sd->npkt++;
|
|
+ sd->pktsz += len;
|
|
+ }
|
|
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
|
|
}
|
|
}
|
|
|
|
/* sub-driver description */
|
|
static const struct sd_desc sd_desc = {
|
|
- .name = MODULE_NAME,
|
|
+ .name = KBUILD_MODNAME,
|
|
.ctrls = sd_ctrls,
|
|
.nctrls = ARRAY_SIZE(sd_ctrls),
|
|
.config = sd_config,
|
|
@@ -2513,6 +2380,7 @@ static const struct sd_desc sd_desc = {
|
|
.isoc_init = sd_isoc_init,
|
|
.start = sd_start,
|
|
.stopN = sd_stopN,
|
|
+ .stop0 = sd_stop0,
|
|
.pkt_scan = sd_pkt_scan,
|
|
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
|
.int_pkt_scan = sd_int_pkt_scan,
|
|
@@ -2581,7 +2449,7 @@ static int sd_probe(struct usb_interface
|
|
}
|
|
|
|
static struct usb_driver sd_driver = {
|
|
- .name = MODULE_NAME,
|
|
+ .name = KBUILD_MODNAME,
|
|
.id_table = device_table,
|
|
.probe = sd_probe,
|
|
.disconnect = gspca_disconnect,
|
|
Index: linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-udma.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/ivtv/ivtv-udma.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/ivtv/ivtv-udma.c
|
|
@@ -57,9 +57,9 @@ int ivtv_udma_fill_sg_list (struct ivtv_
|
|
if (dma->bouncemap[map_offset] == NULL)
|
|
return -1;
|
|
local_irq_save(flags);
|
|
- src = kmap_atomic(dma->map[map_offset], KM_BOUNCE_READ) + offset;
|
|
+ src = kmap_atomic(dma->map[map_offset]) + offset;
|
|
memcpy(page_address(dma->bouncemap[map_offset]) + offset, src, len);
|
|
- kunmap_atomic(src, KM_BOUNCE_READ);
|
|
+ kunmap_atomic(src);
|
|
local_irq_restore(flags);
|
|
sg_set_page(&dma->SGlist[map_offset], dma->bouncemap[map_offset], len, offset);
|
|
}
|
|
Index: linux-3.3.x86_64/drivers/media/video/videobuf2-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/videobuf2-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/videobuf2-core.c
|
|
@@ -19,6 +19,9 @@
|
|
#include <linux/slab.h>
|
|
#include <linux/sched.h>
|
|
|
|
+#include <media/v4l2-dev.h>
|
|
+#include <media/v4l2-fh.h>
|
|
+#include <media/v4l2-event.h>
|
|
#include <media/videobuf2-core.h>
|
|
|
|
static int debug;
|
|
@@ -1642,32 +1645,46 @@ static int __vb2_cleanup_fileio(struct v
|
|
* 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)
|
|
{
|
|
- unsigned long flags;
|
|
- unsigned int ret;
|
|
+ struct video_device *vfd = video_devdata(file);
|
|
+ unsigned long req_events = poll_requested_events(wait);
|
|
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.
|
|
*/
|
|
if (q->num_buffers == 0 && q->fileio == NULL) {
|
|
- if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ)) {
|
|
- ret = __vb2_init_fileio(q, 1);
|
|
- if (ret)
|
|
- return POLLERR;
|
|
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
|
|
+ (req_events & (POLLIN | POLLRDNORM))) {
|
|
+ if (__vb2_init_fileio(q, 1))
|
|
+ return res | POLLERR;
|
|
}
|
|
- if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) {
|
|
- ret = __vb2_init_fileio(q, 0);
|
|
- if (ret)
|
|
- return POLLERR;
|
|
+ if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
|
|
+ (req_events & (POLLOUT | POLLWRNORM))) {
|
|
+ if (__vb2_init_fileio(q, 0))
|
|
+ return res | POLLERR;
|
|
/*
|
|
* Write to OUTPUT queue can be done immediately.
|
|
*/
|
|
- return POLLOUT | POLLWRNORM;
|
|
+ return res | POLLOUT | POLLWRNORM;
|
|
}
|
|
}
|
|
|
|
@@ -1675,7 +1692,7 @@ unsigned int vb2_poll(struct vb2_queue *
|
|
* 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);
|
|
|
|
@@ -1690,10 +1707,11 @@ unsigned int vb2_poll(struct vb2_queue *
|
|
|
|
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);
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/video/videobuf-core.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/videobuf-core.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/videobuf-core.c
|
|
@@ -1129,6 +1129,7 @@ unsigned int videobuf_poll_stream(struct
|
|
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
|
|
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) {
|
|
Index: linux-3.3.x86_64/drivers/media/video/pwc/pwc-if.c
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/video/pwc/pwc-if.c
|
|
+++ linux-3.3.x86_64/drivers/media/video/pwc/pwc-if.c
|
|
@@ -625,10 +625,19 @@ static ssize_t pwc_video_read(struct fil
|
|
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);
|
|
}
|
|
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/Kconfig
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/drivers/media/common/tuners/Kconfig
|
|
+++ linux-3.3.x86_64/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
|
|
@@ -211,4 +218,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
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/tua9001.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/tua9001.c
|
|
@@ -0,0 +1,215 @@
|
|
+/*
|
|
+ * Infineon TUA 9001 silicon tuner driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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 <crope@iki.fi>");
|
|
+MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/tua9001.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/tua9001.h
|
|
@@ -0,0 +1,46 @@
|
|
+/*
|
|
+ * Infineon TUA 9001 silicon tuner driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/tua9001_priv.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/tua9001_priv.h
|
|
@@ -0,0 +1,34 @@
|
|
+/*
|
|
+ * Infineon TUA 9001 silicon tuner driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/af9033.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/af9033.c
|
|
@@ -0,0 +1,919 @@
|
|
+/*
|
|
+ * Afatech AF9033 demodulator driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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;
|
|
+ case AF9033_TUNER_FC0011:
|
|
+ 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;
|
|
+ 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);
|
|
+ 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\n", __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, spec_inv;
|
|
+ u8 tmp, buf[3], bandwidth_reg_val;
|
|
+ u32 if_frequency, freq_cw, adc_freq;
|
|
+
|
|
+ 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) {
|
|
+ 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;
|
|
+
|
|
+ while (if_frequency > (adc_freq / 2))
|
|
+ if_frequency -= adc_freq;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ if (tmp == 1)
|
|
+ freq_cw /= 2;
|
|
+
|
|
+ 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_get_frontend(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct af9033_state *state = fe->demodulator_priv;
|
|
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
+ 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 < 0)
|
|
+ goto err;
|
|
+
|
|
+ switch ((buf[0] >> 0) & 3) {
|
|
+ case 0:
|
|
+ c->transmission_mode = TRANSMISSION_MODE_2K;
|
|
+ break;
|
|
+ case 1:
|
|
+ c->transmission_mode = TRANSMISSION_MODE_8K;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch ((buf[1] >> 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[2] >> 0) & 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[3] >> 0) & 3) {
|
|
+ case 0:
|
|
+ c->modulation = QPSK;
|
|
+ break;
|
|
+ case 1:
|
|
+ c->modulation = QAM_16;
|
|
+ break;
|
|
+ case 2:
|
|
+ c->modulation = QAM_64;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch ((buf[4] >> 0) & 3) {
|
|
+ case 0:
|
|
+ c->bandwidth_hz = 6000000;
|
|
+ break;
|
|
+ case 1:
|
|
+ c->bandwidth_hz = 7000000;
|
|
+ break;
|
|
+ case 2:
|
|
+ c->bandwidth_hz = 8000000;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch ((buf[6] >> 0) & 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;
|
|
+ case 5:
|
|
+ c->code_rate_HP = FEC_NONE;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch ((buf[7] >> 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;
|
|
+ case 5:
|
|
+ c->code_rate_LP = FEC_NONE;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ 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)
|
|
+{
|
|
+ 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)
|
|
+{
|
|
+ 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));
|
|
+
|
|
+ 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)
|
|
+ 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,
|
|
+ .get_frontend = af9033_get_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 <crope@iki.fi>");
|
|
+MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver");
|
|
+MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/af9033.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/af9033.h
|
|
@@ -0,0 +1,75 @@
|
|
+/*
|
|
+ * Afatech AF9033 demodulator driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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 */
|
|
+#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */
|
|
+#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */
|
|
+ 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 */
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/frontends/af9033_priv.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/frontends/af9033_priv.h
|
|
@@ -0,0 +1,470 @@
|
|
+/*
|
|
+ * Afatech AF9033 demodulator driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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;
|
|
+};
|
|
+
|
|
+struct val_snr {
|
|
+ u32 val;
|
|
+ u8 snr;
|
|
+};
|
|
+
|
|
+/* 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 }
|
|
+ },
|
|
+};
|
|
+
|
|
+/* 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 },
|
|
+ { 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 },
|
|
+};
|
|
+
|
|
+/* 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 },
|
|
+};
|
|
+
|
|
+/* 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 },
|
|
+};
|
|
+
|
|
+/* 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 */
|
|
+
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/af9035.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/af9035.c
|
|
@@ -0,0 +1,1164 @@
|
|
+/*
|
|
+ * Afatech AF9035 DVB USB driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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"
|
|
+#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)
|
|
+{
|
|
+ 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
|
|
+#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;
|
|
+ u8 buf[BUF_LEN];
|
|
+ u32 msg_len;
|
|
+ static u8 seq; /* packet sequence number */
|
|
+ u16 checksum, tmpsum;
|
|
+
|
|
+ /* 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 */
|
|
+ checksum = af9035_checksum(buf, buf[0] - 1);
|
|
+ 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;
|
|
+ }
|
|
+ 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__,
|
|
+ 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;
|
|
+
|
|
+ /*
|
|
+ * 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) {
|
|
+ /* 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 */
|
|
+ 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] = 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)) {
|
|
+ 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[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] = 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 {
|
|
+ /*
|
|
+ * 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)
|
|
+{
|
|
+ int ret, i, j, len;
|
|
+ u8 wbuf[1];
|
|
+ u8 rbuf[4];
|
|
+ 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 <daniel-gl@gmx.net> 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;
|
|
+ }
|
|
+
|
|
+ /* 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) {
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ /* 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=%zu\n", __func__, fw->size - 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;
|
|
+}
|
|
+
|
|
+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])
|
|
+{
|
|
+ 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:
|
|
+ case AF9033_TUNER_FC0011:
|
|
+ case AF9033_TUNER_MXL5007T:
|
|
+ case AF9033_TUNER_TDA18218:
|
|
+ af9035_af9033_config[i].spec_inv = 1;
|
|
+ break;
|
|
+ default:
|
|
+ af9035_config.hw_not_supported = true;
|
|
+ warn("tuner ID=%02x not supported, please report!",
|
|
+ tmp);
|
|
+ };
|
|
+
|
|
+ /* 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;
|
|
+}
|
|
+
|
|
+/* 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)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ switch (cmd) {
|
|
+ case FC0011_FE_CALLBACK_POWER:
|
|
+ /* Tuner enable */
|
|
+ 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 */
|
|
+ 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:
|
|
+ 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);
|
|
+
|
|
+ ret = af9035_wr_reg(d, 0xd8e7, 0);
|
|
+ if (ret < 0)
|
|
+ goto err;
|
|
+
|
|
+ usleep_range(10000, 20000);
|
|
+ break;
|
|
+ default:
|
|
+ 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)
|
|
+{
|
|
+ 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;
|
|
+
|
|
+ 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);
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ /* disable I2C-gate */
|
|
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL;
|
|
+ adap->fe_adap[0].fe->callback = af9035_frontend_callback;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err:
|
|
+ pr_debug("%s: failed=%d\n", __func__, ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct tua9001_config af9035_tua9001_config = {
|
|
+ .i2c_addr = 0x60,
|
|
+};
|
|
+
|
|
+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 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;
|
|
+ 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;
|
|
+ case AF9033_TUNER_FC0011:
|
|
+ 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;
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ 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_15A4_9035,
|
|
+ AF9035_15A4_1001,
|
|
+ AF9035_0CCD_0093,
|
|
+ AF9035_07CA_A835,
|
|
+ AF9035_07CA_B835,
|
|
+ AF9035_07CA_1867,
|
|
+ AF9035_07CA_A867,
|
|
+ AF9035_07CA_0825,
|
|
+};
|
|
+
|
|
+static struct usb_device_id af9035_id[] = {
|
|
+ [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] = {
|
|
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_B835)},
|
|
+ [AF9035_07CA_1867] = {
|
|
+ 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)},
|
|
+ {},
|
|
+};
|
|
+
|
|
+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-02.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 = 5,
|
|
+ .devices = {
|
|
+ {
|
|
+ .name = "Afatech AF9035 reference design",
|
|
+ .cold_ids = {
|
|
+ &af9035_id[AF9035_15A4_9035],
|
|
+ &af9035_id[AF9035_15A4_1001],
|
|
+ },
|
|
+ }, {
|
|
+ .name = "TerraTec Cinergy T Stick",
|
|
+ .cold_ids = {
|
|
+ &af9035_id[AF9035_0CCD_0093],
|
|
+ },
|
|
+ }, {
|
|
+ .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 = {
|
|
+ &af9035_id[AF9035_07CA_1867],
|
|
+ &af9035_id[AF9035_07CA_A867],
|
|
+ },
|
|
+ }, {
|
|
+ .name = "AVerMedia Twinstar (A825)",
|
|
+ .cold_ids = {
|
|
+ &af9035_id[AF9035_07CA_0825],
|
|
+ },
|
|
+ },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ .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,
|
|
+ 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_usb_driver(af9035_usb_driver);
|
|
+
|
|
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
|
|
+MODULE_DESCRIPTION("Afatech AF9035 driver");
|
|
+MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/media/dvb/dvb-usb/af9035.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/dvb/dvb-usb/af9035.h
|
|
@@ -0,0 +1,120 @@
|
|
+/*
|
|
+ * Afatech AF9035 DVB USB driver
|
|
+ *
|
|
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
|
|
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
|
|
+ *
|
|
+ * 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
|
|
+
|
|
+/* prefix for dvb-usb log writings */
|
|
+#define DVB_USB_LOG_PREFIX "af9035"
|
|
+
|
|
+#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;
|
|
+ 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 */
|
|
+ 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 */
|
|
+};
|
|
+
|
|
+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
|
|
+#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
|
|
+#define CMD_FW_SCATTER_WR 0x29
|
|
+
|
|
+#endif
|
|
Index: linux-3.3.x86_64/MAINTAINERS
|
|
===================================================================
|
|
--- linux-3.3.x86_64.orig/MAINTAINERS
|
|
+++ linux-3.3.x86_64/MAINTAINERS
|
|
@@ -2659,6 +2659,13 @@ S: Maintained
|
|
F: Documentation/hwmon/f71805f
|
|
F: drivers/hwmon/f71805f.c
|
|
|
|
+FC0011 TUNER DRIVER
|
|
+M: Michael Buesch <m@bues.ch>
|
|
+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 <eparis@redhat.com>
|
|
S: Maintained
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/fc0011.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/drivers/media/common/tuners/fc0011.c
|
|
@@ -0,0 +1,524 @@
|
|
+/*
|
|
+ * Fitipower FC0011 tuner driver
|
|
+ *
|
|
+ * Copyright (C) 2012 Michael Buesch <m@bues.ch>
|
|
+ *
|
|
+ * Derived from FC0012 tuner driver:
|
|
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
|
|
+ *
|
|
+ * 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;
|
|
+ usleep_range(10000, 20000);
|
|
+ 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 < 3) {
|
|
+ /* 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;
|
|
+ usleep_range(10000, 50000);
|
|
+
|
|
+ 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 <m@bues.ch>");
|
|
+MODULE_LICENSE("GPL");
|
|
Index: linux-3.3.x86_64/drivers/media/common/tuners/fc0011.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-3.3.x86_64/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_ */
|