From b4797048f70e142a65ee2139085edb44b166d238 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sun, 10 Oct 2010 13:45:54 -0300 Subject: [media] stv090x: added function to control GPIOs from the outside This patch STV090X adds and exports a function to control the GPIOs of the stv090x-devices. Cc: Manu Abraham Signed-off-by: Martin Wilks Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 4e0fc2c..63cb4f8 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -4516,6 +4516,20 @@ err: return -1; } +int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir, u8 value, + u8 xor_value) +{ + struct stv090x_state *state = fe->demodulator_priv; + u8 reg = 0; + + STV090x_SETFIELD(reg, GPIOx_OPD_FIELD, dir); + STV090x_SETFIELD(reg, GPIOx_CONFIG_FIELD, value); + STV090x_SETFIELD(reg, GPIOx_XOR_FIELD, xor_value); + + return stv090x_write_reg(state, STV090x_GPIOxCFG(gpio), reg); +} +EXPORT_SYMBOL(stv090x_set_gpio); + static struct dvb_frontend_ops stv090x_ops = { .info = { diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h index dd1b93a..11754f2 100644 --- a/drivers/media/dvb/frontends/stv090x.h +++ b/drivers/media/dvb/frontends/stv090x.h @@ -104,6 +104,11 @@ struct stv090x_config { extern struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, struct i2c_adapter *i2c, enum stv090x_demodulator demod); + +/* dir = 0 -> output, dir = 1 -> input/open-drain */ +extern int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, + u8 dir, u8 value, u8 xor_value); + #else static inline struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, @@ -113,6 +118,13 @@ static inline struct dvb_frontend *stv090x_attach(const struct stv090x_config *c printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } + +static inline int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, + u8 opd, u8 value, u8 xor_value) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} #endif /* CONFIG_DVB_STV090x */ #endif /* __STV090x_H */ -- cgit v0.10.2 From 4f7200a8a0253e7a4b74cbf1a0a3868cccdee647 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sat, 9 Oct 2010 10:12:34 -0300 Subject: [media] stv090x: add tei-field to config-structure Some backends want to receive the full transport stream including uncorrected packets. To have that feature this patchs add a field to the config-structure called TEI (transport stream error indicator). Cc: Manu Abraham Signed-off-by: Martin Wilks Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 63cb4f8..dba2c04 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -4169,6 +4169,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) switch (state->config->ts1_mode) { case STV090x_TSMODE_PARALLEL_PUNCTURED: reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) @@ -4177,6 +4178,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_DVBCI: reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) @@ -4185,6 +4187,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_SERIAL_PUNCTURED: reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) @@ -4193,6 +4196,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_SERIAL_CONTINUOUS: reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) @@ -4206,6 +4210,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) switch (state->config->ts2_mode) { case STV090x_TSMODE_PARALLEL_PUNCTURED: reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) @@ -4214,6 +4219,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_DVBCI: reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) @@ -4222,6 +4228,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_SERIAL_PUNCTURED: reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) @@ -4230,6 +4237,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_SERIAL_CONTINUOUS: reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h index 11754f2..a3fefa7 100644 --- a/drivers/media/dvb/frontends/stv090x.h +++ b/drivers/media/dvb/frontends/stv090x.h @@ -78,6 +78,9 @@ struct stv090x_config { u32 ts1_clk; u32 ts2_clk; + u8 ts1_tei : 1; + u8 ts2_tei : 1; + enum stv090x_i2crpt repeater_level; u8 tuner_bbgain; /* default: 10db */ -- cgit v0.10.2 From 739ff04f63ba6498b287021649cb999e639c3c83 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sun, 31 Oct 2010 16:24:19 -0300 Subject: [media] technisat-usb2: added driver for Technisat's USB2.0 DVB-S/S2 receiver This patch is adding support for Technisat's new USB2.0 DVB-S/S2 receiver device. The development was sponsored by Technisat. The Green led is toggle depending on the frontend-state. The Red LED is turned on all the time. The MAC address reading from the EEPROM along with the LRC-method to check whether its valid. Support for the IR-receiver of the Technisat USB2 box. The keys of small, black remote-control are built-in, repeated key behaviour are simulated. The i2c-mutex of the dvb-usb-structure is used as a general mutex for USB requests, as there are 3 threads racing for atomic requests consisting of multiple usb-requests. A module option is there which disables the toggling of LEDs by the driver on certain triggers. Useful when being used in a "dark" environment. [mchehab@redhat.com: Fix merge conflicts with RC renaming patches] Signed-off-by: Martin Wilks Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 3d48ba0..fe4f894 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -358,3 +358,11 @@ config DVB_USB_LME2510 select DVB_IX2505V if !DVB_FE_CUSTOMISE help Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 . + +config DVB_USB_TECHNISAT_USB2 + tristate "Technisat DVB-S/S2 USB2.0 support" + depends on DVB_USB + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE + help + Say Y here to support the Technisat USB2 DVB-S/S2 device diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 5b1d12f..4bac13d 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -91,6 +91,9 @@ obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o dvb-usb-lmedm04-objs = lmedm04.o obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o +dvb-usb-technisat-usb2-objs = technisat-usb2.o +obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 1a6310b..d0bce04 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -312,4 +312,5 @@ #define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac #define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001 #define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002 +#define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500 #endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 23005b3..f511418 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -246,7 +246,7 @@ static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d) dev->map_name = d->props.rc.core.rc_codes; dev->change_protocol = d->props.rc.core.change_protocol; dev->allowed_protos = d->props.rc.core.allowed_protos; - dev->driver_type = RC_DRIVER_SCANCODE; + dev->driver_type = d->props.rc.core.driver_type; usb_to_input_id(d->udev, &dev->input_id); dev->input_name = "IR-receiver inside an USB DVB receiver"; dev->input_phys = d->rc_phys; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 65fa926..76a8096 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -181,6 +181,7 @@ struct dvb_rc_legacy { * @rc_codes: name of rc codes table * @protocol: type of protocol(s) currently used by the driver * @allowed_protos: protocol(s) supported by the driver + * @driver_type: Used to point if a device supports raw mode * @change_protocol: callback to change protocol * @rc_query: called to query an event event. * @rc_interval: time in ms between two queries. @@ -190,6 +191,7 @@ struct dvb_rc { char *rc_codes; u64 protocol; u64 allowed_protos; + enum rc_driver_type driver_type; int (*change_protocol)(struct rc_dev *dev, u64 rc_type); char *module_name; int (*rc_query) (struct dvb_usb_device *d); diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/dvb/dvb-usb/technisat-usb2.c new file mode 100644 index 0000000..406602a --- /dev/null +++ b/drivers/media/dvb/dvb-usb/technisat-usb2.c @@ -0,0 +1,808 @@ +/* + * Linux driver for Technisat DVB-S/S2 USB 2.0 device + * + * Copyright (C) 2010 Patrick Boettcher, + * Kernel Labs Inc. PO Box 745, St James, NY 11780 + * + * Development was sponsored by Technisat Digital UK Limited, whose + * registered office is Witan Gate House 500 - 600 Witan Gate West, + * Milton Keynes, MK9 1SH + * + * 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. + * + * + * 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. + * + * THIS PROGRAM IS PROVIDED "AS IS" AND BOTH THE COPYRIGHT HOLDER AND + * TECHNISAT DIGITAL UK LTD DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS PROGRAM INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COPYRIGHT HOLDER + * NOR TECHNISAT DIGITAL UK LIMITED SHALL BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS PROGRAM. See the + * GNU General Public License for more details. + */ + +#define DVB_USB_LOG_PREFIX "technisat-usb2" +#include "dvb-usb.h" + +#include "stv6110x.h" +#include "stv090x.h" + +/* module parameters */ +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (bit-mask: 1=info,2=eeprom,4=i2c,8=rc)." \ + DVB_USB_DEBUG_STATUS); + +/* disables all LED control command and + * also does not start the signal polling thread */ +static int disable_led_control; +module_param(disable_led_control, int, 0444); +MODULE_PARM_DESC(disable_led_control, + "disable LED control of the device " + "(default: 0 - LED control is active)."); + +/* device private data */ +struct technisat_usb2_state { + struct dvb_usb_device *dev; + struct delayed_work green_led_work; + u8 power_state; + + u16 last_scan_code; +}; + +/* debug print helpers */ +#define deb_info(args...) dprintk(debug, 0x01, args) +#define deb_eeprom(args...) dprintk(debug, 0x02, args) +#define deb_i2c(args...) dprintk(debug, 0x04, args) +#define deb_rc(args...) dprintk(debug, 0x08, args) + +/* vendor requests */ +#define SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST 0xB3 +#define SET_FRONT_END_RESET_VENDOR_REQUEST 0xB4 +#define GET_VERSION_INFO_VENDOR_REQUEST 0xB5 +#define SET_GREEN_LED_VENDOR_REQUEST 0xB6 +#define SET_RED_LED_VENDOR_REQUEST 0xB7 +#define GET_IR_DATA_VENDOR_REQUEST 0xB8 +#define SET_LED_TIMER_DIVIDER_VENDOR_REQUEST 0xB9 +#define SET_USB_REENUMERATION 0xBA + +/* i2c-access methods */ +#define I2C_SPEED_100KHZ_BIT 0x40 + +#define I2C_STATUS_NAK 7 +#define I2C_STATUS_OK 8 + +static int technisat_usb2_i2c_access(struct usb_device *udev, + u8 device_addr, u8 *tx, u8 txlen, u8 *rx, u8 rxlen) +{ + u8 b[64]; + int ret, actual_length; + + deb_i2c("i2c-access: %02x, tx: ", device_addr); + debug_dump(tx, txlen, deb_i2c); + deb_i2c(" "); + + if (txlen > 62) { + err("i2c TX buffer can't exceed 62 bytes (dev 0x%02x)", + device_addr); + txlen = 62; + } + if (rxlen > 62) { + err("i2c RX buffer can't exceed 62 bytes (dev 0x%02x)", + device_addr); + txlen = 62; + } + + b[0] = I2C_SPEED_100KHZ_BIT; + b[1] = device_addr << 1; + + if (rx != NULL) { + b[0] |= rxlen; + b[1] |= 1; + } + + memcpy(&b[2], tx, txlen); + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, 0x01), + b, 2 + txlen, + NULL, 1000); + + if (ret < 0) { + err("i2c-error: out failed %02x = %d", device_addr, ret); + return -ENODEV; + } + + ret = usb_bulk_msg(udev, + usb_rcvbulkpipe(udev, 0x01), + b, 64, &actual_length, 1000); + if (ret < 0) { + err("i2c-error: in failed %02x = %d", device_addr, ret); + return -ENODEV; + } + + if (b[0] != I2C_STATUS_OK) { + err("i2c-error: %02x = %d", device_addr, b[0]); + /* handle tuner-i2c-nak */ + if (!(b[0] == I2C_STATUS_NAK && + device_addr == 0x60 + /* && device_is_technisat_usb2 */)) + return -ENODEV; + } + + deb_i2c("status: %d, ", b[0]); + + if (rx != NULL) { + memcpy(rx, &b[2], rxlen); + + deb_i2c("rx (%d): ", rxlen); + debug_dump(rx, rxlen, deb_i2c); + } + + deb_i2c("\n"); + + return 0; +} + +static int technisat_usb2_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int num) +{ + int ret = 0, i; + struct dvb_usb_device *d = i2c_get_adapdata(adap); + + /* Ensure nobody else hits the i2c bus while we're sending our + sequence of messages, (such as the remote control thread) */ + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + if (i+1 < num && msg[i+1].flags & I2C_M_RD) { + ret = technisat_usb2_i2c_access(d->udev, msg[i+1].addr, + msg[i].buf, msg[i].len, + msg[i+1].buf, msg[i+1].len); + if (ret != 0) + break; + i++; + } else { + ret = technisat_usb2_i2c_access(d->udev, msg[i].addr, + msg[i].buf, msg[i].len, + NULL, 0); + if (ret != 0) + break; + } + } + + if (ret == 0) + ret = i; + + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static u32 technisat_usb2_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm technisat_usb2_i2c_algo = { + .master_xfer = technisat_usb2_i2c_xfer, + .functionality = technisat_usb2_i2c_func, +}; + +#if 0 +static void technisat_usb2_frontend_reset(struct usb_device *udev) +{ + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SET_FRONT_END_RESET_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + 10, 0, + NULL, 0, 500); +} +#endif + +/* LED control */ +enum technisat_usb2_led_state { + LED_OFF, + LED_BLINK, + LED_ON, + LED_UNDEFINED +}; + +static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum technisat_usb2_led_state state) +{ + int ret; + + u8 led[8] = { + red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, + 0 + }; + + if (disable_led_control && state != LED_OFF) + return 0; + + switch (state) { + case LED_ON: + led[1] = 0x82; + break; + case LED_BLINK: + led[1] = 0x82; + if (red) { + led[2] = 0x02; + led[3] = 10; + led[4] = 10; + } else { + led[2] = 0xff; + led[3] = 50; + led[4] = 50; + } + led[5] = 1; + break; + + default: + case LED_OFF: + led[1] = 0x80; + break; + } + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + 0, 0, + led, sizeof(led), 500); + + mutex_unlock(&d->i2c_mutex); + return ret; +} + +static int technisat_usb2_set_led_timer(struct dvb_usb_device *d, u8 red, u8 green) +{ + int ret; + u8 b = 0; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + SET_LED_TIMER_DIVIDER_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + (red << 8) | green, 0, + &b, 1, 500); + + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static void technisat_usb2_green_led_control(struct work_struct *work) +{ + struct technisat_usb2_state *state = + container_of(work, struct technisat_usb2_state, green_led_work.work); + struct dvb_frontend *fe = state->dev->adapter[0].fe; + + if (state->power_state == 0) + goto schedule; + + if (fe != NULL) { + enum fe_status status; + + if (fe->ops.read_status(fe, &status) != 0) + goto schedule; + + if (status & FE_HAS_LOCK) { + u32 ber; + + if (fe->ops.read_ber(fe, &ber) != 0) + goto schedule; + + if (ber > 1000) + technisat_usb2_set_led(state->dev, 0, LED_BLINK); + else + technisat_usb2_set_led(state->dev, 0, LED_ON); + } else + technisat_usb2_set_led(state->dev, 0, LED_OFF); + } + +schedule: + schedule_delayed_work(&state->green_led_work, + msecs_to_jiffies(500)); +} + +/* method to find out whether the firmware has to be downloaded or not */ +static int technisat_usb2_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, int *cold) +{ + int ret; + u8 version[3]; + + /* first select the interface */ + if (usb_set_interface(udev, 0, 1) != 0) { + err("could not set alternate setting to 0"); + } else { + info("set alternate setting"); + } + + *cold = 0; /* by default do not download a firmware - just in case something is wrong */ + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + GET_VERSION_INFO_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_IN, + 0, 0, + version, sizeof(version), 500); + + if (ret < 0) + *cold = 1; + else { + info("firmware version: %d.%d", version[1], version[2]); + *cold = 0; + } + + return 0; +} + +/* power control */ +static int technisat_usb2_power_ctrl(struct dvb_usb_device *d, int level) +{ + struct technisat_usb2_state *state = d->priv; + + state->power_state = level; + + if (disable_led_control) + return 0; + + /* green led is turned off in any case - will be turned on when tuning */ + technisat_usb2_set_led(d, 0, LED_OFF); + /* red led is turned on all the time */ + technisat_usb2_set_led(d, 1, LED_ON); + return 0; +} + +/* mac address reading - from the eeprom */ +#if 0 +static void technisat_usb2_eeprom_dump(struct dvb_usb_device *d) +{ + u8 reg; + u8 b[16]; + int i,j; + + /* full EEPROM dump */ + for (j = 0; j < 256 * 4; j += 16) { + reg = j; + if (technisat_usb2_i2c_access(d->udev, 0x50 + j / 256, ®, 1, b, 16) != 0) + break; + + deb_eeprom("EEPROM: %01x%02x: ", j / 256, reg); + for (i = 0; i < 16; i++) + deb_eeprom("%02x ", b[i]); + deb_eeprom("\n"); + } +} +#endif + +static u8 technisat_usb2_calc_lrc(const u8 *b, u16 length) +{ + u8 lrc = 0; + while (--length) + lrc ^= *b++; + return lrc; +} + +static int technisat_usb2_eeprom_lrc_read(struct dvb_usb_device *d, + u16 offset, u8 *b, u16 length, u8 tries) +{ + u8 bo = offset & 0xff; + struct i2c_msg msg[] = { + { + .addr = 0x50 | ((offset >> 8) & 0x3), + .buf = &bo, + .len = 1 + }, { + .addr = 0x50 | ((offset >> 8) & 0x3), + .flags = I2C_M_RD, + .buf = b, + .len = length + } + }; + + while (tries--) { + int status; + + if (i2c_transfer(&d->i2c_adap, msg, 2) != 2) + break; + + status = + technisat_usb2_calc_lrc(b, length - 1) == b[length - 1]; + + if (status) + return 0; + } + + return -EREMOTEIO; +} + +#define EEPROM_MAC_START 0x3f8 +#define EEPROM_MAC_TOTAL 8 +static int technisat_usb2_read_mac_address(struct dvb_usb_device *d, + u8 mac[]) +{ + u8 buf[EEPROM_MAC_TOTAL]; + + if (technisat_usb2_eeprom_lrc_read(d, EEPROM_MAC_START, + buf, EEPROM_MAC_TOTAL, 4) != 0) + return -ENODEV; + + memcpy(mac, buf, 6); + return 0; +} + +/* frontend attach */ +static int technisat_usb2_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + int i; + u8 gpio[3] = { 0 }; /* 0 = 2, 1 = 3, 2 = 4 */ + + gpio[2] = 1; /* high - voltage ? */ + + switch (voltage) { + case SEC_VOLTAGE_13: + gpio[0] = 1; + break; + case SEC_VOLTAGE_18: + gpio[0] = 1; + gpio[1] = 1; + break; + default: + case SEC_VOLTAGE_OFF: + break; + } + + for (i = 0; i < 3; i++) + if (stv090x_set_gpio(fe, i+2, 0, gpio[i], 0) != 0) + return -EREMOTEIO; + return 0; +} + +static struct stv090x_config technisat_usb2_stv090x_config = { + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 8000000, + .address = 0x68, + + .ts1_mode = STV090x_TSMODE_DVBCI, + .ts1_clk = 13400000, + .ts1_tei = 1, + + .repeater_level = STV090x_RPTLEVEL_64, + + .tuner_bbgain = 6, +}; + +static struct stv6110x_config technisat_usb2_stv6110x_config = { + .addr = 0x60, + .refclk = 16000000, + .clk_div = 2, +}; + +static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a) +{ + struct usb_device *udev = a->dev->udev; + int ret; + + a->fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config, + &a->dev->i2c_adap, STV090x_DEMODULATOR_0); + + if (a->fe) { + struct stv6110x_devctl *ctl; + + ctl = dvb_attach(stv6110x_attach, + a->fe, + &technisat_usb2_stv6110x_config, + &a->dev->i2c_adap); + + if (ctl) { + technisat_usb2_stv090x_config.tuner_init = ctl->tuner_init; + technisat_usb2_stv090x_config.tuner_sleep = ctl->tuner_sleep; + technisat_usb2_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; + technisat_usb2_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; + technisat_usb2_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; + technisat_usb2_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; + technisat_usb2_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; + technisat_usb2_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; + technisat_usb2_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; + technisat_usb2_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; + technisat_usb2_stv090x_config.tuner_get_status = ctl->tuner_get_status; + + /* call the init function once to initialize + tuner's clock output divider and demod's + master clock */ + if (a->fe->ops.init) + a->fe->ops.init(a->fe); + + if (mutex_lock_interruptible(&a->dev->i2c_mutex) < 0) + return -EAGAIN; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + 0, 0, + NULL, 0, 500); + mutex_unlock(&a->dev->i2c_mutex); + + if (ret != 0) + err("could not set IF_CLK to external"); + + a->fe->ops.set_voltage = technisat_usb2_set_voltage; + + /* if everything was successful assign a nice name to the frontend */ + strlcpy(a->fe->ops.info.name, a->dev->desc->name, + sizeof(a->fe->ops.info.name)); + } else { + dvb_frontend_detach(a->fe); + a->fe = NULL; + } + } + + technisat_usb2_set_led_timer(a->dev, 1, 1); + + return a->fe == NULL ? -ENODEV : 0; +} + +/* Remote control */ + +/* the device is giving providing raw IR-signals to the host mapping + * it only to one remote control is just the default implementation + */ +#define NOMINAL_IR_BIT_TRANSITION_TIME_US 889 +#define NOMINAL_IR_BIT_TIME_US (2 * NOMINAL_IR_BIT_TRANSITION_TIME_US) + +#define FIRMWARE_CLOCK_TICK 83333 +#define FIRMWARE_CLOCK_DIVISOR 256 + +#define IR_PERCENT_TOLERANCE 15 + +#define NOMINAL_IR_BIT_TRANSITION_TICKS ((NOMINAL_IR_BIT_TRANSITION_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK) +#define NOMINAL_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICKS / FIRMWARE_CLOCK_DIVISOR) + +#define NOMINAL_IR_BIT_TIME_TICKS ((NOMINAL_IR_BIT_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK) +#define NOMINAL_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICKS / FIRMWARE_CLOCK_DIVISOR) + +#define MINIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT - ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) +#define MAXIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT + ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) + +#define MINIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT - ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) +#define MAXIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT + ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) + +static int technisat_usb2_get_ir(struct dvb_usb_device *d) +{ + u8 buf[62], *b; + int ret; + struct ir_raw_event ev; + + buf[0] = GET_IR_DATA_VENDOR_REQUEST; + buf[1] = 0x08; + buf[2] = 0x8f; + buf[3] = MINIMUM_IR_BIT_TRANSITION_TICK_COUNT; + buf[4] = MAXIMUM_IR_BIT_TIME_TICK_COUNT; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GET_IR_DATA_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_OUT, + 0, 0, + buf, 5, 500); + if (ret < 0) + goto unlock; + + buf[1] = 0; + buf[2] = 0; + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), + GET_IR_DATA_VENDOR_REQUEST, + USB_TYPE_VENDOR | USB_DIR_IN, + 0x8080, 0, + buf, sizeof(buf), 500); + +unlock: + mutex_unlock(&d->i2c_mutex); + + if (ret < 0) + return ret; + + if (ret == 1) + return 0; /* no key pressed */ + + /* decoding */ + b = buf+1; + +#if 0 + deb_rc("RC: %d ", ret); + debug_dump(b, ret, deb_rc); +#endif + + ev.pulse = 0; + while (1) { + ev.pulse = !ev.pulse; + ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * FIRMWARE_CLOCK_TICK) / 1000; + ir_raw_event_store(d->rc_dev, &ev); + + b++; + if (*b == 0xff) { + ev.pulse = 0; + ev.duration = 888888*2; + ir_raw_event_store(d->rc_dev, &ev); + break; + } + } + + ir_raw_event_handle(d->rc_dev); + + return 1; +} + +static int technisat_usb2_rc_query(struct dvb_usb_device *d) +{ + int ret = technisat_usb2_get_ir(d); + + if (ret < 0) + return ret; + + if (ret == 0) + return 0; + + if (!disable_led_control) + technisat_usb2_set_led(d, 1, LED_BLINK); + + return 0; +} + +/* DVB-USB and USB stuff follows */ +static struct usb_device_id technisat_usb2_id_table[] = { + { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_DVB_S2) }, + { 0 } /* Terminating entry */ +}; + +/* device description */ +static struct dvb_usb_device_properties technisat_usb2_devices = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .identify_state = technisat_usb2_identify_state, + .firmware = "dvb-usb-SkyStar_USB_HD_FW_v17_63.HEX.fw", + + .size_of_priv = sizeof(struct technisat_usb2_state), + + .i2c_algo = &technisat_usb2_i2c_algo, + + .power_ctrl = technisat_usb2_power_ctrl, + .read_mac_address = technisat_usb2_read_mac_address, + + .num_adapters = 1, + .adapter = { + { + .frontend_attach = technisat_usb2_frontend_attach, + + .stream = { + .type = USB_ISOC, + .count = 8, + .endpoint = 0x2, + .u = { + .isoc = { + .framesperurb = 32, + .framesize = 2048, + .interval = 3, + } + } + }, + + .size_of_priv = 0, + }, + }, + + .num_device_descs = 1, + .devices = { + { "Technisat SkyStar USB HD (DVB-S/S2)", + { &technisat_usb2_id_table[0], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = 100, + .rc_codes = RC_MAP_TECHNISAT_USB2, + .module_name = "technisat-usb2", + .rc_query = technisat_usb2_rc_query, + .allowed_protos = RC_TYPE_ALL, + .driver_type = RC_DRIVER_IR_RAW, + } +}; + +static int technisat_usb2_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *dev; + + if (dvb_usb_device_init(intf, &technisat_usb2_devices, THIS_MODULE, + &dev, adapter_nr) != 0) + return -ENODEV; + + if (dev) { + struct technisat_usb2_state *state = dev->priv; + state->dev = dev; + + if (!disable_led_control) { + INIT_DELAYED_WORK(&state->green_led_work, + technisat_usb2_green_led_control); + schedule_delayed_work(&state->green_led_work, + msecs_to_jiffies(500)); + } + } + + return 0; +} + +static void technisat_usb2_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *dev = usb_get_intfdata(intf); + + /* work and stuff was only created when the device is is hot-state */ + if (dev != NULL) { + struct technisat_usb2_state *state = dev->priv; + if (state != NULL) { + cancel_rearming_delayed_work(&state->green_led_work); + flush_scheduled_work(); + } + } + + dvb_usb_device_exit(intf); +} + +static struct usb_driver technisat_usb2_driver = { + .name = "dvb_usb_technisat_usb2", + .probe = technisat_usb2_probe, + .disconnect = technisat_usb2_disconnect, + .id_table = technisat_usb2_id_table, +}; + +/* module stuff */ +static int __init technisat_usb2_module_init(void) +{ + int result = usb_register(&technisat_usb2_driver); + if (result) { + err("usb_register failed. Code %d", result); + return result; + } + + return 0; +} + +static void __exit technisat_usb2_module_exit(void) +{ + usb_deregister(&technisat_usb2_driver); +} + +module_init(technisat_usb2_module_init); +module_exit(technisat_usb2_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for Technisat DVB-S/S2 USB 2.0 device"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 0659e9f..f0c8055 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-real-audio-220-32-keys.o \ rc-streamzap.o \ rc-tbs-nec.o \ + rc-technisat-usb2.o \ rc-terratec-cinergy-xs.o \ rc-terratec-slim.o \ rc-tevii-nec.o \ diff --git a/drivers/media/rc/keymaps/rc-technisat-usb2.c b/drivers/media/rc/keymaps/rc-technisat-usb2.c new file mode 100644 index 0000000..4afe577 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-technisat-usb2.c @@ -0,0 +1,93 @@ +/* rc-technisat-usb2.c - Keytable for SkyStar HD USB + * + * Copyright (C) 2010 Patrick Boettcher, + * Kernel Labs Inc. PO Box 745, St James, NY 11780 + * + * Development was sponsored by Technisat Digital UK Limited, whose + * registered office is Witan Gate House 500 - 600 Witan Gate West, + * Milton Keynes, MK9 1SH + * + * 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. + * + * + * 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. + * + * THIS PROGRAM IS PROVIDED "AS IS" AND BOTH THE COPYRIGHT HOLDER AND + * TECHNISAT DIGITAL UK LTD DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS PROGRAM INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COPYRIGHT HOLDER + * NOR TECHNISAT DIGITAL UK LIMITED SHALL BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS PROGRAM. See the + * GNU General Public License for more details. + */ + +#include + +static struct rc_map_table technisat_usb2[] = { + {0x0a0c, KEY_POWER}, + {0x0a01, KEY_1}, + {0x0a02, KEY_2}, + {0x0a03, KEY_3}, + {0x0a0d, KEY_MUTE}, + {0x0a04, KEY_4}, + {0x0a05, KEY_5}, + {0x0a06, KEY_6}, + {0x0a38, KEY_VIDEO}, /* EXT */ + {0x0a07, KEY_7}, + {0x0a08, KEY_8}, + {0x0a09, KEY_9}, + {0x0a00, KEY_0}, + {0x0a4f, KEY_INFO}, + {0x0a20, KEY_CHANNELUP}, + {0x0a52, KEY_MENU}, + {0x0a11, KEY_VOLUMEUP}, + {0x0a57, KEY_OK}, + {0x0a10, KEY_VOLUMEDOWN}, + {0x0a2f, KEY_EPG}, + {0x0a21, KEY_CHANNELDOWN}, + {0x0a22, KEY_REFRESH}, + {0x0a3c, KEY_TEXT}, + {0x0a76, KEY_ENTER}, /* HOOK */ + {0x0a0f, KEY_HELP}, + {0x0a6b, KEY_RED}, + {0x0a6c, KEY_GREEN}, + {0x0a6d, KEY_YELLOW}, + {0x0a6e, KEY_BLUE}, + {0x0a29, KEY_STOP}, + {0x0a23, KEY_LANGUAGE}, + {0x0a53, KEY_TV}, + {0x0a0a, KEY_PROGRAM}, +}; + +static struct rc_map_list technisat_usb2_map = { + .map = { + .scan = technisat_usb2, + .size = ARRAY_SIZE(technisat_usb2), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_TECHNISAT_USB2, + } +}; + +static int __init init_rc_map(void) +{ + return rc_map_register(&technisat_usb2_map); +} + +static void __exit exit_rc_map(void) +{ + rc_map_unregister(&technisat_usb2_map); +} + +module_init(init_rc_map) +module_exit(exit_rc_map) + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_LICENSE("GPL"); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index ee9e2f74..4617117 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -131,6 +131,7 @@ void rc_map_init(void); #define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys" #define RC_MAP_STREAMZAP "rc-streamzap" #define RC_MAP_TBS_NEC "rc-tbs-nec" +#define RC_MAP_TECHNISAT_USB2 "rc-technisat-usb2" #define RC_MAP_TERRATEC_CINERGY_XS "rc-terratec-cinergy-xs" #define RC_MAP_TERRATEC_SLIM "rc-terratec-slim" #define RC_MAP_TEVII_NEC "rc-tevii-nec" -- cgit v0.10.2 From 4ec02ea0d88d811f61e2c7765418eff770936871 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 10 Jan 2011 21:42:12 -0200 Subject: [media] technisat-usb2: Don't use a deprecated call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/dvb/dvb-usb/technisat-usb2.c: In function ‘technisat_usb2_disconnect’: drivers/media/dvb/dvb-usb/technisat-usb2.c:770: warning: ‘cancel_rearming_delayed_work’ is deprecated (declared at include/linux/workqueue.h:421) Cc: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/dvb/dvb-usb/technisat-usb2.c index 406602a..10151aa 100644 --- a/drivers/media/dvb/dvb-usb/technisat-usb2.c +++ b/drivers/media/dvb/dvb-usb/technisat-usb2.c @@ -767,7 +767,7 @@ static void technisat_usb2_disconnect(struct usb_interface *intf) if (dev != NULL) { struct technisat_usb2_state *state = dev->priv; if (state != NULL) { - cancel_rearming_delayed_work(&state->green_led_work); + cancel_delayed_work_sync(&state->green_led_work); flush_scheduled_work(); } } -- cgit v0.10.2 From f8f3914cf922f5f9e1d60e9e10f6fb92742907ad Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Thu, 29 Jul 2010 14:44:25 -0300 Subject: [media] v4l: Add multi-planar API definitions to the V4L2 API Multi-planar API is as a backwards-compatible extension of the V4L2 API, which allows video buffers to consist of one or more planes. Planes are separate memory buffers; each has its own mapping, backed by usually separate physical memory buffers. Many different uses for the multi-planar API are possible, examples include: - embedded devices requiring video components to be placed in physically separate buffers, e.g. for Samsung S3C/S5P SoC series' video codec, Y and interleaved Cb/Cr components reside in buffers in different memory banks; - applications may receive (or choose to store) video data of one video buffer in separate memory buffers; such data would have to be temporarily copied together into one buffer before passing it to a V4L2 device; - applications or drivers may want to pass metadata related to a buffer and it may not be possible to place it in the same buffer, together with video data. [mchehab@redhat.com: CodingStyle fixes] Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park Reviewed-by: Marek Szyprowski Signed-off-by: Marek Szyprowski Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index f51327e..1a9d90a 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -165,6 +165,8 @@ const char *v4l2_type_names[] = { [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap", [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay", + [V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE] = "vid-cap-mplane", + [V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE] = "vid-out-mplane", }; EXPORT_SYMBOL(v4l2_type_names); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 5f6f470..bb0a3ae 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -70,6 +70,7 @@ * Moved from videodev.h */ #define VIDEO_MAX_FRAME 32 +#define VIDEO_MAX_PLANES 8 #ifndef __KERNEL__ @@ -157,9 +158,23 @@ enum v4l2_buf_type { /* Experimental */ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, #endif + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10, V4L2_BUF_TYPE_PRIVATE = 0x80, }; +#define V4L2_TYPE_IS_MULTIPLANAR(type) \ + ((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE \ + || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + +#define V4L2_TYPE_IS_OUTPUT(type) \ + ((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT \ + || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE \ + || (type) == V4L2_BUF_TYPE_VIDEO_OVERLAY \ + || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY \ + || (type) == V4L2_BUF_TYPE_VBI_OUTPUT \ + || (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) + enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, V4L2_TUNER_ANALOG_TV = 2, @@ -245,6 +260,11 @@ struct v4l2_capability { #define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */ #define V4L2_CAP_RDS_OUTPUT 0x00000800 /* Is an RDS encoder */ +/* Is a video capture device that supports multiplanar formats */ +#define V4L2_CAP_VIDEO_CAPTURE_MPLANE 0x00001000 +/* Is a video output device that supports multiplanar formats */ +#define V4L2_CAP_VIDEO_OUTPUT_MPLANE 0x00002000 + #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ #define V4L2_CAP_RADIO 0x00040000 /* is a radio device */ @@ -517,6 +537,62 @@ struct v4l2_requestbuffers { __u32 reserved[2]; }; +/** + * struct v4l2_plane - plane info for multi-planar buffers + * @bytesused: number of bytes occupied by data in the plane (payload) + * @length: size of this plane (NOT the payload) in bytes + * @mem_offset: when memory in the associated struct v4l2_buffer is + * V4L2_MEMORY_MMAP, equals the offset from the start of + * the device memory for this plane (or is a "cookie" that + * should be passed to mmap() called on the video node) + * @userptr: when memory is V4L2_MEMORY_USERPTR, a userspace pointer + * pointing to this plane + * @data_offset: offset in the plane to the start of data; usually 0, + * unless there is a header in front of the data + * + * Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer + * with two planes can have one plane for Y, and another for interleaved CbCr + * components. Each plane can reside in a separate memory buffer, or even in + * a completely separate memory node (e.g. in embedded devices). + */ +struct v4l2_plane { + __u32 bytesused; + __u32 length; + union { + __u32 mem_offset; + unsigned long userptr; + } m; + __u32 data_offset; + __u32 reserved[11]; +}; + +/** + * struct v4l2_buffer - video buffer info + * @index: id number of the buffer + * @type: buffer type (type == *_MPLANE for multiplanar buffers) + * @bytesused: number of bytes occupied by data in the buffer (payload); + * unused (set to 0) for multiplanar buffers + * @flags: buffer informational flags + * @field: field order of the image in the buffer + * @timestamp: frame timestamp + * @timecode: frame timecode + * @sequence: sequence count of this frame + * @memory: the method, in which the actual video data is passed + * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP; + * offset from the start of the device memory for this plane, + * (or a "cookie" that should be passed to mmap() as offset) + * @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR; + * a userspace pointer pointing to this buffer + * @planes: for multiplanar buffers; userspace pointer to the array of plane + * info structs for this buffer + * @length: size in bytes of the buffer (NOT its payload) for single-plane + * buffers (when type != *_MPLANE); number of elements in the + * planes array for multi-plane buffers + * @input: input number from which the video data has has been captured + * + * Contains data exchanged by application and driver using one of the Streaming + * I/O methods. + */ struct v4l2_buffer { __u32 index; enum v4l2_buf_type type; @@ -532,6 +608,7 @@ struct v4l2_buffer { union { __u32 offset; unsigned long userptr; + struct v4l2_plane *planes; } m; __u32 length; __u32 input; @@ -1622,12 +1699,56 @@ struct v4l2_mpeg_vbi_fmt_ivtv { * A G G R E G A T E S T R U C T U R E S */ -/* Stream data format +/** + * struct v4l2_plane_pix_format - additional, per-plane format definition + * @sizeimage: maximum size in bytes required for data, for which + * this plane will be used + * @bytesperline: distance in bytes between the leftmost pixels in two + * adjacent lines + */ +struct v4l2_plane_pix_format { + __u32 sizeimage; + __u16 bytesperline; + __u16 reserved[7]; +} __attribute__ ((packed)); + +/** + * struct v4l2_pix_format_mplane - multiplanar format definition + * @width: image width in pixels + * @height: image height in pixels + * @pixelformat: little endian four character code (fourcc) + * @field: field order (for interlaced video) + * @colorspace: supplemental to pixelformat + * @plane_fmt: per-plane information + * @num_planes: number of planes for this format + */ +struct v4l2_pix_format_mplane { + __u32 width; + __u32 height; + __u32 pixelformat; + enum v4l2_field field; + enum v4l2_colorspace colorspace; + + struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES]; + __u8 num_planes; + __u8 reserved[11]; +} __attribute__ ((packed)); + +/** + * struct v4l2_format - stream data format + * @type: type of the data stream + * @pix: definition of an image format + * @pix_mp: definition of a multiplanar image format + * @win: definition of an overlaid image + * @vbi: raw VBI capture or output parameters + * @sliced: sliced VBI capture or output parameters + * @raw_data: placeholder for future extensions and custom formats */ struct v4l2_format { enum v4l2_buf_type type; union { struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ + struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */ struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ @@ -1635,7 +1756,6 @@ struct v4l2_format { } fmt; }; - /* Stream type-dependent parameters */ struct v4l2_streamparm { -- cgit v0.10.2 From d14e6d76ebf740fd0d0bd296933993a555938896 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Thu, 23 Dec 2010 04:15:27 -0300 Subject: [media] v4l: Add multi-planar ioctl handling code Add multi-planar API core ioctl handling and conversion functions. [mchehab@redhat.com: CondingStyle fixup] Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park Reviewed-by: Marek Szyprowski Signed-off-by: Marek Szyprowski Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 1a9d90a..8360ed2 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -428,20 +428,33 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd, struct v4l2_buffer *p) { struct v4l2_timecode *tc = &p->timecode; + struct v4l2_plane *plane; + int i; dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, " - "bytesused=%d, flags=0x%08d, " - "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n", + "flags=0x%08d, field=%0d, sequence=%d, memory=%s\n", p->timestamp.tv_sec / 3600, (int)(p->timestamp.tv_sec / 60) % 60, (int)(p->timestamp.tv_sec % 60), (long)p->timestamp.tv_usec, p->index, prt_names(p->type, v4l2_type_names), - p->bytesused, p->flags, - p->field, p->sequence, - prt_names(p->memory, v4l2_memory_names), - p->m.userptr, p->length); + p->flags, p->field, p->sequence, + prt_names(p->memory, v4l2_memory_names)); + + if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) { + for (i = 0; i < p->length; ++i) { + plane = &p->m.planes[i]; + dbgarg2("plane %d: bytesused=%d, data_offset=0x%08x " + "offset/userptr=0x%08lx, length=%d\n", + i, plane->bytesused, plane->data_offset, + plane->m.userptr, plane->length); + } + } else { + dbgarg2("bytesused=%d, offset/userptr=0x%08lx, length=%d\n", + p->bytesused, p->m.userptr, p->length); + } + dbgarg2("timecode=%02d:%02d:%02d type=%d, " "flags=0x%08d, frames=%d, userbits=0x%08x\n", tc->hours, tc->minutes, tc->seconds, @@ -469,6 +482,27 @@ static inline void v4l_print_pix_fmt(struct video_device *vfd, fmt->bytesperline, fmt->sizeimage, fmt->colorspace); }; +static inline void v4l_print_pix_fmt_mplane(struct video_device *vfd, + struct v4l2_pix_format_mplane *fmt) +{ + int i; + + dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, " + "colorspace=%d, num_planes=%d\n", + fmt->width, fmt->height, + (fmt->pixelformat & 0xff), + (fmt->pixelformat >> 8) & 0xff, + (fmt->pixelformat >> 16) & 0xff, + (fmt->pixelformat >> 24) & 0xff, + prt_names(fmt->field, v4l2_field_names), + fmt->colorspace, fmt->num_planes); + + for (i = 0; i < fmt->num_planes; ++i) + dbgarg2("plane %d: bytesperline=%d sizeimage=%d\n", i, + fmt->plane_fmt[i].bytesperline, + fmt->plane_fmt[i].sizeimage); +} + static inline void v4l_print_ext_ctrls(unsigned int cmd, struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals) { @@ -522,7 +556,12 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (ops->vidioc_g_fmt_vid_cap) + if (ops->vidioc_g_fmt_vid_cap || + ops->vidioc_g_fmt_vid_cap_mplane) + return 0; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (ops->vidioc_g_fmt_vid_cap_mplane) return 0; break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: @@ -530,7 +569,12 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) return 0; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (ops->vidioc_g_fmt_vid_out) + if (ops->vidioc_g_fmt_vid_out || + ops->vidioc_g_fmt_vid_out_mplane) + return 0; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (ops->vidioc_g_fmt_vid_out_mplane) return 0; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: @@ -561,12 +605,70 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) return -EINVAL; } +/** + * fmt_sp_to_mp() - Convert a single-plane format to its multi-planar 1-plane + * equivalent + */ +static int fmt_sp_to_mp(const struct v4l2_format *f_sp, + struct v4l2_format *f_mp) +{ + struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp; + const struct v4l2_pix_format *pix = &f_sp->fmt.pix; + + if (f_sp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + f_mp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + else if (f_sp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + f_mp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + else + return -EINVAL; + + pix_mp->width = pix->width; + pix_mp->height = pix->height; + pix_mp->pixelformat = pix->pixelformat; + pix_mp->field = pix->field; + pix_mp->colorspace = pix->colorspace; + pix_mp->num_planes = 1; + pix_mp->plane_fmt[0].sizeimage = pix->sizeimage; + pix_mp->plane_fmt[0].bytesperline = pix->bytesperline; + + return 0; +} + +/** + * fmt_mp_to_sp() - Convert a multi-planar 1-plane format to its single-planar + * equivalent + */ +static int fmt_mp_to_sp(const struct v4l2_format *f_mp, + struct v4l2_format *f_sp) +{ + const struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp; + struct v4l2_pix_format *pix = &f_sp->fmt.pix; + + if (f_mp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + f_sp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + else if (f_mp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + f_sp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + else + return -EINVAL; + + pix->width = pix_mp->width; + pix->height = pix_mp->height; + pix->pixelformat = pix_mp->pixelformat; + pix->field = pix_mp->field; + pix->colorspace = pix_mp->colorspace; + pix->sizeimage = pix_mp->plane_fmt[0].sizeimage; + pix->bytesperline = pix_mp->plane_fmt[0].bytesperline; + + return 0; +} + static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vfd = video_devdata(file); const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; void *fh = file->private_data; + struct v4l2_format f_copy; long ret = -EINVAL; if (ops == NULL) { @@ -635,6 +737,11 @@ static long __video_do_ioctl(struct file *file, if (ops->vidioc_enum_fmt_vid_cap) ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (ops->vidioc_enum_fmt_vid_cap_mplane) + ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, + fh, f); + break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: if (ops->vidioc_enum_fmt_vid_overlay) ret = ops->vidioc_enum_fmt_vid_overlay(file, @@ -644,6 +751,11 @@ static long __video_do_ioctl(struct file *file, if (ops->vidioc_enum_fmt_vid_out) ret = ops->vidioc_enum_fmt_vid_out(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (ops->vidioc_enum_fmt_vid_out_mplane) + ret = ops->vidioc_enum_fmt_vid_out_mplane(file, + fh, f); + break; case V4L2_BUF_TYPE_PRIVATE: if (ops->vidioc_enum_fmt_type_private) ret = ops->vidioc_enum_fmt_type_private(file, @@ -672,22 +784,90 @@ static long __video_do_ioctl(struct file *file, switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (ops->vidioc_g_fmt_vid_cap) + if (ops->vidioc_g_fmt_vid_cap) { ret = ops->vidioc_g_fmt_vid_cap(file, fh, f); + } else if (ops->vidioc_g_fmt_vid_cap_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_g_fmt_vid_cap_mplane(file, fh, + &f_copy); + if (ret) + break; + + /* Driver is currently in multi-planar format, + * we can't return it in single-planar API*/ + if (f_copy.fmt.pix_mp.num_planes > 1) { + ret = -EBUSY; + break; + } + + ret = fmt_mp_to_sp(&f_copy, f); + } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (ops->vidioc_g_fmt_vid_cap_mplane) { + ret = ops->vidioc_g_fmt_vid_cap_mplane(file, + fh, f); + } else if (ops->vidioc_g_fmt_vid_cap) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_g_fmt_vid_cap(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } + if (!ret) + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: if (ops->vidioc_g_fmt_vid_overlay) ret = ops->vidioc_g_fmt_vid_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (ops->vidioc_g_fmt_vid_out) + if (ops->vidioc_g_fmt_vid_out) { ret = ops->vidioc_g_fmt_vid_out(file, fh, f); + } else if (ops->vidioc_g_fmt_vid_out_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_g_fmt_vid_out_mplane(file, fh, + &f_copy); + if (ret) + break; + + /* Driver is currently in multi-planar format, + * we can't return it in single-planar API*/ + if (f_copy.fmt.pix_mp.num_planes > 1) { + ret = -EBUSY; + break; + } + + ret = fmt_mp_to_sp(&f_copy, f); + } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (ops->vidioc_g_fmt_vid_out_mplane) { + ret = ops->vidioc_g_fmt_vid_out_mplane(file, + fh, f); + } else if (ops->vidioc_g_fmt_vid_out) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_g_fmt_vid_out(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } + if (!ret) + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: if (ops->vidioc_g_fmt_vid_out_overlay) ret = ops->vidioc_g_fmt_vid_out_overlay(file, @@ -731,8 +911,44 @@ static long __video_do_ioctl(struct file *file, case V4L2_BUF_TYPE_VIDEO_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.pix); v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (ops->vidioc_s_fmt_vid_cap) + if (ops->vidioc_s_fmt_vid_cap) { ret = ops->vidioc_s_fmt_vid_cap(file, fh, f); + } else if (ops->vidioc_s_fmt_vid_cap_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_s_fmt_vid_cap_mplane(file, fh, + &f_copy); + if (ret) + break; + + if (f_copy.fmt.pix_mp.num_planes > 1) { + /* Drivers shouldn't adjust from 1-plane + * to more than 1-plane formats */ + ret = -EBUSY; + WARN_ON(1); + break; + } + + ret = fmt_mp_to_sp(&f_copy, f); + } + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + CLEAR_AFTER_FIELD(f, fmt.pix_mp); + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + if (ops->vidioc_s_fmt_vid_cap_mplane) { + ret = ops->vidioc_s_fmt_vid_cap_mplane(file, + fh, f); + } else if (ops->vidioc_s_fmt_vid_cap && + f->fmt.pix_mp.num_planes == 1) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_s_fmt_vid_cap(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); @@ -743,8 +959,44 @@ static long __video_do_ioctl(struct file *file, case V4L2_BUF_TYPE_VIDEO_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.pix); v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (ops->vidioc_s_fmt_vid_out) + if (ops->vidioc_s_fmt_vid_out) { ret = ops->vidioc_s_fmt_vid_out(file, fh, f); + } else if (ops->vidioc_s_fmt_vid_out_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_s_fmt_vid_out_mplane(file, fh, + &f_copy); + if (ret) + break; + + if (f_copy.fmt.pix_mp.num_planes > 1) { + /* Drivers shouldn't adjust from 1-plane + * to more than 1-plane formats */ + ret = -EBUSY; + WARN_ON(1); + break; + } + + ret = fmt_mp_to_sp(&f_copy, f); + } + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + CLEAR_AFTER_FIELD(f, fmt.pix_mp); + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + if (ops->vidioc_s_fmt_vid_out_mplane) { + ret = ops->vidioc_s_fmt_vid_out_mplane(file, + fh, f); + } else if (ops->vidioc_s_fmt_vid_out && + f->fmt.pix_mp.num_planes == 1) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_s_fmt_vid_out(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_mp_to_sp(&f_copy, f); + } break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); @@ -793,11 +1045,47 @@ static long __video_do_ioctl(struct file *file, switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.pix); - if (ops->vidioc_try_fmt_vid_cap) + if (ops->vidioc_try_fmt_vid_cap) { ret = ops->vidioc_try_fmt_vid_cap(file, fh, f); + } else if (ops->vidioc_try_fmt_vid_cap_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_try_fmt_vid_cap_mplane(file, + fh, &f_copy); + if (ret) + break; + + if (f_copy.fmt.pix_mp.num_planes > 1) { + /* Drivers shouldn't adjust from 1-plane + * to more than 1-plane formats */ + ret = -EBUSY; + WARN_ON(1); + break; + } + ret = fmt_mp_to_sp(&f_copy, f); + } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + CLEAR_AFTER_FIELD(f, fmt.pix_mp); + if (ops->vidioc_try_fmt_vid_cap_mplane) { + ret = ops->vidioc_try_fmt_vid_cap_mplane(file, + fh, f); + } else if (ops->vidioc_try_fmt_vid_cap && + f->fmt.pix_mp.num_planes == 1) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_try_fmt_vid_cap(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } + if (!ret) + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); if (ops->vidioc_try_fmt_vid_overlay) @@ -806,11 +1094,47 @@ static long __video_do_ioctl(struct file *file, break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.pix); - if (ops->vidioc_try_fmt_vid_out) + if (ops->vidioc_try_fmt_vid_out) { ret = ops->vidioc_try_fmt_vid_out(file, fh, f); + } else if (ops->vidioc_try_fmt_vid_out_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_try_fmt_vid_out_mplane(file, + fh, &f_copy); + if (ret) + break; + + if (f_copy.fmt.pix_mp.num_planes > 1) { + /* Drivers shouldn't adjust from 1-plane + * to more than 1-plane formats */ + ret = -EBUSY; + WARN_ON(1); + break; + } + ret = fmt_mp_to_sp(&f_copy, f); + } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + CLEAR_AFTER_FIELD(f, fmt.pix_mp); + if (ops->vidioc_try_fmt_vid_out_mplane) { + ret = ops->vidioc_try_fmt_vid_out_mplane(file, + fh, f); + } else if (ops->vidioc_try_fmt_vid_out && + f->fmt.pix_mp.num_planes == 1) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_try_fmt_vid_out(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } + if (!ret) + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); if (ops->vidioc_try_fmt_vid_out_overlay) @@ -1975,7 +2299,7 @@ static unsigned long cmd_input_size(unsigned int cmd) switch (cmd) { CMDINSIZE(ENUM_FMT, fmtdesc, type); CMDINSIZE(G_FMT, format, type); - CMDINSIZE(QUERYBUF, buffer, type); + CMDINSIZE(QUERYBUF, buffer, length); CMDINSIZE(G_PARM, streamparm, type); CMDINSIZE(ENUMSTD, standard, index); CMDINSIZE(ENUMINPUT, input, index); @@ -2000,6 +2324,49 @@ static unsigned long cmd_input_size(unsigned int cmd) } } +static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, + void * __user *user_ptr, void ***kernel_ptr) +{ + int ret = 0; + + switch (cmd) { + case VIDIOC_QUERYBUF: + case VIDIOC_QBUF: + case VIDIOC_DQBUF: { + struct v4l2_buffer *buf = parg; + + if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && buf->length > 0) { + if (buf->length > VIDEO_MAX_PLANES) { + ret = -EINVAL; + break; + } + *user_ptr = (void __user *)buf->m.planes; + *kernel_ptr = (void **)&buf->m.planes; + *array_size = sizeof(struct v4l2_plane) * buf->length; + ret = 1; + } + break; + } + + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: { + struct v4l2_ext_controls *ctrls = parg; + + if (ctrls->count != 0) { + *user_ptr = (void __user *)ctrls->controls; + *kernel_ptr = (void **)&ctrls->controls; + *array_size = sizeof(struct v4l2_ext_control) + * ctrls->count; + ret = 1; + } + break; + } + } + + return ret; +} + long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg) { @@ -2007,16 +2374,14 @@ long video_ioctl2(struct file *file, void *mbuf = NULL; void *parg = (void *)arg; long err = -EINVAL; - int is_ext_ctrl; - size_t ctrls_size = 0; + bool has_array_args; + size_t array_size = 0; void __user *user_ptr = NULL; + void **kernel_ptr = NULL; #ifdef __OLD_VIDIOC_ cmd = video_fix_command(cmd); #endif - is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || - cmd == VIDIOC_TRY_EXT_CTRLS); - /* Copy arguments into temp kernel buffer */ if (_IOC_DIR(cmd) != _IOC_NONE) { if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { @@ -2045,43 +2410,43 @@ long video_ioctl2(struct file *file, } } - if (is_ext_ctrl) { - struct v4l2_ext_controls *p = parg; + err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr); + if (err < 0) + goto out; + has_array_args = err; - /* In case of an error, tell the caller that it wasn't - a specific control that caused it. */ - p->error_idx = p->count; - user_ptr = (void __user *)p->controls; - if (p->count) { - ctrls_size = sizeof(struct v4l2_ext_control) * p->count; - /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ - mbuf = kmalloc(ctrls_size, GFP_KERNEL); - err = -ENOMEM; - if (NULL == mbuf) - goto out_ext_ctrl; - err = -EFAULT; - if (copy_from_user(mbuf, user_ptr, ctrls_size)) - goto out_ext_ctrl; - p->controls = mbuf; - } + if (has_array_args) { + /* + * When adding new types of array args, make sure that the + * parent argument to ioctl (which contains the pointer to the + * array) fits into sbuf (so that mbuf will still remain + * unused up to here). + */ + mbuf = kmalloc(array_size, GFP_KERNEL); + err = -ENOMEM; + if (NULL == mbuf) + goto out_array_args; + err = -EFAULT; + if (copy_from_user(mbuf, user_ptr, array_size)) + goto out_array_args; + *kernel_ptr = mbuf; } /* Handles IOCTL */ err = __video_do_ioctl(file, cmd, parg); if (err == -ENOIOCTLCMD) err = -EINVAL; - if (is_ext_ctrl) { - struct v4l2_ext_controls *p = parg; - p->controls = (void *)user_ptr; - if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) + if (has_array_args) { + *kernel_ptr = user_ptr; + if (copy_to_user(user_ptr, mbuf, array_size)) err = -EFAULT; - goto out_ext_ctrl; + goto out_array_args; } if (err < 0) goto out; -out_ext_ctrl: +out_array_args: /* Copy results into user buffer */ switch (_IOC_DIR(cmd)) { case _IOC_READ: diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 67df375..1572c7f 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -37,6 +37,10 @@ struct v4l2_ioctl_ops { struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_vid_out) (struct file *file, void *fh, struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_vid_cap_mplane)(struct file *file, void *fh, + struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_vid_out_mplane)(struct file *file, void *fh, + struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh, struct v4l2_fmtdesc *f); @@ -57,6 +61,10 @@ struct v4l2_ioctl_ops { struct v4l2_format *f); int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_g_fmt_vid_cap_mplane)(struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_g_fmt_vid_out_mplane)(struct file *file, void *fh, + struct v4l2_format *f); int (*vidioc_g_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); @@ -77,6 +85,10 @@ struct v4l2_ioctl_ops { struct v4l2_format *f); int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_s_fmt_vid_cap_mplane)(struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_s_fmt_vid_out_mplane)(struct file *file, void *fh, + struct v4l2_format *f); int (*vidioc_s_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); @@ -97,6 +109,10 @@ struct v4l2_ioctl_ops { struct v4l2_format *f); int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_try_fmt_vid_cap_mplane)(struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_try_fmt_vid_out_mplane)(struct file *file, void *fh, + struct v4l2_format *f); int (*vidioc_try_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); -- cgit v0.10.2 From 52a3082fea41ffe77003be76ac1496d60bb7908e Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Thu, 29 Jul 2010 14:56:47 -0300 Subject: [media] v4l: Add compat functions for the multi-planar API Add multi-planar ioctl handling to the 32bit compatibility layer. [mchehab@redhat.com: Merged with a fixup patch from Pawel] Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index dc82eb8..c19208a 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -97,6 +97,14 @@ static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi return 0; } +static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp, + struct v4l2_pix_format_mplane __user *up) +{ + if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane))) + return -EFAULT; + return 0; +} + static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up) { if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format))) @@ -104,6 +112,14 @@ static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi return 0; } +static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp, + struct v4l2_pix_format_mplane __user *up) +{ + if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane))) + return -EFAULT; + return 0; +} + static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up) { if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format))) @@ -136,6 +152,7 @@ struct v4l2_format32 { enum v4l2_buf_type type; union { struct v4l2_pix_format pix; + struct v4l2_pix_format_mplane pix_mp; struct v4l2_window32 win; struct v4l2_vbi_format vbi; struct v4l2_sliced_vbi_format sliced; @@ -152,6 +169,10 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp, + &up->fmt.pix_mp); case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: return get_v4l2_window32(&kp->fmt.win, &up->fmt.win); @@ -181,6 +202,10 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp, + &up->fmt.pix_mp); case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: return put_v4l2_window32(&kp->fmt.win, &up->fmt.win); @@ -232,6 +257,17 @@ static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 return 0; } +struct v4l2_plane32 { + __u32 bytesused; + __u32 length; + union { + __u32 mem_offset; + compat_long_t userptr; + } m; + __u32 data_offset; + __u32 reserved[11]; +}; + struct v4l2_buffer32 { __u32 index; enum v4l2_buf_type type; @@ -247,14 +283,64 @@ struct v4l2_buffer32 { union { __u32 offset; compat_long_t userptr; + compat_caddr_t planes; } m; __u32 length; __u32 input; __u32 reserved; }; +static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32, + enum v4l2_memory memory) +{ + void __user *up_pln; + compat_long_t p; + + if (copy_in_user(up, up32, 2 * sizeof(__u32)) || + copy_in_user(&up->data_offset, &up32->data_offset, + sizeof(__u32))) + return -EFAULT; + + if (memory == V4L2_MEMORY_USERPTR) { + if (get_user(p, &up32->m.userptr)) + return -EFAULT; + up_pln = compat_ptr(p); + if (put_user((unsigned long)up_pln, &up->m.userptr)) + return -EFAULT; + } else { + if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset, + sizeof(__u32))) + return -EFAULT; + } + + return 0; +} + +static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32, + enum v4l2_memory memory) +{ + if (copy_in_user(up32, up, 2 * sizeof(__u32)) || + copy_in_user(&up32->data_offset, &up->data_offset, + sizeof(__u32))) + return -EFAULT; + + /* For MMAP, driver might've set up the offset, so copy it back. + * USERPTR stays the same (was userspace-provided), so no copying. */ + if (memory == V4L2_MEMORY_MMAP) + if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset, + sizeof(__u32))) + return -EFAULT; + + return 0; +} + static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) { + struct v4l2_plane32 __user *uplane32; + struct v4l2_plane __user *uplane; + compat_caddr_t p; + int num_planes; + int ret; if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) || get_user(kp->index, &up->index) || @@ -263,33 +349,84 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user get_user(kp->memory, &up->memory) || get_user(kp->input, &up->input)) return -EFAULT; - switch (kp->memory) { - case V4L2_MEMORY_MMAP: - if (get_user(kp->length, &up->length) || - get_user(kp->m.offset, &up->m.offset)) + + if (V4L2_TYPE_IS_OUTPUT(kp->type)) + if (get_user(kp->bytesused, &up->bytesused) || + get_user(kp->field, &up->field) || + get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) || + get_user(kp->timestamp.tv_usec, + &up->timestamp.tv_usec)) return -EFAULT; - break; - case V4L2_MEMORY_USERPTR: - { - compat_long_t tmp; - if (get_user(kp->length, &up->length) || - get_user(tmp, &up->m.userptr)) + if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { + if (get_user(kp->length, &up->length)) return -EFAULT; - kp->m.userptr = (unsigned long)compat_ptr(tmp); + num_planes = kp->length; + if (num_planes == 0) { + kp->m.planes = NULL; + /* num_planes == 0 is legal, e.g. when userspace doesn't + * need planes array on DQBUF*/ + return 0; } - break; - case V4L2_MEMORY_OVERLAY: - if (get_user(kp->m.offset, &up->m.offset)) + + if (get_user(p, &up->m.planes)) return -EFAULT; - break; + + uplane32 = compat_ptr(p); + if (!access_ok(VERIFY_READ, uplane32, + num_planes * sizeof(struct v4l2_plane32))) + return -EFAULT; + + /* We don't really care if userspace decides to kill itself + * by passing a very big num_planes value */ + uplane = compat_alloc_user_space(num_planes * + sizeof(struct v4l2_plane)); + kp->m.planes = uplane; + + while (--num_planes >= 0) { + ret = get_v4l2_plane32(uplane, uplane32, kp->memory); + if (ret) + return ret; + ++uplane; + ++uplane32; + } + } else { + switch (kp->memory) { + case V4L2_MEMORY_MMAP: + if (get_user(kp->length, &up->length) || + get_user(kp->m.offset, &up->m.offset)) + return -EFAULT; + break; + case V4L2_MEMORY_USERPTR: + { + compat_long_t tmp; + + if (get_user(kp->length, &up->length) || + get_user(tmp, &up->m.userptr)) + return -EFAULT; + + kp->m.userptr = (unsigned long)compat_ptr(tmp); + } + break; + case V4L2_MEMORY_OVERLAY: + if (get_user(kp->m.offset, &up->m.offset)) + return -EFAULT; + break; + } } + return 0; } static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) { + struct v4l2_plane32 __user *uplane32; + struct v4l2_plane __user *uplane; + compat_caddr_t p; + int num_planes; + int ret; + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) || put_user(kp->index, &up->index) || put_user(kp->type, &up->type) || @@ -297,22 +434,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user put_user(kp->memory, &up->memory) || put_user(kp->input, &up->input)) return -EFAULT; - switch (kp->memory) { - case V4L2_MEMORY_MMAP: - if (put_user(kp->length, &up->length) || - put_user(kp->m.offset, &up->m.offset)) - return -EFAULT; - break; - case V4L2_MEMORY_USERPTR: - if (put_user(kp->length, &up->length) || - put_user(kp->m.userptr, &up->m.userptr)) - return -EFAULT; - break; - case V4L2_MEMORY_OVERLAY: - if (put_user(kp->m.offset, &up->m.offset)) - return -EFAULT; - break; - } + if (put_user(kp->bytesused, &up->bytesused) || put_user(kp->field, &up->field) || put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) || @@ -321,6 +443,43 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user put_user(kp->sequence, &up->sequence) || put_user(kp->reserved, &up->reserved)) return -EFAULT; + + if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { + num_planes = kp->length; + if (num_planes == 0) + return 0; + + uplane = kp->m.planes; + if (get_user(p, &up->m.planes)) + return -EFAULT; + uplane32 = compat_ptr(p); + + while (--num_planes >= 0) { + ret = put_v4l2_plane32(uplane, uplane32, kp->memory); + if (ret) + return ret; + ++uplane; + ++uplane32; + } + } else { + switch (kp->memory) { + case V4L2_MEMORY_MMAP: + if (put_user(kp->length, &up->length) || + put_user(kp->m.offset, &up->m.offset)) + return -EFAULT; + break; + case V4L2_MEMORY_USERPTR: + if (put_user(kp->length, &up->length) || + put_user(kp->m.userptr, &up->m.userptr)) + return -EFAULT; + break; + case V4L2_MEMORY_OVERLAY: + if (put_user(kp->m.offset, &up->m.offset)) + return -EFAULT; + break; + } + } + return 0; } @@ -442,12 +601,13 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext if (get_user(p, &up->controls)) return -EFAULT; ucontrols = compat_ptr(p); - if (!access_ok(VERIFY_READ, ucontrols, n * sizeof(struct v4l2_ext_control))) + if (!access_ok(VERIFY_READ, ucontrols, + n * sizeof(struct v4l2_ext_control32))) return -EFAULT; kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); kp->controls = kcontrols; while (--n >= 0) { - if (copy_in_user(kcontrols, ucontrols, sizeof(*kcontrols))) + if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols))) return -EFAULT; if (ctrl_is_pointer(kcontrols->id)) { void __user *s; @@ -483,7 +643,8 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext if (get_user(p, &up->controls)) return -EFAULT; ucontrols = compat_ptr(p); - if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(struct v4l2_ext_control))) + if (!access_ok(VERIFY_WRITE, ucontrols, + n * sizeof(struct v4l2_ext_control32))) return -EFAULT; while (--n >= 0) { -- cgit v0.10.2 From e23ccc0ad9258634e6d52cedf473b35dc34416c7 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Mon, 11 Oct 2010 10:56:41 -0300 Subject: [media] v4l: add videobuf2 Video for Linux 2 driver framework Videobuf2 is a Video for Linux 2 API-compatible driver framework for multimedia devices. It acts as an intermediate layer between userspace applications and device drivers. It also provides low-level, modular memory management functions for drivers. Videobuf2 eases driver development, reduces drivers' code size and aids in proper and consistent implementation of V4L2 API in drivers. Videobuf2 memory management backend is fully modular. This allows custom memory management routines for devices and platforms with non-standard memory management requirements to be plugged in, without changing the high-level buffer management functions and API. The framework provides: - implementations of streaming I/O V4L2 ioctls and file operations - high-level video buffer, video queue and state management functions - video buffer memory allocation and management Signed-off-by: Pawel Osciak Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park CC: Pawel Osciak Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index aa02160..a4a6aa7 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -44,6 +44,9 @@ config V4L2_MEM2MEM_DEV tristate depends on VIDEOBUF_GEN +config VIDEOBUF2_CORE + tristate + # # Multimedia Video device configuration # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a509d31..6a1feba 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -111,6 +111,8 @@ obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o +obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o + obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c new file mode 100644 index 0000000..b856bd1 --- /dev/null +++ b/drivers/media/video/videobuf2-core.c @@ -0,0 +1,1405 @@ +/* + * videobuf2-core.c - V4L2 driver helper framework + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Pawel Osciak + * Marek Szyprowski + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static int debug; +module_param(debug, int, 0644); + +#define dprintk(level, fmt, arg...) \ + do { \ + if (debug >= level) \ + printk(KERN_DEBUG "vb2: " fmt, ## arg); \ + } while (0) + +#define call_memop(q, plane, op, args...) \ + (((q)->mem_ops->op) ? \ + ((q)->mem_ops->op(args)) : 0) + +#define call_qop(q, op, args...) \ + (((q)->ops->op) ? ((q)->ops->op(args)) : 0) + +/** + * __vb2_buf_mem_alloc() - allocate video memory for the given buffer + */ +static int __vb2_buf_mem_alloc(struct vb2_buffer *vb, + unsigned long *plane_sizes) +{ + struct vb2_queue *q = vb->vb2_queue; + void *mem_priv; + int plane; + + /* Allocate memory for all planes in this buffer */ + for (plane = 0; plane < vb->num_planes; ++plane) { + mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane], + plane_sizes[plane]); + if (!mem_priv) + goto free; + + /* Associate allocator private data with this plane */ + vb->planes[plane].mem_priv = mem_priv; + vb->v4l2_planes[plane].length = plane_sizes[plane]; + } + + return 0; +free: + /* Free already allocated memory if one of the allocations failed */ + for (; plane > 0; --plane) + call_memop(q, plane, put, vb->planes[plane - 1].mem_priv); + + return -ENOMEM; +} + +/** + * __vb2_buf_mem_free() - free memory of the given buffer + */ +static void __vb2_buf_mem_free(struct vb2_buffer *vb) +{ + struct vb2_queue *q = vb->vb2_queue; + unsigned int plane; + + for (plane = 0; plane < vb->num_planes; ++plane) { + call_memop(q, plane, put, vb->planes[plane].mem_priv); + vb->planes[plane].mem_priv = NULL; + dprintk(3, "Freed plane %d of buffer %d\n", + plane, vb->v4l2_buf.index); + } +} + +/** + * __vb2_buf_userptr_put() - release userspace memory associated with + * a USERPTR buffer + */ +static void __vb2_buf_userptr_put(struct vb2_buffer *vb) +{ + struct vb2_queue *q = vb->vb2_queue; + unsigned int plane; + + for (plane = 0; plane < vb->num_planes; ++plane) { + void *mem_priv = vb->planes[plane].mem_priv; + + if (mem_priv) { + call_memop(q, plane, put_userptr, mem_priv); + vb->planes[plane].mem_priv = NULL; + } + } +} + +/** + * __setup_offsets() - setup unique offsets ("cookies") for every plane in + * every buffer on the queue + */ +static void __setup_offsets(struct vb2_queue *q) +{ + unsigned int buffer, plane; + struct vb2_buffer *vb; + unsigned long off = 0; + + for (buffer = 0; buffer < q->num_buffers; ++buffer) { + vb = q->bufs[buffer]; + if (!vb) + continue; + + for (plane = 0; plane < vb->num_planes; ++plane) { + vb->v4l2_planes[plane].m.mem_offset = off; + + dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n", + buffer, plane, off); + + off += vb->v4l2_planes[plane].length; + off = PAGE_ALIGN(off); + } + } +} + +/** + * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type) + * video buffer memory for all buffers/planes on the queue and initializes the + * queue + * + * Returns the number of buffers successfully allocated. + */ +static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, + unsigned int num_buffers, unsigned int num_planes, + unsigned long plane_sizes[]) +{ + unsigned int buffer; + struct vb2_buffer *vb; + int ret; + + for (buffer = 0; buffer < num_buffers; ++buffer) { + /* Allocate videobuf buffer structures */ + vb = kzalloc(q->buf_struct_size, GFP_KERNEL); + if (!vb) { + dprintk(1, "Memory alloc for buffer struct failed\n"); + break; + } + + /* Length stores number of planes for multiplanar buffers */ + if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) + vb->v4l2_buf.length = num_planes; + + vb->state = VB2_BUF_STATE_DEQUEUED; + vb->vb2_queue = q; + vb->num_planes = num_planes; + vb->v4l2_buf.index = buffer; + vb->v4l2_buf.type = q->type; + vb->v4l2_buf.memory = memory; + + /* Allocate video buffer memory for the MMAP type */ + if (memory == V4L2_MEMORY_MMAP) { + ret = __vb2_buf_mem_alloc(vb, plane_sizes); + if (ret) { + dprintk(1, "Failed allocating memory for " + "buffer %d\n", buffer); + kfree(vb); + break; + } + /* + * Call the driver-provided buffer initialization + * callback, if given. An error in initialization + * results in queue setup failure. + */ + ret = call_qop(q, buf_init, vb); + if (ret) { + dprintk(1, "Buffer %d %p initialization" + " failed\n", buffer, vb); + __vb2_buf_mem_free(vb); + kfree(vb); + break; + } + } + + q->bufs[buffer] = vb; + } + + q->num_buffers = buffer; + + __setup_offsets(q); + + dprintk(1, "Allocated %d buffers, %d plane(s) each\n", + q->num_buffers, num_planes); + + return buffer; +} + +/** + * __vb2_free_mem() - release all video buffer memory for a given queue + */ +static void __vb2_free_mem(struct vb2_queue *q) +{ + unsigned int buffer; + struct vb2_buffer *vb; + + for (buffer = 0; buffer < q->num_buffers; ++buffer) { + vb = q->bufs[buffer]; + if (!vb) + continue; + + /* Free MMAP buffers or release USERPTR buffers */ + if (q->memory == V4L2_MEMORY_MMAP) + __vb2_buf_mem_free(vb); + else + __vb2_buf_userptr_put(vb); + } +} + +/** + * __vb2_queue_free() - free the queue - video memory and related information + * and return the queue to an uninitialized state. Might be called even if the + * queue has already been freed. + */ +static int __vb2_queue_free(struct vb2_queue *q) +{ + unsigned int buffer; + + /* Call driver-provided cleanup function for each buffer, if provided */ + if (q->ops->buf_cleanup) { + for (buffer = 0; buffer < q->num_buffers; ++buffer) { + if (NULL == q->bufs[buffer]) + continue; + q->ops->buf_cleanup(q->bufs[buffer]); + } + } + + /* Release video buffer memory */ + __vb2_free_mem(q); + + /* Free videobuf buffers */ + for (buffer = 0; buffer < q->num_buffers; ++buffer) { + kfree(q->bufs[buffer]); + q->bufs[buffer] = NULL; + } + + q->num_buffers = 0; + q->memory = 0; + + return 0; +} + +/** + * __verify_planes_array() - verify that the planes array passed in struct + * v4l2_buffer from userspace can be safely used + */ +static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b) +{ + /* Is memory for copying plane information present? */ + if (NULL == b->m.planes) { + dprintk(1, "Multi-planar buffer passed but " + "planes array not provided\n"); + return -EINVAL; + } + + if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) { + dprintk(1, "Incorrect planes array length, " + "expected %d, got %d\n", vb->num_planes, b->length); + return -EINVAL; + } + + return 0; +} + +/** + * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be + * returned to userspace + */ +static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) +{ + struct vb2_queue *q = vb->vb2_queue; + int ret = 0; + + /* Copy back data such as timestamp, input, etc. */ + memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); + b->input = vb->v4l2_buf.input; + b->reserved = vb->v4l2_buf.reserved; + + if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) { + ret = __verify_planes_array(vb, b); + if (ret) + return ret; + + /* + * Fill in plane-related data if userspace provided an array + * for it. The memory and size is verified above. + */ + memcpy(b->m.planes, vb->v4l2_planes, + b->length * sizeof(struct v4l2_plane)); + } else { + /* + * We use length and offset in v4l2_planes array even for + * single-planar buffers, but userspace does not. + */ + b->length = vb->v4l2_planes[0].length; + b->bytesused = vb->v4l2_planes[0].bytesused; + if (q->memory == V4L2_MEMORY_MMAP) + b->m.offset = vb->v4l2_planes[0].m.mem_offset; + else if (q->memory == V4L2_MEMORY_USERPTR) + b->m.userptr = vb->v4l2_planes[0].m.userptr; + } + + b->flags = 0; + + switch (vb->state) { + case VB2_BUF_STATE_QUEUED: + case VB2_BUF_STATE_ACTIVE: + b->flags |= V4L2_BUF_FLAG_QUEUED; + break; + case VB2_BUF_STATE_ERROR: + b->flags |= V4L2_BUF_FLAG_ERROR; + /* fall through */ + case VB2_BUF_STATE_DONE: + b->flags |= V4L2_BUF_FLAG_DONE; + break; + case VB2_BUF_STATE_DEQUEUED: + /* nothing */ + break; + } + + if (vb->num_planes_mapped == vb->num_planes) + b->flags |= V4L2_BUF_FLAG_MAPPED; + + return ret; +} + +/** + * vb2_querybuf() - query video buffer information + * @q: videobuf queue + * @b: buffer struct passed from userspace to vidioc_querybuf handler + * in driver + * + * Should be called from vidioc_querybuf ioctl handler in driver. + * This function will verify the passed v4l2_buffer structure and fill the + * relevant information for the userspace. + * + * The return values from this function are intended to be directly returned + * from vidioc_querybuf handler in driver. + */ +int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + struct vb2_buffer *vb; + + if (b->type != q->type) { + dprintk(1, "querybuf: wrong buffer type\n"); + return -EINVAL; + } + + if (b->index >= q->num_buffers) { + dprintk(1, "querybuf: buffer index out of range\n"); + return -EINVAL; + } + vb = q->bufs[b->index]; + + return __fill_v4l2_buffer(vb, b); +} +EXPORT_SYMBOL(vb2_querybuf); + +/** + * __verify_userptr_ops() - verify that all memory operations required for + * USERPTR queue type have been provided + */ +static int __verify_userptr_ops(struct vb2_queue *q) +{ + if (!(q->io_modes & VB2_USERPTR) || !q->mem_ops->get_userptr || + !q->mem_ops->put_userptr) + return -EINVAL; + + return 0; +} + +/** + * __verify_mmap_ops() - verify that all memory operations required for + * MMAP queue type have been provided + */ +static int __verify_mmap_ops(struct vb2_queue *q) +{ + if (!(q->io_modes & VB2_MMAP) || !q->mem_ops->alloc || + !q->mem_ops->put || !q->mem_ops->mmap) + return -EINVAL; + + return 0; +} + +/** + * __buffers_in_use() - return true if any buffers on the queue are in use and + * the queue cannot be freed (by the means of REQBUFS(0)) call + */ +static bool __buffers_in_use(struct vb2_queue *q) +{ + unsigned int buffer, plane; + struct vb2_buffer *vb; + + for (buffer = 0; buffer < q->num_buffers; ++buffer) { + vb = q->bufs[buffer]; + for (plane = 0; plane < vb->num_planes; ++plane) { + /* + * If num_users() has not been provided, call_memop + * will return 0, apparently nobody cares about this + * case anyway. If num_users() returns more than 1, + * we are not the only user of the plane's memory. + */ + if (call_memop(q, plane, num_users, + vb->planes[plane].mem_priv) > 1) + return true; + } + } + + return false; +} + +/** + * vb2_reqbufs() - Initiate streaming + * @q: videobuf2 queue + * @req: struct passed from userspace to vidioc_reqbufs handler in driver + * + * Should be called from vidioc_reqbufs ioctl handler of a driver. + * This function: + * 1) verifies streaming parameters passed from the userspace, + * 2) sets up the queue, + * 3) negotiates number of buffers and planes per buffer with the driver + * to be used during streaming, + * 4) allocates internal buffer structures (struct vb2_buffer), according to + * the agreed parameters, + * 5) for MMAP memory type, allocates actual video memory, using the + * memory handling/allocation routines provided during queue initialization + * + * If req->count is 0, all the memory will be freed instead. + * If the queue has been allocated previously (by a previous vb2_reqbufs) call + * and the queue is not busy, memory will be reallocated. + * + * The return values from this function are intended to be directly returned + * from vidioc_reqbufs handler in driver. + */ +int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) +{ + unsigned int num_buffers, num_planes; + unsigned long plane_sizes[VIDEO_MAX_PLANES]; + int ret = 0; + + if (req->memory != V4L2_MEMORY_MMAP + && req->memory != V4L2_MEMORY_USERPTR) { + dprintk(1, "reqbufs: unsupported memory type\n"); + return -EINVAL; + } + + if (req->type != q->type) { + dprintk(1, "reqbufs: requested type is incorrect\n"); + return -EINVAL; + } + + if (q->streaming) { + dprintk(1, "reqbufs: streaming active\n"); + return -EBUSY; + } + + /* + * Make sure all the required memory ops for given memory type + * are available. + */ + if (req->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) { + dprintk(1, "reqbufs: MMAP for current setup unsupported\n"); + return -EINVAL; + } + + if (req->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) { + dprintk(1, "reqbufs: USERPTR for current setup unsupported\n"); + return -EINVAL; + } + + if (req->count == 0 || q->num_buffers != 0) { + /* + * We already have buffers allocated, so first check if they + * are not in use and can be freed. + */ + if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) { + dprintk(1, "reqbufs: memory in use, cannot free\n"); + return -EBUSY; + } + + ret = __vb2_queue_free(q); + if (ret != 0) + return ret; + } + + /* + * Make sure the requested values and current defaults are sane. + */ + num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME); + memset(plane_sizes, 0, sizeof(plane_sizes)); + memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); + + /* + * Ask the driver how many buffers and planes per buffer it requires. + * Driver also sets the size and allocator context for each plane. + */ + ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, + plane_sizes, q->alloc_ctx); + if (ret) + return ret; + + /* Finally, allocate buffers and video memory */ + ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes, + plane_sizes); + if (ret < 0) { + dprintk(1, "Memory allocation failed with error: %d\n", ret); + return ret; + } + + /* + * Check if driver can handle the allocated number of buffers. + */ + if (ret < num_buffers) { + unsigned int orig_num_buffers; + + orig_num_buffers = num_buffers = ret; + ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, + plane_sizes, q->alloc_ctx); + if (ret) + goto free_mem; + + if (orig_num_buffers < num_buffers) { + ret = -ENOMEM; + goto free_mem; + } + + /* + * Ok, driver accepted smaller number of buffers. + */ + ret = num_buffers; + } + + q->memory = req->memory; + + /* + * Return the number of successfully allocated buffers + * to the userspace. + */ + req->count = ret; + + return 0; + +free_mem: + __vb2_queue_free(q); + return ret; +} +EXPORT_SYMBOL_GPL(vb2_reqbufs); + +/** + * vb2_plane_vaddr() - Return a kernel virtual address of a given plane + * @vb: vb2_buffer to which the plane in question belongs to + * @plane_no: plane number for which the address is to be returned + * + * This function returns a kernel virtual address of a given plane if + * such a mapping exist, NULL otherwise. + */ +void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no) +{ + struct vb2_queue *q = vb->vb2_queue; + + if (plane_no > vb->num_planes) + return NULL; + + return call_memop(q, plane_no, vaddr, vb->planes[plane_no].mem_priv); + +} +EXPORT_SYMBOL_GPL(vb2_plane_vaddr); + +/** + * vb2_plane_cookie() - Return allocator specific cookie for the given plane + * @vb: vb2_buffer to which the plane in question belongs to + * @plane_no: plane number for which the cookie is to be returned + * + * This function returns an allocator specific cookie for a given plane if + * available, NULL otherwise. The allocator should provide some simple static + * inline function, which would convert this cookie to the allocator specific + * type that can be used directly by the driver to access the buffer. This can + * be for example physical address, pointer to scatter list or IOMMU mapping. + */ +void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no) +{ + struct vb2_queue *q = vb->vb2_queue; + + if (plane_no > vb->num_planes) + return NULL; + + return call_memop(q, plane_no, cookie, vb->planes[plane_no].mem_priv); +} +EXPORT_SYMBOL_GPL(vb2_plane_cookie); + +/** + * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished + * @vb: vb2_buffer returned from the driver + * @state: either VB2_BUF_STATE_DONE if the operation finished successfully + * or VB2_BUF_STATE_ERROR if the operation finished with an error + * + * This function should be called by the driver after a hardware operation on + * a buffer is finished and the buffer may be returned to userspace. The driver + * cannot use this buffer anymore until it is queued back to it by videobuf + * by the means of buf_queue callback. Only buffers previously queued to the + * driver by buf_queue can be passed to this function. + */ +void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) +{ + struct vb2_queue *q = vb->vb2_queue; + unsigned long flags; + + if (vb->state != VB2_BUF_STATE_ACTIVE) + return; + + if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR) + return; + + dprintk(4, "Done processing on buffer %d, state: %d\n", + vb->v4l2_buf.index, vb->state); + + /* Add the buffer to the done buffers list */ + spin_lock_irqsave(&q->done_lock, flags); + vb->state = state; + list_add_tail(&vb->done_entry, &q->done_list); + atomic_dec(&q->queued_count); + spin_unlock_irqrestore(&q->done_lock, flags); + + /* Inform any processes that may be waiting for buffers */ + wake_up(&q->done_wq); +} +EXPORT_SYMBOL_GPL(vb2_buffer_done); + +/** + * __fill_vb2_buffer() - fill a vb2_buffer with information provided in + * a v4l2_buffer by the userspace + */ +static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b, + struct v4l2_plane *v4l2_planes) +{ + unsigned int plane; + int ret; + + if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { + /* + * Verify that the userspace gave us a valid array for + * plane information. + */ + ret = __verify_planes_array(vb, b); + if (ret) + return ret; + + /* Fill in driver-provided information for OUTPUT types */ + if (V4L2_TYPE_IS_OUTPUT(b->type)) { + /* + * Will have to go up to b->length when API starts + * accepting variable number of planes. + */ + for (plane = 0; plane < vb->num_planes; ++plane) { + v4l2_planes[plane].bytesused = + b->m.planes[plane].bytesused; + v4l2_planes[plane].data_offset = + b->m.planes[plane].data_offset; + } + } + + if (b->memory == V4L2_MEMORY_USERPTR) { + for (plane = 0; plane < vb->num_planes; ++plane) { + v4l2_planes[plane].m.userptr = + b->m.planes[plane].m.userptr; + v4l2_planes[plane].length = + b->m.planes[plane].length; + } + } + } else { + /* + * Single-planar buffers do not use planes array, + * so fill in relevant v4l2_buffer struct fields instead. + * In videobuf we use our internal V4l2_planes struct for + * single-planar buffers as well, for simplicity. + */ + if (V4L2_TYPE_IS_OUTPUT(b->type)) + v4l2_planes[0].bytesused = b->bytesused; + + if (b->memory == V4L2_MEMORY_USERPTR) { + v4l2_planes[0].m.userptr = b->m.userptr; + v4l2_planes[0].length = b->length; + } + } + + vb->v4l2_buf.field = b->field; + vb->v4l2_buf.timestamp = b->timestamp; + + return 0; +} + +/** + * __qbuf_userptr() - handle qbuf of a USERPTR buffer + */ +static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b) +{ + struct v4l2_plane planes[VIDEO_MAX_PLANES]; + struct vb2_queue *q = vb->vb2_queue; + void *mem_priv; + unsigned int plane; + int ret; + int write = !V4L2_TYPE_IS_OUTPUT(q->type); + + /* Verify and copy relevant information provided by the userspace */ + ret = __fill_vb2_buffer(vb, b, planes); + if (ret) + return ret; + + for (plane = 0; plane < vb->num_planes; ++plane) { + /* Skip the plane if already verified */ + if (vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr + && vb->v4l2_planes[plane].length == planes[plane].length) + continue; + + dprintk(3, "qbuf: userspace address for plane %d changed, " + "reacquiring memory\n", plane); + + /* Release previously acquired memory if present */ + if (vb->planes[plane].mem_priv) + call_memop(q, plane, put_userptr, + vb->planes[plane].mem_priv); + + vb->planes[plane].mem_priv = NULL; + + /* Acquire each plane's memory */ + if (q->mem_ops->get_userptr) { + mem_priv = q->mem_ops->get_userptr(q->alloc_ctx[plane], + planes[plane].m.userptr, + planes[plane].length, + write); + if (IS_ERR(mem_priv)) { + dprintk(1, "qbuf: failed acquiring userspace " + "memory for plane %d\n", plane); + ret = PTR_ERR(mem_priv); + goto err; + } + vb->planes[plane].mem_priv = mem_priv; + } + } + + /* + * Call driver-specific initialization on the newly acquired buffer, + * if provided. + */ + ret = call_qop(q, buf_init, vb); + if (ret) { + dprintk(1, "qbuf: buffer initialization failed\n"); + goto err; + } + + /* + * Now that everything is in order, copy relevant information + * provided by userspace. + */ + for (plane = 0; plane < vb->num_planes; ++plane) + vb->v4l2_planes[plane] = planes[plane]; + + return 0; +err: + /* In case of errors, release planes that were already acquired */ + for (; plane > 0; --plane) { + call_memop(q, plane, put_userptr, + vb->planes[plane - 1].mem_priv); + vb->planes[plane - 1].mem_priv = NULL; + } + + return ret; +} + +/** + * __qbuf_mmap() - handle qbuf of an MMAP buffer + */ +static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b) +{ + return __fill_vb2_buffer(vb, b, vb->v4l2_planes); +} + +/** + * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing + */ +static void __enqueue_in_driver(struct vb2_buffer *vb) +{ + struct vb2_queue *q = vb->vb2_queue; + + vb->state = VB2_BUF_STATE_ACTIVE; + atomic_inc(&q->queued_count); + q->ops->buf_queue(vb); +} + +/** + * vb2_qbuf() - Queue a buffer from userspace + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_qbuf handler + * in driver + * + * Should be called from vidioc_qbuf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) calls buf_prepare callback in the driver (if provided), in which + * driver-specific buffer initialization can be performed, + * 3) if streaming is on, queues the buffer in driver by the means of buf_queue + * callback for processing. + * + * The return values from this function are intended to be directly returned + * from vidioc_qbuf handler in driver. + */ +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + struct vb2_buffer *vb; + int ret = 0; + + if (b->type != q->type) { + dprintk(1, "qbuf: invalid buffer type\n"); + return -EINVAL; + } + + if (b->index >= q->num_buffers) { + dprintk(1, "qbuf: buffer index out of range\n"); + return -EINVAL; + } + + vb = q->bufs[b->index]; + if (NULL == vb) { + /* Should never happen */ + dprintk(1, "qbuf: buffer is NULL\n"); + return -EINVAL; + } + + if (b->memory != q->memory) { + dprintk(1, "qbuf: invalid memory type\n"); + return -EINVAL; + } + + if (vb->state != VB2_BUF_STATE_DEQUEUED) { + dprintk(1, "qbuf: buffer already in use\n"); + return -EINVAL; + } + + if (q->memory == V4L2_MEMORY_MMAP) + ret = __qbuf_mmap(vb, b); + else if (q->memory == V4L2_MEMORY_USERPTR) + ret = __qbuf_userptr(vb, b); + else { + WARN(1, "Invalid queue type\n"); + return -EINVAL; + } + + if (ret) + return ret; + + ret = call_qop(q, buf_prepare, vb); + if (ret) { + dprintk(1, "qbuf: buffer preparation failed\n"); + return ret; + } + + /* + * Add to the queued buffers list, a buffer will stay on it until + * dequeued in dqbuf. + */ + list_add_tail(&vb->queued_entry, &q->queued_list); + vb->state = VB2_BUF_STATE_QUEUED; + + /* + * If already streaming, give the buffer to driver for processing. + * If not, the buffer will be given to driver on next streamon. + */ + if (q->streaming) + __enqueue_in_driver(vb); + + dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); + return 0; +} +EXPORT_SYMBOL_GPL(vb2_qbuf); + +/** + * __vb2_wait_for_done_vb() - wait for a buffer to become available + * for dequeuing + * + * Will sleep if required for nonblocking == false. + */ +static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) +{ + /* + * All operations on vb_done_list are performed under done_lock + * spinlock protection. However, buffers may be removed from + * it and returned to userspace only while holding both driver's + * lock and the done_lock spinlock. Thus we can be sure that as + * long as we hold the driver's lock, the list will remain not + * empty if list_empty() check succeeds. + */ + + for (;;) { + int ret; + + if (!q->streaming) { + dprintk(1, "Streaming off, will not wait for buffers\n"); + return -EINVAL; + } + + if (!list_empty(&q->done_list)) { + /* + * Found a buffer that we were waiting for. + */ + break; + } + + if (nonblocking) { + dprintk(1, "Nonblocking and no buffers to dequeue, " + "will not wait\n"); + return -EAGAIN; + } + + /* + * We are streaming and blocking, wait for another buffer to + * become ready or for streamoff. Driver's lock is released to + * allow streamoff or qbuf to be called while waiting. + */ + call_qop(q, wait_prepare, q); + + /* + * All locks have been released, it is safe to sleep now. + */ + dprintk(3, "Will sleep waiting for buffers\n"); + ret = wait_event_interruptible(q->done_wq, + !list_empty(&q->done_list) || !q->streaming); + + /* + * We need to reevaluate both conditions again after reacquiring + * the locks or return an error if one occurred. + */ + call_qop(q, wait_finish, q); + if (ret) + return ret; + } + return 0; +} + +/** + * __vb2_get_done_vb() - get a buffer ready for dequeuing + * + * Will sleep if required for nonblocking == false. + */ +static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, + int nonblocking) +{ + unsigned long flags; + int ret; + + /* + * Wait for at least one buffer to become available on the done_list. + */ + ret = __vb2_wait_for_done_vb(q, nonblocking); + if (ret) + return ret; + + /* + * Driver's lock has been held since we last verified that done_list + * is not empty, so no need for another list_empty(done_list) check. + */ + spin_lock_irqsave(&q->done_lock, flags); + *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry); + list_del(&(*vb)->done_entry); + spin_unlock_irqrestore(&q->done_lock, flags); + + return 0; +} + +/** + * vb2_wait_for_all_buffers() - wait until all buffers are given back to vb2 + * @q: videobuf2 queue + * + * This function will wait until all buffers that have been given to the driver + * by buf_queue() are given back to vb2 with vb2_buffer_done(). It doesn't call + * wait_prepare, wait_finish pair. It is intended to be called with all locks + * taken, for example from stop_streaming() callback. + */ +int vb2_wait_for_all_buffers(struct vb2_queue *q) +{ + if (!q->streaming) { + dprintk(1, "Streaming off, will not wait for buffers\n"); + return -EINVAL; + } + + wait_event(q->done_wq, !atomic_read(&q->queued_count)); + return 0; +} +EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers); + +/** + * vb2_dqbuf() - Dequeue a buffer to the userspace + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_dqbuf handler + * in driver + * @nonblocking: if true, this call will not sleep waiting for a buffer if no + * buffers ready for dequeuing are present. Normally the driver + * would be passing (file->f_flags & O_NONBLOCK) here + * + * Should be called from vidioc_dqbuf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) calls buf_finish callback in the driver (if provided), in which + * driver can perform any additional operations that may be required before + * returning the buffer to userspace, such as cache sync, + * 3) the buffer struct members are filled with relevant information for + * the userspace. + * + * The return values from this function are intended to be directly returned + * from vidioc_dqbuf handler in driver. + */ +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) +{ + struct vb2_buffer *vb = NULL; + int ret; + + if (b->type != q->type) { + dprintk(1, "dqbuf: invalid buffer type\n"); + return -EINVAL; + } + + ret = __vb2_get_done_vb(q, &vb, nonblocking); + if (ret < 0) { + dprintk(1, "dqbuf: error getting next done buffer\n"); + return ret; + } + + ret = call_qop(q, buf_finish, vb); + if (ret) { + dprintk(1, "dqbuf: buffer finish failed\n"); + return ret; + } + + switch (vb->state) { + case VB2_BUF_STATE_DONE: + dprintk(3, "dqbuf: Returning done buffer\n"); + break; + case VB2_BUF_STATE_ERROR: + dprintk(3, "dqbuf: Returning done buffer with errors\n"); + break; + default: + dprintk(1, "dqbuf: Invalid buffer state\n"); + return -EINVAL; + } + + /* Fill buffer information for the userspace */ + __fill_v4l2_buffer(vb, b); + /* Remove from videobuf queue */ + list_del(&vb->queued_entry); + + dprintk(1, "dqbuf of buffer %d, with state %d\n", + vb->v4l2_buf.index, vb->state); + + vb->state = VB2_BUF_STATE_DEQUEUED; + return 0; +} +EXPORT_SYMBOL_GPL(vb2_dqbuf); + +/** + * vb2_streamon - start streaming + * @q: videobuf2 queue + * @type: type argument passed from userspace to vidioc_streamon handler + * + * Should be called from vidioc_streamon handler of a driver. + * This function: + * 1) verifies current state + * 2) starts streaming and passes any previously queued buffers to the driver + * + * The return values from this function are intended to be directly returned + * from vidioc_streamon handler in the driver. + */ +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) +{ + struct vb2_buffer *vb; + + if (type != q->type) { + dprintk(1, "streamon: invalid stream type\n"); + return -EINVAL; + } + + if (q->streaming) { + dprintk(1, "streamon: already streaming\n"); + return -EBUSY; + } + + /* + * Cannot start streaming on an OUTPUT device if no buffers have + * been queued yet. + */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + if (list_empty(&q->queued_list)) { + dprintk(1, "streamon: no output buffers queued\n"); + return -EINVAL; + } + } + + q->streaming = 1; + + /* + * Let driver notice that streaming state has been enabled. + */ + call_qop(q, start_streaming, q); + + /* + * If any buffers were queued before streamon, + * we can now pass them to driver for processing. + */ + list_for_each_entry(vb, &q->queued_list, queued_entry) + __enqueue_in_driver(vb); + + dprintk(3, "Streamon successful\n"); + return 0; +} +EXPORT_SYMBOL_GPL(vb2_streamon); + +/** + * __vb2_queue_cancel() - cancel and stop (pause) streaming + * + * Removes all queued buffers from driver's queue and all buffers queued by + * userspace from videobuf's queue. Returns to state after reqbufs. + */ +static void __vb2_queue_cancel(struct vb2_queue *q) +{ + unsigned int i; + + /* + * Tell driver to stop all transactions and release all queued + * buffers. + */ + if (q->streaming) + call_qop(q, stop_streaming, q); + q->streaming = 0; + + /* + * Remove all buffers from videobuf's list... + */ + INIT_LIST_HEAD(&q->queued_list); + /* + * ...and done list; userspace will not receive any buffers it + * has not already dequeued before initiating cancel. + */ + INIT_LIST_HEAD(&q->done_list); + wake_up_all(&q->done_wq); + + /* + * Reinitialize all buffers for next use. + */ + for (i = 0; i < q->num_buffers; ++i) + q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED; +} + +/** + * vb2_streamoff - stop streaming + * @q: videobuf2 queue + * @type: type argument passed from userspace to vidioc_streamoff handler + * + * Should be called from vidioc_streamoff handler of a driver. + * This function: + * 1) verifies current state, + * 2) stop streaming and dequeues any queued buffers, including those previously + * passed to the driver (after waiting for the driver to finish). + * + * This call can be used for pausing playback. + * The return values from this function are intended to be directly returned + * from vidioc_streamoff handler in the driver + */ +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) +{ + if (type != q->type) { + dprintk(1, "streamoff: invalid stream type\n"); + return -EINVAL; + } + + if (!q->streaming) { + dprintk(1, "streamoff: not streaming\n"); + return -EINVAL; + } + + /* + * Cancel will pause streaming and remove all buffers from the driver + * and videobuf, effectively returning control over them to userspace. + */ + __vb2_queue_cancel(q); + + dprintk(3, "Streamoff successful\n"); + return 0; +} +EXPORT_SYMBOL_GPL(vb2_streamoff); + +/** + * __find_plane_by_offset() - find plane associated with the given offset off + */ +static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, + unsigned int *_buffer, unsigned int *_plane) +{ + struct vb2_buffer *vb; + unsigned int buffer, plane; + + /* + * Go over all buffers and their planes, comparing the given offset + * with an offset assigned to each plane. If a match is found, + * return its buffer and plane numbers. + */ + for (buffer = 0; buffer < q->num_buffers; ++buffer) { + vb = q->bufs[buffer]; + + for (plane = 0; plane < vb->num_planes; ++plane) { + if (vb->v4l2_planes[plane].m.mem_offset == off) { + *_buffer = buffer; + *_plane = plane; + return 0; + } + } + } + + return -EINVAL; +} + +/** + * vb2_mmap() - map video buffers into application address space + * @q: videobuf2 queue + * @vma: vma passed to the mmap file operation handler in the driver + * + * Should be called from mmap file operation handler of a driver. + * This function maps one plane of one of the available video buffers to + * userspace. To map whole video memory allocated on reqbufs, this function + * has to be called once per each plane per each buffer previously allocated. + * + * When the userspace application calls mmap, it passes to it an offset returned + * to it earlier by the means of vidioc_querybuf handler. That offset acts as + * a "cookie", which is then used to identify the plane to be mapped. + * This function finds a plane with a matching offset and a mapping is performed + * by the means of a provided memory operation. + * + * The return values from this function are intended to be directly returned + * from the mmap handler in driver. + */ +int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) +{ + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; + struct vb2_plane *vb_plane; + struct vb2_buffer *vb; + unsigned int buffer, plane; + int ret; + + if (q->memory != V4L2_MEMORY_MMAP) { + dprintk(1, "Queue is not currently set up for mmap\n"); + return -EINVAL; + } + + /* + * Check memory area access mode. + */ + if (!(vma->vm_flags & VM_SHARED)) { + dprintk(1, "Invalid vma flags, VM_SHARED needed\n"); + return -EINVAL; + } + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + if (!(vma->vm_flags & VM_WRITE)) { + dprintk(1, "Invalid vma flags, VM_WRITE needed\n"); + return -EINVAL; + } + } else { + if (!(vma->vm_flags & VM_READ)) { + dprintk(1, "Invalid vma flags, VM_READ needed\n"); + return -EINVAL; + } + } + + /* + * Find the plane corresponding to the offset passed by userspace. + */ + ret = __find_plane_by_offset(q, off, &buffer, &plane); + if (ret) + return ret; + + vb = q->bufs[buffer]; + vb_plane = &vb->planes[plane]; + + ret = q->mem_ops->mmap(vb_plane->mem_priv, vma); + if (ret) + return ret; + + vb_plane->mapped = 1; + vb->num_planes_mapped++; + + dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane); + return 0; +} +EXPORT_SYMBOL_GPL(vb2_mmap); + + +/** + * vb2_poll() - implements poll userspace operation + * @q: videobuf2 queue + * @file: file argument passed to the poll file operation handler + * @wait: wait argument passed to the poll file operation handler + * + * This function implements poll file operation handler for a driver. + * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will + * be informed that the file descriptor of a video device is available for + * reading. + * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor + * will be reported as available for writing. + * + * 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; + struct vb2_buffer *vb = NULL; + + /* + * There is nothing to wait for if no buffers have already been queued. + */ + if (list_empty(&q->queued_list)) + return POLLERR; + + poll_wait(file, &q->done_wq, wait); + + /* + * Take first buffer available for dequeuing. + */ + spin_lock_irqsave(&q->done_lock, flags); + if (!list_empty(&q->done_list)) + vb = list_first_entry(&q->done_list, struct vb2_buffer, + done_entry); + spin_unlock_irqrestore(&q->done_lock, flags); + + 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 0; +} +EXPORT_SYMBOL_GPL(vb2_poll); + +/** + * vb2_queue_init() - initialize a videobuf2 queue + * @q: videobuf2 queue; this structure should be allocated in driver + * + * The vb2_queue structure should be allocated by the driver. The driver is + * responsible of clearing it's content and setting initial values for some + * required entries before calling this function. + * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer + * to the struct vb2_queue description in include/media/videobuf2-core.h + * for more information. + */ +int vb2_queue_init(struct vb2_queue *q) +{ + BUG_ON(!q); + BUG_ON(!q->ops); + BUG_ON(!q->mem_ops); + BUG_ON(!q->type); + BUG_ON(!q->io_modes); + + BUG_ON(!q->ops->queue_setup); + BUG_ON(!q->ops->buf_queue); + + INIT_LIST_HEAD(&q->queued_list); + INIT_LIST_HEAD(&q->done_list); + spin_lock_init(&q->done_lock); + init_waitqueue_head(&q->done_wq); + + if (q->buf_struct_size == 0) + q->buf_struct_size = sizeof(struct vb2_buffer); + + return 0; +} +EXPORT_SYMBOL_GPL(vb2_queue_init); + +/** + * vb2_queue_release() - stop streaming, release the queue and free memory + * @q: videobuf2 queue + * + * This function stops streaming and performs necessary clean ups, including + * freeing video buffer memory. The driver is responsible for freeing + * the vb2_queue structure itself. + */ +void vb2_queue_release(struct vb2_queue *q) +{ + __vb2_queue_cancel(q); + __vb2_queue_free(q); +} +EXPORT_SYMBOL_GPL(vb2_queue_release); + +MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2"); +MODULE_AUTHOR("Pawel Osciak, Marek Szyprowski"); +MODULE_LICENSE("GPL"); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h new file mode 100644 index 0000000..1dafac0 --- /dev/null +++ b/include/media/videobuf2-core.h @@ -0,0 +1,371 @@ +/* + * videobuf2-core.h - V4L2 driver helper framework + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Pawel Osciak + * + * 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. + */ +#ifndef _MEDIA_VIDEOBUF2_CORE_H +#define _MEDIA_VIDEOBUF2_CORE_H + +#include +#include +#include +#include + +struct vb2_alloc_ctx; + +/** + * struct vb2_mem_ops - memory handling/memory allocator operations + * @alloc: allocate video memory and, optionally, allocator private data, + * return NULL on failure or a pointer to allocator private, + * per-buffer data on success; the returned private structure + * will then be passed as buf_priv argument to other ops in this + * structure + * @put: inform the allocator that the buffer will no longer be used; + * usually will result in the allocator freeing the buffer (if + * no other users of this buffer are present); the buf_priv + * argument is the allocator private per-buffer structure + * previously returned from the alloc callback + * @get_userptr: acquire userspace memory for a hardware operation; used for + * USERPTR memory types; vaddr is the address passed to the + * videobuf layer when queuing a video buffer of USERPTR type; + * should return an allocator private per-buffer structure + * associated with the buffer on success, NULL on failure; + * the returned private structure will then be passed as buf_priv + * argument to other ops in this structure + * @put_userptr: inform the allocator that a USERPTR buffer will no longer + * be used + * @vaddr: return a kernel virtual address to a given memory buffer + * associated with the passed private structure or NULL if no + * such mapping exists + * @cookie: return allocator specific cookie for a given memory buffer + * associated with the passed private structure or NULL if not + * available + * @num_users: return the current number of users of a memory buffer; + * return 1 if the videobuf layer (or actually the driver using + * it) is the only user + * @mmap: setup a userspace mapping for a given memory buffer under + * the provided virtual memory region + * + * Required ops for USERPTR types: get_userptr, put_userptr. + * Required ops for MMAP types: alloc, put, num_users, mmap. + */ +struct vb2_mem_ops { + void *(*alloc)(void *alloc_ctx, unsigned long size); + void (*put)(void *buf_priv); + + void *(*get_userptr)(void *alloc_ctx, unsigned long vaddr, + unsigned long size, int write); + void (*put_userptr)(void *buf_priv); + + void *(*vaddr)(void *buf_priv); + void *(*cookie)(void *buf_priv); + + unsigned int (*num_users)(void *buf_priv); + + int (*mmap)(void *buf_priv, struct vm_area_struct *vma); +}; + +struct vb2_plane { + void *mem_priv; + int mapped:1; +}; + +/** + * enum vb2_io_modes - queue access methods + * @VB2_MMAP: driver supports MMAP with streaming API + * @VB2_USERPTR: driver supports USERPTR with streaming API + * @VB2_READ: driver supports read() style access + * @VB2_WRITE: driver supports write() style access + */ +enum vb2_io_modes { + VB2_MMAP = (1 << 0), + VB2_USERPTR = (1 << 1), + VB2_READ = (1 << 2), + VB2_WRITE = (1 << 3), +}; + +/** + * enum vb2_fileio_flags - flags for selecting a mode of the file io emulator, + * by default the 'streaming' style is used by the file io emulator + * @VB2_FILEIO_READ_ONCE: report EOF after reading the first buffer + * @VB2_FILEIO_WRITE_IMMEDIATELY: queue buffer after each write() call + */ +enum vb2_fileio_flags { + VB2_FILEIO_READ_ONCE = (1 << 0), + VB2_FILEIO_WRITE_IMMEDIATELY = (1 << 1), +}; + +/** + * enum vb2_buffer_state - current video buffer state + * @VB2_BUF_STATE_DEQUEUED: buffer under userspace control + * @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver + * @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used + * in a hardware operation + * @VB2_BUF_STATE_DONE: buffer returned from driver to videobuf, but + * not yet dequeued to userspace + * @VB2_BUF_STATE_ERROR: same as above, but the operation on the buffer + * has ended with an error, which will be reported + * to the userspace when it is dequeued + */ +enum vb2_buffer_state { + VB2_BUF_STATE_DEQUEUED, + VB2_BUF_STATE_QUEUED, + VB2_BUF_STATE_ACTIVE, + VB2_BUF_STATE_DONE, + VB2_BUF_STATE_ERROR, +}; + +struct vb2_queue; + +/** + * struct vb2_buffer - represents a video buffer + * @v4l2_buf: struct v4l2_buffer associated with this buffer; can + * be read by the driver and relevant entries can be + * changed by the driver in case of CAPTURE types + * (such as timestamp) + * @v4l2_planes: struct v4l2_planes associated with this buffer; can + * be read by the driver and relevant entries can be + * changed by the driver in case of CAPTURE types + * (such as bytesused); NOTE that even for single-planar + * types, the v4l2_planes[0] struct should be used + * instead of v4l2_buf for filling bytesused - drivers + * should use the vb2_set_plane_payload() function for that + * @vb2_queue: the queue to which this driver belongs + * @num_planes: number of planes in the buffer + * on an internal driver queue + * @state: current buffer state; do not change + * @queued_entry: entry on the queued buffers list, which holds all + * buffers queued from userspace + * @done_entry: entry on the list that stores all buffers ready to + * be dequeued to userspace + * @planes: private per-plane information; do not change + * @num_planes_mapped: number of mapped planes; do not change + */ +struct vb2_buffer { + struct v4l2_buffer v4l2_buf; + struct v4l2_plane v4l2_planes[VIDEO_MAX_PLANES]; + + struct vb2_queue *vb2_queue; + + unsigned int num_planes; + +/* Private: internal use only */ + enum vb2_buffer_state state; + + struct list_head queued_entry; + struct list_head done_entry; + + struct vb2_plane planes[VIDEO_MAX_PLANES]; + unsigned int num_planes_mapped; +}; + +/** + * struct vb2_ops - driver-specific callbacks + * + * @queue_setup: called from a VIDIOC_REQBUFS handler, before + * memory allocation; driver should return the required + * number of buffers in num_buffers, the required number + * of planes per buffer in num_planes; the size of each + * plane should be set in the sizes[] array and optional + * per-plane allocator specific context in alloc_ctxs[] + * array + * @wait_prepare: release any locks taken while calling vb2 functions; + * it is called before an ioctl needs to wait for a new + * buffer to arrive; required to avoid a deadlock in + * blocking access type + * @wait_finish: reacquire all locks released in the previous callback; + * required to continue operation after sleeping while + * waiting for a new buffer to arrive + * @buf_init: called once after allocating a buffer (in MMAP case) + * or after acquiring a new USERPTR buffer; drivers may + * perform additional buffer-related initialization; + * initialization failure (return != 0) will prevent + * queue setup from completing successfully; optional + * @buf_prepare: called every time the buffer is queued from userspace; + * drivers may perform any initialization required before + * each hardware operation in this callback; + * if an error is returned, the buffer will not be queued + * in driver; optional + * @buf_finish: called before every dequeue of the buffer back to + * userspace; drivers may perform any operations required + * before userspace accesses the buffer; optional + * @buf_cleanup: called once before the buffer is freed; drivers may + * perform any additional cleanup; optional + * @start_streaming: called once before entering 'streaming' state; enables + * driver to receive buffers over buf_queue() callback + * @stop_streaming: called when 'streaming' state must be disabled; driver + * should stop any DMA transactions or wait until they + * finish and give back all buffers it got from buf_queue() + * callback; may use vb2_wait_for_all_buffers() function + * @buf_queue: passes buffer vb to the driver; driver may start + * hardware operation on this buffer; driver should give + * the buffer back by calling vb2_buffer_done() function + */ +struct vb2_ops { + int (*queue_setup)(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned long sizes[], + void *alloc_ctxs[]); + + void (*wait_prepare)(struct vb2_queue *q); + void (*wait_finish)(struct vb2_queue *q); + + int (*buf_init)(struct vb2_buffer *vb); + int (*buf_prepare)(struct vb2_buffer *vb); + int (*buf_finish)(struct vb2_buffer *vb); + void (*buf_cleanup)(struct vb2_buffer *vb); + + int (*start_streaming)(struct vb2_queue *q); + int (*stop_streaming)(struct vb2_queue *q); + + void (*buf_queue)(struct vb2_buffer *vb); +}; + +/** + * struct vb2_queue - a videobuf queue + * + * @type: queue type (see V4L2_BUF_TYPE_* in linux/videodev2.h + * @io_modes: supported io methods (see vb2_io_modes enum) + * @io_flags: additional io flags (see vb2_fileio_flags enum) + * @ops: driver-specific callbacks + * @mem_ops: memory allocator specific callbacks + * @drv_priv: driver private data + * @buf_struct_size: size of the driver-specific buffer structure; + * "0" indicates the driver doesn't want to use a custom buffer + * structure type, so sizeof(struct vb2_buffer) will is used + * + * @memory: current memory type used + * @bufs: videobuf buffer structures + * @num_buffers: number of allocated/used buffers + * @queued_list: list of buffers currently queued from userspace + * @queued_count: number of buffers owned by the driver + * @done_list: list of buffers ready to be dequeued to userspace + * @done_lock: lock to protect done_list list + * @done_wq: waitqueue for processes waiting for buffers ready to be dequeued + * @alloc_ctx: memory type/allocator-specific contexts for each plane + * @streaming: current streaming state + */ +struct vb2_queue { + enum v4l2_buf_type type; + unsigned int io_modes; + unsigned int io_flags; + + const struct vb2_ops *ops; + const struct vb2_mem_ops *mem_ops; + void *drv_priv; + unsigned int buf_struct_size; + +/* private: internal use only */ + enum v4l2_memory memory; + struct vb2_buffer *bufs[VIDEO_MAX_FRAME]; + unsigned int num_buffers; + + struct list_head queued_list; + + atomic_t queued_count; + struct list_head done_list; + spinlock_t done_lock; + wait_queue_head_t done_wq; + + void *alloc_ctx[VIDEO_MAX_PLANES]; + + unsigned int streaming:1; +}; + +void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no); +void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no); + +void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state); +int vb2_wait_for_all_buffers(struct vb2_queue *q); + +int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b); +int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req); + +int vb2_queue_init(struct vb2_queue *q); + +void vb2_queue_release(struct vb2_queue *q); + +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b); +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking); + +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type); +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type); + +int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma); +unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait); + +/** + * vb2_is_streaming() - return streaming status of the queue + * @q: videobuf queue + */ +static inline bool vb2_is_streaming(struct vb2_queue *q) +{ + return q->streaming; +} + +/** + * vb2_is_busy() - return busy status of the queue + * @q: videobuf queue + * + * This function checks if queue has any buffers allocated. + */ +static inline bool vb2_is_busy(struct vb2_queue *q) +{ + return (q->num_buffers > 0); +} + +/** + * vb2_get_drv_priv() - return driver private data associated with the queue + * @q: videobuf queue + */ +static inline void *vb2_get_drv_priv(struct vb2_queue *q) +{ + return q->drv_priv; +} + +/** + * vb2_set_plane_payload() - set bytesused for the plane plane_no + * @vb: buffer for which plane payload should be set + * @plane_no: plane number for which payload should be set + * @size: payload in bytes + */ +static inline void vb2_set_plane_payload(struct vb2_buffer *vb, + unsigned int plane_no, unsigned long size) +{ + if (plane_no < vb->num_planes) + vb->v4l2_planes[plane_no].bytesused = size; +} + +/** + * vb2_get_plane_payload() - set bytesused for the plane plane_no + * @vb: buffer for which plane payload should be set + * @plane_no: plane number for which payload should be set + * @size: payload in bytes + */ +static inline unsigned long vb2_get_plane_payload(struct vb2_buffer *vb, + unsigned int plane_no) +{ + if (plane_no < vb->num_planes) + return vb->v4l2_planes[plane_no].bytesused; + return 0; +} + +/** + * vb2_plane_size() - return plane size in bytes + * @vb: buffer for which plane size should be returned + * @plane_no: plane number for which size should be returned + */ +static inline unsigned long +vb2_plane_size(struct vb2_buffer *vb, unsigned int plane_no) +{ + if (plane_no < vb->num_planes) + return vb->v4l2_planes[plane_no].length; + return 0; +} + +#endif /* _MEDIA_VIDEOBUF2_CORE_H */ -- cgit v0.10.2 From 004cc3781c40a577b0349eef825efaaebc42dd43 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 9 Dec 2010 10:20:47 -0300 Subject: [media] v4l: videobuf2: add generic memory handling routines Add generic memory handling routines for userspace pointer handling, contiguous memory verification and mapping. Signed-off-by: Marek Szyprowski Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park CC: Pawel Osciak Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index a4a6aa7..aae5005 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -47,6 +47,9 @@ config V4L2_MEM2MEM_DEV config VIDEOBUF2_CORE tristate +config VIDEOBUF2_MEMOPS + tristate + # # Multimedia Video device configuration # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 6a1feba..fca0444 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -112,6 +112,7 @@ obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o +obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c new file mode 100644 index 0000000..053c157 --- /dev/null +++ b/drivers/media/video/videobuf2-memops.c @@ -0,0 +1,232 @@ +/* + * videobuf2-memops.c - generic memory handling routines for videobuf2 + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Pawel Osciak + * Marek Szyprowski + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * vb2_get_vma() - acquire and lock the virtual memory area + * @vma: given virtual memory area + * + * This function attempts to acquire an area mapped in the userspace for + * the duration of a hardware operation. The area is "locked" by performing + * the same set of operation that are done when process calls fork() and + * memory areas are duplicated. + * + * Returns a copy of a virtual memory region on success or NULL. + */ +struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) +{ + struct vm_area_struct *vma_copy; + + vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); + if (vma_copy == NULL) + return NULL; + + if (vma->vm_ops && vma->vm_ops->open) + vma->vm_ops->open(vma); + + if (vma->vm_file) + get_file(vma->vm_file); + + memcpy(vma_copy, vma, sizeof(*vma)); + + vma_copy->vm_mm = NULL; + vma_copy->vm_next = NULL; + vma_copy->vm_prev = NULL; + + return vma_copy; +} + +/** + * vb2_put_userptr() - release a userspace virtual memory area + * @vma: virtual memory region associated with the area to be released + * + * This function releases the previously acquired memory area after a hardware + * operation. + */ +void vb2_put_vma(struct vm_area_struct *vma) +{ + if (!vma) + return; + + if (vma->vm_file) + fput(vma->vm_file); + + if (vma->vm_ops && vma->vm_ops->close) + vma->vm_ops->close(vma); + + kfree(vma); +} + +/** + * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory + * @vaddr: starting virtual address of the area to be verified + * @size: size of the area + * @res_paddr: will return physical address for the given vaddr + * @res_vma: will return locked copy of struct vm_area for the given area + * + * This function will go through memory area of size @size mapped at @vaddr and + * verify that the underlying physical pages are contiguous. If they are + * contiguous the virtual memory area is locked and a @res_vma is filled with + * the copy and @res_pa set to the physical address of the buffer. + * + * Returns 0 on success. + */ +int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, + struct vm_area_struct **res_vma, dma_addr_t *res_pa) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + unsigned long offset, start, end; + unsigned long this_pfn, prev_pfn; + dma_addr_t pa = 0; + int ret = -EFAULT; + + start = vaddr; + offset = start & ~PAGE_MASK; + end = start + size; + + down_read(&mm->mmap_sem); + vma = find_vma(mm, start); + + if (vma == NULL || vma->vm_end < end) + goto done; + + for (prev_pfn = 0; start < end; start += PAGE_SIZE) { + ret = follow_pfn(vma, start, &this_pfn); + if (ret) + goto done; + + if (prev_pfn == 0) + pa = this_pfn << PAGE_SHIFT; + else if (this_pfn != prev_pfn + 1) { + ret = -EFAULT; + goto done; + } + prev_pfn = this_pfn; + } + + /* + * Memory is contigous, lock vma and return to the caller + */ + *res_vma = vb2_get_vma(vma); + if (*res_vma == NULL) { + ret = -ENOMEM; + goto done; + } + *res_pa = pa + offset; + ret = 0; + +done: + up_read(&mm->mmap_sem); + return ret; +} + +/** + * vb2_mmap_pfn_range() - map physical pages to userspace + * @vma: virtual memory region for the mapping + * @paddr: starting physical address of the memory to be mapped + * @size: size of the memory to be mapped + * @vm_ops: vm operations to be assigned to the created area + * @priv: private data to be associated with the area + * + * Returns 0 on success. + */ +int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr, + unsigned long size, + const struct vm_operations_struct *vm_ops, + void *priv) +{ + int ret; + + size = min_t(unsigned long, vma->vm_end - vma->vm_start, size); + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT, + size, vma->vm_page_prot); + if (ret) { + printk(KERN_ERR "Remapping memory failed, error: %d\n", ret); + return ret; + } + + vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; + vma->vm_private_data = priv; + vma->vm_ops = vm_ops; + + vma->vm_ops->open(vma); + + printk(KERN_DEBUG "%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n", + __func__, paddr, vma->vm_start, size); + + return 0; +} + +/** + * vb2_common_vm_open() - increase refcount of the vma + * @vma: virtual memory region for the mapping + * + * This function adds another user to the provided vma. It expects + * struct vb2_vmarea_handler pointer in vma->vm_private_data. + */ +static void vb2_common_vm_open(struct vm_area_struct *vma) +{ + struct vb2_vmarea_handler *h = vma->vm_private_data; + + printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n", + __func__, h, atomic_read(h->refcount), vma->vm_start, + vma->vm_end); + + atomic_inc(h->refcount); +} + +/** + * vb2_common_vm_close() - decrease refcount of the vma + * @vma: virtual memory region for the mapping + * + * This function releases the user from the provided vma. It expects + * struct vb2_vmarea_handler pointer in vma->vm_private_data. + */ +static void vb2_common_vm_close(struct vm_area_struct *vma) +{ + struct vb2_vmarea_handler *h = vma->vm_private_data; + + printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n", + __func__, h, atomic_read(h->refcount), vma->vm_start, + vma->vm_end); + + h->put(h->arg); +} + +/** + * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmaped + * video buffers + */ +const struct vm_operations_struct vb2_common_vm_ops = { + .open = vb2_common_vm_open, + .close = vb2_common_vm_close, +}; +EXPORT_SYMBOL_GPL(vb2_common_vm_ops); + +MODULE_DESCRIPTION("common memory handling routines for videobuf2"); +MODULE_AUTHOR("Pawel Osciak"); +MODULE_LICENSE("GPL"); diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h new file mode 100644 index 0000000..fee1703 --- /dev/null +++ b/include/media/videobuf2-memops.h @@ -0,0 +1,45 @@ +/* + * videobuf2-memops.h - generic memory handling routines for videobuf2 + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Pawel Osciak + * Marek Szyprowski + * + * 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. + */ + +#ifndef _MEDIA_VIDEOBUF2_MEMOPS_H +#define _MEDIA_VIDEOBUF2_MEMOPS_H + +#include + +/** + * vb2_vmarea_handler - common vma refcount tracking handler + * @refcount: pointer to refcount entry in the buffer + * @put: callback to function that decreases buffer refcount + * @arg: argument for @put callback + */ +struct vb2_vmarea_handler { + atomic_t *refcount; + void (*put)(void *arg); + void *arg; +}; + +extern const struct vm_operations_struct vb2_common_vm_ops; + +int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, + struct vm_area_struct **res_vma, dma_addr_t *res_pa); + +int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr, + unsigned long size, + const struct vm_operations_struct *vm_ops, + void *priv); + +struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma); +void vb2_put_vma(struct vm_area_struct *vma); + + +#endif -- cgit v0.10.2 From 3c18ff06d811f743d380e6436fac0143ffcf1266 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Mon, 11 Oct 2010 10:58:53 -0300 Subject: [media] v4l: videobuf2: add vmalloc allocator Add an implementation of contiguous virtual memory allocator and handling routines for videobuf2, implemented on top of vmalloc()/vfree() calls. Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski CC: Pawel Osciak Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index aae5005..b8c21a7 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -50,6 +50,11 @@ config VIDEOBUF2_CORE config VIDEOBUF2_MEMOPS tristate +config VIDEOBUF2_VMALLOC + select VIDEOBUF2_CORE + select VIDEOBUF2_MEMOPS + tristate + # # Multimedia Video device configuration # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index fca0444..e31c88e 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -113,6 +113,7 @@ obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o +obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c new file mode 100644 index 0000000..b5e6936 --- /dev/null +++ b/drivers/media/video/videobuf2-vmalloc.c @@ -0,0 +1,132 @@ +/* + * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2 + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Pawel Osciak + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include + +struct vb2_vmalloc_buf { + void *vaddr; + unsigned long size; + atomic_t refcount; + struct vb2_vmarea_handler handler; +}; + +static void vb2_vmalloc_put(void *buf_priv); + +static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size) +{ + struct vb2_vmalloc_buf *buf; + + buf = kzalloc(sizeof *buf, GFP_KERNEL); + if (!buf) + return NULL; + + buf->size = size; + buf->vaddr = vmalloc_user(buf->size); + buf->handler.refcount = &buf->refcount; + buf->handler.put = vb2_vmalloc_put; + buf->handler.arg = buf; + + if (!buf->vaddr) { + printk(KERN_ERR "vmalloc of size %ld failed\n", buf->size); + kfree(buf); + return NULL; + } + + atomic_inc(&buf->refcount); + printk(KERN_DEBUG "Allocated vmalloc buffer of size %ld at vaddr=%p\n", + buf->size, buf->vaddr); + + return buf; +} + +static void vb2_vmalloc_put(void *buf_priv) +{ + struct vb2_vmalloc_buf *buf = buf_priv; + + if (atomic_dec_and_test(&buf->refcount)) { + printk(KERN_DEBUG "%s: Freeing vmalloc mem at vaddr=%p\n", + __func__, buf->vaddr); + vfree(buf->vaddr); + kfree(buf); + } +} + +static void *vb2_vmalloc_vaddr(void *buf_priv) +{ + struct vb2_vmalloc_buf *buf = buf_priv; + + BUG_ON(!buf); + + if (!buf->vaddr) { + printk(KERN_ERR "Address of an unallocated plane requested\n"); + return NULL; + } + + return buf->vaddr; +} + +static unsigned int vb2_vmalloc_num_users(void *buf_priv) +{ + struct vb2_vmalloc_buf *buf = buf_priv; + return atomic_read(&buf->refcount); +} + +static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma) +{ + struct vb2_vmalloc_buf *buf = buf_priv; + int ret; + + if (!buf) { + printk(KERN_ERR "No memory to map\n"); + return -EINVAL; + } + + ret = remap_vmalloc_range(vma, buf->vaddr, 0); + if (ret) { + printk(KERN_ERR "Remapping vmalloc memory, error: %d\n", ret); + return ret; + } + + /* + * Make sure that vm_areas for 2 buffers won't be merged together + */ + vma->vm_flags |= VM_DONTEXPAND; + + /* + * Use common vm_area operations to track buffer refcount. + */ + vma->vm_private_data = &buf->handler; + vma->vm_ops = &vb2_common_vm_ops; + + vma->vm_ops->open(vma); + + return 0; +} + +const struct vb2_mem_ops vb2_vmalloc_memops = { + .alloc = vb2_vmalloc_alloc, + .put = vb2_vmalloc_put, + .vaddr = vb2_vmalloc_vaddr, + .mmap = vb2_vmalloc_mmap, + .num_users = vb2_vmalloc_num_users, +}; +EXPORT_SYMBOL_GPL(vb2_vmalloc_memops); + +MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2"); +MODULE_AUTHOR("Pawel Osciak"); +MODULE_LICENSE("GPL"); diff --git a/include/media/videobuf2-vmalloc.h b/include/media/videobuf2-vmalloc.h new file mode 100644 index 0000000..a76b8af --- /dev/null +++ b/include/media/videobuf2-vmalloc.h @@ -0,0 +1,20 @@ +/* + * videobuf2-vmalloc.h - vmalloc memory allocator for videobuf2 + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Pawel Osciak + * + * 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. + */ + +#ifndef _MEDIA_VIDEOBUF2_VMALLOC_H +#define _MEDIA_VIDEOBUF2_VMALLOC_H + +#include + +extern const struct vb2_mem_ops vb2_vmalloc_memops; + +#endif -- cgit v0.10.2 From 1a758d4e847f7743ac1b5aa79fde3ba2d8b5132b Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Mon, 11 Oct 2010 10:59:36 -0300 Subject: [media] v4l: videobuf2: add DMA coherent allocator Add an implementation of DMA coherent memory allocator and handling routines for videobuf2, implemented on top of dma_alloc_coherent() call. Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski CC: Pawel Osciak Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index b8c21a7..b64183e 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -50,6 +50,11 @@ config VIDEOBUF2_CORE config VIDEOBUF2_MEMOPS tristate +config VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_CORE + select VIDEOBUF2_MEMOPS + tristate + config VIDEOBUF2_VMALLOC select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index e31c88e..106d40b 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -114,6 +114,7 @@ obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o +obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c new file mode 100644 index 0000000..bb6a560 --- /dev/null +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -0,0 +1,185 @@ +/* + * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2 + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Pawel Osciak + * + * 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. + */ + +#include +#include +#include + +#include +#include + +struct vb2_dc_conf { + struct device *dev; +}; + +struct vb2_dc_buf { + struct vb2_dc_conf *conf; + void *vaddr; + dma_addr_t paddr; + unsigned long size; + struct vm_area_struct *vma; + atomic_t refcount; + struct vb2_vmarea_handler handler; +}; + +static void vb2_dma_contig_put(void *buf_priv); + +static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size) +{ + struct vb2_dc_conf *conf = alloc_ctx; + struct vb2_dc_buf *buf; + + buf = kzalloc(sizeof *buf, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->paddr, + GFP_KERNEL); + if (!buf->vaddr) { + dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n", + buf->size); + kfree(buf); + return ERR_PTR(-ENOMEM); + } + + buf->conf = conf; + buf->size = size; + + buf->handler.refcount = &buf->refcount; + buf->handler.put = vb2_dma_contig_put; + buf->handler.arg = buf; + + atomic_inc(&buf->refcount); + + return buf; +} + +static void vb2_dma_contig_put(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + if (atomic_dec_and_test(&buf->refcount)) { + dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr, + buf->paddr); + kfree(buf); + } +} + +static void *vb2_dma_contig_cookie(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + return (void *)buf->paddr; +} + +static void *vb2_dma_contig_vaddr(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + if (!buf) + return 0; + + return buf->vaddr; +} + +static unsigned int vb2_dma_contig_num_users(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + return atomic_read(&buf->refcount); +} + +static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma) +{ + struct vb2_dc_buf *buf = buf_priv; + + if (!buf) { + printk(KERN_ERR "No buffer to map\n"); + return -EINVAL; + } + + return vb2_mmap_pfn_range(vma, buf->paddr, buf->size, + &vb2_common_vm_ops, &buf->handler); +} + +static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr, + unsigned long size, int write) +{ + struct vb2_dc_buf *buf; + struct vm_area_struct *vma; + dma_addr_t paddr = 0; + int ret; + + buf = kzalloc(sizeof *buf, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + ret = vb2_get_contig_userptr(vaddr, size, &vma, &paddr); + if (ret) { + printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n", + vaddr); + kfree(buf); + return ERR_PTR(ret); + } + + buf->size = size; + buf->paddr = paddr; + buf->vma = vma; + + return buf; +} + +static void vb2_dma_contig_put_userptr(void *mem_priv) +{ + struct vb2_dc_buf *buf = mem_priv; + + if (!buf) + return; + + vb2_put_vma(buf->vma); + kfree(buf); +} + +const struct vb2_mem_ops vb2_dma_contig_memops = { + .alloc = vb2_dma_contig_alloc, + .put = vb2_dma_contig_put, + .cookie = vb2_dma_contig_cookie, + .vaddr = vb2_dma_contig_vaddr, + .mmap = vb2_dma_contig_mmap, + .get_userptr = vb2_dma_contig_get_userptr, + .put_userptr = vb2_dma_contig_put_userptr, + .num_users = vb2_dma_contig_num_users, +}; +EXPORT_SYMBOL_GPL(vb2_dma_contig_memops); + +void *vb2_dma_contig_init_ctx(struct device *dev) +{ + struct vb2_dc_conf *conf; + + conf = kzalloc(sizeof *conf, GFP_KERNEL); + if (!conf) + return ERR_PTR(-ENOMEM); + + conf->dev = dev; + + return conf; +} +EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx); + +void vb2_dma_contig_cleanup_ctx(void *alloc_ctx) +{ + kfree(alloc_ctx); +} +EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx); + +MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2"); +MODULE_AUTHOR("Pawel Osciak"); +MODULE_LICENSE("GPL"); diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h new file mode 100644 index 0000000..fb7ca84 --- /dev/null +++ b/include/media/videobuf2-dma-contig.h @@ -0,0 +1,29 @@ +/* + * videobuf2-dma-coherent.h - DMA coherent memory allocator for videobuf2 + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Pawel Osciak + * + * 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. + */ + +#ifndef _MEDIA_VIDEOBUF2_DMA_COHERENT_H +#define _MEDIA_VIDEOBUF2_DMA_COHERENT_H + +#include + +static inline unsigned long vb2_dma_contig_plane_paddr( + struct vb2_buffer *vb, unsigned int plane_no) +{ + return (unsigned long)vb2_plane_cookie(vb, plane_no); +} + +void *vb2_dma_contig_init_ctx(struct device *dev); +void vb2_dma_contig_cleanup_ctx(void *alloc_ctx); + +extern const struct vb2_mem_ops vb2_dma_contig_memops; + +#endif -- cgit v0.10.2 From 5ba3f757f0592ca001266b4a6214d0332349909c Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 29 Nov 2010 11:53:34 -0300 Subject: [media] v4l: videobuf2: add DMA scatter/gather allocator Add an implementation of DMA scatter/gather allocator and handling routines for videobuf2. For mmap operation mode it is implemented on top of alloc_page + sg_set_page/_free_page. For userptr operation mode it is implemented on top of get_user_pages + sg_set_page/put_page. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski CC: Pawel Osciak Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index b64183e..8710d90 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -60,6 +60,12 @@ config VIDEOBUF2_VMALLOC select VIDEOBUF2_MEMOPS tristate + +config VIDEOBUF2_DMA_SG + #depends on HAS_DMA + select VIDEOBUF2_CORE + select VIDEOBUF2_MEMOPS + tristate # # Multimedia Video device configuration # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 106d40b..40833fe 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -115,6 +115,7 @@ obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o +obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c new file mode 100644 index 0000000..20b5c5d --- /dev/null +++ b/drivers/media/video/videobuf2-dma-sg.c @@ -0,0 +1,292 @@ +/* + * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2 + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Andrzej Pietrasiewicz + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct vb2_dma_sg_buf { + void *vaddr; + struct page **pages; + int write; + int offset; + struct vb2_dma_sg_desc sg_desc; + atomic_t refcount; + struct vb2_vmarea_handler handler; +}; + +static void vb2_dma_sg_put(void *buf_priv); + +static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size) +{ + struct vb2_dma_sg_buf *buf; + int i; + + buf = kzalloc(sizeof *buf, GFP_KERNEL); + if (!buf) + return NULL; + + buf->vaddr = NULL; + buf->write = 0; + buf->offset = 0; + buf->sg_desc.size = size; + buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + + buf->sg_desc.sglist = vmalloc(buf->sg_desc.num_pages * + sizeof(*buf->sg_desc.sglist)); + if (!buf->sg_desc.sglist) + goto fail_sglist_alloc; + memset(buf->sg_desc.sglist, 0, buf->sg_desc.num_pages * + sizeof(*buf->sg_desc.sglist)); + sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages); + + buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *), + GFP_KERNEL); + if (!buf->pages) + goto fail_pages_array_alloc; + + for (i = 0; i < buf->sg_desc.num_pages; ++i) { + buf->pages[i] = alloc_page(GFP_KERNEL); + if (NULL == buf->pages[i]) + goto fail_pages_alloc; + sg_set_page(&buf->sg_desc.sglist[i], + buf->pages[i], PAGE_SIZE, 0); + } + + buf->handler.refcount = &buf->refcount; + buf->handler.put = vb2_dma_sg_put; + buf->handler.arg = buf; + + atomic_inc(&buf->refcount); + + printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n", + __func__, buf->sg_desc.num_pages); + + if (!buf->vaddr) + buf->vaddr = vm_map_ram(buf->pages, + buf->sg_desc.num_pages, + -1, + PAGE_KERNEL); + return buf; + +fail_pages_alloc: + while (--i >= 0) + __free_page(buf->pages[i]); + +fail_pages_array_alloc: + vfree(buf->sg_desc.sglist); + +fail_sglist_alloc: + kfree(buf); + return NULL; +} + +static void vb2_dma_sg_put(void *buf_priv) +{ + struct vb2_dma_sg_buf *buf = buf_priv; + int i = buf->sg_desc.num_pages; + + if (atomic_dec_and_test(&buf->refcount)) { + printk(KERN_DEBUG "%s: Freeing buffer of %d pages\n", __func__, + buf->sg_desc.num_pages); + if (buf->vaddr) + vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages); + vfree(buf->sg_desc.sglist); + while (--i >= 0) + __free_page(buf->pages[i]); + kfree(buf->pages); + kfree(buf); + } +} + +static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, + unsigned long size, int write) +{ + struct vb2_dma_sg_buf *buf; + unsigned long first, last; + int num_pages_from_user, i; + + buf = kzalloc(sizeof *buf, GFP_KERNEL); + if (!buf) + return NULL; + + buf->vaddr = NULL; + buf->write = write; + buf->offset = vaddr & ~PAGE_MASK; + buf->sg_desc.size = size; + + first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; + last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; + buf->sg_desc.num_pages = last - first + 1; + + buf->sg_desc.sglist = vmalloc( + buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist)); + if (!buf->sg_desc.sglist) + goto userptr_fail_sglist_alloc; + + memset(buf->sg_desc.sglist, 0, + buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist)); + sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages); + + buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *), + GFP_KERNEL); + if (!buf->pages) + goto userptr_fail_pages_array_alloc; + + down_read(¤t->mm->mmap_sem); + num_pages_from_user = get_user_pages(current, current->mm, + vaddr & PAGE_MASK, + buf->sg_desc.num_pages, + write, + 1, /* force */ + buf->pages, + NULL); + up_read(¤t->mm->mmap_sem); + if (num_pages_from_user != buf->sg_desc.num_pages) + goto userptr_fail_get_user_pages; + + sg_set_page(&buf->sg_desc.sglist[0], buf->pages[0], + PAGE_SIZE - buf->offset, buf->offset); + size -= PAGE_SIZE - buf->offset; + for (i = 1; i < buf->sg_desc.num_pages; ++i) { + sg_set_page(&buf->sg_desc.sglist[i], buf->pages[i], + min_t(size_t, PAGE_SIZE, size), 0); + size -= min_t(size_t, PAGE_SIZE, size); + } + return buf; + +userptr_fail_get_user_pages: + printk(KERN_DEBUG "get_user_pages requested/got: %d/%d]\n", + num_pages_from_user, buf->sg_desc.num_pages); + while (--num_pages_from_user >= 0) + put_page(buf->pages[num_pages_from_user]); + +userptr_fail_pages_array_alloc: + vfree(buf->sg_desc.sglist); + +userptr_fail_sglist_alloc: + kfree(buf); + return NULL; +} + +/* + * @put_userptr: inform the allocator that a USERPTR buffer will no longer + * be used + */ +static void vb2_dma_sg_put_userptr(void *buf_priv) +{ + struct vb2_dma_sg_buf *buf = buf_priv; + int i = buf->sg_desc.num_pages; + + printk(KERN_DEBUG "%s: Releasing userspace buffer of %d pages\n", + __func__, buf->sg_desc.num_pages); + if (buf->vaddr) + vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages); + while (--i >= 0) { + if (buf->write) + set_page_dirty_lock(buf->pages[i]); + put_page(buf->pages[i]); + } + vfree(buf->sg_desc.sglist); + kfree(buf->pages); + kfree(buf); +} + +static void *vb2_dma_sg_vaddr(void *buf_priv) +{ + struct vb2_dma_sg_buf *buf = buf_priv; + + BUG_ON(!buf); + + if (!buf->vaddr) + buf->vaddr = vm_map_ram(buf->pages, + buf->sg_desc.num_pages, + -1, + PAGE_KERNEL); + + /* add offset in case userptr is not page-aligned */ + return buf->vaddr + buf->offset; +} + +static unsigned int vb2_dma_sg_num_users(void *buf_priv) +{ + struct vb2_dma_sg_buf *buf = buf_priv; + + return atomic_read(&buf->refcount); +} + +static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma) +{ + struct vb2_dma_sg_buf *buf = buf_priv; + unsigned long uaddr = vma->vm_start; + unsigned long usize = vma->vm_end - vma->vm_start; + int i = 0; + + if (!buf) { + printk(KERN_ERR "No memory to map\n"); + return -EINVAL; + } + + do { + int ret; + + ret = vm_insert_page(vma, uaddr, buf->pages[i++]); + if (ret) { + printk(KERN_ERR "Remapping memory, error: %d\n", ret); + return ret; + } + + uaddr += PAGE_SIZE; + usize -= PAGE_SIZE; + } while (usize > 0); + + + /* + * Use common vm_area operations to track buffer refcount. + */ + vma->vm_private_data = &buf->handler; + vma->vm_ops = &vb2_common_vm_ops; + + vma->vm_ops->open(vma); + + return 0; +} + +static void *vb2_dma_sg_cookie(void *buf_priv) +{ + struct vb2_dma_sg_buf *buf = buf_priv; + + return &buf->sg_desc; +} + +const struct vb2_mem_ops vb2_dma_sg_memops = { + .alloc = vb2_dma_sg_alloc, + .put = vb2_dma_sg_put, + .get_userptr = vb2_dma_sg_get_userptr, + .put_userptr = vb2_dma_sg_put_userptr, + .vaddr = vb2_dma_sg_vaddr, + .mmap = vb2_dma_sg_mmap, + .num_users = vb2_dma_sg_num_users, + .cookie = vb2_dma_sg_cookie, +}; +EXPORT_SYMBOL_GPL(vb2_dma_sg_memops); + +MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2"); +MODULE_AUTHOR("Andrzej Pietrasiewicz"); +MODULE_LICENSE("GPL"); diff --git a/include/media/videobuf2-dma-sg.h b/include/media/videobuf2-dma-sg.h new file mode 100644 index 0000000..0038526 --- /dev/null +++ b/include/media/videobuf2-dma-sg.h @@ -0,0 +1,32 @@ +/* + * videobuf2-dma-sg.h - DMA scatter/gather memory allocator for videobuf2 + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Andrzej Pietrasiewicz + * + * 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. + */ + +#ifndef _MEDIA_VIDEOBUF2_DMA_SG_H +#define _MEDIA_VIDEOBUF2_DMA_SG_H + +#include + +struct vb2_dma_sg_desc { + unsigned long size; + unsigned int num_pages; + struct scatterlist *sglist; +}; + +static inline struct vb2_dma_sg_desc *vb2_dma_sg_plane_desc( + struct vb2_buffer *vb, unsigned int plane_no) +{ + return (struct vb2_dma_sg_desc *)vb2_plane_cookie(vb, plane_no); +} + +extern const struct vb2_mem_ops vb2_dma_sg_memops; + +#endif -- cgit v0.10.2 From b25748fe612626d6c9e344482bb028d76c1e11f2 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 6 Dec 2010 05:56:55 -0300 Subject: [media] v4l: videobuf2: add read() and write() emulator Add a generic file io (read and write) emulator for videobuf2. It uses MMAP memory type buffers and generic vb2 calls: req_bufs, qbuf and dqbuf. Video date is being copied from mmap buffers to userspace with standard copy_to_user() function. To add support for file io the driver needs to provide an additional callback - read_setup or write_setup. It should provide the default number of buffers used by emulator and flags. With these flags one can detemine the style of read() or write() emulation. By default 'streaming' style is used. With VB2_FILEIO_READ_ONCE flag one can select 'one shot' mode for read() emulator. With VB2_FILEIO_WRITE_IMMEDIATE flag one can select immediate conversion of write calls to qbuf for write() emulator, so the vb2 will not wait until each buffer is filled completely before queueing it to the driver. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park CC: Pawel Osciak Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index b856bd1..73f3b22 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -453,6 +453,11 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) unsigned long plane_sizes[VIDEO_MAX_PLANES]; int ret = 0; + if (q->fileio) { + dprintk(1, "reqbufs: file io in progress\n"); + return -EBUSY; + } + if (req->memory != V4L2_MEMORY_MMAP && req->memory != V4L2_MEMORY_USERPTR) { dprintk(1, "reqbufs: unsupported memory type\n"); @@ -824,6 +829,11 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) struct vb2_buffer *vb; int ret = 0; + if (q->fileio) { + dprintk(1, "qbuf: file io in progress\n"); + return -EBUSY; + } + if (b->type != q->type) { dprintk(1, "qbuf: invalid buffer type\n"); return -EINVAL; @@ -1028,6 +1038,11 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) struct vb2_buffer *vb = NULL; int ret; + if (q->fileio) { + dprintk(1, "dqbuf: file io in progress\n"); + return -EBUSY; + } + if (b->type != q->type) { dprintk(1, "dqbuf: invalid buffer type\n"); return -EINVAL; @@ -1087,6 +1102,11 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { struct vb2_buffer *vb; + if (q->fileio) { + dprintk(1, "streamon: file io in progress\n"); + return -EBUSY; + } + if (type != q->type) { dprintk(1, "streamon: invalid stream type\n"); return -EINVAL; @@ -1180,6 +1200,11 @@ static void __vb2_queue_cancel(struct vb2_queue *q) */ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { + if (q->fileio) { + dprintk(1, "streamoff: file io in progress\n"); + return -EBUSY; + } + if (type != q->type) { dprintk(1, "streamoff: invalid stream type\n"); return -EINVAL; @@ -1303,6 +1328,8 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) } EXPORT_SYMBOL_GPL(vb2_mmap); +static int __vb2_init_fileio(struct vb2_queue *q, int read); +static int __vb2_cleanup_fileio(struct vb2_queue *q); /** * vb2_poll() - implements poll userspace operation @@ -1323,9 +1350,30 @@ EXPORT_SYMBOL_GPL(vb2_mmap); unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { unsigned long flags; + unsigned int ret; struct vb2_buffer *vb = NULL; /* + * Start file io emulator 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 ret; + } + if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) { + ret = __vb2_init_fileio(q, 0); + if (ret) + return ret; + /* + * Write to OUTPUT queue can be done immediately. + */ + return POLLOUT | POLLWRNORM; + } + } + + /* * There is nothing to wait for if no buffers have already been queued. */ if (list_empty(&q->queued_list)) @@ -1395,11 +1443,362 @@ EXPORT_SYMBOL_GPL(vb2_queue_init); */ void vb2_queue_release(struct vb2_queue *q) { + __vb2_cleanup_fileio(q); __vb2_queue_cancel(q); __vb2_queue_free(q); } EXPORT_SYMBOL_GPL(vb2_queue_release); +/** + * struct vb2_fileio_buf - buffer context used by file io emulator + * + * vb2 provides a compatibility layer and emulator of file io (read and + * write) calls on top of streaming API. This structure is used for + * tracking context related to the buffers. + */ +struct vb2_fileio_buf { + void *vaddr; + unsigned int size; + unsigned int pos; + unsigned int queued:1; +}; + +/** + * struct vb2_fileio_data - queue context used by file io emulator + * + * vb2 provides a compatibility layer and emulator of file io (read and + * write) calls on top of streaming API. For proper operation it required + * this structure to save the driver state between each call of the read + * or write function. + */ +struct vb2_fileio_data { + struct v4l2_requestbuffers req; + struct v4l2_buffer b; + struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME]; + unsigned int index; + unsigned int q_count; + unsigned int dq_count; + unsigned int flags; +}; + +/** + * __vb2_init_fileio() - initialize file io emulator + * @q: videobuf2 queue + * @read: mode selector (1 means read, 0 means write) + */ +static int __vb2_init_fileio(struct vb2_queue *q, int read) +{ + struct vb2_fileio_data *fileio; + int i, ret; + unsigned int count = 0; + + /* + * Sanity check + */ + if ((read && !(q->io_modes & VB2_READ)) || + (!read && !(q->io_modes & VB2_WRITE))) + BUG(); + + /* + * Check if device supports mapping buffers to kernel virtual space. + */ + if (!q->mem_ops->vaddr) + return -EBUSY; + + /* + * Check if streaming api has not been already activated. + */ + if (q->streaming || q->num_buffers > 0) + return -EBUSY; + + /* + * Start with count 1, driver can increase it in queue_setup() + */ + count = 1; + + dprintk(3, "setting up file io: mode %s, count %d, flags %08x\n", + (read) ? "read" : "write", count, q->io_flags); + + fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL); + if (fileio == NULL) + return -ENOMEM; + + fileio->flags = q->io_flags; + + /* + * Request buffers and use MMAP type to force driver + * to allocate buffers by itself. + */ + fileio->req.count = count; + fileio->req.memory = V4L2_MEMORY_MMAP; + fileio->req.type = q->type; + ret = vb2_reqbufs(q, &fileio->req); + if (ret) + goto err_kfree; + + /* + * Check if plane_count is correct + * (multiplane buffers are not supported). + */ + if (q->bufs[0]->num_planes != 1) { + fileio->req.count = 0; + ret = -EBUSY; + goto err_reqbufs; + } + + /* + * Get kernel address of each buffer. + */ + for (i = 0; i < q->num_buffers; i++) { + fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0); + if (fileio->bufs[i].vaddr == NULL) + goto err_reqbufs; + fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0); + } + + /* + * Read mode requires pre queuing of all buffers. + */ + if (read) { + /* + * Queue all buffers. + */ + for (i = 0; i < q->num_buffers; i++) { + struct v4l2_buffer *b = &fileio->b; + memset(b, 0, sizeof(*b)); + b->type = q->type; + b->memory = q->memory; + b->index = i; + ret = vb2_qbuf(q, b); + if (ret) + goto err_reqbufs; + fileio->bufs[i].queued = 1; + } + + /* + * Start streaming. + */ + ret = vb2_streamon(q, q->type); + if (ret) + goto err_reqbufs; + } + + q->fileio = fileio; + + return ret; + +err_reqbufs: + vb2_reqbufs(q, &fileio->req); + +err_kfree: + kfree(fileio); + return ret; +} + +/** + * __vb2_cleanup_fileio() - free resourced used by file io emulator + * @q: videobuf2 queue + */ +static int __vb2_cleanup_fileio(struct vb2_queue *q) +{ + struct vb2_fileio_data *fileio = q->fileio; + + if (fileio) { + /* + * Hack fileio context to enable direct calls to vb2 ioctl + * interface. + */ + q->fileio = NULL; + + vb2_streamoff(q, q->type); + fileio->req.count = 0; + vb2_reqbufs(q, &fileio->req); + kfree(fileio); + dprintk(3, "file io emulator closed\n"); + } + return 0; +} + +/** + * __vb2_perform_fileio() - perform a single file io (read or write) operation + * @q: videobuf2 queue + * @data: pointed to target userspace buffer + * @count: number of bytes to read or write + * @ppos: file handle position tracking pointer + * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) + * @read: access mode selector (1 means read, 0 means write) + */ +static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock, int read) +{ + struct vb2_fileio_data *fileio; + struct vb2_fileio_buf *buf; + int ret, index; + + dprintk(3, "file io: mode %s, offset %ld, count %ld, %sblocking\n", + read ? "read" : "write", (long)*ppos, count, + nonblock ? "non" : ""); + + if (!data) + return -EINVAL; + + /* + * Initialize emulator on first call. + */ + if (!q->fileio) { + ret = __vb2_init_fileio(q, read); + dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); + if (ret) + return ret; + } + fileio = q->fileio; + + /* + * Hack fileio context to enable direct calls to vb2 ioctl interface. + * The pointer will be restored before returning from this function. + */ + q->fileio = NULL; + + index = fileio->index; + buf = &fileio->bufs[index]; + + /* + * Check if we need to dequeue the buffer. + */ + if (buf->queued) { + struct vb2_buffer *vb; + + /* + * Call vb2_dqbuf to get buffer back. + */ + memset(&fileio->b, 0, sizeof(fileio->b)); + fileio->b.type = q->type; + fileio->b.memory = q->memory; + fileio->b.index = index; + ret = vb2_dqbuf(q, &fileio->b, nonblock); + dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); + if (ret) + goto end; + fileio->dq_count += 1; + + /* + * Get number of bytes filled by the driver + */ + vb = q->bufs[index]; + buf->size = vb2_get_plane_payload(vb, 0); + buf->queued = 0; + } + + /* + * Limit count on last few bytes of the buffer. + */ + if (buf->pos + count > buf->size) { + count = buf->size - buf->pos; + dprintk(5, "reducing read count: %ld\n", count); + } + + /* + * Transfer data to userspace. + */ + dprintk(3, "file io: copying %ld bytes - buffer %d, offset %u\n", + count, index, buf->pos); + if (read) + ret = copy_to_user(data, buf->vaddr + buf->pos, count); + else + ret = copy_from_user(buf->vaddr + buf->pos, data, count); + if (ret) { + dprintk(3, "file io: error copying data\n"); + ret = -EFAULT; + goto end; + } + + /* + * Update counters. + */ + buf->pos += count; + *ppos += count; + + /* + * Queue next buffer if required. + */ + if (buf->pos == buf->size || + (!read && (fileio->flags & VB2_FILEIO_WRITE_IMMEDIATELY))) { + /* + * Check if this is the last buffer to read. + */ + if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) && + fileio->dq_count == 1) { + dprintk(3, "file io: read limit reached\n"); + /* + * Restore fileio pointer and release the context. + */ + q->fileio = fileio; + return __vb2_cleanup_fileio(q); + } + + /* + * Call vb2_qbuf and give buffer to the driver. + */ + memset(&fileio->b, 0, sizeof(fileio->b)); + fileio->b.type = q->type; + fileio->b.memory = q->memory; + fileio->b.index = index; + fileio->b.bytesused = buf->pos; + ret = vb2_qbuf(q, &fileio->b); + dprintk(5, "file io: vb2_dbuf result: %d\n", ret); + if (ret) + goto end; + + /* + * Buffer has been queued, update the status + */ + buf->pos = 0; + buf->queued = 1; + buf->size = q->bufs[0]->v4l2_planes[0].length; + fileio->q_count += 1; + + /* + * Switch to the next buffer + */ + fileio->index = (index + 1) % q->num_buffers; + + /* + * Start streaming if required. + */ + if (!read && !q->streaming) { + ret = vb2_streamon(q, q->type); + if (ret) + goto end; + } + } + + /* + * Return proper number of bytes processed. + */ + if (ret == 0) + ret = count; +end: + /* + * Restore the fileio context and block vb2 ioctl interface. + */ + q->fileio = fileio; + return ret; +} + +size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblocking) +{ + return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1); +} +EXPORT_SYMBOL_GPL(vb2_read); + +size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblocking) +{ + return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0); +} +EXPORT_SYMBOL_GPL(vb2_write); + MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2"); MODULE_AUTHOR("Pawel Osciak, Marek Szyprowski"); MODULE_LICENSE("GPL"); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 1dafac0..0d71fc5 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -18,6 +18,7 @@ #include struct vb2_alloc_ctx; +struct vb2_fileio_data; /** * struct vb2_mem_ops - memory handling/memory allocator operations @@ -54,6 +55,7 @@ struct vb2_alloc_ctx; * * Required ops for USERPTR types: get_userptr, put_userptr. * Required ops for MMAP types: alloc, put, num_users, mmap. + * Required ops for read/write access types: alloc, put, num_users, vaddr */ struct vb2_mem_ops { void *(*alloc)(void *alloc_ctx, unsigned long size); @@ -249,6 +251,7 @@ struct vb2_ops { * @done_wq: waitqueue for processes waiting for buffers ready to be dequeued * @alloc_ctx: memory type/allocator-specific contexts for each plane * @streaming: current streaming state + * @fileio: file io emulator internal data, used only if emulator is active */ struct vb2_queue { enum v4l2_buf_type type; @@ -275,6 +278,8 @@ struct vb2_queue { void *alloc_ctx[VIDEO_MAX_PLANES]; unsigned int streaming:1; + + struct vb2_fileio_data *fileio; }; void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no); @@ -298,6 +303,10 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type); int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma); unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait); +size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock); +size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock); /** * vb2_is_streaming() - return streaming status of the queue -- cgit v0.10.2 From 08b99e26a347441c50c1113802801a122dd1eb7a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Jan 2011 17:12:34 -0300 Subject: [media] vb2 core: Fix a few printk warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/video/videobuf2-core.c: In function ‘__vb2_perform_fileio’: drivers/media/video/videobuf2-core.c:1638: warning: format ‘%ld’ expects type ‘long int’, but argument 4 has type ‘size_t’ drivers/media/video/videobuf2-core.c:1697: warning: format ‘%ld’ expects type ‘long int’, but argument 2 has type ‘size_t’ drivers/media/video/videobuf2-core.c:1703: warning: format ‘%ld’ expects type ‘long int’, but argument 2 has type ‘size_t’ Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 73f3b22..cc7ab0a 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -1635,7 +1635,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ struct vb2_fileio_buf *buf; int ret, index; - dprintk(3, "file io: mode %s, offset %ld, count %ld, %sblocking\n", + dprintk(3, "file io: mode %s, offset %ld, count %zd, %sblocking\n", read ? "read" : "write", (long)*ppos, count, nonblock ? "non" : ""); @@ -1694,13 +1694,13 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ if (buf->pos + count > buf->size) { count = buf->size - buf->pos; - dprintk(5, "reducing read count: %ld\n", count); + dprintk(5, "reducing read count: %zd\n", count); } /* * Transfer data to userspace. */ - dprintk(3, "file io: copying %ld bytes - buffer %d, offset %u\n", + dprintk(3, "file io: copying %zd bytes - buffer %d, offset %u\n", count, index, buf->pos); if (read) ret = copy_to_user(data, buf->vaddr + buf->pos, count); -- cgit v0.10.2 From c4c0a78d459f2e4fcc7c4f3621a34e71ec7970d0 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Wed, 12 Jan 2011 05:57:26 -0300 Subject: [media] Fix mmap() example in the V4L2 API DocBook Correct ioctl return value handling and fix coding style issues. [mchehab@redhat.com: return -1 is OK, according with ioctl manpages. Reverting ioctl changes] Signed-off-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/io.xml b/Documentation/DocBook/v4l/io.xml index d424886..a975043 100644 --- a/Documentation/DocBook/v4l/io.xml +++ b/Documentation/DocBook/v4l/io.xml @@ -141,63 +141,63 @@ struct { } *buffers; unsigned int i; -memset (&reqbuf, 0, sizeof (reqbuf)); +memset(&reqbuf, 0, sizeof(reqbuf)); reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbuf.memory = V4L2_MEMORY_MMAP; reqbuf.count = 20; if (-1 == ioctl (fd, &VIDIOC-REQBUFS;, &reqbuf)) { if (errno == EINVAL) - printf ("Video capturing or mmap-streaming is not supported\n"); + printf("Video capturing or mmap-streaming is not supported\n"); else - perror ("VIDIOC_REQBUFS"); + perror("VIDIOC_REQBUFS"); - exit (EXIT_FAILURE); + exit(EXIT_FAILURE); } /* We want at least five buffers. */ if (reqbuf.count < 5) { /* You may need to free the buffers here. */ - printf ("Not enough buffer memory\n"); - exit (EXIT_FAILURE); + printf("Not enough buffer memory\n"); + exit(EXIT_FAILURE); } -buffers = calloc (reqbuf.count, sizeof (*buffers)); -assert (buffers != NULL); +buffers = calloc(reqbuf.count, sizeof(*buffers)); +assert(buffers != NULL); for (i = 0; i < reqbuf.count; i++) { &v4l2-buffer; buffer; - memset (&buffer, 0, sizeof (buffer)); + memset(&buffer, 0, sizeof(buffer)); buffer.type = reqbuf.type; buffer.memory = V4L2_MEMORY_MMAP; buffer.index = i; if (-1 == ioctl (fd, &VIDIOC-QUERYBUF;, &buffer)) { - perror ("VIDIOC_QUERYBUF"); - exit (EXIT_FAILURE); + perror("VIDIOC_QUERYBUF"); + exit(EXIT_FAILURE); } buffers[i].length = buffer.length; /* remember for munmap() */ - buffers[i].start = mmap (NULL, buffer.length, - PROT_READ | PROT_WRITE, /* recommended */ - MAP_SHARED, /* recommended */ - fd, buffer.m.offset); + buffers[i].start = mmap(NULL, buffer.length, + PROT_READ | PROT_WRITE, /* recommended */ + MAP_SHARED, /* recommended */ + fd, buffer.m.offset); if (MAP_FAILED == buffers[i].start) { /* If you do not exit here you should unmap() and free() the buffers mapped so far. */ - perror ("mmap"); - exit (EXIT_FAILURE); + perror("mmap"); + exit(EXIT_FAILURE); } } /* Cleanup. */ for (i = 0; i < reqbuf.count; i++) - munmap (buffers[i].start, buffers[i].length); + munmap(buffers[i].start, buffers[i].length); -- cgit v0.10.2 From 53b5d5749b6fcca37c7ad60cd40feafadd390b70 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Fri, 7 Jan 2011 01:41:33 -0300 Subject: [media] Add multi-planar API documentation Add DocBook documentation for the new multi-planar API extensions to the Video for Linux 2 API DocBook. Signed-off-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index be34dcb..74923d7 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -129,6 +129,7 @@ v4l2_audioout"> v4l2_bt_timings"> v4l2_buffer"> +v4l2_plane"> v4l2_capability"> v4l2_captureparm"> v4l2_clip"> @@ -167,6 +168,8 @@ v4l2_output"> v4l2_outputparm"> v4l2_pix_format"> +v4l2_pix_format_mplane"> +v4l2_plane_pix_format"> v4l2_queryctrl"> v4l2_querymenu"> v4l2_rect"> @@ -202,6 +205,7 @@ + diff --git a/Documentation/DocBook/v4l/common.xml b/Documentation/DocBook/v4l/common.xml index cea23e1..dbab79c 100644 --- a/Documentation/DocBook/v4l/common.xml +++ b/Documentation/DocBook/v4l/common.xml @@ -846,6 +846,8 @@ conversion routine or library for integration into applications. + &sub-planar-apis; +
Image Cropping, Insertion and Scaling diff --git a/Documentation/DocBook/v4l/compat.xml b/Documentation/DocBook/v4l/compat.xml index c9ce61d..223c24c 100644 --- a/Documentation/DocBook/v4l/compat.xml +++ b/Documentation/DocBook/v4l/compat.xml @@ -2353,6 +2353,17 @@ that used it. It was originally scheduled for removal in 2.6.35.
+
+ V4L2 in Linux 2.6.38 + + + Multi-planar API added. Does not affect the compatibility of + current drivers and applications. See + multi-planar API + for details. + + +
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/v4l/dev-capture.xml b/Documentation/DocBook/v4l/dev-capture.xml index 32807e4..2237c66 100644 --- a/Documentation/DocBook/v4l/dev-capture.xml +++ b/Documentation/DocBook/v4l/dev-capture.xml @@ -18,7 +18,8 @@ files are used for video output devices. Querying Capabilities Devices supporting the video capture interface set the -V4L2_CAP_VIDEO_CAPTURE flag in the +V4L2_CAP_VIDEO_CAPTURE or +V4L2_CAP_VIDEO_CAPTURE_MPLANE flag in the capabilities field of &v4l2-capability; returned by the &VIDIOC-QUERYCAP; ioctl. As secondary device functions they may also support the video overlay @@ -64,9 +65,11 @@ linkend="crop" />. To query the current image format applications set the type field of a &v4l2-format; to -V4L2_BUF_TYPE_VIDEO_CAPTURE and call the +V4L2_BUF_TYPE_VIDEO_CAPTURE or +V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE and call the &VIDIOC-G-FMT; ioctl with a pointer to this structure. Drivers fill -the &v4l2-pix-format; pix member of the +the &v4l2-pix-format; pix or the +&v4l2-pix-format-mplane; pix_mp member of the fmt union. To request different parameters applications set the @@ -84,8 +87,8 @@ adjust the parameters and finally return the actual parameters as without disabling I/O or possibly time consuming hardware preparations. - The contents of &v4l2-pix-format; are discussed in . See also the specification of the + The contents of &v4l2-pix-format; and &v4l2-pix-format-mplane; +are discussed in . See also the specification of the VIDIOC_G_FMT, VIDIOC_S_FMT and VIDIOC_TRY_FMT ioctls for details. Video capture devices must implement both the diff --git a/Documentation/DocBook/v4l/dev-output.xml b/Documentation/DocBook/v4l/dev-output.xml index 63c3c20..919e22c 100644 --- a/Documentation/DocBook/v4l/dev-output.xml +++ b/Documentation/DocBook/v4l/dev-output.xml @@ -17,7 +17,8 @@ files are used for video capture devices. Querying Capabilities Devices supporting the video output interface set the -V4L2_CAP_VIDEO_OUTPUT flag in the +V4L2_CAP_VIDEO_OUTPUT or +V4L2_CAP_VIDEO_OUTPUT_MPLANE flag in the capabilities field of &v4l2-capability; returned by the &VIDIOC-QUERYCAP; ioctl. As secondary device functions they may also support the raw VBI @@ -60,9 +61,11 @@ linkend="crop" />. To query the current image format applications set the type field of a &v4l2-format; to -V4L2_BUF_TYPE_VIDEO_OUTPUT and call the +V4L2_BUF_TYPE_VIDEO_OUTPUT or +V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE and call the &VIDIOC-G-FMT; ioctl with a pointer to this structure. Drivers fill -the &v4l2-pix-format; pix member of the +the &v4l2-pix-format; pix or the +&v4l2-pix-format-mplane; pix_mp member of the fmt union. To request different parameters applications set the @@ -80,8 +83,8 @@ adjust the parameters and finally return the actual parameters as without disabling I/O or possibly time consuming hardware preparations. - The contents of &v4l2-pix-format; are discussed in . See also the specification of the + The contents of &v4l2-pix-format; and &v4l2-pix-format-mplane; +are discussed in . See also the specification of the VIDIOC_G_FMT, VIDIOC_S_FMT and VIDIOC_TRY_FMT ioctls for details. Video output devices must implement both the diff --git a/Documentation/DocBook/v4l/func-mmap.xml b/Documentation/DocBook/v4l/func-mmap.xml index 2e2fc39..786732b 100644 --- a/Documentation/DocBook/v4l/func-mmap.xml +++ b/Documentation/DocBook/v4l/func-mmap.xml @@ -45,7 +45,10 @@ just specify a NULL pointer here. Length of the memory area to map. This must be the same value as returned by the driver in the &v4l2-buffer; -length field. +length field for the +single-planar API, and the same value as returned by the driver +in the &v4l2-plane; length field for the +multi-planar API. @@ -106,7 +109,10 @@ flag. Offset of the buffer in device memory. This must be the same value as returned by the driver in the &v4l2-buffer; -m union offset field. +m union offset field for +the single-planar API, and the same value as returned by the driver +in the &v4l2-plane; m union +mem_offset field for the multi-planar API. diff --git a/Documentation/DocBook/v4l/func-munmap.xml b/Documentation/DocBook/v4l/func-munmap.xml index 502ed49..e2c4190 100644 --- a/Documentation/DocBook/v4l/func-munmap.xml +++ b/Documentation/DocBook/v4l/func-munmap.xml @@ -37,7 +37,8 @@ Length of the mapped buffer. This must be the same value as given to mmap() and returned by the driver in the &v4l2-buffer; length -field. +field for the single-planar API and in the &v4l2-plane; +length field for the multi-planar API. diff --git a/Documentation/DocBook/v4l/io.xml b/Documentation/DocBook/v4l/io.xml index a975043..227e7ac 100644 --- a/Documentation/DocBook/v4l/io.xml +++ b/Documentation/DocBook/v4l/io.xml @@ -121,18 +121,22 @@ mapped. Before applications can access the buffers they must map them into their address space with the &func-mmap; function. The location of the buffers in device memory can be determined with the -&VIDIOC-QUERYBUF; ioctl. The m.offset and -length returned in a &v4l2-buffer; are -passed as sixth and second parameter to the -mmap() function. The offset and length values -must not be modified. Remember the buffers are allocated in physical -memory, as opposed to virtual memory which can be swapped out to disk. -Applications should free the buffers as soon as possible with the -&func-munmap; function. +&VIDIOC-QUERYBUF; ioctl. In the single-planar API case, the +m.offset and length +returned in a &v4l2-buffer; are passed as sixth and second parameter to the +mmap() function. When using the multi-planar API, +struct &v4l2-buffer; contains an array of &v4l2-plane; structures, each +containing its own m.offset and +length. When using the multi-planar API, every +plane of every buffer has to be mapped separately, so the number of +calls to &func-mmap; should be equal to number of buffers times number of +planes in each buffer. The offset and length values must not be modified. +Remember, the buffers are allocated in physical memory, as opposed to virtual +memory, which can be swapped out to disk. Applications should free the buffers +as soon as possible with the &func-munmap; function. - Mapping buffers - + Mapping buffers in the single-planar API &v4l2-requestbuffers; reqbuf; struct { @@ -201,6 +205,88 @@ for (i = 0; i < reqbuf.count; i++) + + Mapping buffers in the multi-planar API + +&v4l2-requestbuffers; reqbuf; +/* Our current format uses 3 planes per buffer */ +#define FMT_NUM_PLANES = 3; + +struct { + void *start[FMT_NUM_PLANES]; + size_t length[FMT_NUM_PLANES]; +} *buffers; +unsigned int i, j; + +memset(&reqbuf, 0, sizeof(reqbuf)); +reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; +reqbuf.memory = V4L2_MEMORY_MMAP; +reqbuf.count = 20; + +if (ioctl(fd, &VIDIOC-REQBUFS;, &reqbuf) < 0) { + if (errno == EINVAL) + printf("Video capturing or mmap-streaming is not supported\n"); + else + perror("VIDIOC_REQBUFS"); + + exit(EXIT_FAILURE); +} + +/* We want at least five buffers. */ + +if (reqbuf.count < 5) { + /* You may need to free the buffers here. */ + printf("Not enough buffer memory\n"); + exit(EXIT_FAILURE); +} + +buffers = calloc(reqbuf.count, sizeof(*buffers)); +assert(buffers != NULL); + +for (i = 0; i < reqbuf.count; i++) { + &v4l2-buffer; buffer; + &v4l2-plane; planes[FMT_NUM_PLANES]; + + memset(&buffer, 0, sizeof(buffer)); + buffer.type = reqbuf.type; + buffer.memory = V4L2_MEMORY_MMAP; + buffer.index = i; + /* length in struct v4l2_buffer in multi-planar API stores the size + * of planes array. */ + buffer.length = FMT_NUM_PLANES; + buffer.m.planes = planes; + + if (ioctl(fd, &VIDIOC-QUERYBUF;, &buffer) < 0) { + perror("VIDIOC_QUERYBUF"); + exit(EXIT_FAILURE); + } + + /* Every plane has to be mapped separately */ + for (j = 0; j < FMT_NUM_PLANES; j++) { + buffers[i].length[j] = buffer.m.planes[j].length; /* remember for munmap() */ + + buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length, + PROT_READ | PROT_WRITE, /* recommended */ + MAP_SHARED, /* recommended */ + fd, buffer.m.planes[j].m.offset); + + if (MAP_FAILED == buffers[i].start[j]) { + /* If you do not exit here you should unmap() and free() + the buffers and planes mapped so far. */ + perror("mmap"); + exit(EXIT_FAILURE); + } + } +} + +/* Cleanup. */ + +for (i = 0; i < reqbuf.count; i++) + for (j = 0; j < FMT_NUM_PLANES; j++) + munmap(buffers[i].start[j], buffers[i].length[j]); + + + Conceptually streaming drivers maintain two buffer queues, an incoming and an outgoing queue. They separate the synchronous capture or output operation locked to a video clock from the application which is @@ -286,13 +372,13 @@ pointer method (not only memory mapping) is supported must be determined by calling the &VIDIOC-REQBUFS; ioctl. This I/O method combines advantages of the read/write and -memory mapping methods. Buffers are allocated by the application +memory mapping methods. Buffers (planes) are allocated by the application itself, and can reside for example in virtual or shared memory. Only pointers to data are exchanged, these pointers and meta-information -are passed in &v4l2-buffer;. The driver must be switched -into user pointer I/O mode by calling the &VIDIOC-REQBUFS; with the -desired buffer type. No buffers are allocated beforehands, -consequently they are not indexed and cannot be queried like mapped +are passed in &v4l2-buffer; (or in &v4l2-plane; in the multi-planar API case). +The driver must be switched into user pointer I/O mode by calling the +&VIDIOC-REQBUFS; with the desired buffer type. No buffers (planes) are allocated +beforehand, consequently they are not indexed and cannot be queried like mapped buffers with the VIDIOC_QUERYBUF ioctl. @@ -316,7 +402,7 @@ if (ioctl (fd, &VIDIOC-REQBUFS;, &reqbuf) == -1) { - Buffer addresses and sizes are passed on the fly with the + Buffer (plane) addresses and sizes are passed on the fly with the &VIDIOC-QBUF; ioctl. Although buffers are commonly cycled, applications can pass different addresses and sizes at each VIDIOC_QBUF call. If required by the hardware the @@ -396,11 +482,18 @@ rest should be evident. Buffers A buffer contains data exchanged by application and -driver using one of the Streaming I/O methods. Only pointers to -buffers are exchanged, the data itself is not copied. These pointers, -together with meta-information like timestamps or field parity, are -stored in a struct v4l2_buffer, argument to -the &VIDIOC-QUERYBUF;, &VIDIOC-QBUF; and &VIDIOC-DQBUF; ioctl. +driver using one of the Streaming I/O methods. In the multi-planar API, the +data is held in planes, while the buffer structure acts as a container +for the planes. Only pointers to buffers (planes) are exchanged, the data +itself is not copied. These pointers, together with meta-information like +timestamps or field parity, are stored in a struct +v4l2_buffer, argument to +the &VIDIOC-QUERYBUF;, &VIDIOC-QBUF; and &VIDIOC-DQBUF; ioctl. +In the multi-planar API, some plane-specific members of struct +v4l2_buffer, such as pointers and sizes for each +plane, are stored in struct v4l2_plane instead. +In that case, struct v4l2_buffer contains an array of +plane structures. Nominally timestamps refer to the first data byte transmitted. In practice however the wide range of hardware covered by the V4L2 API @@ -551,26 +644,40 @@ in accordance with the selected I/O method. __u32 offset - When memory is -V4L2_MEMORY_MMAP this is the offset of the buffer -from the start of the device memory. The value is returned by the -driver and apart of serving as parameter to the &func-mmap; function -not useful for applications. See for details. + For the single-planar API and when +memory is V4L2_MEMORY_MMAP this +is the offset of the buffer from the start of the device memory. The value is +returned by the driver and apart of serving as parameter to the &func-mmap; +function not useful for applications. See for details + unsigned long userptr - When memory is -V4L2_MEMORY_USERPTR this is a pointer to the -buffer (casted to unsigned long type) in virtual memory, set by the -application. See for details. + For the single-planar API and when +memory is V4L2_MEMORY_USERPTR +this is a pointer to the buffer (casted to unsigned long type) in virtual +memory, set by the application. See for details. + + + + + struct v4l2_plane + *planes + When using the multi-planar API, contains a userspace pointer + to an array of &v4l2-plane;. The size of the array should be put + in the length field of this + v4l2_buffer structure. __u32 length - Size of the buffer (not the payload) in bytes. + Size of the buffer (not the payload) in bytes for the + single-planar API. For the multi-planar API should contain the + number of elements in the planes array. + __u32 @@ -596,6 +703,66 @@ should set this to 0. + + struct <structname>v4l2_plane</structname> + + &cs-ustr; + + + __u32 + bytesused + + The number of bytes occupied by data in the plane + (its payload). + + + __u32 + length + + Size in bytes of the plane (not its payload). + + + union + m + + + + + + __u32 + mem_offset + When the memory type in the containing &v4l2-buffer; is + V4L2_MEMORY_MMAP, this is the value that + should be passed to &func-mmap;, similar to the + offset field in &v4l2-buffer;. + + + + __unsigned long + userptr + When the memory type in the containing &v4l2-buffer; is + V4L2_MEMORY_USERPTR, this is a userspace + pointer to the memory allocated for this plane by an application. + + + + __u32 + data_offset + + Offset in bytes to video data in the plane, if applicable. + + + + __u32 + reserved[11] + + Reserved for future use. Should be zeroed by an + application. + + + +
+ enum v4l2_buf_type @@ -604,13 +771,27 @@ should set this to 0. V4L2_BUF_TYPE_VIDEO_CAPTURE 1 - Buffer of a video capture stream, see Buffer of a single-planar video capture stream, see . + + + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE + + 9 + Buffer of a multi-planar video capture stream, see . V4L2_BUF_TYPE_VIDEO_OUTPUT 2 - Buffer of a video output stream, see Buffer of a single-planar video output stream, see . + + + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE + + 10 + Buffer of a multi-planar video output stream, see . diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index cfffc88..28f9c70 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -2,12 +2,16 @@ The V4L2 API was primarily designed for devices exchanging image data with applications. The -v4l2_pix_format structure defines the format -and layout of an image in memory. Image formats are negotiated with -the &VIDIOC-S-FMT; ioctl. (The explanations here focus on video +v4l2_pix_format and v4l2_pix_format_mplane + structures define the format and layout of an image in memory. +The former is used with the single-planar API, while the latter is used with the +multi-planar version (see ). Image formats are +negotiated with the &VIDIOC-S-FMT; ioctl. (The explanations here focus on video capturing and output, for overlay frame buffer formats see also &VIDIOC-G-FBUF;.) +
+ Single-planar format structure
struct <structname>v4l2_pix_format</structname> @@ -106,6 +110,98 @@ set this field to zero.
+
+ +
+ Multi-planar format structures + The v4l2_plane_pix_format structures define + size and layout for each of the planes in a multi-planar format. + The v4l2_pix_format_mplane structure contains + information common to all planes (such as image width and height) and + an array of v4l2_plane_pix_format structures, + describing all planes of that format. + + struct <structname>vl42_plane_pix_format</structname> + + &cs-str; + + + __u32 + sizeimage + Maximum size in bytes required for image data in this plane. + + + + __u16 + bytesperline + Distance in bytes between the leftmost pixels in two adjacent + lines. + + + __u16 + reserved[7] + Reserved for future extensions. Should be zeroed by the + application. + + + +
+ + struct <structname>v4l2_pix_format_mplane</structname> + + &cs-str; + + + __u32 + width + Image width in pixels. + + + __u32 + height + Image height in pixels. + + + __u32 + pixelformat + The pixel format. Both single- and multi-planar four character +codes can be used. + + + &v4l2-field; + field + See &v4l2-pix-format;. + + + &v4l2-colorspace; + colorspace + See &v4l2-pix-format;. + + + &v4l2-plane-pix-format; + plane_fmt[VIDEO_MAX_PLANES] + An array of structures describing format of each plane this + pixel format consists of. The number of valid entries in this array + has to be put in the num_planes + field. + + + __u8 + num_planes + Number of planes (i.e. separate memory buffers) for this format + and the number of valid entries in the + plane_fmt array. + + + __u8 + reserved[11] + Reserved for future extensions. Should be zeroed by the + application. + + + +
+
Standard Image Formats @@ -142,11 +238,19 @@ leftmost pixel of the second row from the top, and so on. The last row has just as many pad bytes after it as the other rows. In V4L2 each format has an identifier which looks like -PIX_FMT_XXX, defined in the videodev2.h -header file. These identifiers -represent four character codes +PIX_FMT_XXX, defined in the videodev.h header file. These identifiers +represent four character (FourCC) codes which are also listed below, however they are not the same as those used in the Windows world. + + For some formats, data is stored in separate, discontiguous +memory buffers. Those formats are identified by a separate set of FourCC codes +and are referred to as "multi-planar formats". For example, a YUV422 frame is +normally stored in one memory buffer, but it can also be placed in two or three +separate buffers, with Y component in one buffer and CbCr components in another +in the 2-planar version or with each component in its own buffer in the +3-planar case. Those sub-buffers are referred to as "planes".
diff --git a/Documentation/DocBook/v4l/planar-apis.xml b/Documentation/DocBook/v4l/planar-apis.xml new file mode 100644 index 0000000..8be7552 --- /dev/null +++ b/Documentation/DocBook/v4l/planar-apis.xml @@ -0,0 +1,81 @@ +
+ Single- and multi-planar APIs + + Some devices require data for each input or output video frame + to be placed in discontiguous memory buffers. In such cases one + video frame has to be addressed using more than one memory address, i.e. one + pointer per "plane". A plane is a sub-buffer of current frame. For examples + of such formats see . + + Initially, V4L2 API did not support multi-planar buffers and a set of + extensions has been introduced to handle them. Those extensions constitute + what is being referred to as the "multi-planar API". + + Some of the V4L2 API calls and structures are interpreted differently, + depending on whether single- or multi-planar API is being used. An application + can choose whether to use one or the other by passing a corresponding buffer + type to its ioctl calls. Multi-planar versions of buffer types are suffixed with + an `_MPLANE' string. For a list of available multi-planar buffer types + see &v4l2-buf-type;. + + +
+ Multi-planar formats + Multi-planar API introduces new multi-planar formats. Those formats + use a separate set of FourCC codes. It is important to distinguish between + the multi-planar API and a multi-planar format. Multi-planar API calls can + handle all single-planar formats as well, while the single-planar API cannot + handle multi-planar formats. Applications do not have to switch between APIs + when handling both single- and multi-planar devices and should use the + multi-planar API version for both single- and multi-planar formats. + Drivers that do not support multi-planar API can still be handled with it, + utilizing a compatibility layer built into standard V4L2 ioctl handling. + +
+ +
+ Single and multi-planar API compatibility layer + In most casesThe compatibility layer does not cover + drivers that do not use video_ioctl2() call., applications + can use the multi-planar API with older drivers that support + only its single-planar version and vice versa. Appropriate conversion is + done seamlessly for both applications and drivers in the V4L2 core. The + general rule of thumb is: as long as an application uses formats that + a driver supports, it can use either API (although use of multi-planar + formats is only possible with the multi-planar API). The list of formats + supported by a driver can be obtained using the &VIDIOC-ENUM-FMT; call. + It is possible, but discouraged, for a driver or an application to support + and use both versions of the API. +
+ +
+ Calls that distinguish between single and multi-planar APIs + + + &VIDIOC-QUERYCAP; + Two additional multi-planar capabilities are added. They can + be set together with non-multi-planar ones for devices that handle + both single- and multi-planar formats. + + + &VIDIOC-G-FMT;, &VIDIOC-S-FMT;, &VIDIOC-TRY-FMT; + New structures for describing multi-planar formats are added: + &v4l2-pix-format-mplane; and &v4l2-plane-pix-format;. Drivers may + define new multi-planar formats, which have distinct FourCC codes from + the existing single-planar ones. + + + + &VIDIOC-QBUF;, &VIDIOC-DQBUF;, &VIDIOC-QUERYBUF; + A new &v4l2-plane; structure for describing planes is added. + Arrays of this structure are passed in the new + m.planes field of &v4l2-buffer;. + + + + &VIDIOC-REQBUFS; + Will allocate multi-planar buffers as requested. + + +
+
diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml index 9288af9..9c7a175 100644 --- a/Documentation/DocBook/v4l/v4l2.xml +++ b/Documentation/DocBook/v4l/v4l2.xml @@ -85,6 +85,17 @@ Remote Controller chapter. + + + Pawel + Osciak + Designed and documented the multi-planar API. + +
+ pawel AT osciak.com +
+
+
@@ -102,7 +113,8 @@ Remote Controller chapter. 2010 2011 Bill Dirks, Michael H. Schimek, Hans Verkuil, Martin -Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab +Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab, + Pawel Osciak Except when explicitly stated as GPL, programming examples within @@ -116,6 +128,13 @@ structs, ioctls) must be noted in more detail in the history chapter applications. --> + 2.6.38 + po + Added the multi-planar API. + + + + 2.6.37 2010-08-06 hv diff --git a/Documentation/DocBook/v4l/vidioc-enum-fmt.xml b/Documentation/DocBook/v4l/vidioc-enum-fmt.xml index 960d446..71d373b 100644 --- a/Documentation/DocBook/v4l/vidioc-enum-fmt.xml +++ b/Documentation/DocBook/v4l/vidioc-enum-fmt.xml @@ -76,7 +76,9 @@ pixelformat field. Type of the data stream, set by the application. Only these types are valid here: V4L2_BUF_TYPE_VIDEO_CAPTURE, +V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, V4L2_BUF_TYPE_VIDEO_OUTPUT, +V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_BUF_TYPE_VIDEO_OVERLAY, and custom (driver defined) types with code V4L2_BUF_TYPE_PRIVATE and higher. diff --git a/Documentation/DocBook/v4l/vidioc-g-fmt.xml b/Documentation/DocBook/v4l/vidioc-g-fmt.xml index 7c7d1b7..a4ae59b 100644 --- a/Documentation/DocBook/v4l/vidioc-g-fmt.xml +++ b/Documentation/DocBook/v4l/vidioc-g-fmt.xml @@ -60,11 +60,13 @@ application. type field of a struct v4l2_format to the respective buffer (stream) type. For example video capture devices use -V4L2_BUF_TYPE_VIDEO_CAPTURE. When the application +V4L2_BUF_TYPE_VIDEO_CAPTURE or +V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE. When the application calls the VIDIOC_G_FMT ioctl with a pointer to this structure the driver fills the respective member of the fmt union. In case of video capture devices -that is the &v4l2-pix-format; pix member. +that is either the &v4l2-pix-format; pix or +the &v4l2-pix-format-mplane; pix_mp member. When the requested buffer type is not supported drivers return an &EINVAL;. @@ -134,6 +136,15 @@ devices. + &v4l2-pix-format-mplane; + pix_mp + Definition of an image format, see , used by video capture and output +devices that support the multi-planar +version of the API. + + + &v4l2-window; win Definition of an overlaid image, see v4l2_buffer returned by a &VIDIOC-QUERYBUF; ioctl will do as well. When the buffer is intended for output (type is -V4L2_BUF_TYPE_VIDEO_OUTPUT or +V4L2_BUF_TYPE_VIDEO_OUTPUT, +V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, or V4L2_BUF_TYPE_VBI_OUTPUT) applications must also initialize the bytesused, field and @@ -75,7 +76,11 @@ supports capturing from specific video inputs and you want to specify a video input, then flags should be set to V4L2_BUF_FLAG_INPUT and the field input must be initialized to the desired input. -The reserved field must be set to 0. +The reserved field must be set to 0. When using +the multi-planar API, the +m.planes field must contain a userspace pointer +to a filled-in array of &v4l2-plane; and the length +field must be set to the number of elements in that array. To enqueue a memory mapped @@ -93,10 +98,13 @@ structure the driver sets the buffer applications set the memory field to V4L2_MEMORY_USERPTR, the m.userptr field to the address of the -buffer and length to its size. -When VIDIOC_QBUF is called with a pointer to this -structure the driver sets the V4L2_BUF_FLAG_QUEUED -flag and clears the V4L2_BUF_FLAG_MAPPED and +buffer and length to its size. When the multi-planar +API is used, m.userptr and +length members of the passed array of &v4l2-plane; +have to be used instead. When VIDIOC_QBUF is called with +a pointer to this structure the driver sets the +V4L2_BUF_FLAG_QUEUED flag and clears the +V4L2_BUF_FLAG_MAPPED and V4L2_BUF_FLAG_DONE flags in the flags field, or it returns an error code. This ioctl locks the memory pages of the buffer in physical memory, @@ -115,7 +123,9 @@ remaining fields or returns an error code. The driver may also set V4L2_BUF_FLAG_ERROR in the flags field. It indicates a non-critical (recoverable) streaming error. In such case the application may continue as normal, but should be aware that data in the -dequeued buffer might be corrupted. +dequeued buffer might be corrupted. When using the multi-planar API, the +planes array does not have to be passed; the m.planes +member must be set to NULL in that case. By default VIDIOC_DQBUF blocks when no buffer is in the outgoing queue. When the diff --git a/Documentation/DocBook/v4l/vidioc-querybuf.xml b/Documentation/DocBook/v4l/vidioc-querybuf.xml index e649805..5c104d4 100644 --- a/Documentation/DocBook/v4l/vidioc-querybuf.xml +++ b/Documentation/DocBook/v4l/vidioc-querybuf.xml @@ -61,6 +61,10 @@ buffer at any time after buffers have been allocated with the to the number of buffers allocated with &VIDIOC-REQBUFS; (&v4l2-requestbuffers; count) minus one. The reserved field should to set to 0. +When using the multi-planar API, the +m.planes field must contain a userspace pointer to an +array of &v4l2-plane; and the length field has +to be set to the number of elements in that array. After calling VIDIOC_QUERYBUF with a pointer to this structure drivers return an error code or fill the rest of the structure. @@ -70,11 +74,13 @@ the structure. V4L2_BUF_FLAG_QUEUED and V4L2_BUF_FLAG_DONE flags will be valid. The memory field will be set to the current -I/O method, the m.offset +I/O method. For the single-planar API, the m.offset contains the offset of the buffer from the start of the device memory, -the length field its size. The driver may -or may not set the remaining fields and flags, they are meaningless in -this context. +the length field its size. For the multi-planar API, +fields m.mem_offset and +length in the m.planes +array elements will be used instead. The driver may or may not set the remaining +fields and flags, they are meaningless in this context. The v4l2_buffer structure is specified in . diff --git a/Documentation/DocBook/v4l/vidioc-querycap.xml b/Documentation/DocBook/v4l/vidioc-querycap.xml index d499da9..9369976 100644 --- a/Documentation/DocBook/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/v4l/vidioc-querycap.xml @@ -142,14 +142,30 @@ this array to zero. V4L2_CAP_VIDEO_CAPTURE 0x00000001 - The device supports the Video Capture interface. + The device supports single-planar formats through the Video Capture interface. An application can use either +the single or the multi-planar API. + + + V4L2_CAP_VIDEO_CAPTURE_MPLANE + 0x00001000 + The device supports multi-planar formats through the Video Capture interface. An application has to use the +multi-planar API. V4L2_CAP_VIDEO_OUTPUT 0x00000002 - The device supports the Video Output interface. + The device supports single-planar formats through the Video Output interface. An application can use either +the single or the multi-planar API. + + + V4L2_CAP_VIDEO_OUTPUT_MPLANE + 0x00002000 + The device supports multi-planar formats through the Video Output interface. An application has to use the +multi-planar API. V4L2_CAP_VIDEO_OVERLAY -- cgit v0.10.2 From 908a0d7c588ef87e5cf0a26805e6002a78ac9d13 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 12 Jan 2011 06:50:24 -0300 Subject: [media] v4l: mem2mem: port to videobuf2 Port memory-to-memory framework to videobuf2 framework. Add support for multi-planar Video for Linux 2 API extensions to the memory-to-memory driver framework. Based on the original patch written by Pawel Osciak. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 8710d90..3749c89 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -42,7 +42,7 @@ config VIDEO_TUNER config V4L2_MEM2MEM_DEV tristate - depends on VIDEOBUF_GEN + depends on VIDEOBUF2_CORE config VIDEOBUF2_CORE tristate @@ -989,6 +989,7 @@ if V4L_MEM2MEM_DRIVERS config VIDEO_MEM2MEM_TESTDEV tristate "Virtual test device for mem2mem framework" depends on VIDEO_DEV && VIDEO_V4L2 + depends on BROKEN select VIDEOBUF_VMALLOC select V4L2_MEM2MEM_DEV default n @@ -999,6 +1000,7 @@ config VIDEO_MEM2MEM_TESTDEV config VIDEO_SAMSUNG_S5P_FIMC tristate "Samsung S5P FIMC (video postprocessor) driver" depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P + depends on BROKEN select VIDEOBUF_DMA_CONTIG select V4L2_MEM2MEM_DEV help diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c index ac832a2..a78e5c9 100644 --- a/drivers/media/video/v4l2-mem2mem.c +++ b/drivers/media/video/v4l2-mem2mem.c @@ -17,7 +17,7 @@ #include #include -#include +#include #include MODULE_DESCRIPTION("Mem to mem device framework for videobuf"); @@ -65,21 +65,16 @@ struct v4l2_m2m_dev { static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) { - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return &m2m_ctx->cap_q_ctx; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (V4L2_TYPE_IS_OUTPUT(type)) return &m2m_ctx->out_q_ctx; - default: - printk(KERN_ERR "Invalid buffer type\n"); - return NULL; - } + else + return &m2m_ctx->cap_q_ctx; } /** - * v4l2_m2m_get_vq() - return videobuf_queue for the given type + * v4l2_m2m_get_vq() - return vb2_queue for the given type */ -struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, +struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) { struct v4l2_m2m_queue_ctx *q_ctx; @@ -95,27 +90,20 @@ EXPORT_SYMBOL(v4l2_m2m_get_vq); /** * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers */ -void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) +void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) { - struct v4l2_m2m_queue_ctx *q_ctx; - struct videobuf_buffer *vb = NULL; + struct v4l2_m2m_buffer *b = NULL; unsigned long flags; - q_ctx = get_queue_ctx(m2m_ctx, type); - if (!q_ctx) - return NULL; - - spin_lock_irqsave(q_ctx->q.irqlock, flags); + spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); if (list_empty(&q_ctx->rdy_queue)) goto end; - vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer, queue); - vb->state = VIDEOBUF_ACTIVE; - + b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list); end: - spin_unlock_irqrestore(q_ctx->q.irqlock, flags); - return vb; + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); + return &b->vb; } EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); @@ -123,26 +111,21 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and * return it */ -void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) +void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) { - struct v4l2_m2m_queue_ctx *q_ctx; - struct videobuf_buffer *vb = NULL; + struct v4l2_m2m_buffer *b = NULL; unsigned long flags; - q_ctx = get_queue_ctx(m2m_ctx, type); - if (!q_ctx) - return NULL; - - spin_lock_irqsave(q_ctx->q.irqlock, flags); + spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); if (!list_empty(&q_ctx->rdy_queue)) { - vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer, - queue); - list_del(&vb->queue); + b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, + list); + list_del(&b->list); q_ctx->num_rdy--; } - spin_unlock_irqrestore(q_ctx->q.irqlock, flags); + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); - return vb; + return &b->vb; } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove); @@ -235,20 +218,20 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) return; } - spin_lock_irqsave(m2m_ctx->out_q_ctx.q.irqlock, flags); + spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) { - spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags); + spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); dprintk("No input buffers available\n"); return; } if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) { - spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags); + spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); dprintk("No output buffers available\n"); return; } - spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags); + spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); if (m2m_dev->m2m_ops->job_ready && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { @@ -291,6 +274,7 @@ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, list_del(&m2m_dev->curr_ctx->queue); m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); + wake_up(&m2m_dev->curr_ctx->finished); m2m_dev->curr_ctx = NULL; spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); @@ -309,10 +293,10 @@ EXPORT_SYMBOL(v4l2_m2m_job_finish); int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_requestbuffers *reqbufs) { - struct videobuf_queue *vq; + struct vb2_queue *vq; vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type); - return videobuf_reqbufs(vq, reqbufs); + return vb2_reqbufs(vq, reqbufs); } EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs); @@ -324,15 +308,22 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs); int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { - struct videobuf_queue *vq; - int ret; + struct vb2_queue *vq; + int ret = 0; + unsigned int i; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - ret = videobuf_querybuf(vq, buf); - - if (buf->memory == V4L2_MEMORY_MMAP - && vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - buf->m.offset += DST_QUEUE_OFF_BASE; + ret = vb2_querybuf(vq, buf); + + /* Adjust MMAP memory offsets for the CAPTURE queue */ + if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { + for (i = 0; i < buf->length; ++i) + buf->m.planes[i].m.mem_offset + += DST_QUEUE_OFF_BASE; + } else { + buf->m.offset += DST_QUEUE_OFF_BASE; + } } return ret; @@ -346,11 +337,11 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { - struct videobuf_queue *vq; + struct vb2_queue *vq; int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - ret = videobuf_qbuf(vq, buf); + ret = vb2_qbuf(vq, buf); if (!ret) v4l2_m2m_try_schedule(m2m_ctx); @@ -365,10 +356,10 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf); int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { - struct videobuf_queue *vq; + struct vb2_queue *vq; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - return videobuf_dqbuf(vq, buf, file->f_flags & O_NONBLOCK); + return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK); } EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf); @@ -378,11 +369,11 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf); int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) { - struct videobuf_queue *vq; + struct vb2_queue *vq; int ret; vq = v4l2_m2m_get_vq(m2m_ctx, type); - ret = videobuf_streamon(vq); + ret = vb2_streamon(vq, type); if (!ret) v4l2_m2m_try_schedule(m2m_ctx); @@ -396,10 +387,10 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamon); int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) { - struct videobuf_queue *vq; + struct vb2_queue *vq; vq = v4l2_m2m_get_vq(m2m_ctx, type); - return videobuf_streamoff(vq); + return vb2_streamoff(vq, type); } EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff); @@ -414,44 +405,53 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff); unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct poll_table_struct *wait) { - struct videobuf_queue *src_q, *dst_q; - struct videobuf_buffer *src_vb = NULL, *dst_vb = NULL; + struct vb2_queue *src_q, *dst_q; + struct vb2_buffer *src_vb = NULL, *dst_vb = NULL; unsigned int rc = 0; + unsigned long flags; src_q = v4l2_m2m_get_src_vq(m2m_ctx); dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); - videobuf_queue_lock(src_q); - videobuf_queue_lock(dst_q); - - if (src_q->streaming && !list_empty(&src_q->stream)) - src_vb = list_first_entry(&src_q->stream, - struct videobuf_buffer, stream); - if (dst_q->streaming && !list_empty(&dst_q->stream)) - dst_vb = list_first_entry(&dst_q->stream, - struct videobuf_buffer, stream); - - if (!src_vb && !dst_vb) { + /* + * There has to be at least one buffer queued on each queued_list, which + * means either in driver already or waiting for driver to claim it + * and start processing. + */ + if ((!src_q->streaming || list_empty(&src_q->queued_list)) + && (!dst_q->streaming || list_empty(&dst_q->queued_list))) { rc = POLLERR; goto end; } - if (src_vb) { - poll_wait(file, &src_vb->done, wait); - if (src_vb->state == VIDEOBUF_DONE - || src_vb->state == VIDEOBUF_ERROR) - rc |= POLLOUT | POLLWRNORM; - } - if (dst_vb) { - poll_wait(file, &dst_vb->done, wait); - if (dst_vb->state == VIDEOBUF_DONE - || dst_vb->state == VIDEOBUF_ERROR) - rc |= POLLIN | POLLRDNORM; - } + if (m2m_ctx->m2m_dev->m2m_ops->unlock) + m2m_ctx->m2m_dev->m2m_ops->unlock(m2m_ctx->priv); + + poll_wait(file, &src_q->done_wq, wait); + poll_wait(file, &dst_q->done_wq, wait); + + if (m2m_ctx->m2m_dev->m2m_ops->lock) + m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv); + + spin_lock_irqsave(&src_q->done_lock, flags); + if (!list_empty(&src_q->done_list)) + src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer, + done_entry); + if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE + || src_vb->state == VB2_BUF_STATE_ERROR)) + rc |= POLLOUT | POLLWRNORM; + spin_unlock_irqrestore(&src_q->done_lock, flags); + + spin_lock_irqsave(&dst_q->done_lock, flags); + if (!list_empty(&dst_q->done_list)) + dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer, + done_entry); + if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE + || dst_vb->state == VB2_BUF_STATE_ERROR)) + rc |= POLLIN | POLLRDNORM; + spin_unlock_irqrestore(&dst_q->done_lock, flags); end: - videobuf_queue_unlock(dst_q); - videobuf_queue_unlock(src_q); return rc; } EXPORT_SYMBOL_GPL(v4l2_m2m_poll); @@ -470,7 +470,7 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct vm_area_struct *vma) { unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - struct videobuf_queue *vq; + struct vb2_queue *vq; if (offset < DST_QUEUE_OFF_BASE) { vq = v4l2_m2m_get_src_vq(m2m_ctx); @@ -479,7 +479,7 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); } - return videobuf_mmap_mapper(vq, vma); + return vb2_mmap(vq, vma); } EXPORT_SYMBOL(v4l2_m2m_mmap); @@ -531,36 +531,41 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_release); * * Usually called from driver's open() function. */ -struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev *m2m_dev, - void (*vq_init)(void *priv, struct videobuf_queue *, - enum v4l2_buf_type)) +struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, + void *drv_priv, + int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)) { struct v4l2_m2m_ctx *m2m_ctx; struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx; - - if (!vq_init) - return ERR_PTR(-EINVAL); + int ret; m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL); if (!m2m_ctx) return ERR_PTR(-ENOMEM); - m2m_ctx->priv = priv; + m2m_ctx->priv = drv_priv; m2m_ctx->m2m_dev = m2m_dev; + init_waitqueue_head(&m2m_ctx->finished); - out_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - cap_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + out_q_ctx = &m2m_ctx->out_q_ctx; + cap_q_ctx = &m2m_ctx->cap_q_ctx; INIT_LIST_HEAD(&out_q_ctx->rdy_queue); INIT_LIST_HEAD(&cap_q_ctx->rdy_queue); + spin_lock_init(&out_q_ctx->rdy_spinlock); + spin_lock_init(&cap_q_ctx->rdy_spinlock); INIT_LIST_HEAD(&m2m_ctx->queue); - vq_init(priv, &out_q_ctx->q, V4L2_BUF_TYPE_VIDEO_OUTPUT); - vq_init(priv, &cap_q_ctx->q, V4L2_BUF_TYPE_VIDEO_CAPTURE); - out_q_ctx->q.priv_data = cap_q_ctx->q.priv_data = priv; + ret = queue_init(drv_priv, &out_q_ctx->q, &cap_q_ctx->q); + + if (ret) + goto err; return m2m_ctx; +err: + kfree(m2m_ctx); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init); @@ -572,7 +577,6 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init); void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) { struct v4l2_m2m_dev *m2m_dev; - struct videobuf_buffer *vb; unsigned long flags; m2m_dev = m2m_ctx->m2m_dev; @@ -582,10 +586,7 @@ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); - vb = v4l2_m2m_next_dst_buf(m2m_ctx); - BUG_ON(NULL == vb); - wait_event(vb->done, vb->state != VIDEOBUF_ACTIVE - && vb->state != VIDEOBUF_QUEUED); + wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING)); } else if (m2m_ctx->job_flags & TRANS_QUEUED) { list_del(&m2m_ctx->queue); m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); @@ -597,11 +598,8 @@ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); } - videobuf_stop(&m2m_ctx->cap_q_ctx.q); - videobuf_stop(&m2m_ctx->out_q_ctx.q); - - videobuf_mmap_free(&m2m_ctx->cap_q_ctx.q); - videobuf_mmap_free(&m2m_ctx->out_q_ctx.q); + vb2_queue_release(&m2m_ctx->cap_q_ctx.q); + vb2_queue_release(&m2m_ctx->out_q_ctx.q); kfree(m2m_ctx); } @@ -611,23 +609,21 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release); * v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list. * * Call from buf_queue(), videobuf_queue_ops callback. - * - * Locking: Caller holds q->irqlock (taken by videobuf before calling buf_queue - * callback in the driver). */ -void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue *vq, - struct videobuf_buffer *vb) +void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb) { + struct v4l2_m2m_buffer *b = container_of(vb, struct v4l2_m2m_buffer, vb); struct v4l2_m2m_queue_ctx *q_ctx; + unsigned long flags; - q_ctx = get_queue_ctx(m2m_ctx, vq->type); + q_ctx = get_queue_ctx(m2m_ctx, vb->vb2_queue->type); if (!q_ctx) return; - list_add_tail(&vb->queue, &q_ctx->rdy_queue); + spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); + list_add_tail(&b->list, &q_ctx->rdy_queue); q_ctx->num_rdy++; - - vb->state = VIDEOBUF_QUEUED; + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 8d149f1..bf5eaaf 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -17,7 +17,7 @@ #ifndef _MEDIA_V4L2_MEM2MEM_H #define _MEDIA_V4L2_MEM2MEM_H -#include +#include /** * struct v4l2_m2m_ops - mem-to-mem device driver callbacks @@ -45,17 +45,20 @@ struct v4l2_m2m_ops { void (*device_run)(void *priv); int (*job_ready)(void *priv); void (*job_abort)(void *priv); + void (*lock)(void *priv); + void (*unlock)(void *priv); }; struct v4l2_m2m_dev; struct v4l2_m2m_queue_ctx { /* private: internal use only */ - struct videobuf_queue q; + struct vb2_queue q; /* Queue for buffers ready to be processed as soon as this * instance receives access to the device */ struct list_head rdy_queue; + spinlock_t rdy_spinlock; u8 num_rdy; }; @@ -72,19 +75,31 @@ struct v4l2_m2m_ctx { /* For device job queue */ struct list_head queue; unsigned long job_flags; + wait_queue_head_t finished; /* Instance private data */ void *priv; }; +struct v4l2_m2m_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev); -struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, +struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type); void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, struct v4l2_m2m_ctx *m2m_ctx); +static inline void +v4l2_m2m_buf_done(struct vb2_buffer *buf, enum vb2_buffer_state state) +{ + vb2_buffer_done(buf, state); +} + int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_requestbuffers *reqbufs); @@ -110,13 +125,13 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops); void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev); -struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev *m2m_dev, - void (*vq_init)(void *priv, struct videobuf_queue *, - enum v4l2_buf_type)); +struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, + void *drv_priv, + int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)); + void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx); -void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue *vq, - struct videobuf_buffer *vb); +void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb); /** * v4l2_m2m_num_src_bufs_ready() - return the number of source buffers ready for @@ -138,7 +153,7 @@ unsigned int v4l2_m2m_num_dst_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx) return m2m_ctx->out_q_ctx.num_rdy; } -void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type); +void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_next_src_buf() - return next source buffer from the list of ready @@ -146,7 +161,7 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type); */ static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) { - return v4l2_m2m_next_buf(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + return v4l2_m2m_next_buf(&m2m_ctx->out_q_ctx); } /** @@ -155,29 +170,28 @@ static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) */ static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) { - return v4l2_m2m_next_buf(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + return v4l2_m2m_next_buf(&m2m_ctx->cap_q_ctx); } /** - * v4l2_m2m_get_src_vq() - return videobuf_queue for source buffers + * v4l2_m2m_get_src_vq() - return vb2_queue for source buffers */ static inline -struct videobuf_queue *v4l2_m2m_get_src_vq(struct v4l2_m2m_ctx *m2m_ctx) +struct vb2_queue *v4l2_m2m_get_src_vq(struct v4l2_m2m_ctx *m2m_ctx) { - return v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + return &m2m_ctx->out_q_ctx.q; } /** - * v4l2_m2m_get_dst_vq() - return videobuf_queue for destination buffers + * v4l2_m2m_get_dst_vq() - return vb2_queue for destination buffers */ static inline -struct videobuf_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx) +struct vb2_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx) { - return v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + return &m2m_ctx->cap_q_ctx.q; } -void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx, - enum v4l2_buf_type type); +void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_src_buf_remove() - take off a source buffer from the list of ready @@ -185,7 +199,7 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx, */ static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) { - return v4l2_m2m_buf_remove(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + return v4l2_m2m_buf_remove(&m2m_ctx->out_q_ctx); } /** @@ -194,7 +208,7 @@ static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) */ static inline void *v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) { - return v4l2_m2m_buf_remove(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + return v4l2_m2m_buf_remove(&m2m_ctx->cap_q_ctx); } #endif /* _MEDIA_V4L2_MEM2MEM_H */ -- cgit v0.10.2 From d80ee38cd845baadef175893b99df24e7a03ec40 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 12 Jan 2011 06:50:55 -0300 Subject: [media] v4l: mem2mem: port m2m_testdev to vb2 This patch ports mem2mem test device to videobuf2 framework. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 3749c89..1c0f080 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -989,8 +989,7 @@ if V4L_MEM2MEM_DRIVERS config VIDEO_MEM2MEM_TESTDEV tristate "Virtual test device for mem2mem framework" depends on VIDEO_DEV && VIDEO_V4L2 - depends on BROKEN - select VIDEOBUF_VMALLOC + select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV default n ---help--- diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index e7e7178..e1f96ea 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev" @@ -201,11 +201,6 @@ struct m2mtest_ctx { struct v4l2_m2m_ctx *m2m_ctx; }; -struct m2mtest_buffer { - /* vb must be first! */ - struct videobuf_buffer vb; -}; - static struct v4l2_queryctrl *get_ctrl(int id) { int i; @@ -219,37 +214,41 @@ static struct v4l2_queryctrl *get_ctrl(int id) } static int device_process(struct m2mtest_ctx *ctx, - struct m2mtest_buffer *in_buf, - struct m2mtest_buffer *out_buf) + struct vb2_buffer *in_vb, + struct vb2_buffer *out_vb) { struct m2mtest_dev *dev = ctx->dev; + struct m2mtest_q_data *q_data; u8 *p_in, *p_out; int x, y, t, w; int tile_w, bytes_left; - struct videobuf_queue *src_q; - struct videobuf_queue *dst_q; + int width, height, bytesperline; - src_q = v4l2_m2m_get_src_vq(ctx->m2m_ctx); - dst_q = v4l2_m2m_get_dst_vq(ctx->m2m_ctx); - p_in = videobuf_queue_to_vaddr(src_q, &in_buf->vb); - p_out = videobuf_queue_to_vaddr(dst_q, &out_buf->vb); + q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT); + + width = q_data->width; + height = q_data->height; + bytesperline = (q_data->width * q_data->fmt->depth) >> 3; + + p_in = vb2_plane_vaddr(in_vb, 0); + p_out = vb2_plane_vaddr(out_vb, 0); if (!p_in || !p_out) { v4l2_err(&dev->v4l2_dev, "Acquiring kernel pointers to buffers failed\n"); return -EFAULT; } - if (in_buf->vb.size > out_buf->vb.size) { + if (vb2_plane_size(in_vb, 0) > vb2_plane_size(out_vb, 0)) { v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n"); return -EINVAL; } - tile_w = (in_buf->vb.width * (q_data[V4L2_M2M_DST].fmt->depth >> 3)) + tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3)) / MEM2MEM_NUM_TILES; - bytes_left = in_buf->vb.bytesperline - tile_w * MEM2MEM_NUM_TILES; + bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; w = 0; - for (y = 0; y < in_buf->vb.height; ++y) { + for (y = 0; y < height; ++y) { for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { if (w & 0x1) { for (x = 0; x < tile_w; ++x) @@ -301,6 +300,21 @@ static void job_abort(void *priv) ctx->aborting = 1; } +static void m2mtest_lock(void *priv) +{ + struct m2mtest_ctx *ctx = priv; + struct m2mtest_dev *dev = ctx->dev; + mutex_lock(&dev->dev_mutex); +} + +static void m2mtest_unlock(void *priv) +{ + struct m2mtest_ctx *ctx = priv; + struct m2mtest_dev *dev = ctx->dev; + mutex_unlock(&dev->dev_mutex); +} + + /* device_run() - prepares and starts the device * * This simulates all the immediate preparations required before starting @@ -311,7 +325,7 @@ static void device_run(void *priv) { struct m2mtest_ctx *ctx = priv; struct m2mtest_dev *dev = ctx->dev; - struct m2mtest_buffer *src_buf, *dst_buf; + struct vb2_buffer *src_buf, *dst_buf; src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); @@ -322,12 +336,11 @@ static void device_run(void *priv) schedule_irq(dev, ctx->transtime); } - static void device_isr(unsigned long priv) { struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv; struct m2mtest_ctx *curr_ctx; - struct m2mtest_buffer *src_buf, *dst_buf; + struct vb2_buffer *src_vb, *dst_vb; unsigned long flags; curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev); @@ -338,31 +351,26 @@ static void device_isr(unsigned long priv) return; } - src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + curr_ctx->num_processed++; + spin_lock_irqsave(&m2mtest_dev->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(&m2mtest_dev->irqlock, flags); + if (curr_ctx->num_processed == curr_ctx->translen || curr_ctx->aborting) { dprintk(curr_ctx->dev, "Finishing transaction\n"); curr_ctx->num_processed = 0; - spin_lock_irqsave(&m2mtest_dev->irqlock, flags); - src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE; - wake_up(&src_buf->vb.done); - wake_up(&dst_buf->vb.done); - spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags); v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx); } else { - spin_lock_irqsave(&m2mtest_dev->irqlock, flags); - src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE; - wake_up(&src_buf->vb.done); - wake_up(&dst_buf->vb.done); - spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags); device_run(curr_ctx); } } - /* * video ioctls */ @@ -423,7 +431,7 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) { - struct videobuf_queue *vq; + struct vb2_queue *vq; struct m2mtest_q_data *q_data; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); @@ -434,7 +442,7 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) f->fmt.pix.width = q_data->width; f->fmt.pix.height = q_data->height; - f->fmt.pix.field = vq->field; + f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.pixelformat = q_data->fmt->fourcc; f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3; f->fmt.pix.sizeimage = q_data->sizeimage; @@ -523,7 +531,7 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) { struct m2mtest_q_data *q_data; - struct videobuf_queue *vq; + struct vb2_queue *vq; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (!vq) @@ -533,7 +541,7 @@ static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) if (!q_data) return -EINVAL; - if (videobuf_queue_is_busy(vq)) { + if (vb2_is_busy(vq)) { v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); return -EBUSY; } @@ -543,7 +551,6 @@ static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) q_data->height = f->fmt.pix.height; q_data->sizeimage = q_data->width * q_data->height * q_data->fmt->depth >> 3; - vq->field = f->fmt.pix.field; dprintk(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d\n", @@ -733,120 +740,94 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = { * Queue operations */ -static void m2mtest_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct m2mtest_ctx *ctx = vq->priv_data; - - dprintk(ctx->dev, "type: %d, index: %d, state: %d\n", - vq->type, vb->i, vb->state); - - videobuf_vmalloc_free(vb); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static int m2mtest_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static int m2mtest_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned long sizes[], + void *alloc_ctxs[]) { - struct m2mtest_ctx *ctx = vq->priv_data; + struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq); struct m2mtest_q_data *q_data; + unsigned int size, count = *nbuffers; q_data = get_q_data(vq->type); - *size = q_data->width * q_data->height * q_data->fmt->depth >> 3; - dprintk(ctx->dev, "size:%d, w/h %d/%d, depth: %d\n", - *size, q_data->width, q_data->height, q_data->fmt->depth); + size = q_data->width * q_data->height * q_data->fmt->depth >> 3; - if (0 == *count) - *count = MEM2MEM_DEF_NUM_BUFS; + while (size * count > MEM2MEM_VID_MEM_LIMIT) + (count)--; - while (*size * *count > MEM2MEM_VID_MEM_LIMIT) - (*count)--; + *nplanes = 1; + *nbuffers = count; + sizes[0] = size; - v4l2_info(&ctx->dev->v4l2_dev, - "%d buffers of size %d set up.\n", *count, *size); + /* + * videobuf2-vmalloc allocator is context-less so no need to set + * alloc_ctxs array. + */ + + dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); return 0; } -static int m2mtest_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) +static int m2mtest_buf_prepare(struct vb2_buffer *vb) { - struct m2mtest_ctx *ctx = vq->priv_data; + struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct m2mtest_q_data *q_data; - int ret; - dprintk(ctx->dev, "type: %d, index: %d, state: %d\n", - vq->type, vb->i, vb->state); + dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); - q_data = get_q_data(vq->type); + q_data = get_q_data(vb->vb2_queue->type); - if (vb->baddr) { - /* User-provided buffer */ - if (vb->bsize < q_data->sizeimage) { - /* Buffer too small to fit a frame */ - v4l2_err(&ctx->dev->v4l2_dev, - "User-provided buffer too small\n"); - return -EINVAL; - } - } else if (vb->state != VIDEOBUF_NEEDS_INIT - && vb->bsize < q_data->sizeimage) { - /* We provide the buffer, but it's already been initialized - * and is too small */ + 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; } - vb->width = q_data->width; - vb->height = q_data->height; - vb->bytesperline = (q_data->width * q_data->fmt->depth) >> 3; - vb->size = q_data->sizeimage; - vb->field = field; - - if (VIDEOBUF_NEEDS_INIT == vb->state) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) { - v4l2_err(&ctx->dev->v4l2_dev, - "Iolock failed\n"); - goto fail; - } - } - - vb->state = VIDEOBUF_PREPARED; + vb2_set_plane_payload(vb, 0, q_data->sizeimage); return 0; -fail: - m2mtest_buf_release(vq, vb); - return ret; } -static void m2mtest_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void m2mtest_buf_queue(struct vb2_buffer *vb) { - struct m2mtest_ctx *ctx = vq->priv_data; - - v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb); + struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); } -static struct videobuf_queue_ops m2mtest_qops = { - .buf_setup = m2mtest_buf_setup, - .buf_prepare = m2mtest_buf_prepare, - .buf_queue = m2mtest_buf_queue, - .buf_release = m2mtest_buf_release, +static struct vb2_ops m2mtest_qops = { + .queue_setup = m2mtest_queue_setup, + .buf_prepare = m2mtest_buf_prepare, + .buf_queue = m2mtest_buf_queue, }; -static void queue_init(void *priv, struct videobuf_queue *vq, - enum v4l2_buf_type type) +static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { struct m2mtest_ctx *ctx = priv; - struct m2mtest_dev *dev = ctx->dev; + int ret; - videobuf_queue_vmalloc_init(vq, &m2mtest_qops, dev->v4l2_dev.dev, - &dev->irqlock, type, V4L2_FIELD_NONE, - sizeof(struct m2mtest_buffer), priv, - &dev->dev_mutex); -} + 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 = &m2mtest_qops; + src_vq->mem_ops = &vb2_vmalloc_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 = &m2mtest_qops; + dst_vq->mem_ops = &vb2_vmalloc_memops; + + return vb2_queue_init(dst_vq); +} /* * File operations @@ -866,7 +847,8 @@ static int m2mtest_open(struct file *file) ctx->transtime = MEM2MEM_DEF_TRANSTIME; ctx->num_processed = 0; - ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, dev->m2m_dev, queue_init); + ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + if (IS_ERR(ctx->m2m_ctx)) { int ret = PTR_ERR(ctx->m2m_ctx); @@ -932,6 +914,8 @@ static struct v4l2_m2m_ops m2m_ops = { .device_run = device_run, .job_ready = job_ready, .job_abort = job_abort, + .lock = m2mtest_lock, + .unlock = m2mtest_unlock, }; static int m2mtest_probe(struct platform_device *pdev) @@ -990,6 +974,7 @@ static int m2mtest_probe(struct platform_device *pdev) return 0; + v4l2_m2m_release(dev->m2m_dev); err_m2m: video_unregister_device(dev->vfd); rel_vdev: -- cgit v0.10.2 From 4a3c9b4f0df43207eb0b4d0da9cb51e185506bd5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 28 Dec 2010 12:32:39 -0300 Subject: [media] v4l: Add multiplanar format fourccs for s5p-fimc driver Add definitions for format with color planes non-contiguous in physical memory. These formats apply only if the V4L2 multiplane extension is used. V4L2_PIX_FMT_NV12M - 2-plane Y/CbCr V4L2_PIX_FMT_NV12MT - 2-plane Y/CbCr tiled (64x32 pixel macroblocks) V4L2_PIX_FMT_YUV420M - 3-plane Y/Cb/Cr Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index bb0a3ae..5122b26 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -339,6 +339,13 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_NV16 v4l2_fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */ #define V4L2_PIX_FMT_NV61 v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */ +/* two non contiguous planes - one Y, one Cr + Cb interleaved */ +#define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */ +#define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 macroblocks */ + +/* three non contiguous planes - Y, Cb, Cr */ +#define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12 YUV420 planar */ + /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */ #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */ -- cgit v0.10.2 From 269da4027c9a3466150308a9fe5f2a3a58336cd8 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 9 Jan 2011 09:01:10 -0300 Subject: [media] v4l: Add DocBook documentation for YU12M, NV12M image formats Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 74923d7..d2f99e5 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -237,6 +237,7 @@ + @@ -251,6 +252,7 @@ + @@ -337,6 +339,7 @@ + @@ -351,6 +354,7 @@ + diff --git a/Documentation/DocBook/v4l/pixfmt-nv12m.xml b/Documentation/DocBook/v4l/pixfmt-nv12m.xml new file mode 100644 index 0000000..c9e166d --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-nv12m.xml @@ -0,0 +1,154 @@ + + + V4L2_PIX_FMT_NV12M ('NV12M') + &manvol; + + + V4L2_PIX_FMT_NV12M + Variation of V4L2_PIX_FMT_NV12 with planes + non contiguous in memory. + + + Description + + This is a multi-planar, two-plane version of the YUV 4:2:0 format. +The three components are separated into two sub-images or planes. +V4L2_PIX_FMT_NV12M differs from V4L2_PIX_FMT_NV12 + in that the two planes are non-contiguous in memory, i.e. the chroma +plane do not necessarily immediately follows the luma plane. +The luminance data occupies the first plane. The Y plane has one byte per pixel. +In the second plane there is a chrominance data with alternating chroma samples. +The CbCr plane is the same width, in bytes, as the Y plane (and of the image), +but is half as tall in pixels. Each CbCr pair belongs to four pixels. For example, +Cb0/Cr0 belongs to +Y'00, Y'01, +Y'10, Y'11. + + V4L2_PIX_FMT_NV12M is intended to be +used only in drivers and applications that support the multi-planar API, +described in . + + If the Y plane has pad bytes after each row, then the +CbCr plane has as many pad bytes after its rows. + + + <constant>V4L2_PIX_FMT_NV12M</constant> 4 × 4 pixel image + + + Byte Order. + Each cell is one byte. + + + + + + start0 + 0: + Y'00 + Y'01 + Y'02 + Y'03 + + + start0 + 4: + Y'10 + Y'11 + Y'12 + Y'13 + + + start0 + 8: + Y'20 + Y'21 + Y'22 + Y'23 + + + start0 + 12: + Y'30 + Y'31 + Y'32 + Y'33 + + + + + + start1 + 0: + Cb00 + Cr00 + Cb01 + Cr01 + + + start1 + 4: + Cb10 + Cr10 + Cb11 + Cr11 + + + + + + + + + Color Sample Location. + + + + + + + 01 + 23 + + + 0 + YY + YY + + + + C + C + + + 1 + YY + YY + + + + + + 2 + YY + YY + + + + C + C + + + 3 + YY + YY + + + + + + + + + + + diff --git a/Documentation/DocBook/v4l/pixfmt-yuv420m.xml b/Documentation/DocBook/v4l/pixfmt-yuv420m.xml new file mode 100644 index 0000000..f5d8f57 --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-yuv420m.xml @@ -0,0 +1,162 @@ + + + V4L2_PIX_FMT_YUV420M ('YU12M') + &manvol; + + + V4L2_PIX_FMT_YUV420M + Variation of V4L2_PIX_FMT_YUV420 + with planes non contiguous in memory. + + + + Description + + This is a multi-planar format, as opposed to a packed format. +The three components are separated into three sub- images or planes. + +The Y plane is first. The Y plane has one byte per pixel. The Cb data +constitutes the second plane which is half the width and half +the height of the Y plane (and of the image). Each Cb belongs to four +pixels, a two-by-two square of the image. For example, +Cb0 belongs to Y'00, +Y'01, Y'10, and +Y'11. The Cr data, just like the Cb plane, is +in the third plane. + + If the Y plane has pad bytes after each row, then the Cb +and Cr planes have half as many pad bytes after their rows. In other +words, two Cx rows (including padding) is exactly as long as one Y row +(including padding). + + V4L2_PIX_FMT_NV12M is intended to be +used only in drivers and applications that support the multi-planar API, +described in . + + + <constant>V4L2_PIX_FMT_YVU420M</constant> 4 × 4 +pixel image + + + Byte Order. + Each cell is one byte. + + + + + + start0 + 0: + Y'00 + Y'01 + Y'02 + Y'03 + + + start0 + 4: + Y'10 + Y'11 + Y'12 + Y'13 + + + start0 + 8: + Y'20 + Y'21 + Y'22 + Y'23 + + + start0 + 12: + Y'30 + Y'31 + Y'32 + Y'33 + + + + start1 + 0: + Cb00 + Cb01 + + + start1 + 2: + Cb10 + Cb11 + + + + start2 + 0: + Cr00 + Cr01 + + + start2 + 2: + Cr10 + Cr11 + + + + + + + + + Color Sample Location. + + + + + + + 01 + 23 + + + 0 + YY + YY + + + + C + C + + + 1 + YY + YY + + + + + + 2 + YY + YY + + + + C + C + + + 3 + YY + YY + + + + + + + + + + + diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index 28f9c70..f8436dc 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -703,10 +703,12 @@ information. &sub-vyuy; &sub-y41p; &sub-yuv420; + &sub-yuv420m; &sub-yuv410; &sub-yuv422p; &sub-yuv411p; &sub-nv12; + &sub-nv12m; &sub-nv16;
-- cgit v0.10.2 From 2dab38e2272e9b30540e2782ef7aa8bc45848144 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 1 Dec 2010 10:14:59 -0300 Subject: [media] s5p-fimc: Porting to videobuf 2 Porting to videobuf 2 and minor cleanup. Separate videobuf_queue_ops are are created for m2m and capture video nodes. Signed-off-by: Sylwester Nawrocki Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 1c0f080..9f20fc5 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -999,8 +999,7 @@ config VIDEO_MEM2MEM_TESTDEV config VIDEO_SAMSUNG_S5P_FIMC tristate "Samsung S5P FIMC (video postprocessor) driver" depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P - depends on BROKEN - select VIDEOBUF_DMA_CONTIG + select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help This is a v4l2 driver for the S5P camera interface diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 2f50080..24b77b7 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -27,8 +27,8 @@ #include #include #include -#include -#include +#include +#include #include "fimc-core.h" @@ -141,7 +141,7 @@ static int fimc_isp_subdev_init(struct fimc_dev *fimc, int index) * Locking: The caller holds fimc->slock spinlock. */ int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, - struct fimc_vid_buffer *fimc_vb) + struct fimc_vid_buffer *fimc_vb) { struct fimc_vid_cap *cap = &fimc->vid_cap; struct fimc_ctx *ctx = cap->ctx; @@ -149,7 +149,7 @@ int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, BUG_ON(!fimc || !fimc_vb); - ret = fimc_prepare_addr(ctx, fimc_vb, &ctx->d_frame, + ret = fimc_prepare_addr(ctx, &fimc_vb->vb, &ctx->d_frame, &fimc_vb->paddr); if (ret) return ret; @@ -174,7 +174,7 @@ static int fimc_stop_capture(struct fimc_dev *fimc) { unsigned long flags; struct fimc_vid_cap *cap; - int ret; + struct fimc_vid_buffer *buf; cap = &fimc->vid_cap; @@ -199,12 +199,224 @@ static int fimc_stop_capture(struct fimc_dev *fimc) 1 << ST_CAPT_STREAM); fimc->vid_cap.active_buf_cnt = 0; + + /* Release buffers that were enqueued in the driver by videobuf2. */ + while (!list_empty(&cap->pending_buf_q)) { + buf = pending_queue_pop(cap); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + + while (!list_empty(&cap->active_buf_q)) { + buf = active_queue_pop(cap); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&fimc->slock, flags); dbg("state: 0x%lx", fimc->state); return 0; } +static int start_streaming(struct vb2_queue *q) +{ + struct fimc_ctx *ctx = q->drv_priv; + struct fimc_dev *fimc = ctx->fimc_dev; + struct s3c_fimc_isp_info *isp_info; + int ret; + + ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) + return ret; + + ret = fimc_prepare_config(ctx, ctx->state); + if (ret) + return ret; + + isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index]; + fimc_hw_set_camera_type(fimc, isp_info); + fimc_hw_set_camera_source(fimc, isp_info); + fimc_hw_set_camera_offset(fimc, &ctx->s_frame); + + if (ctx->state & FIMC_PARAMS) { + ret = fimc_set_scaler_info(ctx); + if (ret) { + err("Scaler setup error"); + return ret; + } + fimc_hw_set_input_path(ctx); + fimc_hw_set_scaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_hw_set_effect(ctx); + } + + fimc_hw_set_output_path(ctx); + fimc_hw_set_out_dma(ctx); + + INIT_LIST_HEAD(&fimc->vid_cap.pending_buf_q); + INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); + fimc->vid_cap.active_buf_cnt = 0; + fimc->vid_cap.frame_count = 0; + fimc->vid_cap.buf_index = fimc_hw_get_frame_index(fimc); + + set_bit(ST_CAPT_PEND, &fimc->state); + + return 0; +} + +static int stop_streaming(struct vb2_queue *q) +{ + struct fimc_ctx *ctx = q->drv_priv; + struct fimc_dev *fimc = ctx->fimc_dev; + unsigned long flags; + + spin_lock_irqsave(&fimc->slock, flags); + if (!fimc_capture_running(fimc) && !fimc_capture_pending(fimc)) { + spin_unlock_irqrestore(&fimc->slock, flags); + return -EINVAL; + } + spin_unlock_irqrestore(&fimc->slock, flags); + + return fimc_stop_capture(fimc); +} + +static unsigned int get_plane_size(struct fimc_frame *frame, unsigned int plane) +{ + unsigned long size = 0; + + if (!frame || plane > frame->fmt->buff_cnt - 1) + return 0; + + if (1 == frame->fmt->planes_cnt) { + size = (frame->width * frame->height * frame->fmt->depth) >> 3; + } else if (frame->fmt->planes_cnt <= 3) { + switch (plane) { + case 0: + size = frame->width * frame->height; + break; + case 1: + case 2: + if (S5P_FIMC_YCBCR420 == frame->fmt->color + && 2 != frame->fmt->planes_cnt) + size = (frame->width * frame->height) >> 2; + else /* 422 */ + size = (frame->width * frame->height) >> 1; + break; + } + } else { + size = 0; + } + + return size; +} + +static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, + unsigned int *num_planes, unsigned long sizes[], + void *allocators[]) +{ + struct fimc_ctx *ctx = vq->drv_priv; + struct fimc_fmt *fmt = fmt = ctx->d_frame.fmt; + struct fimc_frame *frame; + + if (!fmt) + return -EINVAL; + + *num_planes = fmt->buff_cnt; + + dbg("%s, buffer count=%d, plane count=%d", + __func__, *num_buffers, *num_planes); + + frame = ctx_get_frame(ctx, vq->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + sizes[0] = get_plane_size(frame, 0); + allocators[0] = ctx->fimc_dev->alloc_ctx; + + return -EINVAL; +} + +static int buffer_init(struct vb2_buffer *vb) +{ + /* TODO: */ + return 0; +} + +static int buffer_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct fimc_ctx *ctx = vq->drv_priv; + struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev; + struct fimc_frame *frame; + unsigned long size; + int i; + + frame = ctx_get_frame(ctx, vq->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + for (i = 0; i < frame->fmt->buff_cnt; i++) { + size = get_plane_size(frame, i); + + if (vb2_plane_size(vb, i) < size) { + v4l2_err(v4l2_dev, "User buffer too small (%ld < %ld)\n", + vb2_plane_size(vb, i), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, size); + } + + return 0; +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_vid_buffer *buf + = container_of(vb, struct fimc_vid_buffer, vb); + struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + unsigned long flags; + + spin_lock_irqsave(&fimc->slock, flags); + fimc_vid_cap_buf_queue(fimc, buf); + + dbg("active_buf_cnt: %d", fimc->vid_cap.active_buf_cnt); + + if (vid_cap->active_buf_cnt >= vid_cap->reqbufs_count || + vid_cap->active_buf_cnt >= FIMC_MAX_OUT_BUFS) { + if (!test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) { + fimc_activate_capture(ctx); + dbg(""); + } + } + spin_unlock_irqrestore(&fimc->slock, flags); +} + +static void fimc_lock(struct vb2_queue *vq) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + mutex_lock(&ctx->fimc_dev->lock); +} + +static void fimc_unlock(struct vb2_queue *vq) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + mutex_unlock(&ctx->fimc_dev->lock); +} + +static struct vb2_ops fimc_capture_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_init = buffer_init, + .wait_prepare = fimc_unlock, + .wait_finish = fimc_lock, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, +}; + static int fimc_capture_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); @@ -244,11 +456,10 @@ static int fimc_capture_close(struct file *file) if (--fimc->vid_cap.refcnt == 0) { fimc_stop_capture(fimc); - - videobuf_stop(&fimc->vid_cap.vbq); - videobuf_mmap_free(&fimc->vid_cap.vbq); + vb2_queue_release(&fimc->vid_cap.vbq); v4l2_err(&fimc->vid_cap.v4l2_dev, "releasing ISP\n"); + v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0); fimc_subdev_unregister(fimc); } @@ -262,13 +473,12 @@ static unsigned int fimc_capture_poll(struct file *file, { struct fimc_ctx *ctx = file->private_data; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_vid_cap *cap = &fimc->vid_cap; int ret; if (mutex_lock_interruptible(&fimc->lock)) return POLLERR; - ret = videobuf_poll_stream(file, &cap->vbq, wait); + ret = vb2_poll(&fimc->vid_cap.vbq, file, wait); mutex_unlock(&fimc->lock); return ret; @@ -278,13 +488,12 @@ static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) { struct fimc_ctx *ctx = file->private_data; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_vid_cap *cap = &fimc->vid_cap; int ret; if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; - ret = videobuf_mmap_mapper(&cap->vbq, vma); + ret = vb2_mmap(&fimc->vid_cap.vbq, vma); mutex_unlock(&fimc->lock); return ret; @@ -470,7 +679,7 @@ static int fimc_cap_g_input(struct file *file, void *priv, } static int fimc_cap_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) + enum v4l2_buf_type type) { struct s3c_fimc_isp_info *isp_info; struct fimc_ctx *ctx = priv; @@ -525,7 +734,7 @@ static int fimc_cap_streamon(struct file *file, void *priv, fimc->vid_cap.buf_index = fimc_hw_get_frame_index(fimc); set_bit(ST_CAPT_PEND, &fimc->state); - ret = videobuf_streamon(&fimc->vid_cap.vbq); + ret = vb2_streamon(&fimc->vid_cap.vbq, type); s_unlock: mutex_unlock(&fimc->lock); @@ -533,7 +742,7 @@ s_unlock: } static int fimc_cap_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) + enum v4l2_buf_type type) { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; @@ -553,7 +762,8 @@ static int fimc_cap_streamoff(struct file *file, void *priv, return -ERESTARTSYS; fimc_stop_capture(fimc); - ret = videobuf_streamoff(&cap->vbq); + ret = vb2_streamoff(&cap->vbq, type); + mutex_unlock(&fimc->lock); return ret; } @@ -572,7 +782,7 @@ static int fimc_cap_reqbufs(struct file *file, void *priv, if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; - ret = videobuf_reqbufs(&cap->vbq, reqbufs); + ret = vb2_reqbufs(&cap->vbq, reqbufs); if (!ret) cap->reqbufs_count = reqbufs->count; @@ -589,7 +799,7 @@ static int fimc_cap_querybuf(struct file *file, void *priv, if (fimc_capture_active(ctx->fimc_dev)) return -EBUSY; - return videobuf_querybuf(&cap->vbq, buf); + return vb2_querybuf(&cap->vbq, buf); } static int fimc_cap_qbuf(struct file *file, void *priv, @@ -603,7 +813,7 @@ static int fimc_cap_qbuf(struct file *file, void *priv, if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; - ret = videobuf_qbuf(&cap->vbq, buf); + ret = vb2_qbuf(&cap->vbq, buf); mutex_unlock(&fimc->lock); return ret; @@ -618,7 +828,7 @@ static int fimc_cap_dqbuf(struct file *file, void *priv, if (mutex_lock_interruptible(&ctx->fimc_dev->lock)) return -ERESTARTSYS; - ret = videobuf_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf, + ret = vb2_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK); mutex_unlock(&ctx->fimc_dev->lock); @@ -777,6 +987,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc) struct fimc_vid_cap *vid_cap; struct fimc_ctx *ctx; struct v4l2_format f; + struct vb2_queue *q; int ret; ctx = kzalloc(sizeof *ctx, GFP_KERNEL); @@ -827,10 +1038,16 @@ int fimc_register_capture_device(struct fimc_dev *fimc) spin_lock_init(&ctx->slock); vid_cap->ctx = ctx; - videobuf_queue_dma_contig_init(&vid_cap->vbq, &fimc_qops, - vid_cap->v4l2_dev.dev, &fimc->irqlock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct fimc_vid_buffer), (void *)ctx, NULL); + q = &fimc->vid_cap.vbq; + memset(q, 0, sizeof(*q)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = fimc->vid_cap.ctx; + q->ops = &fimc_capture_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct fimc_vid_buffer); + + vb2_queue_init(q); ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); if (ret) { diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 817aa66..65d25b3 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -25,7 +25,8 @@ #include #include #include -#include +#include +#include #include "fimc-core.h" @@ -283,7 +284,7 @@ static void fimc_capture_handler(struct fimc_dev *fimc) if (!list_empty(&cap->active_buf_q)) { v_buf = active_queue_pop(cap); - fimc_buf_finish(fimc, v_buf); + vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); } if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { @@ -300,10 +301,6 @@ static void fimc_capture_handler(struct fimc_dev *fimc) dbg("hw ptr: %d, sw ptr: %d", fimc_hw_get_frame_index(fimc), cap->buf_index); - spin_lock(&fimc->irqlock); - v_buf->vb.state = VIDEOBUF_ACTIVE; - spin_unlock(&fimc->irqlock); - /* Move the buffer to the capture active queue */ active_queue_add(cap, v_buf); @@ -324,8 +321,6 @@ static void fimc_capture_handler(struct fimc_dev *fimc) static irqreturn_t fimc_isr(int irq, void *priv) { - struct fimc_vid_buffer *src_buf, *dst_buf; - struct fimc_ctx *ctx; struct fimc_dev *fimc = priv; BUG_ON(!fimc); @@ -334,17 +329,17 @@ static irqreturn_t fimc_isr(int irq, void *priv) spin_lock(&fimc->slock); if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) { - ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev); + struct vb2_buffer *src_vb, *dst_vb; + struct fimc_ctx *ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev); + if (!ctx || !ctx->m2m_ctx) goto isr_unlock; - src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - if (src_buf && dst_buf) { - spin_lock(&fimc->irqlock); - src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE; - wake_up(&src_buf->vb.done); - wake_up(&dst_buf->vb.done); - spin_unlock(&fimc->irqlock); + + src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + if (src_vb && dst_vb) { + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx); } goto isr_unlock; @@ -365,13 +360,13 @@ isr_unlock: } /* The color format (planes_cnt, buff_cnt) must be already configured. */ -int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf, +int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, struct fimc_frame *frame, struct fimc_addr *paddr) { int ret = 0; u32 pix_size; - if (buf == NULL || frame == NULL) + if (vb == NULL || frame == NULL) return -EINVAL; pix_size = frame->width * frame->height; @@ -381,7 +376,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf, frame->size, pix_size); if (frame->fmt->buff_cnt == 1) { - paddr->y = videobuf_to_dma_contig(&buf->vb); + paddr->y = vb2_dma_contig_plane_paddr(vb, 0); switch (frame->fmt->planes_cnt) { case 1: paddr->cb = 0; @@ -499,7 +494,7 @@ static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) { struct fimc_frame *s_frame, *d_frame; - struct fimc_vid_buffer *buf = NULL; + struct vb2_buffer *vb = NULL; int ret = 0; s_frame = &ctx->s_frame; @@ -522,15 +517,15 @@ int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) ctx->scaler.enabled = 1; if (flags & FIMC_SRC_ADDR) { - buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, buf, s_frame, &s_frame->paddr); + vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr); if (ret) return ret; } if (flags & FIMC_DST_ADDR) { - buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, buf, d_frame, &d_frame->paddr); + vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, d_frame, &d_frame->paddr); } return ret; @@ -587,7 +582,8 @@ static void fimc_dma_run(void *priv) fimc_activate_capture(ctx); - ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP); + ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | + FIMC_SRC_FMT | FIMC_DST_FMT); fimc_hw_activate_input_dma(fimc, true); dma_unlock: @@ -599,106 +595,72 @@ static void fimc_job_abort(void *priv) /* Nothing done in job_abort. */ } -static void fimc_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, + unsigned int *num_planes, unsigned long sizes[], + void *allocators[]) { - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; -} + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + struct fimc_frame *fr; -static int fimc_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct fimc_ctx *ctx = vq->priv_data; - struct fimc_frame *frame; + fr = ctx_get_frame(ctx, vq->type); + if (IS_ERR(fr)) + return PTR_ERR(fr); - frame = ctx_get_frame(ctx, vq->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); + *num_planes = 1; + + sizes[0] = (fr->width * fr->height * fr->fmt->depth) >> 3; + allocators[0] = ctx->fimc_dev->alloc_ctx; - *size = (frame->width * frame->height * frame->fmt->depth) >> 3; - if (0 == *count) - *count = 1; return 0; } -static int fimc_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) +static int fimc_buf_prepare(struct vb2_buffer *vb) { - struct fimc_ctx *ctx = vq->priv_data; - struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev; + struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct fimc_frame *frame; - int ret; - frame = ctx_get_frame(ctx, vq->type); + frame = ctx_get_frame(ctx, vb->vb2_queue->type); if (IS_ERR(frame)) return PTR_ERR(frame); - if (vb->baddr) { - if (vb->bsize < frame->size) { - v4l2_err(v4l2_dev, - "User-provided buffer too small (%d < %d)\n", - vb->bsize, frame->size); - WARN_ON(1); - return -EINVAL; - } - } else if (vb->state != VIDEOBUF_NEEDS_INIT - && vb->bsize < frame->size) { + if (vb2_plane_size(vb, 0) < frame->size) { + dbg("%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), (long)frame->size); return -EINVAL; } - vb->width = frame->width; - vb->height = frame->height; - vb->bytesperline = (frame->width * frame->fmt->depth) >> 3; - vb->size = frame->size; - vb->field = field; - - if (VIDEOBUF_NEEDS_INIT == vb->state) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) { - v4l2_err(v4l2_dev, "Iolock failed\n"); - fimc_buf_release(vq, vb); - return ret; - } - } - vb->state = VIDEOBUF_PREPARED; - + vb2_set_plane_payload(vb, 0, frame->size); return 0; } -static void fimc_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void fimc_buf_queue(struct vb2_buffer *vb) { - struct fimc_ctx *ctx = vq->priv_data; - struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_vid_cap *cap = &fimc->vid_cap; - unsigned long flags; + struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); - if ((ctx->state & FIMC_CTX_M2M) && ctx->m2m_ctx) { - v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb); - } else if (ctx->state & FIMC_CTX_CAP) { - spin_lock_irqsave(&fimc->slock, flags); - fimc_vid_cap_buf_queue(fimc, (struct fimc_vid_buffer *)vb); + if (ctx->m2m_ctx) + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); +} - dbg("fimc->cap.active_buf_cnt: %d", - fimc->vid_cap.active_buf_cnt); +static void fimc_lock(struct vb2_queue *vq) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + mutex_lock(&ctx->fimc_dev->lock); +} - if (cap->active_buf_cnt >= cap->reqbufs_count || - cap->active_buf_cnt >= FIMC_MAX_OUT_BUFS) { - if (!test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) - fimc_activate_capture(ctx); - } - spin_unlock_irqrestore(&fimc->slock, flags); - } +static void fimc_unlock(struct vb2_queue *vq) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + mutex_unlock(&ctx->fimc_dev->lock); } -struct videobuf_queue_ops fimc_qops = { - .buf_setup = fimc_buf_setup, - .buf_prepare = fimc_buf_prepare, - .buf_queue = fimc_buf_queue, - .buf_release = fimc_buf_release, +struct vb2_ops fimc_qops = { + .queue_setup = fimc_queue_setup, + .buf_prepare = fimc_buf_prepare, + .buf_queue = fimc_buf_queue, + .wait_prepare = fimc_unlock, + .wait_finish = fimc_lock, }; static int fimc_m2m_querycap(struct file *file, void *priv, @@ -867,7 +829,7 @@ static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; struct v4l2_device *v4l2_dev = &fimc->m2m.v4l2_dev; - struct videobuf_queue *vq; + struct vb2_queue *vq; struct fimc_frame *frame; struct v4l2_pix_format *pix; unsigned long flags; @@ -881,9 +843,8 @@ static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) return -ERESTARTSYS; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - mutex_lock(&vq->vb_lock); - if (videobuf_queue_is_busy(vq)) { + if (vb2_is_streaming(vq)) { v4l2_err(v4l2_dev, "%s: queue (%d) busy\n", __func__, f->type); ret = -EBUSY; goto sf_out; @@ -921,7 +882,6 @@ static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frame->offs_h = 0; frame->offs_v = 0; frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3; - vq->field = pix->field; spin_lock_irqsave(&ctx->slock, flags); ctx->state |= FIMC_PARAMS; @@ -930,7 +890,6 @@ static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); sf_out: - mutex_unlock(&vq->vb_lock); mutex_unlock(&fimc->lock); return ret; } @@ -968,6 +927,11 @@ static int fimc_m2m_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_ctx *ctx = priv; + + /* The source and target color format need to be set */ + if (~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT)) + return -EINVAL; + return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); } @@ -1301,16 +1265,32 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { }; -static void queue_init(void *priv, struct videobuf_queue *vq, - enum v4l2_buf_type type) +static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = ctx; + src_vq->ops = &fimc_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - videobuf_queue_dma_contig_init(vq, &fimc_qops, - &fimc->pdev->dev, - &fimc->irqlock, type, V4L2_FIELD_NONE, - sizeof(struct fimc_vid_buffer), priv, NULL); + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = ctx; + dst_vq->ops = &fimc_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + + return vb2_queue_init(dst_vq); } static int fimc_m2m_open(struct file *file) @@ -1355,7 +1335,7 @@ static int fimc_m2m_open(struct file *file) ctx->out_path = FIMC_DMA; spin_lock_init(&ctx->slock); - ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, fimc->m2m.m2m_dev, queue_init); + ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { err = PTR_ERR(ctx->m2m_ctx); kfree(ctx); @@ -1415,7 +1395,6 @@ static struct v4l2_m2m_ops m2m_ops = { .job_abort = fimc_job_abort, }; - static int fimc_register_m2m_device(struct fimc_dev *fimc) { struct video_device *vfd; @@ -1548,7 +1527,6 @@ static int fimc_probe(struct platform_device *pdev) fimc->pdata = pdev->dev.platform_data; fimc->state = ST_IDLE; - spin_lock_init(&fimc->irqlock); init_waitqueue_head(&fimc->irq_queue); spin_lock_init(&fimc->slock); @@ -1597,6 +1575,13 @@ static int fimc_probe(struct platform_device *pdev) goto err_clk; } + /* Initialize contiguous memory allocator */ + fimc->alloc_ctx = vb2_dma_contig_init_ctx(&fimc->pdev->dev); + if (IS_ERR(fimc->alloc_ctx)) { + ret = PTR_ERR(fimc->alloc_ctx); + goto err_irq; + } + ret = fimc_register_m2m_device(fimc); if (ret) goto err_irq; @@ -1656,6 +1641,9 @@ static int __devexit fimc_remove(struct platform_device *pdev) fimc_unregister_capture_device(fimc); fimc_clk_release(fimc); + + vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + iounmap(fimc->regs); release_resource(fimc->regs_res); kfree(fimc->regs_res); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 4f047d3..590fbf2 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -16,7 +16,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -70,13 +71,6 @@ enum fimc_dev_flags { #define fimc_capture_streaming(dev) \ test_bit(ST_CAPT_STREAM, &(dev)->state) -#define fimc_buf_finish(dev, vid_buf) do { \ - spin_lock(&(dev)->irqlock); \ - (vid_buf)->vb.state = VIDEOBUF_DONE; \ - spin_unlock(&(dev)->irqlock); \ - wake_up(&(vid_buf)->vb.done); \ -} while (0) - enum fimc_datapath { FIMC_CAMERA, FIMC_DMA, @@ -260,7 +254,8 @@ struct fimc_addr { * @index: buffer index for the output DMA engine */ struct fimc_vid_buffer { - struct videobuf_buffer vb; + struct vb2_buffer vb; + struct list_head list; struct fimc_addr paddr; int index; }; @@ -331,13 +326,14 @@ struct fimc_m2m_device { */ struct fimc_vid_cap { struct fimc_ctx *ctx; + struct vb2_alloc_ctx *alloc_ctx; struct video_device *vfd; struct v4l2_device v4l2_dev; - struct v4l2_subdev *sd; + struct v4l2_subdev *sd;; struct v4l2_mbus_framefmt fmt; struct list_head pending_buf_q; struct list_head active_buf_q; - struct videobuf_queue vbq; + struct vb2_queue vbq; int active_buf_cnt; int buf_index; unsigned int frame_count; @@ -417,7 +413,6 @@ struct fimc_ctx; * @regs: the mapped hardware registers * @regs_res: the resource claimed for IO registers * @irq: interrupt number of the FIMC subdevice - * @irqlock: spinlock protecting videobuffer queue * @irq_queue: * @m2m: memory-to-memory V4L2 device information * @vid_cap: camera capture device information @@ -434,11 +429,11 @@ struct fimc_dev { void __iomem *regs; struct resource *regs_res; int irq; - spinlock_t irqlock; wait_queue_head_t irq_queue; struct fimc_m2m_device m2m; struct fimc_vid_cap vid_cap; unsigned long state; + struct vb2_alloc_ctx *alloc_ctx; }; /** @@ -482,8 +477,6 @@ struct fimc_ctx { struct v4l2_m2m_ctx *m2m_ctx; }; -extern struct videobuf_queue_ops fimc_qops; - static inline int tiled_fmt(struct fimc_fmt *fmt) { return 0; @@ -622,7 +615,7 @@ struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f, int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f); int fimc_set_scaler_info(struct fimc_ctx *ctx); int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags); -int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf, +int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, struct fimc_frame *frame, struct fimc_addr *paddr); /* -----------------------------------------------------*/ @@ -649,28 +642,27 @@ static inline void fimc_deactivate_capture(struct fimc_dev *fimc) } /* - * Add video buffer to the active buffers queue. - * The caller holds irqlock spinlock. + * Add buf to the capture active buffers queue. + * Locking: Need to be called with fimc_dev::slock held. */ static inline void active_queue_add(struct fimc_vid_cap *vid_cap, - struct fimc_vid_buffer *buf) + struct fimc_vid_buffer *buf) { - buf->vb.state = VIDEOBUF_ACTIVE; - list_add_tail(&buf->vb.queue, &vid_cap->active_buf_q); + list_add_tail(&buf->list, &vid_cap->active_buf_q); vid_cap->active_buf_cnt++; } /* * Pop a video buffer from the capture active buffers queue - * Locking: Need to be called with dev->slock held. + * Locking: Need to be called with fimc_dev::slock held. */ static inline struct fimc_vid_buffer * active_queue_pop(struct fimc_vid_cap *vid_cap) { struct fimc_vid_buffer *buf; buf = list_entry(vid_cap->active_buf_q.next, - struct fimc_vid_buffer, vb.queue); - list_del(&buf->vb.queue); + struct fimc_vid_buffer, list); + list_del(&buf->list); vid_cap->active_buf_cnt--; return buf; } @@ -679,8 +671,7 @@ active_queue_pop(struct fimc_vid_cap *vid_cap) static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap, struct fimc_vid_buffer *buf) { - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vid_cap->pending_buf_q); + list_add_tail(&buf->list, &vid_cap->pending_buf_q); } /* Add video buffer to the capture pending buffers queue */ @@ -689,10 +680,9 @@ pending_queue_pop(struct fimc_vid_cap *vid_cap) { struct fimc_vid_buffer *buf; buf = list_entry(vid_cap->pending_buf_q.next, - struct fimc_vid_buffer, vb.queue); - list_del(&buf->vb.queue); + struct fimc_vid_buffer, list); + list_del(&buf->list); return buf; } - #endif /* FIMC_CORE_H_ */ -- cgit v0.10.2 From ef7af59b2cf950a44576d4364459ffd3d5d1d9bc Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 8 Dec 2010 14:05:08 -0300 Subject: [media] s5p-fimc: Conversion to multiplanar formats Conversion to multiplanar color formats and minor cleanup. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 24b77b7..e746b68 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -280,34 +280,16 @@ static int stop_streaming(struct vb2_queue *q) return fimc_stop_capture(fimc); } -static unsigned int get_plane_size(struct fimc_frame *frame, unsigned int plane) +static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane) { - unsigned long size = 0; - - if (!frame || plane > frame->fmt->buff_cnt - 1) + if (!fr || plane >= fr->fmt->memplanes) return 0; - if (1 == frame->fmt->planes_cnt) { - size = (frame->width * frame->height * frame->fmt->depth) >> 3; - } else if (frame->fmt->planes_cnt <= 3) { - switch (plane) { - case 0: - size = frame->width * frame->height; - break; - case 1: - case 2: - if (S5P_FIMC_YCBCR420 == frame->fmt->color - && 2 != frame->fmt->planes_cnt) - size = (frame->width * frame->height) >> 2; - else /* 422 */ - size = (frame->width * frame->height) >> 1; - break; - } - } else { - size = 0; - } + dbg("%s: w: %d. h: %d. depth[%d]: %d", + __func__, fr->width, fr->height, plane, fr->fmt->depth[plane]); + + return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8; - return size; } static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, @@ -315,25 +297,24 @@ static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, void *allocators[]) { struct fimc_ctx *ctx = vq->drv_priv; - struct fimc_fmt *fmt = fmt = ctx->d_frame.fmt; - struct fimc_frame *frame; + struct fimc_fmt *fmt = ctx->d_frame.fmt; + int i; if (!fmt) return -EINVAL; - *num_planes = fmt->buff_cnt; + *num_planes = fmt->memplanes; dbg("%s, buffer count=%d, plane count=%d", __func__, *num_buffers, *num_planes); - - frame = ctx_get_frame(ctx, vq->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); - sizes[0] = get_plane_size(frame, 0); - allocators[0] = ctx->fimc_dev->alloc_ctx; + for (i = 0; i < fmt->memplanes; i++) { + sizes[i] = get_plane_size(&ctx->d_frame, i); + dbg("plane: %u, plane_size: %lu", i, sizes[i]); + allocators[i] = ctx->fimc_dev->alloc_ctx; + } - return -EINVAL; + return 0; } static int buffer_init(struct vb2_buffer *vb) @@ -347,16 +328,13 @@ static int buffer_prepare(struct vb2_buffer *vb) struct vb2_queue *vq = vb->vb2_queue; struct fimc_ctx *ctx = vq->drv_priv; struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev; - struct fimc_frame *frame; - unsigned long size; int i; - frame = ctx_get_frame(ctx, vq->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); + if (!ctx->d_frame.fmt || vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; - for (i = 0; i < frame->fmt->buff_cnt; i++) { - size = get_plane_size(frame, i); + for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) { + unsigned long size = get_plane_size(&ctx->d_frame, i); if (vb2_plane_size(vb, i) < size) { v4l2_err(v4l2_dev, "User buffer too small (%ld < %ld)\n", @@ -451,7 +429,6 @@ static int fimc_capture_close(struct file *file) if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; - dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); if (--fimc->vid_cap.refcnt == 0) { @@ -519,7 +496,8 @@ static int fimc_vidioc_querycap_capture(struct file *file, void *priv, strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_CAPTURE_MPLANE; return 0; } @@ -560,19 +538,20 @@ static int sync_capture_fmt(struct fimc_ctx *ctx) return 0; } -static int fimc_cap_s_fmt(struct file *file, void *priv, - struct v4l2_format *f) +static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, + struct v4l2_format *f) { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_frame *frame; - struct v4l2_pix_format *pix; + struct v4l2_pix_format_mplane *pix; int ret; + int i; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; - ret = fimc_vidioc_try_fmt(file, priv, f); + ret = fimc_vidioc_try_fmt_mplane(file, priv, f); if (ret) return ret; @@ -583,10 +562,12 @@ static int fimc_cap_s_fmt(struct file *file, void *priv, ret = -EBUSY; goto sf_unlock; } + if (vb2_is_streaming(&fimc->vid_cap.vbq) || fimc_capture_active(fimc)) + return -EBUSY; frame = &ctx->d_frame; - pix = &f->fmt.pix; + pix = &f->fmt.pix_mp; frame->fmt = find_format(f, FMT_FLAGS_M2M | FMT_FLAGS_CAM); if (!frame->fmt) { err("fimc target format not found\n"); @@ -594,14 +575,17 @@ static int fimc_cap_s_fmt(struct file *file, void *priv, goto sf_unlock; } + for (i = 0; i < frame->fmt->colplanes; i++) + frame->payload[i] = pix->plane_fmt[i].bytesperline * pix->height; + /* Output DMA frame pixel size and offsets. */ - frame->f_width = pix->bytesperline * 8 / frame->fmt->depth; + frame->f_width = pix->plane_fmt[0].bytesperline * 8 + / frame->fmt->depth[0]; frame->f_height = pix->height; frame->width = pix->width; frame->height = pix->height; frame->o_width = pix->width; frame->o_height = pix->height; - frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3; frame->offs_h = 0; frame->offs_v = 0; @@ -769,7 +753,7 @@ static int fimc_cap_streamoff(struct file *file, void *priv, } static int fimc_cap_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) + struct v4l2_requestbuffers *reqbufs) { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; @@ -870,13 +854,13 @@ static int fimc_cap_cropcap(struct file *file, void *fh, struct fimc_ctx *ctx = fh; struct fimc_dev *fimc = ctx->fimc_dev; - if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; - f = &ctx->s_frame; + cr->bounds.left = 0; cr->bounds.top = 0; cr->bounds.width = f->o_width; @@ -953,10 +937,10 @@ sc_unlock: static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_querycap = fimc_vidioc_querycap_capture, - .vidioc_enum_fmt_vid_cap = fimc_vidioc_enum_fmt, - .vidioc_try_fmt_vid_cap = fimc_vidioc_try_fmt, - .vidioc_s_fmt_vid_cap = fimc_cap_s_fmt, - .vidioc_g_fmt_vid_cap = fimc_vidioc_g_fmt, + .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane, .vidioc_reqbufs = fimc_cap_reqbufs, .vidioc_querybuf = fimc_cap_querybuf, @@ -980,6 +964,7 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_g_input = fimc_cap_g_input, }; +/* fimc->lock must be already initialized */ int fimc_register_capture_device(struct fimc_dev *fimc) { struct v4l2_device *v4l2_dev = &fimc->vid_cap.v4l2_dev; @@ -987,6 +972,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc) struct fimc_vid_cap *vid_cap; struct fimc_ctx *ctx; struct v4l2_format f; + struct fimc_frame *fr; struct vb2_queue *q; int ret; @@ -999,8 +985,12 @@ int fimc_register_capture_device(struct fimc_dev *fimc) ctx->out_path = FIMC_DMA; ctx->state = FIMC_CTX_CAP; - f.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; - ctx->d_frame.fmt = find_format(&f, FMT_FLAGS_M2M); + /* Default format of the output frames */ + f.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; + fr = &ctx->d_frame; + fr->fmt = find_format(&f, FMT_FLAGS_M2M); + fr->width = fr->f_width = fr->o_width = 640; + fr->height = fr->f_height = fr->o_height = 480; if (!v4l2_dev->name[0]) snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), @@ -1030,7 +1020,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc) vid_cap->active_buf_cnt = 0; vid_cap->reqbufs_count = 0; vid_cap->refcnt = 0; - /* The default color format for image sensor. */ + /* Default color format for image sensor */ vid_cap->fmt.code = V4L2_MBUS_FMT_YUYV8_2X8; INIT_LIST_HEAD(&vid_cap->pending_buf_q); @@ -1040,7 +1030,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc) q = &fimc->vid_cap.vbq; memset(q, 0, sizeof(*q)); - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; q->io_modes = VB2_MMAP | VB2_USERPTR; q->drv_priv = fimc->vid_cap.ctx; q->ops = &fimc_capture_qops; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 65d25b3..fe21097 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -34,106 +34,130 @@ static char *fimc_clock_name[NUM_FIMC_CLOCKS] = { "sclk_fimc", "fimc" }; static struct fimc_fmt fimc_formats[] = { { - .name = "RGB565", - .fourcc = V4L2_PIX_FMT_RGB565X, - .depth = 16, - .color = S5P_FIMC_RGB565, - .buff_cnt = 1, - .planes_cnt = 1, - .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_BE, - .flags = FMT_FLAGS_M2M, + .name = "RGB565", + .fourcc = V4L2_PIX_FMT_RGB565X, + .depth = { 16 }, + .color = S5P_FIMC_RGB565, + .memplanes = 1, + .colplanes = 1, + .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_BE, + .flags = FMT_FLAGS_M2M, }, { - .name = "BGR666", - .fourcc = V4L2_PIX_FMT_BGR666, - .depth = 32, - .color = S5P_FIMC_RGB666, - .buff_cnt = 1, - .planes_cnt = 1, - .flags = FMT_FLAGS_M2M, + .name = "BGR666", + .fourcc = V4L2_PIX_FMT_BGR666, + .depth = { 32 }, + .color = S5P_FIMC_RGB666, + .memplanes = 1, + .colplanes = 1, + .flags = FMT_FLAGS_M2M, }, { - .name = "XRGB-8-8-8-8, 32 bpp", - .fourcc = V4L2_PIX_FMT_RGB32, - .depth = 32, - .color = S5P_FIMC_RGB888, - .buff_cnt = 1, - .planes_cnt = 1, - .flags = FMT_FLAGS_M2M, + .name = "XRGB-8-8-8-8, 32 bpp", + .fourcc = V4L2_PIX_FMT_RGB32, + .depth = { 32 }, + .color = S5P_FIMC_RGB888, + .memplanes = 1, + .colplanes = 1, + .flags = FMT_FLAGS_M2M, }, { - .name = "YUV 4:2:2 packed, YCbYCr", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .color = S5P_FIMC_YCBYCR422, - .buff_cnt = 1, - .planes_cnt = 1, - .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, - .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, + .name = "YUV 4:2:2 packed, YCbYCr", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = { 16 }, + .color = S5P_FIMC_YCBYCR422, + .memplanes = 1, + .colplanes = 1, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, }, { - .name = "YUV 4:2:2 packed, CbYCrY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - .color = S5P_FIMC_CBYCRY422, - .buff_cnt = 1, - .planes_cnt = 1, - .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, - .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, + .name = "YUV 4:2:2 packed, CbYCrY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = { 16 }, + .color = S5P_FIMC_CBYCRY422, + .memplanes = 1, + .colplanes = 1, + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, }, { - .name = "YUV 4:2:2 packed, CrYCbY", - .fourcc = V4L2_PIX_FMT_VYUY, - .depth = 16, - .color = S5P_FIMC_CRYCBY422, - .buff_cnt = 1, - .planes_cnt = 1, - .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, - .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, + .name = "YUV 4:2:2 packed, CrYCbY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = { 16 }, + .color = S5P_FIMC_CRYCBY422, + .memplanes = 1, + .colplanes = 1, + .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, + .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, }, { - .name = "YUV 4:2:2 packed, YCrYCb", - .fourcc = V4L2_PIX_FMT_YVYU, - .depth = 16, - .color = S5P_FIMC_YCRYCB422, - .buff_cnt = 1, - .planes_cnt = 1, - .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, - .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = { 16 }, + .color = S5P_FIMC_YCRYCB422, + .memplanes = 1, + .colplanes = 1, + .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, + .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, }, { - .name = "YUV 4:2:2 planar, Y/Cb/Cr", - .fourcc = V4L2_PIX_FMT_YUV422P, - .depth = 12, - .color = S5P_FIMC_YCBCR422, - .buff_cnt = 1, - .planes_cnt = 3, - .flags = FMT_FLAGS_M2M, + .name = "YUV 4:2:2 planar, Y/Cb/Cr", + .fourcc = V4L2_PIX_FMT_YUV422P, + .depth = { 12 }, + .color = S5P_FIMC_YCBYCR422, + .memplanes = 1, + .colplanes = 3, + .flags = FMT_FLAGS_M2M, }, { - .name = "YUV 4:2:2 planar, Y/CbCr", - .fourcc = V4L2_PIX_FMT_NV16, - .depth = 16, - .color = S5P_FIMC_YCBCR422, - .buff_cnt = 1, - .planes_cnt = 2, - .flags = FMT_FLAGS_M2M, + .name = "YUV 4:2:2 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV16, + .depth = { 16 }, + .color = S5P_FIMC_YCBYCR422, + .memplanes = 1, + .colplanes = 2, + .flags = FMT_FLAGS_M2M, }, { - .name = "YUV 4:2:2 planar, Y/CrCb", - .fourcc = V4L2_PIX_FMT_NV61, - .depth = 16, - .color = S5P_FIMC_RGB565, - .buff_cnt = 1, - .planes_cnt = 2, - .flags = FMT_FLAGS_M2M, + .name = "YUV 4:2:2 planar, Y/CrCb", + .fourcc = V4L2_PIX_FMT_NV61, + .depth = { 16 }, + .color = S5P_FIMC_YCRYCB422, + .memplanes = 1, + .colplanes = 2, + .flags = FMT_FLAGS_M2M, }, { - .name = "YUV 4:2:0 planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV420, - .depth = 12, - .color = S5P_FIMC_YCBCR420, - .buff_cnt = 1, - .planes_cnt = 3, - .flags = FMT_FLAGS_M2M, + .name = "YUV 4:2:0 planar, YCbCr", + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = { 12 }, + .color = S5P_FIMC_YCBCR420, + .memplanes = 1, + .colplanes = 3, + .flags = FMT_FLAGS_M2M, }, { - .name = "YUV 4:2:0 planar, Y/CbCr", - .fourcc = V4L2_PIX_FMT_NV12, - .depth = 12, - .color = S5P_FIMC_YCBCR420, - .buff_cnt = 1, - .planes_cnt = 2, - .flags = FMT_FLAGS_M2M, + .name = "YUV 4:2:0 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV12, + .depth = { 12 }, + .color = S5P_FIMC_YCBCR420, + .memplanes = 1, + .colplanes = 2, + .flags = FMT_FLAGS_M2M, + }, { + .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV12M, + .color = S5P_FIMC_YCBCR420, + .depth = { 8, 4 }, + .memplanes = 2, + .colplanes = 2, + .flags = FMT_FLAGS_M2M, + }, { + .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr", + .fourcc = V4L2_PIX_FMT_YUV420M, + .color = S5P_FIMC_YCBCR420, + .depth = { 8, 2, 2 }, + .memplanes = 3, + .colplanes = 3, + .flags = FMT_FLAGS_M2M, + }, { + .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled", + .fourcc = V4L2_PIX_FMT_NV12MT, + .color = S5P_FIMC_YCBCR420, + .depth = { 8, 4 }, + .memplanes = 2, + .colplanes = 2, + .flags = FMT_FLAGS_M2M, }, }; @@ -359,7 +383,7 @@ isr_unlock: return IRQ_HANDLED; } -/* The color format (planes_cnt, buff_cnt) must be already configured. */ +/* The color format (colplanes, memplanes) must be already configured. */ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, struct fimc_frame *frame, struct fimc_addr *paddr) { @@ -371,13 +395,13 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, pix_size = frame->width * frame->height; - dbg("buff_cnt= %d, planes_cnt= %d, frame->size= %d, pix_size= %d", - frame->fmt->buff_cnt, frame->fmt->planes_cnt, - frame->size, pix_size); + dbg("memplanes= %d, colplanes= %d, pix_size= %d", + frame->fmt->memplanes, frame->fmt->colplanes, pix_size); + + paddr->y = vb2_dma_contig_plane_paddr(vb, 0); - if (frame->fmt->buff_cnt == 1) { - paddr->y = vb2_dma_contig_plane_paddr(vb, 0); - switch (frame->fmt->planes_cnt) { + if (frame->fmt->memplanes == 1) { + switch (frame->fmt->colplanes) { case 1: paddr->cb = 0; paddr->cr = 0; @@ -400,6 +424,12 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, default: return -EINVAL; } + } else { + if (frame->fmt->memplanes >= 2) + paddr->cb = vb2_dma_contig_plane_paddr(vb, 1); + + if (frame->fmt->memplanes == 3) + paddr->cr = vb2_dma_contig_plane_paddr(vb, 2); } dbg("PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d", @@ -454,10 +484,14 @@ static void fimc_set_yuv_order(struct fimc_ctx *ctx) static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + u32 i, depth = 0; + + for (i = 0; i < f->fmt->colplanes; i++) + depth += f->fmt->depth[i]; f->dma_offset.y_h = f->offs_h; if (!variant->pix_hoff) - f->dma_offset.y_h *= (f->fmt->depth >> 3); + f->dma_offset.y_h *= (depth >> 3); f->dma_offset.y_v = f->offs_v; @@ -468,7 +502,7 @@ static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->dma_offset.cr_v = f->offs_v; if (!variant->pix_hoff) { - if (f->fmt->planes_cnt == 3) { + if (f->fmt->colplanes == 3) { f->dma_offset.cb_h >>= 1; f->dma_offset.cr_h >>= 1; } @@ -596,20 +630,31 @@ static void fimc_job_abort(void *priv) } static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, - unsigned int *num_planes, unsigned long sizes[], - void *allocators[]) + unsigned int *num_planes, unsigned long sizes[], + void *allocators[]) { struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - struct fimc_frame *fr; + struct fimc_frame *f; + int i; + + f = ctx_get_frame(ctx, vq->type); + if (IS_ERR(f)) + return PTR_ERR(f); - fr = ctx_get_frame(ctx, vq->type); - if (IS_ERR(fr)) - return PTR_ERR(fr); + /* + * Return number of non-contigous planes (plane buffers) + * depending on the configured color format. + */ + if (f->fmt) + *num_planes = f->fmt->memplanes; - *num_planes = 1; + for (i = 0; i < f->fmt->memplanes; i++) { + sizes[i] = (f->width * f->height * f->fmt->depth[i]) >> 3; + allocators[i] = ctx->fimc_dev->alloc_ctx; + } - sizes[0] = (fr->width * fr->height * fr->fmt->depth) >> 3; - allocators[0] = ctx->fimc_dev->alloc_ctx; + if (*num_buffers == 0) + *num_buffers = 1; return 0; } @@ -618,18 +663,15 @@ static int fimc_buf_prepare(struct vb2_buffer *vb) { struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct fimc_frame *frame; + int i; frame = ctx_get_frame(ctx, vb->vb2_queue->type); if (IS_ERR(frame)) return PTR_ERR(frame); - if (vb2_plane_size(vb, 0) < frame->size) { - dbg("%s data will not fit into plane (%lu < %lu)\n", - __func__, vb2_plane_size(vb, 0), (long)frame->size); - return -EINVAL; - } + for (i = 0; i < frame->fmt->memplanes; i++) + vb2_set_plane_payload(vb, i, frame->payload[i]); - vb2_set_plane_payload(vb, 0, frame->size); return 0; } @@ -674,12 +716,13 @@ static int fimc_m2m_querycap(struct file *file, void *priv, cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); cap->capabilities = V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; } -int fimc_vidioc_enum_fmt(struct file *file, void *priv, +int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct fimc_fmt *fmt; @@ -694,7 +737,8 @@ int fimc_vidioc_enum_fmt(struct file *file, void *priv, return 0; } -int fimc_vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv, + struct v4l2_format *f) { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; @@ -747,29 +791,28 @@ struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f, } -int fimc_vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, + struct v4l2_format *f) { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; struct samsung_fimc_variant *variant = fimc->variant; - struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct fimc_fmt *fmt; u32 max_width, mod_x, mod_y, mask; - int ret = -EINVAL, is_output = 0; + int ret, i, is_output = 0; - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (ctx->state & FIMC_CTX_CAP) return -EINVAL; is_output = 1; - } else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + } else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { return -EINVAL; } - dbg("w: %d, h: %d, bpl: %d", - pix->width, pix->height, pix->bytesperline); - if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; + dbg("w: %d, h: %d", pix->width, pix->height); mask = is_output ? FMT_FLAGS_M2M : FMT_FLAGS_M2M | FMT_FLAGS_CAM; fmt = find_format(f, mask); @@ -796,7 +839,7 @@ int fimc_vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mod_x = 6; /* 64 x 32 pixels tile */ mod_y = 5; } else { - if (fimc->id == 1 && fimc->variant->pix_hoff) + if (fimc->id == 1 && variant->pix_hoff) mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; else mod_y = mod_x; @@ -807,15 +850,26 @@ int fimc_vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) v4l_bound_align_image(&pix->width, 16, max_width, mod_x, &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); - if (pix->bytesperline == 0 || - (pix->bytesperline * 8 / fmt->depth) > pix->width) - pix->bytesperline = (pix->width * fmt->depth) >> 3; + pix->num_planes = fmt->memplanes; + + for (i = 0; i < pix->num_planes; ++i) { + int bpl = pix->plane_fmt[i].bytesperline; + + dbg("[%d] bpl: %d, depth: %d, w: %d, h: %d", + i, bpl, fmt->depth[i], pix->width, pix->height); - if (pix->sizeimage == 0) - pix->sizeimage = pix->height * pix->bytesperline; + if (!bpl || (bpl * 8 / fmt->depth[i]) > pix->width) + bpl = (pix->width * fmt->depth[0]) >> 3; - dbg("w: %d, h: %d, bpl: %d, depth: %d", - pix->width, pix->height, pix->bytesperline, fmt->depth); + if (!pix->plane_fmt[i].sizeimage) + pix->plane_fmt[i].sizeimage = pix->height * bpl; + + pix->plane_fmt[i].bytesperline = bpl; + + dbg("[%d]: bpl: %d, sizeimage: %d", + i, pix->plane_fmt[i].bytesperline, + pix->plane_fmt[i].sizeimage); + } ret = 0; @@ -824,18 +878,18 @@ tf_out: return ret; } -static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv, + struct v4l2_format *f) { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; - struct v4l2_device *v4l2_dev = &fimc->m2m.v4l2_dev; struct vb2_queue *vq; struct fimc_frame *frame; - struct v4l2_pix_format *pix; + struct v4l2_pix_format_mplane *pix; unsigned long flags; - int ret = 0; + int i, ret = 0; - ret = fimc_vidioc_try_fmt(file, priv, f); + ret = fimc_vidioc_try_fmt_mplane(file, priv, f); if (ret) return ret; @@ -845,35 +899,40 @@ static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (vb2_is_streaming(vq)) { - v4l2_err(v4l2_dev, "%s: queue (%d) busy\n", __func__, f->type); + v4l2_err(&fimc->vid_cap.v4l2_dev, "%s: queue (%d) busy\n", + __func__, f->type); ret = -EBUSY; goto sf_out; } spin_lock_irqsave(&ctx->slock, flags); - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { frame = &ctx->s_frame; ctx->state |= FIMC_SRC_FMT; - } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { frame = &ctx->d_frame; ctx->state |= FIMC_DST_FMT; } else { spin_unlock_irqrestore(&ctx->slock, flags); - v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, + v4l2_err(&fimc->m2m.v4l2_dev, "Wrong buffer/video queue type (%d)\n", f->type); ret = -EINVAL; goto sf_out; } spin_unlock_irqrestore(&ctx->slock, flags); - pix = &f->fmt.pix; + pix = &f->fmt.pix_mp; frame->fmt = find_format(f, FMT_FLAGS_M2M); if (!frame->fmt) { ret = -EINVAL; goto sf_out; } - frame->f_width = pix->bytesperline * 8 / frame->fmt->depth; + for (i = 0; i < frame->fmt->colplanes; i++) + frame->payload[i] = pix->plane_fmt[i].bytesperline * pix->height; + + frame->f_width = pix->plane_fmt[0].bytesperline * 8 / + frame->fmt->depth[0]; frame->f_height = pix->height; frame->width = pix->width; frame->height = pix->height; @@ -881,7 +940,6 @@ static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frame->o_height = pix->height; frame->offs_h = 0; frame->offs_v = 0; - frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3; spin_lock_irqsave(&ctx->slock, flags); ctx->state |= FIMC_PARAMS; @@ -929,8 +987,12 @@ static int fimc_m2m_streamon(struct file *file, void *priv, struct fimc_ctx *ctx = priv; /* The source and target color format need to be set */ - if (~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT)) + if (V4L2_TYPE_IS_OUTPUT(type)) { + if (~ctx->state & FIMC_SRC_FMT) + return -EINVAL; + } else if (~ctx->state & FIMC_DST_FMT) { return -EINVAL; + } return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); } @@ -1134,7 +1196,8 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) { struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_frame *f; - u32 min_size, halign; + u32 min_size, halign, depth = 0; + int i; if (cr->c.top < 0 || cr->c.left < 0) { v4l2_err(&fimc->m2m.v4l2_dev, @@ -1142,9 +1205,9 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) return -EINVAL; } - if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) f = (ctx->state & FIMC_CTX_CAP) ? &ctx->s_frame : &ctx->d_frame; - else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && ctx->state & FIMC_CTX_M2M) f = &ctx->s_frame; else @@ -1164,10 +1227,13 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) halign = 4; } + for (i = 0; i < f->fmt->colplanes; i++) + depth += f->fmt->depth[i]; + v4l_bound_align_image(&cr->c.width, min_size, f->o_width, ffs(min_size) - 1, &cr->c.height, min_size, f->o_height, - halign, 64/(ALIGN(f->fmt->depth, 8))); + halign, 64/(ALIGN(depth, 8))); /* adjust left/top if cropping rectangle is out of bounds */ if (cr->c.left + cr->c.width > f->o_width) @@ -1199,7 +1265,7 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) if (ret) return ret; - f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? + f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? &ctx->s_frame : &ctx->d_frame; if (mutex_lock_interruptible(&fimc->lock)) @@ -1208,7 +1274,7 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) spin_lock_irqsave(&ctx->slock, flags); if (~ctx->state & (FIMC_SRC_FMT | FIMC_DST_FMT)) { /* Check to see if scaling ratio is within supported range */ - if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame); else ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame); @@ -1234,17 +1300,17 @@ scr_unlock: static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { .vidioc_querycap = fimc_m2m_querycap, - .vidioc_enum_fmt_vid_cap = fimc_vidioc_enum_fmt, - .vidioc_enum_fmt_vid_out = fimc_vidioc_enum_fmt, + .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane, + .vidioc_enum_fmt_vid_out_mplane = fimc_vidioc_enum_fmt_mplane, - .vidioc_g_fmt_vid_cap = fimc_vidioc_g_fmt, - .vidioc_g_fmt_vid_out = fimc_vidioc_g_fmt, + .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = fimc_vidioc_g_fmt_mplane, - .vidioc_try_fmt_vid_cap = fimc_vidioc_try_fmt, - .vidioc_try_fmt_vid_out = fimc_vidioc_try_fmt, + .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane, + .vidioc_try_fmt_vid_out_mplane = fimc_vidioc_try_fmt_mplane, - .vidioc_s_fmt_vid_cap = fimc_m2m_s_fmt, - .vidioc_s_fmt_vid_out = fimc_m2m_s_fmt, + .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, + .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, .vidioc_reqbufs = fimc_m2m_reqbufs, .vidioc_querybuf = fimc_m2m_querybuf, @@ -1265,7 +1331,8 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { }; -static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) { struct fimc_ctx *ctx = priv; int ret; diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 590fbf2..187af60 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -84,7 +84,6 @@ enum fimc_color_fmt { S5P_FIMC_RGB888, S5P_FIMC_RGB30_LOCAL, S5P_FIMC_YCBCR420 = 0x20, - S5P_FIMC_YCBCR422, S5P_FIMC_YCBYCR422, S5P_FIMC_YCRYCB422, S5P_FIMC_CBYCRY422, @@ -151,18 +150,18 @@ enum fimc_color_fmt { * @name: format description * @fourcc: the fourcc code for this format, 0 if not applicable * @color: the corresponding fimc_color_fmt - * @depth: driver's private 'number of bits per pixel' - * @buff_cnt: number of physically non-contiguous data planes - * @planes_cnt: number of physically contiguous data planes + * @depth: per plane driver's private 'number of bits per pixel' + * @memplanes: number of physically non-contiguous data planes + * @colplanes: number of physically contiguous data planes */ struct fimc_fmt { enum v4l2_mbus_pixelcode mbus_code; char *name; u32 fourcc; u32 color; - u16 buff_cnt; - u16 planes_cnt; - u16 depth; + u16 memplanes; + u16 colplanes; + u8 depth[VIDEO_MAX_PLANES]; u16 flags; #define FMT_FLAGS_CAM (1 << 0) #define FMT_FLAGS_M2M (1 << 1) @@ -272,7 +271,7 @@ struct fimc_vid_buffer { * @height: image pixel weight * @paddr: image frame buffer physical addresses * @buf_cnt: number of buffers depending on a color format - * @size: image size in bytes + * @payload: image size in bytes (w x h x bpp) * @color: color format * @dma_offset: DMA offset in bytes */ @@ -285,7 +284,7 @@ struct fimc_frame { u32 offs_v; u32 width; u32 height; - u32 size; + unsigned long payload[VIDEO_MAX_PLANES]; struct fimc_addr paddr; struct fimc_dma_offset dma_offset; struct fimc_fmt *fmt; @@ -479,7 +478,7 @@ struct fimc_ctx { static inline int tiled_fmt(struct fimc_fmt *fmt) { - return 0; + return fmt->fourcc == V4L2_PIX_FMT_NV12MT; } static inline void fimc_hw_clear_irq(struct fimc_dev *dev) @@ -535,12 +534,12 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, { struct fimc_frame *frame; - if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) { + if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) { if (ctx->state & FIMC_CTX_M2M) frame = &ctx->s_frame; else return ERR_PTR(-EINVAL); - } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) { + } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) { frame = &ctx->d_frame; } else { v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, @@ -582,7 +581,7 @@ void fimc_hw_set_input_path(struct fimc_ctx *ctx); void fimc_hw_set_output_path(struct fimc_ctx *ctx); void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, - int index); + int index); int fimc_hw_set_camera_source(struct fimc_dev *fimc, struct s3c_fimc_isp_info *cam); int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); @@ -593,12 +592,12 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, /* -----------------------------------------------------*/ /* fimc-core.c */ -int fimc_vidioc_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f); -int fimc_vidioc_g_fmt(struct file *file, void *priv, - struct v4l2_format *f); -int fimc_vidioc_try_fmt(struct file *file, void *priv, - struct v4l2_format *f); +int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f); +int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv, + struct v4l2_format *f); +int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, + struct v4l2_format *f); int fimc_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc); int fimc_vidioc_g_ctrl(struct file *file, void *priv, diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 511631a..5ed8f06 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -143,7 +143,7 @@ void fimc_hw_set_target_format(struct fimc_ctx *ctx) case S5P_FIMC_YCRYCB422: case S5P_FIMC_CBYCRY422: case S5P_FIMC_CRYCBY422: - if (frame->fmt->planes_cnt == 1) + if (frame->fmt->colplanes == 1) cfg |= S5P_CITRGFMT_YCBCR422_1P; else cfg |= S5P_CITRGFMT_YCBCR422; @@ -219,11 +219,11 @@ void fimc_hw_set_out_dma(struct fimc_ctx *ctx) cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK | S5P_CIOCTRL_YCBCR_PLANE_MASK); - if (frame->fmt->planes_cnt == 1) + if (frame->fmt->colplanes == 1) cfg |= ctx->out_order_1p; - else if (frame->fmt->planes_cnt == 2) + else if (frame->fmt->colplanes == 2) cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE; - else if (frame->fmt->planes_cnt == 3) + else if (frame->fmt->colplanes == 3) cfg |= S5P_CIOCTRL_YCBCR_3PLANE; writel(cfg, dev->regs + S5P_CIOCTRL); @@ -428,7 +428,7 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) case S5P_FIMC_YCBCR420: cfg |= S5P_MSCTRL_INFORMAT_YCBCR420; - if (frame->fmt->planes_cnt == 2) + if (frame->fmt->colplanes == 2) cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE; else cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; @@ -438,13 +438,13 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) case S5P_FIMC_YCRYCB422: case S5P_FIMC_CBYCRY422: case S5P_FIMC_CRYCBY422: - if (frame->fmt->planes_cnt == 1) { + if (frame->fmt->colplanes == 1) { cfg |= ctx->in_order_1p | S5P_MSCTRL_INFORMAT_YCBCR422_1P; } else { cfg |= S5P_MSCTRL_INFORMAT_YCBCR422; - if (frame->fmt->planes_cnt == 2) + if (frame->fmt->colplanes == 2) cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE; else -- cgit v0.10.2 From 8293ebfce2053242e30069e65427e7560aff1fee Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 1 Dec 2010 10:25:18 -0300 Subject: [media] s5p-fimc: Use v4l core mutex in ioctl and file operations Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index e746b68..3b208f8 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -406,29 +406,23 @@ static int fimc_capture_open(struct file *file) if (fimc_m2m_active(fimc)) return -EBUSY; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - if (++fimc->vid_cap.refcnt == 1) { ret = fimc_isp_subdev_init(fimc, -1); if (ret) { fimc->vid_cap.refcnt--; - ret = -EIO; + return -EIO; } } file->private_data = fimc->vid_cap.ctx; - mutex_unlock(&fimc->lock); - return ret; + return 0; } static int fimc_capture_close(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); if (--fimc->vid_cap.refcnt == 0) { @@ -441,7 +435,6 @@ static int fimc_capture_close(struct file *file) fimc_subdev_unregister(fimc); } - mutex_unlock(&fimc->lock); return 0; } @@ -450,30 +443,16 @@ static unsigned int fimc_capture_poll(struct file *file, { struct fimc_ctx *ctx = file->private_data; struct fimc_dev *fimc = ctx->fimc_dev; - int ret; - if (mutex_lock_interruptible(&fimc->lock)) - return POLLERR; - - ret = vb2_poll(&fimc->vid_cap.vbq, file, wait); - mutex_unlock(&fimc->lock); - - return ret; + return vb2_poll(&fimc->vid_cap.vbq, file, wait); } static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) { struct fimc_ctx *ctx = file->private_data; struct fimc_dev *fimc = ctx->fimc_dev; - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - ret = vb2_mmap(&fimc->vid_cap.vbq, vma); - mutex_unlock(&fimc->lock); - - return ret; + return vb2_mmap(&fimc->vid_cap.vbq, vma); } /* video device file operations */ @@ -555,13 +534,6 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, if (ret) return ret; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - - if (fimc_capture_active(fimc)) { - ret = -EBUSY; - goto sf_unlock; - } if (vb2_is_streaming(&fimc->vid_cap.vbq) || fimc_capture_active(fimc)) return -EBUSY; @@ -571,8 +543,7 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, frame->fmt = find_format(f, FMT_FLAGS_M2M | FMT_FLAGS_CAM); if (!frame->fmt) { err("fimc target format not found\n"); - ret = -EINVAL; - goto sf_unlock; + return -EINVAL; } for (i = 0; i < frame->fmt->colplanes; i++) @@ -589,12 +560,9 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, frame->offs_h = 0; frame->offs_v = 0; - ret = sync_capture_fmt(ctx); - ctx->state |= (FIMC_PARAMS | FIMC_DST_FMT); -sf_unlock: - mutex_unlock(&fimc->lock); + ret = sync_capture_fmt(ctx); return ret; } @@ -623,21 +591,16 @@ static int fimc_cap_s_input(struct file *file, void *priv, struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; struct s3c_platform_fimc *pdata = fimc->pdata; - int ret; if (fimc_capture_active(ctx->fimc_dev)) return -EBUSY; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; + if (i >= FIMC_MAX_CAMIF_CLIENTS || !pdata->isp_info[i]) + return -EINVAL; - if (i >= FIMC_MAX_CAMIF_CLIENTS || !pdata->isp_info[i]) { - ret = -EINVAL; - goto si_unlock; - } if (fimc->vid_cap.sd) { - ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0); + int ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0); if (ret) err("s_power failed: %d", ret); } @@ -645,11 +608,7 @@ static int fimc_cap_s_input(struct file *file, void *priv, /* Release the attached sensor subdevice. */ fimc_subdev_unregister(fimc); - ret = fimc_isp_subdev_init(fimc, i); - -si_unlock: - mutex_unlock(&fimc->lock); - return ret; + return fimc_isp_subdev_init(fimc, i); } static int fimc_cap_g_input(struct file *file, void *priv, @@ -665,112 +624,41 @@ static int fimc_cap_g_input(struct file *file, void *priv, static int fimc_cap_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct s3c_fimc_isp_info *isp_info; struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; - int ret = -EBUSY; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; if (fimc_capture_active(fimc) || !fimc->vid_cap.sd) - goto s_unlock; + return -EBUSY; if (!(ctx->state & FIMC_DST_FMT)) { v4l2_err(&fimc->vid_cap.v4l2_dev, "Format is not set\n"); - ret = -EINVAL; - goto s_unlock; - } - - ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1); - if (ret && ret != -ENOIOCTLCMD) - goto s_unlock; - - ret = fimc_prepare_config(ctx, ctx->state); - if (ret) - goto s_unlock; - - isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index]; - fimc_hw_set_camera_type(fimc, isp_info); - fimc_hw_set_camera_source(fimc, isp_info); - fimc_hw_set_camera_offset(fimc, &ctx->s_frame); - - if (ctx->state & FIMC_PARAMS) { - ret = fimc_set_scaler_info(ctx); - if (ret) { - err("Scaler setup error"); - goto s_unlock; - } - fimc_hw_set_input_path(ctx); - fimc_hw_set_scaler(ctx); - fimc_hw_set_target_format(ctx); - fimc_hw_set_rotation(ctx); - fimc_hw_set_effect(ctx); + return -EINVAL; } - fimc_hw_set_output_path(ctx); - fimc_hw_set_out_dma(ctx); - - INIT_LIST_HEAD(&fimc->vid_cap.pending_buf_q); - INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); - fimc->vid_cap.active_buf_cnt = 0; - fimc->vid_cap.frame_count = 0; - fimc->vid_cap.buf_index = fimc_hw_get_frame_index(fimc); - - set_bit(ST_CAPT_PEND, &fimc->state); - ret = vb2_streamon(&fimc->vid_cap.vbq, type); - -s_unlock: - mutex_unlock(&fimc->lock); - return ret; + return vb2_streamon(&fimc->vid_cap.vbq, type); } static int fimc_cap_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) + enum v4l2_buf_type type) { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_vid_cap *cap = &fimc->vid_cap; - unsigned long flags; - int ret; - - spin_lock_irqsave(&fimc->slock, flags); - if (!fimc_capture_running(fimc) && !fimc_capture_pending(fimc)) { - spin_unlock_irqrestore(&fimc->slock, flags); - dbg("state: 0x%lx", fimc->state); - return -EINVAL; - } - spin_unlock_irqrestore(&fimc->slock, flags); - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - fimc_stop_capture(fimc); - ret = vb2_streamoff(&cap->vbq, type); - - mutex_unlock(&fimc->lock); - return ret; + return vb2_streamoff(&fimc->vid_cap.vbq, type); } static int fimc_cap_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_vid_cap *cap = &fimc->vid_cap; + struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap; int ret; - if (fimc_capture_active(ctx->fimc_dev)) - return -EBUSY; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; ret = vb2_reqbufs(&cap->vbq, reqbufs); if (!ret) cap->reqbufs_count = reqbufs->count; - mutex_unlock(&fimc->lock); return ret; } @@ -780,9 +668,6 @@ static int fimc_cap_querybuf(struct file *file, void *priv, struct fimc_ctx *ctx = priv; struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap; - if (fimc_capture_active(ctx->fimc_dev)) - return -EBUSY; - return vb2_querybuf(&cap->vbq, buf); } @@ -790,33 +675,16 @@ static int fimc_cap_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_vid_cap *cap = &fimc->vid_cap; - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - - ret = vb2_qbuf(&cap->vbq, buf); - - mutex_unlock(&fimc->lock); - return ret; + struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap; + return vb2_qbuf(&cap->vbq, buf); } static int fimc_cap_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { struct fimc_ctx *ctx = priv; - int ret; - - if (mutex_lock_interruptible(&ctx->fimc_dev->lock)) - return -ERESTARTSYS; - - ret = vb2_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf, + return vb2_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK); - - mutex_unlock(&ctx->fimc_dev->lock); - return ret; } static int fimc_cap_s_ctrl(struct file *file, void *priv, @@ -825,9 +693,6 @@ static int fimc_cap_s_ctrl(struct file *file, void *priv, struct fimc_ctx *ctx = priv; int ret = -EINVAL; - if (mutex_lock_interruptible(&ctx->fimc_dev->lock)) - return -ERESTARTSYS; - /* Allow any controls but 90/270 rotation while streaming */ if (!fimc_capture_active(ctx->fimc_dev) || ctrl->id != V4L2_CID_ROTATE || @@ -842,8 +707,6 @@ static int fimc_cap_s_ctrl(struct file *file, void *priv, if (ret == -EINVAL) ret = v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd, core, s_ctrl, ctrl); - - mutex_unlock(&ctx->fimc_dev->lock); return ret; } @@ -852,13 +715,10 @@ static int fimc_cap_cropcap(struct file *file, void *fh, { struct fimc_frame *f; struct fimc_ctx *ctx = fh; - struct fimc_dev *fimc = ctx->fimc_dev; if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; f = &ctx->s_frame; cr->bounds.left = 0; @@ -867,7 +727,6 @@ static int fimc_cap_cropcap(struct file *file, void *fh, cr->bounds.height = f->o_height; cr->defrect = cr->bounds; - mutex_unlock(&fimc->lock); return 0; } @@ -875,19 +734,14 @@ static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) { struct fimc_frame *f; struct fimc_ctx *ctx = file->private_data; - struct fimc_dev *fimc = ctx->fimc_dev; - - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; 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; - mutex_unlock(&fimc->lock); return 0; } @@ -906,13 +760,10 @@ static int fimc_cap_s_crop(struct file *file, void *fh, if (ret) return ret; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - if (!(ctx->state & FIMC_DST_FMT)) { v4l2_err(&fimc->vid_cap.v4l2_dev, "Capture color format not set\n"); - goto sc_unlock; + return -EINVAL; /* TODO: make sure this is the right value */ } f = &ctx->s_frame; @@ -920,17 +771,15 @@ static int fimc_cap_s_crop(struct file *file, void *fh, ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame); if (ret) { v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range"); - } else { - ret = 0; - f->offs_h = cr->c.left; - f->offs_v = cr->c.top; - f->width = cr->c.width; - f->height = cr->c.height; + return ret; } -sc_unlock: - mutex_unlock(&fimc->lock); - return ret; + f->offs_h = cr->c.left; + f->offs_v = cr->c.top; + f->width = cr->c.width; + f->height = cr->c.height; + + return 0; } @@ -1013,6 +862,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc) vfd->ioctl_ops = &fimc_capture_ioctl_ops; vfd->minor = -1; vfd->release = video_device_release; + vfd->lock = &fimc->lock; video_set_drvdata(vfd, fimc); vid_cap = &fimc->vid_cap; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index fe21097..9ebb925 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -741,22 +741,17 @@ int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_frame *frame; frame = ctx_get_frame(ctx, f->type); if (IS_ERR(frame)) return PTR_ERR(frame); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - f->fmt.pix.width = frame->width; f->fmt.pix.height = frame->height; f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.pixelformat = frame->fmt->fourcc; - mutex_unlock(&fimc->lock); return 0; } @@ -800,7 +795,8 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct fimc_fmt *fmt; u32 max_width, mod_x, mod_y, mask; - int ret, i, is_output = 0; + int i, is_output = 0; + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (ctx->state & FIMC_CTX_CAP) @@ -810,8 +806,6 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, return -EINVAL; } - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; dbg("w: %d, h: %d", pix->width, pix->height); mask = is_output ? FMT_FLAGS_M2M : FMT_FLAGS_M2M | FMT_FLAGS_CAM; @@ -819,13 +813,13 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, if (!fmt) { v4l2_err(&fimc->m2m.v4l2_dev, "Fourcc format (0x%X) invalid.\n", pix->pixelformat); - goto tf_out; + return -EINVAL; } if (pix->field == V4L2_FIELD_ANY) pix->field = V4L2_FIELD_NONE; else if (V4L2_FIELD_NONE != pix->field) - goto tf_out; + return -EINVAL; if (is_output) { max_width = variant->pix_limit->scaler_dis_w; @@ -871,11 +865,7 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, pix->plane_fmt[i].sizeimage); } - ret = 0; - -tf_out: - mutex_unlock(&fimc->lock); - return ret; + return 0; } static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv, @@ -888,45 +878,33 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv, struct v4l2_pix_format_mplane *pix; unsigned long flags; int i, ret = 0; + u32 tmp; ret = fimc_vidioc_try_fmt_mplane(file, priv, f); if (ret) return ret; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (vb2_is_streaming(vq)) { - v4l2_err(&fimc->vid_cap.v4l2_dev, "%s: queue (%d) busy\n", - __func__, f->type); - ret = -EBUSY; - goto sf_out; + v4l2_err(&fimc->m2m.v4l2_dev, "queue (%d) busy\n", f->type); + return -EBUSY; } - spin_lock_irqsave(&ctx->slock, flags); if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { frame = &ctx->s_frame; - ctx->state |= FIMC_SRC_FMT; } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { frame = &ctx->d_frame; - ctx->state |= FIMC_DST_FMT; } else { - spin_unlock_irqrestore(&ctx->slock, flags); v4l2_err(&fimc->m2m.v4l2_dev, "Wrong buffer/video queue type (%d)\n", f->type); - ret = -EINVAL; - goto sf_out; + return -EINVAL; } - spin_unlock_irqrestore(&ctx->slock, flags); pix = &f->fmt.pix_mp; frame->fmt = find_format(f, FMT_FLAGS_M2M); - if (!frame->fmt) { - ret = -EINVAL; - goto sf_out; - } + if (!frame->fmt) + return -EINVAL; for (i = 0; i < frame->fmt->colplanes; i++) frame->payload[i] = pix->plane_fmt[i].bytesperline * pix->height; @@ -942,14 +920,13 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv, frame->offs_v = 0; spin_lock_irqsave(&ctx->slock, flags); - ctx->state |= FIMC_PARAMS; + tmp = (frame == &ctx->d_frame) ? FIMC_DST_FMT : FIMC_SRC_FMT; + ctx->state |= FIMC_PARAMS | tmp; spin_unlock_irqrestore(&ctx->slock, flags); dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); -sf_out: - mutex_unlock(&fimc->lock); - return ret; + return 0; } static int fimc_m2m_reqbufs(struct file *file, void *priv, @@ -1018,11 +995,8 @@ int fimc_vidioc_queryctrl(struct file *file, void *priv, } if (ctx->state & FIMC_CTX_CAP) { - if (mutex_lock_interruptible(&ctx->fimc_dev->lock)) - return -ERESTARTSYS; - ret = v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd, + return v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd, core, queryctrl, qc); - mutex_unlock(&ctx->fimc_dev->lock); } return ret; } @@ -1032,10 +1006,6 @@ int fimc_vidioc_g_ctrl(struct file *file, void *priv, { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; - int ret = 0; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; switch (ctrl->id) { case V4L2_CID_HFLIP: @@ -1049,18 +1019,17 @@ int fimc_vidioc_g_ctrl(struct file *file, void *priv, break; default: if (ctx->state & FIMC_CTX_CAP) { - ret = v4l2_subdev_call(fimc->vid_cap.sd, core, - g_ctrl, ctrl); + return v4l2_subdev_call(fimc->vid_cap.sd, core, + g_ctrl, ctrl); } else { v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n"); - ret = -EINVAL; + return -EINVAL; } } dbg("ctrl->value= %d", ctrl->value); - mutex_unlock(&fimc->lock); - return ret; + return 0; } int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl) @@ -1151,22 +1120,17 @@ static int fimc_m2m_cropcap(struct file *file, void *fh, { struct fimc_frame *frame; struct fimc_ctx *ctx = fh; - struct fimc_dev *fimc = ctx->fimc_dev; frame = ctx_get_frame(ctx, cr->type); if (IS_ERR(frame)) return PTR_ERR(frame); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - cr->bounds.left = 0; cr->bounds.top = 0; cr->bounds.width = frame->f_width; cr->bounds.height = frame->f_height; cr->defrect = cr->bounds; - mutex_unlock(&fimc->lock); return 0; } @@ -1174,21 +1138,16 @@ static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) { struct fimc_frame *frame; struct fimc_ctx *ctx = file->private_data; - struct fimc_dev *fimc = ctx->fimc_dev; frame = ctx_get_frame(ctx, cr->type); if (IS_ERR(frame)) return PTR_ERR(frame); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - cr->c.left = frame->offs_h; cr->c.top = frame->offs_v; cr->c.width = frame->width; cr->c.height = frame->height; - mutex_unlock(&fimc->lock); return 0; } @@ -1268,9 +1227,6 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? &ctx->s_frame : &ctx->d_frame; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - spin_lock_irqsave(&ctx->slock, flags); if (~ctx->state & (FIMC_SRC_FMT | FIMC_DST_FMT)) { /* Check to see if scaling ratio is within supported range */ @@ -1280,8 +1236,8 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame); if (ret) { v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range"); - ret = -EINVAL; - goto scr_unlock; + spin_unlock_irqrestore(&ctx->slock, flags); + return -EINVAL; } } ctx->state |= FIMC_PARAMS; @@ -1291,9 +1247,7 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) f->width = cr->c.width; f->height = cr->c.height; -scr_unlock: spin_unlock_irqrestore(&ctx->slock, flags); - mutex_unlock(&fimc->lock); return 0; } @@ -1364,10 +1318,6 @@ static int fimc_m2m_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); struct fimc_ctx *ctx = NULL; - int err = 0; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; dbg("pid: %d, state: 0x%lx, refcnt: %d", task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); @@ -1376,19 +1326,15 @@ static int fimc_m2m_open(struct file *file) * Return if the corresponding video capture node * is already opened. */ - if (fimc->vid_cap.refcnt > 0) { - err = -EBUSY; - goto err_unlock; - } + if (fimc->vid_cap.refcnt > 0) + return -EBUSY; fimc->m2m.refcnt++; set_bit(ST_OUTDMA_RUN, &fimc->state); ctx = kzalloc(sizeof *ctx, GFP_KERNEL); - if (!ctx) { - err = -ENOMEM; - goto err_unlock; - } + if (!ctx) + return -ENOMEM; file->private_data = ctx; ctx->fimc_dev = fimc; @@ -1404,13 +1350,12 @@ static int fimc_m2m_open(struct file *file) ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { - err = PTR_ERR(ctx->m2m_ctx); + int err = PTR_ERR(ctx->m2m_ctx); kfree(ctx); + return err; } -err_unlock: - mutex_unlock(&fimc->lock); - return err; + return 0; } static int fimc_m2m_release(struct file *file) @@ -1418,8 +1363,6 @@ static int fimc_m2m_release(struct file *file) struct fimc_ctx *ctx = file->private_data; struct fimc_dev *fimc = ctx->fimc_dev; - mutex_lock(&fimc->lock); - dbg("pid: %d, state: 0x%lx, refcnt= %d", task_pid_nr(current), fimc->state, fimc->m2m.refcnt); @@ -1428,7 +1371,6 @@ static int fimc_m2m_release(struct file *file) if (--fimc->m2m.refcnt <= 0) clear_bit(ST_OUTDMA_RUN, &fimc->state); - mutex_unlock(&fimc->lock); return 0; } @@ -1494,6 +1436,7 @@ static int fimc_register_m2m_device(struct fimc_dev *fimc) vfd->ioctl_ops = &fimc_m2m_ioctl_ops; vfd->minor = -1; vfd->release = video_device_release; + vfd->lock = &fimc->lock; snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev)); -- cgit v0.10.2 From df7e09a351199ad9a70eb9ae3b072cc4fc59a9bb Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 27 Dec 2010 14:42:15 -0300 Subject: [media] s5p-fimc: Rename s3c_fimc* to s5p_fimc* Change s3c_fimc.h header file name to s5p_fimc.h, replace s3c_fimc_* names with s5p_fimc_*. s3c_fimc need to be reserved for S3C series and s5p-fimc driver will not cover CAMIF devices in S3C SoC series. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 3b208f8..2dc6e82 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -33,7 +33,7 @@ #include "fimc-core.h" static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc, - struct s3c_fimc_isp_info *isp_info) + struct s5p_fimc_isp_info *isp_info) { struct i2c_adapter *i2c_adap; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; @@ -86,8 +86,8 @@ static void fimc_subdev_unregister(struct fimc_dev *fimc) static int fimc_subdev_attach(struct fimc_dev *fimc, int index) { struct fimc_vid_cap *vid_cap = &fimc->vid_cap; - struct s3c_platform_fimc *pdata = fimc->pdata; - struct s3c_fimc_isp_info *isp_info; + struct s5p_platform_fimc *pdata = fimc->pdata; + struct s5p_fimc_isp_info *isp_info; struct v4l2_subdev *sd; int i; @@ -115,7 +115,7 @@ static int fimc_subdev_attach(struct fimc_dev *fimc, int index) static int fimc_isp_subdev_init(struct fimc_dev *fimc, int index) { - struct s3c_fimc_isp_info *isp_info; + struct s5p_fimc_isp_info *isp_info; int ret; ret = fimc_subdev_attach(fimc, index); @@ -221,7 +221,7 @@ static int start_streaming(struct vb2_queue *q) { struct fimc_ctx *ctx = q->drv_priv; struct fimc_dev *fimc = ctx->fimc_dev; - struct s3c_fimc_isp_info *isp_info; + struct s5p_fimc_isp_info *isp_info; int ret; ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1); @@ -570,8 +570,8 @@ static int fimc_cap_enum_input(struct file *file, void *priv, struct v4l2_input *i) { struct fimc_ctx *ctx = priv; - struct s3c_platform_fimc *pldata = ctx->fimc_dev->pdata; - struct s3c_fimc_isp_info *isp_info; + struct s5p_platform_fimc *pldata = ctx->fimc_dev->pdata; + struct s5p_fimc_isp_info *isp_info; if (i->index >= FIMC_MAX_CAMIF_CLIENTS) return -EINVAL; @@ -590,7 +590,7 @@ static int fimc_cap_s_input(struct file *file, void *priv, { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; - struct s3c_platform_fimc *pdata = fimc->pdata; + struct s5p_platform_fimc *pdata = fimc->pdata; if (fimc_capture_active(ctx->fimc_dev)) return -EBUSY; diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 187af60..b23d492 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "regs-fimc.h" @@ -421,7 +421,7 @@ struct fimc_dev { spinlock_t slock; struct mutex lock; struct platform_device *pdev; - struct s3c_platform_fimc *pdata; + struct s5p_platform_fimc *pdata; struct samsung_fimc_variant *variant; int id; struct clk *clock[NUM_FIMC_CLOCKS]; @@ -583,12 +583,12 @@ void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, int index); int fimc_hw_set_camera_source(struct fimc_dev *fimc, - struct s3c_fimc_isp_info *cam); + struct s5p_fimc_isp_info *cam); int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, - struct s3c_fimc_isp_info *cam); + struct s5p_fimc_isp_info *cam); int fimc_hw_set_camera_type(struct fimc_dev *fimc, - struct s3c_fimc_isp_info *cam); + struct s5p_fimc_isp_info *cam); /* -----------------------------------------------------*/ /* fimc-core.c */ diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 5ed8f06..ae33bc1 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include "fimc-core.h" @@ -532,7 +532,7 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev, } int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, - struct s3c_fimc_isp_info *cam) + struct s5p_fimc_isp_info *cam) { u32 cfg = readl(fimc->regs + S5P_CIGCTRL); @@ -557,7 +557,7 @@ int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, } int fimc_hw_set_camera_source(struct fimc_dev *fimc, - struct s3c_fimc_isp_info *cam) + struct s5p_fimc_isp_info *cam) { struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; u32 cfg = 0; @@ -624,7 +624,7 @@ int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) } int fimc_hw_set_camera_type(struct fimc_dev *fimc, - struct s3c_fimc_isp_info *cam) + struct s5p_fimc_isp_info *cam) { u32 cfg, tmp; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; diff --git a/include/media/s3c_fimc.h b/include/media/s3c_fimc.h deleted file mode 100644 index ca1b673..0000000 --- a/include/media/s3c_fimc.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Samsung S5P SoC camera interface driver header - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd - * Author: Sylwester Nawrocki, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef S3C_FIMC_H_ -#define S3C_FIMC_H_ - -enum cam_bus_type { - FIMC_ITU_601 = 1, - FIMC_ITU_656, - FIMC_MIPI_CSI2, - FIMC_LCD_WB, /* FIFO link from LCD mixer */ -}; - -#define FIMC_CLK_INV_PCLK (1 << 0) -#define FIMC_CLK_INV_VSYNC (1 << 1) -#define FIMC_CLK_INV_HREF (1 << 2) -#define FIMC_CLK_INV_HSYNC (1 << 3) - -struct i2c_board_info; - -/** - * struct s3c_fimc_isp_info - image sensor information required for host - * interace configuration. - * - * @board_info: pointer to I2C subdevice's board info - * @bus_type: determines bus type, MIPI, ITU-R BT.601 etc. - * @i2c_bus_num: i2c control bus id the sensor is attached to - * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) - * @bus_width: camera data bus width in bits - * @flags: flags defining bus signals polarity inversion (High by default) - */ -struct s3c_fimc_isp_info { - struct i2c_board_info *board_info; - enum cam_bus_type bus_type; - u16 i2c_bus_num; - u16 mux_id; - u16 bus_width; - u16 flags; -}; - - -#define FIMC_MAX_CAMIF_CLIENTS 2 - -/** - * struct s3c_platform_fimc - camera host interface platform data - * - * @isp_info: properties of camera sensor required for host interface setup - */ -struct s3c_platform_fimc { - struct s3c_fimc_isp_info *isp_info[FIMC_MAX_CAMIF_CLIENTS]; -}; -#endif /* S3C_FIMC_H_ */ diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h new file mode 100644 index 0000000..eb8793f --- /dev/null +++ b/include/media/s5p_fimc.h @@ -0,0 +1,60 @@ +/* + * Samsung S5P SoC camera interface driver header + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd + * Author: Sylwester Nawrocki, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef S5P_FIMC_H_ +#define S5P_FIMC_H_ + +enum cam_bus_type { + FIMC_ITU_601 = 1, + FIMC_ITU_656, + FIMC_MIPI_CSI2, + FIMC_LCD_WB, /* FIFO link from LCD mixer */ +}; + +#define FIMC_CLK_INV_PCLK (1 << 0) +#define FIMC_CLK_INV_VSYNC (1 << 1) +#define FIMC_CLK_INV_HREF (1 << 2) +#define FIMC_CLK_INV_HSYNC (1 << 3) + +struct i2c_board_info; + +/** + * struct s5p_fimc_isp_info - image sensor information required for host + * interace configuration. + * + * @board_info: pointer to I2C subdevice's board info + * @bus_type: determines bus type, MIPI, ITU-R BT.601 etc. + * @i2c_bus_num: i2c control bus id the sensor is attached to + * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) + * @bus_width: camera data bus width in bits + * @flags: flags defining bus signals polarity inversion (High by default) + */ +struct s5p_fimc_isp_info { + struct i2c_board_info *board_info; + enum cam_bus_type bus_type; + u16 i2c_bus_num; + u16 mux_id; + u16 bus_width; + u16 flags; +}; + + +#define FIMC_MAX_CAMIF_CLIENTS 2 + +/** + * struct s5p_platform_fimc - camera host interface platform data + * + * @isp_info: properties of camera sensor required for host interface setup + */ +struct s5p_platform_fimc { + struct s5p_fimc_isp_info *isp_info[FIMC_MAX_CAMIF_CLIENTS]; +}; +#endif /* S5P_FIMC_H_ */ -- cgit v0.10.2 From 3d0ce7ed037af9bafec498246d0e4af8302d993a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 27 Dec 2010 15:02:16 -0300 Subject: [media] s5p-fimc: Derive camera bus width from mediabus pixelcode Remove bus_width from s5p_fimc_isp_info data structure. Determine camera data bus width based on mediabus pixel format. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index ae33bc1..6366011 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -561,37 +561,42 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, { struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; u32 cfg = 0; + u32 bus_width; + int i; + + static const struct { + u32 pixelcode; + u32 cisrcfmt; + u16 bus_width; + } pix_desc[] = { + { V4L2_MBUS_FMT_YUYV8_2X8, S5P_CISRCFMT_ORDER422_YCBYCR, 8 }, + { V4L2_MBUS_FMT_YVYU8_2X8, S5P_CISRCFMT_ORDER422_YCRYCB, 8 }, + { V4L2_MBUS_FMT_VYUY8_2X8, S5P_CISRCFMT_ORDER422_CRYCBY, 8 }, + { V4L2_MBUS_FMT_UYVY8_2X8, S5P_CISRCFMT_ORDER422_CBYCRY, 8 }, + /* TODO: Add pixel codes for 16-bit bus width */ + }; if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { + for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { + if (fimc->vid_cap.fmt.code == pix_desc[i].pixelcode) { + cfg = pix_desc[i].cisrcfmt; + bus_width = pix_desc[i].bus_width; + break; + } + } - switch (fimc->vid_cap.fmt.code) { - case V4L2_MBUS_FMT_YUYV8_2X8: - cfg = S5P_CISRCFMT_ORDER422_YCBYCR; - break; - case V4L2_MBUS_FMT_YVYU8_2X8: - cfg = S5P_CISRCFMT_ORDER422_YCRYCB; - break; - case V4L2_MBUS_FMT_VYUY8_2X8: - cfg = S5P_CISRCFMT_ORDER422_CRYCBY; - break; - case V4L2_MBUS_FMT_UYVY8_2X8: - cfg = S5P_CISRCFMT_ORDER422_CBYCRY; - break; - default: - err("camera image format not supported: %d", - fimc->vid_cap.fmt.code); + if (i == ARRAY_SIZE(pix_desc)) { + v4l2_err(&fimc->vid_cap.v4l2_dev, + "Camera color format not supported: %d\n", + fimc->vid_cap.fmt.code); return -EINVAL; } if (cam->bus_type == FIMC_ITU_601) { - if (cam->bus_width == 8) { + if (bus_width == 8) cfg |= S5P_CISRCFMT_ITU601_8BIT; - } else if (cam->bus_width == 16) { + else if (bus_width == 16) cfg |= S5P_CISRCFMT_ITU601_16BIT; - } else { - err("invalid bus width: %d", cam->bus_width); - return -EINVAL; - } } /* else defaults to ITU-R BT.656 8-bit */ } diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index eb8793f..d30b9dee 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -34,7 +34,6 @@ struct i2c_board_info; * @bus_type: determines bus type, MIPI, ITU-R BT.601 etc. * @i2c_bus_num: i2c control bus id the sensor is attached to * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) - * @bus_width: camera data bus width in bits * @flags: flags defining bus signals polarity inversion (High by default) */ struct s5p_fimc_isp_info { @@ -42,7 +41,6 @@ struct s5p_fimc_isp_info { enum cam_bus_type bus_type; u16 i2c_bus_num; u16 mux_id; - u16 bus_width; u16 flags; }; -- cgit v0.10.2 From e9654ef28740867c0aa151bbaec93f090f60cd24 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 27 Dec 2010 15:05:58 -0300 Subject: [media] s5p-fimc: Enable interworking without subdev s_stream Prevent VIDIOC_STREAMON failing when s_stream callback is not implemented by a sensor subdev driver. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 2dc6e82..c6689a2 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -191,7 +191,8 @@ static int fimc_stop_capture(struct fimc_dev *fimc) FIMC_SHUTDOWN_TIMEOUT); ret = v4l2_subdev_call(cap->sd, video, s_stream, 0); - if (ret) + + if (ret && ret != -ENOIOCTLCMD) v4l2_err(&fimc->vid_cap.v4l2_dev, "s_stream(0) failed\n"); spin_lock_irqsave(&fimc->slock, flags); -- cgit v0.10.2 From d9160afd31565e44ebbd909b283baad8c9a3c224 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 8 Dec 2010 12:48:23 -0300 Subject: [media] s5p-fimc: Use default input DMA burst count Increase the input DMA "successive burst count" to default value 4 to improve DMA performance. Minor cleanup. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 6366011..c4703b5 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -417,12 +417,10 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) | S5P_MSCTRL_C_INT_IN_MASK | S5P_MSCTRL_2P_IN_ORDER_MASK); - cfg |= (S5P_MSCTRL_FRAME_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY); + cfg |= S5P_MSCTRL_IN_BURST_COUNT(4) | S5P_MSCTRL_INPUT_MEMORY; switch (frame->fmt->color) { - case S5P_FIMC_RGB565: - case S5P_FIMC_RGB666: - case S5P_FIMC_RGB888: + case S5P_FIMC_RGB565...S5P_FIMC_RGB888: cfg |= S5P_MSCTRL_INFORMAT_RGB; break; case S5P_FIMC_YCBCR420: @@ -434,10 +432,7 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; break; - case S5P_FIMC_YCBYCR422: - case S5P_FIMC_YCRYCB422: - case S5P_FIMC_CBYCRY422: - case S5P_FIMC_CRYCBY422: + case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422: if (frame->fmt->colplanes == 1) { cfg |= ctx->in_order_1p | S5P_MSCTRL_INFORMAT_YCBCR422_1P; diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h index 57e33f8..74ca705 100644 --- a/drivers/media/video/s5p-fimc/regs-fimc.h +++ b/drivers/media/video/s5p-fimc/regs-fimc.h @@ -210,7 +210,7 @@ /* Input DMA control */ #define S5P_MSCTRL 0xfc -#define S5P_MSCTRL_IN_BURST_COUNT_MASK (3 << 24) +#define S5P_MSCTRL_IN_BURST_COUNT_MASK (0xF << 24) #define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16) #define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16 #define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15) @@ -237,7 +237,7 @@ #define S5P_MSCTRL_INFORMAT_RGB (3 << 1) #define S5P_MSCTRL_INFORMAT_MASK (3 << 1) #define S5P_MSCTRL_ENVID (1 << 0) -#define S5P_MSCTRL_FRAME_COUNT(x) ((x) << 24) +#define S5P_MSCTRL_IN_BURST_COUNT(x) ((x) << 24) /* Output DMA Y/Cb/Cr offset */ #define S5P_CIOYOFF 0x168 -- cgit v0.10.2 From ac75934cc644051dc1b33b234448ea4a0990f31f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 27 Dec 2010 14:47:32 -0300 Subject: [media] s5p-fimc: Enable simultaneous rotation and flipping Map all (0, 90, 180, 270) deg counterclockwise rotation and horizontal and vertical flip controls to (0, 90) deg rotation, horizontal and vertical flip transformations available in the device. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 9ebb925..b8d59f4 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1055,13 +1055,6 @@ int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl) struct fimc_dev *fimc = ctx->fimc_dev; unsigned long flags; - if (ctx->rotation != 0 && - (ctrl->id == V4L2_CID_HFLIP || ctrl->id == V4L2_CID_VFLIP)) { - v4l2_err(&fimc->m2m.v4l2_dev, - "Simultaneous flip and rotation is not supported\n"); - return -EINVAL; - } - spin_lock_irqsave(&ctx->slock, flags); switch (ctrl->id) { @@ -1102,7 +1095,7 @@ int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl) } static int fimc_m2m_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) + struct v4l2_control *ctrl) { struct fimc_ctx *ctx = priv; int ret = 0; diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index c4703b5..c14c831 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -37,11 +37,11 @@ void fimc_hw_reset(struct fimc_dev *dev) writel(cfg, dev->regs + S5P_CIGCTRL); } -static u32 fimc_hw_get_in_flip(u32 ctx_flip) +static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx) { u32 flip = S5P_MSCTRL_FLIP_NORMAL; - switch (ctx_flip) { + switch (ctx->flip) { case FLIP_X_AXIS: flip = S5P_MSCTRL_FLIP_X_MIRROR; break; @@ -51,16 +51,20 @@ static u32 fimc_hw_get_in_flip(u32 ctx_flip) case FLIP_XY_AXIS: flip = S5P_MSCTRL_FLIP_180; break; + default: + break; } + if (ctx->rotation <= 90) + return flip; - return flip; + return (flip ^ S5P_MSCTRL_FLIP_180) & S5P_MSCTRL_FLIP_180; } -static u32 fimc_hw_get_target_flip(u32 ctx_flip) +static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx) { u32 flip = S5P_CITRGFMT_FLIP_NORMAL; - switch (ctx_flip) { + switch (ctx->flip) { case FLIP_X_AXIS: flip = S5P_CITRGFMT_FLIP_X_MIRROR; break; @@ -70,11 +74,13 @@ static u32 fimc_hw_get_target_flip(u32 ctx_flip) case FLIP_XY_AXIS: flip = S5P_CITRGFMT_FLIP_180; break; - case FLIP_NONE: + default: break; - } - return flip; + if (ctx->rotation <= 90) + return flip; + + return (flip ^ S5P_CITRGFMT_FLIP_180) & S5P_CITRGFMT_FLIP_180; } void fimc_hw_set_rotation(struct fimc_ctx *ctx) @@ -84,10 +90,7 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx) cfg = readl(dev->regs + S5P_CITRGFMT); cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90 | - S5P_CITRGFMT_FLIP_180); - - flip = readl(dev->regs + S5P_MSCTRL); - flip &= ~S5P_MSCTRL_FLIP_MASK; + S5P_CITRGFMT_FLIP_180); /* * The input and output rotator cannot work simultaneously. @@ -95,26 +98,22 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx) * in direct fifo output mode. */ if (ctx->rotation == 90 || ctx->rotation == 270) { - if (ctx->out_path == FIMC_LCDFIFO) { - cfg |= S5P_CITRGFMT_INROT90; - if (ctx->rotation == 270) - flip |= S5P_MSCTRL_FLIP_180; - } else { - cfg |= S5P_CITRGFMT_OUTROT90; - if (ctx->rotation == 270) - cfg |= S5P_CITRGFMT_FLIP_180; - } - } else if (ctx->rotation == 180) { if (ctx->out_path == FIMC_LCDFIFO) - flip |= S5P_MSCTRL_FLIP_180; + cfg |= S5P_CITRGFMT_INROT90; else - cfg |= S5P_CITRGFMT_FLIP_180; + cfg |= S5P_CITRGFMT_OUTROT90; } - if (ctx->rotation == 180 || ctx->rotation == 270) - writel(flip, dev->regs + S5P_MSCTRL); - cfg |= fimc_hw_get_target_flip(ctx->flip); - writel(cfg, dev->regs + S5P_CITRGFMT); + if (ctx->out_path == FIMC_DMA) { + cfg |= fimc_hw_get_target_flip(ctx); + writel(cfg, dev->regs + S5P_CITRGFMT); + } else { + /* LCD FIFO path */ + flip = readl(dev->regs + S5P_MSCTRL); + flip &= ~S5P_MSCTRL_FLIP_MASK; + flip |= fimc_hw_get_in_flip(ctx); + writel(flip, dev->regs + S5P_MSCTRL); + } } void fimc_hw_set_target_format(struct fimc_ctx *ctx) @@ -131,18 +130,13 @@ void fimc_hw_set_target_format(struct fimc_ctx *ctx) S5P_CITRGFMT_VSIZE_MASK); switch (frame->fmt->color) { - case S5P_FIMC_RGB565: - case S5P_FIMC_RGB666: - case S5P_FIMC_RGB888: + case S5P_FIMC_RGB565...S5P_FIMC_RGB888: cfg |= S5P_CITRGFMT_RGB; break; case S5P_FIMC_YCBCR420: cfg |= S5P_CITRGFMT_YCBCR420; break; - case S5P_FIMC_YCBYCR422: - case S5P_FIMC_YCRYCB422: - case S5P_FIMC_CBYCRY422: - case S5P_FIMC_CRYCBY422: + case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422: if (frame->fmt->colplanes == 1) cfg |= S5P_CITRGFMT_YCBCR422_1P; else @@ -410,8 +404,7 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) /* Set the input DMA to process single frame only. */ cfg = readl(dev->regs + S5P_MSCTRL); - cfg &= ~(S5P_MSCTRL_FLIP_MASK - | S5P_MSCTRL_INFORMAT_MASK + cfg &= ~(S5P_MSCTRL_INFORMAT_MASK | S5P_MSCTRL_IN_BURST_COUNT_MASK | S5P_MSCTRL_INPUT_MASK | S5P_MSCTRL_C_INT_IN_MASK @@ -450,13 +443,6 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) break; } - /* - * Input DMA flip mode (and rotation). - * Do not allow simultaneous rotation and flipping. - */ - if (!ctx->rotation && ctx->out_path == FIMC_LCDFIFO) - cfg |= fimc_hw_get_in_flip(ctx->flip); - writel(cfg, dev->regs + S5P_MSCTRL); /* Input/output DMA linear/tiled mode. */ -- cgit v0.10.2 From a25be18dfb6e1b172498a9f6c9793d67057000b0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 27 Dec 2010 15:34:43 -0300 Subject: [media] s5p-fimc: Add control of the external sensor clock Manage the camera sensor clock in the host driver rather than leaving this task for sensor drivers. The clock frequency must be passed in the sensor's and host driver's platform data. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index c6689a2..03ddf5d 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -113,26 +113,43 @@ static int fimc_subdev_attach(struct fimc_dev *fimc, int index) return -ENODEV; } -static int fimc_isp_subdev_init(struct fimc_dev *fimc, int index) +static int fimc_isp_subdev_init(struct fimc_dev *fimc, unsigned int index) { struct s5p_fimc_isp_info *isp_info; int ret; + if (index >= FIMC_MAX_CAMIF_CLIENTS) + return -EINVAL; + + isp_info = fimc->pdata->isp_info[index]; + if (!isp_info) + return -EINVAL; + + if (isp_info->clk_frequency) + clk_set_rate(fimc->clock[CLK_CAM], isp_info->clk_frequency); + + ret = clk_enable(fimc->clock[CLK_CAM]); + if (ret) + return ret; + ret = fimc_subdev_attach(fimc, index); if (ret) return ret; - isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index]; ret = fimc_hw_set_camera_polarity(fimc, isp_info); - if (!ret) { - ret = v4l2_subdev_call(fimc->vid_cap.sd, core, - s_power, 1); - if (!ret) - return ret; - } + if (ret) + return ret; + + ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 1); + if (!ret) + return ret; + /* enabling power failed so unregister subdev */ fimc_subdev_unregister(fimc); - err("ISP initialization failed: %d", ret); + + v4l2_err(&fimc->vid_cap.v4l2_dev, "ISP initialization failed: %d\n", + ret); + return ret; } @@ -190,10 +207,7 @@ static int fimc_stop_capture(struct fimc_dev *fimc) test_bit(ST_CAPT_SHUT, &fimc->state), FIMC_SHUTDOWN_TIMEOUT); - ret = v4l2_subdev_call(cap->sd, video, s_stream, 0); - - if (ret && ret != -ENOIOCTLCMD) - v4l2_err(&fimc->vid_cap.v4l2_dev, "s_stream(0) failed\n"); + v4l2_subdev_call(cap->sd, video, s_stream, 0); spin_lock_irqsave(&fimc->slock, flags); fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND | @@ -408,7 +422,7 @@ static int fimc_capture_open(struct file *file) return -EBUSY; if (++fimc->vid_cap.refcnt == 1) { - ret = fimc_isp_subdev_init(fimc, -1); + ret = fimc_isp_subdev_init(fimc, 0); if (ret) { fimc->vid_cap.refcnt--; return -EIO; @@ -433,6 +447,7 @@ static int fimc_capture_close(struct file *file) v4l2_err(&fimc->vid_cap.v4l2_dev, "releasing ISP\n"); v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0); + clk_disable(fimc->clock[CLK_CAM]); fimc_subdev_unregister(fimc); } @@ -604,6 +619,8 @@ static int fimc_cap_s_input(struct file *file, void *priv, int ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0); if (ret) err("s_power failed: %d", ret); + + clk_disable(fimc->clock[CLK_CAM]); } /* Release the attached sensor subdevice. */ diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index b8d59f4..db249e6 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -30,7 +30,9 @@ #include "fimc-core.h" -static char *fimc_clock_name[NUM_FIMC_CLOCKS] = { "sclk_fimc", "fimc" }; +static char *fimc_clocks[MAX_FIMC_CLOCKS] = { + "sclk_fimc", "fimc", "sclk_cam" +}; static struct fimc_fmt fimc_formats[] = { { @@ -1478,7 +1480,7 @@ static void fimc_unregister_m2m_device(struct fimc_dev *fimc) static void fimc_clk_release(struct fimc_dev *fimc) { int i; - for (i = 0; i < NUM_FIMC_CLOCKS; i++) { + for (i = 0; i < fimc->num_clocks; i++) { if (fimc->clock[i]) { clk_disable(fimc->clock[i]); clk_put(fimc->clock[i]); @@ -1489,15 +1491,16 @@ static void fimc_clk_release(struct fimc_dev *fimc) static int fimc_clk_get(struct fimc_dev *fimc) { int i; - for (i = 0; i < NUM_FIMC_CLOCKS; i++) { - fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clock_name[i]); - if (IS_ERR(fimc->clock[i])) { - dev_err(&fimc->pdev->dev, - "failed to get fimc clock: %s\n", - fimc_clock_name[i]); - return -ENXIO; + 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])) { + clk_enable(fimc->clock[i]); + continue; } - clk_enable(fimc->clock[i]); + dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n", + fimc_clocks[i]); + return -ENXIO; } return 0; } @@ -1508,6 +1511,7 @@ static int fimc_probe(struct platform_device *pdev) struct resource *res; struct samsung_fimc_driverdata *drv_data; int ret = 0; + int cap_input_index = -1; dev_dbg(&pdev->dev, "%s():\n", __func__); @@ -1557,10 +1561,26 @@ static int fimc_probe(struct platform_device *pdev) goto err_req_region; } + fimc->num_clocks = MAX_FIMC_CLOCKS - 1; + /* + * Check if vide capture node needs to be registered for this device + * instance. + */ + if (fimc->pdata) { + int i; + for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i) + if (fimc->pdata->isp_info[i]) + break; + if (i < FIMC_MAX_CAMIF_CLIENTS) { + cap_input_index = i; + fimc->num_clocks++; + } + } + ret = fimc_clk_get(fimc); if (ret) goto err_regs_unmap; - clk_set_rate(fimc->clock[0], drv_data->lclk_frequency); + clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { @@ -1590,19 +1610,12 @@ static int fimc_probe(struct platform_device *pdev) goto err_irq; /* At least one camera sensor is required to register capture node */ - if (fimc->pdata) { - int i; - for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i) - if (fimc->pdata->isp_info[i]) - break; - - if (i < FIMC_MAX_CAMIF_CLIENTS) { - ret = fimc_register_capture_device(fimc); - if (ret) - goto err_m2m; - } + if (cap_input_index >= 0) { + ret = fimc_register_capture_device(fimc); + if (ret) + goto err_m2m; + clk_disable(fimc->clock[CLK_CAM]); } - /* * Exclude the additional output DMA address registers by masking * them out on HW revisions that provide extended capabilites. diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index b23d492..1c6aa69 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -37,7 +37,7 @@ /* Time to wait for next frame VSYNC interrupt while stopping operation. */ #define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) -#define NUM_FIMC_CLOCKS 2 +#define MAX_FIMC_CLOCKS 3 #define MODULE_NAME "s5p-fimc" #define FIMC_MAX_DEVS 4 #define FIMC_MAX_OUT_BUFS 4 @@ -45,7 +45,13 @@ #define SCALER_MAX_VRATIO 64 #define DMA_MIN_SIZE 8 -/* FIMC device state flags */ +/* indices to the clocks array */ +enum { + CLK_BUS, + CLK_GATE, + CLK_CAM, +}; + enum fimc_dev_flags { /* for m2m node */ ST_IDLE, @@ -407,7 +413,8 @@ struct fimc_ctx; * @lock: the mutex protecting this data structure * @pdev: pointer to the FIMC platform device * @pdata: pointer to the device platform data - * @id: FIMC device index (0..2) + * @id: FIMC device index (0..FIMC_MAX_DEVS) + * @num_clocks: the number of clocks managed by this device instance * @clock[]: the clocks required for FIMC operation * @regs: the mapped hardware registers * @regs_res: the resource claimed for IO registers @@ -423,8 +430,9 @@ struct fimc_dev { struct platform_device *pdev; struct s5p_platform_fimc *pdata; struct samsung_fimc_variant *variant; - int id; - struct clk *clock[NUM_FIMC_CLOCKS]; + u16 id; + u16 num_clocks; + struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; struct resource *regs_res; int irq; diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index d30b9dee..0d457ca 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -31,6 +31,7 @@ struct i2c_board_info; * interace configuration. * * @board_info: pointer to I2C subdevice's board info + * @clk_frequency: frequency of the clock the host interface provides to sensor * @bus_type: determines bus type, MIPI, ITU-R BT.601 etc. * @i2c_bus_num: i2c control bus id the sensor is attached to * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) @@ -38,6 +39,7 @@ struct i2c_board_info; */ struct s5p_fimc_isp_info { struct i2c_board_info *board_info; + unsigned long clk_frequency; enum cam_bus_type bus_type; u16 i2c_bus_num; u16 mux_id; -- cgit v0.10.2 From 10038bea7a729bcb8c51a2c856f5b58b33646d65 Mon Sep 17 00:00:00 2001 From: Hyunwoong Kim Date: Wed, 22 Dec 2010 00:56:05 -0300 Subject: [media] s5p-fimc: fix the value of YUV422 1-plane formats Some color formats are mismatched in s5p-fimc driver. CIOCTRL[1:0], order422_out, should be set 2b'00 not 2b'11 to use V4L2_PIX_FMT_YUYV. Because in V4L2 standard V4L2_PIX_FMT_YUYV means "start + 0: Y'00 Cb00 Y'01 Cr00 Y'02 Cb01 Y'03 Cr01". According to datasheet 2b'00 is right value for V4L2_PIX_FMT_YUYV. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index db249e6..70d6b4c 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -450,34 +450,34 @@ static void fimc_set_yuv_order(struct fimc_ctx *ctx) /* Set order for 1 plane input formats. */ switch (ctx->s_frame.fmt->color) { case S5P_FIMC_YCRYCB422: - ctx->in_order_1p = S5P_FIMC_IN_YCRYCB; + ctx->in_order_1p = S5P_MSCTRL_ORDER422_CBYCRY; break; case S5P_FIMC_CBYCRY422: - ctx->in_order_1p = S5P_FIMC_IN_CBYCRY; + ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCRYCB; break; case S5P_FIMC_CRYCBY422: - ctx->in_order_1p = S5P_FIMC_IN_CRYCBY; + ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCBYCR; break; case S5P_FIMC_YCBYCR422: default: - ctx->in_order_1p = S5P_FIMC_IN_YCBYCR; + ctx->in_order_1p = S5P_MSCTRL_ORDER422_CRYCBY; break; } dbg("ctx->in_order_1p= %d", ctx->in_order_1p); switch (ctx->d_frame.fmt->color) { case S5P_FIMC_YCRYCB422: - ctx->out_order_1p = S5P_FIMC_OUT_YCRYCB; + ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CBYCRY; break; case S5P_FIMC_CBYCRY422: - ctx->out_order_1p = S5P_FIMC_OUT_CBYCRY; + ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCRYCB; break; case S5P_FIMC_CRYCBY422: - ctx->out_order_1p = S5P_FIMC_OUT_CRYCBY; + ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCBYCR; break; case S5P_FIMC_YCBYCR422: default: - ctx->out_order_1p = S5P_FIMC_OUT_YCBYCR; + ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CRYCBY; break; } dbg("ctx->out_order_1p= %d", ctx->out_order_1p); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 1c6aa69..562d15d 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -99,18 +99,6 @@ enum fimc_color_fmt { #define fimc_fmt_is_rgb(x) ((x) & 0x10) -/* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats. */ -#define S5P_FIMC_OUT_CRYCBY S5P_CIOCTRL_ORDER422_CRYCBY -#define S5P_FIMC_OUT_CBYCRY S5P_CIOCTRL_ORDER422_YCRYCB -#define S5P_FIMC_OUT_YCRYCB S5P_CIOCTRL_ORDER422_CBYCRY -#define S5P_FIMC_OUT_YCBYCR S5P_CIOCTRL_ORDER422_YCBYCR - -/* Input Y/Cb/Cr components order for 1 plane YCbCr 4:2:2 color formats. */ -#define S5P_FIMC_IN_CRYCBY S5P_MSCTRL_ORDER422_CRYCBY -#define S5P_FIMC_IN_CBYCRY S5P_MSCTRL_ORDER422_YCRYCB -#define S5P_FIMC_IN_YCRYCB S5P_MSCTRL_ORDER422_CBYCRY -#define S5P_FIMC_IN_YCBYCR S5P_MSCTRL_ORDER422_YCBYCR - /* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */ #define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h index 74ca705..fe19b4b 100644 --- a/drivers/media/video/s5p-fimc/regs-fimc.h +++ b/drivers/media/video/s5p-fimc/regs-fimc.h @@ -98,8 +98,8 @@ #define S5P_CIOCTRL 0x4c #define S5P_CIOCTRL_ORDER422_MASK (3 << 0) #define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0) -#define S5P_CIOCTRL_ORDER422_YCRYCB (1 << 0) -#define S5P_CIOCTRL_ORDER422_CBYCRY (2 << 0) +#define S5P_CIOCTRL_ORDER422_CBYCRY (1 << 0) +#define S5P_CIOCTRL_ORDER422_YCRYCB (2 << 0) #define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0) #define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2) #define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3) @@ -223,10 +223,10 @@ #define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13) #define S5P_MSCTRL_FLIP_180 (3 << 13) #define S5P_MSCTRL_ORDER422_SHIFT 4 -#define S5P_MSCTRL_ORDER422_CRYCBY (0 << 4) -#define S5P_MSCTRL_ORDER422_YCRYCB (1 << 4) -#define S5P_MSCTRL_ORDER422_CBYCRY (2 << 4) -#define S5P_MSCTRL_ORDER422_YCBYCR (3 << 4) +#define S5P_MSCTRL_ORDER422_YCBYCR (0 << 4) +#define S5P_MSCTRL_ORDER422_CBYCRY (1 << 4) +#define S5P_MSCTRL_ORDER422_YCRYCB (2 << 4) +#define S5P_MSCTRL_ORDER422_CRYCBY (3 << 4) #define S5P_MSCTRL_ORDER422_MASK (3 << 4) #define S5P_MSCTRL_INPUT_EXTCAM (0 << 3) #define S5P_MSCTRL_INPUT_MEMORY (1 << 3) -- cgit v0.10.2 From b241c6d6f4ceddddfd8097c702dcfdd6b38fbe18 Mon Sep 17 00:00:00 2001 From: Hyunwoong Kim Date: Tue, 28 Dec 2010 11:27:13 -0300 Subject: [media] s5p-fimc: Configure scaler registers depending on FIMC version The main scaler has four SFRs for main scaler ratio depending on FIMC version. FIMC 4.x has only two SFRs and FIMC 5.x has four SFRs for main scaler. Those are MainHorRatio, MainHorRatio_ext, MainVerRatio and MainverRatio_ext. The FIMC 5.x has 15 bit resolution for scaling ratio as below. {MainHorRatio,MainHorRatio_ext} = {[14:6],[5:0]}. {MainVerRatio,MainVerRatio_ext} = {[14:6],[5:0]}. MainHorRatio = CISCCTRL[24:16], MainHorRatio_ext = CIEXTEN[15:10] MainVerRatio = CISCCTRL[8:0], MainVerRatio_ext = CIEXTEN[5:0] This patch supports FIMC 4.x and FIMC 5.x using platform_device_id::driver_data. Reviewed-by: Jonghun Han Signed-off-by: Hyunwoong Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 03ddf5d..91304ee 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -237,6 +237,7 @@ static int start_streaming(struct vb2_queue *q) struct fimc_ctx *ctx = q->drv_priv; struct fimc_dev *fimc = ctx->fimc_dev; struct s5p_fimc_isp_info *isp_info; + struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; int ret; ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1); @@ -259,7 +260,11 @@ static int start_streaming(struct vb2_queue *q) return ret; } fimc_hw_set_input_path(ctx); - fimc_hw_set_scaler(ctx); + fimc_hw_set_prescaler(ctx); + if (variant->has_mainscaler_ext) + fimc_hw_set_mainscaler_ext(ctx); + else + fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx); diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 70d6b4c..59fa512 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -248,6 +248,7 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) struct fimc_scaler *sc = &ctx->scaler; struct fimc_frame *s_frame = &ctx->s_frame; struct fimc_frame *d_frame = &ctx->d_frame; + struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; int tx, ty, sx, sy; int ret; @@ -286,8 +287,14 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) sc->pre_dst_width = sx / sc->pre_hratio; sc->pre_dst_height = sy / sc->pre_vratio; - sc->main_hratio = (sx << 8) / (tx << sc->hfactor); - sc->main_vratio = (sy << 8) / (ty << sc->vfactor); + if (variant->has_mainscaler_ext) { + sc->main_hratio = (sx << 14) / (tx << sc->hfactor); + sc->main_vratio = (sy << 14) / (ty << sc->vfactor); + } else { + sc->main_hratio = (sx << 8) / (tx << sc->hfactor); + sc->main_vratio = (sy << 8) / (ty << sc->vfactor); + + } sc->scaleup_h = (tx >= sx) ? 1 : 0; sc->scaleup_v = (ty >= sy) ? 1 : 0; @@ -571,6 +578,7 @@ static void fimc_dma_run(void *priv) { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc; + struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; unsigned long flags; u32 ret; @@ -603,7 +611,12 @@ static void fimc_dma_run(void *priv) err("Scaler setup error"); goto dma_unlock; } - fimc_hw_set_scaler(ctx); + + fimc_hw_set_prescaler(ctx); + if (variant->has_mainscaler_ext) + fimc_hw_set_mainscaler_ext(ctx); + else + fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx); @@ -1730,6 +1743,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, + .has_mainscaler_ext = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 1, @@ -1751,6 +1765,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv310 = { .has_inp_rot = 1, .has_out_rot = 1, .has_cistatus2 = 1, + .has_mainscaler_ext = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 1, @@ -1761,6 +1776,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv310 = { static struct samsung_fimc_variant fimc2_variant_s5pv310 = { .pix_hoff = 1, .has_cistatus2 = 1, + .has_mainscaler_ext = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 1, diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 562d15d..8aee9e3 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -361,6 +361,8 @@ struct fimc_pix_limit { * @has_inp_rot: set if has input rotator * @has_out_rot: set if has output rotator * @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision + * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register + * are present in this IP revision * @pix_limit: pixel size constraints for the scaler * @min_inp_pixsize: minimum input pixel size * @min_out_pixsize: minimum output pixel size @@ -372,6 +374,7 @@ struct samsung_fimc_variant { unsigned int has_inp_rot:1; unsigned int has_out_rot:1; unsigned int has_cistatus2:1; + unsigned int has_mainscaler_ext:1; struct fimc_pix_limit *pix_limit; u16 min_inp_pixsize; u16 min_out_pixsize; @@ -569,7 +572,9 @@ void fimc_hw_set_target_format(struct fimc_ctx *ctx); void fimc_hw_set_out_dma(struct fimc_ctx *ctx); void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); -void fimc_hw_set_scaler(struct fimc_ctx *ctx); +void fimc_hw_set_prescaler(struct fimc_ctx *ctx); +void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); +void fimc_hw_set_mainscaler_ext(struct fimc_ctx *ctx); void fimc_hw_en_capture(struct fimc_ctx *ctx); void fimc_hw_set_effect(struct fimc_ctx *ctx); void fimc_hw_set_in_dma(struct fimc_ctx *ctx); diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index c14c831..064fa0a 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -243,7 +243,7 @@ void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable) writel(cfg, dev->regs + S5P_CIOCTRL); } -static void fimc_hw_set_prescaler(struct fimc_ctx *ctx) +void fimc_hw_set_prescaler(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; struct fimc_scaler *sc = &ctx->scaler; @@ -261,7 +261,7 @@ static void fimc_hw_set_prescaler(struct fimc_ctx *ctx) writel(cfg, dev->regs + S5P_CISCPREDST); } -void fimc_hw_set_scaler(struct fimc_ctx *ctx) +static void fimc_hw_set_scaler(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; struct fimc_scaler *sc = &ctx->scaler; @@ -269,8 +269,6 @@ void fimc_hw_set_scaler(struct fimc_ctx *ctx) struct fimc_frame *dst_frame = &ctx->d_frame; u32 cfg = 0; - fimc_hw_set_prescaler(ctx); - if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW)) cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE); @@ -310,13 +308,55 @@ void fimc_hw_set_scaler(struct fimc_ctx *ctx) cfg |= S5P_CISCCTRL_INTERLACE; } + writel(cfg, dev->regs + S5P_CISCCTRL); +} + +void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_scaler *sc = &ctx->scaler; + u32 cfg; + + dbg("main_hratio= 0x%X main_vratio= 0x%X", + sc->main_hratio, sc->main_vratio); + + fimc_hw_set_scaler(ctx); + + cfg = readl(dev->regs + S5P_CISCCTRL); + cfg &= ~S5P_CISCCTRL_MHRATIO_MASK; + cfg &= ~S5P_CISCCTRL_MVRATIO_MASK; + cfg |= S5P_CISCCTRL_MHRATIO(sc->main_hratio); + cfg |= S5P_CISCCTRL_MVRATIO(sc->main_vratio); + + writel(cfg, dev->regs + S5P_CISCCTRL); +} + +void fimc_hw_set_mainscaler_ext(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_scaler *sc = &ctx->scaler; + u32 cfg, cfg_ext; + dbg("main_hratio= 0x%X main_vratio= 0x%X", sc->main_hratio, sc->main_vratio); - cfg |= S5P_CISCCTRL_SC_HORRATIO(sc->main_hratio); - cfg |= S5P_CISCCTRL_SC_VERRATIO(sc->main_vratio); + fimc_hw_set_scaler(ctx); + + cfg = readl(dev->regs + S5P_CISCCTRL); + cfg &= ~S5P_CISCCTRL_MHRATIO_MASK; + cfg &= ~S5P_CISCCTRL_MVRATIO_MASK; + cfg |= S5P_CISCCTRL_MHRATIO_EXT(sc->main_hratio); + cfg |= S5P_CISCCTRL_MVRATIO_EXT(sc->main_vratio); writel(cfg, dev->regs + S5P_CISCCTRL); + + cfg_ext = readl(dev->regs + S5P_CIEXTEN); + cfg_ext &= ~S5P_CIEXTEN_MHRATIO_EXT_MASK; + cfg_ext &= ~S5P_CIEXTEN_MVRATIO_EXT_MASK; + cfg_ext |= S5P_CIEXTEN_MHRATIO_EXT(sc->main_hratio); + cfg_ext |= S5P_CIEXTEN_MVRATIO_EXT(sc->main_vratio); + + writel(cfg_ext, dev->regs + S5P_CIEXTEN); } void fimc_hw_en_capture(struct fimc_ctx *ctx) diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h index fe19b4b..04669a5 100644 --- a/drivers/media/video/s5p-fimc/regs-fimc.h +++ b/drivers/media/video/s5p-fimc/regs-fimc.h @@ -139,8 +139,12 @@ #define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11) #define S5P_CISCCTRL_RGB_EXT (1 << 10) #define S5P_CISCCTRL_ONE2ONE (1 << 9) -#define S5P_CISCCTRL_SC_HORRATIO(x) ((x) << 16) -#define S5P_CISCCTRL_SC_VERRATIO(x) ((x) << 0) +#define S5P_CISCCTRL_MHRATIO(x) ((x) << 16) +#define S5P_CISCCTRL_MVRATIO(x) ((x) << 0) +#define S5P_CISCCTRL_MHRATIO_MASK (0x1ff << 16) +#define S5P_CISCCTRL_MVRATIO_MASK (0x1ff << 0) +#define S5P_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16) +#define S5P_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0) /* Target area */ #define S5P_CITAREA 0x5c @@ -263,6 +267,10 @@ /* Real output DMA image size (extension register) */ #define S5P_CIEXTEN 0x188 +#define S5P_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10) +#define S5P_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f) +#define S5P_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10) +#define S5P_CIEXTEN_MVRATIO_EXT_MASK 0x3f #define S5P_CIDMAPARAM 0x18c #define S5P_CIDMAPARAM_R_LINEAR (0 << 29) -- cgit v0.10.2 From 70f66ea2aafbd9022a5dcdfd823538e540873585 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 28 Dec 2010 11:37:55 -0300 Subject: [media] s5p-fimc: Move scaler details handling to the register API file Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 91304ee..2b03c95 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -237,7 +237,6 @@ static int start_streaming(struct vb2_queue *q) struct fimc_ctx *ctx = q->drv_priv; struct fimc_dev *fimc = ctx->fimc_dev; struct s5p_fimc_isp_info *isp_info; - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; int ret; ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1); @@ -261,10 +260,7 @@ static int start_streaming(struct vb2_queue *q) } fimc_hw_set_input_path(ctx); fimc_hw_set_prescaler(ctx); - if (variant->has_mainscaler_ext) - fimc_hw_set_mainscaler_ext(ctx); - else - fimc_hw_set_mainscaler(ctx); + fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx); diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 59fa512..09bfac4 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -578,7 +578,6 @@ static void fimc_dma_run(void *priv) { struct fimc_ctx *ctx = priv; struct fimc_dev *fimc; - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; unsigned long flags; u32 ret; @@ -613,10 +612,7 @@ static void fimc_dma_run(void *priv) } fimc_hw_set_prescaler(ctx); - if (variant->has_mainscaler_ext) - fimc_hw_set_mainscaler_ext(ctx); - else - fimc_hw_set_mainscaler(ctx); + fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 8aee9e3..1c15358 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -574,7 +574,6 @@ void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); void fimc_hw_set_prescaler(struct fimc_ctx *ctx); void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); -void fimc_hw_set_mainscaler_ext(struct fimc_ctx *ctx); void fimc_hw_en_capture(struct fimc_ctx *ctx); void fimc_hw_set_effect(struct fimc_ctx *ctx); void fimc_hw_set_in_dma(struct fimc_ctx *ctx); diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 064fa0a..8e5a0bb 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -314,6 +314,7 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; + struct samsung_fimc_variant *variant = dev->variant; struct fimc_scaler *sc = &ctx->scaler; u32 cfg; @@ -323,40 +324,26 @@ void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) fimc_hw_set_scaler(ctx); cfg = readl(dev->regs + S5P_CISCCTRL); - cfg &= ~S5P_CISCCTRL_MHRATIO_MASK; - cfg &= ~S5P_CISCCTRL_MVRATIO_MASK; - cfg |= S5P_CISCCTRL_MHRATIO(sc->main_hratio); - cfg |= S5P_CISCCTRL_MVRATIO(sc->main_vratio); - writel(cfg, dev->regs + S5P_CISCCTRL); -} + if (variant->has_mainscaler_ext) { + cfg &= ~(S5P_CISCCTRL_MHRATIO_MASK | S5P_CISCCTRL_MVRATIO_MASK); + cfg |= S5P_CISCCTRL_MHRATIO_EXT(sc->main_hratio); + cfg |= S5P_CISCCTRL_MVRATIO_EXT(sc->main_vratio); + writel(cfg, dev->regs + S5P_CISCCTRL); -void fimc_hw_set_mainscaler_ext(struct fimc_ctx *ctx) -{ - struct fimc_dev *dev = ctx->fimc_dev; - struct fimc_scaler *sc = &ctx->scaler; - u32 cfg, cfg_ext; + cfg = readl(dev->regs + S5P_CIEXTEN); - dbg("main_hratio= 0x%X main_vratio= 0x%X", - sc->main_hratio, sc->main_vratio); - - fimc_hw_set_scaler(ctx); - - cfg = readl(dev->regs + S5P_CISCCTRL); - cfg &= ~S5P_CISCCTRL_MHRATIO_MASK; - cfg &= ~S5P_CISCCTRL_MVRATIO_MASK; - cfg |= S5P_CISCCTRL_MHRATIO_EXT(sc->main_hratio); - cfg |= S5P_CISCCTRL_MVRATIO_EXT(sc->main_vratio); - - writel(cfg, dev->regs + S5P_CISCCTRL); - - cfg_ext = readl(dev->regs + S5P_CIEXTEN); - cfg_ext &= ~S5P_CIEXTEN_MHRATIO_EXT_MASK; - cfg_ext &= ~S5P_CIEXTEN_MVRATIO_EXT_MASK; - cfg_ext |= S5P_CIEXTEN_MHRATIO_EXT(sc->main_hratio); - cfg_ext |= S5P_CIEXTEN_MVRATIO_EXT(sc->main_vratio); - - writel(cfg_ext, dev->regs + S5P_CIEXTEN); + cfg &= ~(S5P_CIEXTEN_MVRATIO_EXT_MASK | + S5P_CIEXTEN_MHRATIO_EXT_MASK); + cfg |= S5P_CIEXTEN_MHRATIO_EXT(sc->main_hratio); + cfg |= S5P_CIEXTEN_MVRATIO_EXT(sc->main_vratio); + writel(cfg, dev->regs + S5P_CIEXTEN); + } else { + cfg &= ~(S5P_CISCCTRL_MHRATIO_MASK | S5P_CISCCTRL_MVRATIO_MASK); + cfg |= S5P_CISCCTRL_MHRATIO(sc->main_hratio); + cfg |= S5P_CISCCTRL_MVRATIO(sc->main_vratio); + writel(cfg, dev->regs + S5P_CISCCTRL); + } } void fimc_hw_en_capture(struct fimc_ctx *ctx) -- cgit v0.10.2 From 1b09f292eb99fd6a3601e8421c463905dbb0a866 Mon Sep 17 00:00:00 2001 From: Hyunwoong Kim Date: Tue, 28 Dec 2010 22:12:43 -0300 Subject: [media] s5p-fimc: update checking scaling ratio range Horizontal and vertical scaling range are according to the following equations. If (SRC_Width >= 64 x DST_Width) { Exit(-1); /* Out of Horizontal scale range} If (SRC_Height >= 64 x DST_Height) { Exit(-1); /* Out of Vertical scale range} fimc_check_scaler_ratio() is used to check if horizontal and vertical scale range are valid or not. To use fimc_check_scaler_ratio, source and destination format should be set by VIDIOC_S_FMT. And in case of scaling up, it doesn't have to check the scale range. Reviewed-by: Jonghun Han Signed-off-by: Hyunwoong Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 2b03c95..c326f6b 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -787,7 +787,9 @@ static int fimc_cap_s_crop(struct file *file, void *fh, f = &ctx->s_frame; /* Check for the pixel scaling ratio when cropping input image. */ - ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame); + ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height, + ctx->d_frame.width, ctx->d_frame.height, + ctx->rotation); if (ret) { v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range"); return ret; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 09bfac4..560cd21 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -200,24 +200,21 @@ static struct v4l2_queryctrl *get_ctrl(int id) return NULL; } -int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f) +int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot) { - if (r->width > f->width) { - if (f->width > (r->width * SCALER_MAX_HRATIO)) - return -EINVAL; - } else { - if ((f->width * SCALER_MAX_HRATIO) < r->width) - return -EINVAL; - } + int tx, ty; - if (r->height > f->height) { - if (f->height > (r->height * SCALER_MAX_VRATIO)) - return -EINVAL; + if (rot == 90 || rot == 270) { + ty = dw; + tx = dh; } else { - if ((f->height * SCALER_MAX_VRATIO) < r->height) - return -EINVAL; + tx = dw; + ty = dh; } + if ((sw >= SCALER_MAX_HRATIO * tx) || (sh >= SCALER_MAX_VRATIO * ty)) + return -EINVAL; + return 0; } @@ -1065,6 +1062,7 @@ int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl) struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; struct fimc_dev *fimc = ctx->fimc_dev; unsigned long flags; + int ret = 0; spin_lock_irqsave(&ctx->slock, flags); @@ -1084,6 +1082,20 @@ int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl) break; case V4L2_CID_ROTATE: + if (!(~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT))) { + ret = fimc_check_scaler_ratio(ctx->s_frame.width, + ctx->s_frame.height, + ctx->d_frame.width, + ctx->d_frame.height, + ctrl->value); + if (ret) { + v4l2_err(&fimc->m2m.v4l2_dev, + "Out of scaler range"); + spin_unlock_irqrestore(&ctx->slock, flags); + return -EINVAL; + } + } + /* Check for the output rotator availability */ if ((ctrl->value == 90 || ctrl->value == 270) && (ctx->in_path == FIMC_DMA && !variant->has_out_rot)) { @@ -1232,18 +1244,27 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) &ctx->s_frame : &ctx->d_frame; spin_lock_irqsave(&ctx->slock, flags); - if (~ctx->state & (FIMC_SRC_FMT | FIMC_DST_FMT)) { - /* Check to see if scaling ratio is within supported range */ - if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame); - else - ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame); + /* Check to see if scaling ratio is within supported range */ + if (!(~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT))) { + if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height, + ctx->d_frame.width, + ctx->d_frame.height, + ctx->rotation); + } else { + ret = fimc_check_scaler_ratio(ctx->s_frame.width, + ctx->s_frame.height, + cr->c.width, cr->c.height, + ctx->rotation); + } + if (ret) { v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range"); spin_unlock_irqrestore(&ctx->slock, flags); return -EINVAL; } } + ctx->state |= FIMC_PARAMS; f->offs_h = cr->c.left; diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 1c15358..57bff0d 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -611,7 +611,7 @@ struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask); struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f, unsigned int mask); -int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f); +int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot); int fimc_set_scaler_info(struct fimc_ctx *ctx); int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags); int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, -- cgit v0.10.2 From 65777e5c58bd3b25fcba39eddb98d8a8ce35d3be Mon Sep 17 00:00:00 2001 From: Hyunwoong Kim Date: Wed, 29 Dec 2010 04:47:49 -0300 Subject: [media] s5p-fimc: Support stop_streaming and job_abort This patch adds callback functions, stop_streaming and job_abort, to abort or finish any DMA in progress. stop_streaming is called by videobuf2 framework and job_abort is called by m2m framework. ST_M2M_PEND state is added to discard the next job. Reviewed-by: Jonghun Han Signed-off-by: Hyunwoong Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 560cd21..cd8a300 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -307,6 +307,23 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) return 0; } +static int stop_streaming(struct vb2_queue *q) +{ + struct fimc_ctx *ctx = q->drv_priv; + struct fimc_dev *fimc = ctx->fimc_dev; + + if (!fimc_m2m_pending(fimc)) + return 0; + + set_bit(ST_M2M_SHUT, &fimc->state); + + wait_event_timeout(fimc->irq_queue, + !test_bit(ST_M2M_SHUT, &fimc->state), + FIMC_SHUTDOWN_TIMEOUT); + + return 0; +} + static void fimc_capture_handler(struct fimc_dev *fimc) { struct fimc_vid_cap *cap = &fimc->vid_cap; @@ -358,7 +375,10 @@ static irqreturn_t fimc_isr(int irq, void *priv) spin_lock(&fimc->slock); - if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) { + if (test_and_clear_bit(ST_M2M_SHUT, &fimc->state)) { + wake_up(&fimc->irq_queue); + goto isr_unlock; + } else if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) { struct vb2_buffer *src_vb, *dst_vb; struct fimc_ctx *ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev); @@ -634,7 +654,17 @@ dma_unlock: static void fimc_job_abort(void *priv) { - /* Nothing done in job_abort. */ + struct fimc_ctx *ctx = priv; + struct fimc_dev *fimc = ctx->fimc_dev; + + if (!fimc_m2m_pending(fimc)) + return; + + set_bit(ST_M2M_SHUT, &fimc->state); + + wait_event_timeout(fimc->irq_queue, + !test_bit(ST_M2M_SHUT, &fimc->state), + FIMC_SHUTDOWN_TIMEOUT); } static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, @@ -711,6 +741,7 @@ struct vb2_ops fimc_qops = { .buf_queue = fimc_buf_queue, .wait_prepare = fimc_unlock, .wait_finish = fimc_lock, + .stop_streaming = stop_streaming, }; static int fimc_m2m_querycap(struct file *file, void *priv, diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 57bff0d..4829a25 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -57,6 +57,7 @@ enum fimc_dev_flags { ST_IDLE, ST_OUTDMA_RUN, ST_M2M_PEND, + ST_M2M_SHUT, /* for capture node */ ST_CAPT_PEND, ST_CAPT_RUN, -- cgit v0.10.2 From 5bbe425e57d3ca715cfe9c98bac5f8f07a96c81a Mon Sep 17 00:00:00 2001 From: Hyunwoong Kim Date: Wed, 29 Dec 2010 07:08:32 -0300 Subject: [media] s5p-fimc: fix MSCTRL.FIFO_CTRL for performance enhancement This patch fixes the value of FIFO_CTRL in MSCTRL. Main-scaler has the value to specify a basis FIFO control of input DMA. The description of FIFO_CTRL has been changed as below. 0 = FIFO Empty (Next burst transaction is possible when FIFO is empty) 1 = FIFO Full (Next burst transaction is possible except Full FIFO) Value '1' is recommended to enhance the FIMC operation performance. Reviewed-by: Jonghun Han Signed-off-by: Hyunwoong Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 8e5a0bb..10684ae 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -437,7 +437,9 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) | S5P_MSCTRL_C_INT_IN_MASK | S5P_MSCTRL_2P_IN_ORDER_MASK); - cfg |= S5P_MSCTRL_IN_BURST_COUNT(4) | S5P_MSCTRL_INPUT_MEMORY; + cfg |= (S5P_MSCTRL_IN_BURST_COUNT(4) + | S5P_MSCTRL_INPUT_MEMORY + | S5P_MSCTRL_FIFO_CTRL_FULL); switch (frame->fmt->color) { case S5P_FIMC_RGB565...S5P_FIMC_RGB888: diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h index 04669a5..0fea3e6 100644 --- a/drivers/media/video/s5p-fimc/regs-fimc.h +++ b/drivers/media/video/s5p-fimc/regs-fimc.h @@ -226,6 +226,7 @@ #define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13) #define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13) #define S5P_MSCTRL_FLIP_180 (3 << 13) +#define S5P_MSCTRL_FIFO_CTRL_FULL (1 << 12) #define S5P_MSCTRL_ORDER422_SHIFT 4 #define S5P_MSCTRL_ORDER422_YCBYCR (0 << 4) #define S5P_MSCTRL_ORDER422_CBYCRY (1 << 4) -- cgit v0.10.2 From ba10795ed1018488964e6054b5076ec31a471d96 Mon Sep 17 00:00:00 2001 From: Sungchun Kang Date: Thu, 30 Dec 2010 02:35:28 -0300 Subject: [media] s5p-fimc: fimc_stop_capture bug fix When is called fimc_stop_capture, it seems that wait_event_timeout used improperly. It should be wake up by irq handler. Reviewed-by Jonghun Han Signed-off-by: Sungchun Kang Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index c326f6b..5159cc8 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -204,7 +204,7 @@ static int fimc_stop_capture(struct fimc_dev *fimc) spin_unlock_irqrestore(&fimc->slock, flags); wait_event_timeout(fimc->irq_queue, - test_bit(ST_CAPT_SHUT, &fimc->state), + !test_bit(ST_CAPT_SHUT, &fimc->state), FIMC_SHUTDOWN_TIMEOUT); v4l2_subdev_call(cap->sd, video, s_stream, 0); -- cgit v0.10.2 From 22fb985adad023de15ad1a4ee0271a4dd97ed995 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 30 Nov 2010 12:54:56 -0300 Subject: [media] Add chip identity for NOON010PC30 camera sensor Add ID for NOON010PC30 camera chip and reserve ID range for Siliconfile sensors. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 44fe44e..ff4a52c 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -209,6 +209,9 @@ enum { /* module sn9c20x: just ident 10000 */ V4L2_IDENT_SN9C20X = 10000, + /* Siliconfile sensors: reserved range 10100 - 10199 */ + V4L2_IDENT_NOON010PC30 = 10100, + /* module cx231xx and cx25840 */ V4L2_IDENT_CX2310X_AV = 23099, /* Integrated A/V decoder; not in '100 */ V4L2_IDENT_CX23100 = 23100, -- cgit v0.10.2 From f8e2e3eab8f70103fb4680e2da202e14199c199a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 3 Jan 2011 08:07:30 -0300 Subject: [media] Add v4l2 subdev driver for NOON010PC30L image sensor Add I2C/V4L2 subdev driver for Siliconfile NOON010PC30 CIF camera. The driver implements basic functionality, i.e. CIF/QCIF/QQCIF resolution and color format selection, automatic/manual color balance control. Other functions like cropping, rotation/flip, exposure etc. can be easily implemented if needed. Reviewed-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9f20fc5..f86e407 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -740,6 +740,12 @@ config VIDEO_VIA_CAMERA Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems with ov7670 sensors. +config VIDEO_NOON010PC30 + tristate "NOON010PC30 CIF camera sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This driver supports NOON010PC30 CIF camera from Siliconfile + config SOC_CAMERA tristate "SoC camera support" depends on VIDEO_V4L2 && HAS_DMA && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 40833fe..251b7ca 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o +obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c new file mode 100644 index 0000000..35f722a --- /dev/null +++ b/drivers/media/video/noon010pc30.c @@ -0,0 +1,792 @@ +/* + * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP + * + * Copyright (C) 2010 Samsung Electronics + * Contact: Sylwester Nawrocki, + * + * Initial register configuration based on a driver authored by + * HeungJun Kim . + * + * 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 vergsion. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable."); + +#define MODULE_NAME "NOON010PC30" + +/* + * Register offsets within a page + * b15..b8 - page id, b7..b0 - register address + */ +#define POWER_CTRL_REG 0x0001 +#define PAGEMODE_REG 0x03 +#define DEVICE_ID_REG 0x0004 +#define NOON010PC30_ID 0x86 +#define VDO_CTL_REG(n) (0x0010 + (n)) +#define SYNC_CTL_REG 0x0012 +/* Window size and position */ +#define WIN_ROWH_REG 0x0013 +#define WIN_ROWL_REG 0x0014 +#define WIN_COLH_REG 0x0015 +#define WIN_COLL_REG 0x0016 +#define WIN_HEIGHTH_REG 0x0017 +#define WIN_HEIGHTL_REG 0x0018 +#define WIN_WIDTHH_REG 0x0019 +#define WIN_WIDTHL_REG 0x001A +#define HBLANKH_REG 0x001B +#define HBLANKL_REG 0x001C +#define VSYNCH_REG 0x001D +#define VSYNCL_REG 0x001E +/* VSYNC control */ +#define VS_CTL_REG(n) (0x00A1 + (n)) +/* page 1 */ +#define ISP_CTL_REG(n) (0x0110 + (n)) +#define YOFS_REG 0x0119 +#define DARK_YOFS_REG 0x011A +#define SAT_CTL_REG 0x0120 +#define BSAT_REG 0x0121 +#define RSAT_REG 0x0122 +/* Color correction */ +#define CMC_CTL_REG 0x0130 +#define CMC_OFSGH_REG 0x0133 +#define CMC_OFSGL_REG 0x0135 +#define CMC_SIGN_REG 0x0136 +#define CMC_GOFS_REG 0x0137 +#define CMC_COEF_REG(n) (0x0138 + (n)) +#define CMC_OFS_REG(n) (0x0141 + (n)) +/* Gamma correction */ +#define GMA_CTL_REG 0x0160 +#define GMA_COEF_REG(n) (0x0161 + (n)) +/* Lens Shading */ +#define LENS_CTRL_REG 0x01D0 +#define LENS_XCEN_REG 0x01D1 +#define LENS_YCEN_REG 0x01D2 +#define LENS_RC_REG 0x01D3 +#define LENS_GC_REG 0x01D4 +#define LENS_BC_REG 0x01D5 +#define L_AGON_REG 0x01D6 +#define L_AGOFF_REG 0x01D7 +/* Page 3 - Auto Exposure */ +#define AE_CTL_REG(n) (0x0310 + (n)) +#define AE_CTL9_REG 0x032C +#define AE_CTL10_REG 0x032D +#define AE_YLVL_REG 0x031C +#define AE_YTH_REG(n) (0x031D + (n)) +#define AE_WGT_REG 0x0326 +#define EXP_TIMEH_REG 0x0333 +#define EXP_TIMEM_REG 0x0334 +#define EXP_TIMEL_REG 0x0335 +#define EXP_MMINH_REG 0x0336 +#define EXP_MMINL_REG 0x0337 +#define EXP_MMAXH_REG 0x0338 +#define EXP_MMAXM_REG 0x0339 +#define EXP_MMAXL_REG 0x033A +/* Page 4 - Auto White Balance */ +#define AWB_CTL_REG(n) (0x0410 + (n)) +#define AWB_ENABE 0x80 +#define AWB_WGHT_REG 0x0419 +#define BGAIN_PAR_REG(n) (0x044F + (n)) +/* Manual white balance, when AWB_CTL2[0]=1 */ +#define MWB_RGAIN_REG 0x0466 +#define MWB_BGAIN_REG 0x0467 + +/* The token to mark an array end */ +#define REG_TERM 0xFFFF + +struct noon010_format { + enum v4l2_mbus_pixelcode code; + enum v4l2_colorspace colorspace; + u16 ispctl1_reg; +}; + +struct noon010_frmsize { + u16 width; + u16 height; + int vid_ctl1; +}; + +static const char * const noon010_supply_name[] = { + "vdd_core", "vddio", "vdda" +}; + +#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name) + +struct noon010_info { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + const struct noon010pc30_platform_data *pdata; + const struct noon010_format *curr_fmt; + const struct noon010_frmsize *curr_win; + unsigned int hflip:1; + unsigned int vflip:1; + unsigned int power:1; + u8 i2c_reg_page; + struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; + u32 gpio_nreset; + u32 gpio_nstby; +}; + +struct i2c_regval { + u16 addr; + u16 val; +}; + +/* Supported resolutions. */ +static const struct noon010_frmsize noon010_sizes[] = { + { + .width = 352, + .height = 288, + .vid_ctl1 = 0, + }, { + .width = 176, + .height = 144, + .vid_ctl1 = 0x10, + }, { + .width = 88, + .height = 72, + .vid_ctl1 = 0x20, + }, +}; + +/* Supported pixel formats. */ +static const struct noon010_format noon010_formats[] = { + { + .code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x03, + }, { + .code = V4L2_MBUS_FMT_YVYU8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x02, + }, { + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0, + }, { + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x01, + }, { + .code = V4L2_MBUS_FMT_RGB565_2X8_BE, + .colorspace = V4L2_COLORSPACE_JPEG, + .ispctl1_reg = 0x40, + }, +}; + +static const struct i2c_regval noon010_base_regs[] = { + { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C }, + /* Color corection and saturation */ + { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 }, + { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 }, + { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, + { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C }, + { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F }, + { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 }, + { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 }, + { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 }, + { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B }, + { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 }, + { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 }, + { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C }, + { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 }, + { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 }, + /* Automatic white balance */ + { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E }, + { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 }, + /* Auto exposure */ + { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 }, + { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 }, + { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 }, + { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E }, + { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 }, + { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 }, + { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 }, + /* Lens shading compensation */ + { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, + { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 }, + { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E }, + { REG_TERM, 0 }, +}; + +static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd) +{ + return container_of(sd, struct noon010_info, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct noon010_info, hdl)->sd; +} + +static inline int set_i2c_page(struct noon010_info *info, + struct i2c_client *client, unsigned int reg) +{ + u32 page = reg >> 8 & 0xFF; + int ret = 0; + + if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { + ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); + if (!ret) + info->i2c_reg_page = page; + } + return ret; +} + +static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct noon010_info *info = to_noon010(sd); + int ret = set_i2c_page(info, client, reg_addr); + + if (ret) + return ret; + return i2c_smbus_read_byte_data(client, reg_addr & 0xFF); +} + +static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct noon010_info *info = to_noon010(sd); + int ret = set_i2c_page(info, client, reg_addr); + + if (ret) + return ret; + return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val); +} + +static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd, + const struct i2c_regval *msg) +{ + while (msg->addr != REG_TERM) { + int ret = cam_i2c_write(sd, msg->addr, msg->val); + + if (ret) + return ret; + msg++; + } + return 0; +} + +/* Device reset and sleep mode control */ +static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) +{ + struct noon010_info *info = to_noon010(sd); + u8 reg = sleep ? 0xF1 : 0xF0; + int ret = 0; + + if (reset) + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); + if (!ret) { + ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); + if (reset && !ret) + info->i2c_reg_page = -1; + } + return ret; +} + +/* Automatic white balance control */ +static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) +{ + int ret; + + ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F); + if (!ret) + ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B); + return ret; +} + +static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) +{ + struct noon010_info *info = to_noon010(sd); + int reg, ret; + + reg = cam_i2c_read(sd, VDO_CTL_REG(1)); + if (reg < 0) + return reg; + + reg &= 0x7C; + if (hflip) + reg |= 0x01; + if (vflip) + reg |= 0x02; + + ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80); + if (!ret) { + info->hflip = hflip; + info->vflip = vflip; + } + return ret; +} + +/* Configure resolution and color format */ +static int noon010_set_params(struct v4l2_subdev *sd) +{ + struct noon010_info *info = to_noon010(sd); + int ret; + + if (!info->curr_win) + return -EINVAL; + + ret = cam_i2c_write(sd, VDO_CTL_REG(0), info->curr_win->vid_ctl1); + + if (!ret && info->curr_fmt) + ret = cam_i2c_write(sd, ISP_CTL_REG(0), + info->curr_fmt->ispctl1_reg); + return ret; +} + +/* Find nearest matching image pixel size. */ +static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf) +{ + unsigned int min_err = ~0; + int i = ARRAY_SIZE(noon010_sizes); + const struct noon010_frmsize *fsize = &noon010_sizes[0], + *match = NULL; + + while (i--) { + int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + + if (err < min_err) { + min_err = err; + match = fsize; + } + fsize++; + } + if (match) { + mf->width = match->width; + mf->height = match->height; + return 0; + } + return -EINVAL; +} + +static int power_enable(struct noon010_info *info) +{ + int ret; + + if (info->power) { + v4l2_info(&info->sd, "%s: sensor is already on\n", __func__); + return 0; + } + + if (gpio_is_valid(info->gpio_nstby)) + gpio_set_value(info->gpio_nstby, 0); + + if (gpio_is_valid(info->gpio_nreset)) + gpio_set_value(info->gpio_nreset, 0); + + ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); + if (ret) + return ret; + + if (gpio_is_valid(info->gpio_nreset)) { + msleep(50); + gpio_set_value(info->gpio_nreset, 1); + } + if (gpio_is_valid(info->gpio_nstby)) { + udelay(1000); + gpio_set_value(info->gpio_nstby, 1); + } + if (gpio_is_valid(info->gpio_nreset)) { + udelay(1000); + gpio_set_value(info->gpio_nreset, 0); + msleep(100); + gpio_set_value(info->gpio_nreset, 1); + msleep(20); + } + info->power = 1; + + v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); + return 0; +} + +static int power_disable(struct noon010_info *info) +{ + int ret; + + if (!info->power) { + v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); + return 0; + } + + ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); + if (ret) + return ret; + + if (gpio_is_valid(info->gpio_nstby)) + gpio_set_value(info->gpio_nstby, 0); + + if (gpio_is_valid(info->gpio_nreset)) + gpio_set_value(info->gpio_nreset, 0); + + info->power = 0; + + v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); + + return 0; +} + +static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", + __func__, ctrl->id, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + return noon010_enable_autowhitebalance(sd, ctrl->val); + case V4L2_CID_BLUE_BALANCE: + return cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); + case V4L2_CID_RED_BALANCE: + return cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); + default: + return -EINVAL; + } +} + +static int noon010_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (!code || index >= ARRAY_SIZE(noon010_formats)) + return -EINVAL; + + *code = noon010_formats[index].code; + return 0; +} + +static int noon010_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) +{ + struct noon010_info *info = to_noon010(sd); + int ret; + + if (!mf) + return -EINVAL; + + if (!info->curr_win || !info->curr_fmt) { + ret = noon010_set_params(sd); + if (ret) + return ret; + } + + mf->width = info->curr_win->width; + mf->height = info->curr_win->height; + mf->code = info->curr_fmt->code; + mf->colorspace = info->curr_fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +/* Return nearest media bus frame format. */ +static const struct noon010_format *try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + int i = ARRAY_SIZE(noon010_formats); + + noon010_try_frame_size(mf); + + while (i--) + if (mf->code == noon010_formats[i].code) + break; + + mf->code = noon010_formats[i].code; + + return &noon010_formats[i]; +} + +static int noon010_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + if (!sd || !mf) + return -EINVAL; + + try_fmt(sd, mf); + return 0; +} + +static int noon010_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct noon010_info *info = to_noon010(sd); + + if (!sd || !mf) + return -EINVAL; + + info->curr_fmt = try_fmt(sd, mf); + + return noon010_set_params(sd); +} + +static int noon010_base_config(struct v4l2_subdev *sd) +{ + struct noon010_info *info = to_noon010(sd); + int ret; + + ret = noon010_bulk_write_reg(sd, noon010_base_regs); + if (!ret) { + info->curr_fmt = &noon010_formats[0]; + info->curr_win = &noon010_sizes[0]; + ret = noon010_set_params(sd); + } + if (!ret) + ret = noon010_set_flip(sd, 1, 0); + if (!ret) + ret = noon010_power_ctrl(sd, false, false); + + /* sync the handler and the registers state */ + v4l2_ctrl_handler_setup(&to_noon010(sd)->hdl); + return ret; +} + +static int noon010_s_power(struct v4l2_subdev *sd, int on) +{ + struct noon010_info *info = to_noon010(sd); + const struct noon010pc30_platform_data *pdata = info->pdata; + int ret = 0; + + if (WARN(pdata == NULL, "No platform data!\n")) + return -ENOMEM; + + if (on) { + ret = power_enable(info); + if (ret) + return ret; + ret = noon010_base_config(sd); + } else { + noon010_power_ctrl(sd, false, true); + ret = power_disable(info); + info->curr_win = NULL; + info->curr_fmt = NULL; + } + + return ret; +} + +static int noon010_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, + V4L2_IDENT_NOON010PC30, 0); +} + +static int noon010_log_status(struct v4l2_subdev *sd) +{ + struct noon010_info *info = to_noon010(sd); + + v4l2_ctrl_handler_log_status(&info->hdl, sd->name); + return 0; +} + +static const struct v4l2_ctrl_ops noon010_ctrl_ops = { + .s_ctrl = noon010_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops noon010_core_ops = { + .g_chip_ident = noon010_g_chip_ident, + .s_power = noon010_s_power, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .log_status = noon010_log_status, +}; + +static const struct v4l2_subdev_video_ops noon010_video_ops = { + .g_mbus_fmt = noon010_g_fmt, + .s_mbus_fmt = noon010_s_fmt, + .try_mbus_fmt = noon010_try_fmt, + .enum_mbus_fmt = noon010_enum_fmt, +}; + +static const struct v4l2_subdev_ops noon010_ops = { + .core = &noon010_core_ops, + .video = &noon010_video_ops, +}; + +/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ +static int noon010_detect(struct i2c_client *client, struct noon010_info *info) +{ + int ret; + + ret = power_enable(info); + if (ret) + return ret; + + ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); + if (ret < 0) + dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); + + power_disable(info); + + return ret == NOON010PC30_ID ? 0 : -ENODEV; +} + +static int noon010_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct noon010_info *info; + struct v4l2_subdev *sd; + const struct noon010pc30_platform_data *pdata + = client->dev.platform_data; + int ret; + int i; + + if (!pdata) { + dev_err(&client->dev, "No platform data!\n"); + return -EIO; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + sd = &info->sd; + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); + v4l2_i2c_subdev_init(sd, client, &noon010_ops); + + v4l2_ctrl_handler_init(&info->hdl, 3); + + v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 127, 1, 64); + v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); + + sd->ctrl_handler = &info->hdl; + + ret = info->hdl.error; + if (ret) + goto np_err; + + info->pdata = client->dev.platform_data; + info->i2c_reg_page = -1; + info->gpio_nreset = -EINVAL; + info->gpio_nstby = -EINVAL; + + if (gpio_is_valid(pdata->gpio_nreset)) { + ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST"); + if (ret) { + dev_err(&client->dev, "GPIO request error: %d\n", ret); + goto np_err; + } + info->gpio_nreset = pdata->gpio_nreset; + gpio_direction_output(info->gpio_nreset, 0); + gpio_export(info->gpio_nreset, 0); + } + + if (gpio_is_valid(pdata->gpio_nstby)) { + ret = gpio_request(pdata->gpio_nstby, "NOON010PC30 NSTBY"); + if (ret) { + dev_err(&client->dev, "GPIO request error: %d\n", ret); + goto np_gpio_err; + } + info->gpio_nstby = pdata->gpio_nstby; + gpio_direction_output(info->gpio_nstby, 0); + gpio_export(info->gpio_nstby, 0); + } + + for (i = 0; i < NOON010_NUM_SUPPLIES; i++) + info->supply[i].supply = noon010_supply_name[i]; + + ret = regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, + info->supply); + if (ret) + goto np_reg_err; + + ret = noon010_detect(client, info); + if (!ret) + return 0; + + /* the sensor detection failed */ + regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); +np_reg_err: + if (gpio_is_valid(info->gpio_nstby)) + gpio_free(info->gpio_nstby); +np_gpio_err: + if (gpio_is_valid(info->gpio_nreset)) + gpio_free(info->gpio_nreset); +np_err: + v4l2_ctrl_handler_free(&info->hdl); + v4l2_device_unregister_subdev(sd); + kfree(info); + return ret; +} + +static int noon010_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct noon010_info *info = to_noon010(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&info->hdl); + + regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); + + if (gpio_is_valid(info->gpio_nreset)) + gpio_free(info->gpio_nreset); + + if (gpio_is_valid(info->gpio_nstby)) + gpio_free(info->gpio_nstby); + + kfree(info); + return 0; +} + +static const struct i2c_device_id noon010_id[] = { + { MODULE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, noon010_id); + + +static struct i2c_driver noon010_i2c_driver = { + .driver = { + .name = MODULE_NAME + }, + .probe = noon010_probe, + .remove = noon010_remove, + .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_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/include/media/noon010pc30.h b/include/media/noon010pc30.h new file mode 100644 index 0000000..58eafee --- /dev/null +++ b/include/media/noon010pc30.h @@ -0,0 +1,28 @@ +/* + * Driver header for NOON010PC30L camera sensor chip. + * + * Copyright (c) 2010 Samsung Electronics, Co. Ltd + * Contact: Sylwester Nawrocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef NOON010PC30_H +#define NOON010PC30_H + +/** + * @clk_rate: the clock frequency in Hz + * @gpio_nreset: GPIO driving nRESET pin + * @gpio_nstby: GPIO driving nSTBY pin + */ + +struct noon010pc30_platform_data { + unsigned long clk_rate; + int gpio_nreset; + int gpio_nstby; +}; + +#endif /* NOON010PC30_H */ -- cgit v0.10.2 From 7757ddda6f4febbc52342d82440dd4f7a7d4f14f Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Mon, 3 Jan 2011 15:30:14 -0300 Subject: [media] DiB0700: add function to change I2C-speed This commit adds a function to the DiB0700 USB driver which allows drivers to change the I2C clock speed. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h index 3537d65..b2a87f2 100644 --- a/drivers/media/dvb/dvb-usb/dib0700.h +++ b/drivers/media/dvb/dvb-usb/dib0700.h @@ -32,6 +32,7 @@ extern int dvb_usb_dib0700_debug; // 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog) // 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1) // 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " ) +#define REQUEST_SET_I2C_PARAM 0x10 #define REQUEST_SET_RC 0x11 #define REQUEST_NEW_I2C_READ 0x12 #define REQUEST_NEW_I2C_WRITE 0x13 @@ -61,6 +62,7 @@ extern struct i2c_algorithm dib0700_i2c_algo; extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, int *cold); extern int dib0700_change_protocol(struct rc_dev *dev, u64 rc_type); +extern int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz); extern int dib0700_device_count; extern int dvb_usb_dib0700_ir_proto; diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 98ffb40..ebda77d 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -328,6 +328,30 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, return dib0700_ctrl_wr(d, b, 10); } +int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz) +{ + u16 divider; + u8 b[8]; + + if (scl_kHz == 0) + return -EINVAL; + + b[0] = REQUEST_SET_I2C_PARAM; + divider = (u16) (30000 / scl_kHz); + b[2] = (u8) (divider >> 8); + b[3] = (u8) (divider & 0xff); + divider = (u16) (72000 / scl_kHz); + b[4] = (u8) (divider >> 8); + b[5] = (u8) (divider & 0xff); + divider = (u16) (72000 / scl_kHz); /* clock: 72MHz */ + b[6] = (u8) (divider >> 8); + b[7] = (u8) (divider & 0xff); + + deb_info("setting I2C speed: %04x %04x %04x (%d kHz).", (b[2] << 8) | (b[3]), (b[4] << 8) | b[5], (b[6] << 8) | b[7], scl_kHz); + return dib0700_ctrl_wr(d, b, 8); +} + + int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3) { switch (clk_MHz) { @@ -459,10 +483,20 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id); - if (onoff) - st->channel_state |= 1 << adap->id; - else - st->channel_state &= ~(1 << adap->id); + st->channel_state &= ~0x3; + if ((adap->stream.props.endpoint != 2) && (adap->stream.props.endpoint != 3)) { + deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->stream.props.endpoint); + if (onoff) + st->channel_state |= 1 << (adap->id); + else + st->channel_state |= 1 << ~(adap->id); + } + else { + if (onoff) + st->channel_state |= 1 << (adap->stream.props.endpoint-2); + else + st->channel_state |= 1 << (3-adap->stream.props.endpoint); + } b[2] |= st->channel_state; -- cgit v0.10.2 From 4c70e074f8c496dc06798188d71be13162115d32 Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Mon, 3 Jan 2011 15:33:37 -0300 Subject: [media] DiB8000: add diversity support This patch adds a set a functions which allow the handling of multiple demodulator in a diversity reception chain. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index df17b91..625e421 100644 --- a/drivers/media/dvb/frontends/dib8000.c +++ b/drivers/media/dvb/frontends/dib8000.c @@ -22,6 +22,7 @@ #define LAYER_C 3 #define FE_CALLBACK_TIME_NEVER 0xffffffff +#define MAX_NUMBER_OF_FRONTENDS 6 static int debug; module_param(debug, int, 0644); @@ -37,7 +38,6 @@ struct i2c_device { }; struct dib8000_state { - struct dvb_frontend fe; struct dib8000_config cfg; struct i2c_device i2c; @@ -68,6 +68,8 @@ struct dib8000_state { u8 isdbt_cfg_loaded; enum frontend_tune_state tune_state; u32 status; + + struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; }; enum dib8000_power_mode { @@ -122,111 +124,111 @@ static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val) return dib8000_i2c_write16(&state->i2c, reg, val); } -static const int16_t coeff_2k_sb_1seg_dqpsk[8] = { +static const s16 coeff_2k_sb_1seg_dqpsk[8] = { (769 << 5) | 0x0a, (745 << 5) | 0x03, (595 << 5) | 0x0d, (769 << 5) | 0x0a, (920 << 5) | 0x09, (784 << 5) | 0x02, (519 << 5) | 0x0c, - (920 << 5) | 0x09 + (920 << 5) | 0x09 }; -static const int16_t coeff_2k_sb_1seg[8] = { +static const s16 coeff_2k_sb_1seg[8] = { (692 << 5) | 0x0b, (683 << 5) | 0x01, (519 << 5) | 0x09, (692 << 5) | 0x0b, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f }; -static const int16_t coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = { +static const s16 coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = { (832 << 5) | 0x10, (912 << 5) | 0x05, (900 << 5) | 0x12, (832 << 5) | 0x10, (-931 << 5) | 0x0f, (912 << 5) | 0x04, (807 << 5) | 0x11, - (-931 << 5) | 0x0f + (-931 << 5) | 0x0f }; -static const int16_t coeff_2k_sb_3seg_0dqpsk[8] = { +static const s16 coeff_2k_sb_3seg_0dqpsk[8] = { (622 << 5) | 0x0c, (941 << 5) | 0x04, (796 << 5) | 0x10, (622 << 5) | 0x0c, (982 << 5) | 0x0c, (519 << 5) | 0x02, (572 << 5) | 0x0e, - (982 << 5) | 0x0c + (982 << 5) | 0x0c }; -static const int16_t coeff_2k_sb_3seg_1dqpsk[8] = { +static const s16 coeff_2k_sb_3seg_1dqpsk[8] = { (699 << 5) | 0x14, (607 << 5) | 0x04, (944 << 5) | 0x13, (699 << 5) | 0x14, (-720 << 5) | 0x0d, (640 << 5) | 0x03, (866 << 5) | 0x12, - (-720 << 5) | 0x0d + (-720 << 5) | 0x0d }; -static const int16_t coeff_2k_sb_3seg[8] = { +static const s16 coeff_2k_sb_3seg[8] = { (664 << 5) | 0x0c, (925 << 5) | 0x03, (937 << 5) | 0x10, (664 << 5) | 0x0c, (-610 << 5) | 0x0a, (697 << 5) | 0x01, (836 << 5) | 0x0e, - (-610 << 5) | 0x0a + (-610 << 5) | 0x0a }; -static const int16_t coeff_4k_sb_1seg_dqpsk[8] = { +static const s16 coeff_4k_sb_1seg_dqpsk[8] = { (-955 << 5) | 0x0e, (687 << 5) | 0x04, (818 << 5) | 0x10, (-955 << 5) | 0x0e, (-922 << 5) | 0x0d, (750 << 5) | 0x03, (665 << 5) | 0x0f, - (-922 << 5) | 0x0d + (-922 << 5) | 0x0d }; -static const int16_t coeff_4k_sb_1seg[8] = { +static const s16 coeff_4k_sb_1seg[8] = { (638 << 5) | 0x0d, (683 << 5) | 0x02, (638 << 5) | 0x0d, (638 << 5) | 0x0d, (-655 << 5) | 0x0a, (517 << 5) | 0x00, (698 << 5) | 0x0d, - (-655 << 5) | 0x0a + (-655 << 5) | 0x0a }; -static const int16_t coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = { +static const s16 coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = { (-707 << 5) | 0x14, (910 << 5) | 0x06, (889 << 5) | 0x16, (-707 << 5) | 0x14, (-958 << 5) | 0x13, (993 << 5) | 0x05, (523 << 5) | 0x14, - (-958 << 5) | 0x13 + (-958 << 5) | 0x13 }; -static const int16_t coeff_4k_sb_3seg_0dqpsk[8] = { +static const s16 coeff_4k_sb_3seg_0dqpsk[8] = { (-723 << 5) | 0x13, (910 << 5) | 0x05, (777 << 5) | 0x14, (-723 << 5) | 0x13, (-568 << 5) | 0x0f, (547 << 5) | 0x03, (696 << 5) | 0x12, - (-568 << 5) | 0x0f + (-568 << 5) | 0x0f }; -static const int16_t coeff_4k_sb_3seg_1dqpsk[8] = { +static const s16 coeff_4k_sb_3seg_1dqpsk[8] = { (-940 << 5) | 0x15, (607 << 5) | 0x05, (915 << 5) | 0x16, (-940 << 5) | 0x15, (-848 << 5) | 0x13, (683 << 5) | 0x04, (543 << 5) | 0x14, - (-848 << 5) | 0x13 + (-848 << 5) | 0x13 }; -static const int16_t coeff_4k_sb_3seg[8] = { +static const s16 coeff_4k_sb_3seg[8] = { (612 << 5) | 0x12, (910 << 5) | 0x04, (864 << 5) | 0x14, (612 << 5) | 0x12, (-869 << 5) | 0x13, (683 << 5) | 0x02, (869 << 5) | 0x12, - (-869 << 5) | 0x13 + (-869 << 5) | 0x13 }; -static const int16_t coeff_8k_sb_1seg_dqpsk[8] = { +static const s16 coeff_8k_sb_1seg_dqpsk[8] = { (-835 << 5) | 0x12, (684 << 5) | 0x05, (735 << 5) | 0x14, (-835 << 5) | 0x12, (-598 << 5) | 0x10, (781 << 5) | 0x04, (739 << 5) | 0x13, - (-598 << 5) | 0x10 + (-598 << 5) | 0x10 }; -static const int16_t coeff_8k_sb_1seg[8] = { +static const s16 coeff_8k_sb_1seg[8] = { (673 << 5) | 0x0f, (683 << 5) | 0x03, (808 << 5) | 0x12, (673 << 5) | 0x0f, (585 << 5) | 0x0f, (512 << 5) | 0x01, (780 << 5) | 0x0f, - (585 << 5) | 0x0f + (585 << 5) | 0x0f }; -static const int16_t coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = { +static const s16 coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = { (863 << 5) | 0x17, (930 << 5) | 0x07, (878 << 5) | 0x19, (863 << 5) | 0x17, (0 << 5) | 0x14, (521 << 5) | 0x05, (980 << 5) | 0x18, - (0 << 5) | 0x14 + (0 << 5) | 0x14 }; -static const int16_t coeff_8k_sb_3seg_0dqpsk[8] = { +static const s16 coeff_8k_sb_3seg_0dqpsk[8] = { (-924 << 5) | 0x17, (910 << 5) | 0x06, (774 << 5) | 0x17, (-924 << 5) | 0x17, (-877 << 5) | 0x15, (565 << 5) | 0x04, (553 << 5) | 0x15, - (-877 << 5) | 0x15 + (-877 << 5) | 0x15 }; -static const int16_t coeff_8k_sb_3seg_1dqpsk[8] = { +static const s16 coeff_8k_sb_3seg_1dqpsk[8] = { (-921 << 5) | 0x19, (607 << 5) | 0x06, (881 << 5) | 0x19, (-921 << 5) | 0x19, (-921 << 5) | 0x14, (713 << 5) | 0x05, (1018 << 5) | 0x18, - (-921 << 5) | 0x14 + (-921 << 5) | 0x14 }; -static const int16_t coeff_8k_sb_3seg[8] = { +static const s16 coeff_8k_sb_3seg[8] = { (514 << 5) | 0x14, (910 << 5) | 0x05, (861 << 5) | 0x17, (514 << 5) | 0x14, (690 << 5) | 0x14, (683 << 5) | 0x03, (662 << 5) | 0x15, - (690 << 5) | 0x14 + (690 << 5) | 0x14 }; -static const int16_t ana_fe_coeff_3seg[24] = { +static const s16 ana_fe_coeff_3seg[24] = { 81, 80, 78, 74, 68, 61, 54, 45, 37, 28, 19, 11, 4, 1022, 1017, 1013, 1010, 1008, 1008, 1008, 1008, 1010, 1014, 1017 }; -static const int16_t ana_fe_coeff_1seg[24] = { +static const s16 ana_fe_coeff_1seg[24] = { 249, 226, 164, 82, 5, 981, 970, 988, 1018, 20, 31, 26, 8, 1012, 1000, 1018, 1012, 8, 15, 14, 9, 3, 1017, 1003 }; -static const int16_t ana_fe_coeff_13seg[24] = { +static const s16 ana_fe_coeff_13seg[24] = { 396, 305, 105, -51, -77, -12, 41, 31, -11, -30, -11, 14, 15, -2, -13, -7, 5, 8, 1, -6, -7, -3, 0, 1 }; static u16 fft_to_mode(struct dib8000_state *state) { u16 mode; - switch (state->fe.dtv_property_cache.transmission_mode) { + switch (state->fe[0]->dtv_property_cache.transmission_mode) { case TRANSMISSION_MODE_2K: mode = 1; break; @@ -249,16 +251,17 @@ static void dib8000_set_acquisition_mode(struct dib8000_state *state) dprintk("acquisition mode activated"); dib8000_write_word(state, 298, nud); } - -static int dib8000_set_output_mode(struct dib8000_state *state, int mode) +static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) { + struct dib8000_state *state = fe->demodulator_priv; + u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */ outreg = 0; fifo_threshold = 1792; smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); - dprintk("-I- Setting output mode for demod %p to %d", &state->fe, mode); + dprintk("-I- Setting output mode for demod %p to %d", &state->fe[0], mode); switch (mode) { case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock @@ -292,7 +295,7 @@ static int dib8000_set_output_mode(struct dib8000_state *state, int mode) break; default: - dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe); + dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe[0]); return -EINVAL; } @@ -342,7 +345,7 @@ static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_pow { /* by default everything is going to be powered off */ u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0xffff, - reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00; + reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00; /* now, depending on the requested mode, we power on */ switch (mode) { @@ -411,8 +414,9 @@ static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_s return ret; } -static int dib8000_set_bandwidth(struct dib8000_state *state, u32 bw) +static int dib8000_set_bandwidth(struct dvb_frontend *fe, u32 bw) { + struct dib8000_state *state = fe->demodulator_priv; u32 timf; if (bw == 0) @@ -478,7 +482,7 @@ static void dib8000_reset_pll(struct dib8000_state *state) // clk_cfg1 clk_cfg1 = (1 << 10) | (0 << 9) | (pll->IO_CLK_en_core << 8) | - (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | (1 << 3) | (pll->pll_range << 1) | (pll->pll_reset << 0); + (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | (1 << 3) | (pll->pll_range << 1) | (pll->pll_reset << 0); dib8000_write_word(state, 902, clk_cfg1); clk_cfg1 = (clk_cfg1 & 0xfff7) | (pll->pll_bypass << 3); @@ -491,7 +495,7 @@ static void dib8000_reset_pll(struct dib8000_state *state) dib8000_write_word(state, 904, (0 << 15) | (0 << 12) | (0 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1)); else if (state->cfg.refclksel != 0) dib8000_write_word(state, 904, - (0 << 15) | (1 << 12) | ((state->cfg.refclksel & 0x3) << 10) | (pll->modulo << 8) | (pll-> + (0 << 15) | (1 << 12) | ((state->cfg.refclksel & 0x3) << 10) | (pll->modulo << 8) | (pll-> ADClkSrc << 7) | (0 << 1)); else dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | (3 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1)); @@ -560,7 +564,7 @@ static const u16 dib8000_defaults[] = { 0xd4c0, /*1, 32, - 0x6680 // P_corm_thres Lock algorithms configuration */ + 0x6680 // P_corm_thres Lock algorithms configuration */ 11, 80, /* set ADC level to -16 */ (1 << 13) - 825 - 117, @@ -627,10 +631,10 @@ static const u16 dib8000_defaults[] = { 1, 338, (1 << 12) | // P_ctrl_corm_thres4pre_freq_inh=1 - (1 << 10) | // P_ctrl_pre_freq_mode_sat=1 - (0 << 9) | // P_ctrl_pre_freq_inh=0 - (3 << 5) | // P_ctrl_pre_freq_step=3 - (1 << 0), // P_pre_freq_win_len=1 + (1 << 10) | // P_ctrl_pre_freq_mode_sat=1 + (0 << 9) | // P_ctrl_pre_freq_inh=0 + (3 << 5) | // P_ctrl_pre_freq_step=3 + (1 << 0), // P_pre_freq_win_len=1 1, 903, (0 << 4) | 2, // P_divclksel=0 P_divbitsel=2 (was clk=3,bit=1 for MPW) @@ -717,7 +721,7 @@ static int dib8000_reset(struct dvb_frontend *fe) if (dib8000_reset_gpio(state) != 0) dprintk("GPIO reset was not successful."); - if (dib8000_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + if (dib8000_set_output_mode(fe, OUTMODE_HIGH_Z) != 0) dprintk("OUTPUT_MODE could not be resetted."); state->current_agc = NULL; @@ -752,7 +756,7 @@ static int dib8000_reset(struct dvb_frontend *fe) /* unforce divstr regardless whether i2c enumeration was done or not */ dib8000_write_word(state, 1285, dib8000_read_word(state, 1285) & ~(1 << 1)); - dib8000_set_bandwidth(state, 6000); + dib8000_set_bandwidth(fe, 6000); dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON); dib8000_sad_calib(state); @@ -778,7 +782,7 @@ static int dib8000_update_lna(struct dib8000_state *state) // read dyn_gain here (because it is demod-dependent and not tuner) dyn_gain = dib8000_read_word(state, 390); - if (state->cfg.update_lna(&state->fe, dyn_gain)) { // LNA has changed + if (state->cfg.update_lna(state->fe[0], dyn_gain)) { // LNA has changed dib8000_restart_agc(state); return 1; } @@ -865,7 +869,7 @@ static int dib8000_agc_soft_split(struct dib8000_state *state) split_offset = state->current_agc->split.max; else split_offset = state->current_agc->split.max * - (agc - state->current_agc->split.min_thres) / (state->current_agc->split.max_thres - state->current_agc->split.min_thres); + (agc - state->current_agc->split.min_thres) / (state->current_agc->split.max_thres - state->current_agc->split.min_thres); dprintk("AGC split_offset: %d", split_offset); @@ -900,7 +904,7 @@ static int dib8000_agc_startup(struct dvb_frontend *fe) case CT_AGC_STEP_0: //AGC initialization if (state->cfg.agc_control) - state->cfg.agc_control(&state->fe, 1); + state->cfg.agc_control(fe, 1); dib8000_restart_agc(state); @@ -924,7 +928,7 @@ static int dib8000_agc_startup(struct dvb_frontend *fe) dib8000_agc_soft_split(state); if (state->cfg.agc_control) - state->cfg.agc_control(&state->fe, 0); + state->cfg.agc_control(fe, 0); *tune_state = CT_AGC_STOP; break; @@ -936,29 +940,29 @@ static int dib8000_agc_startup(struct dvb_frontend *fe) } -static const int32_t lut_1000ln_mant[] = +static const s32 lut_1000ln_mant[] = { 908, 7003, 7090, 7170, 7244, 7313, 7377, 7438, 7495, 7549, 7600 }; -int32_t dib8000_get_adc_power(struct dvb_frontend *fe, uint8_t mode) +s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) { - struct dib8000_state *state = fe->demodulator_priv; - uint32_t ix = 0, tmp_val = 0, exp = 0, mant = 0; - int32_t val; - - val = dib8000_read32(state, 384); - /* mode = 1 : ln_agcpower calc using mant-exp conversion and mantis look up table */ - if (mode) { - tmp_val = val; - while (tmp_val >>= 1) - exp++; - mant = (val * 1000 / (1<demodulator_priv; + u32 ix = 0, tmp_val = 0, exp = 0, mant = 0; + s32 val; + + val = dib8000_read32(state, 384); + /* mode = 1 : ln_agcpower calc using mant-exp conversion and mantis look up table */ + if (mode) { + tmp_val = val; + while (tmp_val >>= 1) + exp++; + mant = (val * 1000 / (1<fe.dtv_property_cache.inversion ^ i); + dib8000_write_word(state, 26, state->fe[0]->dtv_property_cache.inversion ^ i); - if (state->fe.dtv_property_cache.isdbt_sb_mode) { + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { //compute new dds_freq for the seg and adjust prbs int seg_offset = - state->fe.dtv_property_cache.isdbt_sb_segment_idx - (state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) - - (state->fe.dtv_property_cache.isdbt_sb_segment_count % 2); + state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx - (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) - + (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2); int clk = state->cfg.pll->internal; u32 segtodds = ((u32) (430 << 23) / clk) << 3; // segtodds = SegBW / Fclk * pow(2,26) int dds_offset = seg_offset * segtodds; int new_dds, sub_channel; - if ((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) // if even + if ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) // if even dds_offset -= (int)(segtodds / 2); if (state->cfg.pll->ifreq == 0) { - if ((state->fe.dtv_property_cache.inversion ^ i) == 0) { + if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) { dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); new_dds = dds_offset; } else @@ -1027,35 +1031,34 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear // - the segment of center frequency with an odd total number of segments // - the segment to the left of center frequency with an even total number of segments // - the segment to the right of center frequency with an even total number of segments - if ((state->fe.dtv_property_cache.delivery_system == SYS_ISDBT) && (state->fe.dtv_property_cache.isdbt_sb_mode == 1) - && - (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) - && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == - ((state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) - || (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) - && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == (state->fe.dtv_property_cache.isdbt_sb_segment_count / 2))) - || (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) - && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == - ((state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) - )) { + if ((state->fe[0]->dtv_property_cache.delivery_system == SYS_ISDBT) && (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) + && (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) + && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == + ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2))) + || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == + ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + )) { new_dds -= ((u32) (850 << 22) / clk) << 4; // new_dds = 850 (freq shift in KHz) / Fclk * pow(2,26) } } else { - if ((state->fe.dtv_property_cache.inversion ^ i) == 0) + if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) new_dds = state->cfg.pll->ifreq - dds_offset; else new_dds = state->cfg.pll->ifreq + dds_offset; } dib8000_write_word(state, 27, (u16) ((new_dds >> 16) & 0x01ff)); dib8000_write_word(state, 28, (u16) (new_dds & 0xffff)); - if (state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) // if odd - sub_channel = ((state->fe.dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3; + if (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) // if odd + sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3; else // if even - sub_channel = ((state->fe.dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3; + sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3; sub_channel -= 6; - if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K - || state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) { + if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K + || state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) { dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); //adp_pass =1 dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); //pha3_force_pha_shift = 1 } else { @@ -1063,7 +1066,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); //pha3_force_pha_shift = 0 } - switch (state->fe.dtv_property_cache.transmission_mode) { + switch (state->fe[0]->dtv_property_cache.transmission_mode) { case TRANSMISSION_MODE_2K: switch (sub_channel) { case -6: @@ -1209,7 +1212,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear } break; } - } else { // if not state->fe.dtv_property_cache.isdbt_sb_mode + } else { // if not state->fe[0]->dtv_property_cache.isdbt_sb_mode dib8000_write_word(state, 27, (u16) ((state->cfg.pll->ifreq >> 16) & 0x01ff)); dib8000_write_word(state, 28, (u16) (state->cfg.pll->ifreq & 0xffff)); dib8000_write_word(state, 26, (u16) ((state->cfg.pll->ifreq >> 25) & 0x0003)); @@ -1218,7 +1221,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 10, (seq << 4)); // dib8000_write_word(state, 287, (dib8000_read_word(state, 287) & 0xe000) | 0x1000); - switch (state->fe.dtv_property_cache.guard_interval) { + switch (state->fe[0]->dtv_property_cache.guard_interval) { case GUARD_INTERVAL_1_32: guard = 0; break; @@ -1238,7 +1241,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear max_constellation = DQPSK; for (i = 0; i < 3; i++) { - switch (state->fe.dtv_property_cache.layer[i].modulation) { + switch (state->fe[0]->dtv_property_cache.layer[i].modulation) { case DQPSK: constellation = 0; break; @@ -1254,7 +1257,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear break; } - switch (state->fe.dtv_property_cache.layer[i].fec) { + switch (state->fe[0]->dtv_property_cache.layer[i].fec) { case FEC_1_2: crate = 1; break; @@ -1273,26 +1276,26 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear break; } - if ((state->fe.dtv_property_cache.layer[i].interleaving > 0) && - ((state->fe.dtv_property_cache.layer[i].interleaving <= 3) || - (state->fe.dtv_property_cache.layer[i].interleaving == 4 && state->fe.dtv_property_cache.isdbt_sb_mode == 1)) - ) - timeI = state->fe.dtv_property_cache.layer[i].interleaving; + if ((state->fe[0]->dtv_property_cache.layer[i].interleaving > 0) && + ((state->fe[0]->dtv_property_cache.layer[i].interleaving <= 3) || + (state->fe[0]->dtv_property_cache.layer[i].interleaving == 4 && state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1)) + ) + timeI = state->fe[0]->dtv_property_cache.layer[i].interleaving; else timeI = 0; - dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe.dtv_property_cache.layer[i].segment_count & 0xf) << 6) | - (crate << 3) | timeI); - if (state->fe.dtv_property_cache.layer[i].segment_count > 0) { + dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe[0]->dtv_property_cache.layer[i].segment_count & 0xf) << 6) | + (crate << 3) | timeI); + if (state->fe[0]->dtv_property_cache.layer[i].segment_count > 0) { switch (max_constellation) { case DQPSK: case QPSK: - if (state->fe.dtv_property_cache.layer[i].modulation == QAM_16 || - state->fe.dtv_property_cache.layer[i].modulation == QAM_64) - max_constellation = state->fe.dtv_property_cache.layer[i].modulation; + if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_16 || + state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) + max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; break; case QAM_16: - if (state->fe.dtv_property_cache.layer[i].modulation == QAM_64) - max_constellation = state->fe.dtv_property_cache.layer[i].modulation; + if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) + max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; break; } } @@ -1303,34 +1306,34 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear //dib8000_write_word(state, 5, 13); /*p_last_seg = 13*/ dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | - ((state->fe.dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe.dtv_property_cache. + ((state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe[0]->dtv_property_cache. isdbt_sb_mode & 1) << 4)); - dprintk("mode = %d ; guard = %d", mode, state->fe.dtv_property_cache.guard_interval); + dprintk("mode = %d ; guard = %d", mode, state->fe[0]->dtv_property_cache.guard_interval); /* signal optimization parameter */ - if (state->fe.dtv_property_cache.isdbt_partial_reception) { - seg_diff_mask = (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0]; + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception) { + seg_diff_mask = (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0]; for (i = 1; i < 3; i++) nbseg_diff += - (state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * state->fe.dtv_property_cache.layer[i].segment_count; + (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; for (i = 0; i < nbseg_diff; i++) seg_diff_mask |= 1 << permu_seg[i + 1]; } else { for (i = 0; i < 3; i++) nbseg_diff += - (state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * state->fe.dtv_property_cache.layer[i].segment_count; + (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; for (i = 0; i < nbseg_diff; i++) seg_diff_mask |= 1 << permu_seg[i]; } dprintk("nbseg_diff = %X (%d)", seg_diff_mask, seg_diff_mask); state->differential_constellation = (seg_diff_mask != 0); - dib8000_set_diversity_in(&state->fe, state->diversity_onoff); + dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb - if (state->fe.dtv_property_cache.isdbt_partial_reception == 1) // 3-segments + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) // 3-segments seg_mask13 = 0x00E0; else // 1-segment seg_mask13 = 0x0040; @@ -1340,7 +1343,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear // WRITE: Mode & Diff mask dib8000_write_word(state, 0, (mode << 13) | seg_diff_mask); - if ((seg_diff_mask) || (state->fe.dtv_property_cache.isdbt_sb_mode)) + if ((seg_diff_mask) || (state->fe[0]->dtv_property_cache.isdbt_sb_mode)) dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); else dib8000_write_word(state, 268, (2 << 9) | 39); //init value @@ -1351,26 +1354,26 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 353, seg_mask13); // ADDR 353 -/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */ - // dib8000_write_word(state, 351, (state->fe.dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5 ); +/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */ + // dib8000_write_word(state, 351, (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5 ); // ---- SMALL ---- - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { - switch (state->fe.dtv_property_cache.transmission_mode) { + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + switch (state->fe[0]->dtv_property_cache.transmission_mode) { case TRANSMISSION_MODE_2K: - if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg - if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK ncoeff = coeff_2k_sb_1seg_dqpsk; else // QPSK or QAM ncoeff = coeff_2k_sb_1seg; } else { // 3-segments - if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment - if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; else // QPSK or QAM on external segments ncoeff = coeff_2k_sb_3seg_0dqpsk; } else { // QPSK or QAM on central segment - if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments ncoeff = coeff_2k_sb_3seg_1dqpsk; else // QPSK or QAM on external segments ncoeff = coeff_2k_sb_3seg; @@ -1379,20 +1382,20 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear break; case TRANSMISSION_MODE_4K: - if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg - if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK ncoeff = coeff_4k_sb_1seg_dqpsk; else // QPSK or QAM ncoeff = coeff_4k_sb_1seg; } else { // 3-segments - if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment - if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; } else { // QPSK or QAM on external segments ncoeff = coeff_4k_sb_3seg_0dqpsk; } } else { // QPSK or QAM on central segment - if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments ncoeff = coeff_4k_sb_3seg_1dqpsk; } else // QPSK or QAM on external segments ncoeff = coeff_4k_sb_3seg; @@ -1403,20 +1406,20 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear case TRANSMISSION_MODE_AUTO: case TRANSMISSION_MODE_8K: default: - if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg - if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK ncoeff = coeff_8k_sb_1seg_dqpsk; else // QPSK or QAM ncoeff = coeff_8k_sb_1seg; } else { // 3-segments - if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment - if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; } else { // QPSK or QAM on external segments ncoeff = coeff_8k_sb_3seg_0dqpsk; } } else { // QPSK or QAM on central segment - if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments ncoeff = coeff_8k_sb_3seg_1dqpsk; } else // QPSK or QAM on external segments ncoeff = coeff_8k_sb_3seg; @@ -1430,22 +1433,22 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear // P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 dib8000_write_word(state, 351, - (state->fe.dtv_property_cache.isdbt_sb_mode << 9) | (state->fe.dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5); + (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 9) | (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5); // ---- COFF ---- // Carloff, the most robust - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // Sound Broadcasting mode - use both TMCC and AC pilots + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { // Sound Broadcasting mode - use both TMCC and AC pilots // P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64 // P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 dib8000_write_word(state, 187, - (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe.dtv_property_cache.isdbt_partial_reception & 1) << 2) - | 0x3); + (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 2) + | 0x3); -/* // P_small_coef_ext_enable = 1 */ -/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */ +/* // P_small_coef_ext_enable = 1 */ +/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */ - if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg // P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width= (P_mode == 3) , P_coff_one_seg_sym= (P_mode-1) if (mode == 3) @@ -1469,10 +1472,10 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 186, 80); } else { // Sound Broadcasting mode 3 seg // P_coff_one_seg_sym= 1, P_coff_one_seg_width= 1, P_coff_winlen=63, P_coff_thres_lock=15 - /* if (mode == 3) */ - /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */ - /* else */ - /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */ + /* if (mode == 3) */ + /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */ + /* else */ + /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */ dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); // P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, @@ -1509,7 +1512,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); } // ---- FFT ---- - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1 && state->fe.dtv_property_cache.isdbt_partial_reception == 0) // 1-seg + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 && state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) // 1-seg dib8000_write_word(state, 178, 64); // P_fft_powrange=64 else dib8000_write_word(state, 178, 32); // P_fft_powrange=32 @@ -1518,12 +1521,12 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) */ /* if ( ( nbseg_diff>0)&&(nbseg_diff<13)) - dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */ + dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */ dib8000_write_word(state, 189, ~seg_mask13 | seg_diff_mask); /* P_lmod4_seg_inh */ dib8000_write_word(state, 192, ~seg_mask13 | seg_diff_mask); /* P_pha3_seg_inh */ dib8000_write_word(state, 225, ~seg_mask13 | seg_diff_mask); /* P_tac_seg_inh */ - if ((!state->fe.dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0)) + if ((!state->fe[0]->dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0)) dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ else dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask); /* P_equal_noise_seg_inh */ @@ -1538,8 +1541,8 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 211, seg_mask13 & (~seg_diff_mask)); /* P_des_seg_enabled */ /* offset loop parameters */ - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x40); @@ -1551,8 +1554,8 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ dib8000_write_word(state, 32, ((9 - mode) << 12) | (6 << 8) | 0x80); - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (11-P_mode) */ dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (10 - mode)); @@ -1564,7 +1567,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (8 - mode)); /* P_dvsy_sync_wait - reuse mode */ - switch (state->fe.dtv_property_cache.transmission_mode) { + switch (state->fe[0]->dtv_property_cache.transmission_mode) { case TRANSMISSION_MODE_8K: mode = 256; break; @@ -1624,15 +1627,15 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear } // ---- ANA_FE ---- - if (state->fe.dtv_property_cache.isdbt_sb_mode) { - if (state->fe.dtv_property_cache.isdbt_partial_reception == 1) // 3-segments + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) // 3-segments ana_fe = ana_fe_coeff_3seg; else // 1-segment ana_fe = ana_fe_coeff_1seg; } else ana_fe = ana_fe_coeff_13seg; - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0) + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0) for (mode = 0; mode < 24; mode++) dib8000_write_word(state, 117 + mode, ana_fe[mode]); @@ -1648,11 +1651,11 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear // "P_cspu_left_edge" not used => do not care // "P_cspu_right_edge" not used => do not care - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb dib8000_write_word(state, 228, 1); // P_2d_mode_byp=1 dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); // P_cspu_win_cut = 0 - if (state->fe.dtv_property_cache.isdbt_partial_reception == 0 // 1-segment - && state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0 // 1-segment + && state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) { //dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); // P_adp_pass = 0 dib8000_write_word(state, 265, 15); // P_equal_noise_sel = 15 } @@ -1664,7 +1667,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear // ---- TMCC ---- for (i = 0; i < 3; i++) tmcc_pow += - (((state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe.dtv_property_cache.layer[i].segment_count); + (((state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe[0]->dtv_property_cache.layer[i].segment_count); // Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); // Threshold is set at 1/4 of max power. tmcc_pow *= (1 << (9 - 2)); @@ -1678,7 +1681,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear if (state->isdbt_cfg_loaded == 0) dib8000_write_word(state, 250, 3285); /*p_2d_hspeed_thr0 */ - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) state->isdbt_cfg_loaded = 0; else state->isdbt_cfg_loaded = 1; @@ -1693,38 +1696,38 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe) int slist = 0; - state->fe.dtv_property_cache.inversion = 0; - if (!state->fe.dtv_property_cache.isdbt_sb_mode) - state->fe.dtv_property_cache.layer[0].segment_count = 13; - state->fe.dtv_property_cache.layer[0].modulation = QAM_64; - state->fe.dtv_property_cache.layer[0].fec = FEC_2_3; - state->fe.dtv_property_cache.layer[0].interleaving = 0; + state->fe[0]->dtv_property_cache.inversion = 0; + if (!state->fe[0]->dtv_property_cache.isdbt_sb_mode) + state->fe[0]->dtv_property_cache.layer[0].segment_count = 13; + state->fe[0]->dtv_property_cache.layer[0].modulation = QAM_64; + state->fe[0]->dtv_property_cache.layer[0].fec = FEC_2_3; + state->fe[0]->dtv_property_cache.layer[0].interleaving = 0; //choose the right list, in sb, always do everything - if (state->fe.dtv_property_cache.isdbt_sb_mode) { - state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; - state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; slist = 7; dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); } else { - if (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) { - if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { + if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) { + if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { slist = 7; dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 to have autosearch start ok with mode2 } else slist = 3; } else { - if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { + if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { slist = 2; dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 } else slist = 0; } - if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) - state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; - if (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) - state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; dprintk("using list for autosearch : %d", slist); dib8000_set_channel(state, (unsigned char)slist, 1); @@ -1786,7 +1789,7 @@ static int dib8000_tune(struct dvb_frontend *fe) if (state == NULL) return -EINVAL; - dib8000_set_bandwidth(state, state->fe.dtv_property_cache.bandwidth_hz / 1000); + dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000); dib8000_set_channel(state, 0, 0); // restart demod @@ -1799,17 +1802,16 @@ static int dib8000_tune(struct dvb_frontend *fe) // never achieved a lock before - wait for timfreq to update if (state->timf == 0) { - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg msleep(300); else // Sound Broadcasting mode 3 seg msleep(500); } else // 13 seg msleep(200); } - //dump_reg(state); - if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40 alpha to check on board */ dib8000_write_word(state, 32, ((13 - mode) << 12) | (6 << 8) | 0x40); @@ -1854,26 +1856,38 @@ static int dib8000_tune(struct dvb_frontend *fe) static int dib8000_wakeup(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend; + int ret; dib8000_set_power_mode(state, DIB8000M_POWER_ALL); dib8000_set_adc_state(state, DIBX000_ADC_ON); if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) dprintk("could not start Slow ADC"); + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + ret = state->fe[index_frontend]->ops.init(state->fe[index_frontend]); + if (ret<0) + return ret; + } + return 0; } static int dib8000_sleep(struct dvb_frontend *fe) { - struct dib8000_state *st = fe->demodulator_priv; - if (1) { - dib8000_set_output_mode(st, OUTMODE_HIGH_Z); - dib8000_set_power_mode(st, DIB8000M_POWER_INTERFACE_ONLY); - return dib8000_set_adc_state(st, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(st, DIBX000_ADC_OFF); - } else { + struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend; + int ret; - return 0; + 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) + return ret; } + + dib8000_set_output_mode(fe, OUTMODE_HIGH_Z); + dib8000_set_power_mode(state, DIB8000M_POWER_INTERFACE_ONLY); + return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF); } enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) @@ -1891,16 +1905,40 @@ int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tun } EXPORT_SYMBOL(dib8000_set_tune_state); - - - static int dib8000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dib8000_state *state = fe->demodulator_priv; u16 i, val = 0; + fe_status_t stat; + u8 index_frontend, sub_index_frontend; fe->dtv_property_cache.bandwidth_hz = 6000000; + 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); + if (stat&FE_HAS_SYNC) { + dprintk("TMCC lock on the slave%i", index_frontend); + /* synchronize the cache with the other frontends */ + state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], fep); + for (sub_index_frontend=0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) { + if (sub_index_frontend != index_frontend) { + state->fe[sub_index_frontend]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode; + state->fe[sub_index_frontend]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; + state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode; + state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval; + state->fe[sub_index_frontend]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception; + for (i = 0; i < 3; i++) { + state->fe[sub_index_frontend]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count; + state->fe[sub_index_frontend]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving; + state->fe[sub_index_frontend]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec; + state->fe[sub_index_frontend]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation; + } + } + } + return 0; + } + } + fe->dtv_property_cache.isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1; val = dib8000_read_word(state, 570); @@ -1992,112 +2030,201 @@ static int dib8000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par break; } } + + /* synchronize the cache with the other frontends */ + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode = fe->dtv_property_cache.isdbt_sb_mode; + state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion; + state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode; + state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval; + state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception = fe->dtv_property_cache.isdbt_partial_reception; + for (i = 0; i < 3; i++) { + state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count = fe->dtv_property_cache.layer[i].segment_count; + state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving = fe->dtv_property_cache.layer[i].interleaving; + state->fe[index_frontend]->dtv_property_cache.layer[i].fec = fe->dtv_property_cache.layer[i].fec; + state->fe[index_frontend]->dtv_property_cache.layer[i].modulation = fe->dtv_property_cache.layer[i].modulation; + } + } return 0; } static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dib8000_state *state = fe->demodulator_priv; + u8 nbr_pending, exit_condition, index_frontend; + s8 index_frontend_success = -1; int time, ret; + int time_slave = FE_CALLBACK_TIME_NEVER; + + if (state->fe[0]->dtv_property_cache.frequency == 0) { + dprintk("dib8000: must at least specify frequency "); + return 0; + } + + if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) { + dprintk("dib8000: no bandwidth specified, set to default "); + state->fe[0]->dtv_property_cache.bandwidth_hz = 6000000; + } - fe->dtv_property_cache.delivery_system = SYS_ISDBT; + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + /* synchronization of the cache */ + state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_ISDBT; + memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); - dib8000_set_output_mode(state, OUTMODE_HIGH_Z); + dib8000_set_output_mode(state->fe[index_frontend], OUTMODE_HIGH_Z); + if (state->fe[index_frontend]->ops.tuner_ops.set_params) + state->fe[index_frontend]->ops.tuner_ops.set_params(state->fe[index_frontend], fep); - if (fe->ops.tuner_ops.set_params) - fe->ops.tuner_ops.set_params(fe, fep); + dib8000_set_tune_state(state->fe[index_frontend], CT_AGC_START); + } /* start up the AGC */ - state->tune_state = CT_AGC_START; do { - time = dib8000_agc_startup(fe); + time = dib8000_agc_startup(state->fe[0]); + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + time_slave = dib8000_agc_startup(state->fe[index_frontend]); + if (time == FE_CALLBACK_TIME_NEVER) + time = time_slave; + else if ((time_slave != FE_CALLBACK_TIME_NEVER) && (time_slave > time)) + time = time_slave; + } if (time != FE_CALLBACK_TIME_NEVER) msleep(time / 10); else break; - } while (state->tune_state != CT_AGC_STOP); - - if (state->fe.dtv_property_cache.frequency == 0) { - dprintk("dib8000: must at least specify frequency "); - return 0; + exit_condition = 1; + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_AGC_STOP) { + exit_condition = 0; + break; + } + } + } while (exit_condition == 0); + + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + dib8000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); + + if ((state->fe[0]->dtv_property_cache.delivery_system != SYS_ISDBT) || + (state->fe[0]->dtv_property_cache.inversion == INVERSION_AUTO) || + (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) || + (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) || + (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) && + (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0xff) && + (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0) && + ((state->fe[0]->dtv_property_cache.layer[0].modulation == QAM_AUTO) || + (state->fe[0]->dtv_property_cache.layer[0].fec == FEC_AUTO))) || + (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) && + (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0xff) && + (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0) && + ((state->fe[0]->dtv_property_cache.layer[1].modulation == QAM_AUTO) || + (state->fe[0]->dtv_property_cache.layer[1].fec == FEC_AUTO))) || + (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) && + (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0xff) && + (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0) && + ((state->fe[0]->dtv_property_cache.layer[2].modulation == QAM_AUTO) || + (state->fe[0]->dtv_property_cache.layer[2].fec == FEC_AUTO))) || + (((state->fe[0]->dtv_property_cache.layer[0].segment_count == 0) || + ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) && + ((state->fe[0]->dtv_property_cache.layer[1].segment_count == 0) || + ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && + ((state->fe[0]->dtv_property_cache.layer[2].segment_count == 0) || ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) { + int i = 80000; + u8 found = 0; + u8 tune_failed = 0; + + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000); + dib8000_autosearch_start(state->fe[index_frontend]); + } + + do { + msleep(10); + nbr_pending = 0; + exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (((tune_failed >> index_frontend) & 0x1) == 0) { + found = dib8000_autosearch_irq(state->fe[index_frontend]); + switch (found) { + case 0: /* tune pending */ + nbr_pending++; + break; + case 2: + dprintk("autosearch succeed on the frontend%i", index_frontend); + exit_condition = 2; + index_frontend_success = index_frontend; + break; + default: + dprintk("unhandled autosearch result"); + case 1: + tune_failed |= (1 << index_frontend); + dprintk("autosearch failed for the frontend%i", index_frontend); + break; + } + } + } + + /* if all tune are done and no success, exit: tune failed */ + if ((nbr_pending == 0) && (exit_condition == 0)) + exit_condition = 1; + } while ((exit_condition == 0) && i--); + + if (exit_condition == 1) { /* tune failed */ + dprintk("tune failed"); + return 0; + } + + dprintk("tune success on frontend%i", index_frontend_success); + + dib8000_get_frontend(fe, fep); + } + + for (index_frontend=0, ret=0; (ret >= 0) && (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + ret = dib8000_tune(state->fe[index_frontend]); } - if (state->fe.dtv_property_cache.bandwidth_hz == 0) { - dprintk("dib8000: no bandwidth specified, set to default "); - state->fe.dtv_property_cache.bandwidth_hz = 6000000; + /* set output mode and diversity input */ + dib8000_set_output_mode(state->fe[0], state->cfg.output_mode); + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + dib8000_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY); + dib8000_set_diversity_in(state->fe[index_frontend-1], 1); } - state->tune_state = CT_DEMOD_START; - - if ((state->fe.dtv_property_cache.delivery_system != SYS_ISDBT) || - (state->fe.dtv_property_cache.inversion == INVERSION_AUTO) || - (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) || - (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) || - (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) && - (state->fe.dtv_property_cache.layer[0].segment_count != 0xff) && - (state->fe.dtv_property_cache.layer[0].segment_count != 0) && - ((state->fe.dtv_property_cache.layer[0].modulation == QAM_AUTO) || - (state->fe.dtv_property_cache.layer[0].fec == FEC_AUTO))) || - (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) && - (state->fe.dtv_property_cache.layer[1].segment_count != 0xff) && - (state->fe.dtv_property_cache.layer[1].segment_count != 0) && - ((state->fe.dtv_property_cache.layer[1].modulation == QAM_AUTO) || - (state->fe.dtv_property_cache.layer[1].fec == FEC_AUTO))) || - (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) && - (state->fe.dtv_property_cache.layer[2].segment_count != 0xff) && - (state->fe.dtv_property_cache.layer[2].segment_count != 0) && - ((state->fe.dtv_property_cache.layer[2].modulation == QAM_AUTO) || - (state->fe.dtv_property_cache.layer[2].fec == FEC_AUTO))) || - (((state->fe.dtv_property_cache.layer[0].segment_count == 0) || - ((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) && - ((state->fe.dtv_property_cache.layer[1].segment_count == 0) || - ((state->fe.dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && - ((state->fe.dtv_property_cache.layer[2].segment_count == 0) || ((state->fe.dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) { - int i = 800, found; - - dib8000_set_bandwidth(state, fe->dtv_property_cache.bandwidth_hz / 1000); - dib8000_autosearch_start(fe); - do { - msleep(10); - found = dib8000_autosearch_irq(fe); - } while (found == 0 && i--); - - dprintk("Frequency %d Hz, autosearch returns: %d", fep->frequency, found); - - if (found == 0 || found == 1) - return 0; // no channel found - - dib8000_get_frontend(fe, fep); - } + /* turn off the diversity of the last chip */ + dib8000_set_diversity_in(state->fe[index_frontend-1], 0); - ret = dib8000_tune(fe); + return ret; +} - /* make this a config parameter */ - dib8000_set_output_mode(state, state->cfg.output_mode); +static u16 dib8000_read_lock(struct dvb_frontend *fe) { + struct dib8000_state *state = fe->demodulator_priv; - return ret; + return dib8000_read_word(state, 568); } static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) { struct dib8000_state *state = fe->demodulator_priv; - u16 lock = dib8000_read_word(state, 568); + u16 lock_slave = 0, lock = dib8000_read_word(state, 568); + u8 index_frontend; + + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + lock_slave |= dib8000_read_lock(state->fe[index_frontend]); *stat = 0; - if ((lock >> 13) & 1) + if (((lock >> 13) & 1) || ((lock_slave >> 13) & 1)) *stat |= FE_HAS_SIGNAL; - if ((lock >> 8) & 1) /* Equal */ + if (((lock >> 8) & 1) || ((lock_slave >> 8) & 1)) /* Equal */ *stat |= FE_HAS_CARRIER; - if (((lock >> 1) & 0xf) == 0xf) /* TMCC_SYNC */ + if ((((lock >> 1) & 0xf) == 0xf) || (((lock_slave >> 1) & 0xf) == 0xf)) /* TMCC_SYNC */ *stat |= FE_HAS_SYNC; - if (((lock >> 12) & 1) && ((lock >> 5) & 7)) /* FEC MPEG */ + if ((((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) && ((lock >> 5) & 7)) /* FEC MPEG */ *stat |= FE_HAS_LOCK; - if ((lock >> 12) & 1) { + if (((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) { lock = dib8000_read_word(state, 554); /* Viterbi Layer A */ if (lock & 0x01) *stat |= FE_HAS_VITERBI; @@ -2131,44 +2258,120 @@ static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) static int dib8000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) { struct dib8000_state *state = fe->demodulator_priv; - u16 val = dib8000_read_word(state, 390); - *strength = 65535 - val; + u8 index_frontend; + u16 val; + + *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); + if (val > 65535 - *strength) + *strength = 65535; + else + *strength += val; + } + + val = 65535 - dib8000_read_word(state, 390); + if (val > 65535 - *strength) + *strength = 65535; + else + *strength += val; return 0; } -static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) +static u32 dib8000_get_snr(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; + u32 n, s, exp; u16 val; - s32 signal_mant, signal_exp, noise_mant, noise_exp; - u32 result = 0; val = dib8000_read_word(state, 542); - noise_mant = (val >> 6) & 0xff; - noise_exp = (val & 0x3f); + n = (val >> 6) & 0xff; + exp = (val & 0x3f); + if ((exp & 0x20) != 0) + exp -= 0x40; + n <<= exp+16; val = dib8000_read_word(state, 543); - signal_mant = (val >> 6) & 0xff; - signal_exp = (val & 0x3f); + s = (val >> 6) & 0xff; + exp = (val & 0x3f); + if ((exp & 0x20) != 0) + exp -= 0x40; + s <<= exp+16; + + if (n > 0) { + u32 t = (s/n) << 16; + return t + ((s << 16) - n*t) / n; + } + return 0xffffffff; +} + +static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend; + u32 snr_master; - if ((noise_exp & 0x20) != 0) - noise_exp -= 0x40; - if ((signal_exp & 0x20) != 0) - signal_exp -= 0x40; + snr_master = dib8000_get_snr(fe); + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + snr_master += dib8000_get_snr(state->fe[index_frontend]); - if (signal_mant != 0) - result = intlog10(2) * 10 * signal_exp + 10 * intlog10(signal_mant); - else - result = intlog10(2) * 10 * signal_exp - 100; - if (noise_mant != 0) - result -= intlog10(2) * 10 * noise_exp + 10 * intlog10(noise_mant); + if (snr_master != 0) { + snr_master = 10*intlog10(snr_master>>16); + *snr = snr_master / ((1 << 24) / 10); + } else - result -= intlog10(2) * 10 * noise_exp - 100; + *snr = 0; - *snr = result / ((1 << 24) / 10); return 0; } +int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +{ + struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend = 1; + + while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) + index_frontend++; + if (index_frontend < MAX_NUMBER_OF_FRONTENDS) { + dprintk("set slave fe %p to index %i", fe_slave, index_frontend); + state->fe[index_frontend] = fe_slave; + return 0; + } + + dprintk("too many slave frontend"); + return -ENOMEM; +} +EXPORT_SYMBOL(dib8000_set_slave_frontend); + +int dib8000_remove_slave_frontend(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend = 1; + + while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) + index_frontend++; + if (index_frontend != 1) { + dprintk("remove slave fe %p (index %i)", state->fe[index_frontend-1], index_frontend-1); + state->fe[index_frontend] = NULL; + return 0; + } + + dprintk("no frontend to be removed"); + return -ENODEV; +} +EXPORT_SYMBOL(dib8000_remove_slave_frontend); + +struct dvb_frontend * dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +{ + struct dib8000_state *state = fe->demodulator_priv; + + if (slave_index >= MAX_NUMBER_OF_FRONTENDS) + return NULL; + return state->fe[slave_index]; +} +EXPORT_SYMBOL(dib8000_get_slave_frontend); + + int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr) { int k = 0; @@ -2227,7 +2430,13 @@ static int dib8000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_fron static void dib8000_release(struct dvb_frontend *fe) { struct dib8000_state *st = fe->demodulator_priv; + u8 index_frontend; + + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) + dvb_frontend_detach(st->fe[index_frontend]); + dibx000_exit_i2c_master(&st->i2c_master); + kfree(st->fe[0]); kfree(st); } @@ -2242,19 +2451,19 @@ EXPORT_SYMBOL(dib8000_get_i2c_master); int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) { struct dib8000_state *st = fe->demodulator_priv; - u16 val = dib8000_read_word(st, 299) & 0xffef; - val |= (onoff & 0x1) << 4; + u16 val = dib8000_read_word(st, 299) & 0xffef; + val |= (onoff & 0x1) << 4; - dprintk("pid filter enabled %d", onoff); - return dib8000_write_word(st, 299, val); + dprintk("pid filter enabled %d", onoff); + return dib8000_write_word(st, 299, val); } EXPORT_SYMBOL(dib8000_pid_filter_ctrl); int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) { struct dib8000_state *st = fe->demodulator_priv; - dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); - return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0); + dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); + return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0); } EXPORT_SYMBOL(dib8000_pid_filter); @@ -2298,6 +2507,9 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s state = kzalloc(sizeof(struct dib8000_state), GFP_KERNEL); if (state == NULL) return NULL; + fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL); + if (fe == NULL) + return NULL; memcpy(&state->cfg, cfg, sizeof(struct dib8000_config)); state->i2c.adap = i2c_adap; @@ -2311,9 +2523,9 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s if ((state->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (state->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) state->cfg.output_mode = OUTMODE_MPEG2_FIFO; - fe = &state->fe; + state->fe[0] = fe; fe->demodulator_priv = state; - memcpy(&state->fe.ops, &dib8000_ops, sizeof(struct dvb_frontend_ops)); + memcpy(&state->fe[0]->ops, &dib8000_ops, sizeof(struct dvb_frontend_ops)); state->timf_default = cfg->pll->timf; diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h index e0a9ded..558b7e8 100644 --- a/drivers/media/dvb/frontends/dib8000.h +++ b/drivers/media/dvb/frontends/dib8000.h @@ -50,6 +50,9 @@ extern int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_st extern enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe); extern void dib8000_pwm_agc_reset(struct dvb_frontend *fe); extern s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode); +extern int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); +extern int dib8000_remove_slave_frontend(struct dvb_frontend *fe); +extern struct dvb_frontend * dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); #else static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) { @@ -111,6 +114,22 @@ static inline s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return 0; } +static inline int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +int dib8000_remove_slave_frontend(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline struct dvb_frontend * dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) { + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} #endif #endif -- cgit v0.10.2 From b994d19268756b640ccc76f0b0d47ee13c0f8af9 Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Mon, 3 Jan 2011 15:39:35 -0300 Subject: [media] DiBx000: add addition i2c-interface names This patch adds the possibitity to use different I2C-ports to talk to slave-devices than the standard ones. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c index 2311c0a..9bd95a9 100644 --- a/drivers/media/dvb/frontends/dibx000_common.c +++ b/drivers/media/dvb/frontends/dibx000_common.c @@ -17,9 +17,144 @@ static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) struct i2c_msg msg = { .addr = mst->i2c_addr,.flags = 0,.buf = b,.len = 4 }; + return i2c_transfer(mst->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; } +static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg) +{ + u8 wb[2] = { reg >> 8, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[2] = { + {.addr = mst->i2c_addr,.flags = 0,.buf = wb,.len = 2}, + {.addr = mst->i2c_addr,.flags = I2C_M_RD,.buf = rb,.len = 2}, + }; + + if (i2c_transfer(mst->i2c_adap, msg, 2) != 2) + dprintk("i2c read error on %d", reg); + + return (rb[0] << 8) | rb[1]; +} + +static int dibx000_is_i2c_done(struct dibx000_i2c_master *mst) +{ + int i = 100; // max_i2c_polls; + u16 status; + + while (((status = dibx000_read_word(mst, mst->base_reg + 2)) & 0x0100) == 0 && --i > 0); + + /* i2c timed out */ + if (i == 0) + return -EREMOTEIO; + + /* no acknowledge */ + if ((status & 0x0080) == 0) + return -EREMOTEIO; + + return 0; +} + +static int dibx000_master_i2c_write(struct dibx000_i2c_master *mst, struct i2c_msg *msg, u8 stop) +{ + u16 data; + u16 da; + u16 i; + u16 txlen = msg->len, len; + const u8 *b = msg->buf; + + while (txlen) { + dibx000_read_word(mst, mst->base_reg + 2); // reset fifo ptr + + len = txlen > 8 ? 8 : txlen; + for (i = 0; i < len; i += 2) { + data = *b++ << 8; + if (i+1 < len) + data |= *b++; + dibx000_write_word(mst, mst->base_reg, data); + } + da = (((u8) (msg->addr)) << 9) | // addr + (1 << 8) | // master + (1 << 7) | // rq + (0 << 6) | // stop + (0 << 5) | // start + ((len & 0x7) << 2) | // nb 8 bytes == 0 here + (0 << 1) | // rw + (0 << 0); // irqen + + if (txlen == msg->len) + da |= 1 << 5; /* start */ + + if (txlen-len == 0 && stop) + da |= 1 << 6; /* stop */ + + dibx000_write_word(mst, mst->base_reg+1, da); + + if (dibx000_is_i2c_done(mst) != 0) + return -EREMOTEIO; + txlen -= len; + } + + return 0; +} + +static int dibx000_master_i2c_read(struct dibx000_i2c_master *mst, struct i2c_msg *msg) +{ + u16 da; + u8 *b = msg->buf; + u16 rxlen = msg->len, len; + + while (rxlen) { + len = rxlen > 8 ? 8 : rxlen; + da = (((u8) (msg->addr)) << 9) | // addr + (1 << 8) | // master + (1 << 7) | // rq + (0 << 6) | // stop + (0 << 5) | // start + ((len & 0x7) << 2) | // nb + (1 << 1) | // rw + (0 << 0); // irqen + + if (rxlen == msg->len) + da |= 1 << 5; /* start */ + + if (rxlen-len == 0) + da |= 1 << 6; /* stop */ + dibx000_write_word(mst, mst->base_reg+1, da); + + if (dibx000_is_i2c_done(mst) != 0) + return -EREMOTEIO; + + rxlen -= len; + + while (len) { + da = dibx000_read_word(mst, mst->base_reg); + *b++ = (da >> 8) & 0xff; + len--; + if (len >= 1) { + *b++ = da & 0xff; + len--; + } + } + } + + return 0; +} + +int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed) +{ + struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + + if (mst->device_rev < DIB7000MC && speed < 235) + speed = 235; + return dibx000_write_word(mst, mst->base_reg + 3, (u16)(60000 / speed)); + +} +EXPORT_SYMBOL(dibx000_i2c_set_speed); + +static u32 dibx000_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf) @@ -32,6 +167,66 @@ static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, return 0; } +static int dibx000_i2c_master_xfer_gpio12(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + int msg_index; + int ret = 0; + + dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_1_2); + for (msg_index = 0; msg_indexi2c_addr; + m[0].buf = tx_open; + m[0].len = 4; + + memcpy(&m[1], msg, sizeof(struct i2c_msg) * num); + + dibx000_i2c_gate_ctrl(mst, tx_close, 0, 0); + m[num + 1].addr = mst->i2c_addr; + m[num + 1].buf = tx_close; + m[num + 1].len = 4; + + return i2c_transfer(mst->i2c_adap, m, 2 + num) == 2 + num ? num : -EIO; } +static struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = { + .master_xfer = dibx000_i2c_gated_gpio67_xfer, + .functionality = dibx000_i2c_func, +}; + static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) { @@ -91,8 +312,8 @@ static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { }; struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, - enum dibx000_i2c_interface intf, - int gating) + enum dibx000_i2c_interface intf, + int gating) { struct i2c_adapter *i2c = NULL; @@ -101,6 +322,18 @@ struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, if (gating) i2c = &mst->gated_tuner_i2c_adap; break; + case DIBX000_I2C_INTERFACE_GPIO_1_2: + if (!gating) + i2c = &mst->master_i2c_adap_gpio12; + break; + case DIBX000_I2C_INTERFACE_GPIO_3_4: + if (!gating) + i2c = &mst->master_i2c_adap_gpio34; + break; + case DIBX000_I2C_INTERFACE_GPIO_6_7: + if (gating) + i2c = &mst->master_i2c_adap_gpio67; + break; default: printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n"); break; @@ -126,8 +359,8 @@ void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst) EXPORT_SYMBOL(dibx000_reset_i2c_master); static int i2c_adapter_init(struct i2c_adapter *i2c_adap, - struct i2c_algorithm *algo, const char *name, - struct dibx000_i2c_master *mst) + struct i2c_algorithm *algo, const char *name, + struct dibx000_i2c_master *mst) { strncpy(i2c_adap->name, name, sizeof(i2c_adap->name)); i2c_adap->algo = algo; @@ -139,7 +372,7 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap, } int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, - struct i2c_adapter *i2c_adap, u8 i2c_addr) + struct i2c_adapter *i2c_adap, u8 i2c_addr) { u8 tx[4]; struct i2c_msg m = {.addr = i2c_addr >> 1,.buf = tx,.len = 4 }; @@ -153,11 +386,33 @@ int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, else mst->base_reg = 768; + mst->gated_tuner_i2c_adap.dev.parent = mst->i2c_adap->dev.parent; + if (i2c_adapter_init + (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, + "DiBX000 tuner I2C bus", mst) != 0) + printk(KERN_ERR + "DiBX000: could not initialize the tuner i2c_adapter\n"); + + mst->master_i2c_adap_gpio12.dev.parent = mst->i2c_adap->dev.parent; + if (i2c_adapter_init + (&mst->master_i2c_adap_gpio12, &dibx000_i2c_master_gpio12_xfer_algo, + "DiBX000 master GPIO12 I2C bus", mst) != 0) + printk(KERN_ERR + "DiBX000: could not initialize the master i2c_adapter\n"); + + mst->master_i2c_adap_gpio34.dev.parent = mst->i2c_adap->dev.parent; + if (i2c_adapter_init + (&mst->master_i2c_adap_gpio34, &dibx000_i2c_master_gpio34_xfer_algo, + "DiBX000 master GPIO34 I2C bus", mst) != 0) + printk(KERN_ERR + "DiBX000: could not initialize the master i2c_adapter\n"); + + mst->master_i2c_adap_gpio67.dev.parent = mst->i2c_adap->dev.parent; if (i2c_adapter_init - (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, - "DiBX000 tuner I2C bus", mst) != 0) + (&mst->master_i2c_adap_gpio67, &dibx000_i2c_gated_gpio67_algo, + "DiBX000 master GPIO67 I2C bus", mst) != 0) printk(KERN_ERR - "DiBX000: could not initialize the tuner i2c_adapter\n"); + "DiBX000: could not initialize the master i2c_adapter\n"); /* initialize the i2c-master by closing the gate */ dibx000_i2c_gate_ctrl(mst, tx, 0, 0); @@ -170,16 +425,19 @@ EXPORT_SYMBOL(dibx000_init_i2c_master); void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst) { i2c_del_adapter(&mst->gated_tuner_i2c_adap); + i2c_del_adapter(&mst->master_i2c_adap_gpio12); + i2c_del_adapter(&mst->master_i2c_adap_gpio34); + i2c_del_adapter(&mst->master_i2c_adap_gpio67); } EXPORT_SYMBOL(dibx000_exit_i2c_master); u32 systime(void) { - struct timespec t; + struct timespec t; - t = current_kernel_time(); - return (t.tv_sec * 10000) + (t.tv_nsec / 100000); + t = current_kernel_time(); + return (t.tv_sec * 10000) + (t.tv_nsec / 100000); } EXPORT_SYMBOL(systime); diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h index 4f5d141..b7ad066 100644 --- a/drivers/media/dvb/frontends/dibx000_common.h +++ b/drivers/media/dvb/frontends/dibx000_common.h @@ -4,7 +4,8 @@ enum dibx000_i2c_interface { DIBX000_I2C_INTERFACE_TUNER = 0, DIBX000_I2C_INTERFACE_GPIO_1_2 = 1, - DIBX000_I2C_INTERFACE_GPIO_3_4 = 2 + DIBX000_I2C_INTERFACE_GPIO_3_4 = 2, + DIBX000_I2C_INTERFACE_GPIO_6_7 = 3 }; struct dibx000_i2c_master { @@ -17,8 +18,11 @@ struct dibx000_i2c_master { enum dibx000_i2c_interface selected_interface; -// struct i2c_adapter tuner_i2c_adap; +// struct i2c_adapter tuner_i2c_adap; struct i2c_adapter gated_tuner_i2c_adap; + struct i2c_adapter master_i2c_adap_gpio12; + struct i2c_adapter master_i2c_adap_gpio34; + struct i2c_adapter master_i2c_adap_gpio67; struct i2c_adapter *i2c_adap; u8 i2c_addr; @@ -27,14 +31,15 @@ struct dibx000_i2c_master { }; extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, - u16 device_rev, struct i2c_adapter *i2c_adap, - u8 i2c_addr); + u16 device_rev, struct i2c_adapter *i2c_adap, + u8 i2c_addr); extern struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master - *mst, - enum dibx000_i2c_interface - intf, int gating); + *mst, + enum dibx000_i2c_interface + intf, int gating); extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst); +extern int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed); extern u32 systime(void); @@ -42,10 +47,10 @@ extern u32 systime(void); #define BAND_UHF 0x02 #define BAND_VHF 0x04 #define BAND_SBAND 0x08 -#define BAND_FM 0x10 +#define BAND_FM 0x10 #define BAND_CBAND 0x20 -#define BAND_OF_FREQUENCY(freq_kHz) ((freq_kHz) <= 170000 ? BAND_CBAND : \ +#define BAND_OF_FREQUENCY(freq_kHz) ( (freq_kHz) <= 170000 ? BAND_CBAND : \ (freq_kHz) <= 115000 ? BAND_FM : \ (freq_kHz) <= 250000 ? BAND_VHF : \ (freq_kHz) <= 863000 ? BAND_UHF : \ -- cgit v0.10.2 From 28fafca78797be701208c0880ec1c79ffa267f9d Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Tue, 4 Jan 2011 04:27:11 -0300 Subject: [media] DiB0090: misc improvements This patch adds several performance improvements and prepares the usage of firmware-based devices. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c index 65240b7..0e87a0b 100644 --- a/drivers/media/dvb/frontends/dib0090.c +++ b/drivers/media/dvb/frontends/dib0090.c @@ -45,6 +45,7 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); } \ } while (0) +#define CONFIG_SYS_DVBT #define CONFIG_SYS_ISDBT #define CONFIG_BAND_CBAND #define CONFIG_BAND_VHF @@ -76,6 +77,34 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); #define EN_SBD 0x44E9 #define EN_CAB 0x88E9 +/* Calibration defines */ +#define DC_CAL 0x1 +#define WBD_CAL 0x2 +#define TEMP_CAL 0x4 +#define CAPTRIM_CAL 0x8 + +#define KROSUS_PLL_LOCKED 0x800 +#define KROSUS 0x2 + +/* Use those defines to identify SOC version */ +#define SOC 0x02 +#define SOC_7090_P1G_11R1 0x82 +#define SOC_7090_P1G_21R1 0x8a +#define SOC_8090_P1G_11R1 0x86 +#define SOC_8090_P1G_21R1 0x8e + +/* else use thos ones to check */ +#define P1A_B 0x0 +#define P1C 0x1 +#define P1D_E_F 0x3 +#define P1G 0x7 +#define P1G_21R2 0xf + +#define MP001 0x1 /* Single 9090/8096 */ +#define MP005 0x4 /* Single Sband */ +#define MP008 0x6 /* Dual diversity VHF-UHF-LBAND */ +#define MP009 0x7 /* Dual diversity 29098 CBAND-UHF-LBAND-SBAND */ + #define pgm_read_word(w) (*w) struct dc_calibration; @@ -84,7 +113,7 @@ struct dib0090_tuning { u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ u8 switch_trim; u8 lna_tune; - u8 lna_bias; + u16 lna_bias; u16 v2i; u16 mix; u16 load; @@ -99,13 +128,19 @@ struct dib0090_pll { u8 topresc; }; +struct dib0090_identity { + u8 version; + u8 product; + u8 p1g; + u8 in_soc; +}; + struct dib0090_state { struct i2c_adapter *i2c; struct dvb_frontend *fe; const struct dib0090_config *config; u8 current_band; - u16 revision; enum frontend_tune_state tune_state; u32 current_rf; @@ -143,15 +178,34 @@ struct dib0090_state { u8 tuner_is_tuned; u8 agc_freeze; - u8 reset; + struct dib0090_identity identity; + + u32 rf_request; + u8 current_standard; + + u8 calibrate; + u32 rest; + u16 bias; + s16 temperature; + + u8 wbd_calibration_gain; + const struct dib0090_wbd_slope *current_wbd_table; + u16 wbdmux; +}; + +struct dib0090_fw_state { + struct i2c_adapter *i2c; + struct dvb_frontend *fe; + struct dib0090_identity identity; + const struct dib0090_config *config; }; static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg) { u8 b[2]; struct i2c_msg msg[2] = { - {.addr = state->config->i2c_address, .flags = 0, .buf = ®, .len = 1}, - {.addr = state->config->i2c_address, .flags = I2C_M_RD, .buf = b, .len = 2}, + {.addr = state->config->i2c_address,.flags = 0,.buf = ®,.len = 1}, + {.addr = state->config->i2c_address,.flags = I2C_M_RD,.buf = b,.len = 2}, }; if (i2c_transfer(state->i2c, msg, 2) != 2) { printk(KERN_WARNING "DiB0090 I2C read failed\n"); @@ -163,7 +217,29 @@ static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg) static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val) { u8 b[3] = { reg & 0xff, val >> 8, val & 0xff }; - struct i2c_msg msg = {.addr = state->config->i2c_address, .flags = 0, .buf = b, .len = 3 }; + struct i2c_msg msg = {.addr = state->config->i2c_address,.flags = 0,.buf = b,.len = 3 }; + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "DiB0090 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg) +{ + u8 b[2]; + struct i2c_msg msg = {.addr = reg,.flags = I2C_M_RD,.buf = b,.len = 2 }; + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "DiB0090 I2C read failed\n"); + return 0; + } + return (b[0] << 8) | b[1]; +} + +static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val) +{ + u8 b[2] = { val >> 8, val & 0xff }; + struct i2c_msg msg = {.addr = reg,.flags = 0,.buf = b,.len = 2 }; if (i2c_transfer(state->i2c, &msg, 1) != 1) { printk(KERN_WARNING "DiB0090 I2C write failed\n"); return -EREMOTEIO; @@ -183,89 +259,329 @@ static void dib0090_write_regs(struct dib0090_state *state, u8 r, const u16 * b, } while (--c); } -static u16 dib0090_identify(struct dvb_frontend *fe) +static int dib0090_identify(struct dvb_frontend *fe) { struct dib0090_state *state = fe->tuner_priv; u16 v; + struct dib0090_identity *identity = &state->identity; v = dib0090_read_reg(state, 0x1a); -#ifdef FIRMWARE_FIREFLY - /* pll is not locked locked */ - if (!(v & 0x800)) - dprintk("FE%d : Identification : pll is not yet locked", fe->id); -#endif + identity->p1g = 0; + identity->in_soc = 0; + + dprintk("Tuner identification (Version = 0x%04x)", v); /* without PLL lock info */ - v &= 0x3ff; - dprintk("P/V: %04x:", v); + v &= ~KROSUS_PLL_LOCKED; - if ((v >> 8) & 0xf) - dprintk("FE%d : Product ID = 0x%x : KROSUS", fe->id, (v >> 8) & 0xf); - else - return 0xff; - - v &= 0xff; - if (((v >> 5) & 0x7) == 0x1) - dprintk("FE%d : MP001 : 9090/8096", fe->id); - else if (((v >> 5) & 0x7) == 0x4) - dprintk("FE%d : MP005 : Single Sband", fe->id); - else if (((v >> 5) & 0x7) == 0x6) - dprintk("FE%d : MP008 : diversity VHF-UHF-LBAND", fe->id); - else if (((v >> 5) & 0x7) == 0x7) - dprintk("FE%d : MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND", fe->id); - else - return 0xff; - - /* revision only */ - if ((v & 0x1f) == 0x3) - dprintk("FE%d : P1-D/E/F detected", fe->id); - else if ((v & 0x1f) == 0x1) - dprintk("FE%d : P1C detected", fe->id); - else if ((v & 0x1f) == 0x0) { -#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT - dprintk("FE%d : P1-A/B detected: using previous driver - support will be removed soon", fe->id); - dib0090_p1b_register(fe); -#else - dprintk("FE%d : P1-A/B detected: driver is deactivated - not available", fe->id); - return 0xff; -#endif + identity->version = v & 0xff; + identity->product = (v >> 8) & 0xf; + + if (identity->product != KROSUS) + goto identification_error; + + if ((identity->version & 0x3) == SOC) { + identity->in_soc = 1; + switch (identity->version) { + case SOC_8090_P1G_11R1: + dprintk("SOC 8090 P1-G11R1 Has been detected"); + identity->p1g = 1; + break; + case SOC_8090_P1G_21R1: + dprintk("SOC 8090 P1-G21R1 Has been detected"); + identity->p1g = 1; + break; + case SOC_7090_P1G_11R1: + dprintk("SOC 7090 P1-G11R1 Has been detected"); + identity->p1g = 1; + break; + case SOC_7090_P1G_21R1: + dprintk("SOC 7090 P1-G21R1 Has been detected"); + identity->p1g = 1; + break; + default: + goto identification_error; + } + } else { + switch ((identity->version >> 5) & 0x7) { + case MP001: + dprintk("MP001 : 9090/8096"); + break; + case MP005: + dprintk("MP005 : Single Sband"); + break; + case MP008: + dprintk("MP008 : diversity VHF-UHF-LBAND"); + break; + case MP009: + dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND"); + break; + default: + goto identification_error; + } + + switch (identity->version & 0x1f) { + case P1G_21R2: + dprintk("P1G_21R2 detected"); + identity->p1g = 1; + break; + case P1G: + dprintk("P1G detected"); + identity->p1g = 1; + break; + case P1D_E_F: + dprintk("P1D/E/F detected"); + break; + case P1C: + dprintk("P1C detected"); + break; + case P1A_B: + dprintk("P1-A/B detected: driver is deactivated - not available"); + goto identification_error; + break; + default: + goto identification_error; + } } - return v; + return 0; + + identification_error: + return -EIO; +} + +static int dib0090_fw_identify(struct dvb_frontend *fe) +{ + struct dib0090_fw_state *state = fe->tuner_priv; + struct dib0090_identity *identity = &state->identity; + + u16 v = dib0090_fw_read_reg(state, 0x1a); + identity->p1g = 0; + identity->in_soc = 0; + + dprintk("FE: Tuner identification (Version = 0x%04x)", v); + + /* without PLL lock info */ + v &= ~KROSUS_PLL_LOCKED; + + identity->version = v & 0xff; + identity->product = (v >> 8) & 0xf; + + if (identity->product != KROSUS) + goto identification_error; + + //From the SOC the version definition has changed + + if ((identity->version & 0x3) == SOC) { + identity->in_soc = 1; + switch (identity->version) { + case SOC_8090_P1G_11R1: + dprintk("SOC 8090 P1-G11R1 Has been detected"); + identity->p1g = 1; + break; + case SOC_8090_P1G_21R1: + dprintk("SOC 8090 P1-G21R1 Has been detected"); + identity->p1g = 1; + break; + case SOC_7090_P1G_11R1: + dprintk("SOC 7090 P1-G11R1 Has been detected"); + identity->p1g = 1; + break; + case SOC_7090_P1G_21R1: + dprintk("SOC 7090 P1-G21R1 Has been detected"); + identity->p1g = 1; + break; + default: + goto identification_error; + } + } else { + switch ((identity->version >> 5) & 0x7) { + case MP001: + dprintk("MP001 : 9090/8096"); + break; + case MP005: + dprintk("MP005 : Single Sband"); + break; + case MP008: + dprintk("MP008 : diversity VHF-UHF-LBAND"); + break; + case MP009: + dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND"); + break; + default: + goto identification_error; + } + + switch (identity->version & 0x1f) { + case P1G_21R2: + dprintk("P1G_21R2 detected"); + identity->p1g = 1; + break; + case P1G: + dprintk("P1G detected"); + identity->p1g = 1; + break; + case P1D_E_F: + dprintk("P1D/E/F detected"); + break; + case P1C: + dprintk("P1C detected"); + break; + case P1A_B: + dprintk("P1-A/B detected: driver is deactivated - not available"); + goto identification_error; + break; + default: + goto identification_error; + } + } + + return 0; + + identification_error: + return -EIO;; } static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg) { struct dib0090_state *state = fe->tuner_priv; + u16 PllCfg, i, v; HARD_RESET(state); - dib0090_write_reg(state, 0x24, EN_PLL); + dib0090_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ - /* adcClkOutRatio=8->7, release reset */ - dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0); + if (!cfg->in_soc) { + /* adcClkOutRatio=8->7, release reset */ + dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0); + if (cfg->clkoutdrive != 0) + dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) + | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); + else + dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) + | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); + } + + /* Read Pll current config * */ + PllCfg = dib0090_read_reg(state, 0x21); + + /** Reconfigure PLL if current setting is different from default setting **/ + if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && (!cfg->in_soc) + && !cfg->io.pll_bypass) { + + /* Set Bypass mode */ + PllCfg |= (1 << 15); + dib0090_write_reg(state, 0x21, PllCfg); + + /* Set Reset Pll */ + PllCfg &= ~(1 << 13); + dib0090_write_reg(state, 0x21, PllCfg); + + /*** Set new Pll configuration in bypass and reset state ***/ + PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv); + dib0090_write_reg(state, 0x21, PllCfg); + + /* Remove Reset Pll */ + PllCfg |= (1 << 13); + dib0090_write_reg(state, 0x21, PllCfg); + + /*** Wait for PLL lock ***/ + i = 100; + do { + v = !!(dib0090_read_reg(state, 0x1a) & 0x800); + if (v) + break; + } while (--i); + + if (i == 0) { + dprintk("Pll: Unable to lock Pll"); + return; + } + + /* Finally Remove Bypass mode */ + PllCfg &= ~(1 << 15); + dib0090_write_reg(state, 0x21, PllCfg); + } + + if (cfg->io.pll_bypass) { + PllCfg |= (cfg->io.pll_bypass << 15); + dib0090_write_reg(state, 0x21, PllCfg); + } +} + +static int dib0090_fw_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg) +{ + struct dib0090_fw_state *state = fe->tuner_priv; + u16 PllCfg; + u16 v; + int i; + + dprintk("fw reset digital"); + HARD_RESET(state); + + dib0090_fw_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); + dib0090_fw_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ + + dib0090_fw_write_reg(state, 0x20, + ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (cfg->data_tx_drv << 4) | cfg->ls_cfg_pad_drv); + + v = (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 9) | (0 << 8) | (cfg->clkouttobamse << 4) | (0 << 2) | (0); if (cfg->clkoutdrive != 0) - dib0090_write_reg(state, 0x23, - (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 10) | (1 << 9) | (0 << 8) | (cfg->clkoutdrive << 5) | (cfg-> - clkouttobamse - << 4) | (0 - << - 2) - | (0)); + v |= cfg->clkoutdrive << 5; else - dib0090_write_reg(state, 0x23, - (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 10) | (1 << 9) | (0 << 8) | (7 << 5) | (cfg-> - clkouttobamse << 4) | (0 - << - 2) - | (0)); + v |= 7 << 5; + + v |= 2 << 10; + dib0090_fw_write_reg(state, 0x23, v); + + /* Read Pll current config * */ + PllCfg = dib0090_fw_read_reg(state, 0x21); + + /** Reconfigure PLL if current setting is different from default setting **/ + if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && !cfg->io.pll_bypass) { - /* enable pll, de-activate reset, ratio: 2/1 = 60MHz */ - dib0090_write_reg(state, 0x21, - (cfg->io.pll_bypass << 15) | (1 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)); + /* Set Bypass mode */ + PllCfg |= (1 << 15); + dib0090_fw_write_reg(state, 0x21, PllCfg); + /* Set Reset Pll */ + PllCfg &= ~(1 << 13); + dib0090_fw_write_reg(state, 0x21, PllCfg); + + /*** Set new Pll configuration in bypass and reset state ***/ + PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv); + dib0090_fw_write_reg(state, 0x21, PllCfg); + + /* Remove Reset Pll */ + PllCfg |= (1 << 13); + dib0090_fw_write_reg(state, 0x21, PllCfg); + + /*** Wait for PLL lock ***/ + i = 100; + do { + v = !!(dib0090_fw_read_reg(state, 0x1a) & 0x800); + if (v) + break; + } while (--i); + + if (i == 0) { + dprintk("Pll: Unable to lock Pll"); + return -EIO; + } + + /* Finally Remove Bypass mode */ + PllCfg &= ~(1 << 15); + dib0090_fw_write_reg(state, 0x21, PllCfg); + } + + if (cfg->io.pll_bypass) { + PllCfg |= (cfg->io.pll_bypass << 15); + dib0090_fw_write_reg(state, 0x21, PllCfg); + } + + return dib0090_fw_identify(fe); } static int dib0090_wakeup(struct dvb_frontend *fe) @@ -273,6 +589,9 @@ static int dib0090_wakeup(struct dvb_frontend *fe) struct dib0090_state *state = fe->tuner_priv; if (state->config->sleep) state->config->sleep(fe, 0); + + /* enable dataTX in case we have been restarted in the wrong moment */ + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); return 0; } @@ -292,8 +611,75 @@ void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) else dib0090_write_reg(state, 0x04, 1); } + EXPORT_SYMBOL(dib0090_dcc_freq); +static const u16 bb_ramp_pwm_normal_socs[] = { + 550, /* max BB gain in 10th of dB */ + (1 << 9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ + 440, + (4 << 9) | 0, /* BB_RAMP3 = 26dB */ + (0 << 9) | 208, /* BB_RAMP4 */ + (4 << 9) | 208, /* BB_RAMP5 = 29dB */ + (0 << 9) | 440, /* BB_RAMP6 */ +}; + +static const u16 rf_ramp_pwm_cband_7090[] = { + 280, /* max RF gain in 10th of dB */ + 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 504, /* ramp_max = maximum X used on the ramp */ + (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */ + (0 << 10) | 504, /* RF_RAMP6, LNA 1 */ + (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */ + (0 << 10) | 364, /* RF_RAMP8, LNA 2 */ + (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */ + (0 << 10) | 228, /* GAIN_4_2, LNA 3 */ + (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ + (0 << 10) | 109, /* RF_RAMP4, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_cband_8090[] = { + 345, /* max RF gain in 10th of dB */ + 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1000, /* ramp_max = maximum X used on the ramp */ + (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */ + (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */ + (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */ + (0 << 10) | 772, /* RF_RAMP6, LNA 2 */ + (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */ + (0 << 10) | 496, /* RF_RAMP8, LNA 3 */ + (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */ + (0 << 10) | 200, /* GAIN_4_2, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_uhf_7090[] = { + 407, /* max RF gain in 10th of dB */ + 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 529, /* ramp_max = maximum X used on the ramp */ + (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ + (0 << 10) | 176, /* RF_RAMP4, LNA 1 */ + (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */ + (0 << 10) | 529, /* RF_RAMP6, LNA 2 */ + (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */ + (0 << 10) | 400, /* RF_RAMP8, LNA 3 */ + (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */ + (0 << 10) | 316, /* GAIN_4_2, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_uhf_8090[] = { + 388, /* max RF gain in 10th of dB */ + 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1008, /* ramp_max = maximum X used on the ramp */ + (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ + (0 << 10) | 369, /* RF_RAMP4, LNA 1 */ + (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */ + (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */ + (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */ + (0 << 10) | 809, /* RF_RAMP8, LNA 3 */ + (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */ + (0 << 10) | 659, /* GAIN_4_2, LNA 4 */ +}; + static const u16 rf_ramp_pwm_cband[] = { 0, /* max RF gain in 10th of dB */ 0, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ @@ -326,6 +712,16 @@ static const u16 rf_ramp_uhf[] = { 0, 0, 127, /* CBAND : 0.0 dB */ }; +static const u16 rf_ramp_cband_broadmatching[] = /* for p1G only */ +{ + 314, /* Calibrated at 200MHz order has been changed g4-g3-g2-g1 */ + 84, 314, 127, /* LNA1 */ + 80, 230, 255, /* LNA2 */ + 80, 150, 127, /* LNA3 It was measured 12dB, do not lock if 120 */ + 70, 70, 127, /* LNA4 */ + 0, 0, 127, /* CBAND */ +}; + static const u16 rf_ramp_cband[] = { 332, /* max RF gain in 10th of dB */ 132, 252, 127, /* LNA1, dB */ @@ -380,8 +776,8 @@ static const u16 bb_ramp_pwm_normal[] = { }; struct slope { - int16_t range; - int16_t slope; + s16 range; + s16 slope; }; static u16 slopes_to_scale(const struct slope *slopes, u8 num, s16 val) { @@ -597,19 +993,40 @@ void dib0090_pwm_gain_reset(struct dvb_frontend *fe) #endif #ifdef CONFIG_BAND_CBAND if (state->current_band == BAND_CBAND) { - dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband); - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); + if (state->identity.in_soc) { + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); + if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) + dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_8090); + else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) + dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090); + } else { + dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband); + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); + } } else #endif #ifdef CONFIG_BAND_VHF if (state->current_band == BAND_VHF) { - dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf); - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); + if (state->identity.in_soc) { + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); + //dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf_socs); /* TODO */ + } else { + dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf); + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); + } } else #endif { - dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf); - dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); + if (state->identity.in_soc) { + if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) + dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_8090); + else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) + dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_7090); + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); + } else { + dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf); + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); + } } if (state->rf_ramp[0] != 0) @@ -617,11 +1034,22 @@ void dib0090_pwm_gain_reset(struct dvb_frontend *fe) else dib0090_write_reg(state, 0x32, (0 << 11)); + dib0090_write_reg(state, 0x04, 0x01); dib0090_write_reg(state, 0x39, (1 << 10)); } } + EXPORT_SYMBOL(dib0090_pwm_gain_reset); +static u32 dib0090_get_slow_adc_val(struct dib0090_state *state) +{ + u16 adc_val = dib0090_read_reg(state, 0x1d); + if (state->identity.in_soc) { + adc_val >>= 2; + } + return adc_val; +} + int dib0090_gain_control(struct dvb_frontend *fe) { struct dib0090_state *state = fe->tuner_priv; @@ -643,18 +1071,21 @@ int dib0090_gain_control(struct dvb_frontend *fe) } else #endif #ifdef CONFIG_BAND_VHF - if (state->current_band == BAND_VHF) { + if (state->current_band == BAND_VHF && !state->identity.p1g) { dib0090_set_rframp(state, rf_ramp_vhf); dib0090_set_bbramp(state, bb_ramp_boost); } else #endif #ifdef CONFIG_BAND_CBAND - if (state->current_band == BAND_CBAND) { + if (state->current_band == BAND_CBAND && !state->identity.p1g) { dib0090_set_rframp(state, rf_ramp_cband); dib0090_set_bbramp(state, bb_ramp_boost); } else #endif - { + if ((state->current_band == BAND_CBAND || state->current_band == BAND_VHF) && state->identity.p1g) { + dib0090_set_rframp(state, rf_ramp_cband_broadmatching); + dib0090_set_bbramp(state, bb_ramp_boost); + } else { dib0090_set_rframp(state, rf_ramp_uhf); dib0090_set_bbramp(state, bb_ramp_boost); } @@ -669,17 +1100,25 @@ int dib0090_gain_control(struct dvb_frontend *fe) *tune_state = CT_AGC_STEP_0; } else if (!state->agc_freeze) { - s16 wbd; + s16 wbd = 0, i, cnt; int adc; - wbd_val = dib0090_read_reg(state, 0x1d); + wbd_val = dib0090_get_slow_adc_val(state); - /* read and calc the wbd power */ - wbd = dib0090_wbd_to_db(state, wbd_val); + if (*tune_state == CT_AGC_STEP_0) + cnt = 5; + else + cnt = 1; + + for (i = 0; i < cnt; i++) { + wbd_val = dib0090_get_slow_adc_val(state); + wbd += dib0090_wbd_to_db(state, wbd_val); + } + wbd /= cnt; wbd_error = state->wbd_target - wbd; if (*tune_state == CT_AGC_STEP_0) { - if (wbd_error < 0 && state->rf_gain_limit > 0) { + if (wbd_error < 0 && state->rf_gain_limit > 0 && !state->identity.p1g) { #ifdef CONFIG_BAND_CBAND /* in case of CBAND tune reduce first the lt_gain2 before adjusting the RF gain */ u8 ltg2 = (state->rf_lt_def >> 10) & 0x7; @@ -700,39 +1139,39 @@ int dib0090_gain_control(struct dvb_frontend *fe) adc_error = (s16) (((s32) ADC_TARGET) - adc); #ifdef CONFIG_STANDARD_DAB if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) - adc_error += 130; + adc_error -= 10; #endif #ifdef CONFIG_STANDARD_DVBT if (state->fe->dtv_property_cache.delivery_system == STANDARD_DVBT && - (state->fe->dtv_property_cache.modulation == QAM_64 || state->fe->dtv_property_cache.modulation == QAM_16)) + (state->fe->dtv_property_cache.modulation == QAM_64 || state->fe->dtv_property_cache.modulation == QAM_16)) adc_error += 60; #endif #ifdef CONFIG_SYS_ISDBT if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) && (((state->fe->dtv_property_cache.layer[0].segment_count > - 0) - && - ((state->fe->dtv_property_cache.layer[0].modulation == - QAM_64) - || (state->fe->dtv_property_cache.layer[0]. - modulation == QAM_16))) - || - ((state->fe->dtv_property_cache.layer[1].segment_count > - 0) - && - ((state->fe->dtv_property_cache.layer[1].modulation == - QAM_64) - || (state->fe->dtv_property_cache.layer[1]. - modulation == QAM_16))) - || - ((state->fe->dtv_property_cache.layer[2].segment_count > - 0) - && - ((state->fe->dtv_property_cache.layer[2].modulation == - QAM_64) - || (state->fe->dtv_property_cache.layer[2]. - modulation == QAM_16))) - ) - ) + 0) + && + ((state->fe->dtv_property_cache.layer[0].modulation == + QAM_64) + || (state->fe->dtv_property_cache. + layer[0].modulation == QAM_16))) + || + ((state->fe->dtv_property_cache.layer[1].segment_count > + 0) + && + ((state->fe->dtv_property_cache.layer[1].modulation == + QAM_64) + || (state->fe->dtv_property_cache. + layer[1].modulation == QAM_16))) + || + ((state->fe->dtv_property_cache.layer[2].segment_count > + 0) + && + ((state->fe->dtv_property_cache.layer[2].modulation == + QAM_64) + || (state->fe->dtv_property_cache. + layer[2].modulation == QAM_16))) + ) + ) adc_error += 60; #endif @@ -760,9 +1199,9 @@ int dib0090_gain_control(struct dvb_frontend *fe) } #ifdef DEBUG_AGC dprintk - ("FE: %d, tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm", - (u32) fe->id, (u32) *tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val, - (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA)); + ("tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm", + (u32) * tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val, + (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA)); #endif } @@ -771,6 +1210,7 @@ int dib0090_gain_control(struct dvb_frontend *fe) dib0090_gain_apply(state, adc_error, wbd_error, apply_gain_immediatly); return ret; } + EXPORT_SYMBOL(dib0090_gain_control); void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt) @@ -785,13 +1225,53 @@ void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * if (rflt) *rflt = (state->rf_lt_def >> 10) & 0x7; } + EXPORT_SYMBOL(dib0090_get_current_gain); -u16 dib0090_get_wbd_offset(struct dvb_frontend *tuner) +u16 dib0090_get_wbd_offset(struct dvb_frontend *fe) { - struct dib0090_state *st = tuner->tuner_priv; - return st->wbd_offset; + struct dib0090_state *state = fe->tuner_priv; + u32 f_MHz = state->fe->dtv_property_cache.frequency / 1000000; + s32 current_temp = state->temperature; + s32 wbd_thot, wbd_tcold; + const struct dib0090_wbd_slope *wbd = state->current_wbd_table; + + while (f_MHz > wbd->max_freq) + wbd++; + + dprintk("using wbd-table-entry with max freq %d", wbd->max_freq); + + if (current_temp < 0) + current_temp = 0; + if (current_temp > 128) + current_temp = 128; + + //What Wbd gain to apply for this range of frequency + state->wbdmux &= ~(7 << 13); + if (wbd->wbd_gain != 0) + state->wbdmux |= (wbd->wbd_gain << 13); + else + state->wbdmux |= (4 << 13); // 4 is the default WBD gain + + dib0090_write_reg(state, 0x10, state->wbdmux); + + //All the curves are linear with slope*f/64+offset + wbd_thot = wbd->offset_hot - (((u32) wbd->slope_hot * f_MHz) >> 6); + wbd_tcold = wbd->offset_cold - (((u32) wbd->slope_cold * f_MHz) >> 6); + + // Iet assumes that thot-tcold = 130 equiv 128, current temperature ref is -30deg + + wbd_tcold += ((wbd_thot - wbd_tcold) * current_temp) >> 7; + + //for (offset = 0; offset < 1000; offset += 4) + // dbgp("offset = %d -> %d\n", offset, dib0090_wbd_to_db(state, offset)); + state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + wbd_tcold); // get the value in dBm from the offset + dprintk("wbd-target: %d dB", (u32) state->wbd_target); + dprintk("wbd offset applied is %d", wbd_tcold); + + return state->wbd_offset + wbd_tcold; } + EXPORT_SYMBOL(dib0090_get_wbd_offset); static const u16 dib0090_defaults[] = { @@ -801,7 +1281,7 @@ static const u16 dib0090_defaults[] = { 0x99a0, 0x6008, 0x0000, - 0x8acb, + 0x8bcb, 0x0000, 0x0405, 0x0000, @@ -829,8 +1309,6 @@ static const u16 dib0090_defaults[] = { 1, 0x39, 0x0000, - 1, 0x1b, - EN_IQADC | EN_BB | EN_BIAS | EN_DIGCLK | EN_PLL | EN_CRYSTAL, 2, 0x1e, 0x07FF, 0x0007, @@ -844,50 +1322,126 @@ static const u16 dib0090_defaults[] = { 0 }; -static int dib0090_reset(struct dvb_frontend *fe) -{ - struct dib0090_state *state = fe->tuner_priv; - u16 l, r, *n; +static const u16 dib0090_p1g_additionnal_defaults[] = { + // additionnal INITIALISATION for p1g to be written after dib0090_defaults + 1, 0x05, + 0xabcd, - dib0090_reset_digital(fe, state->config); - state->revision = dib0090_identify(fe); - - /* Revision definition */ - if (state->revision == 0xff) - return -EINVAL; -#ifdef EFUSE - else if ((state->revision & 0x1f) >= 3) /* Update the efuse : Only available for KROSUS > P1C */ - dib0090_set_EFUSE(state); -#endif + 1, 0x11, + 0x00b4, -#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT - if (!(state->revision & 0x1)) /* it is P1B - reset is already done */ - return 0; -#endif + 1, 0x1c, + 0xfffd, + + 1, 0x40, + 0x108, + 0 +}; + +static void dib0090_set_default_config(struct dib0090_state *state, const u16 * n) +{ + u16 l, r; - /* Upload the default values */ - n = (u16 *) dib0090_defaults; l = pgm_read_word(n++); while (l) { r = pgm_read_word(n++); do { - /* DEBUG_TUNER */ - /* dprintk("%d, %d, %d", l, r, pgm_read_word(n)); */ dib0090_write_reg(state, r, pgm_read_word(n++)); r++; } while (--l); l = pgm_read_word(n++); } +} + +#define CAP_VALUE_MIN (u8) 9 +#define CAP_VALUE_MAX (u8) 40 +#define HR_MIN (u8) 25 +#define HR_MAX (u8) 40 +#define POLY_MIN (u8) 0 +#define POLY_MAX (u8) 8 + +void dib0090_set_EFUSE(struct dib0090_state *state) +{ + u8 c,h,n; + u16 e2,e4; + u16 cal; + + e2=dib0090_read_reg(state,0x26); + e4=dib0090_read_reg(state,0x28); + + if ((state->identity.version == P1D_E_F) || // All P1F uses the internal calibration + (state->identity.version == P1G) || (e2 == 0xffff)) { //W0090G11R1 and W0090G11R1-D : We will find the calibration Value of the Baseband + + dib0090_write_reg(state,0x22,0x10); //Start the Calib + cal = (dib0090_read_reg(state,0x22)>>6) & 0x3ff; + + if ((cal<670) || (cal==1023)) //Cal at 800 would give too high value for the n + cal=850; //Recenter the n to 32 + n = 165 - ((cal * 10)>>6) ; + e2 = e4 = (3<<12) | (34<<6) | (n); + } + + if (e2!=e4) { + e2 &= e4; /* Remove the redundancy */ + } + + if (e2 != 0xffff) { + c = e2 & 0x3f; + n = (e2 >> 12) & 0xf; + h= (e2 >> 6) & 0x3f; + + if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN)) + c=32; + if ((h >= HR_MAX) || (h <= HR_MIN)) + h=34; + if ((n >= POLY_MAX) || (n <= POLY_MIN)) + n=3; + + dib0090_write_reg(state,0x13, (h << 10)) ; + e2 = (n<<11) | ((h>>2)<<6) | (c); + dib0090_write_reg(state,0x2, e2) ; /* Load the BB_2 */ + } +} + +static int dib0090_reset(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + + dib0090_reset_digital(fe, state->config); + if (dib0090_identify(fe) < 0) + return -EIO; + +#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT + if (!(state->identity.version & 0x1)) /* it is P1B - reset is already done */ + return 0; +#endif + + if (!state->identity.in_soc) { + if ((dib0090_read_reg(state, 0x1a) >> 5) & 0x2) + dib0090_write_reg(state, 0x1b, (EN_IQADC | EN_BB | EN_BIAS | EN_DIGCLK | EN_PLL | EN_CRYSTAL)); + else + dib0090_write_reg(state, 0x1b, (EN_DIGCLK | EN_PLL | EN_CRYSTAL)); + } + + dib0090_set_default_config(state, dib0090_defaults); + + if (state->identity.in_soc) + dib0090_write_reg(state, 0x18, 0x2910); /* charge pump current = 0 */ + + if (state->identity.p1g) + dib0090_set_default_config(state, dib0090_p1g_additionnal_defaults); + + if (((state->identity.version & 0x1f) >= P1D_E_F) || (state->identity.in_soc)) /* Update the efuse : Only available for KROSUS > P1C and SOC as well*/ + dib0090_set_EFUSE(state); /* Congigure in function of the crystal */ if (state->config->io.clock_khz >= 24000) - l = 1; + dib0090_write_reg(state, 0x14, 1); else - l = 2; - dib0090_write_reg(state, 0x14, l); + dib0090_write_reg(state, 0x14, 2); dprintk("Pll lock : %d", (dib0090_read_reg(state, 0x1a) >> 11) & 0x1); - state->reset = 3; /* enable iq-offset-calibration and wbd-calibration when tuning next time */ + state->calibrate = DC_CAL | WBD_CAL | TEMP_CAL; /* enable iq-offset-calibration and wbd-calibration when tuning next time */ return 0; } @@ -927,11 +1481,11 @@ static int dib0090_get_offset(struct dib0090_state *state, enum frontend_tune_st } struct dc_calibration { - uint8_t addr; - uint8_t offset; - uint8_t pga:1; - uint16_t bb1; - uint8_t i:1; + u8 addr; + u8 offset; + u8 pga:1; + u16 bb1; + u8 i:1; }; static const struct dc_calibration dc_table[] = { @@ -944,6 +1498,17 @@ static const struct dc_calibration dc_table[] = { {0}, }; +static const struct dc_calibration dc_p1g_table[] = { + /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */ + /* addr ; trim reg offset ; pga ; CTRL_BB1 value ; i or q */ + {0x06, 5, 1, (1 << 13) | (0 << 8) | (15 << 3), 1}, // offset_trim2_i_chann 0 0 5 0 0 1 6 9 5 + {0x07, 11, 1, (1 << 13) | (0 << 8) | (15 << 3), 0}, // offset_trim2_q_chann 0 0 5 0 0 1 7 15 11 + /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */ + {0x06, 0, 0, (1 << 13) | (29 << 8) | (15 << 3), 1}, // offset_trim1_i_chann 0 0 5 0 0 1 6 4 0 + {0x06, 10, 0, (1 << 13) | (29 << 8) | (15 << 3), 0}, // offset_trim1_q_chann 0 0 5 0 0 1 6 14 10 + {0}, +}; + static void dib0090_set_trim(struct dib0090_state *state) { u16 *val; @@ -962,41 +1527,45 @@ static void dib0090_set_trim(struct dib0090_state *state) static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state) { int ret = 0; + u16 reg; switch (*tune_state) { - case CT_TUNER_START: - /* init */ - dprintk("Internal DC calibration"); - - /* the LNA is off */ - dib0090_write_reg(state, 0x24, 0x02ed); + dprintk("Start DC offset calibration"); /* force vcm2 = 0.8V */ state->bb6 = 0; state->bb7 = 0x040d; + /* the LNA AND LO are off */ + reg = dib0090_read_reg(state, 0x24) & 0x0ffb; /* shutdown lna and lo */ + dib0090_write_reg(state, 0x24, reg); + + state->wbdmux = dib0090_read_reg(state, 0x10); + dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x7 << 3) | 0x3); // connect BB, disable WDB enable* + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); //Discard the DataTX + state->dc = dc_table; + if (state->identity.p1g) + state->dc = dc_p1g_table; *tune_state = CT_TUNER_STEP_0; /* fall through */ case CT_TUNER_STEP_0: + dprintk("Sart/continue DC calibration for %s path", (state->dc->i == 1) ? "I" : "Q"); dib0090_write_reg(state, 0x01, state->dc->bb1); dib0090_write_reg(state, 0x07, state->bb7 | (state->dc->i << 7)); state->step = 0; - state->min_adc_diff = 1023; - *tune_state = CT_TUNER_STEP_1; ret = 50; break; case CT_TUNER_STEP_1: dib0090_set_trim(state); - *tune_state = CT_TUNER_STEP_2; break; @@ -1007,7 +1576,13 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front break; case CT_TUNER_STEP_5: /* found an offset */ - dprintk("FE%d: IQC read=%d, current=%x", state->fe->id, (u32) state->adc_diff, state->step); + dprintk("adc_diff = %d, current step= %d", (u32) state->adc_diff, state->step); + if (state->step == 0 && state->adc_diff < 0) { + state->min_adc_diff = -1023; + dprintk("Change of sign of the minimum adc diff"); + } + + dprintk("adc_diff = %d, min_adc_diff = %d current_step = %d", state->adc_diff, state->min_adc_diff, state->step); /* first turn for this frequency */ if (state->step == 0) { @@ -1017,20 +1592,21 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front state->step = 0x10; } - state->adc_diff = ABS(state->adc_diff); - - if (state->adc_diff < state->min_adc_diff && steps(state->step) < 15) { /* stop search when the delta to 0 is increasing */ + /* Look for a change of Sign in the Adc_diff.min_adc_diff is used to STORE the setp N-1 */ + if ((state->adc_diff & 0x8000) == (state->min_adc_diff & 0x8000) && steps(state->step) < 15) { + /* stop search when the delta the sign is changing and Steps =15 and Step=0 is force for continuance */ state->step++; - state->min_adc_diff = state->adc_diff; + state->min_adc_diff = state->adc_diff; //min is used as N-1 *tune_state = CT_TUNER_STEP_1; } else { - /* the minimum was what we have seen in the step before */ - state->step--; - dib0090_set_trim(state); + if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) { //Come back to the previous state since the delta was better + dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step", state->adc_diff, state->min_adc_diff); + state->step--; + } - dprintk("FE%d: BB Offset Cal, BBreg=%hd,Offset=%hd,Value Set=%hd", state->fe->id, state->dc->addr, state->adc_diff, - state->step); + dib0090_set_trim(state); + dprintk("BB Offset Cal, BBreg=%hd,Offset=%hd,Value Set=%hd", state->dc->addr, state->adc_diff, state->step); state->dc++; if (state->dc->addr == 0) /* done */ @@ -1042,10 +1618,10 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front break; case CT_TUNER_STEP_6: - dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008); + dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008); //Force the test bus to be off dib0090_write_reg(state, 0x1f, 0x7); *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ - state->reset &= ~0x1; + state->calibrate &= ~DC_CAL; default: break; } @@ -1054,21 +1630,43 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front static int dib0090_wbd_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state) { + u8 wbd_gain; + const struct dib0090_wbd_slope *wbd = state->current_wbd_table; + switch (*tune_state) { case CT_TUNER_START: - /* WBD-mode=log, Bias=2, Gain=6, Testmode=1, en=1, WBDMUX=1 */ - dib0090_write_reg(state, 0x10, 0xdb09 | (1 << 10)); - dib0090_write_reg(state, 0x24, EN_UHF & 0x0fff); + while (state->current_rf / 1000 > wbd->max_freq) + wbd++; + if (wbd->wbd_gain != 0) + wbd_gain = wbd->wbd_gain; + else { + wbd_gain = 4; +#if defined(CONFIG_BAND_LBAND) || defined(CONFIG_BAND_SBAND) + if ((state->current_band == BAND_LBAND) || (state->current_band == BAND_SBAND)) + wbd_gain = 2; +#endif + } + if (wbd_gain == state->wbd_calibration_gain) { /* the WBD calibration has already been done */ + *tune_state = CT_TUNER_START; + state->calibrate &= ~WBD_CAL; + return 0; + } + + dib0090_write_reg(state, 0x10, 0x1b81 | (1 << 10) | (wbd_gain << 13) | (1 << 3)); // Force: WBD enable,gain to 4, mux to WBD + + dib0090_write_reg(state, 0x24, ((EN_UHF & 0x0fff) | (1 << 1))); //Discard all LNA but crystal !!! *tune_state = CT_TUNER_STEP_0; + state->wbd_calibration_gain = wbd_gain; return 90; /* wait for the WBDMUX to switch and for the ADC to sample */ + case CT_TUNER_STEP_0: - state->wbd_offset = dib0090_read_reg(state, 0x1d); + state->wbd_offset = dib0090_get_slow_adc_val(state); dprintk("WBD calibration offset = %d", state->wbd_offset); - *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ - state->reset &= ~0x2; + state->calibrate &= ~WBD_CAL; break; + default: break; } @@ -1092,6 +1690,15 @@ static void dib0090_set_bandwidth(struct dib0090_state *state) state->bb_1_def |= tmp; dib0090_write_reg(state, 0x01, state->bb_1_def); /* be sure that we have the right bb-filter */ + + dib0090_write_reg(state, 0x03, 0x6008); /* = 0x6008 : vcm3_trim = 1 ; filter2_gm1_trim = 8 ; filter2_cutoff_freq = 0 */ + dib0090_write_reg(state, 0x04, 0x1); /* 0 = 1KHz ; 1 = 50Hz ; 2 = 150Hz ; 3 = 50KHz ; 4 = servo fast */ + if (state->identity.in_soc) { + dib0090_write_reg(state, 0x05, 0x9bcf); /* attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 1 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 15 */ + } else { + dib0090_write_reg(state, 0x02, (5 << 11) | (8 << 6) | (22 & 0x3f)); /* 22 = cap_value */ + dib0090_write_reg(state, 0x05, 0xabcd); /* = 0xabcd : attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 2 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 13 */ + } } static const struct dib0090_pll dib0090_pll_table[] = { @@ -1180,6 +1787,266 @@ static const struct dib0090_tuning dib0090_tuning_table[] = { #endif }; +static const struct dib0090_tuning dib0090_p1g_tuning_table[] = { + //max_freq, switch_trim, lna_tune, lna_bias, v2i, mix, load, tuner_enable; +#ifdef CONFIG_BAND_CBAND + {170000, 4, 1, 0x820f, 0x300, 0x2d22, 0x82cb, EN_CAB}, // FM EN_CAB +#endif +#ifdef CONFIG_BAND_VHF + {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, // VHF EN_VHF + {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, // VHF EN_VHF + {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, // VHF EN_VHF +#endif +#ifdef CONFIG_BAND_UHF + {510000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {540000, 2, 1, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {600000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {630000, 2, 4, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {680000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {720000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF +#endif +#ifdef CONFIG_BAND_LBAND + {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD + {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD + {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD +#endif +#ifdef CONFIG_BAND_SBAND + {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, // SBD EN_SBD + {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, // SBD EN_SBD +#endif +}; + +static const struct dib0090_pll dib0090_p1g_pll_table[] = { +#ifdef CONFIG_BAND_CBAND + {57000, 0, 11, 48, 6}, // CAB + {70000, 1, 11, 48, 6}, // CAB + {86000, 0, 10, 32, 4}, // CAB + {105000, 1, 10, 32, 4}, // FM + {115000, 0, 9, 24, 6}, // FM + {140000, 1, 9, 24, 6}, // MID FM VHF + {170000, 0, 8, 16, 4}, // MID FM VHF +#endif +#ifdef CONFIG_BAND_VHF + {200000, 1, 8, 16, 4}, // VHF + {230000, 0, 7, 12, 6}, // VHF + {280000, 1, 7, 12, 6}, // MID VHF UHF + {340000, 0, 6, 8, 4}, // MID VHF UHF + {380000, 1, 6, 8, 4}, // MID VHF UHF + {455000, 0, 5, 6, 6}, // MID VHF UHF +#endif +#ifdef CONFIG_BAND_UHF + {580000, 1, 5, 6, 6}, // UHF + {680000, 0, 4, 4, 4}, // UHF + {860000, 1, 4, 4, 4}, // UHF +#endif +#ifdef CONFIG_BAND_LBAND + {1800000, 1, 2, 2, 4}, // LBD +#endif +#ifdef CONFIG_BAND_SBAND + {2900000, 0, 1, 1, 6}, // SBD +#endif +}; + +static const struct dib0090_tuning dib0090_p1g_tuning_table_fm_vhf_on_cband[] = { + //max_freq, switch_trim, lna_tune, lna_bias, v2i, mix, load, tuner_enable; +#ifdef CONFIG_BAND_CBAND + {184000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, // FM EN_CAB // 0x8190 Good perf but higher current //0x4187 Low current + {227000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, // FM EN_CAB + {380000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, // FM EN_CAB +#endif +#ifdef CONFIG_BAND_UHF + {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF +#endif +#ifdef CONFIG_BAND_LBAND + {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD + {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD + {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD +#endif +#ifdef CONFIG_BAND_SBAND + {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, // SBD EN_SBD + {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, // SBD EN_SBD +#endif +}; + +static const struct dib0090_tuning dib0090_tuning_table_cband_7090[] = { + //max_freq, switch_trim, lna_tune, lna_bias, v2i, mix, load, tuner_enable; +#ifdef CONFIG_BAND_CBAND + //{ 184000, 4, 3, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB }, // 0x81ce 0x8190 Good perf but higher current //0x4187 Low current + {300000, 4, 3, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, + {380000, 4, 10, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, //0x4187 + {570000, 4, 10, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, + {858000, 4, 5, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, +#endif +}; + +static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tune_state *tune_state) +{ + int ret = 0; + u16 lo4 = 0xe900; + + s16 adc_target; + u16 adc; + s8 step_sign; + u8 force_soft_search = 0; + + if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) + force_soft_search = 1; + + if (*tune_state == CT_TUNER_START) { + dprintk("Start Captrim search : %s", (force_soft_search == 1) ? "FORCE SOFT SEARCH" : "AUTO"); + dib0090_write_reg(state, 0x10, 0x2B1); + dib0090_write_reg(state, 0x1e, 0x0032); + + if (!state->tuner_is_tuned) { + /* prepare a complete captrim */ + if (!state->identity.p1g || force_soft_search) + state->step = state->captrim = state->fcaptrim = 64; + + state->current_rf = state->rf_request; + } else { /* we are already tuned to this frequency - the configuration is correct */ + if (!state->identity.p1g || force_soft_search) { + /* do a minimal captrim even if the frequency has not changed */ + state->step = 4; + state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f; + } + } + state->adc_diff = 3000; // start with a unreachable high number : only set for KROSUS < P1G */ + *tune_state = CT_TUNER_STEP_0; + + } else if (*tune_state == CT_TUNER_STEP_0) { + if (state->identity.p1g && !force_soft_search) { + // 30MHz => Code 15 for the ration => 128us to lock. Giving approximately + u8 ratio = 31; // (state->config->io.clock_khz / 1024 + 1) & 0x1f; + + dib0090_write_reg(state, 0x40, (3 << 7) | (ratio << 2) | (1 << 1) | 1); + dib0090_read_reg(state, 0x40); + //dib0090_write_reg(state, 0x40, (3<<7) | ((((state->config->io.clock_khz >> 11)+1) & 0x1f)<<2) | (1<<1) | 1); + ret = 50; + } else { + state->step /= 2; + dib0090_write_reg(state, 0x18, lo4 | state->captrim); + + if (state->identity.in_soc) + ret = 25; + } + *tune_state = CT_TUNER_STEP_1; + + } else if (*tune_state == CT_TUNER_STEP_1) { + if (state->identity.p1g && !force_soft_search) { + dib0090_write_reg(state, 0x40, 0x18c | (0 << 1) | 0); + dib0090_read_reg(state, 0x40); + + state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7F; + dprintk("***Final Captrim= 0x%x", state->fcaptrim); + *tune_state = CT_TUNER_STEP_3; + + } else { + /* MERGE for all krosus before P1G */ + adc = dib0090_get_slow_adc_val(state); + dprintk("CAPTRIM=%d; ADC = %d (ADC) & %dmV", (u32) state->captrim, (u32) adc, (u32) (adc) * (u32) 1800 / (u32) 1024); + + if (state->rest == 0 || state->identity.in_soc) { /* Just for 8090P SOCS where auto captrim HW bug : TO CHECK IN ACI for SOCS !!! if 400 for 8090p SOC => tune issue !!! */ + adc_target = 200; + } else + adc_target = 400; + + if (adc >= adc_target) { + adc -= adc_target; + step_sign = -1; + } else { + adc = adc_target - adc; + step_sign = 1; + } + + if (adc < state->adc_diff) { + dprintk("CAPTRIM=%d is closer to target (%d/%d)", (u32) state->captrim, (u32) adc, (u32) state->adc_diff); + state->adc_diff = adc; + state->fcaptrim = state->captrim; + //we could break here, to save time, if we reached a close-enough value + //e.g.: if (state->adc_diff < 20) + //break; + } + + state->captrim += step_sign * state->step; + if (state->step >= 1) + *tune_state = CT_TUNER_STEP_0; + else + *tune_state = CT_TUNER_STEP_2; + + ret = 25; //LOLO changed from 15 + } + } else if (*tune_state == CT_TUNER_STEP_2) { /* this step is only used by krosus < P1G */ + /*write the final cptrim config */ + dib0090_write_reg(state, 0x18, lo4 | state->fcaptrim); + + *tune_state = CT_TUNER_STEP_3; + + } else if (*tune_state == CT_TUNER_STEP_3) { + state->calibrate &= ~CAPTRIM_CAL; + *tune_state = CT_TUNER_STEP_0; + } + + return ret; +} + +static int dib0090_get_temperature(struct dib0090_state *state, enum frontend_tune_state *tune_state) +{ + int ret = 15; + s16 val; + + //The assumption is that the AGC is not active + switch (*tune_state) { + case CT_TUNER_START: + state->wbdmux = dib0090_read_reg(state, 0x10); + dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x8 << 3)); //Move to the bias and clear the wbd enable + + state->bias = dib0090_read_reg(state, 0x13); + dib0090_write_reg(state, 0x13, state->bias | (0x3 << 8)); //Move to the Ref + + *tune_state = CT_TUNER_STEP_0; + /* wait for the WBDMUX to switch and for the ADC to sample */ + break; + + case CT_TUNER_STEP_0: + state->adc_diff = dib0090_get_slow_adc_val(state); // Get the value for the Ref + dib0090_write_reg(state, 0x13, (state->bias & ~(0x3 << 8)) | (0x2 << 8)); //Move to the Ptat + *tune_state = CT_TUNER_STEP_1; + break; + + case CT_TUNER_STEP_1: + val = dib0090_get_slow_adc_val(state); // Get the value for the Ptat + state->temperature = ((s16) ((val - state->adc_diff) * 180) >> 8) + 55; // +55 is defined as = -30deg + + dprintk("temperature: %d C", state->temperature - 30); + + *tune_state = CT_TUNER_STEP_2; + break; + + case CT_TUNER_STEP_2: + //Reload the start values. + dib0090_write_reg(state, 0x13, state->bias); + dib0090_write_reg(state, 0x10, state->wbdmux); /* write back original WBDMUX */ + + *tune_state = CT_TUNER_START; + state->calibrate &= ~TEMP_CAL; + if (state->config->analog_output == 0) + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); //Set the DataTX + + break; + + default: + ret = 0; + break; + } + return ret; +} + #define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */ static int dib0090_tune(struct dvb_frontend *fe) { @@ -1188,91 +2055,139 @@ static int dib0090_tune(struct dvb_frontend *fe) const struct dib0090_pll *pll = state->current_pll_table_index; enum frontend_tune_state *tune_state = &state->tune_state; - u32 rf; - u16 lo4 = 0xe900, lo5, lo6, Den; + u16 lo5, lo6, Den, tmp; u32 FBDiv, Rest, FREF, VCOF_kHz = 0; - u16 tmp, adc; - int8_t step_sign; int ret = 10; /* 1ms is the default delay most of the time */ u8 c, i; - state->current_band = (u8) BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000); - rf = fe->dtv_property_cache.frequency / 1000 + (state->current_band == - BAND_UHF ? state->config->freq_offset_khz_uhf : state->config->freq_offset_khz_vhf); - /* in any case we first need to do a reset if needed */ - if (state->reset & 0x1) - return dib0090_dc_offset_calibration(state, tune_state); - else if (state->reset & 0x2) - return dib0090_wbd_calibration(state, tune_state); - - /************************* VCO ***************************/ + /************************* VCO ***************************/ /* Default values for FG */ /* from these are needed : */ /* Cp,HFdiv,VCOband,SD,Num,Den,FB and REFDiv */ -#ifdef CONFIG_SYS_ISDBT - if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1) - rf += 850; -#endif + /* in any case we first need to do a calibration if needed */ + if (*tune_state == CT_TUNER_START) { + /* deactivate DataTX before some calibrations */ + if (state->calibrate & (DC_CAL | TEMP_CAL | WBD_CAL)) + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); + else /* Activate DataTX in case a calibration has been done before */ if (state->config->analog_output == 0) + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); + } - if (state->current_rf != rf) { - state->tuner_is_tuned = 0; + if (state->calibrate & DC_CAL) + return dib0090_dc_offset_calibration(state, tune_state); + else if (state->calibrate & WBD_CAL) { + if (state->current_rf == 0) { + state->current_rf = state->fe->dtv_property_cache.frequency / 1000; + } + return dib0090_wbd_calibration(state, tune_state); + } else if (state->calibrate & TEMP_CAL) + return dib0090_get_temperature(state, tune_state); + else if (state->calibrate & CAPTRIM_CAL) + return dib0090_captrim_search(state, tune_state); - tune = dib0090_tuning_table; + if (*tune_state == CT_TUNER_START) { + /* if soc and AGC pwm control, disengage mux to be able to R/W access to 0x01 register to set the right filter (cutoff_freq_select) during the tune sequence, otherwise, SOC SERPAR error when accessing to 0x01 */ + if (state->config->use_pwm_agc && state->identity.in_soc) { + tmp = dib0090_read_reg(state, 0x39); + if ((tmp >> 10) & 0x1) + dib0090_write_reg(state, 0x39, tmp & ~(1 << 10)); // disengage mux : en_mux_bb1 = 0 + } - tmp = (state->revision >> 5) & 0x7; - if (tmp == 0x4 || tmp == 0x7) { - /* CBAND tuner version for VHF */ - if (state->current_band == BAND_FM || state->current_band == BAND_VHF) { - /* Force CBAND */ - state->current_band = BAND_CBAND; - tune = dib0090_tuning_table_fm_vhf_on_cband; + state->current_band = (u8) BAND_OF_FREQUENCY(state->fe->dtv_property_cache.frequency / 1000); + state->rf_request = + state->fe->dtv_property_cache.frequency / 1000 + (state->current_band == + BAND_UHF ? state->config->freq_offset_khz_uhf : state->config-> + freq_offset_khz_vhf); + + /* in ISDB-T 1seg we shift tuning frequency */ + if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1 + && state->fe->dtv_property_cache.isdbt_partial_reception == 0)) { + const struct dib0090_low_if_offset_table *LUT_offset = state->config->low_if; + u8 found_offset = 0; + u32 margin_khz = 100; + + if (LUT_offset != NULL) { + while (LUT_offset->RF_freq != 0xffff) { + if (((state->rf_request > (LUT_offset->RF_freq - margin_khz)) + && (state->rf_request < (LUT_offset->RF_freq + margin_khz))) + && LUT_offset->std == state->fe->dtv_property_cache.delivery_system) { + state->rf_request += LUT_offset->offset_khz; + found_offset = 1; + break; + } + LUT_offset++; + } } + + if (found_offset == 0) + state->rf_request += 400; } + if (state->current_rf != state->rf_request || (state->current_standard != state->fe->dtv_property_cache.delivery_system)) { + state->tuner_is_tuned = 0; + state->current_rf = 0; + state->current_standard = 0; - pll = dib0090_pll_table; - /* Look for the interval */ - while (rf > tune->max_freq) - tune++; - while (rf > pll->max_freq) - pll++; - state->current_tune_table_index = tune; - state->current_pll_table_index = pll; - } + tune = dib0090_tuning_table; + if (state->identity.p1g) + tune = dib0090_p1g_tuning_table; - if (*tune_state == CT_TUNER_START) { + tmp = (state->identity.version >> 5) & 0x7; - if (state->tuner_is_tuned == 0) - state->current_rf = 0; + if (state->identity.in_soc) { + if (state->config->force_cband_input) { /* Use the CBAND input for all band */ + if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF + || state->current_band & BAND_UHF) { + state->current_band = BAND_CBAND; + tune = dib0090_tuning_table_cband_7090; + } + } else { /* Use the CBAND input for all band under UHF */ + if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF) { + state->current_band = BAND_CBAND; + tune = dib0090_tuning_table_cband_7090; + } + } + } else + if (tmp == 0x4 || tmp == 0x7) { + /* CBAND tuner version for VHF */ + if (state->current_band == BAND_FM || state->current_band == BAND_CBAND || state->current_band == BAND_VHF) { + state->current_band = BAND_CBAND; /* Force CBAND */ + + tune = dib0090_tuning_table_fm_vhf_on_cband; + if (state->identity.p1g) + tune = dib0090_p1g_tuning_table_fm_vhf_on_cband; + } + } - if (state->current_rf != rf) { + pll = dib0090_pll_table; + if (state->identity.p1g) + pll = dib0090_p1g_pll_table; - dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim)); + /* Look for the interval */ + while (state->rf_request > tune->max_freq) + tune++; + while (state->rf_request > pll->max_freq) + pll++; - /* external loop filter, otherwise: - * lo5 = (0 << 15) | (0 << 12) | (0 << 11) | (3 << 9) | (4 << 6) | (3 << 4) | 4; - * lo6 = 0x0e34 */ - if (pll->vco_band) - lo5 = 0x049e; - else if (state->config->analog_output) - lo5 = 0x041d; - else - lo5 = 0x041c; + state->current_tune_table_index = tune; + state->current_pll_table_index = pll; - lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7); /* bit 15 is the split to the slave, we do not do it here */ - - if (!state->config->io.pll_int_loop_filt) - lo6 = 0xff28; - else - lo6 = (state->config->io.pll_int_loop_filt << 3); + // select internal switch + dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim)); - VCOF_kHz = (pll->hfdiv * rf) * 2; + // Find the VCO frequency in MHz + VCOF_kHz = (pll->hfdiv * state->rf_request) * 2; - FREF = state->config->io.clock_khz; + FREF = state->config->io.clock_khz; // REFDIV is 1FREF Has to be as Close as possible to 10MHz + if (state->config->fref_clock_ratio != 0) + FREF /= state->config->fref_clock_ratio; + // Determine the FB divider + // The reference is 10MHz, Therefore the FBdivider is on the first digits FBDiv = (VCOF_kHz / pll->topresc / FREF); - Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF; + Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF; //in kHz + // Avoid Spurs in the loopfilter bandwidth if (Rest < LPF) Rest = 0; else if (Rest < 2 * LPF) @@ -1280,147 +2195,155 @@ static int dib0090_tune(struct dvb_frontend *fe) else if (Rest > (FREF - LPF)) { Rest = 0; FBDiv += 1; - } else if (Rest > (FREF - 2 * LPF)) + } //Go to the next FB + else if (Rest > (FREF - 2 * LPF)) Rest = FREF - 2 * LPF; Rest = (Rest * 6528) / (FREF / 10); + state->rest = Rest; - Den = 1; + /* external loop filter, otherwise: + * lo5 = (0 << 15) | (0 << 12) | (0 << 11) | (3 << 9) | (4 << 6) | (3 << 4) | 4; + * lo6 = 0x0e34 */ + + if (Rest == 0) { + if (pll->vco_band) + lo5 = 0x049f; + //else if (state->config->analog_output) + // lo5 = 0x041f; + else + lo5 = 0x041f; + } else { + if (pll->vco_band) + lo5 = 0x049e; + else if (state->config->analog_output) + lo5 = 0x041d; + else + lo5 = 0x041c; + } + + if (state->identity.p1g) { /* Bias is done automatically in P1G */ + if (state->identity.in_soc) { + if (state->identity.version == SOC_8090_P1G_11R1) + lo5 = 0x46f; + else + lo5 = 0x42f; + } else + lo5 = 0x42c; //BIAS Lo set to 4 by default in case of the Captrim search does not take care of the VCO Bias + } + + lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7); /* bit 15 is the split to the slave, we do not do it here */ + + //Internal loop filter set... + if (!state->config->io.pll_int_loop_filt) { + if (state->identity.in_soc) + lo6 = 0xff98; + else if (state->identity.p1g || (Rest == 0)) + lo6 = 0xfff8; + else + lo6 = 0xff28; + } else + lo6 = (state->config->io.pll_int_loop_filt << 3); // take the loop filter value given by the layout + //dprintk("lo6 = 0x%04x", (u32)lo6); - dprintk(" ***** ******* Rest value = %d", Rest); + Den = 1; if (Rest > 0) { if (state->config->analog_output) - lo6 |= (1 << 2) | 2; - else - lo6 |= (1 << 2) | 1; + lo6 |= (1 << 2) | 2; //SigmaDelta and Dither + else { + if (state->identity.in_soc) + lo6 |= (1 << 2) | 2; //SigmaDelta and Dither + else + lo6 |= (1 << 2) | 2; //SigmaDelta and Dither + } Den = 255; } -#ifdef CONFIG_BAND_SBAND - if (state->current_band == BAND_SBAND) - lo6 &= 0xfffb; -#endif - + // Now we have to define the Num and Denum + // LO1 gets the FBdiv dib0090_write_reg(state, 0x15, (u16) FBDiv); - - dib0090_write_reg(state, 0x16, (Den << 8) | 1); - + // LO2 gets the REFDiv + if (state->config->fref_clock_ratio != 0) + dib0090_write_reg(state, 0x16, (Den << 8) | state->config->fref_clock_ratio); + else + dib0090_write_reg(state, 0x16, (Den << 8) | 1); + // LO3 for the Numerator dib0090_write_reg(state, 0x17, (u16) Rest); - + // VCO and HF DIV dib0090_write_reg(state, 0x19, lo5); - + // SIGMA Delta dib0090_write_reg(state, 0x1c, lo6); + // Check if the 0090 is analogged configured + //Disable ADC and DigPLL =0xFF9F, 0xffbf for test purposes. + //Enable The Outputs of the BB on DATA_Tx lo6 = tune->tuner_enable; if (state->config->analog_output) lo6 = (lo6 & 0xff9f) | 0x2; - dib0090_write_reg(state, 0x24, lo6 | EN_LO -#ifdef CONFIG_DIB0090_USE_PWM_AGC - | state->config->use_pwm_agc * EN_CRYSTAL -#endif - ); - - state->current_rf = rf; + dib0090_write_reg(state, 0x24, lo6 | EN_LO | state->config->use_pwm_agc * EN_CRYSTAL); - /* prepare a complete captrim */ - state->step = state->captrim = state->fcaptrim = 64; - - } else { /* we are already tuned to this frequency - the configuration is correct */ - - /* do a minimal captrim even if the frequency has not changed */ - state->step = 4; - state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f; } - state->adc_diff = 3000; - dib0090_write_reg(state, 0x10, 0x2B1); - - dib0090_write_reg(state, 0x1e, 0x0032); + state->current_rf = state->rf_request; + state->current_standard = state->fe->dtv_property_cache.delivery_system; ret = 20; - *tune_state = CT_TUNER_STEP_1; - } else if (*tune_state == CT_TUNER_STEP_0) { - /* nothing */ - } else if (*tune_state == CT_TUNER_STEP_1) { - state->step /= 2; - dib0090_write_reg(state, 0x18, lo4 | state->captrim); - *tune_state = CT_TUNER_STEP_2; - } else if (*tune_state == CT_TUNER_STEP_2) { - - adc = dib0090_read_reg(state, 0x1d); - dprintk("FE %d CAPTRIM=%d; ADC = %d (ADC) & %dmV", (u32) fe->id, (u32) state->captrim, (u32) adc, - (u32) (adc) * (u32) 1800 / (u32) 1024); - - if (adc >= 400) { - adc -= 400; - step_sign = -1; - } else { - adc = 400 - adc; - step_sign = 1; - } + state->calibrate = CAPTRIM_CAL; /* captrim serach now */ + } - if (adc < state->adc_diff) { - dprintk("FE %d CAPTRIM=%d is closer to target (%d/%d)", (u32) fe->id, (u32) state->captrim, (u32) adc, (u32) state->adc_diff); - state->adc_diff = adc; - state->fcaptrim = state->captrim; + else if (*tune_state == CT_TUNER_STEP_0) { /* Warning : because of captrim cal, if you change this step, change it also in _cal.c file because it is the step following captrim cal state machine */ + const struct dib0090_wbd_slope *wbd = state->current_wbd_table; - } +// if(!state->identity.p1g) { + while (state->current_rf / 1000 > wbd->max_freq) + wbd++; +// } - state->captrim += step_sign * state->step; - if (state->step >= 1) - *tune_state = CT_TUNER_STEP_1; - else - *tune_state = CT_TUNER_STEP_3; + dib0090_write_reg(state, 0x1e, 0x07ff); + dprintk("Final Captrim: %d", (u32) state->fcaptrim); + dprintk("HFDIV code: %d", (u32) pll->hfdiv_code); + dprintk("VCO = %d", (u32) pll->vco_band); + dprintk("VCOF in kHz: %d ((%d*%d) << 1))", (u32) ((pll->hfdiv * state->rf_request) * 2), (u32) pll->hfdiv, (u32) state->rf_request); + dprintk("REFDIV: %d, FREF: %d", (u32) 1, (u32) state->config->io.clock_khz); + dprintk("FBDIV: %d, Rest: %d", (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17)); + dprintk("Num: %d, Den: %d, SD: %d", (u32) dib0090_read_reg(state, 0x17), (u32) (dib0090_read_reg(state, 0x16) >> 8), + (u32) dib0090_read_reg(state, 0x1c) & 0x3); - ret = 15; - } else if (*tune_state == CT_TUNER_STEP_3) { - /*write the final cptrim config */ - dib0090_write_reg(state, 0x18, lo4 | state->fcaptrim); +#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */ + c = 4; + i = 3; //wbdmux_bias -#ifdef CONFIG_TUNER_DIB0090_CAPTRIM_MEMORY - state->memory[state->memory_index].cap = state->fcaptrim; -#endif + if (wbd->wbd_gain != 0) //&& !state->identity.p1g) + c = wbd->wbd_gain; - *tune_state = CT_TUNER_STEP_4; - } else if (*tune_state == CT_TUNER_STEP_4) { - dib0090_write_reg(state, 0x1e, 0x07ff); + //Store wideband mux register. + state->wbdmux = (c << 13) | (i << 11) | (WBD | (state->config->use_pwm_agc << 1)); + dib0090_write_reg(state, 0x10, state->wbdmux); - dprintk("FE %d Final Captrim: %d", (u32) fe->id, (u32) state->fcaptrim); - dprintk("FE %d HFDIV code: %d", (u32) fe->id, (u32) pll->hfdiv_code); - dprintk("FE %d VCO = %d", (u32) fe->id, (u32) pll->vco_band); - dprintk("FE %d VCOF in kHz: %d ((%d*%d) << 1))", (u32) fe->id, (u32) ((pll->hfdiv * rf) * 2), (u32) pll->hfdiv, (u32) rf); - dprintk("FE %d REFDIV: %d, FREF: %d", (u32) fe->id, (u32) 1, (u32) state->config->io.clock_khz); - dprintk("FE %d FBDIV: %d, Rest: %d", (u32) fe->id, (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17)); - dprintk("FE %d Num: %d, Den: %d, SD: %d", (u32) fe->id, (u32) dib0090_read_reg(state, 0x17), - (u32) (dib0090_read_reg(state, 0x16) >> 8), (u32) dib0090_read_reg(state, 0x1c) & 0x3); + if ((tune->tuner_enable == EN_CAB) && state->identity.p1g) { + dprintk("P1G : The cable band is selected and lna_tune = %d", tune->lna_tune); + dib0090_write_reg(state, 0x09, tune->lna_bias); + dib0090_write_reg(state, 0x0b, 0xb800 | (tune->lna_tune << 6) | (tune->switch_trim)); + } else + dib0090_write_reg(state, 0x09, (tune->lna_tune << 5) | tune->lna_bias); - c = 4; - i = 3; -#if defined(CONFIG_BAND_LBAND) || defined(CONFIG_BAND_SBAND) - if ((state->current_band == BAND_LBAND) || (state->current_band == BAND_SBAND)) { - c = 2; - i = 2; - } -#endif - dib0090_write_reg(state, 0x10, (c << 13) | (i << 11) | (WBD -#ifdef CONFIG_DIB0090_USE_PWM_AGC - | (state->config->use_pwm_agc << 1) -#endif - )); - dib0090_write_reg(state, 0x09, (tune->lna_tune << 5) | (tune->lna_bias << 0)); dib0090_write_reg(state, 0x0c, tune->v2i); dib0090_write_reg(state, 0x0d, tune->mix); dib0090_write_reg(state, 0x0e, tune->load); + *tune_state = CT_TUNER_STEP_1; - *tune_state = CT_TUNER_STEP_5; - } else if (*tune_state == CT_TUNER_STEP_5) { - + } else if (*tune_state == CT_TUNER_STEP_1) { /* initialize the lt gain register */ state->rf_lt_def = 0x7c00; - dib0090_write_reg(state, 0x0f, state->rf_lt_def); + // dib0090_write_reg(state, 0x0f, state->rf_lt_def); dib0090_set_bandwidth(state); state->tuner_is_tuned = 1; + +// if(!state->identity.p1g) + state->calibrate |= WBD_CAL; // TODO: only do the WBD calibration for new tune +// + state->calibrate |= TEMP_CAL; // Force the Temperature to be remesured at next TUNE. *tune_state = CT_TUNER_STOP; } else ret = FE_CALLBACK_TIME_NEVER; @@ -1440,6 +2363,7 @@ enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe) return state->tune_state; } + EXPORT_SYMBOL(dib0090_get_tune_state); int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) @@ -1449,6 +2373,7 @@ int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tun state->tune_state = tune_state; return 0; } + EXPORT_SYMBOL(dib0090_set_tune_state); static int dib0090_get_frequency(struct dvb_frontend *fe, u32 * frequency) @@ -1462,7 +2387,7 @@ static int dib0090_get_frequency(struct dvb_frontend *fe, u32 * frequency) static int dib0090_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { struct dib0090_state *state = fe->tuner_priv; - uint32_t ret; + u32 ret; state->tune_state = CT_TUNER_START; @@ -1492,6 +2417,29 @@ static const struct dvb_tuner_ops dib0090_ops = { .get_frequency = dib0090_get_frequency, }; +static const struct dvb_tuner_ops dib0090_fw_ops = { + .info = { + .name = "DiBcom DiB0090", + .frequency_min = 45000000, + .frequency_max = 860000000, + .frequency_step = 1000, + }, + .release = dib0090_release, + + .init = NULL, + .sleep = NULL, + .set_params = NULL, + .get_frequency = NULL, +}; + +static const struct dib0090_wbd_slope dib0090_wbd_table_default[] = { + {470, 0, 250, 0, 100, 4}, + {860, 51, 866, 21, 375, 4}, + {1700, 0, 800, 0, 850, 4}, //LBAND Predefinition , to calibrate + {2900, 0, 250, 0, 100, 6}, //SBAND Predefinition , NOT tested Yet + {0xFFFF, 0, 0, 0, 0, 0}, +}; + struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) { struct dib0090_state *st = kzalloc(sizeof(struct dib0090_state), GFP_KERNEL); @@ -1503,6 +2451,11 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte st->fe = fe; fe->tuner_priv = st; + if (config->wbd == NULL) + st->current_wbd_table = dib0090_wbd_table_default; + else + st->current_wbd_table = config->wbd; + if (dib0090_reset(fe) != 0) goto free_mem; @@ -1515,8 +2468,35 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte fe->tuner_priv = NULL; return NULL; } + EXPORT_SYMBOL(dib0090_register); +struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) +{ + struct dib0090_fw_state *st = kzalloc(sizeof(struct dib0090_fw_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + st->config = config; + st->i2c = i2c; + st->fe = fe; + fe->tuner_priv = st; + + if (dib0090_fw_reset_digital(fe, st->config) != 0) + goto free_mem; + + dprintk("DiB0090 FW: successfully identified"); + memcpy(&fe->ops.tuner_ops, &dib0090_fw_ops, sizeof(struct dvb_tuner_ops)); + + return fe; + free_mem: + kfree(st); + fe->tuner_priv = NULL; + return NULL; +} + +EXPORT_SYMBOL(dib0090_fw_register); + MODULE_AUTHOR("Patrick Boettcher "); MODULE_AUTHOR("Olivier Grenie "); MODULE_DESCRIPTION("Driver for the DiBcom 0090 base-band RF Tuner"); diff --git a/drivers/media/dvb/frontends/dib0090.h b/drivers/media/dvb/frontends/dib0090.h index aa7711e..13d8524 100644 --- a/drivers/media/dvb/frontends/dib0090.h +++ b/drivers/media/dvb/frontends/dib0090.h @@ -27,6 +27,21 @@ struct dib0090_io_config { u16 pll_int_loop_filt; }; +struct dib0090_wbd_slope { + u16 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u16 slope_cold; + u16 offset_cold; + u16 slope_hot; + u16 offset_hot; + u8 wbd_gain; +}; + +struct dib0090_low_if_offset_table { + int std; + u32 RF_freq; + s32 offset_khz; +}; + struct dib0090_config { struct dib0090_io_config io; int (*reset) (struct dvb_frontend *, int); @@ -47,10 +62,20 @@ struct dib0090_config { u16 wbd_cband_offset; u8 use_pwm_agc; u8 clkoutdrive; + + u8 ls_cfg_pad_drv; + u8 data_tx_drv; + + u8 in_soc; + const struct dib0090_low_if_offset_table *low_if; + u8 fref_clock_ratio; + u16 force_cband_input; + struct dib0090_wbd_slope *wbd; }; #if defined(CONFIG_DVB_TUNER_DIB0090) || (defined(CONFIG_DVB_TUNER_DIB0090_MODULE) && defined(MODULE)) extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); +extern struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast); extern void dib0090_pwm_gain_reset(struct dvb_frontend *fe); extern u16 dib0090_get_wbd_offset(struct dvb_frontend *tuner); @@ -65,6 +90,12 @@ static inline struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, str return NULL; } +static inline struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0090_config *config) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + static inline void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); -- cgit v0.10.2 From dd316c6bacc2bdb22288507fd479bd2181eb7a7b Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Tue, 4 Jan 2011 04:28:59 -0300 Subject: [media] DIB9000: initial support added This patchs add initial support for the DiB9000-device. This demodulator is firmware-driven. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index b8519ba..a20c153 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -349,6 +349,14 @@ config DVB_DIB7000P A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. +config DVB_DIB9000 + tristate "DiBcom 9000" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + config DVB_TDA10048 tristate "Philips TDA10048HN based" depends on DVB_CORE && I2C diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index b1d9525..31dd201 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o +obj-$(CONFIG_DVB_DIB9000) += dib9000.o dibx000_common.o obj-$(CONFIG_DVB_MT312) += mt312.o obj-$(CONFIG_DVB_VES1820) += ves1820.o obj-$(CONFIG_DVB_VES1X93) += ves1x93.o diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c new file mode 100644 index 0000000..a41e02d --- /dev/null +++ b/drivers/media/dvb/frontends/dib9000.c @@ -0,0 +1,2358 @@ +/* + * Linux-DVB Driver for DiBcom's DiB9000 and demodulator-family. + * + * Copyright (C) 2005-10 DiBcom (http://www.dibcom.fr/) + * + * 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, version 2. + */ +#include +#include +#include + +#include "dvb_math.h" +#include "dvb_frontend.h" + +#include "dib9000.h" +#include "dibx000_common.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB9000: "); printk(args); printk("\n"); } } while (0) +#define MAX_NUMBER_OF_FRONTENDS 6 + +struct i2c_device { + struct i2c_adapter *i2c_adap; + u8 i2c_addr; +}; + +/* 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 DibReleaseLock(lock) mutex_unlock(lock) +#define DibInitLock(lock) mutex_init(lock) +#define DibFreeLock(lock) + +struct dib9000_state { + struct i2c_device i2c; + + struct dibx000_i2c_master i2c_master; + struct i2c_adapter tuner_adap; + struct i2c_adapter component_bus; + + u16 revision; + u8 reg_offs; + + enum frontend_tune_state tune_state; + u32 status; + struct dvb_frontend_parametersContext channel_status; + + u8 fe_id; + +#define DIB9000_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB9000_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB9000_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + + union { /* common for all chips */ + struct { + u8 mobile_mode:1; + } host; + + struct { + struct dib9000_fe_memory_map { + u16 addr; + u16 size; + } fe_mm[18]; + u8 memcmd; + + DIB_LOCK mbx_if_lock; /* to protect read/write operations */ + DIB_LOCK mbx_lock; /* to protect the whole mailbox handling */ + + DIB_LOCK mem_lock; /* to protect the memory accesses */ + DIB_LOCK mem_mbx_lock; /* to protect the memory-based mailbox */ + +#define MBX_MAX_WORDS (256 - 200 - 2) +#define DIB9000_MSG_CACHE_SIZE 2 + u16 message_cache[DIB9000_MSG_CACHE_SIZE][MBX_MAX_WORDS]; + u8 fw_is_running; + } risc; + } platform; + + union { /* common for all platforms */ + struct { + struct dib9000_config cfg; + } d9; + } chip; + + struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; + u16 component_bus_speed; +}; + +u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 +}; + +enum dib9000_power_mode { + DIB9000_POWER_ALL = 0, + + DIB9000_POWER_NO, + DIB9000_POWER_INTERF_ANALOG_AGC, + DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD, + DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD, + DIB9000_POWER_INTERFACE_ONLY, +}; + +enum dib9000_out_messages { + OUT_MSG_HBM_ACK, + OUT_MSG_HOST_BUF_FAIL, + OUT_MSG_REQ_VERSION, + OUT_MSG_BRIDGE_I2C_W, + OUT_MSG_BRIDGE_I2C_R, + OUT_MSG_BRIDGE_APB_W, + OUT_MSG_BRIDGE_APB_R, + OUT_MSG_SCAN_CHANNEL, + OUT_MSG_MONIT_DEMOD, + OUT_MSG_CONF_GPIO, + OUT_MSG_DEBUG_HELP, + OUT_MSG_SUBBAND_SEL, + OUT_MSG_ENABLE_TIME_SLICE, + OUT_MSG_FE_FW_DL, + OUT_MSG_FE_CHANNEL_SEARCH, + OUT_MSG_FE_CHANNEL_TUNE, + OUT_MSG_FE_SLEEP, + OUT_MSG_FE_SYNC, + OUT_MSG_CTL_MONIT, + + OUT_MSG_CONF_SVC, + OUT_MSG_SET_HBM, + OUT_MSG_INIT_DEMOD, + OUT_MSG_ENABLE_DIVERSITY, + OUT_MSG_SET_OUTPUT_MODE, + OUT_MSG_SET_PRIORITARY_CHANNEL, + OUT_MSG_ACK_FRG, + OUT_MSG_INIT_PMU, +}; + +enum dib9000_in_messages { + IN_MSG_DATA, + IN_MSG_FRAME_INFO, + IN_MSG_CTL_MONIT, + IN_MSG_ACK_FREE_ITEM, + IN_MSG_DEBUG_BUF, + IN_MSG_MPE_MONITOR, + IN_MSG_RAWTS_MONITOR, + IN_MSG_END_BRIDGE_I2C_RW, + IN_MSG_END_BRIDGE_APB_RW, + IN_MSG_VERSION, + IN_MSG_END_OF_SCAN, + IN_MSG_MONIT_DEMOD, + IN_MSG_ERROR, + IN_MSG_FE_FW_DL_DONE, + IN_MSG_EVENT, + IN_MSG_ACK_CHANGE_SVC, + IN_MSG_HBM_PROF, +}; + +/* memory_access requests */ +#define FE_MM_W_CHANNEL 0 +#define FE_MM_W_FE_INFO 1 +#define FE_MM_RW_SYNC 2 + +#define FE_SYNC_CHANNEL 1 +#define FE_SYNC_W_GENERIC_MONIT 2 +#define FE_SYNC_COMPONENT_ACCESS 3 + +#define FE_MM_R_CHANNEL_SEARCH_STATE 3 +#define FE_MM_R_CHANNEL_UNION_CONTEXT 4 +#define FE_MM_R_FE_INFO 5 +#define FE_MM_R_FE_MONITOR 6 + +#define FE_MM_W_CHANNEL_HEAD 7 +#define FE_MM_W_CHANNEL_UNION 8 +#define FE_MM_W_CHANNEL_CONTEXT 9 +#define FE_MM_R_CHANNEL_UNION 10 +#define FE_MM_R_CHANNEL_CONTEXT 11 +#define FE_MM_R_CHANNEL_TUNE_STATE 12 + +#define FE_MM_R_GENERIC_MONITORING_SIZE 13 +#define FE_MM_W_GENERIC_MONITORING 14 +#define FE_MM_R_GENERIC_MONITORING 15 + +#define FE_MM_W_COMPONENT_ACCESS 16 +#define FE_MM_RW_COMPONENT_ACCESS_BUFFER 17 +static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, + u8 * b, u32 len); +static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len); + +static u16 to_fw_output_mode(u16 mode) +{ + switch (mode) { + case OUTMODE_HIGH_Z: + return 0; + case OUTMODE_MPEG2_PAR_GATED_CLK: + return 4; + case OUTMODE_MPEG2_PAR_CONT_CLK: + return 8; + case OUTMODE_MPEG2_SERIAL: + return 16; + case OUTMODE_DIVERSITY: + return 128; + case OUTMODE_MPEG2_FIFO: + return 2; + case OUTMODE_ANALOG_ADC: + return 1; + default: + return 0; + } +} + +static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32 len, u16 attribute) +{ + u32 chunk_size = 126; + u32 l; + int ret; + u8 wb[2] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[2] = { + {.addr = state->i2c.i2c_addr >> 1,.flags = 0,.buf = wb,.len = 2}, + {.addr = state->i2c.i2c_addr >> 1,.flags = I2C_M_RD,.buf = b,.len = len}, + }; + + if (state->platform.risc.fw_is_running && (reg < 1024)) + return dib9000_risc_apb_access_read(state, reg, attribute, NULL, 0, b, len); + + if (attribute & DATA_BUS_ACCESS_MODE_8BIT) + wb[0] |= (1 << 5); + if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) + wb[0] |= (1 << 4); + + do { + l = len < chunk_size ? len : chunk_size; + msg[1].len = l; + msg[1].buf = b; + ret = i2c_transfer(state->i2c.i2c_adap, msg, 2) != 2 ? -EREMOTEIO : 0; + if (ret != 0) { + dprintk("i2c read error on %d", reg); + return -EREMOTEIO; + } + + b += l; + len -= l; + + if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)) + reg += l / 2; + } while ((ret == 0) && len); + + return 0; +} + +static u16 dib9000_i2c_read16(struct i2c_device *i2c, u16 reg) +{ + u8 b[2]; + u8 wb[2] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[2] = { + {.addr = i2c->i2c_addr >> 1,.flags = 0,.buf = wb,.len = 2}, + {.addr = i2c->i2c_addr >> 1,.flags = I2C_M_RD,.buf = b,.len = 2}, + }; + + if (i2c_transfer(i2c->i2c_adap, msg, 2) != 2) { + dprintk("read register %x error", reg); + return 0; + } + + return (b[0] << 8) | b[1]; +} + +static inline u16 dib9000_read_word(struct dib9000_state *state, u16 reg) +{ + u8 b[2]; + if (dib9000_read16_attr(state, reg, b, 2, 0) != 0) + return 0; + return (b[0] << 8 | b[1]); +} + +static inline u16 dib9000_read_word_attr(struct dib9000_state *state, u16 reg, u16 attribute) +{ + u8 b[2]; + if (dib9000_read16_attr(state, reg, b, 2, attribute) != 0) + return 0; + return (b[0] << 8 | b[1]); +} + +#define dib9000_read16_noinc_attr(state, reg, b, len, attribute) dib9000_read16_attr(state, reg, b, len, (attribute) | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) + +static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 * buf, u32 len, u16 attribute) +{ + u8 b[255]; + u32 chunk_size = 126; + u32 l; + int ret; + + struct i2c_msg msg = { + .addr = state->i2c.i2c_addr >> 1,.flags = 0,.buf = b,.len = len + 2 + }; + + if (state->platform.risc.fw_is_running && (reg < 1024)) { + if (dib9000_risc_apb_access_write + (state, reg, DATA_BUS_ACCESS_MODE_16BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | attribute, buf, len) != 0) + return -EINVAL; + return 0; + } + + b[0] = (reg >> 8) & 0xff; + b[1] = (reg) & 0xff; + + if (attribute & DATA_BUS_ACCESS_MODE_8BIT) + b[0] |= (1 << 5); + if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) + b[0] |= (1 << 4); + + do { + l = len < chunk_size ? len : chunk_size; + msg.len = l + 2; + memcpy(&b[2], buf, l); + + ret = i2c_transfer(state->i2c.i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; + + buf += l; + len -= l; + + if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)) + reg += l / 2; + } while ((ret == 0) && len); + + return ret; +} + +static int dib9000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) +{ + u8 b[4] = { (reg >> 8) & 0xff, reg & 0xff, (val >> 8) & 0xff, val & 0xff }; + struct i2c_msg msg = { + .addr = i2c->i2c_addr >> 1,.flags = 0,.buf = b,.len = 4 + }; + + return i2c_transfer(i2c->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} + +static inline int dib9000_write_word(struct dib9000_state *state, u16 reg, u16 val) +{ + u8 b[2] = { val >> 8, val & 0xff }; + return dib9000_write16_attr(state, reg, b, 2, 0); +} + +static inline int dib9000_write_word_attr(struct dib9000_state *state, u16 reg, u16 val, u16 attribute) +{ + u8 b[2] = { val >> 8, val & 0xff }; + return dib9000_write16_attr(state, reg, b, 2, attribute); +} + +#define dib9000_write(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, 0) +#define dib9000_write16_noinc(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +#define dib9000_write16_noinc_attr(state, reg, buf, len, attribute) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | (attribute)) + +#define dib9000_mbx_send(state, id, data, len) dib9000_mbx_send_attr(state, id, data, len, 0) +#define dib9000_mbx_get_message(state, id, msg, len) dib9000_mbx_get_message_attr(state, id, msg, len, 0) + +#define MAC_IRQ (1 << 1) +#define IRQ_POL_MSK (1 << 4) + +#define dib9000_risc_mem_read_chunks(state, b, len) dib9000_read16_attr(state, 1063, b, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +#define dib9000_risc_mem_write_chunks(state, buf, len) dib9000_write16_attr(state, 1063, buf, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) + +static void dib9000_risc_mem_setup_cmd(struct dib9000_state *state, u32 addr, u32 len, u8 reading) +{ + u8 b[14] = { 0 }; + +// dprintk("%d memcmd: %d %d %d\n", state->fe_id, addr, addr+len, len); +// b[0] = 0 << 7; + b[1] = 1; + +// b[2] = 0; // 1057 +// b[3] = 0; + b[4] = (u8) (addr >> 8); // 1058 + b[5] = (u8) (addr & 0xff); + +// b[10] = 0; // 1061 +// b[11] = 0; + b[12] = (u8) (addr >> 8); // 1062 + b[13] = (u8) (addr & 0xff); + + addr += len; +// b[6] = 0; // 1059 +// b[7] = 0; + b[8] = (u8) (addr >> 8); // 1060 + b[9] = (u8) (addr & 0xff); + + dib9000_write(state, 1056, b, 14); + if (reading) + dib9000_write_word(state, 1056, (1 << 15) | 1); + state->platform.risc.memcmd = -1; /* if it was called directly reset it - to force a future setup-call to set it */ +} + +static void dib9000_risc_mem_setup(struct dib9000_state *state, u8 cmd) +{ + struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd & 0x7f]; + /* decide whether we need to "refresh" the memory controller */ + if (state->platform.risc.memcmd == cmd && /* same command */ + !(cmd & 0x80 && m->size < 67)) /* and we do not want to read something with less than 67 bytes looping - working around a bug in the memory controller */ + return; + dib9000_risc_mem_setup_cmd(state, m->addr, m->size, cmd & 0x80); + state->platform.risc.memcmd = cmd; +} + +static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u16 len) +{ + if (!state->platform.risc.fw_is_running) + return -EIO; + + DibAcquireLock(&state->platform.risc.mem_lock); + dib9000_risc_mem_setup(state, cmd | 0x80); + dib9000_risc_mem_read_chunks(state, b, len); + DibReleaseLock(&state->platform.risc.mem_lock); + return 0; +} + +static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 * b) +{ + struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd]; + if (!state->platform.risc.fw_is_running) + return -EIO; + + DibAcquireLock(&state->platform.risc.mem_lock); + dib9000_risc_mem_setup(state, cmd); + dib9000_risc_mem_write_chunks(state, b, m->size); + DibReleaseLock(&state->platform.risc.mem_lock); + return 0; +} + +static int dib9000_firmware_download(struct dib9000_state *state, u8 risc_id, u16 key, const u8 * code, u32 len) +{ + u16 offs; + + if (risc_id == 1) + offs = 16; + else + offs = 0; + + /* config crtl reg */ + dib9000_write_word(state, 1024 + offs, 0x000f); + dib9000_write_word(state, 1025 + offs, 0); + dib9000_write_word(state, 1031 + offs, key); + + dprintk("going to download %dB of microcode", len); + if (dib9000_write16_noinc(state, 1026 + offs, (u8 *) code, (u16) len) != 0) { + dprintk("error while downloading microcode for RISC %c", 'A' + risc_id); + return -EIO; + } + + dprintk("Microcode for RISC %c loaded", 'A' + risc_id); + + return 0; +} + +static int dib9000_mbx_host_init(struct dib9000_state *state, u8 risc_id) +{ + u16 mbox_offs; + u16 reset_reg; + u16 tries = 1000; + + if (risc_id == 1) + mbox_offs = 16; + else + mbox_offs = 0; + + /* Reset mailbox */ + dib9000_write_word(state, 1027 + mbox_offs, 0x8000); + + /* Read reset status */ + do { + reset_reg = dib9000_read_word(state, 1027 + mbox_offs); + msleep(100); + } while ((reset_reg & 0x8000) && --tries); + + if (reset_reg & 0x8000) { + dprintk("MBX: init ERROR, no response from RISC %c", 'A' + risc_id); + return -EIO; + } + dprintk("MBX: initialized"); + return 0; +} + +#define MAX_MAILBOX_TRY 100 +static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, u8 len, u16 attr) +{ + u8 ret = 0, *d, b[2]; + u16 tmp; + u16 size; + u32 i; + + if (!state->platform.risc.fw_is_running) + return -EINVAL; + + DibAcquireLock(&state->platform.risc.mbx_if_lock); + tmp = MAX_MAILBOX_TRY; + do { + size = dib9000_read_word_attr(state, 1043, attr) & 0xff; + if ((size + len + 1) > MBX_MAX_WORDS && --tmp) { + dprintk("MBX: RISC mbx full, retrying"); + msleep(100); + } else + break; + } while (1); + + //dprintk( "MBX: size: %d", size); + + if (tmp == 0) { + ret = -EINVAL; + goto out; + } +#ifdef DUMP_MSG + dprintk("--> %02x %d ", id, len + 1); + for (i = 0; i < len; i++) + dprintk("%04x ", data[i]); + dprintk("\n"); +#endif + + /* byte-order conversion - works on big (where it is not necessary) or little endian */ + d = (u8 *) data; + for (i = 0; i < len; i++) { + tmp = data[i]; + *d++ = tmp >> 8; + *d++ = tmp & 0xff; + } + + /* write msg */ + b[0] = id; + b[1] = len + 1; + if (dib9000_write16_noinc_attr(state, 1045, b, 2, attr) != 0 || dib9000_write16_noinc_attr(state, 1045, (u8 *) data, len * 2, attr) != 0) { + ret = -EIO; + goto out; + } + + /* update register nb_mes_in_RX */ + ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr); + + out: + DibReleaseLock(&state->platform.risc.mbx_if_lock); + + return ret; +} + +static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, u16 attr) +{ +#ifdef DUMP_MSG + u16 *d = data; +#endif + + u16 tmp, i; + u8 size; + u8 mc_base; + + if (!state->platform.risc.fw_is_running) + return 0; + + DibAcquireLock(&state->platform.risc.mbx_if_lock); + if (risc_id == 1) + mc_base = 16; + else + mc_base = 0; + + /* Length and type in the first word */ + *data = dib9000_read_word_attr(state, 1029 + mc_base, attr); + + size = *data & 0xff; + if (size <= MBX_MAX_WORDS) { + data++; + size--; /* Initial word already read */ + + dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, size * 2, attr); + + /* to word conversion */ + for (i = 0; i < size; i++) { + tmp = *data; + *data = (tmp >> 8) | (tmp << 8); + data++; + } + +#ifdef DUMP_MSG + dprintk("<-- "); + for (i = 0; i < size + 1; i++) + dprintk("%04x ", d[i]); + dprintk("\n"); +#endif + } else { + dprintk("MBX: message is too big for message cache (%d), flushing message", size); + size--; /* Initial word already read */ + while (size--) + dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, 2, attr); + } + /* Update register nb_mes_in_TX */ + dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr); + + DibReleaseLock(&state->platform.risc.mbx_if_lock); + + return size + 1; +} + +static int dib9000_risc_debug_buf(struct dib9000_state *state, u16 * data, u8 size) +{ + u32 ts = data[1] << 16 | data[0]; + char *b = (char *)&data[2]; + + b[2 * (size - 2) - 1] = '\0'; /* Bullet proof the buffer */ + if (*b == '~') { + b++; + dprintk(b); + } else + dprintk("RISC%d: %d.%04d %s", state->fe_id, ts / 10000, ts % 10000, *b ? b : ""); + return 1; +} + +static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr) +{ + int i; + u8 size; + u16 *block; + /* find a free slot */ + for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) { + block = state->platform.risc.message_cache[i]; + if (*block == 0) { + size = dib9000_mbx_read(state, block, 1, attr); + +// dprintk( "MBX: fetched %04x message to cache", *block); + + switch (*block >> 8) { + case IN_MSG_DEBUG_BUF: + dib9000_risc_debug_buf(state, block + 1, size); /* debug-messages are going to be printed right away */ + *block = 0; /* free the block */ + break; +#if 0 + case IN_MSG_DATA: /* FE-TRACE */ + dib9000_risc_data_process(state, block + 1, size); + *block = 0; + break; +#endif + default: + break; + } + + return 1; + } + } + dprintk("MBX: no free cache-slot found for new message..."); + return -1; +} + +static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr) +{ + if (risc_id == 0) + return (u8) (dib9000_read_word_attr(state, 1028, attr) >> 10) & 0x1f; /* 5 bit field */ + else + return (u8) (dib9000_read_word_attr(state, 1044, attr) >> 8) & 0x7f; /* 7 bit field */ +} + +static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) +{ + int ret = 0; + u16 tmp; + + if (!state->platform.risc.fw_is_running) + return -1; + + DibAcquireLock(&state->platform.risc.mbx_lock); + + if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */ + ret = dib9000_mbx_fetch_to_cache(state, attr); + + tmp = dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ +// if (tmp) +// dprintk( "cleared IRQ: %x", tmp); + DibReleaseLock(&state->platform.risc.mbx_lock); + + return ret; +} + +static int dib9000_mbx_get_message_attr(struct dib9000_state *state, u16 id, u16 * msg, u8 * size, u16 attr) +{ + u8 i; + u16 *block; + u16 timeout = 30; + + *msg = 0; + do { + /* dib9000_mbx_get_from_cache(); */ + for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) { + block = state->platform.risc.message_cache[i]; + if ((*block >> 8) == id) { + *size = (*block & 0xff) - 1; + memcpy(msg, block + 1, (*size) * 2); + *block = 0; /* free the block */ + i = 0; /* signal that we found a message */ + break; + } + } + + if (i == 0) + break; + + if (dib9000_mbx_process(state, attr) == -1) /* try to fetch one message - if any */ + return -1; + + } while (--timeout); + + if (timeout == 0) { + dprintk("waiting for message %d timed out", id); + return -1; + } + + return i == 0; +} + +static int dib9000_risc_check_version(struct dib9000_state *state) +{ + u8 r[4]; + u8 size; + u16 fw_version = 0; + + if (dib9000_mbx_send(state, OUT_MSG_REQ_VERSION, &fw_version, 1) != 0) + return -EIO; + + if (dib9000_mbx_get_message(state, IN_MSG_VERSION, (u16 *) r, &size) < 0) + return -EIO; + + fw_version = (r[0] << 8) | r[1]; + dprintk("RISC: ver: %d.%02d (IC: %d)", fw_version >> 10, fw_version & 0x3ff, (r[2] << 8) | r[3]); + + if ((fw_version >> 10) != 7) + return -EINVAL; + + switch (fw_version & 0x3ff) { + case 11: + case 12: + case 14: + case 15: + case 16: + case 17: + break; + default: + dprintk("RISC: invalid firmware version"); + return -EINVAL; + } + + dprintk("RISC: valid firmware version"); + return 0; +} + +static int dib9000_fw_boot(struct dib9000_state *state, const u8 * codeA, u32 lenA, const u8 * codeB, u32 lenB) +{ + /* Reconfig pool mac ram */ + dib9000_write_word(state, 1225, 0x02); /* A: 8k C, 4 k D - B: 32k C 6 k D - IRAM 96k */ + dib9000_write_word(state, 1226, 0x05); + + /* Toggles IP crypto to Host APB interface. */ + dib9000_write_word(state, 1542, 1); + + /* Set jump and no jump in the dma box */ + dib9000_write_word(state, 1074, 0); + dib9000_write_word(state, 1075, 0); + + /* Set MAC as APB Master. */ + dib9000_write_word(state, 1237, 0); + + /* Reset the RISCs */ + if (codeA != NULL) + dib9000_write_word(state, 1024, 2); + else + dib9000_write_word(state, 1024, 15); + if (codeB != NULL) + dib9000_write_word(state, 1040, 2); + + if (codeA != NULL) + dib9000_firmware_download(state, 0, 0x1234, codeA, lenA); + if (codeB != NULL) + dib9000_firmware_download(state, 1, 0x1234, codeB, lenB); + + /* Run the RISCs */ + if (codeA != NULL) + dib9000_write_word(state, 1024, 0); + if (codeB != NULL) + dib9000_write_word(state, 1040, 0); + + if (codeA != NULL) + if (dib9000_mbx_host_init(state, 0) != 0) + return -EIO; + if (codeB != NULL) + if (dib9000_mbx_host_init(state, 1) != 0) + return -EIO; + + msleep(100); + state->platform.risc.fw_is_running = 1; + + if (dib9000_risc_check_version(state) != 0) + return -EINVAL; + + state->platform.risc.memcmd = 0xff; + return 0; +} + +static u16 dib9000_identify(struct i2c_device *client) +{ + u16 value; + + if ((value = dib9000_i2c_read16(client, 896)) != 0x01b3) { + dprintk("wrong Vendor ID (0x%x)", value); + return 0; + } + + value = dib9000_i2c_read16(client, 897); + if (value != 0x4000 && value != 0x4001 && value != 0x4002 && value != 0x4003 && value != 0x4004 && value != 0x4005) { + dprintk("wrong Device ID (0x%x)", value); + return 0; + } + + /* protect this driver to be used with 7000PC */ + if (value == 0x4000 && dib9000_i2c_read16(client, 769) == 0x4000) { + dprintk("this driver does not work with DiB7000PC"); + return 0; + } + + switch (value) { + case 0x4000: + dprintk("found DiB7000MA/PA/MB/PB"); + break; + case 0x4001: + dprintk("found DiB7000HC"); + break; + case 0x4002: + dprintk("found DiB7000MC"); + break; + case 0x4003: + dprintk("found DiB9000A"); + break; + case 0x4004: + dprintk("found DiB9000H"); + break; + case 0x4005: + dprintk("found DiB9000M"); + break; + } + + return value; +} + +static void dib9000_set_power_mode(struct dib9000_state *state, enum dib9000_power_mode mode) +{ + /* by default everything is going to be powered off */ + u16 reg_903 = 0x3fff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906; + u8 offset; + + if (state->revision == 0x4003 || state->revision == 0x4004 || state->revision == 0x4005) + offset = 1; + else + offset = 0; + + reg_906 = dib9000_read_word(state, 906 + offset) | 0x3; /* keep settings for RISC */ + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB9000_POWER_ALL: + reg_903 = 0x0000; + reg_904 = 0x0000; + reg_905 = 0x0000; + reg_906 = 0x0000; + break; + + /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */ + case DIB9000_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */ + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2)); + break; + + case DIB9000_POWER_INTERF_ANALOG_AGC: + reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10)); + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2)); + reg_906 &= ~((1 << 0)); + break; + + case DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD: + reg_903 = 0x0000; + reg_904 = 0x801f; + reg_905 = 0x0000; + reg_906 &= ~((1 << 0)); + break; + + case DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD: + reg_903 = 0x0000; + reg_904 = 0x8000; + reg_905 = 0x010b; + reg_906 &= ~((1 << 0)); + break; + default: + case DIB9000_POWER_NO: + break; + } + + /* always power down unused parts */ + if (!state->platform.host.mobile_mode) + reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1); + + /* P_sdio_select_clk = 0 on MC and after */ + if (state->revision != 0x4000) + reg_906 <<= 1; + + dib9000_write_word(state, 903 + offset, reg_903); + dib9000_write_word(state, 904 + offset, reg_904); + dib9000_write_word(state, 905 + offset, reg_905); + dib9000_write_word(state, 906 + offset, reg_906); +} + +static int dib9000_fw_reset(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + + dib9000_write_word(state, 1817, 0x0003); // SRAM read lead in + P_host_rdy_cmos=1 + + dib9000_write_word(state, 1227, 1); + dib9000_write_word(state, 1227, 0); + + switch ((state->revision = dib9000_identify(&state->i2c))) { + case 0x4003: + case 0x4004: + case 0x4005: + state->reg_offs = 1; + break; + default: + return -EINVAL; + } + + /* reset the i2c-master to use the host interface */ + dibx000_reset_i2c_master(&state->i2c_master); + + dib9000_set_power_mode(state, DIB9000_POWER_ALL); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib9000_write_word(state, 1794, dib9000_read_word(state, 1794) & ~(1 << 1)); + dib9000_write_word(state, 1796, 0); + dib9000_write_word(state, 1805, 0x805); + + /* restart all parts */ + dib9000_write_word(state, 898, 0xffff); + dib9000_write_word(state, 899, 0xffff); + dib9000_write_word(state, 900, 0x0001); + dib9000_write_word(state, 901, 0xff19); + dib9000_write_word(state, 902, 0x003c); + + dib9000_write_word(state, 898, 0); + dib9000_write_word(state, 899, 0); + dib9000_write_word(state, 900, 0); + dib9000_write_word(state, 901, 0); + dib9000_write_word(state, 902, 0); + + dib9000_write_word(state, 911, state->chip.d9.cfg.if_drives); + + dib9000_set_power_mode(state, DIB9000_POWER_INTERFACE_ONLY); + + return 0; +} + +static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, + u8 * b, u32 len) +{ + u16 mb[10]; + u8 i, s; + + if (address >= 1024 || !state->platform.risc.fw_is_running) + return -EINVAL; + + //dprintk( "APB access thru rd fw %d %x", address, attribute); + + mb[0] = (u16) address; + mb[1] = len / 2; + dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_R, mb, 2, attribute); + switch (dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute)) { + case 1: + s--; // address + for (i = 0; i < s; i++) { + b[i * 2] = (mb[i + 1] >> 8) & 0xff; + b[i * 2 + 1] = (mb[i + 1]) & 0xff; + } + return 0; + default: + return -EIO; + } + return -EIO; +} + +static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len) +{ + u16 mb[10]; + u8 s, i; + + if (address >= 1024 || !state->platform.risc.fw_is_running) + return -EINVAL; + + //dprintk( "APB access thru wr fw %d %x", address, attribute); + + mb[0] = (unsigned short)address; + for (i = 0; i < len && i < 20; i += 2) // 20 bytes max + mb[1 + (i / 2)] = (b[i] << 8 | b[i + 1]); + + dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, 1 + len / 2, attribute); + return dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute) == 1 ? 0 : -EINVAL; +} + +static int dib9000_fw_memmbx_sync(struct dib9000_state *state, u8 i) +{ + u8 index_loop = 10; + + if (!state->platform.risc.fw_is_running) + return 0; + dib9000_risc_mem_write(state, FE_MM_RW_SYNC, &i); + do { + dib9000_risc_mem_read(state, FE_MM_RW_SYNC, &i, 1); + } while (i && index_loop--); + + if (index_loop > 0) + return 0; + return -EIO; +} + +static int dib9000_fw_init(struct dib9000_state *state) +{ + struct dibGPIOFunction *f; + u16 b[40] = { 0 }; + u8 i; + u8 size; + + if (dib9000_fw_boot(state, NULL, 0, state->chip.d9.cfg.microcode_B_fe_buffer, state->chip.d9.cfg.microcode_B_fe_size) != 0) + //if (dib9000_fw_boot(state, microcode_A_buffer, microcode_A_size, microcode_B_buffer, microcode_B_size) != 0) + return -EIO; + + /* initialize the firmware */ + for (i = 0; i < ARRAY_SIZE(state->chip.d9.cfg.gpio_function); i++) { + f = &state->chip.d9.cfg.gpio_function[i]; + if (f->mask) { + switch (f->function) { + case BOARD_GPIO_FUNCTION_COMPONENT_ON: + b[0] = (u16) f->mask; + b[1] = (u16) f->direction; + b[2] = (u16) f->value; + break; + case BOARD_GPIO_FUNCTION_COMPONENT_OFF: + b[3] = (u16) f->mask; + b[4] = (u16) f->direction; + b[5] = (u16) f->value; + break; + } + } + } + if (dib9000_mbx_send(state, OUT_MSG_CONF_GPIO, b, 15) != 0) + return -EIO; + + /* subband */ + b[0] = state->chip.d9.cfg.subband.size; /* type == 0 -> GPIO - PWM not yet supported */ + for (i = 0; i < state->chip.d9.cfg.subband.size; i++) { + b[1 + i * 4] = state->chip.d9.cfg.subband.subband[i].f_mhz; + b[2 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.mask; + b[3 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.direction; + b[4 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.value; + //dprintk( "SBS: %d %d %x %x %x\n", i, b[1 + i*4], b[2 + i*4], b[3 + i*4], b[4 + i*4]); + } + b[1 + i * 4] = 0; /* fe_id */ + if (dib9000_mbx_send(state, OUT_MSG_SUBBAND_SEL, b, 2 + 4 * i) != 0) + return -EIO; + + /* 0 - id, 1 - no_of_frontends */ + b[0] = (0 << 8) | 1; + /* 0 = i2c-address demod, 0 = tuner */ + b[1] = (0 << 8) | (0); //st->i2c_addr ) ); + b[2] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000) >> 16) & 0xffff); + b[3] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000)) & 0xffff); + b[4] = (u16) ((state->chip.d9.cfg.vcxo_timer >> 16) & 0xffff); + b[5] = (u16) ((state->chip.d9.cfg.vcxo_timer) & 0xffff); + b[6] = (u16) ((state->chip.d9.cfg.timing_frequency >> 16) & 0xffff); + b[7] = (u16) ((state->chip.d9.cfg.timing_frequency) & 0xffff); + b[29] = state->chip.d9.cfg.if_drives; + if (dib9000_mbx_send(state, OUT_MSG_INIT_DEMOD, b, ARRAY_SIZE(b)) != 0) + return -EIO; + + if (dib9000_mbx_send(state, OUT_MSG_FE_FW_DL, NULL, 0) != 0) + return -EIO; + + if (dib9000_mbx_get_message(state, IN_MSG_FE_FW_DL_DONE, b, &size) < 0) + return -EIO; + + if (size > ARRAY_SIZE(b)) { + dprintk("error : firmware returned %dbytes needed but the used buffer has only %dbytes\n Firmware init ABORTED", size, (int)ARRAY_SIZE(b)); + return -EINVAL; + } + + for (i = 0; i < size; i += 2) { + state->platform.risc.fe_mm[i / 2].addr = b[i + 0]; + state->platform.risc.fe_mm[i / 2].size = b[i + 1]; + //dprintk( "MM: %d %d %d", state->platform.risc.fe_mm[i/2].addr, state->platform.risc.fe_mm[i/2].size, ARRAY_SIZE(state->platform.risc.fe_mm)); + } + + return 0; +} + +static void dib9000_fw_set_channel_head(struct dib9000_state *state, struct dvb_frontend_parameters *ch) +{ + u8 b[9]; + u32 freq = state->fe[0]->dtv_property_cache.frequency / 1000; + if (state->fe_id % 2) + freq += 101; + + b[0] = (u8) ((freq >> 0) & 0xff); + b[1] = (u8) ((freq >> 8) & 0xff); + b[2] = (u8) ((freq >> 16) & 0xff); + b[3] = (u8) ((freq >> 24) & 0xff); + b[4] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 0) & 0xff); + b[5] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 8) & 0xff); + b[6] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 16) & 0xff); + b[7] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 24) & 0xff); + b[8] = 0x80; /* do not wait for CELL ID when doing autosearch */ + if (state->fe[0]->dtv_property_cache.delivery_system == SYS_DVBT) + b[8] |= 1; + dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_HEAD, b); +} + +static int dib9000_fw_get_channel(struct dvb_frontend *fe, struct dvb_frontend_parameters *channel) +{ + struct dib9000_state *state = fe->demodulator_priv; + struct dibDVBTChannel { + s8 spectrum_inversion; + + s8 nfft; + s8 guard; + s8 constellation; + + s8 hrch; + s8 alpha; + s8 code_rate_hp; + s8 code_rate_lp; + s8 select_hp; + + s8 intlv_native; + }; + struct dibDVBTChannel ch; + int ret = 0; + + DibAcquireLock(&state->platform.risc.mem_mbx_lock); + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + goto error; + ret = -EIO; + } + + dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION, (u8 *) & ch, sizeof(struct dibDVBTChannel)); + + switch (ch.spectrum_inversion&0x7) { + case 1: + state->fe[0]->dtv_property_cache.inversion = INVERSION_ON; + break; + case 0: + state->fe[0]->dtv_property_cache.inversion = INVERSION_OFF; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.inversion = INVERSION_AUTO; + break; + } + switch (ch.nfft) { + case 0: + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; + break; + case 2: + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K; + break; + case 1: + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO; + break; + } + switch (ch.guard) { + case 0: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO; + break; + } + switch (ch.constellation) { + case 2: + state->fe[0]->dtv_property_cache.modulation = QAM_64; + break; + case 1: + state->fe[0]->dtv_property_cache.modulation = QAM_16; + break; + case 0: + state->fe[0]->dtv_property_cache.modulation = QPSK; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.modulation = QAM_AUTO; + break; + } + switch (ch.hrch) { + case 0: + state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_NONE; + break; + case 1: + state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_1; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_AUTO; + break; + } + switch (ch.code_rate_hp) { + case 1: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_1_2; + break; + case 2: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_2_3; + break; + case 3: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_3_4; + break; + case 5: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_5_6; + break; + case 7: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_7_8; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_AUTO; + break; + } + switch (ch.code_rate_lp) { + case 1: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_1_2; + break; + case 2: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_2_3; + break; + case 3: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_3_4; + break; + case 5: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_5_6; + break; + case 7: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_7_8; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_AUTO; + break; + } + + error: + DibReleaseLock(&state->platform.risc.mem_mbx_lock); + return ret; +} + +static int dib9000_fw_set_channel_union(struct dvb_frontend *fe, struct dvb_frontend_parameters *channel) +{ + struct dib9000_state *state = fe->demodulator_priv; + struct dibDVBTChannel { + s8 spectrum_inversion; + + s8 nfft; + s8 guard; + s8 constellation; + + s8 hrch; + s8 alpha; + s8 code_rate_hp; + s8 code_rate_lp; + s8 select_hp; + + s8 intlv_native; + }; + struct dibDVBTChannel ch; + + switch (state->fe[0]->dtv_property_cache.inversion) { + case INVERSION_ON: + ch.spectrum_inversion = 1; + break; + case INVERSION_OFF: + ch.spectrum_inversion = 0; + break; + default: + case INVERSION_AUTO: + ch.spectrum_inversion = -1; + break; + } + switch (state->fe[0]->dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + ch.nfft = 0; + break; + case TRANSMISSION_MODE_4K: + ch.nfft = 2; + break; + case TRANSMISSION_MODE_8K: + ch.nfft = 1; + break; + default: + case TRANSMISSION_MODE_AUTO: + ch.nfft = 1; + break; + } + switch (state->fe[0]->dtv_property_cache.guard_interval) { + case GUARD_INTERVAL_1_32: + ch.guard = 0; + break; + case GUARD_INTERVAL_1_16: + ch.guard = 1; + break; + case GUARD_INTERVAL_1_8: + ch.guard = 2; + break; + case GUARD_INTERVAL_1_4: + ch.guard = 3; + break; + default: + case GUARD_INTERVAL_AUTO: + ch.guard = -1; + break; + } + switch (state->fe[0]->dtv_property_cache.modulation) { + case QAM_64: + ch.constellation = 2; + break; + case QAM_16: + ch.constellation = 1; + break; + case QPSK: + ch.constellation = 0; + break; + default: + case QAM_AUTO: + ch.constellation = -1; + break; + } + switch (state->fe[0]->dtv_property_cache.hierarchy) { + case HIERARCHY_NONE: + ch.hrch = 0; + break; + case HIERARCHY_1: + case HIERARCHY_2: + case HIERARCHY_4: + ch.hrch = 1; + break; + default: + case HIERARCHY_AUTO: + ch.hrch = -1; + break; + } + ch.alpha = 1; + switch (state->fe[0]->dtv_property_cache.code_rate_HP) { + case FEC_1_2: + ch.code_rate_hp = 1; + break; + case FEC_2_3: + ch.code_rate_hp = 2; + break; + case FEC_3_4: + ch.code_rate_hp = 3; + break; + case FEC_5_6: + ch.code_rate_hp = 5; + break; + case FEC_7_8: + ch.code_rate_hp = 7; + break; + default: + case FEC_AUTO: + ch.code_rate_hp = -1; + break; + } + switch (state->fe[0]->dtv_property_cache.code_rate_LP) { + case FEC_1_2: + ch.code_rate_lp = 1; + break; + case FEC_2_3: + ch.code_rate_lp = 2; + break; + case FEC_3_4: + ch.code_rate_lp = 3; + break; + case FEC_5_6: + ch.code_rate_lp = 5; + break; + case FEC_7_8: + ch.code_rate_lp = 7; + break; + default: + case FEC_AUTO: + ch.code_rate_lp = -1; + break; + } + ch.select_hp = 1; + ch.intlv_native = 1; + + dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_UNION, (u8 *) & ch); + + return 0; +} + +static int dib9000_fw_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) +{ + struct dib9000_state *state = fe->demodulator_priv; + int ret = 10, search = state->channel_status.status == CHANNEL_STATUS_PARAMETERS_UNKNOWN; + s8 i; + + switch (state->tune_state) { + case CT_DEMOD_START: + dib9000_fw_set_channel_head(state, ch); + + /* write the channel context - a channel is initialized to 0, so it is OK */ + dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_CONTEXT, (u8 *) fe_info); + dib9000_risc_mem_write(state, FE_MM_W_FE_INFO, (u8 *) fe_info); + + if (search) + dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_SEARCH, NULL, 0); + else { + dib9000_fw_set_channel_union(fe, ch); + dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_TUNE, NULL, 0); + } + state->tune_state = CT_DEMOD_STEP_1; + break; + case CT_DEMOD_STEP_1: + if (search) + dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_SEARCH_STATE, (u8 *) & i, 1); + else + dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_TUNE_STATE, (u8 *) & i, 1); + switch (i) { /* something happened */ + case 0: + break; + case -2: /* tps locks are "slower" than MPEG locks -> even in autosearch data is OK here */ + if (search) + state->status = FE_STATUS_DEMOD_SUCCESS; + else { + state->tune_state = CT_DEMOD_STOP; + state->status = FE_STATUS_LOCKED; + } + break; + default: + state->status = FE_STATUS_TUNE_FAILED; + state->tune_state = CT_DEMOD_STOP; + break; + } + break; + default: + ret = FE_CALLBACK_TIME_NEVER; + break; + } + + return ret; +} + +static int dib9000_fw_set_diversity_in(struct dvb_frontend *fe, int onoff) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 mode = (u16) onoff; + return dib9000_mbx_send(state, OUT_MSG_ENABLE_DIVERSITY, &mode, 1); +} + +static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 outreg, smo_mode; + + dprintk("setting output mode for demod %p to %d", fe, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ + break; + case OUTMODE_DIVERSITY: + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe[0]); + return -EINVAL; + } + + dib9000_write_word(state, 1795, outreg); // has to be written from outside + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: + case OUTMODE_MPEG2_PAR_CONT_CLK: + case OUTMODE_MPEG2_SERIAL: + case OUTMODE_MPEG2_FIFO: + smo_mode = (dib9000_read_word(state, 295) & 0x0010) | (1 << 1); + if (state->chip.d9.cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5); + dib9000_write_word(state, 295, smo_mode); + break; + } + + outreg = to_fw_output_mode(mode); + return dib9000_mbx_send(state, OUT_MSG_SET_OUTPUT_MODE, &outreg, 1); +} + +static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib9000_state *state = i2c_get_adapdata(i2c_adap); + u16 i, len, t, index_msg; + + for (index_msg = 0; index_msg < num; index_msg++) { + if (msg[index_msg].flags & I2C_M_RD) { /* read */ + len = msg[index_msg].len; + if (len > 16) + len = 16; + + if (dib9000_read_word(state, 790) != 0) + dprintk("TunerITF: read busy"); + + dib9000_write_word(state, 784, (u16) (msg[index_msg].addr)); + dib9000_write_word(state, 787, (len / 2) - 1); + dib9000_write_word(state, 786, 1); /* start read */ + + i = 1000; + while (dib9000_read_word(state, 790) != (len / 2) && i) + i--; + + if (i == 0) + dprintk("TunerITF: read failed"); + + for (i = 0; i < len; i += 2) { + t = dib9000_read_word(state, 785); + msg[index_msg].buf[i] = (t >> 8) & 0xff; + msg[index_msg].buf[i + 1] = (t) & 0xff; + } + if (dib9000_read_word(state, 790) != 0) + dprintk("TunerITF: read more data than expected"); + } else { + i = 1000; + while (dib9000_read_word(state, 789) && i) + i--; + if (i == 0) + dprintk("TunerITF: write busy"); + + len = msg[index_msg].len; + if (len > 16) + len = 16; + + for (i = 0; i < len; i += 2) + dib9000_write_word(state, 785, (msg[index_msg].buf[i] << 8) | msg[index_msg].buf[i + 1]); + dib9000_write_word(state, 784, (u16) msg[index_msg].addr); + dib9000_write_word(state, 787, (len / 2) - 1); + dib9000_write_word(state, 786, 0); /* start write */ + + i = 1000; + while (dib9000_read_word(state, 791) > 0 && i) + i--; + if (i == 0) + dprintk("TunerITF: write failed"); + } + } + return num; +} + +int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed) +{ + struct dib9000_state *state = fe->demodulator_priv; + + state->component_bus_speed = speed; + return 0; +} +EXPORT_SYMBOL(dib9000_fw_set_component_bus_speed); + +static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib9000_state *state = i2c_get_adapdata(i2c_adap); + u8 type = 0; /* I2C */ + u8 port = DIBX000_I2C_INTERFACE_GPIO_3_4; + u16 scl = state->component_bus_speed; /* SCL frequency */ + //u16 scl = 208; /* SCL frequency */ + struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[FE_MM_RW_COMPONENT_ACCESS_BUFFER]; + u8 p[13] = { 0 }; + + p[0] = type; + p[1] = port; + p[2] = msg[0].addr << 1; + + p[3] = (u8) scl & 0xff; /* scl */ + p[4] = (u8) (scl >> 8); + +// p[5] = 0; /* attr */ +// p[6] = 0; + +// p[7] = (u8) (msg[0].addr << 1 ); +// p[8] = (u8) (msg[0].addr >> 7 ); + p[7] = 0; + p[8] = 0; + + p[9] = (u8) (msg[0].len); + p[10] = (u8) (msg[0].len >> 8); + if ((num > 1) && (msg[1].flags & I2C_M_RD)) { + p[11] = (u8) (msg[1].len); + p[12] = (u8) (msg[1].len >> 8); + } else { + p[11] = 0; + p[12] = 0; + } + + DibAcquireLock(&state->platform.risc.mem_mbx_lock); + + dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p); + + { /* write-part */ + dib9000_risc_mem_setup_cmd(state, m->addr, msg[0].len, 0); + dib9000_risc_mem_write_chunks(state, msg[0].buf, msg[0].len); + } + + /* do the transaction */ + if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0) { + DibReleaseLock(&state->platform.risc.mem_mbx_lock); + return 0; + } + + /* read back any possible result */ + if ((num > 1) && (msg[1].flags & I2C_M_RD)) + dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1].buf, msg[1].len); + + DibReleaseLock(&state->platform.risc.mem_mbx_lock); + + return num; +} + +static u32 dib9000_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dib9000_tuner_algo = { + .master_xfer = dib9000_tuner_xfer, + .functionality = dib9000_i2c_func, +}; + +static struct i2c_algorithm dib9000_component_bus_algo = { + .master_xfer = dib9000_fw_component_bus_xfer, + .functionality = dib9000_i2c_func, +}; + +struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe) +{ + struct dib9000_state *st = fe->demodulator_priv; + return &st->tuner_adap; +} + +EXPORT_SYMBOL(dib9000_get_tuner_interface); + +struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) +{ + struct dib9000_state *st = fe->demodulator_priv; + return &st->component_bus; +} + +EXPORT_SYMBOL(dib9000_get_component_bus_interface); + +struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +{ + struct dib9000_state *st = fe->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} + +EXPORT_SYMBOL(dib9000_get_i2c_master); + +int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) +{ + struct dib9000_state *st = fe->demodulator_priv; + + st->i2c.i2c_adap = i2c; + return 0; +} + +EXPORT_SYMBOL(dib9000_set_i2c_adapter); + +static int dib9000_cfg_gpio(struct dib9000_state *st, u8 num, u8 dir, u8 val) +{ + st->gpio_dir = dib9000_read_word(st, 773); + st->gpio_dir &= ~(1 << num); /* reset the direction bit */ + st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */ + dib9000_write_word(st, 773, st->gpio_dir); + + st->gpio_val = dib9000_read_word(st, 774); + st->gpio_val &= ~(1 << num); /* reset the direction bit */ + st->gpio_val |= (val & 0x01) << num; /* set the new value */ + dib9000_write_word(st, 774, st->gpio_val); + + dprintk("gpio dir: %04x: gpio val: %04x", st->gpio_dir, st->gpio_val); + + return 0; +} + +int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + struct dib9000_state *state = fe->demodulator_priv; + return dib9000_cfg_gpio(state, num, dir, val); +} + +EXPORT_SYMBOL(dib9000_set_gpio); +int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 val = dib9000_read_word(state, 294 + 1) & 0xffef; + val |= (onoff & 0x1) << 4; + + dprintk("PID filter enabled %d", onoff); + return dib9000_write_word(state, 294 + 1, val); +} + +EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl); +int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + struct dib9000_state *state = fe->demodulator_priv; + dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); + return dib9000_write_word(state, 300 + 1 + id, onoff ? (1 << 13) | pid : 0); +} + +EXPORT_SYMBOL(dib9000_fw_pid_filter); + +int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + return dib9000_fw_init(state); +} + +EXPORT_SYMBOL(dib9000_firmware_post_pll_init); + +static void dib9000_release(struct dvb_frontend *demod) +{ + struct dib9000_state *st = demod->demodulator_priv; + u8 index_frontend; + + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) + dvb_frontend_detach(st->fe[index_frontend]); + + DibFreeLock(&state->platform.risc.mbx_if_lock); + DibFreeLock(&state->platform.risc.mbx_lock); + DibFreeLock(&state->platform.risc.mem_lock); + DibFreeLock(&state->platform.risc.mem_mbx_lock); + dibx000_exit_i2c_master(&st->i2c_master); + + i2c_del_adapter(&st->tuner_adap); + i2c_del_adapter(&st->component_bus); + kfree(st->fe[0]); + kfree(st); +} + +static int dib9000_wakeup(struct dvb_frontend *fe) +{ + return 0; +} + +static int dib9000_sleep(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend; + int ret; + + 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) + return ret; + } + return dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0); +} + +static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend, sub_index_frontend; + fe_status_t stat; + int ret; + + 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); + if (stat & FE_HAS_SYNC) { + dprintk("TPS lock on the slave%i", index_frontend); + + /* synchronize the cache with the other frontends */ + state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], fep); + for (sub_index_frontend=0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) { + if (sub_index_frontend != index_frontend) { + state->fe[sub_index_frontend]->dtv_property_cache.modulation = state->fe[index_frontend]->dtv_property_cache.modulation; + state->fe[sub_index_frontend]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; + state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode; + state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval; + state->fe[sub_index_frontend]->dtv_property_cache.hierarchy = state->fe[index_frontend]->dtv_property_cache.hierarchy; + state->fe[sub_index_frontend]->dtv_property_cache.code_rate_HP = state->fe[index_frontend]->dtv_property_cache.code_rate_HP; + state->fe[sub_index_frontend]->dtv_property_cache.code_rate_LP = state->fe[index_frontend]->dtv_property_cache.code_rate_LP; + state->fe[sub_index_frontend]->dtv_property_cache.rolloff = state->fe[index_frontend]->dtv_property_cache.rolloff; + } + } + return 0; + } + } + + /* get the channel from master chip */ + ret = dib9000_fw_get_channel(fe, fep); + if (ret != 0) + return ret; + + /* synchronize the cache with the other frontends */ + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion; + state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode; + state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval; + state->fe[index_frontend]->dtv_property_cache.modulation = fe->dtv_property_cache.modulation; + state->fe[index_frontend]->dtv_property_cache.hierarchy = fe->dtv_property_cache.hierarchy; + state->fe[index_frontend]->dtv_property_cache.code_rate_HP = fe->dtv_property_cache.code_rate_HP; + state->fe[index_frontend]->dtv_property_cache.code_rate_LP = fe->dtv_property_cache.code_rate_LP; + state->fe[index_frontend]->dtv_property_cache.rolloff = fe->dtv_property_cache.rolloff; + } + + return 0; +} + +static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +{ + struct dib9000_state *state = fe->demodulator_priv; + state->tune_state = tune_state; + if (tune_state == CT_DEMOD_START) + state->status = FE_STATUS_TUNE_PENDING; + + return 0; +} + +static u32 dib9000_get_status(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + return state->status; +} + +static int dib9000_set_channel_status(struct dvb_frontend *fe, struct dvb_frontend_parametersContext *channel_status) +{ + struct dib9000_state *state = fe->demodulator_priv; + + memcpy(&state->channel_status, channel_status, sizeof(struct dvb_frontend_parametersContext)); + return 0; +} + +static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + struct dib9000_state *state = fe->demodulator_priv; + int sleep_time, sleep_time_slave; + u32 frontend_status; + u8 nbr_pending, exit_condition, index_frontend, index_frontend_success; + struct dvb_frontend_parametersContext channel_status; + + /* check that the correct parameters are set */ + if (state->fe[0]->dtv_property_cache.frequency == 0) { + dprintk("dib9000: must specify frequency "); + return 0; + } + + if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) { + dprintk("dib9000: must specify bandwidth "); + return 0; + } + fe->dtv_property_cache.delivery_system = SYS_DVBT; + + /* set the master status */ + if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO || + fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || fep->u.ofdm.constellation == QAM_AUTO || fep->u.ofdm.code_rate_HP == FEC_AUTO) { + /* no channel specified, autosearch the channel */ + state->channel_status.status = CHANNEL_STATUS_PARAMETERS_UNKNOWN; + } else + state->channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; + + /* set mode and status for the different frontends */ + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + dib9000_fw_set_diversity_in(state->fe[index_frontend], 1); + + /* synchronization of the cache */ + memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); + + state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_DVBT; + dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_HIGH_Z); + + dib9000_set_channel_status(state->fe[index_frontend], &state->channel_status); + dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); + } + + /* actual tune */ + exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ + index_frontend_success = 0; + do { + sleep_time = dib9000_fw_tune(state->fe[0], NULL); + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend], NULL); + if (sleep_time == FE_CALLBACK_TIME_NEVER) + sleep_time = sleep_time_slave; + else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time)) + sleep_time = sleep_time_slave; + } + if (sleep_time != FE_CALLBACK_TIME_NEVER) + msleep(sleep_time / 10); + else + break; + + nbr_pending = 0; + exit_condition = 0; + index_frontend_success = 0; + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + frontend_status = -dib9000_get_status(state->fe[index_frontend]); + if (frontend_status > -FE_STATUS_TUNE_PENDING) { + exit_condition = 2; /* tune success */ + index_frontend_success = index_frontend; + break; + } + if (frontend_status == -FE_STATUS_TUNE_PENDING) + nbr_pending++; /* some frontends are still tuning */ + } + if ((exit_condition != 2) && (nbr_pending == 0)) + exit_condition = 1; /* if all tune are done and no success, exit: tune failed */ + + } while (exit_condition == 0); + + /* check the tune result */ + if (exit_condition == 1) { /* tune failed */ + dprintk("tune failed"); + return 0; + } + + dprintk("tune success on frontend%i", index_frontend_success); + + /* synchronize all the channel cache */ + dib9000_get_frontend(state->fe[0], fep); + + /* retune the other frontends with the found channel */ + channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + /* only retune the frontends which was not tuned success */ + if (index_frontend != index_frontend_success) { + dib9000_set_channel_status(state->fe[index_frontend], &channel_status); + dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); + } + } + do { + sleep_time = FE_CALLBACK_TIME_NEVER; + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (index_frontend != index_frontend_success) { + sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend], NULL); + if (sleep_time == FE_CALLBACK_TIME_NEVER) + sleep_time = sleep_time_slave; + else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time)) + sleep_time = sleep_time_slave; + } + } + if (sleep_time != FE_CALLBACK_TIME_NEVER) + msleep(sleep_time / 10); + else + break; + + nbr_pending = 0; + for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (index_frontend != index_frontend_success) { + frontend_status = -dib9000_get_status(state->fe[index_frontend]); + if ((index_frontend != index_frontend_success) && (frontend_status == -FE_STATUS_TUNE_PENDING)) + nbr_pending++; /* some frontends are still tuning */ + } + } + } while (nbr_pending != 0); + + /* set the output mode */ + dib9000_fw_set_output_mode(state->fe[0], state->chip.d9.cfg.output_mode); + for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY); + + /* turn off the diversity for the last frontend */ + dib9000_fw_set_diversity_in(state->fe[index_frontend-1], 0); + + return 0; +} + +static u16 dib9000_read_lock(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + + return dib9000_read_word(state, 535); +} + +static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend; + u16 lock = 0, lock_slave = 0; + + 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]); + + lock = dib9000_read_word(state, 535); + + *stat = 0; + + if ((lock & 0x8000) || (lock_slave & 0x8000)) + *stat |= FE_HAS_SIGNAL; + if ((lock & 0x3000) || (lock_slave & 0x3000)) + *stat |= FE_HAS_CARRIER; + if ((lock & 0x0100) || (lock_slave & 0x0100)) + *stat |= FE_HAS_VITERBI; + if (((lock & 0x0038) == 0x38) || ((lock_slave & 0x0038) == 0x38)) + *stat |= FE_HAS_SYNC; + if ((lock & 0x0008) || (lock_slave & 0x0008)) + *stat |= FE_HAS_LOCK; + + return 0; +} + +static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 c[16]; + + DibAcquireLock(&state->platform.risc.mem_mbx_lock); + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) + return -EIO; + dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, sizeof(c)); + DibReleaseLock(&state->platform.risc.mem_mbx_lock); + + *ber = c[10] << 16 | c[11]; + return 0; +} + +static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend; + u16 c[16]; + u16 val; + + *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); + if (val > 65535 - *strength) + *strength = 65535; + else + *strength += val; + } + + DibAcquireLock(&state->platform.risc.mem_mbx_lock); + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) + return -EIO; + dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, sizeof(c)); + DibReleaseLock(&state->platform.risc.mem_mbx_lock); + + val = 65535 - c[4]; + if (val > 65535 - *strength) + *strength = 65535; + else + *strength += val; + return 0; +} + +static u32 dib9000_get_snr(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 c[16]; + 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; + dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, sizeof(c)); + DibReleaseLock(&state->platform.risc.mem_mbx_lock); + + val = c[7]; + n = (val >> 4) & 0xff; + exp = ((val & 0xf) << 2); + val = c[8]; + exp += ((val >> 14) & 0x3); + if ((exp & 0x20) != 0) + exp -= 0x40; + n <<= exp + 16; + + s = (val >> 6) & 0xFF; + exp = (val & 0x3F); + if ((exp & 0x20) != 0) + exp -= 0x40; + s <<= exp + 16; + + if (n > 0) { + u32 t = (s / n) << 16; + return t + ((s << 16) - n * t) / n; + } + return 0xffffffff; +} + +static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend; + u32 snr_master; + + 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]); + + if ((snr_master >> 16) != 0) { + snr_master = 10 * intlog10(snr_master >> 16); + *snr = snr_master / ((1 << 24) / 10); + } else + *snr = 0; + + return 0; +} + +static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 c[16]; + + DibAcquireLock(&state->platform.risc.mem_mbx_lock); + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) + return -EIO; + dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, sizeof(c)); + DibReleaseLock(&state->platform.risc.mem_mbx_lock); + + *unc = c[12]; + return 0; +} + +int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr) +{ + int k = 0; + u8 new_addr = 0; + struct i2c_device client = {.i2c_adap = i2c }; + + client.i2c_addr = default_addr + 16; + dib9000_i2c_write16(&client, 1796, 0x0); // select DVB-T output + + for (k = no_of_demods - 1; k >= 0; k--) { + /* designated i2c address */ + new_addr = first_addr + (k << 1); + client.i2c_addr = default_addr; + + dib9000_i2c_write16(&client, 1817, 3); + dib9000_i2c_write16(&client, 1796, 0); + dib9000_i2c_write16(&client, 1227, 1); + dib9000_i2c_write16(&client, 1227, 0); + + client.i2c_addr = new_addr; + dib9000_i2c_write16(&client, 1817, 3); + dib9000_i2c_write16(&client, 1796, 0); + dib9000_i2c_write16(&client, 1227, 1); + dib9000_i2c_write16(&client, 1227, 0); + + if (dib9000_identify(&client) == 0) { + client.i2c_addr = default_addr; + if (dib9000_identify(&client) == 0) { + dprintk("DiB9000 #%d: not identified", k); + return -EIO; + } + } + + dib9000_i2c_write16(&client, 1795, (1 << 10) | (4 << 6)); + dib9000_i2c_write16(&client, 1794, (new_addr << 2) | 2); + + dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + new_addr = first_addr | (k << 1); + client.i2c_addr = new_addr; + + dib9000_i2c_write16(&client, 1794, (new_addr << 2)); + dib9000_i2c_write16(&client, 1795, 0); + } + + return 0; +} + +EXPORT_SYMBOL(dib9000_i2c_enumeration); + +int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend = 1; + + while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) + index_frontend++; + if (index_frontend < MAX_NUMBER_OF_FRONTENDS) { + dprintk("set slave fe %p to index %i", fe_slave, index_frontend); + state->fe[index_frontend] = fe_slave; + return 0; + } + + dprintk("too many slave frontend"); + return -ENOMEM; +} +EXPORT_SYMBOL(dib9000_set_slave_frontend); + +int dib9000_remove_slave_frontend(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend = 1; + + while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) + index_frontend++; + if (index_frontend != 1) { + dprintk("remove slave fe %p (index %i)", state->fe[index_frontend-1], index_frontend-1); + state->fe[index_frontend] = NULL; + return 0; + } + + dprintk("no frontend to be removed"); + return -ENODEV; +} +EXPORT_SYMBOL(dib9000_remove_slave_frontend); + +struct dvb_frontend * dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +{ + struct dib9000_state *state = fe->demodulator_priv; + + if (slave_index >= MAX_NUMBER_OF_FRONTENDS) + return NULL; + return state->fe[slave_index]; +} +EXPORT_SYMBOL(dib9000_get_slave_frontend); + +static struct dvb_frontend_ops dib9000_ops; +struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg) +{ + struct dvb_frontend *fe; + struct dib9000_state *st; + st = kzalloc(sizeof(struct dib9000_state), GFP_KERNEL); + if (st == NULL) + return NULL; + fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL); + if (fe == NULL) + return NULL; + + memcpy(&st->chip.d9.cfg, cfg, sizeof(struct dib9000_config)); + st->i2c.i2c_adap = i2c_adap; + st->i2c.i2c_addr = i2c_addr; + + st->gpio_dir = DIB9000_GPIO_DEFAULT_DIRECTIONS; + st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES; + st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS; + + DibInitLock(&st->platform.risc.mbx_if_lock); + DibInitLock(&st->platform.risc.mbx_lock); + DibInitLock(&st->platform.risc.mem_lock); + DibInitLock(&st->platform.risc.mem_mbx_lock); + + st->fe[0] = fe; + fe->demodulator_priv = st; + memcpy(&st->fe[0]->ops, &dib9000_ops, sizeof(struct dvb_frontend_ops)); + + /* Ensure the output mode remains at the previous default if it's + * not specifically set by the caller. + */ + if ((st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) + st->chip.d9.cfg.output_mode = OUTMODE_MPEG2_FIFO; + + if (dib9000_identify(&st->i2c) == 0) + goto error; + + dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c.i2c_adap, st->i2c.i2c_addr); + + st->tuner_adap.dev.parent = i2c_adap->dev.parent; + strncpy(st->tuner_adap.name, "DIB9000_FW TUNER ACCESS", sizeof(st->tuner_adap.name)); + st->tuner_adap.algo = &dib9000_tuner_algo; + st->tuner_adap.algo_data = NULL; + i2c_set_adapdata(&st->tuner_adap, st); + if (i2c_add_adapter(&st->tuner_adap) < 0) + goto error; + + st->component_bus.dev.parent = i2c_adap->dev.parent; + strncpy(st->component_bus.name, "DIB9000_FW COMPONENT BUS ACCESS", sizeof(st->component_bus.name)); + st->component_bus.algo = &dib9000_component_bus_algo; + st->component_bus.algo_data = NULL; + st->component_bus_speed = 340; + i2c_set_adapdata(&st->component_bus, st); + if (i2c_add_adapter(&st->component_bus) < 0) + goto component_bus_add_error; + + dib9000_fw_reset(fe); + + return fe; + + component_bus_add_error: + i2c_del_adapter(&st->tuner_adap); + error: + kfree(st); + return NULL; +} + +EXPORT_SYMBOL(dib9000_attach); + +static struct dvb_frontend_ops dib9000_ops = { + .info = { + .name = "DiBcom 9000", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + 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_RECOVER | FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib9000_release, + + .init = dib9000_wakeup, + .sleep = dib9000_sleep, + + .set_frontend = dib9000_set_frontend, + .get_tune_settings = dib9000_fe_get_tune_settings, + .get_frontend = dib9000_get_frontend, + + .read_status = dib9000_read_status, + .read_ber = dib9000_read_ber, + .read_signal_strength = dib9000_read_signal_strength, + .read_snr = dib9000_read_snr, + .read_ucblocks = dib9000_read_unc_blocks, +}; + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_AUTHOR("Olivier Grenie "); +MODULE_DESCRIPTION("Driver for the DiBcom 9000 COFDM demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dib9000.h b/drivers/media/dvb/frontends/dib9000.h new file mode 100644 index 0000000..995e4bc --- /dev/null +++ b/drivers/media/dvb/frontends/dib9000.h @@ -0,0 +1,130 @@ +#ifndef DIB9000_H +#define DIB9000_H + +#include "dibx000_common.h" + +struct dib9000_config { + u8 dvbt_mode; + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + struct dibx000_bandwidth_config *bw; + + u16 if_drives; + + u32 timing_frequency; + u32 xtal_clock_khz; + u32 vcxo_timer; + u32 demod_clock_khz; + + const u8 *microcode_B_fe_buffer; + u32 microcode_B_fe_size; + + struct dibGPIOFunction gpio_function[2]; + struct dibSubbandSelection subband; + + u8 output_mode; +}; + +#define DEFAULT_DIB9000_I2C_ADDRESS 18 + +#if defined(CONFIG_DVB_DIB9000) || (defined(CONFIG_DVB_DIB9000_MODULE) && defined(MODULE)) +extern struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg); +extern int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr); +extern struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe); +extern struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating); +extern int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val); +extern int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); +extern int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff); +extern int dib9000_firmware_post_pll_init(struct dvb_frontend *fe); +extern int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); +extern int dib9000_remove_slave_frontend(struct dvb_frontend *fe); +extern struct dvb_frontend * dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); +extern struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe); +extern int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c); +extern int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed); +#else +static inline struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib9000_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +int dib9000_remove_slave_frontend(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline struct dvb_frontend * dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) { + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} +#endif + +#endif diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h index b7ad066..c4fd62f 100644 --- a/drivers/media/dvb/frontends/dibx000_common.h +++ b/drivers/media/dvb/frontends/dibx000_common.h @@ -219,6 +219,51 @@ struct dvb_frontend_parametersContext { #define FE_CALLBACK_TIME_NEVER 0xffffffff -#define ABS(x) ((x < 0) ? (-x) : (x)) +#define ABS(x) ((x<0)?(-x):(x)) + +#define DATA_BUS_ACCESS_MODE_8BIT 0x01 +#define DATA_BUS_ACCESS_MODE_16BIT 0x02 +#define DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT 0x10 + +struct dibGPIOFunction { +#define BOARD_GPIO_COMPONENT_BUS_ADAPTER 1 +#define BOARD_GPIO_COMPONENT_DEMOD 2 + u8 component; + +#define BOARD_GPIO_FUNCTION_BOARD_ON 1 +#define BOARD_GPIO_FUNCTION_BOARD_OFF 2 +#define BOARD_GPIO_FUNCTION_COMPONENT_ON 3 +#define BOARD_GPIO_FUNCTION_COMPONENT_OFF 4 +#define BOARD_GPIO_FUNCTION_SUBBAND_PWM 5 +#define BOARD_GPIO_FUNCTION_SUBBAND_GPIO 6 + u8 function; + +/* mask, direction and value are used specify which GPIO to change GPIO0 + * is LSB and possible GPIO31 is MSB. The same bit-position as in the + * mask is used for the direction and the value. Direction == 1 is OUT, + * 0 == IN. For direction "OUT" value is either 1 or 0, for direction IN + * value has no meaning. + * + * In case of BOARD_GPIO_FUNCTION_PWM mask is giving the GPIO to be + * used to do the PWM. Direction gives the PWModulator to be used. + * Value gives the PWM value in device-dependent scale. + */ + u32 mask; + u32 direction; + u32 value; +}; + +#define MAX_NB_SUBBANDS 8 +struct dibSubbandSelection { + u8 size; /* Actual number of subbands. */ + struct { + u16 f_mhz; + struct dibGPIOFunction gpio; + } subband[MAX_NB_SUBBANDS]; +}; + +#define DEMOD_TIMF_SET 0x00 +#define DEMOD_TIMF_GET 0x01 +#define DEMOD_TIMF_UPDATE 0x02 #endif -- cgit v0.10.2 From 713d54a8bd812229410a1902cd9b332a2a27af9f Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Tue, 4 Jan 2011 04:54:31 -0300 Subject: [media] DiB7090: add support for the dib7090 based This patch adds support for the SoC DiB7090 DVB-T demodulator and its melt-in UHF/VHF RF tuner. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 6aa02cb..18495bd 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -26,24 +26,29 @@ MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (defau #define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P: "); printk(args); printk("\n"); } } while (0) +struct i2c_device { + struct i2c_adapter *i2c_adap; + u8 i2c_addr; +}; + struct dib7000p_state { struct dvb_frontend demod; - struct dib7000p_config cfg; + struct dib7000p_config cfg; u8 i2c_addr; - struct i2c_adapter *i2c_adap; + struct i2c_adapter *i2c_adap; struct dibx000_i2c_master i2c_master; u16 wbd_ref; - u8 current_band; + u8 current_band; u32 current_bandwidth; struct dibx000_agc_config *current_agc; u32 timf; - u8 div_force_off : 1; - u8 div_state : 1; + u8 div_force_off:1; + u8 div_state:1; u16 div_sync_wait; u8 agc_state; @@ -51,7 +56,13 @@ struct dib7000p_state { u16 gpio_dir; u16 gpio_val; - u8 sfn_workaround_active :1; + u8 sfn_workaround_active:1; + +#define SOC7090 0x7090 + u16 version; + + u16 tuner_enable; + struct i2c_adapter dib7090_tuner_adap; }; enum dib7000p_power_mode { @@ -60,17 +71,20 @@ enum dib7000p_power_mode { DIB7000P_POWER_INTERFACE_ONLY, }; +static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode); +static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff); + static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg) { u8 wb[2] = { reg >> 8, reg & 0xff }; u8 rb[2]; struct i2c_msg msg[2] = { - { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 }, - { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + {.addr = state->i2c_addr >> 1,.flags = 0,.buf = wb,.len = 2}, + {.addr = state->i2c_addr >> 1,.flags = I2C_M_RD,.buf = rb,.len = 2}, }; if (i2c_transfer(state->i2c_adap, msg, 2) != 2) - dprintk("i2c read error on %d",reg); + dprintk("i2c read error on %d", reg); return (rb[0] << 8) | rb[1]; } @@ -82,11 +96,12 @@ static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val) (val >> 8) & 0xff, val & 0xff, }; struct i2c_msg msg = { - .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 + .addr = state->i2c_addr >> 1,.flags = 0,.buf = b,.len = 4 }; return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; } -static void dib7000p_write_tab(struct dib7000p_state *state, u16 *buf) + +static void dib7000p_write_tab(struct dib7000p_state *state, u16 * buf) { u16 l = 0, r, *n; n = buf; @@ -104,54 +119,54 @@ static void dib7000p_write_tab(struct dib7000p_state *state, u16 *buf) static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) { - int ret = 0; + int ret = 0; u16 outreg, fifo_threshold, smo_mode; outreg = 0; fifo_threshold = 1792; smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); - dprintk( "setting output mode for demod %p to %d", - &state->demod, mode); + dprintk("setting output mode for demod %p to %d", &state->demod, mode); switch (mode) { - case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock - outreg = (1 << 10); /* 0x0400 */ - break; - case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock - outreg = (1 << 10) | (1 << 6); /* 0x0440 */ - break; - case OUTMODE_MPEG2_SERIAL: // STBs with serial input - outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */ - break; - case OUTMODE_DIVERSITY: - if (state->cfg.hostbus_diversity) - outreg = (1 << 10) | (4 << 6); /* 0x0500 */ - else - outreg = (1 << 11); - break; - case OUTMODE_MPEG2_FIFO: // e.g. USB feeding - smo_mode |= (3 << 1); - fifo_threshold = 512; - outreg = (1 << 10) | (5 << 6); - break; - case OUTMODE_ANALOG_ADC: - outreg = (1 << 10) | (3 << 6); - break; - case OUTMODE_HIGH_Z: // disable - outreg = 0; - break; - default: - dprintk( "Unhandled output_mode passed to be set for demod %p",&state->demod); - break; + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + else + outreg = (1 << 11); + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_ANALOG_ADC: + outreg = (1 << 10) | (3 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p", &state->demod); + break; } if (state->cfg.output_mpeg2_in_188_bytes) - smo_mode |= (1 << 5) ; + smo_mode |= (1 << 5); - ret |= dib7000p_write_word(state, 235, smo_mode); - ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ - ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */ + ret |= dib7000p_write_word(state, 235, smo_mode); + ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ + if (state->version != SOC7090) + ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */ return ret; } @@ -161,13 +176,13 @@ static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff) struct dib7000p_state *state = demod->demodulator_priv; if (state->div_force_off) { - dprintk( "diversity combination deactivated - forced by COFDM parameters"); + dprintk("diversity combination deactivated - forced by COFDM parameters"); onoff = 0; dib7000p_write_word(state, 207, 0); } else dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); - state->div_state = (u8)onoff; + state->div_state = (u8) onoff; if (onoff) { dib7000p_write_word(state, 204, 6); @@ -184,37 +199,48 @@ static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff) static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode) { /* by default everything is powered off */ - u16 reg_774 = 0xffff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003, - reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff); + u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003, reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff); /* now, depending on the requested mode, we power on */ switch (mode) { /* power up everything in the demod */ - case DIB7000P_POWER_ALL: - reg_774 = 0x0000; reg_775 = 0x0000; reg_776 = 0x0; reg_899 = 0x0; reg_1280 &= 0x01ff; - break; - - case DIB7000P_POWER_ANALOG_ADC: - /* dem, cfg, iqc, sad, agc */ - reg_774 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10) | (1 << 9)); - /* nud */ - reg_776 &= ~((1 << 0)); - /* Dout */ + case DIB7000P_POWER_ALL: + reg_774 = 0x0000; + reg_775 = 0x0000; + reg_776 = 0x0; + reg_899 = 0x0; + if (state->version == SOC7090) + reg_1280 &= 0x001f; + else + reg_1280 &= 0x01ff; + break; + + case DIB7000P_POWER_ANALOG_ADC: + /* dem, cfg, iqc, sad, agc */ + reg_774 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10) | (1 << 9)); + /* nud */ + reg_776 &= ~((1 << 0)); + /* Dout */ + if (state->version != SOC7090) reg_1280 &= ~((1 << 11)); - /* fall through wanted to enable the interfaces */ + reg_1280 &= ~(1 << 6); + /* fall through wanted to enable the interfaces */ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */ - case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */ + case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */ + if (state->version == SOC7090) + reg_1280 &= ~((1 << 7) | (1 << 5)); + else reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10)); - break; + break; /* TODO following stuff is just converted from the dib7000-driver - check when is used what */ } - dib7000p_write_word(state, 774, reg_774); - dib7000p_write_word(state, 775, reg_775); - dib7000p_write_word(state, 776, reg_776); - dib7000p_write_word(state, 899, reg_899); + dib7000p_write_word(state, 774, reg_774); + dib7000p_write_word(state, 775, reg_775); + dib7000p_write_word(state, 776, reg_776); + dib7000p_write_word(state, 899, reg_899); dib7000p_write_word(state, 1280, reg_1280); return 0; @@ -222,40 +248,57 @@ static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_p static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no) { - u16 reg_908 = dib7000p_read_word(state, 908), - reg_909 = dib7000p_read_word(state, 909); + u16 reg_908 = dib7000p_read_word(state, 908), reg_909 = dib7000p_read_word(state, 909); + u16 reg; switch (no) { - case DIBX000_SLOW_ADC_ON: + case DIBX000_SLOW_ADC_ON: + if (state->version == SOC7090) { + reg = dib7000p_read_word(state, 1925); + + dib7000p_write_word(state, 1925, reg | (1 << 4) | (1 << 2)); /* en_slowAdc = 1 & reset_sladc = 1 */ + + reg = dib7000p_read_word(state, 1925); /* read acces to make it works... strange ... */ + msleep(200); + dib7000p_write_word(state, 1925, reg & ~(1 << 4)); /* en_slowAdc = 1 & reset_sladc = 0 */ + + reg = dib7000p_read_word(state, 72) & ~((0x3 << 14) | (0x3 << 12)); + dib7000p_write_word(state, 72, reg | (1 << 14) | (3 << 12) | 524); /* ref = Vin1 => Vbg ; sel = Vin0 or Vin3 ; (Vin2 = Vcm) */ + } else { reg_909 |= (1 << 1) | (1 << 0); dib7000p_write_word(state, 909, reg_909); reg_909 &= ~(1 << 1); - break; + } + break; - case DIBX000_SLOW_ADC_OFF: - reg_909 |= (1 << 1) | (1 << 0); - break; + case DIBX000_SLOW_ADC_OFF: + if (state->version == SOC7090) { + reg = dib7000p_read_word(state, 1925); + dib7000p_write_word(state, 1925, (reg & ~(1 << 2)) | (1 << 4)); /* reset_sladc = 1 en_slowAdc = 0 */ + } else + reg_909 |= (1 << 1) | (1 << 0); + break; - case DIBX000_ADC_ON: - reg_908 &= 0x0fff; - reg_909 &= 0x0003; - break; + case DIBX000_ADC_ON: + reg_908 &= 0x0fff; + reg_909 &= 0x0003; + break; - case DIBX000_ADC_OFF: // leave the VBG voltage on - reg_908 |= (1 << 14) | (1 << 13) | (1 << 12); - reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); - break; + case DIBX000_ADC_OFF: // leave the VBG voltage on + reg_908 |= (1 << 14) | (1 << 13) | (1 << 12); + reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + break; - case DIBX000_VBG_ENABLE: - reg_908 &= ~(1 << 15); - break; + case DIBX000_VBG_ENABLE: + reg_908 &= ~(1 << 15); + break; - case DIBX000_VBG_DISABLE: - reg_908 |= (1 << 15); - break; + case DIBX000_VBG_DISABLE: + reg_908 |= (1 << 15); + break; - default: - break; + default: + break; } // dprintk( "908: %x, 909: %x\n", reg_908, reg_909); @@ -275,17 +318,17 @@ static int dib7000p_set_bandwidth(struct dib7000p_state *state, u32 bw) state->current_bandwidth = bw; if (state->timf == 0) { - dprintk( "using default timf"); + dprintk("using default timf"); timf = state->cfg.bw->timf; } else { - dprintk( "using updated timf"); + dprintk("using updated timf"); timf = state->timf; } timf = timf * (bw / 50) / 160; dib7000p_write_word(state, 23, (u16) ((timf >> 16) & 0xffff)); - dib7000p_write_word(state, 24, (u16) ((timf ) & 0xffff)); + dib7000p_write_word(state, 24, (u16) ((timf) & 0xffff)); return 0; } @@ -295,7 +338,11 @@ static int dib7000p_sad_calib(struct dib7000p_state *state) /* internal */ // dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth dib7000p_write_word(state, 73, (0 << 1) | (0 << 0)); - dib7000p_write_word(state, 74, 776); // 0.625*3.3 / 4096 + + if (state->version == SOC7090) + dib7000p_write_word(state, 74, 2048); // P_sad_calib_value = (0.9/1.8)*4096 + else + dib7000p_write_word(state, 74, 776); // P_sad_calib_value = 0.625*3.3 / 4096 /* do the calibration */ dib7000p_write_word(state, 73, (1 << 0)); @@ -314,37 +361,92 @@ int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value) state->wbd_ref = value; return dib7000p_write_word(state, 105, (dib7000p_read_word(state, 105) & 0xf000) | value); } - EXPORT_SYMBOL(dib7000p_set_wbd_ref); + static void dib7000p_reset_pll(struct dib7000p_state *state) { struct dibx000_bandwidth_config *bw = &state->cfg.bw[0]; u16 clk_cfg0; - /* force PLL bypass */ - clk_cfg0 = (1 << 15) | ((bw->pll_ratio & 0x3f) << 9) | - (bw->modulo << 7) | (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | - (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0); + if (state->version == SOC7090) { + dib7000p_write_word(state, 1856, (!bw->pll_reset << 13) | (bw->pll_range << 12) | (bw->pll_ratio << 6) | (bw->pll_prediv)); - dib7000p_write_word(state, 900, clk_cfg0); + while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) { + } + + dib7000p_write_word(state, 1857, dib7000p_read_word(state, 1857) | (!bw->pll_bypass << 15)); + } else { + /* force PLL bypass */ + clk_cfg0 = (1 << 15) | ((bw->pll_ratio & 0x3f) << 9) | + (bw->modulo << 7) | (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0); - /* P_pll_cfg */ - dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset); - clk_cfg0 = (bw->pll_bypass << 15) | (clk_cfg0 & 0x7fff); - dib7000p_write_word(state, 900, clk_cfg0); + dib7000p_write_word(state, 900, clk_cfg0); + + /* P_pll_cfg */ + dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset); + clk_cfg0 = (bw->pll_bypass << 15) | (clk_cfg0 & 0x7fff); + dib7000p_write_word(state, 900, clk_cfg0); + } - dib7000p_write_word(state, 18, (u16) (((bw->internal*1000) >> 16) & 0xffff)); - dib7000p_write_word(state, 19, (u16) ( (bw->internal*1000 ) & 0xffff)); - dib7000p_write_word(state, 21, (u16) ( (bw->ifreq >> 16) & 0xffff)); - dib7000p_write_word(state, 22, (u16) ( (bw->ifreq ) & 0xffff)); + dib7000p_write_word(state, 18, (u16) (((bw->internal * 1000) >> 16) & 0xffff)); + dib7000p_write_word(state, 19, (u16) ((bw->internal * 1000) & 0xffff)); + dib7000p_write_word(state, 21, (u16) ((bw->ifreq >> 16) & 0xffff)); + dib7000p_write_word(state, 22, (u16) ((bw->ifreq) & 0xffff)); dib7000p_write_word(state, 72, bw->sad_cfg); } +static u32 dib7000p_get_internal_freq(struct dib7000p_state *state) +{ + u32 internal = (u32) dib7000p_read_word(state, 18) << 16; + internal |= (u32) dib7000p_read_word(state, 19); + internal /= 1000; + + return internal; +} + +int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 reg_1857, reg_1856 = dib7000p_read_word(state, 1856); + u8 loopdiv, prediv; + u32 internal, xtal; + + /* get back old values */ + prediv = reg_1856 & 0x3f; + loopdiv = (reg_1856 >> 6) & 0x3f; + + if ((bw != NULL) && (bw->pll_prediv != prediv || bw->pll_ratio != loopdiv)) { + dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, bw->pll_prediv, loopdiv, bw->pll_ratio); + reg_1856 &= 0xf000; + reg_1857 = dib7000p_read_word(state, 1857); + dib7000p_write_word(state, 1857, reg_1857 & ~(1 << 15)); // desable pll + + dib7000p_write_word(state, 1856, reg_1856 | ((bw->pll_ratio & 0x3f) << 6) | (bw->pll_prediv & 0x3f)); + + /* write new system clk into P_sec_len */ + internal = dib7000p_get_internal_freq(state); + xtal = (internal / loopdiv) * prediv; + internal = 1000 * (xtal / bw->pll_prediv) * bw->pll_ratio; /* new internal */ + dib7000p_write_word(state, 18, (u16) ((internal >> 16) & 0xffff)); + dib7000p_write_word(state, 19, (u16) (internal & 0xffff)); + + dib7000p_write_word(state, 1857, reg_1857 | (1 << 15)); // enable pll + + while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) { + dprintk("Waiting for PLL to lock"); + } + + return 0; + } + return -EIO; +} +EXPORT_SYMBOL(dib7000p_update_pll); + static int dib7000p_reset_gpio(struct dib7000p_state *st) { /* reset the GPIOs */ - dprintk( "gpio dir: %x: val: %x, pwm_pos: %x",st->gpio_dir, st->gpio_val,st->cfg.gpio_pwm_pos); + dprintk("gpio dir: %x: val: %x, pwm_pos: %x", st->gpio_dir, st->gpio_val, st->cfg.gpio_pwm_pos); dib7000p_write_word(st, 1029, st->gpio_dir); dib7000p_write_word(st, 1030, st->gpio_val); @@ -360,13 +462,13 @@ static int dib7000p_reset_gpio(struct dib7000p_state *st) static int dib7000p_cfg_gpio(struct dib7000p_state *st, u8 num, u8 dir, u8 val) { st->gpio_dir = dib7000p_read_word(st, 1029); - st->gpio_dir &= ~(1 << num); /* reset the direction bit */ - st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */ + st->gpio_dir &= ~(1 << num); /* reset the direction bit */ + st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */ dib7000p_write_word(st, 1029, st->gpio_dir); st->gpio_val = dib7000p_read_word(st, 1030); - st->gpio_val &= ~(1 << num); /* reset the direction bit */ - st->gpio_val |= (val & 0x01) << num; /* set the new value */ + st->gpio_val &= ~(1 << num); /* reset the direction bit */ + st->gpio_val |= (val & 0x01) << num; /* set the new value */ dib7000p_write_word(st, 1030, st->gpio_val); return 0; @@ -377,96 +479,97 @@ int dib7000p_set_gpio(struct dvb_frontend *demod, u8 num, u8 dir, u8 val) struct dib7000p_state *state = demod->demodulator_priv; return dib7000p_cfg_gpio(state, num, dir, val); } - EXPORT_SYMBOL(dib7000p_set_gpio); -static u16 dib7000p_defaults[] = -{ +static u16 dib7000p_defaults[] = { // auto search configuration 3, 2, - 0x0004, - 0x1000, - 0x0814, /* Equal Lock */ + 0x0004, + 0x1000, + 0x0814, /* Equal Lock */ 12, 6, - 0x001b, - 0x7740, - 0x005b, - 0x8d80, - 0x01c9, - 0xc380, - 0x0000, - 0x0080, - 0x0000, - 0x0090, - 0x0001, - 0xd4c0, + 0x001b, + 0x7740, + 0x005b, + 0x8d80, + 0x01c9, + 0xc380, + 0x0000, + 0x0080, + 0x0000, + 0x0090, + 0x0001, + 0xd4c0, 1, 26, - 0x6680, // P_timf_alpha=6, P_corm_alpha=6, P_corm_thres=128 default: 6,4,26 + 0x6680, // P_timf_alpha=6, P_corm_alpha=6, P_corm_thres=128 default: 6,4,26 /* set ADC level to -16 */ 11, 79, - (1 << 13) - 825 - 117, - (1 << 13) - 837 - 117, - (1 << 13) - 811 - 117, - (1 << 13) - 766 - 117, - (1 << 13) - 737 - 117, - (1 << 13) - 693 - 117, - (1 << 13) - 648 - 117, - (1 << 13) - 619 - 117, - (1 << 13) - 575 - 117, - (1 << 13) - 531 - 117, - (1 << 13) - 501 - 117, + (1 << 13) - 825 - 117, + (1 << 13) - 837 - 117, + (1 << 13) - 811 - 117, + (1 << 13) - 766 - 117, + (1 << 13) - 737 - 117, + (1 << 13) - 693 - 117, + (1 << 13) - 648 - 117, + (1 << 13) - 619 - 117, + (1 << 13) - 575 - 117, + (1 << 13) - 531 - 117, + (1 << 13) - 501 - 117, 1, 142, - 0x0410, // P_palf_filter_on=1, P_palf_filter_freeze=0, P_palf_alpha_regul=16 + 0x0410, // P_palf_filter_on=1, P_palf_filter_freeze=0, P_palf_alpha_regul=16 /* disable power smoothing */ 8, 145, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, 1, 154, - 1 << 13, // P_fft_freq_dir=1, P_fft_nb_to_cut=0 + 1 << 13, // P_fft_freq_dir=1, P_fft_nb_to_cut=0 1, 168, - 0x0ccd, // P_pha3_thres, default 0x3000 + 0x0ccd, // P_pha3_thres, default 0x3000 // 1, 169, // 0x0010, // P_cti_use_cpe=0, P_cti_use_prog=0, P_cti_win_len=16, default: 0x0010 1, 183, - 0x200f, // P_cspu_regul=512, P_cspu_win_cut=15, default: 0x2005 + 0x200f, // P_cspu_regul=512, P_cspu_win_cut=15, default: 0x2005 + + 1, 212, + 0x169, // P_vit_ksi_dwn = 5 P_vit_ksi_up = 5 0x1e1, // P_vit_ksi_dwn = 4 P_vit_ksi_up = 7 5, 187, - 0x023d, // P_adp_regul_cnt=573, default: 410 - 0x00a4, // P_adp_noise_cnt= - 0x00a4, // P_adp_regul_ext - 0x7ff0, // P_adp_noise_ext - 0x3ccc, // P_adp_fil + 0x023d, // P_adp_regul_cnt=573, default: 410 + 0x00a4, // P_adp_noise_cnt= + 0x00a4, // P_adp_regul_ext + 0x7ff0, // P_adp_noise_ext + 0x3ccc, // P_adp_fil 1, 198, - 0x800, // P_equal_thres_wgn + 0x800, // P_equal_thres_wgn 1, 222, - 0x0010, // P_fec_ber_rs_len=2 + 0x0010, // P_fec_ber_rs_len=2 1, 235, - 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard 2, 901, - 0x0006, // P_clk_cfg1 - (3 << 10) | (1 << 6), // P_divclksel=3 P_divbitsel=1 + 0x0006, // P_clk_cfg1 + (3 << 10) | (1 << 6), // P_divclksel=3 P_divbitsel=1 1, 905, - 0x2c8e, // Tuner IO bank: max drive (14mA) + divout pads max drive + 0x2c8e, // Tuner IO bank: max drive (14mA) + divout pads max drive 0, }; @@ -475,51 +578,65 @@ static int dib7000p_demod_reset(struct dib7000p_state *state) { dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + if (state->version == SOC7090) + dibx000_reset_i2c_master(&state->i2c_master); + dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE); /* restart all parts */ - dib7000p_write_word(state, 770, 0xffff); - dib7000p_write_word(state, 771, 0xffff); - dib7000p_write_word(state, 772, 0x001f); - dib7000p_write_word(state, 898, 0x0003); - /* except i2c, sdio, gpio - control interfaces */ - dib7000p_write_word(state, 1280, 0x01fc - ((1 << 7) | (1 << 6) | (1 << 5)) ); - - dib7000p_write_word(state, 770, 0); - dib7000p_write_word(state, 771, 0); - dib7000p_write_word(state, 772, 0); - dib7000p_write_word(state, 898, 0); + dib7000p_write_word(state, 770, 0xffff); + dib7000p_write_word(state, 771, 0xffff); + dib7000p_write_word(state, 772, 0x001f); + dib7000p_write_word(state, 898, 0x0003); + dib7000p_write_word(state, 1280, 0x001f - ((1 << 4) | (1 << 3))); + + dib7000p_write_word(state, 770, 0); + dib7000p_write_word(state, 771, 0); + dib7000p_write_word(state, 772, 0); + dib7000p_write_word(state, 898, 0); dib7000p_write_word(state, 1280, 0); /* default */ dib7000p_reset_pll(state); if (dib7000p_reset_gpio(state) != 0) - dprintk( "GPIO reset was not successful."); + dprintk("GPIO reset was not successful."); - if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0) - dprintk( "OUTPUT_MODE could not be reset."); + if (state->version == SOC7090) { + dib7000p_write_word(state, 899, 0); - /* unforce divstr regardless whether i2c enumeration was done or not */ - dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1) ); - - dib7000p_set_bandwidth(state, 8000); + /* impulse noise */ + dib7000p_write_word(state, 42, (1<<5) | 3); /* P_iqc_thsat_ipc = 1 ; P_iqc_win2 = 3 */ + dib7000p_write_word(state, 43, 0x2d4); /*-300 fag P_iqc_dect_min = -280 */ + dib7000p_write_word(state, 44, 300); /* 300 fag P_iqc_dect_min = +280 */ + //dib7000p_write_word(state, 273, (1<<6) | 10); /* P_vit_inoise_sel = 1, P_vit_inoise_gain = 10*/ + dib7000p_write_word(state, 273, (1<<6) | 30); //26/* P_vit_inoise_sel = 1, P_vit_inoise_gain = 26*/// FAG + } + if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + dprintk("OUTPUT_MODE could not be reset."); dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); dib7000p_sad_calib(state); dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_OFF); - // P_iqc_alpha_pha, P_iqc_alpha_amp_dcc_alpha, ... - if(state->cfg.tuner_is_baseband) - dib7000p_write_word(state, 36,0x0755); - else - dib7000p_write_word(state, 36,0x1f55); + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1)); + + dib7000p_set_bandwidth(state, 8000); + + if(state->version == SOC7090) { + dib7000p_write_word(state, 36, 0x5755);/* P_iqc_impnc_on =1 & P_iqc_corr_inh = 1 for impulsive noise */ + } else { // P_iqc_alpha_pha, P_iqc_alpha_amp_dcc_alpha, ... + if (state->cfg.tuner_is_baseband) + dib7000p_write_word(state, 36, 0x0755); + else + dib7000p_write_word(state, 36, 0x1f55); + } dib7000p_write_tab(state, dib7000p_defaults); dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); - return 0; } @@ -527,9 +644,9 @@ static void dib7000p_pll_clk_cfg(struct dib7000p_state *state) { u16 tmp = 0; tmp = dib7000p_read_word(state, 903); - dib7000p_write_word(state, 903, (tmp | 0x1)); //pwr-up pll + dib7000p_write_word(state, 903, (tmp | 0x1)); //pwr-up pll tmp = dib7000p_read_word(state, 900); - dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); //use High freq clock + dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); //use High freq clock } static void dib7000p_restart_agc(struct dib7000p_state *state) @@ -547,7 +664,7 @@ static int dib7000p_update_lna(struct dib7000p_state *state) if (state->cfg.update_lna) { // read dyn_gain here (because it is demod-dependent and not fe) dyn_gain = dib7000p_read_word(state, 394); - if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed + if (state->cfg.update_lna(&state->demod, dyn_gain)) { // LNA has changed dib7000p_restart_agc(state); return 1; } @@ -571,24 +688,24 @@ static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band) } if (agc == NULL) { - dprintk( "no valid AGC configuration found for band 0x%02x",band); + dprintk("no valid AGC configuration found for band 0x%02x", band); return -EINVAL; } state->current_agc = agc; /* AGC */ - dib7000p_write_word(state, 75 , agc->setup ); - dib7000p_write_word(state, 76 , agc->inv_gain ); - dib7000p_write_word(state, 77 , agc->time_stabiliz ); + dib7000p_write_word(state, 75, agc->setup); + dib7000p_write_word(state, 76, agc->inv_gain); + dib7000p_write_word(state, 77, agc->time_stabiliz); dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock); // Demod AGC loop configuration dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp); - dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp); + dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp); /* AGC continued */ - dprintk( "WBD: ref: %d, sel: %d, active: %d, alpha: %d", + dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d", state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); if (state->wbd_ref != 0) @@ -598,101 +715,139 @@ static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band) dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8)); - dib7000p_write_word(state, 107, agc->agc1_max); - dib7000p_write_word(state, 108, agc->agc1_min); - dib7000p_write_word(state, 109, agc->agc2_max); - dib7000p_write_word(state, 110, agc->agc2_min); - dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2); - dib7000p_write_word(state, 112, agc->agc1_pt3); + dib7000p_write_word(state, 107, agc->agc1_max); + dib7000p_write_word(state, 108, agc->agc1_min); + dib7000p_write_word(state, 109, agc->agc2_max); + dib7000p_write_word(state, 110, agc->agc2_min); + dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2); + dib7000p_write_word(state, 112, agc->agc1_pt3); dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); - dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); return 0; } +static void dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz) +{ + u32 internal = dib7000p_get_internal_freq(state); + s32 unit_khz_dds_val = 67108864 / (internal); /* 2**26 / Fsampling is the unit 1KHz offset */ + u32 abs_offset_khz = ABS(offset_khz); + u32 dds = state->cfg.bw->ifreq & 0x1ffffff; + u8 invert = !!(state->cfg.bw->ifreq & (1 << 25)); + + dprintk("setting a frequency offset of %dkHz internal freq = %d invert = %d", offset_khz, internal, invert); + + if (offset_khz < 0) + unit_khz_dds_val *= -1; + + /* IF tuner */ + if (invert) + dds -= (abs_offset_khz * unit_khz_dds_val); /* /100 because of /100 on the unit_khz_dds_val line calc for better accuracy */ + else + dds += (abs_offset_khz * unit_khz_dds_val); + + if (abs_offset_khz <= (internal / 2)) { /* Max dds offset is the half of the demod freq */ + dib7000p_write_word(state, 21, (u16) (((dds >> 16) & 0x1ff) | (0 << 10) | (invert << 9))); + dib7000p_write_word(state, 22, (u16) (dds & 0xffff)); + } +} + static int dib7000p_agc_startup(struct dvb_frontend *demod, struct dvb_frontend_parameters *ch) { struct dib7000p_state *state = demod->demodulator_priv; int ret = -1; u8 *agc_state = &state->agc_state; u8 agc_split; + u16 reg; + u32 upd_demod_gain_period = 0x1000; switch (state->agc_state) { - case 0: - // set power-up level: interf+analog+AGC - dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + case 0: + // set power-up level: interf+analog+AGC + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + if (state->version == SOC7090) { + reg = dib7000p_read_word(state, 0x79b) & 0xff00; + dib7000p_write_word(state, 0x79a, upd_demod_gain_period & 0xFFFF); /* lsb */ + dib7000p_write_word(state, 0x79b, reg | (1 << 14) | ((upd_demod_gain_period >> 16) & 0xFF)); // bit 14 = enDemodGain + + /* enable adc i & q */ + reg = dib7000p_read_word(state, 0x780); + dib7000p_write_word(state, 0x780, (reg | (0x3)) & (~(1 << 7))); + } else { dib7000p_set_adc_state(state, DIBX000_ADC_ON); dib7000p_pll_clk_cfg(state); + } - if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency/1000)) != 0) - return -1; - - ret = 7; - (*agc_state)++; - break; + if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency / 1000)) != 0) + return -1; - case 1: - // AGC initialization - if (state->cfg.agc_control) - state->cfg.agc_control(&state->demod, 1); - - dib7000p_write_word(state, 78, 32768); - if (!state->current_agc->perform_agc_softsplit) { - /* we are using the wbd - so slow AGC startup */ - /* force 0 split on WBD and restart AGC */ - dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | (1 << 8)); - (*agc_state)++; - ret = 5; - } else { - /* default AGC startup */ - (*agc_state) = 4; - /* wait AGC rough lock time */ - ret = 7; - } + dib7000p_set_dds(state, 0); + ret = 7; + (*agc_state)++; + break; - dib7000p_restart_agc(state); - break; + case 1: + // AGC initialization + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 1); - case 2: /* fast split search path after 5sec */ - dib7000p_write_word(state, 75, state->current_agc->setup | (1 << 4)); /* freeze AGC loop */ - dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (2 << 9) | (0 << 8)); /* fast split search 0.25kHz */ + dib7000p_write_word(state, 78, 32768); + if (!state->current_agc->perform_agc_softsplit) { + /* we are using the wbd - so slow AGC startup */ + /* force 0 split on WBD and restart AGC */ + dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | (1 << 8)); (*agc_state)++; - ret = 14; - break; + ret = 5; + } else { + /* default AGC startup */ + (*agc_state) = 4; + /* wait AGC rough lock time */ + ret = 7; + } - case 3: /* split search ended */ - agc_split = (u8)dib7000p_read_word(state, 396); /* store the split value for the next time */ - dib7000p_write_word(state, 78, dib7000p_read_word(state, 394)); /* set AGC gain start value */ + dib7000p_restart_agc(state); + break; - dib7000p_write_word(state, 75, state->current_agc->setup); /* std AGC loop */ - dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */ + case 2: /* fast split search path after 5sec */ + dib7000p_write_word(state, 75, state->current_agc->setup | (1 << 4)); /* freeze AGC loop */ + dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (2 << 9) | (0 << 8)); /* fast split search 0.25kHz */ + (*agc_state)++; + ret = 14; + break; - dib7000p_restart_agc(state); + case 3: /* split search ended */ + agc_split = (u8) dib7000p_read_word(state, 396); /* store the split value for the next time */ + dib7000p_write_word(state, 78, dib7000p_read_word(state, 394)); /* set AGC gain start value */ - dprintk( "SPLIT %p: %hd", demod, agc_split); + dib7000p_write_word(state, 75, state->current_agc->setup); /* std AGC loop */ + dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */ - (*agc_state)++; - ret = 5; - break; + dib7000p_restart_agc(state); - case 4: /* LNA startup */ - // wait AGC accurate lock time - ret = 7; + dprintk("SPLIT %p: %hd", demod, agc_split); - if (dib7000p_update_lna(state)) - // wait only AGC rough lock time - ret = 5; - else // nothing was done, go to the next state - (*agc_state)++; - break; + (*agc_state)++; + ret = 5; + break; - case 5: - if (state->cfg.agc_control) - state->cfg.agc_control(&state->demod, 0); + case 4: /* LNA startup */ + // wait AGC accurate lock time + ret = 7; + + if (dib7000p_update_lna(state)) + // wait only AGC rough lock time + ret = 5; + else // nothing was done, go to the next state (*agc_state)++; - break; - default: - break; + break; + + case 5: + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 0); + (*agc_state)++; + break; + default: + break; } return ret; } @@ -703,45 +858,89 @@ static void dib7000p_update_timf(struct dib7000p_state *state) state->timf = timf * 160 / (state->current_bandwidth / 50); dib7000p_write_word(state, 23, (u16) (timf >> 16)); dib7000p_write_word(state, 24, (u16) (timf & 0xffff)); - dprintk( "updated timf_frequency: %d (default: %d)",state->timf, state->cfg.bw->timf); + dprintk("updated timf_frequency: %d (default: %d)", state->timf, state->cfg.bw->timf); + +} +u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) +{ + struct dib7000p_state *state = fe->demodulator_priv; + switch (op) { + case DEMOD_TIMF_SET: + state->timf = timf; + break; + case DEMOD_TIMF_UPDATE: + dib7000p_update_timf(state); + break; + case DEMOD_TIMF_GET: + break; + } + dib7000p_set_bandwidth(state, state->current_bandwidth); + return state->timf; } +EXPORT_SYMBOL(dib7000p_ctrl_timf); static void dib7000p_set_channel(struct dib7000p_state *state, struct dvb_frontend_parameters *ch, u8 seq) { u16 value, est[4]; - dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth)); + dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth)); /* nfft, guard, qam, alpha */ value = 0; switch (ch->u.ofdm.transmission_mode) { - case TRANSMISSION_MODE_2K: value |= (0 << 7); break; - case TRANSMISSION_MODE_4K: value |= (2 << 7); break; - default: - case TRANSMISSION_MODE_8K: value |= (1 << 7); break; + case TRANSMISSION_MODE_2K: + value |= (0 << 7); + break; + case TRANSMISSION_MODE_4K: + value |= (2 << 7); + break; + default: + case TRANSMISSION_MODE_8K: + value |= (1 << 7); + break; } switch (ch->u.ofdm.guard_interval) { - case GUARD_INTERVAL_1_32: value |= (0 << 5); break; - case GUARD_INTERVAL_1_16: value |= (1 << 5); break; - case GUARD_INTERVAL_1_4: value |= (3 << 5); break; - default: - case GUARD_INTERVAL_1_8: value |= (2 << 5); break; + case GUARD_INTERVAL_1_32: + value |= (0 << 5); + break; + case GUARD_INTERVAL_1_16: + value |= (1 << 5); + break; + case GUARD_INTERVAL_1_4: + value |= (3 << 5); + break; + default: + case GUARD_INTERVAL_1_8: + value |= (2 << 5); + break; } switch (ch->u.ofdm.constellation) { - case QPSK: value |= (0 << 3); break; - case QAM_16: value |= (1 << 3); break; - default: - case QAM_64: value |= (2 << 3); break; + case QPSK: + value |= (0 << 3); + break; + case QAM_16: + value |= (1 << 3); + break; + default: + case QAM_64: + value |= (2 << 3); + break; } switch (HIERARCHY_1) { - case HIERARCHY_2: value |= 2; break; - case HIERARCHY_4: value |= 4; break; - default: - case HIERARCHY_1: value |= 1; break; + case HIERARCHY_2: + value |= 2; + break; + case HIERARCHY_4: + value |= 4; + break; + default: + case HIERARCHY_1: + value |= 1; + break; } dib7000p_write_word(state, 0, value); - dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */ + dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */ /* P_dintl_native, P_dintlv_inv, P_hrch, P_code_rate, P_select_hp */ value = 0; @@ -752,39 +951,63 @@ static void dib7000p_set_channel(struct dib7000p_state *state, struct dvb_fronte if (1 == 1) value |= 1; switch ((ch->u.ofdm.hierarchy_information == 0 || 1 == 1) ? ch->u.ofdm.code_rate_HP : ch->u.ofdm.code_rate_LP) { - case FEC_2_3: value |= (2 << 1); break; - case FEC_3_4: value |= (3 << 1); break; - case FEC_5_6: value |= (5 << 1); break; - case FEC_7_8: value |= (7 << 1); break; - default: - case FEC_1_2: value |= (1 << 1); break; + case FEC_2_3: + value |= (2 << 1); + break; + case FEC_3_4: + value |= (3 << 1); + break; + case FEC_5_6: + value |= (5 << 1); + break; + case FEC_7_8: + value |= (7 << 1); + break; + default: + case FEC_1_2: + value |= (1 << 1); + break; } dib7000p_write_word(state, 208, value); /* offset loop parameters */ - dib7000p_write_word(state, 26, 0x6680); // timf(6xxx) - dib7000p_write_word(state, 32, 0x0003); // pha_off_max(xxx3) - dib7000p_write_word(state, 29, 0x1273); // isi - dib7000p_write_word(state, 33, 0x0005); // sfreq(xxx5) + dib7000p_write_word(state, 26, 0x6680); // timf(6xxx) + dib7000p_write_word(state, 32, 0x0003); // pha_off_max(xxx3) + dib7000p_write_word(state, 29, 0x1273); // isi + dib7000p_write_word(state, 33, 0x0005); // sfreq(xxx5) /* P_dvsy_sync_wait */ switch (ch->u.ofdm.transmission_mode) { - case TRANSMISSION_MODE_8K: value = 256; break; - case TRANSMISSION_MODE_4K: value = 128; break; - case TRANSMISSION_MODE_2K: - default: value = 64; break; + case TRANSMISSION_MODE_8K: + value = 256; + break; + case TRANSMISSION_MODE_4K: + value = 128; + break; + case TRANSMISSION_MODE_2K: + default: + value = 64; + break; } switch (ch->u.ofdm.guard_interval) { - case GUARD_INTERVAL_1_16: value *= 2; break; - case GUARD_INTERVAL_1_8: value *= 4; break; - case GUARD_INTERVAL_1_4: value *= 8; break; - default: - case GUARD_INTERVAL_1_32: value *= 1; break; + case GUARD_INTERVAL_1_16: + value *= 2; + break; + case GUARD_INTERVAL_1_8: + value *= 4; + break; + case GUARD_INTERVAL_1_4: + value *= 8; + break; + default: + case GUARD_INTERVAL_1_32: + value *= 1; + break; } if (state->cfg.diversity_delay == 0) - state->div_sync_wait = (value * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo + state->div_sync_wait = (value * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo else - state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for one DVSY-fifo + state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for one DVSY-fifo /* deactive the possibility of diversity reception if extended interleaver */ state->div_force_off = !1 && ch->u.ofdm.transmission_mode != TRANSMISSION_MODE_8K; @@ -792,24 +1015,24 @@ static void dib7000p_set_channel(struct dib7000p_state *state, struct dvb_fronte /* channel estimation fine configuration */ switch (ch->u.ofdm.constellation) { - case QAM_64: - est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ - est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ - est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ - est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ - break; - case QAM_16: - est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ - est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ - est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ - est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ - break; - default: - est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ - est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ - est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ - est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ - break; + case QAM_64: + est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + break; + case QAM_16: + est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + break; + default: + est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; } for (value = 0; value < 4; value++) dib7000p_write_word(state, 187 + value, est[value]); @@ -820,14 +1043,15 @@ static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dvb_fron struct dib7000p_state *state = demod->demodulator_priv; struct dvb_frontend_parameters schan; u32 value, factor; + u32 internal = dib7000p_get_internal_freq(state); schan = *ch; schan.u.ofdm.constellation = QAM_64; - schan.u.ofdm.guard_interval = GUARD_INTERVAL_1_32; - schan.u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; - schan.u.ofdm.code_rate_HP = FEC_2_3; - schan.u.ofdm.code_rate_LP = FEC_3_4; - schan.u.ofdm.hierarchy_information = 0; + schan.u.ofdm.guard_interval = GUARD_INTERVAL_1_32; + schan.u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; + schan.u.ofdm.code_rate_HP = FEC_2_3; + schan.u.ofdm.code_rate_LP = FEC_3_4; + schan.u.ofdm.hierarchy_information = 0; dib7000p_set_channel(state, &schan, 7); @@ -838,15 +1062,15 @@ static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dvb_fron factor = 6; // always use the setting for 8MHz here lock_time for 7,6 MHz are longer - value = 30 * state->cfg.bw->internal * factor; - dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time - dib7000p_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time - value = 100 * state->cfg.bw->internal * factor; - dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time - dib7000p_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time - value = 500 * state->cfg.bw->internal * factor; - dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time - dib7000p_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time + value = 30 * internal * factor; + dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time + dib7000p_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time + value = 100 * internal * factor; + dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time + dib7000p_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time + value = 500 * internal * factor; + dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time + dib7000p_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time value = dib7000p_read_word(state, 0); dib7000p_write_word(state, 0, (u16) ((1 << 9) | value)); @@ -861,101 +1085,101 @@ static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod) struct dib7000p_state *state = demod->demodulator_priv; u16 irq_pending = dib7000p_read_word(state, 1284); - if (irq_pending & 0x1) // failed + if (irq_pending & 0x1) // failed return 1; - if (irq_pending & 0x2) // succeeded + if (irq_pending & 0x2) // succeeded return 2; - return 0; // still pending + return 0; // still pending } static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32 bw) { - static s16 notch[]={16143, 14402, 12238, 9713, 6902, 3888, 759, -2392}; - static u8 sine [] ={0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22, - 24, 25, 27, 28, 30, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, - 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80, - 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 99, 101, 102, 104, 105, - 107, 108, 109, 111, 112, 114, 115, 117, 118, 119, 121, 122, 123, 125, 126, - 128, 129, 130, 132, 133, 134, 136, 137, 138, 140, 141, 142, 144, 145, 146, - 147, 149, 150, 151, 152, 154, 155, 156, 157, 159, 160, 161, 162, 164, 165, - 166, 167, 168, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, - 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, - 199, 200, 201, 202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212, - 213, 214, 215, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224, - 225, 226, 227, 227, 228, 229, 229, 230, 231, 231, 232, 233, 233, 234, 235, - 235, 236, 237, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243, - 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, 249, 249, - 250, 250, 250, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 254, - 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255}; + static s16 notch[] = { 16143, 14402, 12238, 9713, 6902, 3888, 759, -2392 }; + static u8 sine[] = { 0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22, + 24, 25, 27, 28, 30, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, + 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80, + 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 99, 101, 102, 104, 105, + 107, 108, 109, 111, 112, 114, 115, 117, 118, 119, 121, 122, 123, 125, 126, + 128, 129, 130, 132, 133, 134, 136, 137, 138, 140, 141, 142, 144, 145, 146, + 147, 149, 150, 151, 152, 154, 155, 156, 157, 159, 160, 161, 162, 164, 165, + 166, 167, 168, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224, + 225, 226, 227, 227, 228, 229, 229, 230, 231, 231, 232, 233, 233, 234, 235, + 235, 236, 237, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243, + 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, 249, 249, + 250, 250, 250, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 254, + 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255 + }; u32 xtal = state->cfg.bw->xtal_hz / 1000; int f_rel = DIV_ROUND_CLOSEST(rf_khz, xtal) * xtal - rf_khz; int k; - int coef_re[8],coef_im[8]; + int coef_re[8], coef_im[8]; int bw_khz = bw; u32 pha; - dprintk( "relative position of the Spur: %dk (RF: %dk, XTAL: %dk)", f_rel, rf_khz, xtal); + dprintk("relative position of the Spur: %dk (RF: %dk, XTAL: %dk)", f_rel, rf_khz, xtal); - - if (f_rel < -bw_khz/2 || f_rel > bw_khz/2) + if (f_rel < -bw_khz / 2 || f_rel > bw_khz / 2) return; bw_khz /= 100; - dib7000p_write_word(state, 142 ,0x0610); + dib7000p_write_word(state, 142, 0x0610); for (k = 0; k < 8; k++) { - pha = ((f_rel * (k+1) * 112 * 80/bw_khz) /1000) & 0x3ff; + pha = ((f_rel * (k + 1) * 112 * 80 / bw_khz) / 1000) & 0x3ff; - if (pha==0) { + if (pha == 0) { coef_re[k] = 256; coef_im[k] = 0; - } else if(pha < 256) { - coef_re[k] = sine[256-(pha&0xff)]; - coef_im[k] = sine[pha&0xff]; + } else if (pha < 256) { + coef_re[k] = sine[256 - (pha & 0xff)]; + coef_im[k] = sine[pha & 0xff]; } else if (pha == 256) { coef_re[k] = 0; coef_im[k] = 256; } else if (pha < 512) { - coef_re[k] = -sine[pha&0xff]; - coef_im[k] = sine[256 - (pha&0xff)]; + coef_re[k] = -sine[pha & 0xff]; + coef_im[k] = sine[256 - (pha & 0xff)]; } else if (pha == 512) { coef_re[k] = -256; coef_im[k] = 0; } else if (pha < 768) { - coef_re[k] = -sine[256-(pha&0xff)]; - coef_im[k] = -sine[pha&0xff]; + coef_re[k] = -sine[256 - (pha & 0xff)]; + coef_im[k] = -sine[pha & 0xff]; } else if (pha == 768) { coef_re[k] = 0; coef_im[k] = -256; } else { - coef_re[k] = sine[pha&0xff]; - coef_im[k] = -sine[256 - (pha&0xff)]; + coef_re[k] = sine[pha & 0xff]; + coef_im[k] = -sine[256 - (pha & 0xff)]; } coef_re[k] *= notch[k]; - coef_re[k] += (1<<14); - if (coef_re[k] >= (1<<24)) - coef_re[k] = (1<<24) - 1; - coef_re[k] /= (1<<15); + coef_re[k] += (1 << 14); + if (coef_re[k] >= (1 << 24)) + coef_re[k] = (1 << 24) - 1; + coef_re[k] /= (1 << 15); coef_im[k] *= notch[k]; - coef_im[k] += (1<<14); - if (coef_im[k] >= (1<<24)) - coef_im[k] = (1<<24)-1; - coef_im[k] /= (1<<15); + coef_im[k] += (1 << 14); + if (coef_im[k] >= (1 << 24)) + coef_im[k] = (1 << 24) - 1; + coef_im[k] /= (1 << 15); - dprintk( "PALF COEF: %d re: %d im: %d", k, coef_re[k], coef_im[k]); + dprintk("PALF COEF: %d re: %d im: %d", k, coef_re[k], coef_im[k]); dib7000p_write_word(state, 143, (0 << 14) | (k << 10) | (coef_re[k] & 0x3ff)); dib7000p_write_word(state, 144, coef_im[k] & 0x3ff); dib7000p_write_word(state, 143, (1 << 14) | (k << 10) | (coef_re[k] & 0x3ff)); } - dib7000p_write_word(state,143 ,0); + dib7000p_write_word(state, 143, 0); } static int dib7000p_tune(struct dvb_frontend *demod, struct dvb_frontend_parameters *ch) @@ -976,11 +1200,11 @@ static int dib7000p_tune(struct dvb_frontend *demod, struct dvb_frontend_paramet /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ tmp = (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3); if (state->sfn_workaround_active) { - dprintk( "SFN workaround is active"); + dprintk("SFN workaround is active"); tmp |= (1 << 9); - dib7000p_write_word(state, 166, 0x4000); // P_pha3_force_pha_shift + dib7000p_write_word(state, 166, 0x4000); // P_pha3_force_pha_shift } else { - dib7000p_write_word(state, 166, 0x0000); // P_pha3_force_pha_shift + dib7000p_write_word(state, 166, 0x0000); // P_pha3_force_pha_shift } dib7000p_write_word(state, 29, tmp); @@ -993,51 +1217,72 @@ static int dib7000p_tune(struct dvb_frontend *demod, struct dvb_frontend_paramet /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ tmp = (6 << 8) | 0x80; switch (ch->u.ofdm.transmission_mode) { - case TRANSMISSION_MODE_2K: tmp |= (7 << 12); break; - case TRANSMISSION_MODE_4K: tmp |= (8 << 12); break; - default: - case TRANSMISSION_MODE_8K: tmp |= (9 << 12); break; + case TRANSMISSION_MODE_2K: + tmp |= (2 << 12); + break; + case TRANSMISSION_MODE_4K: + tmp |= (3 << 12); + break; + default: + case TRANSMISSION_MODE_8K: + tmp |= (4 << 12); + break; } - dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */ + dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ tmp = (0 << 4); switch (ch->u.ofdm.transmission_mode) { - case TRANSMISSION_MODE_2K: tmp |= 0x6; break; - case TRANSMISSION_MODE_4K: tmp |= 0x7; break; - default: - case TRANSMISSION_MODE_8K: tmp |= 0x8; break; + case TRANSMISSION_MODE_2K: + tmp |= 0x6; + break; + case TRANSMISSION_MODE_4K: + tmp |= 0x7; + break; + default: + case TRANSMISSION_MODE_8K: + tmp |= 0x8; + break; } - dib7000p_write_word(state, 32, tmp); + dib7000p_write_word(state, 32, tmp); /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ tmp = (0 << 4); switch (ch->u.ofdm.transmission_mode) { - case TRANSMISSION_MODE_2K: tmp |= 0x6; break; - case TRANSMISSION_MODE_4K: tmp |= 0x7; break; - default: - case TRANSMISSION_MODE_8K: tmp |= 0x8; break; + case TRANSMISSION_MODE_2K: + tmp |= 0x6; + break; + case TRANSMISSION_MODE_4K: + tmp |= 0x7; + break; + default: + case TRANSMISSION_MODE_8K: + tmp |= 0x8; + break; } - dib7000p_write_word(state, 33, tmp); + dib7000p_write_word(state, 33, tmp); - tmp = dib7000p_read_word(state,509); + tmp = dib7000p_read_word(state, 509); if (!((tmp >> 6) & 0x1)) { /* restart the fec */ - tmp = dib7000p_read_word(state,771); + tmp = dib7000p_read_word(state, 771); dib7000p_write_word(state, 771, tmp | (1 << 1)); dib7000p_write_word(state, 771, tmp); - msleep(10); - tmp = dib7000p_read_word(state,509); + msleep(40); + tmp = dib7000p_read_word(state, 509); } - // we achieved a lock - it's time to update the osc freq - if ((tmp >> 6) & 0x1) + if ((tmp >> 6) & 0x1) { dib7000p_update_timf(state); + /* P_timf_alpha += 2 */ + tmp = dib7000p_read_word(state, 26); + dib7000p_write_word(state, 26, (tmp & ~(0xf << 12)) | ((((tmp >> 12) & 0xf) + 5) << 12)); + } if (state->cfg.spur_protect) - dib7000p_spur_protect(state, ch->frequency/1000, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth)); + dib7000p_spur_protect(state, ch->frequency / 1000, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth)); - dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth)); + dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth)); return 0; } @@ -1046,63 +1291,82 @@ static int dib7000p_wakeup(struct dvb_frontend *demod) struct dib7000p_state *state = demod->demodulator_priv; dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); + if (state->version == SOC7090) + dib7000p_sad_calib(state); return 0; } static int dib7000p_sleep(struct dvb_frontend *demod) { struct dib7000p_state *state = demod->demodulator_priv; + if (state->version == SOC7090) + return dib7090_set_output_mode(demod, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); } static int dib7000p_identify(struct dib7000p_state *st) { u16 value; - dprintk( "checking demod on I2C address: %d (%x)", - st->i2c_addr, st->i2c_addr); + dprintk("checking demod on I2C address: %d (%x)", st->i2c_addr, st->i2c_addr); if ((value = dib7000p_read_word(st, 768)) != 0x01b3) { - dprintk( "wrong Vendor ID (read=0x%x)",value); + dprintk("wrong Vendor ID (read=0x%x)", value); return -EREMOTEIO; } if ((value = dib7000p_read_word(st, 769)) != 0x4000) { - dprintk( "wrong Device ID (%x)",value); + dprintk("wrong Device ID (%x)", value); return -EREMOTEIO; } return 0; } - -static int dib7000p_get_frontend(struct dvb_frontend* fe, - struct dvb_frontend_parameters *fep) +static int dib7000p_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dib7000p_state *state = fe->demodulator_priv; - u16 tps = dib7000p_read_word(state,463); + u16 tps = dib7000p_read_word(state, 463); fep->inversion = INVERSION_AUTO; fep->u.ofdm.bandwidth = BANDWIDTH_TO_INDEX(state->current_bandwidth); switch ((tps >> 8) & 0x3) { - case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break; - case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break; + case 0: + fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; + break; /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */ } switch (tps & 0x3) { - case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; - case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break; - case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break; - case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break; + case 0: + fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; + break; } switch ((tps >> 14) & 0x3) { - case 0: fep->u.ofdm.constellation = QPSK; break; - case 1: fep->u.ofdm.constellation = QAM_16; break; - case 2: - default: fep->u.ofdm.constellation = QAM_64; break; + case 0: + fep->u.ofdm.constellation = QPSK; + break; + case 1: + fep->u.ofdm.constellation = QAM_16; + break; + case 2: + default: + fep->u.ofdm.constellation = QAM_64; + break; } /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ @@ -1110,22 +1374,42 @@ static int dib7000p_get_frontend(struct dvb_frontend* fe, fep->u.ofdm.hierarchy_information = HIERARCHY_NONE; switch ((tps >> 5) & 0x7) { - case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break; - case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break; - case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break; - case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break; - case 7: - default: fep->u.ofdm.code_rate_HP = FEC_7_8; break; + case 1: + fep->u.ofdm.code_rate_HP = FEC_1_2; + break; + case 2: + fep->u.ofdm.code_rate_HP = FEC_2_3; + break; + case 3: + fep->u.ofdm.code_rate_HP = FEC_3_4; + break; + case 5: + fep->u.ofdm.code_rate_HP = FEC_5_6; + break; + case 7: + default: + fep->u.ofdm.code_rate_HP = FEC_7_8; + break; } switch ((tps >> 2) & 0x7) { - case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break; - case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break; - case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break; - case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break; - case 7: - default: fep->u.ofdm.code_rate_LP = FEC_7_8; break; + case 1: + fep->u.ofdm.code_rate_LP = FEC_1_2; + break; + case 2: + fep->u.ofdm.code_rate_LP = FEC_2_3; + break; + case 3: + fep->u.ofdm.code_rate_LP = FEC_3_4; + break; + case 5: + fep->u.ofdm.code_rate_LP = FEC_5_6; + break; + case 7: + default: + fep->u.ofdm.code_rate_LP = FEC_7_8; + break; } /* native interleaver: (dib7000p_read_word(state, 464) >> 5) & 0x1 */ @@ -1133,15 +1417,19 @@ static int dib7000p_get_frontend(struct dvb_frontend* fe, return 0; } -static int dib7000p_set_frontend(struct dvb_frontend* fe, - struct dvb_frontend_parameters *fep) +static int dib7000p_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dib7000p_state *state = fe->demodulator_priv; int time, ret; - dib7000p_set_output_mode(state, OUTMODE_HIGH_Z); + if (state->version == SOC7090) { + dib7090_set_diversity_in(fe, 0); + dib7090_set_output_mode(fe, OUTMODE_HIGH_Z); + } + else + dib7000p_set_output_mode(state, OUTMODE_HIGH_Z); - /* maybe the parameter has been changed */ + /* maybe the parameter has been changed */ state->sfn_workaround_active = buggy_sfn_workaround; if (fe->ops.tuner_ops.set_params) @@ -1156,9 +1444,7 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe, } while (time != -1); if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO || - fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || - fep->u.ofdm.constellation == QAM_AUTO || - fep->u.ofdm.code_rate_HP == FEC_AUTO) { + fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || fep->u.ofdm.constellation == QAM_AUTO || fep->u.ofdm.code_rate_HP == FEC_AUTO) { int i = 800, found; dib7000p_autosearch_start(fe, fep); @@ -1167,9 +1453,9 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe, found = dib7000p_autosearch_is_irq(fe); } while (found == 0 && i--); - dprintk("autosearch returns: %d",found); + dprintk("autosearch returns: %d", found); if (found == 0 || found == 1) - return 0; // no channel found + return 0; // no channel found dib7000p_get_frontend(fe, fep); } @@ -1177,11 +1463,15 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe, ret = dib7000p_tune(fe, fep); /* make this a config parameter */ - dib7000p_set_output_mode(state, state->cfg.output_mode); - return ret; + if (state->version == SOC7090) + dib7090_set_output_mode(fe, state->cfg.output_mode); + else + dib7000p_set_output_mode(state, state->cfg.output_mode); + + return ret; } -static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat) +static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat) { struct dib7000p_state *state = fe->demodulator_priv; u16 lock = dib7000p_read_word(state, 509); @@ -1196,27 +1486,27 @@ static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat) *stat |= FE_HAS_VITERBI; if (lock & 0x0010) *stat |= FE_HAS_SYNC; - if ((lock & 0x0038) == 0x38) + if ((lock & 0x0038) == 0x38) *stat |= FE_HAS_LOCK; return 0; } -static int dib7000p_read_ber(struct dvb_frontend *fe, u32 *ber) +static int dib7000p_read_ber(struct dvb_frontend *fe, u32 * ber) { struct dib7000p_state *state = fe->demodulator_priv; *ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501); return 0; } -static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) { struct dib7000p_state *state = fe->demodulator_priv; *unc = dib7000p_read_word(state, 506); return 0; } -static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 * strength) { struct dib7000p_state *state = fe->demodulator_priv; u16 val = dib7000p_read_word(state, 394); @@ -1224,7 +1514,7 @@ static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength) return 0; } -static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr) +static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr) { struct dib7000p_state *state = fe->demodulator_priv; u16 val; @@ -1240,19 +1530,17 @@ static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr) noise_exp -= 0x40; signal_mant = (val >> 6) & 0xFF; - signal_exp = (val & 0x3F); + signal_exp = (val & 0x3F); if ((signal_exp & 0x20) != 0) signal_exp -= 0x40; if (signal_mant != 0) - result = intlog10(2) * 10 * signal_exp + 10 * - intlog10(signal_mant); + result = intlog10(2) * 10 * signal_exp + 10 * intlog10(signal_mant); else result = intlog10(2) * 10 * signal_exp - 100; if (noise_mant != 0) - result -= intlog10(2) * 10 * noise_exp + 10 * - intlog10(noise_mant); + result -= intlog10(2) * 10 * noise_exp + 10 * intlog10(noise_mant); else result -= intlog10(2) * 10 * noise_exp - 100; @@ -1260,7 +1548,7 @@ static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr) return 0; } -static int dib7000p_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +static int dib7000p_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) { tune->min_delay_ms = 1000; return 0; @@ -1270,6 +1558,7 @@ static void dib7000p_release(struct dvb_frontend *demod) { struct dib7000p_state *st = demod->demodulator_priv; dibx000_exit_i2c_master(&st->i2c_master); + i2c_del_adapter(&st->dib7090_tuner_adap); kfree(st); } @@ -1277,8 +1566,8 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap) { u8 tx[2], rx[2]; struct i2c_msg msg[2] = { - { .addr = 18 >> 1, .flags = 0, .buf = tx, .len = 2 }, - { .addr = 18 >> 1, .flags = I2C_M_RD, .buf = rx, .len = 2 }, + {.addr = 18 >> 1,.flags = 0,.buf = tx,.len = 2}, + {.addr = 18 >> 1,.flags = I2C_M_RD,.buf = rx,.len = 2}, }; tx[0] = 0x03; @@ -1303,7 +1592,7 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap) } EXPORT_SYMBOL(dib7000pc_detection); -struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) { struct dib7000p_state *st = demod->demodulator_priv; return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); @@ -1312,19 +1601,19 @@ EXPORT_SYMBOL(dib7000p_get_i2c_master); int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) { - struct dib7000p_state *state = fe->demodulator_priv; - u16 val = dib7000p_read_word(state, 235) & 0xffef; - val |= (onoff & 0x1) << 4; - dprintk("PID filter enabled %d", onoff); - return dib7000p_write_word(state, 235, val); + struct dib7000p_state *state = fe->demodulator_priv; + u16 val = dib7000p_read_word(state, 235) & 0xffef; + val |= (onoff & 0x1) << 4; + dprintk("PID filter enabled %d", onoff); + return dib7000p_write_word(state, 235, val); } EXPORT_SYMBOL(dib7000p_pid_filter_ctrl); int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) { - struct dib7000p_state *state = fe->demodulator_priv; - dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff); - return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0); + struct dib7000p_state *state = fe->demodulator_priv; + dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff); + return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0); } EXPORT_SYMBOL(dib7000p_pid_filter); @@ -1340,16 +1629,19 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau dpst->i2c_adap = i2c; - for (k = no_of_demods-1; k >= 0; k--) { + for (k = no_of_demods - 1; k >= 0; k--) { dpst->cfg = cfg[k]; /* designated i2c address */ - new_addr = (0x40 + k) << 1; + if (cfg[k].default_i2c_addr != 0) + new_addr = cfg[k].default_i2c_addr + (k << 1); + else + new_addr = (0x40 + k) << 1; dpst->i2c_addr = new_addr; - dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ + dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ if (dib7000p_identify(dpst) != 0) { dpst->i2c_addr = default_addr; - dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ + dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ if (dib7000p_identify(dpst) != 0) { dprintk("DiB7000P #%d: not identified\n", k); kfree(dpst); @@ -1368,7 +1660,10 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau for (k = 0; k < no_of_demods; k++) { dpst->cfg = cfg[k]; - dpst->i2c_addr = (0x40 + k) << 1; + if (cfg[k].default_i2c_addr != 0) + dpst->i2c_addr = (cfg[k].default_i2c_addr + k) << 1; + else + dpst->i2c_addr = (0x40 + k) << 1; // unforce divstr dib7000p_write_word(dpst, 1285, dpst->i2c_addr << 2); @@ -1382,8 +1677,616 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau } EXPORT_SYMBOL(dib7000p_i2c_enumeration); +static const s32 lut_1000ln_mant[] = { + 6908, 6956, 7003, 7047, 7090, 7131, 7170, 7208, 7244, 7279, 7313, 7346, 7377, 7408, 7438, 7467, 7495, 7523, 7549, 7575, 7600 +}; + +static s32 dib7000p_get_adc_power(struct dvb_frontend *fe) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u32 tmp_val = 0, exp = 0, mant = 0; + s32 pow_i; + u16 buf[2]; + u8 ix = 0; + + buf[0] = dib7000p_read_word(state, 0x184); + buf[1] = dib7000p_read_word(state, 0x185); + pow_i = (buf[0] << 16) | buf[1]; + dprintk("raw pow_i = %d", pow_i); + + tmp_val = pow_i; + while (tmp_val >>= 1) + exp++; + + mant = (pow_i * 1000 / (1 << exp)); + dprintk(" mant = %d exp = %d", mant / 1000, exp); + + ix = (u8) ((mant - 1000) / 100); /* index of the LUT */ + dprintk(" ix = %d", ix); + + pow_i = (lut_1000ln_mant[ix] + 693 * (exp - 20) - 6908); + pow_i = (pow_i << 8) / 1000; + dprintk(" pow_i = %d", pow_i); + + return pow_i; +} + +static int map_addr_to_serpar_number(struct i2c_msg *msg) +{ + if ((msg->buf[0] <= 15)) + msg->buf[0] -= 1; + else if (msg->buf[0] == 17) + msg->buf[0] = 15; + else if (msg->buf[0] == 16) + msg->buf[0] = 17; + else if (msg->buf[0] == 19) + msg->buf[0] = 16; + else if (msg->buf[0] >= 21 && msg->buf[0] <= 25) + msg->buf[0] -= 3; + else if (msg->buf[0] == 28) + msg->buf[0] = 23; + else { + return -EINVAL; + } + return 0; +} + +static int w7090p_tuner_write_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); + u8 n_overflow = 1; + u16 i = 1000; + u16 serpar_num = msg[0].buf[0]; + + while (n_overflow == 1 && i) { + n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1; + i--; + if (i == 0) + dprintk("Tuner ITF: write busy (overflow)"); + } + dib7000p_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f)); + dib7000p_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]); + + return num; +} + +static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); + u8 n_overflow = 1, n_empty = 1; + u16 i = 1000; + u16 serpar_num = msg[0].buf[0]; + u16 read_word; + + while (n_overflow == 1 && i) { + n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1; + i--; + if (i == 0) + dprintk("TunerITF: read busy (overflow)"); + } + dib7000p_write_word(state, 1985, (0 << 6) | (serpar_num & 0x3f)); + + i = 1000; + while (n_empty == 1 && i) { + n_empty = dib7000p_read_word(state, 1984) & 0x1; + i--; + if (i == 0) + dprintk("TunerITF: read busy (empty)"); + } + read_word = dib7000p_read_word(state, 1987); + msg[1].buf[0] = (read_word >> 8) & 0xff; + msg[1].buf[1] = (read_word) & 0xff; + + return num; +} + +static int w7090p_tuner_rw_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + if (map_addr_to_serpar_number(&msg[0]) == 0) { /* else = Tuner regs to ignore : DIG_CFG, CTRL_RF_LT, PLL_CFG, PWM1_REG, ADCCLK, DIG_CFG_3; SLEEP_EN... */ + if (num == 1) { /* write */ + return w7090p_tuner_write_serpar(i2c_adap, msg, 1); + } else { /* read */ + return w7090p_tuner_read_serpar(i2c_adap, msg, 2); + } + } + return num; +} + +int dib7090p_rw_on_apb(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num, u16 apb_address) +{ + struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); + u16 word; + + if (num == 1) { /* write */ + dib7000p_write_word(state, apb_address, ((msg[0].buf[1] << 8) | (msg[0].buf[2]))); + } else { + word = dib7000p_read_word(state, apb_address); + msg[1].buf[0] = (word >> 8) & 0xff; + msg[1].buf[1] = (word) & 0xff; + } + + return num; +} + +static int dib7090_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); + + u16 apb_address = 0, word; + int i = 0; + switch (msg[0].buf[0]) { + case 0x12: + apb_address = 1920; + break; + case 0x14: + apb_address = 1921; + break; + case 0x24: + apb_address = 1922; + break; + case 0x1a: + apb_address = 1923; + break; + case 0x22: + apb_address = 1924; + break; + case 0x33: + apb_address = 1926; + break; + case 0x34: + apb_address = 1927; + break; + case 0x35: + apb_address = 1928; + break; + case 0x36: + apb_address = 1929; + break; + case 0x37: + apb_address = 1930; + break; + case 0x38: + apb_address = 1931; + break; + case 0x39: + apb_address = 1932; + break; + case 0x2a: + apb_address = 1935; + break; + case 0x2b: + apb_address = 1936; + break; + case 0x2c: + apb_address = 1937; + break; + case 0x2d: + apb_address = 1938; + break; + case 0x2e: + apb_address = 1939; + break; + case 0x2f: + apb_address = 1940; + break; + case 0x30: + apb_address = 1941; + break; + case 0x31: + apb_address = 1942; + break; + case 0x32: + apb_address = 1943; + break; + case 0x3e: + apb_address = 1944; + break; + case 0x3f: + apb_address = 1945; + break; + case 0x40: + apb_address = 1948; + break; + case 0x25: + apb_address = 914; + break; + case 0x26: + apb_address = 915; + break; + case 0x27: + apb_address = 916; + break; + case 0x28: + apb_address = 917; + break; + case 0x1d: + i = ((dib7000p_read_word(state, 72) >> 12) & 0x3); + word = dib7000p_read_word(state, 384 + i); + msg[1].buf[0] = (word >> 8) & 0xff; + msg[1].buf[1] = (word) & 0xff; + return num; + case 0x1f: + if (num == 1) { /* write */ + word = (u16) ((msg[0].buf[1] << 8) | msg[0].buf[2]); + word &= 0x3; + word = (dib7000p_read_word(state, 72) & ~(3 << 12)) | (word << 12); //Mask bit 12,13 + dib7000p_write_word(state, 72, word); /* Set the proper input */ + return num; + } + } + + if (apb_address != 0) /* R/W acces via APB */ + return dib7090p_rw_on_apb(i2c_adap, msg, num, apb_address); + else /* R/W access via SERPAR */ + return w7090p_tuner_rw_serpar(i2c_adap, msg, num); + + return 0; +} + +static u32 dib7000p_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dib7090_tuner_xfer_algo = { + .master_xfer = dib7090_tuner_xfer, + .functionality = dib7000p_i2c_func, +}; + +struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) +{ + struct dib7000p_state *st = fe->demodulator_priv; + return &st->dib7090_tuner_adap; +} +EXPORT_SYMBOL(dib7090_get_i2c_tuner); + +static int dib7090_host_bus_drive(struct dib7000p_state *state, u8 drive) +{ + u16 reg; + + /* drive host bus 2, 3, 4 */ + reg = dib7000p_read_word(state, 1798) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); + reg |= (drive << 12) | (drive << 6) | drive; + dib7000p_write_word(state, 1798, reg); + + /* drive host bus 5,6 */ + reg = dib7000p_read_word(state, 1799) & ~((0x7 << 2) | (0x7 << 8)); + reg |= (drive << 8) | (drive << 2); + dib7000p_write_word(state, 1799, reg); + + /* drive host bus 7, 8, 9 */ + reg = dib7000p_read_word(state, 1800) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); + reg |= (drive << 12) | (drive << 6) | drive; + dib7000p_write_word(state, 1800, reg); + + /* drive host bus 10, 11 */ + reg = dib7000p_read_word(state, 1801) & ~((0x7 << 2) | (0x7 << 8)); + reg |= (drive << 8) | (drive << 2); + dib7000p_write_word(state, 1801, reg); + + /* drive host bus 12, 13, 14 */ + reg = dib7000p_read_word(state, 1802) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); + reg |= (drive << 12) | (drive << 6) | drive; + dib7000p_write_word(state, 1802, reg); + + return 0; +} + +static u32 dib7090_calcSyncFreq(u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 syncSize) +{ + u32 quantif = 3; + u32 nom = (insertExtSynchro * P_Kin + syncSize); + u32 denom = P_Kout; + u32 syncFreq = ((nom << quantif) / denom); + + if ((syncFreq & ((1 << quantif) - 1)) != 0) + syncFreq = (syncFreq >> quantif) + 1; + else + syncFreq = (syncFreq >> quantif); + + if (syncFreq != 0) + syncFreq = syncFreq - 1; + + return syncFreq; +} + +static int dib7090_cfg_DibTx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 synchroMode, u32 syncWord, u32 syncSize) +{ + u8 index_buf; + u16 rx_copy_buf[22]; + + dprintk("Configure DibStream Tx"); + for (index_buf = 0; index_buf<22; index_buf++) + rx_copy_buf[index_buf] = dib7000p_read_word(state, 1536+index_buf); + + dib7000p_write_word(state, 1615, 1); + dib7000p_write_word(state, 1603, P_Kin); + dib7000p_write_word(state, 1605, P_Kout); + dib7000p_write_word(state, 1606, insertExtSynchro); + dib7000p_write_word(state, 1608, synchroMode); + dib7000p_write_word(state, 1609, (syncWord >> 16) & 0xffff); + dib7000p_write_word(state, 1610, syncWord & 0xffff); + dib7000p_write_word(state, 1612, syncSize); + dib7000p_write_word(state, 1615, 0); + + for (index_buf = 0; index_buf<22; index_buf++) + dib7000p_write_word(state, 1536+index_buf, rx_copy_buf[index_buf]); + + return 0; +} + +static int dib7090_cfg_DibRx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 synchroMode, u32 insertExtSynchro, u32 syncWord, u32 syncSize, + u32 dataOutRate) +{ + u32 syncFreq; + + dprintk("Configure DibStream Rx"); + if ((P_Kin != 0) && (P_Kout != 0)) + { + syncFreq = dib7090_calcSyncFreq(P_Kin, P_Kout, insertExtSynchro, syncSize); + dib7000p_write_word(state, 1542, syncFreq); + } + dib7000p_write_word(state, 1554, 1); + dib7000p_write_word(state, 1536, P_Kin); + dib7000p_write_word(state, 1537, P_Kout); + dib7000p_write_word(state, 1539, synchroMode); + dib7000p_write_word(state, 1540, (syncWord >> 16) & 0xffff); + dib7000p_write_word(state, 1541, syncWord & 0xffff); + dib7000p_write_word(state, 1543, syncSize); + dib7000p_write_word(state, 1544, dataOutRate); + dib7000p_write_word(state, 1554, 0); + + return 0; +} + +static int dib7090_enDivOnHostBus(struct dib7000p_state *state) +{ + u16 reg; + + dprintk("Enable Diversity on host bus"); + reg = (1 << 8) | (1 << 5); // P_enDivOutOnDibTx = 1 ; P_enDibTxOnHostBus = 1 + dib7000p_write_word(state, 1288, reg); + + return dib7090_cfg_DibTx(state, 5, 5, 0, 0, 0, 0); +} + +static int dib7090_enAdcOnHostBus(struct dib7000p_state *state) +{ + u16 reg; + + dprintk("Enable ADC on host bus"); + reg = (1 << 7) | (1 << 5); //P_enAdcOnDibTx = 1 ; P_enDibTxOnHostBus = 1 + dib7000p_write_word(state, 1288, reg); + + return dib7090_cfg_DibTx(state, 20, 5, 10, 0, 0, 0); +} + +static int dib7090_enMpegOnHostBus(struct dib7000p_state *state) +{ + u16 reg; + + dprintk("Enable Mpeg on host bus"); + reg = (1 << 9) | (1 << 5); //P_enMpegOnDibTx = 1 ; P_enDibTxOnHostBus = 1 + dib7000p_write_word(state, 1288, reg); + + return dib7090_cfg_DibTx(state, 8, 5, 0, 0, 0, 0); +} + +static int dib7090_enMpegInput(struct dib7000p_state *state) +{ + dprintk("Enable Mpeg input"); + return dib7090_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); /*outputRate = 8 */ +} + +static int dib7090_enMpegMux(struct dib7000p_state *state, u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2) +{ + u16 reg = (1 << 7) | ((pulseWidth & 0x1f) << 2) | ((enSerialMode & 0x1) << 1) | (enSerialClkDiv2 & 0x1); + + dprintk("Enable Mpeg mux"); + dib7000p_write_word(state, 1287, reg); + + reg &= ~(1 << 7); // P_restart_mpegMux = 0 + dib7000p_write_word(state, 1287, reg); + + reg = (1 << 4); //P_enMpegMuxOnHostBus = 1 + dib7000p_write_word(state, 1288, reg); + + return 0; +} + +static int dib7090_disableMpegMux(struct dib7000p_state *state) +{ + u16 reg; + + dprintk("Disable Mpeg mux"); + dib7000p_write_word(state, 1288, 0); //P_enMpegMuxOnHostBus = 0 + + reg = dib7000p_read_word(state, 1287); + reg &= ~(1 << 7); // P_restart_mpegMux = 0 + dib7000p_write_word(state, 1287, reg); + + return 0; +} + +static int dib7090_set_input_mode(struct dvb_frontend *fe, int mode) +{ + struct dib7000p_state *state = fe->demodulator_priv; + + switch(mode) { + case INPUT_MODE_DIVERSITY: + dprintk("Enable diversity INPUT"); + dib7090_cfg_DibRx(state, 5,5,0,0,0,0,0); + break; + case INPUT_MODE_MPEG: + dprintk("Enable Mpeg INPUT"); + dib7090_cfg_DibRx(state, 8,5,0,0,0,8,0); /*outputRate = 8 */ + break; + case INPUT_MODE_OFF: + default: + dprintk("Disable INPUT"); + dib7090_cfg_DibRx(state, 0,0,0,0,0,0,0); + break; + } + return 0; +} + +static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff) +{ + switch (onoff) { + case 0: /* only use the internal way - not the diversity input */ + dib7090_set_input_mode(fe, INPUT_MODE_MPEG); + break; + case 1: /* both ways */ + case 2: /* only the diversity input */ + dib7090_set_input_mode(fe, INPUT_MODE_DIVERSITY); + break; + } + + return 0; +} + +static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) +{ + struct dib7000p_state *state = fe->demodulator_priv; + + u16 outreg, smo_mode, fifo_threshold; + u8 prefer_mpeg_mux_use = 1; + int ret = 0; + + dib7090_host_bus_drive(state, 1); + + fifo_threshold = 1792; + smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); + outreg = dib7000p_read_word(state, 1286) & ~((1 << 10) | (0x7 << 6) | (1 << 1)); + + switch (mode) { + case OUTMODE_HIGH_Z: + outreg = 0; + break; + + case OUTMODE_MPEG2_SERIAL: + if (prefer_mpeg_mux_use) { + dprintk("Sip 7090P setting output mode TS_SERIAL using Mpeg Mux"); + dib7090_enMpegOnHostBus(state); + dib7090_enMpegInput(state); + if (state->cfg.enMpegOutput == 1) + dib7090_enMpegMux(state, 3, 1, 1); + + } else { /* Use Smooth block */ + dprintk("Sip 7090P setting output mode TS_SERIAL using Smooth bloc"); + dib7090_disableMpegMux(state); + dib7000p_write_word(state, 1288, (1 << 6)); //P_enDemOutInterfOnHostBus = 1 + outreg |= (2 << 6) | (0 << 1); + } + break; + + case OUTMODE_MPEG2_PAR_GATED_CLK: + if (prefer_mpeg_mux_use) { + dprintk("Sip 7090P setting output mode TS_PARALLEL_GATED using Mpeg Mux"); + dib7090_enMpegOnHostBus(state); + dib7090_enMpegInput(state); + if (state->cfg.enMpegOutput == 1) + dib7090_enMpegMux(state, 2, 0, 0); + } else { /* Use Smooth block */ + dprintk("Sip 7090P setting output mode TS_PARALLEL_GATED using Smooth block"); + dib7090_disableMpegMux(state); + dib7000p_write_word(state, 1288, (1 << 6)); //P_enDemOutInterfOnHostBus = 1 + outreg |= (0 << 6); + } + break; + + case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */ + dprintk("Sip 7090P setting output mode TS_PARALLEL_CONT using Smooth block"); + dib7090_disableMpegMux(state); + dib7000p_write_word(state, 1288, (1 << 6)); //P_enDemOutInterfOnHostBus = 1 + outreg |= (1 << 6); + break; + + case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux bloc */ + dprintk("Sip 7090P setting output mode TS_FIFO using Smooth block"); + dib7090_disableMpegMux(state); + dib7000p_write_word(state, 1288, (1 << 6)); //P_enDemOutInterfOnHostBus = 1 + outreg |= (5 << 6); + smo_mode |= (3 << 1); + fifo_threshold = 512; + break; + + case OUTMODE_DIVERSITY: + dprintk("Sip 7090P setting output mode MODE_DIVERSITY"); + dib7090_disableMpegMux(state); + dib7090_enDivOnHostBus(state); + break; + + case OUTMODE_ANALOG_ADC: + dprintk("Sip 7090P setting output mode MODE_ANALOG_ADC"); + dib7090_enAdcOnHostBus(state); + break; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5); + + ret |= dib7000p_write_word(state, 235, smo_mode); + ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ + ret |= dib7000p_write_word(state, 1286, outreg | (1 << 10)); /* allways set Dout active = 1 !!! */ + + return ret; +} + +int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 en_cur_state; + + dprintk("sleep dib7090: %d", onoff); + + en_cur_state = dib7000p_read_word(state, 1922); + + if (en_cur_state > 0xff) { //LNAs and MIX are ON and therefore it is a valid configuration + state->tuner_enable = en_cur_state; + } + + if (onoff) + en_cur_state &= 0x00ff; //Mask to be applied + else { + if (state->tuner_enable != 0) + en_cur_state = state->tuner_enable; + } + + dib7000p_write_word(state, 1922, en_cur_state); + + return 0; +} +EXPORT_SYMBOL(dib7090_tuner_sleep); + +int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart) +{ + dprintk("AGC restart callback: %d", restart); + return 0; +} +EXPORT_SYMBOL(dib7090_agc_restart); + +int dib7090_get_adc_power(struct dvb_frontend *fe) +{ + return dib7000p_get_adc_power(fe); +} +EXPORT_SYMBOL(dib7090_get_adc_power); + +int dib7090_slave_reset(struct dvb_frontend *fe) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 reg; + + reg = dib7000p_read_word(state, 1794); + dib7000p_write_word(state, 1794, reg | (4 << 12)); + + dib7000p_write_word(state, 1032, 0xffff); + return 0; +} +EXPORT_SYMBOL(dib7090_slave_reset); + static struct dvb_frontend_ops dib7000p_ops; -struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) +struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) { struct dvb_frontend *demod; struct dib7000p_state *st; @@ -1400,31 +2303,44 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, /* Ensure the output mode remains at the previous default if it's * not specifically set by the caller. */ - if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && - (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) + if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) st->cfg.output_mode = OUTMODE_MPEG2_FIFO; - demod = &st->demod; + demod = &st->demod; demod->demodulator_priv = st; memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); - dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */ + dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */ if (dib7000p_identify(st) != 0) goto error; + st->version = dib7000p_read_word(st, 897); + /* FIXME: make sure the dev.parent field is initialized, or else - request_firmware() will hit an OOPS (this should be moved somewhere - more common) */ - st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; + request_firmware() will hit an OOPS (this should be moved somewhere + more common) */ dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr); + /* init 7090 tuner adapter */ + strncpy(st->dib7090_tuner_adap.name, "DiB7090 tuner interface", sizeof(st->dib7090_tuner_adap.name)); + st->dib7090_tuner_adap.algo = &dib7090_tuner_xfer_algo; + st->dib7090_tuner_adap.algo_data = NULL; + st->dib7090_tuner_adap.dev.parent = st->i2c_adap->dev.parent; + i2c_set_adapdata(&st->dib7090_tuner_adap, st); + i2c_add_adapter(&st->dib7090_tuner_adap); + dib7000p_demod_reset(st); + if (st->version == SOC7090) { + dib7090_set_output_mode(demod, st->cfg.output_mode); + dib7090_set_diversity_in(demod, 0); + } + return demod; -error: + error: kfree(st); return NULL; } @@ -1432,37 +2348,35 @@ EXPORT_SYMBOL(dib7000p_attach); static struct dvb_frontend_ops dib7000p_ops = { .info = { - .name = "DiBcom 7000PC", - .type = FE_OFDM, - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, - .caps = FE_CAN_INVERSION_AUTO | - 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_RECOVER | - FE_CAN_HIERARCHY_AUTO, - }, - - .release = dib7000p_release, - - .init = dib7000p_wakeup, - .sleep = dib7000p_sleep, - - .set_frontend = dib7000p_set_frontend, - .get_tune_settings = dib7000p_fe_get_tune_settings, - .get_frontend = dib7000p_get_frontend, - - .read_status = dib7000p_read_status, - .read_ber = dib7000p_read_ber, + .name = "DiBcom 7000PC", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + 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_RECOVER | FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib7000p_release, + + .init = dib7000p_wakeup, + .sleep = dib7000p_sleep, + + .set_frontend = dib7000p_set_frontend, + .get_tune_settings = dib7000p_fe_get_tune_settings, + .get_frontend = dib7000p_get_frontend, + + .read_status = dib7000p_read_status, + .read_ber = dib7000p_read_ber, .read_signal_strength = dib7000p_read_signal_strength, - .read_snr = dib7000p_read_snr, - .read_ucblocks = dib7000p_read_unc_blocks, + .read_snr = dib7000p_read_snr, + .read_ucblocks = dib7000p_read_unc_blocks, }; +MODULE_AUTHOR("Olivier Grenie "); MODULE_AUTHOR("Patrick Boettcher "); MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h index da17345..4e3ffc8 100644 --- a/drivers/media/dvb/frontends/dib7000p.h +++ b/drivers/media/dvb/frontends/dib7000p.h @@ -33,59 +33,54 @@ struct dib7000p_config { int (*agc_control) (struct dvb_frontend *, u8 before); u8 output_mode; - u8 disable_sample_and_hold : 1; + u8 disable_sample_and_hold:1; - u8 enable_current_mirror : 1; - u8 diversity_delay; + u8 enable_current_mirror:1; + u16 diversity_delay; + u8 default_i2c_addr; + u8 enMpegOutput : 1; }; #define DEFAULT_DIB7000P_I2C_ADDRESS 18 #if defined(CONFIG_DVB_DIB7000P) || (defined(CONFIG_DVB_DIB7000P_MODULE) && \ - defined(MODULE)) -extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, - u8 i2c_addr, - struct dib7000p_config *cfg); -extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *, - enum dibx000_i2c_interface, - int); -extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, - int no_of_demods, u8 default_addr, - struct dib7000p_config cfg[]); + defined(MODULE)) +extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); +extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); +extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value); extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); extern int dib7000p_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); extern int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); +extern int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw); +extern u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf); +extern int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart); +extern int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff); +extern int dib7090_get_adc_power(struct dvb_frontend *fe); +extern struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe); +extern int dib7090_slave_reset(struct dvb_frontend *fe); #else -static inline -struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, - struct dib7000p_config *cfg) +static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -static inline -struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe, - enum dibx000_i2c_interface i, - int x) +static inline struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -static inline int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, - int no_of_demods, u8 default_addr, - struct dib7000p_config cfg[]) +static inline int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } -static inline int dib7000p_set_gpio(struct dvb_frontend *fe, - u8 num, u8 dir, u8 val) +static inline int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; @@ -102,16 +97,59 @@ static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap) printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } + static inline int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; } static inline int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, uint8_t onoff) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +static inline int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib7090_get_adc_power(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int dib7090_slave_reset(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; } #endif diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h index c4fd62f..cc0fafe 100644 --- a/drivers/media/dvb/frontends/dibx000_common.h +++ b/drivers/media/dvb/frontends/dibx000_common.h @@ -141,8 +141,8 @@ enum dibx000_adc_states { }; #define BANDWIDTH_TO_KHZ(v) ( (v) == BANDWIDTH_8_MHZ ? 8000 : \ - (v) == BANDWIDTH_7_MHZ ? 7000 : \ - (v) == BANDWIDTH_6_MHZ ? 6000 : 8000 ) + (v) == BANDWIDTH_7_MHZ ? 7000 : \ + (v) == BANDWIDTH_6_MHZ ? 6000 : 8000 ) #define BANDWIDTH_TO_INDEX(v) ( \ (v) == 8000 ? BANDWIDTH_8_MHZ : \ @@ -158,53 +158,57 @@ enum dibx000_adc_states { #define OUTMODE_MPEG2_FIFO 5 #define OUTMODE_ANALOG_ADC 6 +#define INPUT_MODE_OFF 0x11 +#define INPUT_MODE_DIVERSITY 0x12 +#define INPUT_MODE_MPEG 0x13 + enum frontend_tune_state { - CT_TUNER_START = 10, - CT_TUNER_STEP_0, - CT_TUNER_STEP_1, - CT_TUNER_STEP_2, - CT_TUNER_STEP_3, - CT_TUNER_STEP_4, - CT_TUNER_STEP_5, - CT_TUNER_STEP_6, - CT_TUNER_STEP_7, - CT_TUNER_STOP, - - CT_AGC_START = 20, - CT_AGC_STEP_0, - CT_AGC_STEP_1, - CT_AGC_STEP_2, - CT_AGC_STEP_3, - CT_AGC_STEP_4, - CT_AGC_STOP, + CT_TUNER_START = 10, + CT_TUNER_STEP_0, + CT_TUNER_STEP_1, + CT_TUNER_STEP_2, + CT_TUNER_STEP_3, + CT_TUNER_STEP_4, + CT_TUNER_STEP_5, + CT_TUNER_STEP_6, + CT_TUNER_STEP_7, + CT_TUNER_STOP, + + CT_AGC_START = 20, + CT_AGC_STEP_0, + CT_AGC_STEP_1, + CT_AGC_STEP_2, + CT_AGC_STEP_3, + CT_AGC_STEP_4, + CT_AGC_STOP, CT_DEMOD_START = 30, - CT_DEMOD_STEP_1, - CT_DEMOD_STEP_2, - CT_DEMOD_STEP_3, - CT_DEMOD_STEP_4, - CT_DEMOD_STEP_5, - CT_DEMOD_STEP_6, - CT_DEMOD_STEP_7, - CT_DEMOD_STEP_8, - CT_DEMOD_STEP_9, - CT_DEMOD_STEP_10, - CT_DEMOD_SEARCH_NEXT = 41, - CT_DEMOD_STEP_LOCKED, - CT_DEMOD_STOP, - - CT_DONE = 100, - CT_SHUTDOWN, + CT_DEMOD_STEP_1, + CT_DEMOD_STEP_2, + CT_DEMOD_STEP_3, + CT_DEMOD_STEP_4, + CT_DEMOD_STEP_5, + CT_DEMOD_STEP_6, + CT_DEMOD_STEP_7, + CT_DEMOD_STEP_8, + CT_DEMOD_STEP_9, + CT_DEMOD_STEP_10, + CT_DEMOD_SEARCH_NEXT = 41, + CT_DEMOD_STEP_LOCKED, + CT_DEMOD_STOP, + + CT_DONE = 100, + CT_SHUTDOWN, }; struct dvb_frontend_parametersContext { #define CHANNEL_STATUS_PARAMETERS_UNKNOWN 0x01 #define CHANNEL_STATUS_PARAMETERS_SET 0x02 - u8 status; - u32 tune_time_estimation[2]; - s32 tps_available; - u16 tps[9]; + u8 status; + u32 tune_time_estimation[2]; + s32 tps_available; + u16 tps[9]; }; #define FE_STATUS_TUNE_FAILED 0 -- cgit v0.10.2 From be9bae10ffa5aeeef051e893c3b15a5d10eb657d Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Tue, 4 Jan 2011 05:42:19 -0300 Subject: [media] DiB0700: add support for several board-layouts This patchs adds support for DiBcom's NIM8096MD, NIM9090M, NIM9090MD, NIM7090, TFE7090PVR (no diversity) reference designs. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index ebda77d..9ab5713 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -186,7 +186,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, msg[i].len, USB_CTRL_GET_TIMEOUT); if (result < 0) { - err("i2c read error (status = %d)\n", result); + deb_info("i2c read error (status = %d)\n", result); break; } @@ -215,7 +215,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, 0, 0, buf, msg[i].len + 4, USB_CTRL_GET_TIMEOUT); if (result < 0) { - err("i2c write error (status = %d)\n", result); + deb_info("i2c write error (status = %d)\n", result); break; } } diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 193cdb7..2b91903 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -12,6 +12,7 @@ #include "dib7000m.h" #include "dib7000p.h" #include "dib8000.h" +#include "dib9000.h" #include "mt2060.h" #include "mt2266.h" #include "tuner-xc2028.h" @@ -29,6 +30,7 @@ MODULE_PARM_DESC(force_lna_activation, "force the activation of Low-Noise-Amplif struct dib0700_adapter_state { int (*set_param_save) (struct dvb_frontend *, struct dvb_frontend_parameters *); + const struct firmware *frontend_firmware; }; /* Hauppauge Nova-T 500 (aka Bristol) @@ -1243,13 +1245,13 @@ static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) static int stk80xx_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) { - return dib8000_pid_filter(adapter->fe, index, pid, onoff); + return dib8000_pid_filter(adapter->fe, index, pid, onoff); } static int stk80xx_pid_filter_ctrl(struct dvb_usb_adapter *adapter, - int onoff) + int onoff) { - return dib8000_pid_filter_ctrl(adapter->fe, onoff); + return dib8000_pid_filter_ctrl(adapter->fe, onoff); } /* STK807x */ @@ -1321,11 +1323,11 @@ static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) /* STK8096GP */ struct dibx000_agc_config dib8090_agc_config[2] = { - { + { BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, - * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, - * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), @@ -1362,12 +1364,12 @@ struct dibx000_agc_config dib8090_agc_config[2] = { 51, 0, - }, - { + }, + { BAND_CBAND, /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, - * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, - * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), @@ -1404,159 +1406,1098 @@ struct dibx000_agc_config dib8090_agc_config[2] = { 51, 0, - } + } }; static struct dibx000_bandwidth_config dib8090_pll_config_12mhz = { - 54000, 13500, - 1, 18, 3, 1, 0, - 0, 0, 1, 1, 2, - (3 << 14) | (1 << 12) | (599 << 0), - (0 << 25) | 0, - 20199727, - 12000000, + 54000, 13500, + 1, 18, 3, 1, 0, + 0, 0, 1, 1, 2, + (3 << 14) | (1 << 12) | (599 << 0), + (0 << 25) | 0, + 20199727, + 12000000, }; static int dib8090_get_adc_power(struct dvb_frontend *fe) { - return dib8000_get_adc_power(fe, 1); + return dib8000_get_adc_power(fe, 1); +} + +static struct dib8000_config dib809x_dib8000_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib8090_agc_config, + .agc_control = dib0090_dcc_freq, + .pll = &dib8090_pll_config_12mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .div_cfg = 0x31, + .output_mode = OUTMODE_MPEG2_FIFO, + .drives = 0x2d98, + .diversity_delay = 48, + .refclksel = 3, + },{ + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 2, + .agc = dib8090_agc_config, + .agc_control = dib0090_dcc_freq, + .pll = &dib8090_pll_config_12mhz, + .tuner_is_baseband = 1, + + .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, + .div_cfg = 0x31, + .output_mode = OUTMODE_DIVERSITY, + .drives = 0x2d08, + .diversity_delay = 1, + .refclksel = 3, + } +}; + +static struct dib0090_wbd_slope dib8090_wbd_table[] = { + /* max freq ; cold slope ; cold offset ; warm slope ; warm offset ; wbd gain */ + { 120, 0, 500, 0, 500, 4 }, /* CBAND */ + { 170, 0, 450, 0, 450, 4 }, /* CBAND */ + { 380, 48, 373, 28, 259, 6 }, /* VHF */ + { 860, 34, 700, 36, 616, 6 }, /* high UHF */ + { 0xFFFF, 34, 700, 36, 616, 6 }, /* default */ +}; + +static struct dib0090_config dib809x_dib0090_config = { + .io.pll_bypass = 1, + .io.pll_range = 1, + .io.pll_prediv = 1, + .io.pll_loopdiv = 20, + .io.adc_clock_ratio = 8, + .io.pll_int_loop_filt = 0, + .io.clock_khz = 12000, + .reset = dib80xx_tuner_reset, + .sleep = dib80xx_tuner_sleep, + .clkouttobamse = 1, + .analog_output = 1, + .i2c_address = DEFAULT_DIB0090_I2C_ADDRESS, + .use_pwm_agc = 1, + .clkoutdrive = 1, + .get_adc_power = dib8090_get_adc_power, + .freq_offset_khz_uhf = -63, + .freq_offset_khz_vhf = -143, + .wbd = dib8090_wbd_table, + .fref_clock_ratio = 6, +}; + +static int dib8096_set_param_override(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + u8 band = BAND_OF_FREQUENCY(fep->frequency/1000); + u16 target; + int ret = 0; + enum frontend_tune_state tune_state = CT_SHUTDOWN; + u16 ltgain, rf_gain_limit; + + ret = state->set_param_save(fe, fep); + if (ret < 0) + return ret; + + target = (dib0090_get_wbd_offset(fe) * 8 * 18 / 33 + 1) / 2; + dib8000_set_wbd_ref(fe, target); + + + if (band == BAND_CBAND) { + deb_info("tuning in CBAND - soft-AGC startup\n"); + /* TODO specific wbd target for dib0090 - needed for startup ? */ + dib0090_set_tune_state(fe, CT_AGC_START); + do { + ret = dib0090_gain_control(fe); + msleep(ret); + tune_state = dib0090_get_tune_state(fe); + if (tune_state == CT_AGC_STEP_0) + dib8000_set_gpio(fe, 6, 0, 1); + else if (tune_state == CT_AGC_STEP_1) { + dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, <gain); + if (rf_gain_limit == 0) + dib8000_set_gpio(fe, 6, 0, 0); + } + } while (tune_state < CT_AGC_STOP); + dib0090_pwm_gain_reset(fe); + dib8000_pwm_agc_reset(fe); + dib8000_set_tune_state(fe, CT_DEMOD_START); + } else { + deb_info("not tuning in CBAND - standard AGC startup\n"); + dib0090_pwm_gain_reset(fe); + } + + return 0; +} + +static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + + if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL) + return -ENODEV; + + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override; + return 0; +} + +static int stk809x_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80); + + adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + + return adap->fe == NULL ? -ENODEV : 0; +} + +static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c; + struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe, 1); + + if (fe_slave) { + tun_i2c = dib8000_get_i2c_master(fe_slave, DIBX000_I2C_INTERFACE_TUNER, 1); + if (dvb_attach(dib0090_register, fe_slave, tun_i2c, &dib809x_dib0090_config) == NULL) + return -ENODEV; + fe_slave->dvb = adap->fe->dvb; + fe_slave->ops.tuner_ops.set_params = dib8096_set_param_override; + } + tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL) + return -ENODEV; + + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override; + + return 0; +} + +static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_frontend *fe_slave; + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(1000); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80); + + adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + if (adap->fe == NULL) + return -ENODEV; + + fe_slave = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]); + dib8000_set_slave_frontend(adap->fe, fe_slave); + + return fe_slave == NULL ? -ENODEV : 0; +} + +/* STK9090M */ +static int dib90x0_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) +{ + return dib9000_fw_pid_filter(adapter->fe, index, pid, onoff); +} + +static int dib90x0_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +{ + return dib9000_fw_pid_filter_ctrl(adapter->fe, onoff); +} + +static int dib90x0_tuner_reset(struct dvb_frontend *fe, int onoff) +{ + return dib9000_set_gpio(fe, 5, 0, !onoff); +} + +static int dib90x0_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + return dib9000_set_gpio(fe, 0, 0, onoff); +} + +static int dib01x0_pmu_update(struct i2c_adapter *i2c, u16 *data, u8 len) +{ + u8 wb[4] = { 0xc >> 8, 0xc & 0xff, 0, 0 }; + u8 rb[2]; + struct i2c_msg msg[2] = { + {.addr = 0x1e >> 1,.flags = 0,.buf = wb,.len = 2}, + {.addr = 0x1e >> 1,.flags = I2C_M_RD,.buf = rb,.len = 2}, + }; + u8 index_data; + + dibx000_i2c_set_speed(i2c, 250); + + if (i2c_transfer(i2c, msg, 2) != 2) + return -EIO; + + switch (rb[0] << 8 | rb[1]) { + case 0: + deb_info("Found DiB0170 rev1: This version of DiB0170 is not supported any longer.\n"); + return -EIO; + case 1: + deb_info("Found DiB0170 rev2"); + break; + case 2: + deb_info("Found DiB0190 rev2"); + break; + default: + deb_info("DiB01x0 not found"); + return -EIO; + } + + for (index_data = 0; index_data < len; index_data += 2) { + wb[2] = (data[index_data + 1] >> 8) & 0xff; + wb[3] = (data[index_data + 1]) & 0xff; + + if (data[index_data] == 0) { + wb[0] = (data[index_data] >> 8) & 0xff; + wb[1] = (data[index_data]) & 0xff; + msg[0].len = 2; + if (i2c_transfer(i2c, msg, 2) != 2) + return -EIO; + wb[2] |= rb[0]; + wb[3] |= rb[1] & ~(3 << 4); + } + + wb[0] = (data[index_data ] >> 8)&0xff; + wb[1] = (data[index_data ] )&0xff; + msg[0].len = 4; + if (i2c_transfer(i2c, &msg[0], 1) != 1) + return -EIO; + } + return 0; +} + +static struct dib9000_config stk9090m_config = { + .output_mpeg2_in_188_bytes = 1, + .output_mode = OUTMODE_MPEG2_FIFO, + .vcxo_timer = 279620, + .timing_frequency = 20452225, + .demod_clock_khz = 60000, + .xtal_clock_khz = 30000, + .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0), + .subband = { + 2, + { + { 240, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0008, 0x0000, 0x0008 } }, /* GPIO 3 to 1 for VHF */ + { 890, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0008, 0x0000, 0x0000 } }, /* GPIO 3 to 0 for UHF */ + { 0 }, + }, + }, + .gpio_function = { + { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_ON, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = (0x10 & ~0x1) | 0x20 }, + { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_OFF, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = 0 | 0x21 }, + }, +}; + +static struct dib9000_config nim9090md_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + .output_mode = OUTMODE_MPEG2_FIFO, + .vcxo_timer = 279620, + .timing_frequency = 20452225, + .demod_clock_khz = 60000, + .xtal_clock_khz = 30000, + .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0), + }, { + .output_mpeg2_in_188_bytes = 1, + .output_mode = OUTMODE_DIVERSITY, + .vcxo_timer = 279620, + .timing_frequency = 20452225, + .demod_clock_khz = 60000, + .xtal_clock_khz = 30000, + .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0), + .subband = { + 2, + { + { 240, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0006, 0x0000, 0x0006 } }, /* GPIO 1 and 2 to 1 for VHF */ + { 890, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0006, 0x0000, 0x0000 } }, /* GPIO 1 and 2 to 0 for UHF */ + { 0 }, + }, + }, + .gpio_function = { + { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_ON, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = (0x10 & ~0x1) | 0x20 }, + { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_OFF, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = 0 | 0x21 }, + }, + } +}; + +static struct dib0090_config dib9090_dib0090_config = { + .io.pll_bypass = 0, + .io.pll_range = 1, + .io.pll_prediv = 1, + .io.pll_loopdiv = 8, + .io.adc_clock_ratio = 8, + .io.pll_int_loop_filt = 0, + .io.clock_khz = 30000, + .reset = dib90x0_tuner_reset, + .sleep = dib90x0_tuner_sleep, + .clkouttobamse = 0, + .analog_output = 0, + .use_pwm_agc = 0, + .clkoutdrive = 0, + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, +}; + +static struct dib0090_config nim9090md_dib0090_config[2] = { + { + .io.pll_bypass = 0, + .io.pll_range = 1, + .io.pll_prediv = 1, + .io.pll_loopdiv = 8, + .io.adc_clock_ratio = 8, + .io.pll_int_loop_filt = 0, + .io.clock_khz = 30000, + .reset = dib90x0_tuner_reset, + .sleep = dib90x0_tuner_sleep, + .clkouttobamse = 1, + .analog_output = 0, + .use_pwm_agc = 0, + .clkoutdrive = 0, + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, + },{ + .io.pll_bypass = 0, + .io.pll_range = 1, + .io.pll_prediv = 1, + .io.pll_loopdiv = 8, + .io.adc_clock_ratio = 8, + .io.pll_int_loop_filt = 0, + .io.clock_khz = 30000, + .reset = dib90x0_tuner_reset, + .sleep = dib90x0_tuner_sleep, + .clkouttobamse = 0, + .analog_output = 0, + .use_pwm_agc = 0, + .clkoutdrive = 0, + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, + } +}; + + +static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *state = adap->priv; + struct dib0700_state *st = adap->dev->priv; + u32 fw_version; + + /* Make use of the new i2c functions from FW 1.20 */ + dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL); + if (fw_version >= 0x10200) + st->fw_use_new_i2c_api = 1; + dib0700_set_i2c_speed(adap->dev, 340); + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80); + + if (request_firmware(&state->frontend_firmware, "dib9090.fw", &adap->dev->udev->dev)) { + deb_info("%s: Upload failed. (file not found?)\n", __func__); + return -ENODEV; + } else { + deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size); + } + stk9090m_config.microcode_B_fe_size = state->frontend_firmware->size; + stk9090m_config.microcode_B_fe_buffer = state->frontend_firmware->data; + + adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config); + + return adap->fe == NULL ? -ENODEV : 0; +} + +static int dib9090_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *state = adap->priv; + struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe); + u16 data_dib190[10] = { + 1, 0x1374, + 2, 0x01a2, + 7, 0x0020, + 0, 0x00ef, + 8, 0x0486, + }; + + if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &dib9090_dib0090_config) == NULL) + return -ENODEV; + i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); + if (dib01x0_pmu_update(i2c, data_dib190, 10) != 0) + return -ENODEV; + dib0700_set_i2c_speed(adap->dev, 2000); + if (dib9000_firmware_post_pll_init(adap->fe) < 0) + return -ENODEV; + release_firmware(state->frontend_firmware); + return 0; +} + +static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *state = adap->priv; + struct dib0700_state *st = adap->dev->priv; + struct i2c_adapter *i2c; + struct dvb_frontend *fe_slave; + u32 fw_version; + + /* Make use of the new i2c functions from FW 1.20 */ + dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL); + if (fw_version >= 0x10200) + st->fw_use_new_i2c_api = 1; + dib0700_set_i2c_speed(adap->dev, 340); + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + dib0700_ctrl_clock(adap->dev, 72, 1); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + if (request_firmware(&state->frontend_firmware, "dib9090.fw", &adap->dev->udev->dev)) { + deb_info("%s: Upload failed. (file not found?)\n", __func__); + return -EIO; + } else { + deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size); + } + nim9090md_config[0].microcode_B_fe_size = state->frontend_firmware->size; + nim9090md_config[0].microcode_B_fe_buffer = state->frontend_firmware->data; + nim9090md_config[1].microcode_B_fe_size = state->frontend_firmware->size; + nim9090md_config[1].microcode_B_fe_buffer = state->frontend_firmware->data; + + dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, 0x80); + adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]); + + if (adap->fe == NULL) + return -ENODEV; + + i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0); + dib9000_i2c_enumeration(i2c, 1, 0x12, 0x82); + + fe_slave = dvb_attach(dib9000_attach, i2c, 0x82, &nim9090md_config[1]); + dib9000_set_slave_frontend(adap->fe, fe_slave); + + return fe_slave == NULL ? -ENODEV : 0; } -static struct dib8000_config dib809x_dib8000_config = { - .output_mpeg2_in_188_bytes = 1, - - .agc_config_count = 2, - .agc = dib8090_agc_config, - .agc_control = dib0090_dcc_freq, - .pll = &dib8090_pll_config_12mhz, - .tuner_is_baseband = 1, - - .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, - .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, - .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, - - .hostbus_diversity = 1, - .div_cfg = 0x31, - .output_mode = OUTMODE_MPEG2_FIFO, - .drives = 0x2d98, - .diversity_delay = 144, - .refclksel = 3, +static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *state = adap->priv; + struct i2c_adapter *i2c; + struct dvb_frontend *fe_slave; + u16 data_dib190[10] = { + 1, 0x5374, + 2, 0x01ae, + 7, 0x0020, + 0, 0x00ef, + 8, 0x0406, + }; + i2c = dib9000_get_tuner_interface(adap->fe); + if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &nim9090md_dib0090_config[0]) == NULL) + return -ENODEV; + i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); + if (dib01x0_pmu_update(i2c, data_dib190, 10) < 0) + return -ENODEV; + dib0700_set_i2c_speed(adap->dev, 2000); + if (dib9000_firmware_post_pll_init(adap->fe) < 0) + return -ENODEV; + + fe_slave = dib9000_get_slave_frontend(adap->fe, 1); + if (fe_slave != NULL) { + i2c = dib9000_get_component_bus_interface(adap->fe); + dib9000_set_i2c_adapter(fe_slave, i2c); + + i2c = dib9000_get_tuner_interface(fe_slave); + if (dvb_attach(dib0090_fw_register, fe_slave, i2c, &nim9090md_dib0090_config[1]) == NULL) + return -ENODEV; + fe_slave->dvb = adap->fe->dvb; + dib9000_fw_set_component_bus_speed(adap->fe, 2000); + if (dib9000_firmware_post_pll_init(fe_slave) < 0) + return -ENODEV; + } + release_firmware(state->frontend_firmware); + + return 0; +} + +/* NIM7090 */ +struct dib7090p_best_adc { + u32 timf; + u32 pll_loopdiv; + u32 pll_prediv; +}; + +static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dib7090p_best_adc *adc) +{ + u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1; + + u16 xtal = 12000; + u32 fcp_min = 1900; /* PLL Minimum Frequency comparator KHz */ + u32 fcp_max = 20000; /* PLL Maximum Frequency comparator KHz */ + u32 fdem_max = 76000; + u32 fdem_min = 69500; + u32 fcp = 0, fs = 0, fdem = 0; + u32 harmonic_id = 0; + + adc->pll_loopdiv = loopdiv; + adc->pll_prediv = prediv; + adc->timf = 0; + + deb_info("bandwidth = %d fdem_min =%d", fe->dtv_property_cache.bandwidth_hz, fdem_min); + + /* Find Min and Max prediv */ + while((xtal/max_prediv) >= fcp_min) + max_prediv++; + + max_prediv--; + min_prediv = max_prediv; + while((xtal/min_prediv) <= fcp_max) { + min_prediv--; + if(min_prediv == 1) + break; + } + deb_info("MIN prediv = %d : MAX prediv = %d", min_prediv, max_prediv); + + min_prediv = 2; + + for(prediv = min_prediv ; prediv < max_prediv; prediv ++) { + fcp = xtal / prediv; + if(fcp > fcp_min && fcp < fcp_max) { + for(loopdiv = 1 ; loopdiv < 64 ; loopdiv++) { + fdem = ((xtal/prediv) * loopdiv); + fs = fdem / 4; + /* test min/max system restrictions */ + + if((fdem >= fdem_min) && (fdem <= fdem_max) && (fs >= fe->dtv_property_cache.bandwidth_hz/1000)) { + spur = 0; + /* test fs harmonics positions */ + for(harmonic_id = (fe->dtv_property_cache.frequency / (1000*fs)) ; harmonic_id <= ((fe->dtv_property_cache.frequency / (1000*fs))+1) ; harmonic_id++) { + if(((fs*harmonic_id) >= ((fe->dtv_property_cache.frequency/1000) - (fe->dtv_property_cache.bandwidth_hz/2000))) && ((fs*harmonic_id) <= ((fe->dtv_property_cache.frequency/1000) + (fe->dtv_property_cache.bandwidth_hz/2000)))) { + spur = 1; + break; + } + } + + if(!spur) { + adc->pll_loopdiv = loopdiv; + adc->pll_prediv = prediv; + adc->timf = 2396745143UL/fdem*(1<<9); + adc->timf+= ((2396745143UL%fdem)<< 9)/fdem; + deb_info("loopdiv=%i prediv=%i timf=%i", loopdiv, prediv, adc->timf); + break; + } + } + } + } + if (!spur) + break; + } + + + if(adc->pll_loopdiv == 0 && adc->pll_prediv == 0) { + return -EINVAL; + } else + return 0; +} + +static int dib7090_agc_startup(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + struct dibx000_bandwidth_config pll; + u16 target; + struct dib7090p_best_adc adc; + int ret; + + ret = state->set_param_save(fe, fep); + if (ret < 0) + return ret; + + memset(&pll, 0, sizeof(struct dibx000_bandwidth_config)); + dib0090_pwm_gain_reset(fe); + target = (dib0090_get_wbd_offset(fe) * 8 + 1) / 2; + dib7000p_set_wbd_ref(fe, target); + + if(dib7090p_get_best_sampling(fe, &adc) == 0) { + pll.pll_ratio = adc.pll_loopdiv; + pll.pll_prediv = adc.pll_prediv; + + dib7000p_update_pll(fe, &pll); + dib7000p_ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf); + } + return 0; +} + +static struct dib0090_wbd_slope dib7090_wbd_table[] = { + { 380, 81, 850, 64, 540 ,4}, + { 860, 51, 866, 21, 375 ,4}, + {1700, 0, 250, 0, 100, 6}, //LBAND Predefinition , NOT tested Yet + {2600, 0, 250, 0, 100, 6}, //SBAND Predefinition , NOT tested Yet + { 0xFFFF, 0, 0, 0, 0 ,0}, }; -static struct dib0090_config dib809x_dib0090_config = { - .io.pll_bypass = 1, - .io.pll_range = 1, - .io.pll_prediv = 1, - .io.pll_loopdiv = 20, - .io.adc_clock_ratio = 8, - .io.pll_int_loop_filt = 0, - .io.clock_khz = 12000, - .reset = dib80xx_tuner_reset, - .sleep = dib80xx_tuner_sleep, - .clkouttobamse = 1, - .analog_output = 1, - .i2c_address = DEFAULT_DIB0090_I2C_ADDRESS, - .wbd_vhf_offset = 100, - .wbd_cband_offset = 450, - .use_pwm_agc = 1, - .clkoutdrive = 1, - .get_adc_power = dib8090_get_adc_power, - .freq_offset_khz_uhf = 0, - .freq_offset_khz_vhf = -143, -}; +struct dibx000_agc_config dib7090_agc_config[2] = { + { + .band_caps = BAND_UHF, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup + + .inv_gain = 687,// inv_gain = 1/ 95.4dB // no boost, lower gain due to ramp quantification + .time_stabiliz = 10, // time_stabiliz + + .alpha_level = 0, // alpha_level + .thlock = 118, // thlock + + .wbd_inv = 0, // wbd_inv + .wbd_ref = 1200, // wbd_ref + .wbd_sel = 3, // wbd_sel + .wbd_alpha = 5, // wbd_alpha + + .agc1_max = 65535, // agc1_max + .agc1_min = 0, // agc1_min + + .agc2_max = 65535, // agc2_max + .agc2_min = 0, // agc2_min + + .agc1_pt1 = 0, // agc1_pt1 + .agc1_pt2 = 32, // agc1_pt2 + .agc1_pt3 = 114, // agc1_pt3 // 40.4dB + .agc1_slope1 = 143, // agc1_slope1 + .agc1_slope2 = 144, // agc1_slope2 + .agc2_pt1 = 114, // agc2_pt1 + .agc2_pt2 = 227, // agc2_pt2 + .agc2_slope1 = 116, // agc2_slope1 + .agc2_slope2 = 117, // agc2_slope2 + + .alpha_mant = 18, // alpha_mant // 5Hz with 95.4dB + .alpha_exp = 0, // alpha_exp + .beta_mant = 20, // beta_mant + .beta_exp = 59, // beta_exp + + .perform_agc_softsplit = 0, // perform_agc_softsplit + } , { + .band_caps = BAND_FM | BAND_VHF | BAND_CBAND, + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup + + .inv_gain = 732,// inv_gain = 1/ 89.5dB // no boost, lower gain due to ramp quantification + .time_stabiliz = 10, // time_stabiliz + + .alpha_level = 0, // alpha_level + .thlock = 118, // thlock + + .wbd_inv = 0, // wbd_inv + .wbd_ref = 1200, // wbd_ref + .wbd_sel = 3, // wbd_sel + .wbd_alpha = 5, // wbd_alpha + + .agc1_max = 65535, // agc1_max : 1 + .agc1_min = 0, // agc1_min + + .agc2_max = 65535, // agc2_max + .agc2_min = 0, // agc2_min + + .agc1_pt1 = 0, // agc1_pt1 + .agc1_pt2 = 0, // agc1_pt2 + .agc1_pt3 = 98, // agc1_pt3 // 34.5dB CBAND P1G + 55dB BB boost = 89.5dB + .agc1_slope1 = 0, // agc1_slope1 + .agc1_slope2 = 167, // agc1_slope2 = Dy/Dx * 2**6 * 2**8 = 1/98 * 2**6 *2**8 : Dy = 1 + .agc1_pt1 = 98, // agc2_pt1 + .agc2_pt2 = 255, // agc2_pt2 + .agc2_slope1 = 104, // agc2_slope1 = Dy/Dx * 2**6 * 2**8 = 1/(255-98) * 2**6 *2**8 + .agc2_slope2 = 0, // agc2_slope2 + + .alpha_mant = 18, // alpha_mant // 5Hz with 95.4dB + .alpha_exp = 0, // alpha_exp + .beta_mant = 20, // beta_mant + .beta_exp = 59, // beta_exp + + .perform_agc_softsplit = 0, // perform_agc_softsplit + } +}; + +static struct dibx000_bandwidth_config dib7090_clock_config_12_mhz = { + 60000, 15000, // internal, sampling + 1, 5, 0, 0, 0, // pll_cfg: prediv, ratio, range, reset, bypass + 0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo + (3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k + (0 << 25) | 0, // ifreq = 0.000000 MHz + 20452225, // timf + 15000000, // xtal_hz +}; + +static struct dib7000p_config nim7090_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + .update_lna = NULL, + + .agc_config_count = 2, + .agc = dib7090_agc_config, + + .bw = &dib7090_clock_config_12_mhz, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + + .agc_control = dib7090_agc_restart, + + .spur_protect = 0, + .disable_sample_and_hold = 0, + .enable_current_mirror = 0, + .diversity_delay = 0, + + .output_mode = OUTMODE_MPEG2_FIFO, + .enMpegOutput = 1, +}; + +static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { + { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + .update_lna = NULL, + + .agc_config_count = 2, + .agc = dib7090_agc_config, + + .bw = &dib7090_clock_config_12_mhz, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + + .agc_control = dib7090_agc_restart, + + .spur_protect = 0, + .disable_sample_and_hold = 0, + .enable_current_mirror = 0, + .diversity_delay = 0, + + .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, + .default_i2c_addr = 0x90, + .enMpegOutput = 1, + },{ + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + .update_lna = NULL, + + .agc_config_count = 2, + .agc = dib7090_agc_config, + + .bw = &dib7090_clock_config_12_mhz, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + + .agc_control = dib7090_agc_restart, + + .spur_protect = 0, + .disable_sample_and_hold = 0, + .enable_current_mirror = 0, + .diversity_delay = 0, + + .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, + .default_i2c_addr = 0x92, + .enMpegOutput = 0, + } +}; + +static const struct dib0090_config nim7090_dib0090_config = { + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib7090_tuner_sleep, + .sleep = dib7090_tuner_sleep, + + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, + + .get_adc_power = dib7090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 0, + + .wbd = dib7090_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, +}; + +static const struct dib0090_config tfe7090pvr_dib0090_config[2] = { + { + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib7090_tuner_sleep, + .sleep = dib7090_tuner_sleep, + + .freq_offset_khz_uhf = 50, + .freq_offset_khz_vhf = 70, + + .get_adc_power = dib7090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 0, + + .wbd = dib7090_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, + },{ + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib7090_tuner_sleep, + .sleep = dib7090_tuner_sleep, + + .freq_offset_khz_uhf = -50, + .freq_offset_khz_vhf = -70, + + .get_adc_power = dib7090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 0, + + .wbd = dib7090_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, + } +}; + +static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); -static int dib8096_set_param_override(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep) -{ - struct dvb_usb_adapter *adap = fe->dvb->priv; - struct dib0700_adapter_state *state = adap->priv; - u8 band = BAND_OF_FREQUENCY(fep->frequency/1000); - u16 offset; - int ret = 0; - enum frontend_tune_state tune_state = CT_SHUTDOWN; - u16 ltgain, rf_gain_limit; - - ret = state->set_param_save(fe, fep); - if (ret < 0) - return ret; - - switch (band) { - case BAND_VHF: - offset = 100; - break; - case BAND_UHF: - offset = 550; - break; - default: - offset = 0; - break; - } - offset += (dib0090_get_wbd_offset(fe) * 8 * 18 / 33 + 1) / 2; - dib8000_set_wbd_ref(fe, offset); - - - if (band == BAND_CBAND) { - deb_info("tuning in CBAND - soft-AGC startup\n"); - /* TODO specific wbd target for dib0090 - needed for startup ? */ - dib0090_set_tune_state(fe, CT_AGC_START); - do { - ret = dib0090_gain_control(fe); - msleep(ret); - tune_state = dib0090_get_tune_state(fe); - if (tune_state == CT_AGC_STEP_0) - dib8000_set_gpio(fe, 6, 0, 1); - else if (tune_state == CT_AGC_STEP_1) { - dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, <gain); - if (rf_gain_limit == 0) - dib8000_set_gpio(fe, 6, 0, 0); - } - } while (tune_state < CT_AGC_STOP); - dib0090_pwm_gain_reset(fe); - dib8000_pwm_agc_reset(fe); - dib8000_set_tune_state(fe, CT_DEMOD_START); - } else { - deb_info("not tuning in CBAND - standard AGC startup\n"); - dib0090_pwm_gain_reset(fe); - } + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, &nim7090_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + return -ENODEV; + } + adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); - return 0; + return adap->fe == NULL ? -ENODEV : 0; } -static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) +static int nim7090_tuner_attach(struct dvb_usb_adapter *adap) { - struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe); + + if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &nim7090_dib0090_config) == NULL) + return -ENODEV; - if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL) - return -ENODEV; + dib7000p_set_gpio(adap->fe, 8, 0, 1); - st->set_param_save = adap->fe->ops.tuner_ops.set_params; - adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override; - return 0; + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup; + return 0; } -static int stk809x_frontend_attach(struct dvb_usb_adapter *adap) +static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) { + struct dib0700_state *st = adap->dev->priv; + + /* The TFE7090 requires the dib0700 to not be in master mode */ + st->disable_streaming_master_mode = 1; + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); - dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); - dib0700_ctrl_clock(adap->dev, 72, 1); - msleep(10); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80); + /* initialize IC 0 */ + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, &tfe7090pvr_dib7000p_config[0]) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + return -ENODEV; + } - adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config); + dib0700_set_i2c_speed(adap->dev, 340); + adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); - return adap->fe == NULL ? -ENODEV : 0; + dib7090_slave_reset(adap->fe); + + if (adap->fe == NULL) + return -ENODEV; + + return 0; +} + +static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap) +{ + struct i2c_adapter *i2c; + + if (adap->dev->adapter[0].fe == NULL) { + err("the master dib7090 has to be initialized first"); + return -ENODEV; /* the master device has not been initialized */ + } + + i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1); + if (dib7000p_i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + return -ENODEV; + } + + adap->fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]); + dib0700_set_i2c_speed(adap->dev, 200); + + return adap->fe == NULL ? -ENODEV : 0; +} + +static int tfe7090pvr_tuner0_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe); + + if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL) + return -ENODEV; + + dib7000p_set_gpio(adap->fe, 8, 0, 1); + + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup; + return 0; +} + +static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe); + + if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL) + return -ENODEV; + + dib7000p_set_gpio(adap->fe, 8, 0, 1); + + st->set_param_save = adap->fe->ops.tuner_ops.set_params; + adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup; + return 0; } /* STK7070PD */ @@ -1856,6 +2797,11 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK8096GP) }, { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DIVERSITY) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM9090M) }, +/* 70 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM8096MD) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM9090MD) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM7090) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090PVR) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -2619,6 +3565,205 @@ struct dvb_usb_device_properties dib0700_devices[] = { RC_TYPE_NEC, .change_protocol = dib0700_change_protocol, }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = dib90x0_pid_filter, + .pid_filter_ctrl = dib90x0_pid_filter_ctrl, + .frontend_attach = stk9090m_frontend_attach, + .tuner_attach = dib9090_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom STK9090M reference design", + { &dib0700_usb_id_table[69], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk80xx_pid_filter, + .pid_filter_ctrl = stk80xx_pid_filter_ctrl, + .frontend_attach = nim8096md_frontend_attach, + .tuner_attach = nim8096md_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom NIM8096MD reference design", + { &dib0700_usb_id_table[70], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = dib90x0_pid_filter, + .pid_filter_ctrl = dib90x0_pid_filter_ctrl, + .frontend_attach = nim9090md_frontend_attach, + .tuner_attach = nim9090md_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom NIM9090MD reference design", + { &dib0700_usb_id_table[71], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = nim7090_frontend_attach, + .tuner_attach = nim7090_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom NIM7090 reference design", + { &dib0700_usb_id_table[72], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 2, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = tfe7090pvr_frontend0_attach, + .tuner_attach = tfe7090pvr_tuner0_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = tfe7090pvr_frontend1_attach, + .tuner_attach = tfe7090pvr_tuner1_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom TFE7090PVR reference design", + { &dib0700_usb_id_table[73], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, }, }; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index d0bce04..b71540d 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -106,8 +106,13 @@ #define USB_PID_DIBCOM_STK807XP 0x1f90 #define USB_PID_DIBCOM_STK807XPVR 0x1f98 #define USB_PID_DIBCOM_STK8096GP 0x1fa0 +#define USB_PID_DIBCOM_NIM8096MD 0x1fa8 #define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 #define USB_PID_DIBCOM_STK7770P 0x1e80 +#define USB_PID_DIBCOM_NIM7090 0x1bb2 +#define USB_PID_DIBCOM_TFE7090PVR 0x1bb4 +#define USB_PID_DIBCOM_NIM9090M 0x2383 +#define USB_PID_DIBCOM_NIM9090MD 0x2384 #define USB_PID_DPOSH_M9206_COLD 0x9206 #define USB_PID_DPOSH_M9206_WARM 0xa090 #define USB_PID_E3C_EC168 0x1689 -- cgit v0.10.2 From b4d6046e841955be9cc49164b03b91c9524f9c2e Mon Sep 17 00:00:00 2001 From: Olivier Grenie Date: Tue, 4 Jan 2011 13:08:14 -0300 Subject: [media] DiBxxxx: Codingstype updates This patchs fix several conding-style violations. Signed-off-by: Olivier Grenie Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 9ab5713..b79af68 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -347,7 +347,8 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz) b[6] = (u8) (divider >> 8); b[7] = (u8) (divider & 0xff); - deb_info("setting I2C speed: %04x %04x %04x (%d kHz).", (b[2] << 8) | (b[3]), (b[4] << 8) | b[5], (b[6] << 8) | b[7], scl_kHz); + deb_info("setting I2C speed: %04x %04x %04x (%d kHz).", + (b[2] << 8) | (b[3]), (b[4] << 8) | b[5], (b[6] << 8) | b[7], scl_kHz); return dib0700_ctrl_wr(d, b, 8); } @@ -484,14 +485,14 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id); st->channel_state &= ~0x3; - if ((adap->stream.props.endpoint != 2) && (adap->stream.props.endpoint != 3)) { + if ((adap->stream.props.endpoint != 2) + && (adap->stream.props.endpoint != 3)) { deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->stream.props.endpoint); if (onoff) st->channel_state |= 1 << (adap->id); else st->channel_state |= 1 << ~(adap->id); - } - else { + } else { if (onoff) st->channel_state |= 1 << (adap->stream.props.endpoint-2); else diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 2b91903..c25b428 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1444,7 +1444,7 @@ static struct dib8000_config dib809x_dib8000_config[2] = { .drives = 0x2d98, .diversity_delay = 48, .refclksel = 3, - },{ + }, { .output_mpeg2_in_188_bytes = 1, .agc_config_count = 2, @@ -1517,27 +1517,26 @@ static int dib8096_set_param_override(struct dvb_frontend *fe, if (band == BAND_CBAND) { - deb_info("tuning in CBAND - soft-AGC startup\n"); - /* TODO specific wbd target for dib0090 - needed for startup ? */ - dib0090_set_tune_state(fe, CT_AGC_START); - do { - ret = dib0090_gain_control(fe); - msleep(ret); - tune_state = dib0090_get_tune_state(fe); - if (tune_state == CT_AGC_STEP_0) - dib8000_set_gpio(fe, 6, 0, 1); - else if (tune_state == CT_AGC_STEP_1) { - dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, <gain); - if (rf_gain_limit == 0) - dib8000_set_gpio(fe, 6, 0, 0); - } - } while (tune_state < CT_AGC_STOP); - dib0090_pwm_gain_reset(fe); - dib8000_pwm_agc_reset(fe); - dib8000_set_tune_state(fe, CT_DEMOD_START); + deb_info("tuning in CBAND - soft-AGC startup\n"); + dib0090_set_tune_state(fe, CT_AGC_START); + do { + ret = dib0090_gain_control(fe); + msleep(ret); + tune_state = dib0090_get_tune_state(fe); + if (tune_state == CT_AGC_STEP_0) + dib8000_set_gpio(fe, 6, 0, 1); + else if (tune_state == CT_AGC_STEP_1) { + dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, <gain); + if (rf_gain_limit == 0) + dib8000_set_gpio(fe, 6, 0, 0); + } + } while (tune_state < CT_AGC_STOP); + dib0090_pwm_gain_reset(fe); + dib8000_pwm_agc_reset(fe); + dib8000_set_tune_state(fe, CT_DEMOD_START); } else { - deb_info("not tuning in CBAND - standard AGC startup\n"); - dib0090_pwm_gain_reset(fe); + deb_info("not tuning in CBAND - standard AGC startup\n"); + dib0090_pwm_gain_reset(fe); } return 0; @@ -1608,7 +1607,7 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) struct dvb_frontend *fe_slave; dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(1000); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); @@ -1619,9 +1618,9 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) dib0700_ctrl_clock(adap->dev, 72, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80); @@ -1662,8 +1661,8 @@ static int dib01x0_pmu_update(struct i2c_adapter *i2c, u16 *data, u8 len) u8 wb[4] = { 0xc >> 8, 0xc & 0xff, 0, 0 }; u8 rb[2]; struct i2c_msg msg[2] = { - {.addr = 0x1e >> 1,.flags = 0,.buf = wb,.len = 2}, - {.addr = 0x1e >> 1,.flags = I2C_M_RD,.buf = rb,.len = 2}, + {.addr = 0x1e >> 1, .flags = 0, .buf = wb, .len = 2}, + {.addr = 0x1e >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2}, }; u8 index_data; @@ -1673,19 +1672,19 @@ static int dib01x0_pmu_update(struct i2c_adapter *i2c, u16 *data, u8 len) return -EIO; switch (rb[0] << 8 | rb[1]) { - case 0: + case 0: deb_info("Found DiB0170 rev1: This version of DiB0170 is not supported any longer.\n"); return -EIO; - case 1: + case 1: deb_info("Found DiB0170 rev2"); break; - case 2: + case 2: deb_info("Found DiB0190 rev2"); break; - default: + default: deb_info("DiB01x0 not found"); return -EIO; - } + } for (index_data = 0; index_data < len; index_data += 2) { wb[2] = (data[index_data + 1] >> 8) & 0xff; @@ -1701,8 +1700,8 @@ static int dib01x0_pmu_update(struct i2c_adapter *i2c, u16 *data, u8 len) wb[3] |= rb[1] & ~(3 << 4); } - wb[0] = (data[index_data ] >> 8)&0xff; - wb[1] = (data[index_data ] )&0xff; + wb[0] = (data[index_data] >> 8)&0xff; + wb[1] = (data[index_data])&0xff; msg[0].len = 4; if (i2c_transfer(i2c, &msg[0], 1) != 1) return -EIO; @@ -1799,7 +1798,7 @@ static struct dib0090_config nim9090md_dib0090_config[2] = { .clkoutdrive = 0, .freq_offset_khz_uhf = 0, .freq_offset_khz_vhf = 0, - },{ + }, { .io.pll_bypass = 0, .io.pll_range = 1, .io.pll_prediv = 1, @@ -1832,7 +1831,7 @@ static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap) dib0700_set_i2c_speed(adap->dev, 340); dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); @@ -1840,9 +1839,9 @@ static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap) dib0700_ctrl_clock(adap->dev, 72, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80); @@ -1881,7 +1880,7 @@ static int dib9090_tuner_attach(struct dvb_usb_adapter *adap) dib0700_set_i2c_speed(adap->dev, 2000); if (dib9000_firmware_post_pll_init(adap->fe) < 0) return -ENODEV; - release_firmware(state->frontend_firmware); + release_firmware(state->frontend_firmware); return 0; } @@ -1900,7 +1899,7 @@ static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap) dib0700_set_i2c_speed(adap->dev, 340); dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); @@ -1908,9 +1907,9 @@ static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap) dib0700_ctrl_clock(adap->dev, 72, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); if (request_firmware(&state->frontend_firmware, "dib9090.fw", &adap->dev->udev->dev)) { @@ -1974,7 +1973,7 @@ static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap) if (dib9000_firmware_post_pll_init(fe_slave) < 0) return -ENODEV; } - release_firmware(state->frontend_firmware); + release_firmware(state->frontend_firmware); return 0; } @@ -2005,43 +2004,43 @@ static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dib7090p_ deb_info("bandwidth = %d fdem_min =%d", fe->dtv_property_cache.bandwidth_hz, fdem_min); /* Find Min and Max prediv */ - while((xtal/max_prediv) >= fcp_min) + while ((xtal/max_prediv) >= fcp_min) max_prediv++; max_prediv--; min_prediv = max_prediv; - while((xtal/min_prediv) <= fcp_max) { + while ((xtal/min_prediv) <= fcp_max) { min_prediv--; - if(min_prediv == 1) + if (min_prediv == 1) break; } deb_info("MIN prediv = %d : MAX prediv = %d", min_prediv, max_prediv); min_prediv = 2; - for(prediv = min_prediv ; prediv < max_prediv; prediv ++) { + for (prediv = min_prediv ; prediv < max_prediv; prediv++) { fcp = xtal / prediv; - if(fcp > fcp_min && fcp < fcp_max) { - for(loopdiv = 1 ; loopdiv < 64 ; loopdiv++) { + if (fcp > fcp_min && fcp < fcp_max) { + for (loopdiv = 1 ; loopdiv < 64 ; loopdiv++) { fdem = ((xtal/prediv) * loopdiv); fs = fdem / 4; /* test min/max system restrictions */ - if((fdem >= fdem_min) && (fdem <= fdem_max) && (fs >= fe->dtv_property_cache.bandwidth_hz/1000)) { + if ((fdem >= fdem_min) && (fdem <= fdem_max) && (fs >= fe->dtv_property_cache.bandwidth_hz/1000)) { spur = 0; /* test fs harmonics positions */ - for(harmonic_id = (fe->dtv_property_cache.frequency / (1000*fs)) ; harmonic_id <= ((fe->dtv_property_cache.frequency / (1000*fs))+1) ; harmonic_id++) { - if(((fs*harmonic_id) >= ((fe->dtv_property_cache.frequency/1000) - (fe->dtv_property_cache.bandwidth_hz/2000))) && ((fs*harmonic_id) <= ((fe->dtv_property_cache.frequency/1000) + (fe->dtv_property_cache.bandwidth_hz/2000)))) { + for (harmonic_id = (fe->dtv_property_cache.frequency / (1000*fs)) ; harmonic_id <= ((fe->dtv_property_cache.frequency / (1000*fs))+1) ; harmonic_id++) { + if (((fs*harmonic_id) >= ((fe->dtv_property_cache.frequency/1000) - (fe->dtv_property_cache.bandwidth_hz/2000))) && ((fs*harmonic_id) <= ((fe->dtv_property_cache.frequency/1000) + (fe->dtv_property_cache.bandwidth_hz/2000)))) { spur = 1; break; } } - if(!spur) { + if (!spur) { adc->pll_loopdiv = loopdiv; adc->pll_prediv = prediv; - adc->timf = 2396745143UL/fdem*(1<<9); - adc->timf+= ((2396745143UL%fdem)<< 9)/fdem; + adc->timf = 2396745143UL/fdem*(1 << 9); + adc->timf += ((2396745143UL%fdem) << 9)/fdem; deb_info("loopdiv=%i prediv=%i timf=%i", loopdiv, prediv, adc->timf); break; } @@ -2053,9 +2052,9 @@ static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dib7090p_ } - if(adc->pll_loopdiv == 0 && adc->pll_prediv == 0) { + if (adc->pll_loopdiv == 0 && adc->pll_prediv == 0) return -EINVAL; - } else + else return 0; } @@ -2077,7 +2076,7 @@ static int dib7090_agc_startup(struct dvb_frontend *fe, struct dvb_frontend_para target = (dib0090_get_wbd_offset(fe) * 8 + 1) / 2; dib7000p_set_wbd_ref(fe, target); - if(dib7090p_get_best_sampling(fe, &adc) == 0) { + if (dib7090p_get_best_sampling(fe, &adc) == 0) { pll.pll_ratio = adc.pll_loopdiv; pll.pll_prediv = adc.pll_prediv; @@ -2088,11 +2087,11 @@ static int dib7090_agc_startup(struct dvb_frontend *fe, struct dvb_frontend_para } static struct dib0090_wbd_slope dib7090_wbd_table[] = { - { 380, 81, 850, 64, 540 ,4}, - { 860, 51, 866, 21, 375 ,4}, - {1700, 0, 250, 0, 100, 6}, //LBAND Predefinition , NOT tested Yet - {2600, 0, 250, 0, 100, 6}, //SBAND Predefinition , NOT tested Yet - { 0xFFFF, 0, 0, 0, 0 ,0}, + { 380, 81, 850, 64, 540, 4}, + { 860, 51, 866, 21, 375, 4}, + {1700, 0, 250, 0, 100, 6}, + {2600, 0, 250, 0, 100, 6}, + { 0xFFFF, 0, 0, 0, 0, 0}, }; struct dibx000_agc_config dib7090_agc_config[2] = { @@ -2100,91 +2099,91 @@ struct dibx000_agc_config dib7090_agc_config[2] = { .band_caps = BAND_UHF, /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ - .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), - .inv_gain = 687,// inv_gain = 1/ 95.4dB // no boost, lower gain due to ramp quantification - .time_stabiliz = 10, // time_stabiliz + .inv_gain = 687, + .time_stabiliz = 10, - .alpha_level = 0, // alpha_level - .thlock = 118, // thlock + .alpha_level = 0, + .thlock = 118, - .wbd_inv = 0, // wbd_inv - .wbd_ref = 1200, // wbd_ref - .wbd_sel = 3, // wbd_sel - .wbd_alpha = 5, // wbd_alpha + .wbd_inv = 0, + .wbd_ref = 1200, + .wbd_sel = 3, + .wbd_alpha = 5, - .agc1_max = 65535, // agc1_max - .agc1_min = 0, // agc1_min + .agc1_max = 65535, + .agc1_min = 0, - .agc2_max = 65535, // agc2_max - .agc2_min = 0, // agc2_min + .agc2_max = 65535, + .agc2_min = 0, - .agc1_pt1 = 0, // agc1_pt1 - .agc1_pt2 = 32, // agc1_pt2 - .agc1_pt3 = 114, // agc1_pt3 // 40.4dB - .agc1_slope1 = 143, // agc1_slope1 - .agc1_slope2 = 144, // agc1_slope2 - .agc2_pt1 = 114, // agc2_pt1 - .agc2_pt2 = 227, // agc2_pt2 - .agc2_slope1 = 116, // agc2_slope1 - .agc2_slope2 = 117, // agc2_slope2 + .agc1_pt1 = 0, + .agc1_pt2 = 32, + .agc1_pt3 = 114, + .agc1_slope1 = 143, + .agc1_slope2 = 144, + .agc2_pt1 = 114, + .agc2_pt2 = 227, + .agc2_slope1 = 116, + .agc2_slope2 = 117, - .alpha_mant = 18, // alpha_mant // 5Hz with 95.4dB - .alpha_exp = 0, // alpha_exp - .beta_mant = 20, // beta_mant - .beta_exp = 59, // beta_exp + .alpha_mant = 18, + .alpha_exp = 0, + .beta_mant = 20, + .beta_exp = 59, - .perform_agc_softsplit = 0, // perform_agc_softsplit + .perform_agc_softsplit = 0, } , { .band_caps = BAND_FM | BAND_VHF | BAND_CBAND, /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ - .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), - .inv_gain = 732,// inv_gain = 1/ 89.5dB // no boost, lower gain due to ramp quantification - .time_stabiliz = 10, // time_stabiliz + .inv_gain = 732, + .time_stabiliz = 10, - .alpha_level = 0, // alpha_level - .thlock = 118, // thlock + .alpha_level = 0, + .thlock = 118, - .wbd_inv = 0, // wbd_inv - .wbd_ref = 1200, // wbd_ref - .wbd_sel = 3, // wbd_sel - .wbd_alpha = 5, // wbd_alpha + .wbd_inv = 0, + .wbd_ref = 1200, + .wbd_sel = 3, + .wbd_alpha = 5, - .agc1_max = 65535, // agc1_max : 1 - .agc1_min = 0, // agc1_min + .agc1_max = 65535, + .agc1_min = 0, - .agc2_max = 65535, // agc2_max - .agc2_min = 0, // agc2_min + .agc2_max = 65535, + .agc2_min = 0, - .agc1_pt1 = 0, // agc1_pt1 - .agc1_pt2 = 0, // agc1_pt2 - .agc1_pt3 = 98, // agc1_pt3 // 34.5dB CBAND P1G + 55dB BB boost = 89.5dB - .agc1_slope1 = 0, // agc1_slope1 - .agc1_slope2 = 167, // agc1_slope2 = Dy/Dx * 2**6 * 2**8 = 1/98 * 2**6 *2**8 : Dy = 1 - .agc1_pt1 = 98, // agc2_pt1 - .agc2_pt2 = 255, // agc2_pt2 - .agc2_slope1 = 104, // agc2_slope1 = Dy/Dx * 2**6 * 2**8 = 1/(255-98) * 2**6 *2**8 - .agc2_slope2 = 0, // agc2_slope2 + .agc1_pt1 = 0, + .agc1_pt2 = 0, + .agc1_pt3 = 98, + .agc1_slope1 = 0, + .agc1_slope2 = 167, + .agc1_pt1 = 98, + .agc2_pt2 = 255, + .agc2_slope1 = 104, + .agc2_slope2 = 0, - .alpha_mant = 18, // alpha_mant // 5Hz with 95.4dB - .alpha_exp = 0, // alpha_exp - .beta_mant = 20, // beta_mant - .beta_exp = 59, // beta_exp + .alpha_mant = 18, + .alpha_exp = 0, + .beta_mant = 20, + .beta_exp = 59, - .perform_agc_softsplit = 0, // perform_agc_softsplit + .perform_agc_softsplit = 0, } }; static struct dibx000_bandwidth_config dib7090_clock_config_12_mhz = { - 60000, 15000, // internal, sampling - 1, 5, 0, 0, 0, // pll_cfg: prediv, ratio, range, reset, bypass - 0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo - (3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k - (0 << 25) | 0, // ifreq = 0.000000 MHz - 20452225, // timf - 15000000, // xtal_hz + 60000, 15000, + 1, 5, 0, 0, 0, + 0, 0, 1, 1, 2, + (3 << 14) | (1 << 12) | (524 << 0), + (0 << 25) | 0, + 20452225, + 15000000, }; static struct dib7000p_config nim7090_dib7000p_config = { @@ -2243,7 +2242,7 @@ static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, .default_i2c_addr = 0x90, .enMpegOutput = 1, - },{ + }, { .output_mpeg2_in_188_bytes = 1, .hostbus_diversity = 1, .tuner_is_baseband = 1, @@ -2340,7 +2339,7 @@ static const struct dib0090_config tfe7090pvr_dib0090_config[2] = { .data_tx_drv = 0, .low_if = NULL, .in_soc = 1, - },{ + }, { .io.clock_khz = 12000, .io.pll_bypass = 0, .io.pll_range = 0, @@ -2378,15 +2377,15 @@ static const struct dib0090_config tfe7090pvr_dib0090_config[2] = { static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) { dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, &nim7090_dib7000p_config) != 0) { @@ -2421,15 +2420,15 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) st->disable_streaming_master_mode = 1; dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); - msleep(10); + msleep(20); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); /* initialize IC 0 */ @@ -2441,7 +2440,7 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) dib0700_set_i2c_speed(adap->dev, 340); adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); - dib7090_slave_reset(adap->fe); + dib7090_slave_reset(adap->fe); if (adap->fe == NULL) return -ENODEV; diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c index 0e87a0b..52ff1a2 100644 --- a/drivers/media/dvb/frontends/dib0090.c +++ b/drivers/media/dvb/frontends/dib0090.c @@ -204,8 +204,8 @@ static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg) { u8 b[2]; struct i2c_msg msg[2] = { - {.addr = state->config->i2c_address,.flags = 0,.buf = ®,.len = 1}, - {.addr = state->config->i2c_address,.flags = I2C_M_RD,.buf = b,.len = 2}, + {.addr = state->config->i2c_address, .flags = 0, .buf = ®, .len = 1}, + {.addr = state->config->i2c_address, .flags = I2C_M_RD, .buf = b, .len = 2}, }; if (i2c_transfer(state->i2c, msg, 2) != 2) { printk(KERN_WARNING "DiB0090 I2C read failed\n"); @@ -217,7 +217,7 @@ static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg) static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val) { u8 b[3] = { reg & 0xff, val >> 8, val & 0xff }; - struct i2c_msg msg = {.addr = state->config->i2c_address,.flags = 0,.buf = b,.len = 3 }; + struct i2c_msg msg = {.addr = state->config->i2c_address, .flags = 0, .buf = b, .len = 3 }; if (i2c_transfer(state->i2c, &msg, 1) != 1) { printk(KERN_WARNING "DiB0090 I2C write failed\n"); return -EREMOTEIO; @@ -228,7 +228,7 @@ static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val) static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg) { u8 b[2]; - struct i2c_msg msg = {.addr = reg,.flags = I2C_M_RD,.buf = b,.len = 2 }; + struct i2c_msg msg = {.addr = reg, .flags = I2C_M_RD, .buf = b, .len = 2 }; if (i2c_transfer(state->i2c, &msg, 1) != 1) { printk(KERN_WARNING "DiB0090 I2C read failed\n"); return 0; @@ -239,7 +239,7 @@ static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg) static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val) { u8 b[2] = { val >> 8, val & 0xff }; - struct i2c_msg msg = {.addr = reg,.flags = 0,.buf = b,.len = 2 }; + struct i2c_msg msg = {.addr = reg, .flags = 0, .buf = b, .len = 2 }; if (i2c_transfer(state->i2c, &msg, 1) != 1) { printk(KERN_WARNING "DiB0090 I2C write failed\n"); return -EREMOTEIO; @@ -347,7 +347,7 @@ static int dib0090_identify(struct dvb_frontend *fe) return 0; - identification_error: +identification_error: return -EIO; } @@ -371,8 +371,6 @@ static int dib0090_fw_identify(struct dvb_frontend *fe) if (identity->product != KROSUS) goto identification_error; - //From the SOC the version definition has changed - if ((identity->version & 0x3) == SOC) { identity->in_soc = 1; switch (identity->version) { @@ -439,7 +437,7 @@ static int dib0090_fw_identify(struct dvb_frontend *fe) return 0; - identification_error: +identification_error: return -EIO;; } @@ -1009,7 +1007,6 @@ void dib0090_pwm_gain_reset(struct dvb_frontend *fe) if (state->current_band == BAND_VHF) { if (state->identity.in_soc) { dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); - //dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf_socs); /* TODO */ } else { dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf); dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); @@ -1044,9 +1041,8 @@ EXPORT_SYMBOL(dib0090_pwm_gain_reset); static u32 dib0090_get_slow_adc_val(struct dib0090_state *state) { u16 adc_val = dib0090_read_reg(state, 0x1d); - if (state->identity.in_soc) { + if (state->identity.in_soc) adc_val >>= 2; - } return adc_val; } @@ -1200,7 +1196,7 @@ int dib0090_gain_control(struct dvb_frontend *fe) #ifdef DEBUG_AGC dprintk ("tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm", - (u32) * tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val, + (u32) *tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val, (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA)); #endif } @@ -1246,26 +1242,20 @@ u16 dib0090_get_wbd_offset(struct dvb_frontend *fe) if (current_temp > 128) current_temp = 128; - //What Wbd gain to apply for this range of frequency state->wbdmux &= ~(7 << 13); if (wbd->wbd_gain != 0) state->wbdmux |= (wbd->wbd_gain << 13); else - state->wbdmux |= (4 << 13); // 4 is the default WBD gain + state->wbdmux |= (4 << 13); dib0090_write_reg(state, 0x10, state->wbdmux); - //All the curves are linear with slope*f/64+offset wbd_thot = wbd->offset_hot - (((u32) wbd->slope_hot * f_MHz) >> 6); wbd_tcold = wbd->offset_cold - (((u32) wbd->slope_cold * f_MHz) >> 6); - // Iet assumes that thot-tcold = 130 equiv 128, current temperature ref is -30deg - wbd_tcold += ((wbd_thot - wbd_tcold) * current_temp) >> 7; - //for (offset = 0; offset < 1000; offset += 4) - // dbgp("offset = %d -> %d\n", offset, dib0090_wbd_to_db(state, offset)); - state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + wbd_tcold); // get the value in dBm from the offset + state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + wbd_tcold); dprintk("wbd-target: %d dB", (u32) state->wbd_target); dprintk("wbd offset applied is %d", wbd_tcold); @@ -1323,7 +1313,6 @@ static const u16 dib0090_defaults[] = { }; static const u16 dib0090_p1g_additionnal_defaults[] = { - // additionnal INITIALISATION for p1g to be written after dib0090_defaults 1, 0x05, 0xabcd, @@ -1362,45 +1351,44 @@ static void dib0090_set_default_config(struct dib0090_state *state, const u16 * void dib0090_set_EFUSE(struct dib0090_state *state) { - u8 c,h,n; - u16 e2,e4; - u16 cal; - - e2=dib0090_read_reg(state,0x26); - e4=dib0090_read_reg(state,0x28); - - if ((state->identity.version == P1D_E_F) || // All P1F uses the internal calibration - (state->identity.version == P1G) || (e2 == 0xffff)) { //W0090G11R1 and W0090G11R1-D : We will find the calibration Value of the Baseband - - dib0090_write_reg(state,0x22,0x10); //Start the Calib - cal = (dib0090_read_reg(state,0x22)>>6) & 0x3ff; - - if ((cal<670) || (cal==1023)) //Cal at 800 would give too high value for the n - cal=850; //Recenter the n to 32 - n = 165 - ((cal * 10)>>6) ; - e2 = e4 = (3<<12) | (34<<6) | (n); - } - - if (e2!=e4) { - e2 &= e4; /* Remove the redundancy */ - } - - if (e2 != 0xffff) { - c = e2 & 0x3f; - n = (e2 >> 12) & 0xf; - h= (e2 >> 6) & 0x3f; - - if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN)) - c=32; - if ((h >= HR_MAX) || (h <= HR_MIN)) - h=34; - if ((n >= POLY_MAX) || (n <= POLY_MIN)) - n=3; - - dib0090_write_reg(state,0x13, (h << 10)) ; - e2 = (n<<11) | ((h>>2)<<6) | (c); - dib0090_write_reg(state,0x2, e2) ; /* Load the BB_2 */ - } + u8 c, h, n; + u16 e2, e4; + u16 cal; + + e2 = dib0090_read_reg(state, 0x26); + e4 = dib0090_read_reg(state, 0x28); + + if ((state->identity.version == P1D_E_F) || + (state->identity.version == P1G) || (e2 == 0xffff)) { + + dib0090_write_reg(state, 0x22, 0x10); + cal = (dib0090_read_reg(state, 0x22) >> 6) & 0x3ff; + + if ((cal < 670) || (cal == 1023)) + cal = 850; + n = 165 - ((cal * 10)>>6) ; + e2 = e4 = (3<<12) | (34<<6) | (n); + } + + if (e2 != e4) + e2 &= e4; /* Remove the redundancy */ + + if (e2 != 0xffff) { + c = e2 & 0x3f; + n = (e2 >> 12) & 0xf; + h = (e2 >> 6) & 0x3f; + + if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN)) + c = 32; + if ((h >= HR_MAX) || (h <= HR_MIN)) + h = 34; + if ((n >= POLY_MAX) || (n <= POLY_MIN)) + n = 3; + + dib0090_write_reg(state, 0x13, (h << 10)) ; + e2 = (n<<11) | ((h>>2)<<6) | (c); + dib0090_write_reg(state, 0x2, e2) ; /* Load the BB_2 */ + } } static int dib0090_reset(struct dvb_frontend *fe) @@ -1425,14 +1413,15 @@ static int dib0090_reset(struct dvb_frontend *fe) dib0090_set_default_config(state, dib0090_defaults); - if (state->identity.in_soc) - dib0090_write_reg(state, 0x18, 0x2910); /* charge pump current = 0 */ + if (state->identity.in_soc) + dib0090_write_reg(state, 0x18, 0x2910); /* charge pump current = 0 */ if (state->identity.p1g) dib0090_set_default_config(state, dib0090_p1g_additionnal_defaults); - if (((state->identity.version & 0x1f) >= P1D_E_F) || (state->identity.in_soc)) /* Update the efuse : Only available for KROSUS > P1C and SOC as well*/ - dib0090_set_EFUSE(state); + /* Update the efuse : Only available for KROSUS > P1C and SOC as well*/ + if (((state->identity.version & 0x1f) >= P1D_E_F) || (state->identity.in_soc)) + dib0090_set_EFUSE(state); /* Congigure in function of the crystal */ if (state->config->io.clock_khz >= 24000) @@ -1501,11 +1490,11 @@ static const struct dc_calibration dc_table[] = { static const struct dc_calibration dc_p1g_table[] = { /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */ /* addr ; trim reg offset ; pga ; CTRL_BB1 value ; i or q */ - {0x06, 5, 1, (1 << 13) | (0 << 8) | (15 << 3), 1}, // offset_trim2_i_chann 0 0 5 0 0 1 6 9 5 - {0x07, 11, 1, (1 << 13) | (0 << 8) | (15 << 3), 0}, // offset_trim2_q_chann 0 0 5 0 0 1 7 15 11 + {0x06, 5, 1, (1 << 13) | (0 << 8) | (15 << 3), 1}, + {0x07, 11, 1, (1 << 13) | (0 << 8) | (15 << 3), 0}, /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */ - {0x06, 0, 0, (1 << 13) | (29 << 8) | (15 << 3), 1}, // offset_trim1_i_chann 0 0 5 0 0 1 6 4 0 - {0x06, 10, 0, (1 << 13) | (29 << 8) | (15 << 3), 0}, // offset_trim1_q_chann 0 0 5 0 0 1 6 14 10 + {0x06, 0, 0, (1 << 13) | (29 << 8) | (15 << 3), 1}, + {0x06, 10, 0, (1 << 13) | (29 << 8) | (15 << 3), 0}, {0}, }; @@ -1542,8 +1531,8 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front dib0090_write_reg(state, 0x24, reg); state->wbdmux = dib0090_read_reg(state, 0x10); - dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x7 << 3) | 0x3); // connect BB, disable WDB enable* - dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); //Discard the DataTX + dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x7 << 3) | 0x3); + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); state->dc = dc_table; @@ -1596,11 +1585,11 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front if ((state->adc_diff & 0x8000) == (state->min_adc_diff & 0x8000) && steps(state->step) < 15) { /* stop search when the delta the sign is changing and Steps =15 and Step=0 is force for continuance */ state->step++; - state->min_adc_diff = state->adc_diff; //min is used as N-1 + state->min_adc_diff = state->adc_diff; *tune_state = CT_TUNER_STEP_1; } else { /* the minimum was what we have seen in the step before */ - if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) { //Come back to the previous state since the delta was better + if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) { dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step", state->adc_diff, state->min_adc_diff); state->step--; } @@ -1618,7 +1607,7 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front break; case CT_TUNER_STEP_6: - dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008); //Force the test bus to be off + dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008); dib0090_write_reg(state, 0x1f, 0x7); *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ state->calibrate &= ~DC_CAL; @@ -1653,9 +1642,9 @@ static int dib0090_wbd_calibration(struct dib0090_state *state, enum frontend_tu return 0; } - dib0090_write_reg(state, 0x10, 0x1b81 | (1 << 10) | (wbd_gain << 13) | (1 << 3)); // Force: WBD enable,gain to 4, mux to WBD + dib0090_write_reg(state, 0x10, 0x1b81 | (1 << 10) | (wbd_gain << 13) | (1 << 3)); - dib0090_write_reg(state, 0x24, ((EN_UHF & 0x0fff) | (1 << 1))); //Discard all LNA but crystal !!! + dib0090_write_reg(state, 0x24, ((EN_UHF & 0x0fff) | (1 << 1))); *tune_state = CT_TUNER_STEP_0; state->wbd_calibration_gain = wbd_gain; return 90; /* wait for the WBDMUX to switch and for the ADC to sample */ @@ -1788,98 +1777,94 @@ static const struct dib0090_tuning dib0090_tuning_table[] = { }; static const struct dib0090_tuning dib0090_p1g_tuning_table[] = { - //max_freq, switch_trim, lna_tune, lna_bias, v2i, mix, load, tuner_enable; #ifdef CONFIG_BAND_CBAND - {170000, 4, 1, 0x820f, 0x300, 0x2d22, 0x82cb, EN_CAB}, // FM EN_CAB + {170000, 4, 1, 0x820f, 0x300, 0x2d22, 0x82cb, EN_CAB}, #endif #ifdef CONFIG_BAND_VHF - {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, // VHF EN_VHF - {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, // VHF EN_VHF - {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, // VHF EN_VHF + {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, + {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, + {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, #endif #ifdef CONFIG_BAND_UHF - {510000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {540000, 2, 1, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {600000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {630000, 2, 4, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {680000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {720000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {510000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {540000, 2, 1, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {600000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {630000, 2, 4, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {680000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {720000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, #endif #ifdef CONFIG_BAND_LBAND - {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD - {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD - {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD + {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, #endif #ifdef CONFIG_BAND_SBAND - {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, // SBD EN_SBD - {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, // SBD EN_SBD + {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, + {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, #endif }; static const struct dib0090_pll dib0090_p1g_pll_table[] = { #ifdef CONFIG_BAND_CBAND - {57000, 0, 11, 48, 6}, // CAB - {70000, 1, 11, 48, 6}, // CAB - {86000, 0, 10, 32, 4}, // CAB - {105000, 1, 10, 32, 4}, // FM - {115000, 0, 9, 24, 6}, // FM - {140000, 1, 9, 24, 6}, // MID FM VHF - {170000, 0, 8, 16, 4}, // MID FM VHF + {57000, 0, 11, 48, 6}, + {70000, 1, 11, 48, 6}, + {86000, 0, 10, 32, 4}, + {105000, 1, 10, 32, 4}, + {115000, 0, 9, 24, 6}, + {140000, 1, 9, 24, 6}, + {170000, 0, 8, 16, 4}, #endif #ifdef CONFIG_BAND_VHF - {200000, 1, 8, 16, 4}, // VHF - {230000, 0, 7, 12, 6}, // VHF - {280000, 1, 7, 12, 6}, // MID VHF UHF - {340000, 0, 6, 8, 4}, // MID VHF UHF - {380000, 1, 6, 8, 4}, // MID VHF UHF - {455000, 0, 5, 6, 6}, // MID VHF UHF + {200000, 1, 8, 16, 4}, + {230000, 0, 7, 12, 6}, + {280000, 1, 7, 12, 6}, + {340000, 0, 6, 8, 4}, + {380000, 1, 6, 8, 4}, + {455000, 0, 5, 6, 6}, #endif #ifdef CONFIG_BAND_UHF - {580000, 1, 5, 6, 6}, // UHF - {680000, 0, 4, 4, 4}, // UHF - {860000, 1, 4, 4, 4}, // UHF + {580000, 1, 5, 6, 6}, + {680000, 0, 4, 4, 4}, + {860000, 1, 4, 4, 4}, #endif #ifdef CONFIG_BAND_LBAND - {1800000, 1, 2, 2, 4}, // LBD + {1800000, 1, 2, 2, 4}, #endif #ifdef CONFIG_BAND_SBAND - {2900000, 0, 1, 1, 6}, // SBD + {2900000, 0, 1, 1, 6}, #endif }; static const struct dib0090_tuning dib0090_p1g_tuning_table_fm_vhf_on_cband[] = { - //max_freq, switch_trim, lna_tune, lna_bias, v2i, mix, load, tuner_enable; #ifdef CONFIG_BAND_CBAND - {184000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, // FM EN_CAB // 0x8190 Good perf but higher current //0x4187 Low current - {227000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, // FM EN_CAB - {380000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, // FM EN_CAB + {184000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, + {227000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, + {380000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, #endif #ifdef CONFIG_BAND_UHF - {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF - {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, // UHF + {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, #endif #ifdef CONFIG_BAND_LBAND - {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD - {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD - {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, // LBD EN_LBD + {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, #endif #ifdef CONFIG_BAND_SBAND - {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, // SBD EN_SBD - {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, // SBD EN_SBD + {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, + {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, #endif }; static const struct dib0090_tuning dib0090_tuning_table_cband_7090[] = { - //max_freq, switch_trim, lna_tune, lna_bias, v2i, mix, load, tuner_enable; #ifdef CONFIG_BAND_CBAND - //{ 184000, 4, 3, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB }, // 0x81ce 0x8190 Good perf but higher current //0x4187 Low current {300000, 4, 3, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, - {380000, 4, 10, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, //0x4187 + {380000, 4, 10, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, {570000, 4, 10, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, {858000, 4, 5, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, #endif @@ -1916,17 +1901,15 @@ static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tun state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f; } } - state->adc_diff = 3000; // start with a unreachable high number : only set for KROSUS < P1G */ + state->adc_diff = 3000; *tune_state = CT_TUNER_STEP_0; } else if (*tune_state == CT_TUNER_STEP_0) { if (state->identity.p1g && !force_soft_search) { - // 30MHz => Code 15 for the ration => 128us to lock. Giving approximately - u8 ratio = 31; // (state->config->io.clock_khz / 1024 + 1) & 0x1f; + u8 ratio = 31; dib0090_write_reg(state, 0x40, (3 << 7) | (ratio << 2) | (1 << 1) | 1); dib0090_read_reg(state, 0x40); - //dib0090_write_reg(state, 0x40, (3<<7) | ((((state->config->io.clock_khz >> 11)+1) & 0x1f)<<2) | (1<<1) | 1); ret = 50; } else { state->step /= 2; @@ -1968,9 +1951,6 @@ static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tun dprintk("CAPTRIM=%d is closer to target (%d/%d)", (u32) state->captrim, (u32) adc, (u32) state->adc_diff); state->adc_diff = adc; state->fcaptrim = state->captrim; - //we could break here, to save time, if we reached a close-enough value - //e.g.: if (state->adc_diff < 20) - //break; } state->captrim += step_sign * state->step; @@ -1979,7 +1959,7 @@ static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tun else *tune_state = CT_TUNER_STEP_2; - ret = 25; //LOLO changed from 15 + ret = 25; } } else if (*tune_state == CT_TUNER_STEP_2) { /* this step is only used by krosus < P1G */ /*write the final cptrim config */ @@ -2000,28 +1980,27 @@ static int dib0090_get_temperature(struct dib0090_state *state, enum frontend_tu int ret = 15; s16 val; - //The assumption is that the AGC is not active switch (*tune_state) { case CT_TUNER_START: state->wbdmux = dib0090_read_reg(state, 0x10); - dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x8 << 3)); //Move to the bias and clear the wbd enable + dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x8 << 3)); state->bias = dib0090_read_reg(state, 0x13); - dib0090_write_reg(state, 0x13, state->bias | (0x3 << 8)); //Move to the Ref + dib0090_write_reg(state, 0x13, state->bias | (0x3 << 8)); *tune_state = CT_TUNER_STEP_0; /* wait for the WBDMUX to switch and for the ADC to sample */ break; case CT_TUNER_STEP_0: - state->adc_diff = dib0090_get_slow_adc_val(state); // Get the value for the Ref - dib0090_write_reg(state, 0x13, (state->bias & ~(0x3 << 8)) | (0x2 << 8)); //Move to the Ptat + state->adc_diff = dib0090_get_slow_adc_val(state); + dib0090_write_reg(state, 0x13, (state->bias & ~(0x3 << 8)) | (0x2 << 8)); *tune_state = CT_TUNER_STEP_1; break; case CT_TUNER_STEP_1: - val = dib0090_get_slow_adc_val(state); // Get the value for the Ptat - state->temperature = ((s16) ((val - state->adc_diff) * 180) >> 8) + 55; // +55 is defined as = -30deg + val = dib0090_get_slow_adc_val(state); + state->temperature = ((s16) ((val - state->adc_diff) * 180) >> 8) + 55; dprintk("temperature: %d C", state->temperature - 30); @@ -2029,14 +2008,13 @@ static int dib0090_get_temperature(struct dib0090_state *state, enum frontend_tu break; case CT_TUNER_STEP_2: - //Reload the start values. dib0090_write_reg(state, 0x13, state->bias); dib0090_write_reg(state, 0x10, state->wbdmux); /* write back original WBDMUX */ *tune_state = CT_TUNER_START; state->calibrate &= ~TEMP_CAL; if (state->config->analog_output == 0) - dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); //Set the DataTX + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); break; @@ -2070,16 +2048,17 @@ static int dib0090_tune(struct dvb_frontend *fe) /* deactivate DataTX before some calibrations */ if (state->calibrate & (DC_CAL | TEMP_CAL | WBD_CAL)) dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); - else /* Activate DataTX in case a calibration has been done before */ if (state->config->analog_output == 0) - dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); + else + /* Activate DataTX in case a calibration has been done before */ + if (state->config->analog_output == 0) + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); } if (state->calibrate & DC_CAL) return dib0090_dc_offset_calibration(state, tune_state); else if (state->calibrate & WBD_CAL) { - if (state->current_rf == 0) { + if (state->current_rf == 0) state->current_rf = state->fe->dtv_property_cache.frequency / 1000; - } return dib0090_wbd_calibration(state, tune_state); } else if (state->calibrate & TEMP_CAL) return dib0090_get_temperature(state, tune_state); @@ -2091,7 +2070,7 @@ static int dib0090_tune(struct dvb_frontend *fe) if (state->config->use_pwm_agc && state->identity.in_soc) { tmp = dib0090_read_reg(state, 0x39); if ((tmp >> 10) & 0x1) - dib0090_write_reg(state, 0x39, tmp & ~(1 << 10)); // disengage mux : en_mux_bb1 = 0 + dib0090_write_reg(state, 0x39, tmp & ~(1 << 10)); } state->current_band = (u8) BAND_OF_FREQUENCY(state->fe->dtv_property_cache.frequency / 1000); @@ -2172,22 +2151,17 @@ static int dib0090_tune(struct dvb_frontend *fe) state->current_tune_table_index = tune; state->current_pll_table_index = pll; - // select internal switch dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim)); - // Find the VCO frequency in MHz VCOF_kHz = (pll->hfdiv * state->rf_request) * 2; - FREF = state->config->io.clock_khz; // REFDIV is 1FREF Has to be as Close as possible to 10MHz + FREF = state->config->io.clock_khz; if (state->config->fref_clock_ratio != 0) FREF /= state->config->fref_clock_ratio; - // Determine the FB divider - // The reference is 10MHz, Therefore the FBdivider is on the first digits FBDiv = (VCOF_kHz / pll->topresc / FREF); - Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF; //in kHz + Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF; - // Avoid Spurs in the loopfilter bandwidth if (Rest < LPF) Rest = 0; else if (Rest < 2 * LPF) @@ -2195,8 +2169,7 @@ static int dib0090_tune(struct dvb_frontend *fe) else if (Rest > (FREF - LPF)) { Rest = 0; FBDiv += 1; - } //Go to the next FB - else if (Rest > (FREF - 2 * LPF)) + } else if (Rest > (FREF - 2 * LPF)) Rest = FREF - 2 * LPF; Rest = (Rest * 6528) / (FREF / 10); state->rest = Rest; @@ -2208,8 +2181,6 @@ static int dib0090_tune(struct dvb_frontend *fe) if (Rest == 0) { if (pll->vco_band) lo5 = 0x049f; - //else if (state->config->analog_output) - // lo5 = 0x041f; else lo5 = 0x041f; } else { @@ -2228,12 +2199,11 @@ static int dib0090_tune(struct dvb_frontend *fe) else lo5 = 0x42f; } else - lo5 = 0x42c; //BIAS Lo set to 4 by default in case of the Captrim search does not take care of the VCO Bias + lo5 = 0x42c; } lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7); /* bit 15 is the split to the slave, we do not do it here */ - //Internal loop filter set... if (!state->config->io.pll_int_loop_filt) { if (state->identity.in_soc) lo6 = 0xff98; @@ -2242,40 +2212,30 @@ static int dib0090_tune(struct dvb_frontend *fe) else lo6 = 0xff28; } else - lo6 = (state->config->io.pll_int_loop_filt << 3); // take the loop filter value given by the layout - //dprintk("lo6 = 0x%04x", (u32)lo6); + lo6 = (state->config->io.pll_int_loop_filt << 3); Den = 1; if (Rest > 0) { if (state->config->analog_output) - lo6 |= (1 << 2) | 2; //SigmaDelta and Dither + lo6 |= (1 << 2) | 2; else { if (state->identity.in_soc) - lo6 |= (1 << 2) | 2; //SigmaDelta and Dither + lo6 |= (1 << 2) | 2; else - lo6 |= (1 << 2) | 2; //SigmaDelta and Dither + lo6 |= (1 << 2) | 2; } Den = 255; } - // Now we have to define the Num and Denum - // LO1 gets the FBdiv dib0090_write_reg(state, 0x15, (u16) FBDiv); - // LO2 gets the REFDiv if (state->config->fref_clock_ratio != 0) dib0090_write_reg(state, 0x16, (Den << 8) | state->config->fref_clock_ratio); else dib0090_write_reg(state, 0x16, (Den << 8) | 1); - // LO3 for the Numerator dib0090_write_reg(state, 0x17, (u16) Rest); - // VCO and HF DIV dib0090_write_reg(state, 0x19, lo5); - // SIGMA Delta dib0090_write_reg(state, 0x1c, lo6); - // Check if the 0090 is analogged configured - //Disable ADC and DigPLL =0xFF9F, 0xffbf for test purposes. - //Enable The Outputs of the BB on DATA_Tx lo6 = tune->tuner_enable; if (state->config->analog_output) lo6 = (lo6 & 0xff9f) | 0x2; @@ -2294,10 +2254,8 @@ static int dib0090_tune(struct dvb_frontend *fe) else if (*tune_state == CT_TUNER_STEP_0) { /* Warning : because of captrim cal, if you change this step, change it also in _cal.c file because it is the step following captrim cal state machine */ const struct dib0090_wbd_slope *wbd = state->current_wbd_table; -// if(!state->identity.p1g) { while (state->current_rf / 1000 > wbd->max_freq) wbd++; -// } dib0090_write_reg(state, 0x1e, 0x07ff); dprintk("Final Captrim: %d", (u32) state->fcaptrim); @@ -2311,12 +2269,11 @@ static int dib0090_tune(struct dvb_frontend *fe) #define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */ c = 4; - i = 3; //wbdmux_bias + i = 3; - if (wbd->wbd_gain != 0) //&& !state->identity.p1g) + if (wbd->wbd_gain != 0) c = wbd->wbd_gain; - //Store wideband mux register. state->wbdmux = (c << 13) | (i << 11) | (WBD | (state->config->use_pwm_agc << 1)); dib0090_write_reg(state, 0x10, state->wbdmux); @@ -2335,15 +2292,12 @@ static int dib0090_tune(struct dvb_frontend *fe) } else if (*tune_state == CT_TUNER_STEP_1) { /* initialize the lt gain register */ state->rf_lt_def = 0x7c00; - // dib0090_write_reg(state, 0x0f, state->rf_lt_def); dib0090_set_bandwidth(state); state->tuner_is_tuned = 1; -// if(!state->identity.p1g) - state->calibrate |= WBD_CAL; // TODO: only do the WBD calibration for new tune -// - state->calibrate |= TEMP_CAL; // Force the Temperature to be remesured at next TUNE. + state->calibrate |= WBD_CAL; + state->calibrate |= TEMP_CAL; *tune_state = CT_TUNER_STOP; } else ret = FE_CALLBACK_TIME_NEVER; @@ -2435,8 +2389,8 @@ static const struct dvb_tuner_ops dib0090_fw_ops = { static const struct dib0090_wbd_slope dib0090_wbd_table_default[] = { {470, 0, 250, 0, 100, 4}, {860, 51, 866, 21, 375, 4}, - {1700, 0, 800, 0, 850, 4}, //LBAND Predefinition , to calibrate - {2900, 0, 250, 0, 100, 6}, //SBAND Predefinition , NOT tested Yet + {1700, 0, 800, 0, 850, 4}, + {2900, 0, 250, 0, 100, 6}, {0xFFFF, 0, 0, 0, 0, 0}, }; @@ -2489,12 +2443,11 @@ struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_ada memcpy(&fe->ops.tuner_ops, &dib0090_fw_ops, sizeof(struct dvb_tuner_ops)); return fe; - free_mem: +free_mem: kfree(st); fe->tuner_priv = NULL; return NULL; } - EXPORT_SYMBOL(dib0090_fw_register); MODULE_AUTHOR("Patrick Boettcher "); diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 18495bd..b3ca3e2 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -79,8 +79,8 @@ static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg) u8 wb[2] = { reg >> 8, reg & 0xff }; u8 rb[2]; struct i2c_msg msg[2] = { - {.addr = state->i2c_addr >> 1,.flags = 0,.buf = wb,.len = 2}, - {.addr = state->i2c_addr >> 1,.flags = I2C_M_RD,.buf = rb,.len = 2}, + {.addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2}, + {.addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2}, }; if (i2c_transfer(state->i2c_adap, msg, 2) != 2) @@ -96,7 +96,7 @@ static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val) (val >> 8) & 0xff, val & 0xff, }; struct i2c_msg msg = { - .addr = state->i2c_addr >> 1,.flags = 0,.buf = b,.len = 4 + .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 }; return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; } @@ -129,13 +129,13 @@ static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) dprintk("setting output mode for demod %p to %d", &state->demod, mode); switch (mode) { - case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + case OUTMODE_MPEG2_PAR_GATED_CLK: outreg = (1 << 10); /* 0x0400 */ break; - case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + case OUTMODE_MPEG2_PAR_CONT_CLK: outreg = (1 << 10) | (1 << 6); /* 0x0440 */ break; - case OUTMODE_MPEG2_SERIAL: // STBs with serial input + case OUTMODE_MPEG2_SERIAL: outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */ break; case OUTMODE_DIVERSITY: @@ -144,7 +144,7 @@ static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) else outreg = (1 << 11); break; - case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + case OUTMODE_MPEG2_FIFO: smo_mode |= (3 << 1); fifo_threshold = 512; outreg = (1 << 10) | (5 << 6); @@ -152,7 +152,7 @@ static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) case OUTMODE_ANALOG_ADC: outreg = (1 << 10) | (3 << 6); break; - case OUTMODE_HIGH_Z: // disable + case OUTMODE_HIGH_Z: outreg = 0; break; default: @@ -284,7 +284,7 @@ static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_ad reg_909 &= 0x0003; break; - case DIBX000_ADC_OFF: // leave the VBG voltage on + case DIBX000_ADC_OFF: reg_908 |= (1 << 14) | (1 << 13) | (1 << 12); reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); break; @@ -336,13 +336,12 @@ static int dib7000p_set_bandwidth(struct dib7000p_state *state, u32 bw) static int dib7000p_sad_calib(struct dib7000p_state *state) { /* internal */ -// dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth dib7000p_write_word(state, 73, (0 << 1) | (0 << 0)); if (state->version == SOC7090) - dib7000p_write_word(state, 74, 2048); // P_sad_calib_value = (0.9/1.8)*4096 + dib7000p_write_word(state, 74, 2048); else - dib7000p_write_word(state, 74, 776); // P_sad_calib_value = 0.625*3.3 / 4096 + dib7000p_write_word(state, 74, 776); /* do the calibration */ dib7000p_write_word(state, 73, (1 << 0)); @@ -371,8 +370,8 @@ static void dib7000p_reset_pll(struct dib7000p_state *state) if (state->version == SOC7090) { dib7000p_write_word(state, 1856, (!bw->pll_reset << 13) | (bw->pll_range << 12) | (bw->pll_ratio << 6) | (bw->pll_prediv)); - while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) { - } + while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) + ; dib7000p_write_word(state, 1857, dib7000p_read_word(state, 1857) | (!bw->pll_bypass << 15)); } else { @@ -420,7 +419,7 @@ int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, bw->pll_prediv, loopdiv, bw->pll_ratio); reg_1856 &= 0xf000; reg_1857 = dib7000p_read_word(state, 1857); - dib7000p_write_word(state, 1857, reg_1857 & ~(1 << 15)); // desable pll + dib7000p_write_word(state, 1857, reg_1857 & ~(1 << 15)); dib7000p_write_word(state, 1856, reg_1856 | ((bw->pll_ratio & 0x3f) << 6) | (bw->pll_prediv & 0x3f)); @@ -431,11 +430,10 @@ int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config dib7000p_write_word(state, 18, (u16) ((internal >> 16) & 0xffff)); dib7000p_write_word(state, 19, (u16) (internal & 0xffff)); - dib7000p_write_word(state, 1857, reg_1857 | (1 << 15)); // enable pll + dib7000p_write_word(state, 1857, reg_1857 | (1 << 15)); - while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) { + while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) dprintk("Waiting for PLL to lock"); - } return 0; } @@ -503,7 +501,7 @@ static u16 dib7000p_defaults[] = { 0xd4c0, 1, 26, - 0x6680, // P_timf_alpha=6, P_corm_alpha=6, P_corm_thres=128 default: 6,4,26 + 0x6680, /* set ADC level to -16 */ 11, 79, @@ -520,7 +518,7 @@ static u16 dib7000p_defaults[] = { (1 << 13) - 501 - 117, 1, 142, - 0x0410, // P_palf_filter_on=1, P_palf_filter_freeze=0, P_palf_alpha_regul=16 + 0x0410, /* disable power smoothing */ 8, 145, @@ -534,42 +532,39 @@ static u16 dib7000p_defaults[] = { 0, 1, 154, - 1 << 13, // P_fft_freq_dir=1, P_fft_nb_to_cut=0 + 1 << 13, 1, 168, - 0x0ccd, // P_pha3_thres, default 0x3000 - -// 1, 169, -// 0x0010, // P_cti_use_cpe=0, P_cti_use_prog=0, P_cti_win_len=16, default: 0x0010 + 0x0ccd, 1, 183, - 0x200f, // P_cspu_regul=512, P_cspu_win_cut=15, default: 0x2005 + 0x200f, 1, 212, - 0x169, // P_vit_ksi_dwn = 5 P_vit_ksi_up = 5 0x1e1, // P_vit_ksi_dwn = 4 P_vit_ksi_up = 7 + 0x169, 5, 187, - 0x023d, // P_adp_regul_cnt=573, default: 410 - 0x00a4, // P_adp_noise_cnt= - 0x00a4, // P_adp_regul_ext - 0x7ff0, // P_adp_noise_ext - 0x3ccc, // P_adp_fil + 0x023d, + 0x00a4, + 0x00a4, + 0x7ff0, + 0x3ccc, 1, 198, - 0x800, // P_equal_thres_wgn + 0x800, 1, 222, - 0x0010, // P_fec_ber_rs_len=2 + 0x0010, 1, 235, - 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + 0x0062, 2, 901, - 0x0006, // P_clk_cfg1 - (3 << 10) | (1 << 6), // P_divclksel=3 P_divbitsel=1 + 0x0006, + (3 << 10) | (1 << 6), 1, 905, - 0x2c8e, // Tuner IO bank: max drive (14mA) + divout pads max drive + 0x2c8e, 0, }; @@ -609,8 +604,7 @@ static int dib7000p_demod_reset(struct dib7000p_state *state) dib7000p_write_word(state, 42, (1<<5) | 3); /* P_iqc_thsat_ipc = 1 ; P_iqc_win2 = 3 */ dib7000p_write_word(state, 43, 0x2d4); /*-300 fag P_iqc_dect_min = -280 */ dib7000p_write_word(state, 44, 300); /* 300 fag P_iqc_dect_min = +280 */ - //dib7000p_write_word(state, 273, (1<<6) | 10); /* P_vit_inoise_sel = 1, P_vit_inoise_gain = 10*/ - dib7000p_write_word(state, 273, (1<<6) | 30); //26/* P_vit_inoise_sel = 1, P_vit_inoise_gain = 26*/// FAG + dib7000p_write_word(state, 273, (1<<6) | 30); } if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0) dprintk("OUTPUT_MODE could not be reset."); @@ -624,9 +618,9 @@ static int dib7000p_demod_reset(struct dib7000p_state *state) dib7000p_set_bandwidth(state, 8000); - if(state->version == SOC7090) { + if (state->version == SOC7090) { dib7000p_write_word(state, 36, 0x5755);/* P_iqc_impnc_on =1 & P_iqc_corr_inh = 1 for impulsive noise */ - } else { // P_iqc_alpha_pha, P_iqc_alpha_amp_dcc_alpha, ... + } else { if (state->cfg.tuner_is_baseband) dib7000p_write_word(state, 36, 0x0755); else @@ -644,9 +638,9 @@ static void dib7000p_pll_clk_cfg(struct dib7000p_state *state) { u16 tmp = 0; tmp = dib7000p_read_word(state, 903); - dib7000p_write_word(state, 903, (tmp | 0x1)); //pwr-up pll + dib7000p_write_word(state, 903, (tmp | 0x1)); tmp = dib7000p_read_word(state, 900); - dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); //use High freq clock + dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); } static void dib7000p_restart_agc(struct dib7000p_state *state) @@ -660,11 +654,9 @@ static int dib7000p_update_lna(struct dib7000p_state *state) { u16 dyn_gain; - // when there is no LNA to program return immediatly if (state->cfg.update_lna) { - // read dyn_gain here (because it is demod-dependent and not fe) dyn_gain = dib7000p_read_word(state, 394); - if (state->cfg.update_lna(&state->demod, dyn_gain)) { // LNA has changed + if (state->cfg.update_lna(&state->demod, dyn_gain)) { dib7000p_restart_agc(state); return 1; } @@ -763,12 +755,11 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod, struct dvb_frontend_ switch (state->agc_state) { case 0: - // set power-up level: interf+analog+AGC dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); if (state->version == SOC7090) { reg = dib7000p_read_word(state, 0x79b) & 0xff00; dib7000p_write_word(state, 0x79a, upd_demod_gain_period & 0xFFFF); /* lsb */ - dib7000p_write_word(state, 0x79b, reg | (1 << 14) | ((upd_demod_gain_period >> 16) & 0xFF)); // bit 14 = enDemodGain + dib7000p_write_word(state, 0x79b, reg | (1 << 14) | ((upd_demod_gain_period >> 16) & 0xFF)); /* enable adc i & q */ reg = dib7000p_read_word(state, 0x780); @@ -787,7 +778,6 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod, struct dvb_frontend_ break; case 1: - // AGC initialization if (state->cfg.agc_control) state->cfg.agc_control(&state->demod, 1); @@ -831,13 +821,11 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod, struct dvb_frontend_ break; case 4: /* LNA startup */ - // wait AGC accurate lock time ret = 7; if (dib7000p_update_lna(state)) - // wait only AGC rough lock time ret = 5; - else // nothing was done, go to the next state + else (*agc_state)++; break; @@ -971,10 +959,10 @@ static void dib7000p_set_channel(struct dib7000p_state *state, struct dvb_fronte dib7000p_write_word(state, 208, value); /* offset loop parameters */ - dib7000p_write_word(state, 26, 0x6680); // timf(6xxx) - dib7000p_write_word(state, 32, 0x0003); // pha_off_max(xxx3) - dib7000p_write_word(state, 29, 0x1273); // isi - dib7000p_write_word(state, 33, 0x0005); // sfreq(xxx5) + dib7000p_write_word(state, 26, 0x6680); + dib7000p_write_word(state, 32, 0x0003); + dib7000p_write_word(state, 29, 0x1273); + dib7000p_write_word(state, 33, 0x0005); /* P_dvsy_sync_wait */ switch (ch->u.ofdm.transmission_mode) { @@ -1005,9 +993,9 @@ static void dib7000p_set_channel(struct dib7000p_state *state, struct dvb_fronte break; } if (state->cfg.diversity_delay == 0) - state->div_sync_wait = (value * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo + state->div_sync_wait = (value * 3) / 2 + 48; else - state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for one DVSY-fifo + state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay; /* deactive the possibility of diversity reception if extended interleaver */ state->div_force_off = !1 && ch->u.ofdm.transmission_mode != TRANSMISSION_MODE_8K; @@ -1061,16 +1049,15 @@ static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dvb_fron else factor = 6; - // always use the setting for 8MHz here lock_time for 7,6 MHz are longer value = 30 * internal * factor; - dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time - dib7000p_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time + dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); + dib7000p_write_word(state, 7, (u16) (value & 0xffff)); value = 100 * internal * factor; - dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time - dib7000p_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time + dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); + dib7000p_write_word(state, 9, (u16) (value & 0xffff)); value = 500 * internal * factor; - dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time - dib7000p_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time + dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); + dib7000p_write_word(state, 11, (u16) (value & 0xffff)); value = dib7000p_read_word(state, 0); dib7000p_write_word(state, 0, (u16) ((1 << 9) | value)); @@ -1085,13 +1072,13 @@ static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod) struct dib7000p_state *state = demod->demodulator_priv; u16 irq_pending = dib7000p_read_word(state, 1284); - if (irq_pending & 0x1) // failed + if (irq_pending & 0x1) return 1; - if (irq_pending & 0x2) // succeeded + if (irq_pending & 0x2) return 2; - return 0; // still pending + return 0; } static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32 bw) @@ -1202,9 +1189,9 @@ static int dib7000p_tune(struct dvb_frontend *demod, struct dvb_frontend_paramet if (state->sfn_workaround_active) { dprintk("SFN workaround is active"); tmp |= (1 << 9); - dib7000p_write_word(state, 166, 0x4000); // P_pha3_force_pha_shift + dib7000p_write_word(state, 166, 0x4000); } else { - dib7000p_write_word(state, 166, 0x0000); // P_pha3_force_pha_shift + dib7000p_write_word(state, 166, 0x0000); } dib7000p_write_word(state, 29, tmp); @@ -1425,8 +1412,7 @@ static int dib7000p_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_pa if (state->version == SOC7090) { dib7090_set_diversity_in(fe, 0); dib7090_set_output_mode(fe, OUTMODE_HIGH_Z); - } - else + } else dib7000p_set_output_mode(state, OUTMODE_HIGH_Z); /* maybe the parameter has been changed */ @@ -1455,7 +1441,7 @@ static int dib7000p_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_pa dprintk("autosearch returns: %d", found); if (found == 0 || found == 1) - return 0; // no channel found + return 0; dib7000p_get_frontend(fe, fep); } @@ -1566,8 +1552,8 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap) { u8 tx[2], rx[2]; struct i2c_msg msg[2] = { - {.addr = 18 >> 1,.flags = 0,.buf = tx,.len = 2}, - {.addr = 18 >> 1,.flags = I2C_M_RD,.buf = rx,.len = 2}, + {.addr = 18 >> 1, .flags = 0, .buf = tx, .len = 2}, + {.addr = 18 >> 1, .flags = I2C_M_RD, .buf = rx, .len = 2}, }; tx[0] = 0x03; @@ -1725,9 +1711,8 @@ static int map_addr_to_serpar_number(struct i2c_msg *msg) msg->buf[0] -= 3; else if (msg->buf[0] == 28) msg->buf[0] = 23; - else { + else return -EINVAL; - } return 0; } @@ -1909,7 +1894,7 @@ static int dib7090_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[] if (num == 1) { /* write */ word = (u16) ((msg[0].buf[1] << 8) | msg[0].buf[2]); word &= 0x3; - word = (dib7000p_read_word(state, 72) & ~(3 << 12)) | (word << 12); //Mask bit 12,13 + word = (dib7000p_read_word(state, 72) & ~(3 << 12)) | (word << 12); dib7000p_write_word(state, 72, word); /* Set the proper input */ return num; } @@ -1996,7 +1981,7 @@ static int dib7090_cfg_DibTx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout u16 rx_copy_buf[22]; dprintk("Configure DibStream Tx"); - for (index_buf = 0; index_buf<22; index_buf++) + for (index_buf = 0; index_buf < 22; index_buf++) rx_copy_buf[index_buf] = dib7000p_read_word(state, 1536+index_buf); dib7000p_write_word(state, 1615, 1); @@ -2009,7 +1994,7 @@ static int dib7090_cfg_DibTx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout dib7000p_write_word(state, 1612, syncSize); dib7000p_write_word(state, 1615, 0); - for (index_buf = 0; index_buf<22; index_buf++) + for (index_buf = 0; index_buf < 22; index_buf++) dib7000p_write_word(state, 1536+index_buf, rx_copy_buf[index_buf]); return 0; @@ -2021,8 +2006,7 @@ static int dib7090_cfg_DibRx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout u32 syncFreq; dprintk("Configure DibStream Rx"); - if ((P_Kin != 0) && (P_Kout != 0)) - { + if ((P_Kin != 0) && (P_Kout != 0)) { syncFreq = dib7090_calcSyncFreq(P_Kin, P_Kout, insertExtSynchro, syncSize); dib7000p_write_word(state, 1542, syncFreq); } @@ -2044,7 +2028,7 @@ static int dib7090_enDivOnHostBus(struct dib7000p_state *state) u16 reg; dprintk("Enable Diversity on host bus"); - reg = (1 << 8) | (1 << 5); // P_enDivOutOnDibTx = 1 ; P_enDibTxOnHostBus = 1 + reg = (1 << 8) | (1 << 5); dib7000p_write_word(state, 1288, reg); return dib7090_cfg_DibTx(state, 5, 5, 0, 0, 0, 0); @@ -2055,7 +2039,7 @@ static int dib7090_enAdcOnHostBus(struct dib7000p_state *state) u16 reg; dprintk("Enable ADC on host bus"); - reg = (1 << 7) | (1 << 5); //P_enAdcOnDibTx = 1 ; P_enDibTxOnHostBus = 1 + reg = (1 << 7) | (1 << 5); dib7000p_write_word(state, 1288, reg); return dib7090_cfg_DibTx(state, 20, 5, 10, 0, 0, 0); @@ -2066,7 +2050,7 @@ static int dib7090_enMpegOnHostBus(struct dib7000p_state *state) u16 reg; dprintk("Enable Mpeg on host bus"); - reg = (1 << 9) | (1 << 5); //P_enMpegOnDibTx = 1 ; P_enDibTxOnHostBus = 1 + reg = (1 << 9) | (1 << 5); dib7000p_write_word(state, 1288, reg); return dib7090_cfg_DibTx(state, 8, 5, 0, 0, 0, 0); @@ -2085,10 +2069,10 @@ static int dib7090_enMpegMux(struct dib7000p_state *state, u16 pulseWidth, u16 e dprintk("Enable Mpeg mux"); dib7000p_write_word(state, 1287, reg); - reg &= ~(1 << 7); // P_restart_mpegMux = 0 + reg &= ~(1 << 7); dib7000p_write_word(state, 1287, reg); - reg = (1 << 4); //P_enMpegMuxOnHostBus = 1 + reg = (1 << 4); dib7000p_write_word(state, 1288, reg); return 0; @@ -2099,10 +2083,10 @@ static int dib7090_disableMpegMux(struct dib7000p_state *state) u16 reg; dprintk("Disable Mpeg mux"); - dib7000p_write_word(state, 1288, 0); //P_enMpegMuxOnHostBus = 0 + dib7000p_write_word(state, 1288, 0); reg = dib7000p_read_word(state, 1287); - reg &= ~(1 << 7); // P_restart_mpegMux = 0 + reg &= ~(1 << 7); dib7000p_write_word(state, 1287, reg); return 0; @@ -2112,19 +2096,19 @@ static int dib7090_set_input_mode(struct dvb_frontend *fe, int mode) { struct dib7000p_state *state = fe->demodulator_priv; - switch(mode) { - case INPUT_MODE_DIVERSITY: + switch (mode) { + case INPUT_MODE_DIVERSITY: dprintk("Enable diversity INPUT"); - dib7090_cfg_DibRx(state, 5,5,0,0,0,0,0); + dib7090_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0); break; - case INPUT_MODE_MPEG: + case INPUT_MODE_MPEG: dprintk("Enable Mpeg INPUT"); - dib7090_cfg_DibRx(state, 8,5,0,0,0,8,0); /*outputRate = 8 */ + dib7090_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); /*outputRate = 8 */ break; - case INPUT_MODE_OFF: - default: + case INPUT_MODE_OFF: + default: dprintk("Disable INPUT"); - dib7090_cfg_DibRx(state, 0,0,0,0,0,0,0); + dib7090_cfg_DibRx(state, 0, 0, 0, 0, 0, 0, 0); break; } return 0; @@ -2175,7 +2159,7 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) } else { /* Use Smooth block */ dprintk("Sip 7090P setting output mode TS_SERIAL using Smooth bloc"); dib7090_disableMpegMux(state); - dib7000p_write_word(state, 1288, (1 << 6)); //P_enDemOutInterfOnHostBus = 1 + dib7000p_write_word(state, 1288, (1 << 6)); outreg |= (2 << 6) | (0 << 1); } break; @@ -2190,7 +2174,7 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) } else { /* Use Smooth block */ dprintk("Sip 7090P setting output mode TS_PARALLEL_GATED using Smooth block"); dib7090_disableMpegMux(state); - dib7000p_write_word(state, 1288, (1 << 6)); //P_enDemOutInterfOnHostBus = 1 + dib7000p_write_word(state, 1288, (1 << 6)); outreg |= (0 << 6); } break; @@ -2198,14 +2182,14 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */ dprintk("Sip 7090P setting output mode TS_PARALLEL_CONT using Smooth block"); dib7090_disableMpegMux(state); - dib7000p_write_word(state, 1288, (1 << 6)); //P_enDemOutInterfOnHostBus = 1 + dib7000p_write_word(state, 1288, (1 << 6)); outreg |= (1 << 6); break; case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux bloc */ dprintk("Sip 7090P setting output mode TS_FIFO using Smooth block"); dib7090_disableMpegMux(state); - dib7000p_write_word(state, 1288, (1 << 6)); //P_enDemOutInterfOnHostBus = 1 + dib7000p_write_word(state, 1288, (1 << 6)); outreg |= (5 << 6); smo_mode |= (3 << 1); fifo_threshold = 512; @@ -2242,12 +2226,11 @@ int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) en_cur_state = dib7000p_read_word(state, 1922); - if (en_cur_state > 0xff) { //LNAs and MIX are ON and therefore it is a valid configuration + if (en_cur_state > 0xff) state->tuner_enable = en_cur_state; - } if (onoff) - en_cur_state &= 0x00ff; //Mask to be applied + en_cur_state &= 0x00ff; else { if (state->tuner_enable != 0) en_cur_state = state->tuner_enable; @@ -2275,13 +2258,13 @@ EXPORT_SYMBOL(dib7090_get_adc_power); int dib7090_slave_reset(struct dvb_frontend *fe) { struct dib7000p_state *state = fe->demodulator_priv; - u16 reg; + u16 reg; - reg = dib7000p_read_word(state, 1794); - dib7000p_write_word(state, 1794, reg | (4 << 12)); + reg = dib7000p_read_word(state, 1794); + dib7000p_write_word(state, 1794, reg | (4 << 12)); - dib7000p_write_word(state, 1032, 0xffff); - return 0; + dib7000p_write_word(state, 1032, 0xffff); + return 0; } EXPORT_SYMBOL(dib7090_slave_reset); @@ -2340,7 +2323,7 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, return demod; - error: +error: kfree(st); return NULL; } diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h index 4e3ffc8..0179f94 100644 --- a/drivers/media/dvb/frontends/dib7000p.h +++ b/drivers/media/dvb/frontends/dib7000p.h @@ -39,7 +39,7 @@ struct dib7000p_config { u16 diversity_delay; u8 default_i2c_addr; - u8 enMpegOutput : 1; + u8 enMpegOutput:1; }; #define DEFAULT_DIB7000P_I2C_ADDRESS 18 diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index 625e421..3961fed 100644 --- a/drivers/media/dvb/frontends/dib8000.c +++ b/drivers/media/dvb/frontends/dib8000.c @@ -261,7 +261,8 @@ static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) fifo_threshold = 1792; smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); - dprintk("-I- Setting output mode for demod %p to %d", &state->fe[0], mode); + dprintk("-I- Setting output mode for demod %p to %d", + &state->fe[0], mode); switch (mode) { case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock @@ -295,7 +296,8 @@ static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) break; default: - dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe[0]); + dprintk("Unhandled output_mode passed to be set for demod %p", + &state->fe[0]); return -EINVAL; } @@ -345,7 +347,8 @@ static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_pow { /* by default everything is going to be powered off */ u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0xffff, - reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00; + reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, + reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00; /* now, depending on the requested mode, we power on */ switch (mode) { @@ -482,7 +485,8 @@ static void dib8000_reset_pll(struct dib8000_state *state) // clk_cfg1 clk_cfg1 = (1 << 10) | (0 << 9) | (pll->IO_CLK_en_core << 8) | - (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | (1 << 3) | (pll->pll_range << 1) | (pll->pll_reset << 0); + (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | (1 << 3) | + (pll->pll_range << 1) | (pll->pll_reset << 0); dib8000_write_word(state, 902, clk_cfg1); clk_cfg1 = (clk_cfg1 & 0xfff7) | (pll->pll_bypass << 3); @@ -492,11 +496,12 @@ static void dib8000_reset_pll(struct dib8000_state *state) /* smpl_cfg: P_refclksel=2, P_ensmplsel=1 nodivsmpl=1 */ if (state->cfg.pll->ADClkSrc == 0) - dib8000_write_word(state, 904, (0 << 15) | (0 << 12) | (0 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1)); + dib8000_write_word(state, 904, (0 << 15) | (0 << 12) | (0 << 10) | + (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1)); else if (state->cfg.refclksel != 0) - dib8000_write_word(state, 904, - (0 << 15) | (1 << 12) | ((state->cfg.refclksel & 0x3) << 10) | (pll->modulo << 8) | (pll-> - ADClkSrc << 7) | (0 << 1)); + dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | + ((state->cfg.refclksel & 0x3) << 10) | (pll->modulo << 8) | + (pll->ADClkSrc << 7) | (0 << 1)); else dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | (3 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1)); @@ -627,14 +632,14 @@ static const u16 dib8000_defaults[] = { 1, 285, 0x0020, //p_fec_ 1, 299, - 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + 0x0062, /* P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard */ 1, 338, (1 << 12) | // P_ctrl_corm_thres4pre_freq_inh=1 - (1 << 10) | // P_ctrl_pre_freq_mode_sat=1 - (0 << 9) | // P_ctrl_pre_freq_inh=0 - (3 << 5) | // P_ctrl_pre_freq_step=3 - (1 << 0), // P_pre_freq_win_len=1 + (1 << 10) | + (0 << 9) | /* P_ctrl_pre_freq_inh=0 */ + (3 << 5) | /* P_ctrl_pre_freq_step=3 */ + (1 << 0), /* P_pre_freq_win_len=1 */ 1, 903, (0 << 4) | 2, // P_divclksel=0 P_divbitsel=2 (was clk=3,bit=1 for MPW) @@ -782,7 +787,7 @@ static int dib8000_update_lna(struct dib8000_state *state) // read dyn_gain here (because it is demod-dependent and not tuner) dyn_gain = dib8000_read_word(state, 390); - if (state->cfg.update_lna(state->fe[0], dyn_gain)) { // LNA has changed + if (state->cfg.update_lna(state->fe[0], dyn_gain)) { dib8000_restart_agc(state); return 1; } @@ -869,7 +874,8 @@ static int dib8000_agc_soft_split(struct dib8000_state *state) split_offset = state->current_agc->split.max; else split_offset = state->current_agc->split.max * - (agc - state->current_agc->split.min_thres) / (state->current_agc->split.max_thres - state->current_agc->split.min_thres); + (agc - state->current_agc->split.min_thres) / + (state->current_agc->split.max_thres - state->current_agc->split.min_thres); dprintk("AGC split_offset: %d", split_offset); @@ -952,14 +958,13 @@ s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) s32 val; val = dib8000_read32(state, 384); - /* mode = 1 : ln_agcpower calc using mant-exp conversion and mantis look up table */ if (mode) { tmp_val = val; while (tmp_val >>= 1) exp++; mant = (val * 1000 / (1<fe[0]->dtv_property_cache.inversion ^ i); + dib8000_write_word(state, 26, state->fe[0]->dtv_property_cache.inversion^i); if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { //compute new dds_freq for the seg and adjust prbs int seg_offset = - state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx - (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) - + state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx - + (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) - (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2); int clk = state->cfg.pll->internal; u32 segtodds = ((u32) (430 << 23) / clk) << 3; // segtodds = SegBW / Fclk * pow(2,26) int dds_offset = seg_offset * segtodds; int new_dds, sub_channel; - if ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) // if even + if ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) dds_offset -= (int)(segtodds / 2); if (state->cfg.pll->ifreq == 0) { @@ -1031,7 +1037,8 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear // - the segment of center frequency with an odd total number of segments // - the segment to the left of center frequency with an even total number of segments // - the segment to the right of center frequency with an even total number of segments - if ((state->fe[0]->dtv_property_cache.delivery_system == SYS_ISDBT) && (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) + if ((state->fe[0]->dtv_property_cache.delivery_system == SYS_ISDBT) + && (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) && (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) @@ -1051,9 +1058,9 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear } dib8000_write_word(state, 27, (u16) ((new_dds >> 16) & 0x01ff)); dib8000_write_word(state, 28, (u16) (new_dds & 0xffff)); - if (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) // if odd + if (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3; - else // if even + else sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3; sub_channel -= 6; @@ -1212,7 +1219,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear } break; } - } else { // if not state->fe[0]->dtv_property_cache.isdbt_sb_mode + } else { dib8000_write_word(state, 27, (u16) ((state->cfg.pll->ifreq >> 16) & 0x01ff)); dib8000_write_word(state, 28, (u16) (state->cfg.pll->ifreq & 0xffff)); dib8000_write_word(state, 26, (u16) ((state->cfg.pll->ifreq >> 25) & 0x0003)); @@ -1332,8 +1339,8 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear state->differential_constellation = (seg_diff_mask != 0); dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) // 3-segments + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) seg_mask13 = 0x00E0; else // 1-segment seg_mask13 = 0x0040; @@ -1355,25 +1362,24 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 353, seg_mask13); // ADDR 353 /* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */ - // dib8000_write_word(state, 351, (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5 ); // ---- SMALL ---- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { switch (state->fe[0]->dtv_property_cache.transmission_mode) { case TRANSMISSION_MODE_2K: - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) ncoeff = coeff_2k_sb_1seg_dqpsk; else // QPSK or QAM ncoeff = coeff_2k_sb_1seg; } else { // 3-segments - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; else // QPSK or QAM on external segments ncoeff = coeff_2k_sb_3seg_0dqpsk; } else { // QPSK or QAM on central segment - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) ncoeff = coeff_2k_sb_3seg_1dqpsk; else // QPSK or QAM on external segments ncoeff = coeff_2k_sb_3seg; @@ -1382,20 +1388,20 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear break; case TRANSMISSION_MODE_4K: - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) ncoeff = coeff_4k_sb_1seg_dqpsk; else // QPSK or QAM ncoeff = coeff_4k_sb_1seg; } else { // 3-segments - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; } else { // QPSK or QAM on external segments ncoeff = coeff_4k_sb_3seg_0dqpsk; } } else { // QPSK or QAM on central segment - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { ncoeff = coeff_4k_sb_3seg_1dqpsk; } else // QPSK or QAM on external segments ncoeff = coeff_4k_sb_3seg; @@ -1406,20 +1412,20 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear case TRANSMISSION_MODE_AUTO: case TRANSMISSION_MODE_8K: default: - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) ncoeff = coeff_8k_sb_1seg_dqpsk; else // QPSK or QAM ncoeff = coeff_8k_sb_1seg; } else { // 3-segments - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; } else { // QPSK or QAM on external segments ncoeff = coeff_8k_sb_3seg_0dqpsk; } } else { // QPSK or QAM on central segment - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { ncoeff = coeff_8k_sb_3seg_1dqpsk; } else // QPSK or QAM on external segments ncoeff = coeff_8k_sb_3seg; @@ -1437,7 +1443,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear // ---- COFF ---- // Carloff, the most robust - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { // Sound Broadcasting mode - use both TMCC and AC pilots + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { // P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64 // P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 @@ -1448,7 +1454,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear /* // P_small_coef_ext_enable = 1 */ /* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */ - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width= (P_mode == 3) , P_coff_one_seg_sym= (P_mode-1) if (mode == 3) @@ -1512,7 +1518,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); } // ---- FFT ---- - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 && state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) // 1-seg + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 && state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) dib8000_write_word(state, 178, 64); // P_fft_powrange=64 else dib8000_write_word(state, 178, 32); // P_fft_powrange=32 @@ -1542,7 +1548,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear /* offset loop parameters */ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x40); @@ -1555,7 +1561,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear dib8000_write_word(state, 32, ((9 - mode) << 12) | (6 << 8) | 0x80); if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (11-P_mode) */ dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (10 - mode)); @@ -1628,7 +1634,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear // ---- ANA_FE ---- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) // 3-segments + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) ana_fe = ana_fe_coeff_3seg; else // 1-segment ana_fe = ana_fe_coeff_1seg; @@ -1651,10 +1657,10 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear // "P_cspu_left_edge" not used => do not care // "P_cspu_right_edge" not used => do not care - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { dib8000_write_word(state, 228, 1); // P_2d_mode_byp=1 dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); // P_cspu_win_cut = 0 - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0 // 1-segment + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0 && state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) { //dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); // P_adp_pass = 0 dib8000_write_word(state, 265, 15); // P_equal_noise_sel = 15 @@ -1803,7 +1809,7 @@ static int dib8000_tune(struct dvb_frontend *fe) // never achieved a lock before - wait for timfreq to update if (state->timf == 0) { if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) msleep(300); else // Sound Broadcasting mode 3 seg msleep(500); @@ -1811,7 +1817,7 @@ static int dib8000_tune(struct dvb_frontend *fe) msleep(200); } if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40 alpha to check on board */ dib8000_write_word(state, 32, ((13 - mode) << 12) | (6 << 8) | 0x40); @@ -1864,9 +1870,9 @@ static int dib8000_wakeup(struct dvb_frontend *fe) if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) dprintk("could not start Slow ADC"); - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ret = state->fe[index_frontend]->ops.init(state->fe[index_frontend]); - if (ret<0) + if (ret < 0) return ret; } @@ -1879,7 +1885,7 @@ static int dib8000_sleep(struct dvb_frontend *fe) u8 index_frontend; int ret; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + 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) return ret; @@ -1914,13 +1920,13 @@ static int dib8000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par fe->dtv_property_cache.bandwidth_hz = 6000000; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + 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); if (stat&FE_HAS_SYNC) { dprintk("TMCC lock on the slave%i", index_frontend); /* synchronize the cache with the other frontends */ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], fep); - for (sub_index_frontend=0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) { + for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) { if (sub_index_frontend != index_frontend) { state->fe[sub_index_frontend]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode; state->fe[sub_index_frontend]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; @@ -2032,7 +2038,7 @@ static int dib8000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par } /* synchronize the cache with the other frontends */ - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode = fe->dtv_property_cache.isdbt_sb_mode; state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion; state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode; @@ -2066,7 +2072,7 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par state->fe[0]->dtv_property_cache.bandwidth_hz = 6000000; } - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { /* synchronization of the cache */ state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_ISDBT; memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); @@ -2081,7 +2087,7 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par /* start up the AGC */ do { time = dib8000_agc_startup(state->fe[0]); - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { time_slave = dib8000_agc_startup(state->fe[index_frontend]); if (time == FE_CALLBACK_TIME_NEVER) time = time_slave; @@ -2093,7 +2099,7 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par else break; exit_condition = 1; - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_AGC_STOP) { exit_condition = 0; break; @@ -2101,7 +2107,7 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par } } while (exit_condition == 0); - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) dib8000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); if ((state->fe[0]->dtv_property_cache.delivery_system != SYS_ISDBT) || @@ -2132,31 +2138,30 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par u8 found = 0; u8 tune_failed = 0; - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000); dib8000_autosearch_start(state->fe[index_frontend]); } do { - msleep(10); + msleep(20); nbr_pending = 0; exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { if (((tune_failed >> index_frontend) & 0x1) == 0) { found = dib8000_autosearch_irq(state->fe[index_frontend]); switch (found) { - case 0: /* tune pending */ + case 0: /* tune pending */ nbr_pending++; break; - case 2: + case 2: dprintk("autosearch succeed on the frontend%i", index_frontend); exit_condition = 2; index_frontend_success = index_frontend; break; - default: + default: dprintk("unhandled autosearch result"); - case 1: - tune_failed |= (1 << index_frontend); + case 1: dprintk("autosearch failed for the frontend%i", index_frontend); break; } @@ -2178,13 +2183,12 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par dib8000_get_frontend(fe, fep); } - for (index_frontend=0, ret=0; (ret >= 0) && (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0, ret = 0; (ret >= 0) && (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) ret = dib8000_tune(state->fe[index_frontend]); - } /* set output mode and diversity input */ dib8000_set_output_mode(state->fe[0], state->cfg.output_mode); - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { dib8000_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY); dib8000_set_diversity_in(state->fe[index_frontend-1], 1); } @@ -2195,7 +2199,8 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par return ret; } -static u16 dib8000_read_lock(struct dvb_frontend *fe) { +static u16 dib8000_read_lock(struct dvb_frontend *fe) +{ struct dib8000_state *state = fe->demodulator_priv; return dib8000_read_word(state, 568); @@ -2207,7 +2212,7 @@ static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) u16 lock_slave = 0, lock = dib8000_read_word(state, 568); u8 index_frontend; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) lock_slave |= dib8000_read_lock(state->fe[index_frontend]); *stat = 0; @@ -2262,7 +2267,7 @@ static int dib8000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) u16 val; *strength = 0; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + 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); if (val > 65535 - *strength) *strength = 65535; @@ -2312,7 +2317,7 @@ static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) u32 snr_master; snr_master = dib8000_get_snr(fe); - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) snr_master += dib8000_get_snr(state->fe[index_frontend]); if (snr_master != 0) { @@ -2361,7 +2366,7 @@ int dib8000_remove_slave_frontend(struct dvb_frontend *fe) } EXPORT_SYMBOL(dib8000_remove_slave_frontend); -struct dvb_frontend * dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) { struct dib8000_state *state = fe->demodulator_priv; @@ -2432,7 +2437,7 @@ static void dib8000_release(struct dvb_frontend *fe) struct dib8000_state *st = fe->demodulator_priv; u8 index_frontend; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) dvb_frontend_detach(st->fe[index_frontend]); dibx000_exit_i2c_master(&st->i2c_master); diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h index 558b7e8..617f9eb 100644 --- a/drivers/media/dvb/frontends/dib8000.h +++ b/drivers/media/dvb/frontends/dib8000.h @@ -52,7 +52,7 @@ extern void dib8000_pwm_agc_reset(struct dvb_frontend *fe); extern s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode); extern int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); extern int dib8000_remove_slave_frontend(struct dvb_frontend *fe); -extern struct dvb_frontend * dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); +extern struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); #else static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) { @@ -126,9 +126,10 @@ int dib8000_remove_slave_frontend(struct dvb_frontend *fe) return -ENODEV; } -static inline struct dvb_frontend * dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) { +static inline struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +{ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; + return NULL; } #endif diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c index a41e02d..43fb6e4 100644 --- a/drivers/media/dvb/frontends/dib9000.c +++ b/drivers/media/dvb/frontends/dib9000.c @@ -31,7 +31,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) do { if (mutex_lock_interruptible(lock) < 0) dprintk("could not get the lock"); } while (0) #define DibReleaseLock(lock) mutex_unlock(lock) #define DibInitLock(lock) mutex_init(lock) #define DibFreeLock(lock) @@ -187,8 +187,7 @@ enum dib9000_in_messages { #define FE_MM_W_COMPONENT_ACCESS 16 #define FE_MM_RW_COMPONENT_ACCESS_BUFFER 17 -static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, - u8 * b, u32 len); +static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len); static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len); static u16 to_fw_output_mode(u16 mode) @@ -220,8 +219,8 @@ static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32 int ret; u8 wb[2] = { reg >> 8, reg & 0xff }; struct i2c_msg msg[2] = { - {.addr = state->i2c.i2c_addr >> 1,.flags = 0,.buf = wb,.len = 2}, - {.addr = state->i2c.i2c_addr >> 1,.flags = I2C_M_RD,.buf = b,.len = len}, + {.addr = state->i2c.i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2}, + {.addr = state->i2c.i2c_addr >> 1, .flags = I2C_M_RD, .buf = b, .len = len}, }; if (state->platform.risc.fw_is_running && (reg < 1024)) @@ -257,8 +256,8 @@ static u16 dib9000_i2c_read16(struct i2c_device *i2c, u16 reg) u8 b[2]; u8 wb[2] = { reg >> 8, reg & 0xff }; struct i2c_msg msg[2] = { - {.addr = i2c->i2c_addr >> 1,.flags = 0,.buf = wb,.len = 2}, - {.addr = i2c->i2c_addr >> 1,.flags = I2C_M_RD,.buf = b,.len = 2}, + {.addr = i2c->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2}, + {.addr = i2c->i2c_addr >> 1, .flags = I2C_M_RD, .buf = b, .len = 2}, }; if (i2c_transfer(i2c->i2c_adap, msg, 2) != 2) { @@ -295,12 +294,12 @@ static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 * int ret; struct i2c_msg msg = { - .addr = state->i2c.i2c_addr >> 1,.flags = 0,.buf = b,.len = len + 2 + .addr = state->i2c.i2c_addr >> 1, .flags = 0, .buf = b, .len = len + 2 }; if (state->platform.risc.fw_is_running && (reg < 1024)) { if (dib9000_risc_apb_access_write - (state, reg, DATA_BUS_ACCESS_MODE_16BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | attribute, buf, len) != 0) + (state, reg, DATA_BUS_ACCESS_MODE_16BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | attribute, buf, len) != 0) return -EINVAL; return 0; } @@ -334,7 +333,7 @@ static int dib9000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) { u8 b[4] = { (reg >> 8) & 0xff, reg & 0xff, (val >> 8) & 0xff, val & 0xff }; struct i2c_msg msg = { - .addr = i2c->i2c_addr >> 1,.flags = 0,.buf = b,.len = 4 + .addr = i2c->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 }; return i2c_transfer(i2c->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; @@ -369,24 +368,24 @@ static void dib9000_risc_mem_setup_cmd(struct dib9000_state *state, u32 addr, u3 { u8 b[14] = { 0 }; -// dprintk("%d memcmd: %d %d %d\n", state->fe_id, addr, addr+len, len); -// b[0] = 0 << 7; +/* dprintk("%d memcmd: %d %d %d\n", state->fe_id, addr, addr+len, len); */ +/* b[0] = 0 << 7; */ b[1] = 1; -// b[2] = 0; // 1057 -// b[3] = 0; - b[4] = (u8) (addr >> 8); // 1058 +/* b[2] = 0; */ +/* b[3] = 0; */ + b[4] = (u8) (addr >> 8); b[5] = (u8) (addr & 0xff); -// b[10] = 0; // 1061 -// b[11] = 0; - b[12] = (u8) (addr >> 8); // 1062 +/* b[10] = 0; */ +/* b[11] = 0; */ + b[12] = (u8) (addr >> 8); b[13] = (u8) (addr & 0xff); addr += len; -// b[6] = 0; // 1059 -// b[7] = 0; - b[8] = (u8) (addr >> 8); // 1060 +/* b[6] = 0; */ +/* b[7] = 0; */ + b[8] = (u8) (addr >> 8); b[9] = (u8) (addr & 0xff); dib9000_write(state, 1056, b, 14); @@ -400,7 +399,7 @@ static void dib9000_risc_mem_setup(struct dib9000_state *state, u8 cmd) struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd & 0x7f]; /* decide whether we need to "refresh" the memory controller */ if (state->platform.risc.memcmd == cmd && /* same command */ - !(cmd & 0x80 && m->size < 67)) /* and we do not want to read something with less than 67 bytes looping - working around a bug in the memory controller */ + !(cmd & 0x80 && m->size < 67)) /* and we do not want to read something with less than 67 bytes looping - working around a bug in the memory controller */ return; dib9000_risc_mem_setup_cmd(state, m->addr, m->size, cmd & 0x80); state->platform.risc.memcmd = cmd; @@ -506,7 +505,7 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, break; } while (1); - //dprintk( "MBX: size: %d", size); + /*dprintk( "MBX: size: %d", size); */ if (tmp == 0) { ret = -EINVAL; @@ -538,7 +537,7 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, /* update register nb_mes_in_RX */ ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr); - out: +out: DibReleaseLock(&state->platform.risc.mbx_if_lock); return ret; @@ -625,7 +624,7 @@ static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr) if (*block == 0) { size = dib9000_mbx_read(state, block, 1, attr); -// dprintk( "MBX: fetched %04x message to cache", *block); +/* dprintk( "MBX: fetched %04x message to cache", *block); */ switch (*block >> 8) { case IN_MSG_DEBUG_BUF: @@ -671,8 +670,8 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) ret = dib9000_mbx_fetch_to_cache(state, attr); tmp = dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ -// if (tmp) -// dprintk( "cleared IRQ: %x", tmp); +/* if (tmp) */ +/* dprintk( "cleared IRQ: %x", tmp); */ DibReleaseLock(&state->platform.risc.mbx_lock); return ret; @@ -805,7 +804,8 @@ static u16 dib9000_identify(struct i2c_device *client) { u16 value; - if ((value = dib9000_i2c_read16(client, 896)) != 0x01b3) { + value = dib9000_i2c_read16(client, 896); + if (value != 0x01b3) { dprintk("wrong Vendor ID (0x%x)", value); return 0; } @@ -916,7 +916,7 @@ static int dib9000_fw_reset(struct dvb_frontend *fe) { struct dib9000_state *state = fe->demodulator_priv; - dib9000_write_word(state, 1817, 0x0003); // SRAM read lead in + P_host_rdy_cmos=1 + dib9000_write_word(state, 1817, 0x0003); dib9000_write_word(state, 1227, 1); dib9000_write_word(state, 1227, 0); @@ -961,8 +961,7 @@ static int dib9000_fw_reset(struct dvb_frontend *fe) return 0; } -static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, - u8 * b, u32 len) +static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len) { u16 mb[10]; u8 i, s; @@ -970,14 +969,14 @@ static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address if (address >= 1024 || !state->platform.risc.fw_is_running) return -EINVAL; - //dprintk( "APB access thru rd fw %d %x", address, attribute); + /* dprintk( "APB access thru rd fw %d %x", address, attribute); */ mb[0] = (u16) address; mb[1] = len / 2; dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_R, mb, 2, attribute); switch (dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute)) { case 1: - s--; // address + s--; for (i = 0; i < s; i++) { b[i * 2] = (mb[i + 1] >> 8) & 0xff; b[i * 2 + 1] = (mb[i + 1]) & 0xff; @@ -997,10 +996,10 @@ static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 addres if (address >= 1024 || !state->platform.risc.fw_is_running) return -EINVAL; - //dprintk( "APB access thru wr fw %d %x", address, attribute); + /* dprintk( "APB access thru wr fw %d %x", address, attribute); */ mb[0] = (unsigned short)address; - for (i = 0; i < len && i < 20; i += 2) // 20 bytes max + for (i = 0; i < len && i < 20; i += 2) mb[1 + (i / 2)] = (b[i] << 8 | b[i + 1]); dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, 1 + len / 2, attribute); @@ -1031,7 +1030,6 @@ static int dib9000_fw_init(struct dib9000_state *state) u8 size; if (dib9000_fw_boot(state, NULL, 0, state->chip.d9.cfg.microcode_B_fe_buffer, state->chip.d9.cfg.microcode_B_fe_size) != 0) - //if (dib9000_fw_boot(state, microcode_A_buffer, microcode_A_size, microcode_B_buffer, microcode_B_size) != 0) return -EIO; /* initialize the firmware */ @@ -1062,7 +1060,6 @@ static int dib9000_fw_init(struct dib9000_state *state) b[2 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.mask; b[3 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.direction; b[4 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.value; - //dprintk( "SBS: %d %d %x %x %x\n", i, b[1 + i*4], b[2 + i*4], b[3 + i*4], b[4 + i*4]); } b[1 + i * 4] = 0; /* fe_id */ if (dib9000_mbx_send(state, OUT_MSG_SUBBAND_SEL, b, 2 + 4 * i) != 0) @@ -1071,7 +1068,7 @@ static int dib9000_fw_init(struct dib9000_state *state) /* 0 - id, 1 - no_of_frontends */ b[0] = (0 << 8) | 1; /* 0 = i2c-address demod, 0 = tuner */ - b[1] = (0 << 8) | (0); //st->i2c_addr ) ); + b[1] = (0 << 8) | (0); b[2] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000) >> 16) & 0xffff); b[3] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000)) & 0xffff); b[4] = (u16) ((state->chip.d9.cfg.vcxo_timer >> 16) & 0xffff); @@ -1089,14 +1086,14 @@ static int dib9000_fw_init(struct dib9000_state *state) return -EIO; if (size > ARRAY_SIZE(b)) { - dprintk("error : firmware returned %dbytes needed but the used buffer has only %dbytes\n Firmware init ABORTED", size, (int)ARRAY_SIZE(b)); + dprintk("error : firmware returned %dbytes needed but the used buffer has only %dbytes\n Firmware init ABORTED", size, + (int)ARRAY_SIZE(b)); return -EINVAL; } for (i = 0; i < size; i += 2) { state->platform.risc.fe_mm[i / 2].addr = b[i + 0]; state->platform.risc.fe_mm[i / 2].size = b[i + 1]; - //dprintk( "MM: %d %d %d", state->platform.risc.fe_mm[i/2].addr, state->platform.risc.fe_mm[i/2].size, ARRAY_SIZE(state->platform.risc.fe_mm)); } return 0; @@ -1150,9 +1147,9 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe, struct dvb_frontend_p ret = -EIO; } - dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION, (u8 *) & ch, sizeof(struct dibDVBTChannel)); + dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION, (u8 *) &ch, sizeof(struct dibDVBTChannel)); - switch (ch.spectrum_inversion&0x7) { + switch (ch.spectrum_inversion & 0x7) { case 1: state->fe[0]->dtv_property_cache.inversion = INVERSION_ON; break; @@ -1267,7 +1264,7 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe, struct dvb_frontend_p break; } - error: +error: DibReleaseLock(&state->platform.risc.mem_mbx_lock); return ret; } @@ -1412,7 +1409,7 @@ static int dib9000_fw_set_channel_union(struct dvb_frontend *fe, struct dvb_fron ch.select_hp = 1; ch.intlv_native = 1; - dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_UNION, (u8 *) & ch); + dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_UNION, (u8 *) &ch); return 0; } @@ -1441,9 +1438,9 @@ static int dib9000_fw_tune(struct dvb_frontend *fe, struct dvb_frontend_paramete break; case CT_DEMOD_STEP_1: if (search) - dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_SEARCH_STATE, (u8 *) & i, 1); + dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_SEARCH_STATE, (u8 *) &i, 1); else - dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_TUNE_STATE, (u8 *) & i, 1); + dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_TUNE_STATE, (u8 *) &i, 1); switch (i) { /* something happened */ case 0: break; @@ -1484,22 +1481,22 @@ static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode) dprintk("setting output mode for demod %p to %d", fe, mode); switch (mode) { - case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + case OUTMODE_MPEG2_PAR_GATED_CLK: outreg = (1 << 10); /* 0x0400 */ break; - case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + case OUTMODE_MPEG2_PAR_CONT_CLK: outreg = (1 << 10) | (1 << 6); /* 0x0440 */ break; - case OUTMODE_MPEG2_SERIAL: // STBs with serial input + case OUTMODE_MPEG2_SERIAL: outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ break; case OUTMODE_DIVERSITY: outreg = (1 << 10) | (4 << 6); /* 0x0500 */ break; - case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + case OUTMODE_MPEG2_FIFO: outreg = (1 << 10) | (5 << 6); break; - case OUTMODE_HIGH_Z: // disable + case OUTMODE_HIGH_Z: outreg = 0; break; default: @@ -1507,7 +1504,7 @@ static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode) return -EINVAL; } - dib9000_write_word(state, 1795, outreg); // has to be written from outside + dib9000_write_word(state, 1795, outreg); switch (mode) { case OUTMODE_MPEG2_PAR_GATED_CLK: @@ -1596,10 +1593,9 @@ EXPORT_SYMBOL(dib9000_fw_set_component_bus_speed); static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) { struct dib9000_state *state = i2c_get_adapdata(i2c_adap); - u8 type = 0; /* I2C */ + u8 type = 0; /* I2C */ u8 port = DIBX000_I2C_INTERFACE_GPIO_3_4; - u16 scl = state->component_bus_speed; /* SCL frequency */ - //u16 scl = 208; /* SCL frequency */ + u16 scl = state->component_bus_speed; /* SCL frequency */ struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[FE_MM_RW_COMPONENT_ACCESS_BUFFER]; u8 p[13] = { 0 }; @@ -1610,11 +1606,6 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2 p[3] = (u8) scl & 0xff; /* scl */ p[4] = (u8) (scl >> 8); -// p[5] = 0; /* attr */ -// p[6] = 0; - -// p[7] = (u8) (msg[0].addr << 1 ); -// p[8] = (u8) (msg[0].addr >> 7 ); p[7] = 0; p[8] = 0; @@ -1672,7 +1663,6 @@ struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe) struct dib9000_state *st = fe->demodulator_priv; return &st->tuner_adap; } - EXPORT_SYMBOL(dib9000_get_tuner_interface); struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) @@ -1680,7 +1670,6 @@ struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) struct dib9000_state *st = fe->demodulator_priv; return &st->component_bus; } - EXPORT_SYMBOL(dib9000_get_component_bus_interface); struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) @@ -1688,7 +1677,6 @@ struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000 struct dib9000_state *st = fe->demodulator_priv; return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); } - EXPORT_SYMBOL(dib9000_get_i2c_master); int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) @@ -1698,7 +1686,6 @@ int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) st->i2c.i2c_adap = i2c; return 0; } - EXPORT_SYMBOL(dib9000_set_i2c_adapter); static int dib9000_cfg_gpio(struct dib9000_state *st, u8 num, u8 dir, u8 val) @@ -1723,8 +1710,8 @@ int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) struct dib9000_state *state = fe->demodulator_priv; return dib9000_cfg_gpio(state, num, dir, val); } - EXPORT_SYMBOL(dib9000_set_gpio); + int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) { struct dib9000_state *state = fe->demodulator_priv; @@ -1734,15 +1721,14 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) dprintk("PID filter enabled %d", onoff); return dib9000_write_word(state, 294 + 1, val); } - EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl); + int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) { struct dib9000_state *state = fe->demodulator_priv; dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); return dib9000_write_word(state, 300 + 1 + id, onoff ? (1 << 13) | pid : 0); } - EXPORT_SYMBOL(dib9000_fw_pid_filter); int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) @@ -1750,7 +1736,6 @@ int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) struct dib9000_state *state = fe->demodulator_priv; return dib9000_fw_init(state); } - EXPORT_SYMBOL(dib9000_firmware_post_pll_init); static void dib9000_release(struct dvb_frontend *demod) @@ -1758,7 +1743,7 @@ static void dib9000_release(struct dvb_frontend *demod) struct dib9000_state *st = demod->demodulator_priv; u8 index_frontend; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) dvb_frontend_detach(st->fe[index_frontend]); DibFreeLock(&state->platform.risc.mbx_if_lock); @@ -1784,7 +1769,7 @@ static int dib9000_sleep(struct dvb_frontend *fe) u8 index_frontend; int ret; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + 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) return ret; @@ -1805,23 +1790,32 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par fe_status_t stat; int ret; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + 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); if (stat & FE_HAS_SYNC) { dprintk("TPS lock on the slave%i", index_frontend); /* synchronize the cache with the other frontends */ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], fep); - for (sub_index_frontend=0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) { + for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); + sub_index_frontend++) { if (sub_index_frontend != index_frontend) { - state->fe[sub_index_frontend]->dtv_property_cache.modulation = state->fe[index_frontend]->dtv_property_cache.modulation; - state->fe[sub_index_frontend]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; - state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode; - state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval; - state->fe[sub_index_frontend]->dtv_property_cache.hierarchy = state->fe[index_frontend]->dtv_property_cache.hierarchy; - state->fe[sub_index_frontend]->dtv_property_cache.code_rate_HP = state->fe[index_frontend]->dtv_property_cache.code_rate_HP; - state->fe[sub_index_frontend]->dtv_property_cache.code_rate_LP = state->fe[index_frontend]->dtv_property_cache.code_rate_LP; - state->fe[sub_index_frontend]->dtv_property_cache.rolloff = state->fe[index_frontend]->dtv_property_cache.rolloff; + state->fe[sub_index_frontend]->dtv_property_cache.modulation = + state->fe[index_frontend]->dtv_property_cache.modulation; + state->fe[sub_index_frontend]->dtv_property_cache.inversion = + state->fe[index_frontend]->dtv_property_cache.inversion; + state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = + state->fe[index_frontend]->dtv_property_cache.transmission_mode; + state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = + state->fe[index_frontend]->dtv_property_cache.guard_interval; + state->fe[sub_index_frontend]->dtv_property_cache.hierarchy = + state->fe[index_frontend]->dtv_property_cache.hierarchy; + state->fe[sub_index_frontend]->dtv_property_cache.code_rate_HP = + state->fe[index_frontend]->dtv_property_cache.code_rate_HP; + state->fe[sub_index_frontend]->dtv_property_cache.code_rate_LP = + state->fe[index_frontend]->dtv_property_cache.code_rate_LP; + state->fe[sub_index_frontend]->dtv_property_cache.rolloff = + state->fe[index_frontend]->dtv_property_cache.rolloff; } } return 0; @@ -1834,7 +1828,7 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par return ret; /* synchronize the cache with the other frontends */ - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion; state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode; state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval; @@ -1894,14 +1888,14 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par /* set the master status */ if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO || - fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || fep->u.ofdm.constellation == QAM_AUTO || fep->u.ofdm.code_rate_HP == FEC_AUTO) { + fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || fep->u.ofdm.constellation == QAM_AUTO || fep->u.ofdm.code_rate_HP == FEC_AUTO) { /* no channel specified, autosearch the channel */ state->channel_status.status = CHANNEL_STATUS_PARAMETERS_UNKNOWN; } else state->channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; /* set mode and status for the different frontends */ - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { dib9000_fw_set_diversity_in(state->fe[index_frontend], 1); /* synchronization of the cache */ @@ -1915,11 +1909,11 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par } /* actual tune */ - exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ + exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ index_frontend_success = 0; do { sleep_time = dib9000_fw_tune(state->fe[0], NULL); - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend], NULL); if (sleep_time == FE_CALLBACK_TIME_NEVER) sleep_time = sleep_time_slave; @@ -1934,23 +1928,23 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par nbr_pending = 0; exit_condition = 0; index_frontend_success = 0; - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { frontend_status = -dib9000_get_status(state->fe[index_frontend]); if (frontend_status > -FE_STATUS_TUNE_PENDING) { - exit_condition = 2; /* tune success */ + exit_condition = 2; /* tune success */ index_frontend_success = index_frontend; break; } if (frontend_status == -FE_STATUS_TUNE_PENDING) - nbr_pending++; /* some frontends are still tuning */ + nbr_pending++; /* some frontends are still tuning */ } if ((exit_condition != 2) && (nbr_pending == 0)) - exit_condition = 1; /* if all tune are done and no success, exit: tune failed */ + exit_condition = 1; /* if all tune are done and no success, exit: tune failed */ } while (exit_condition == 0); /* check the tune result */ - if (exit_condition == 1) { /* tune failed */ + if (exit_condition == 1) { /* tune failed */ dprintk("tune failed"); return 0; } @@ -1962,7 +1956,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par /* retune the other frontends with the found channel */ channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { /* only retune the frontends which was not tuned success */ if (index_frontend != index_frontend_success) { dib9000_set_channel_status(state->fe[index_frontend], &channel_status); @@ -1971,7 +1965,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par } do { sleep_time = FE_CALLBACK_TIME_NEVER; - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { if (index_frontend != index_frontend_success) { sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend], NULL); if (sleep_time == FE_CALLBACK_TIME_NEVER) @@ -1986,22 +1980,22 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par break; nbr_pending = 0; - for (index_frontend=0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { if (index_frontend != index_frontend_success) { frontend_status = -dib9000_get_status(state->fe[index_frontend]); if ((index_frontend != index_frontend_success) && (frontend_status == -FE_STATUS_TUNE_PENDING)) - nbr_pending++; /* some frontends are still tuning */ + nbr_pending++; /* some frontends are still tuning */ } } } while (nbr_pending != 0); /* set the output mode */ dib9000_fw_set_output_mode(state->fe[0], state->chip.d9.cfg.output_mode); - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY); /* turn off the diversity for the last frontend */ - dib9000_fw_set_diversity_in(state->fe[index_frontend-1], 0); + dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0); return 0; } @@ -2019,7 +2013,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) u8 index_frontend; u16 lock = 0, lock_slave = 0; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + 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]); lock = dib9000_read_word(state, 535); @@ -2063,7 +2057,7 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) u16 val; *strength = 0; - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + 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); if (val > 65535 - *strength) *strength = 65535; @@ -2127,7 +2121,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) u32 snr_master; snr_master = dib9000_get_snr(fe); - for (index_frontend=1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + 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]); if ((snr_master >> 16) != 0) { @@ -2161,7 +2155,7 @@ int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defaul struct i2c_device client = {.i2c_adap = i2c }; client.i2c_addr = default_addr + 16; - dib9000_i2c_write16(&client, 1796, 0x0); // select DVB-T output + dib9000_i2c_write16(&client, 1796, 0x0); for (k = no_of_demods - 1; k >= 0; k--) { /* designated i2c address */ @@ -2203,7 +2197,6 @@ int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defaul return 0; } - EXPORT_SYMBOL(dib9000_i2c_enumeration); int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) @@ -2232,7 +2225,7 @@ int dib9000_remove_slave_frontend(struct dvb_frontend *fe) while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) index_frontend++; if (index_frontend != 1) { - dprintk("remove slave fe %p (index %i)", state->fe[index_frontend-1], index_frontend-1); + dprintk("remove slave fe %p (index %i)", state->fe[index_frontend - 1], index_frontend - 1); state->fe[index_frontend] = NULL; return 0; } @@ -2242,7 +2235,7 @@ int dib9000_remove_slave_frontend(struct dvb_frontend *fe) } EXPORT_SYMBOL(dib9000_remove_slave_frontend); -struct dvb_frontend * dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) { struct dib9000_state *state = fe->demodulator_priv; @@ -2313,13 +2306,12 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c return fe; - component_bus_add_error: +component_bus_add_error: i2c_del_adapter(&st->tuner_adap); - error: +error: kfree(st); return NULL; } - EXPORT_SYMBOL(dib9000_attach); static struct dvb_frontend_ops dib9000_ops = { diff --git a/drivers/media/dvb/frontends/dib9000.h b/drivers/media/dvb/frontends/dib9000.h index 995e4bc..b5781a4 100644 --- a/drivers/media/dvb/frontends/dib9000.h +++ b/drivers/media/dvb/frontends/dib9000.h @@ -38,7 +38,7 @@ extern int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 ono extern int dib9000_firmware_post_pll_init(struct dvb_frontend *fe); extern int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); extern int dib9000_remove_slave_frontend(struct dvb_frontend *fe); -extern struct dvb_frontend * dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); +extern struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); extern struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe); extern int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c); extern int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed); @@ -103,7 +103,8 @@ int dib9000_remove_slave_frontend(struct dvb_frontend *fe) return -ENODEV; } -static inline struct dvb_frontend * dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) { +static inline struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +{ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c index 9bd95a9..f6938f9 100644 --- a/drivers/media/dvb/frontends/dibx000_common.c +++ b/drivers/media/dvb/frontends/dibx000_common.c @@ -26,8 +26,8 @@ static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg) u8 wb[2] = { reg >> 8, reg & 0xff }; u8 rb[2]; struct i2c_msg msg[2] = { - {.addr = mst->i2c_addr,.flags = 0,.buf = wb,.len = 2}, - {.addr = mst->i2c_addr,.flags = I2C_M_RD,.buf = rb,.len = 2}, + {.addr = mst->i2c_addr, .flags = 0, .buf = wb, .len = 2}, + {.addr = mst->i2c_addr, .flags = I2C_M_RD, .buf = rb, .len = 2}, }; if (i2c_transfer(mst->i2c_adap, msg, 2) != 2) @@ -38,10 +38,11 @@ static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg) static int dibx000_is_i2c_done(struct dibx000_i2c_master *mst) { - int i = 100; // max_i2c_polls; + int i = 100; u16 status; - while (((status = dibx000_read_word(mst, mst->base_reg + 2)) & 0x0100) == 0 && --i > 0); + while (((status = dibx000_read_word(mst, mst->base_reg + 2)) & 0x0100) == 0 && --i > 0) + ; /* i2c timed out */ if (i == 0) @@ -63,7 +64,7 @@ static int dibx000_master_i2c_write(struct dibx000_i2c_master *mst, struct i2c_m const u8 *b = msg->buf; while (txlen) { - dibx000_read_word(mst, mst->base_reg + 2); // reset fifo ptr + dibx000_read_word(mst, mst->base_reg + 2); len = txlen > 8 ? 8 : txlen; for (i = 0; i < len; i += 2) { @@ -72,14 +73,14 @@ static int dibx000_master_i2c_write(struct dibx000_i2c_master *mst, struct i2c_m data |= *b++; dibx000_write_word(mst, mst->base_reg, data); } - da = (((u8) (msg->addr)) << 9) | // addr - (1 << 8) | // master - (1 << 7) | // rq - (0 << 6) | // stop - (0 << 5) | // start - ((len & 0x7) << 2) | // nb 8 bytes == 0 here - (0 << 1) | // rw - (0 << 0); // irqen + da = (((u8) (msg->addr)) << 9) | + (1 << 8) | + (1 << 7) | + (0 << 6) | + (0 << 5) | + ((len & 0x7) << 2) | + (0 << 1) | + (0 << 0); if (txlen == msg->len) da |= 1 << 5; /* start */ @@ -105,14 +106,14 @@ static int dibx000_master_i2c_read(struct dibx000_i2c_master *mst, struct i2c_ms while (rxlen) { len = rxlen > 8 ? 8 : rxlen; - da = (((u8) (msg->addr)) << 9) | // addr - (1 << 8) | // master - (1 << 7) | // rq - (0 << 6) | // stop - (0 << 5) | // start - ((len & 0x7) << 2) | // nb - (1 << 1) | // rw - (0 << 0); // irqen + da = (((u8) (msg->addr)) << 9) | + (1 << 8) | + (1 << 7) | + (0 << 6) | + (0 << 5) | + ((len & 0x7) << 2) | + (1 << 1) | + (0 << 0); if (rxlen == msg->len) da |= 1 << 5; /* start */ @@ -174,15 +175,12 @@ static int dibx000_i2c_master_xfer_gpio12(struct i2c_adapter *i2c_adap, struct i int ret = 0; dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_1_2); - for (msg_index = 0; msg_index Date: Sun, 16 Jan 2011 13:01:20 -0200 Subject: [media] dib7000p: Fix 4-byte wrong alignments for some case statements ERROR: switch and case should be at the same indent switch (ch->u.ofdm.transmission_mode) { [...] + case TRANSMISSION_MODE_4K: WARNING: please, no spaces at the start of a line + case TRANSMISSION_MODE_4K:$ ERROR: switch and case should be at the same indent switch (ch->u.ofdm.transmission_mode) { [...] + case TRANSMISSION_MODE_4K: WARNING: please, no spaces at the start of a line + case TRANSMISSION_MODE_4K:$ ERROR: switch and case should be at the same indent switch (ch->u.ofdm.transmission_mode) { [...] + case TRANSMISSION_MODE_4K: WARNING: please, no spaces at the start of a line + case TRANSMISSION_MODE_4K:$ Cc: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index b3ca3e2..900af60 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -880,7 +880,7 @@ static void dib7000p_set_channel(struct dib7000p_state *state, struct dvb_fronte case TRANSMISSION_MODE_2K: value |= (0 << 7); break; - case TRANSMISSION_MODE_4K: + case TRANSMISSION_MODE_4K: value |= (2 << 7); break; default: @@ -1207,7 +1207,7 @@ static int dib7000p_tune(struct dvb_frontend *demod, struct dvb_frontend_paramet case TRANSMISSION_MODE_2K: tmp |= (2 << 12); break; - case TRANSMISSION_MODE_4K: + case TRANSMISSION_MODE_4K: tmp |= (3 << 12); break; default: @@ -1223,7 +1223,7 @@ static int dib7000p_tune(struct dvb_frontend *demod, struct dvb_frontend_paramet case TRANSMISSION_MODE_2K: tmp |= 0x6; break; - case TRANSMISSION_MODE_4K: + case TRANSMISSION_MODE_4K: tmp |= 0x7; break; default: @@ -1325,7 +1325,7 @@ static int dib7000p_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_pa case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break; - /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */ + /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */ } switch (tps & 0x3) { -- cgit v0.10.2 From 2c2c441b10644a273ff82a43ec18034ec1dd4c4e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 16 Jan 2011 13:02:52 -0200 Subject: [media] dib8000: Fix some wrong alignments Something went wrong with the alignments for the if clause at line 2113. Instead of using one tab for alignments, it used 3 tabs, plus one space character. WARNING: suspect code indent for conditional statements (8, 33) + if ((state->fe[0]->dtv_property_cache.delivery_system != SYS_ISDBT) || [...] + int i = 80000; WARNING: suspect code indent for conditional statements (33, 41) + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000); WARNING: suspect code indent for conditional statements (33, 41) + do { + msleep(20); WARNING: suspect code indent for conditional statements (41, 49) + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (((tune_failed >> index_frontend) & 0x1) == 0) { WARNING: suspect code indent for conditional statements (49, 57) + if (((tune_failed >> index_frontend) & 0x1) == 0) { + found = dib8000_autosearch_irq(state->fe[index_frontend]); WARNING: suspect code indent for conditional statements (41, 49) + if ((nbr_pending == 0) && (exit_condition == 0)) + exit_condition = 1; Cc: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index 3961fed..3e20aa8 100644 --- a/drivers/media/dvb/frontends/dib8000.c +++ b/drivers/media/dvb/frontends/dib8000.c @@ -2134,54 +2134,54 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par ((state->fe[0]->dtv_property_cache.layer[1].segment_count == 0) || ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && ((state->fe[0]->dtv_property_cache.layer[2].segment_count == 0) || ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) { - int i = 80000; - u8 found = 0; - u8 tune_failed = 0; - - for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { - dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000); - dib8000_autosearch_start(state->fe[index_frontend]); - } - - do { - msleep(20); - nbr_pending = 0; - exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ - for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { - if (((tune_failed >> index_frontend) & 0x1) == 0) { - found = dib8000_autosearch_irq(state->fe[index_frontend]); - switch (found) { - case 0: /* tune pending */ - nbr_pending++; - break; - case 2: - dprintk("autosearch succeed on the frontend%i", index_frontend); - exit_condition = 2; - index_frontend_success = index_frontend; - break; - default: - dprintk("unhandled autosearch result"); - case 1: - dprintk("autosearch failed for the frontend%i", index_frontend); - break; - } - } - } - - /* if all tune are done and no success, exit: tune failed */ - if ((nbr_pending == 0) && (exit_condition == 0)) - exit_condition = 1; - } while ((exit_condition == 0) && i--); - - if (exit_condition == 1) { /* tune failed */ - dprintk("tune failed"); - return 0; - } - - dprintk("tune success on frontend%i", index_frontend_success); - - dib8000_get_frontend(fe, fep); - } + int i = 80000; + u8 found = 0; + u8 tune_failed = 0; + + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000); + dib8000_autosearch_start(state->fe[index_frontend]); + } + + do { + msleep(20); + nbr_pending = 0; + exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (((tune_failed >> index_frontend) & 0x1) == 0) { + found = dib8000_autosearch_irq(state->fe[index_frontend]); + switch (found) { + case 0: /* tune pending */ + nbr_pending++; + break; + case 2: + dprintk("autosearch succeed on the frontend%i", index_frontend); + exit_condition = 2; + index_frontend_success = index_frontend; + break; + default: + dprintk("unhandled autosearch result"); + case 1: + dprintk("autosearch failed for the frontend%i", index_frontend); + break; + } + } + } + + /* if all tune are done and no success, exit: tune failed */ + if ((nbr_pending == 0) && (exit_condition == 0)) + exit_condition = 1; + } while ((exit_condition == 0) && i--); + + if (exit_condition == 1) { /* tune failed */ + dprintk("tune failed"); + return 0; + } + + dprintk("tune success on frontend%i", index_frontend_success); + + dib8000_get_frontend(fe, fep); + } for (index_frontend = 0, ret = 0; (ret >= 0) && (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) ret = dib8000_tune(state->fe[index_frontend]); -- cgit v0.10.2 From 5bd0dc2d8a0db2f2e119ea50d07bf49e5e038f12 Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Mon, 10 Jan 2011 06:36:09 -0300 Subject: [media] stv090x: make sleep/wakeup specific to the demod path The STV0900 features two demodulator paths in one chip. Thus it is not possible to use the generic power off function of the chip when sending one of them to standby. The other path will stop working in that case. The sleep function now switches off functionality specific to the demod path. The global stuff is only switched off, when both paths are in sleep mode. The wakeup function always turns on the global functionality and then works specific to the path. Signed-off-by: Andreas Regel Signed-off-by: Oliver Endriss Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index dba2c04..e26bfd7 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -3846,6 +3846,7 @@ static int stv090x_sleep(struct dvb_frontend *fe) { struct stv090x_state *state = fe->demodulator_priv; u32 reg; + u8 full_standby = 0; if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; @@ -3858,24 +3859,119 @@ static int stv090x_sleep(struct dvb_frontend *fe) if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; - dprintk(FE_DEBUG, 1, "Set %s to sleep", - state->device == STV0900 ? "STV0900" : "STV0903"); + dprintk(FE_DEBUG, 1, "Set %s(%d) to sleep", + state->device == STV0900 ? "STV0900" : "STV0903", + state->demod); - reg = stv090x_read_reg(state, STV090x_SYNTCTRL); - STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01); - if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) - goto err; + mutex_lock(&state->internal->demod_lock); - reg = stv090x_read_reg(state, STV090x_TSTTNR1); - STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0); - if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) - goto err; + switch (state->demod) { + case STV090x_DEMODULATOR_0: + /* power off ADC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) + goto err; + /* power off DiSEqC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR2); + STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) + goto err; + + /* check whether path 2 is already sleeping, that is when + ADC2 is off */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + if (STV090x_GETFIELD(reg, ADC2_PON_FIELD) == 0) + full_standby = 1; + + /* stop clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 1); + /* ADC 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 1); + /* FEC clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 1); + /* viterbi 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 1); + /* TS clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + case STV090x_DEMODULATOR_1: + /* power off ADC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + STV090x_SETFIELD(reg, ADC2_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) + goto err; + /* power off DiSEqC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR4); + STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) + goto err; + + /* check whether path 1 is already sleeping, that is when + ADC1 is off */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + if (STV090x_GETFIELD(reg, ADC1_PON_FIELD) == 0) + full_standby = 1; + + /* stop clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 1); + /* ADC 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 1); + /* FEC clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 1); + /* viterbi 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 1); + /* TS clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + + default: + dprintk(FE_ERROR, 1, "Wrong demodulator!"); + break; + } + + if (full_standby) { + /* general power off */ + reg = stv090x_read_reg(state, STV090x_SYNTCTRL); + STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) + goto err; + } + + mutex_unlock(&state->internal->demod_lock); return 0; err_gateoff: stv090x_i2c_gate_ctrl(state, 0); err: + mutex_unlock(&state->internal->demod_lock); dprintk(FE_ERROR, 1, "I/O error"); return -1; } @@ -3885,21 +3981,94 @@ static int stv090x_wakeup(struct dvb_frontend *fe) struct stv090x_state *state = fe->demodulator_priv; u32 reg; - dprintk(FE_DEBUG, 1, "Wake %s from standby", - state->device == STV0900 ? "STV0900" : "STV0903"); + dprintk(FE_DEBUG, 1, "Wake %s(%d) from standby", + state->device == STV0900 ? "STV0900" : "STV0903", + state->demod); + + mutex_lock(&state->internal->demod_lock); + /* general power on */ reg = stv090x_read_reg(state, STV090x_SYNTCTRL); STV090x_SETFIELD(reg, STANDBY_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) goto err; - reg = stv090x_read_reg(state, STV090x_TSTTNR1); - STV090x_SETFIELD(reg, ADC1_PON_FIELD, 1); - if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) - goto err; + switch (state->demod) { + case STV090x_DEMODULATOR_0: + /* power on ADC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + STV090x_SETFIELD(reg, ADC1_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) + goto err; + /* power on DiSEqC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR2); + STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) + goto err; + + /* activate clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 0); + /* ADC 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 0); + /* FEC clock */ + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 0); + /* viterbi 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 0); + /* TS clock */ + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + case STV090x_DEMODULATOR_1: + /* power on ADC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + STV090x_SETFIELD(reg, ADC2_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) + goto err; + /* power on DiSEqC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR4); + STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) + goto err; + + /* activate clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 0); + /* ADC 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 0); + /* FEC clock */ + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 0); + /* viterbi 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 0); + /* TS clock */ + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + + default: + dprintk(FE_ERROR, 1, "Wrong demodulator!"); + break; + } + + mutex_unlock(&state->internal->demod_lock); return 0; err: + mutex_unlock(&state->internal->demod_lock); dprintk(FE_ERROR, 1, "I/O error"); return -1; } @@ -4622,20 +4791,10 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, mutex_init(&state->internal->demod_lock); mutex_init(&state->internal->tuner_lock); - if (stv090x_sleep(&state->frontend) < 0) { - dprintk(FE_ERROR, 1, "Error putting device to sleep"); - goto error; - } - if (stv090x_setup(&state->frontend) < 0) { dprintk(FE_ERROR, 1, "Error setting up device"); goto error; } - if (stv090x_wakeup(&state->frontend) < 0) { - dprintk(FE_ERROR, 1, "Error waking device"); - goto error; - } - dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", state->device == STV0900 ? "STV0900" : "STV0903", demod, -- cgit v0.10.2 From f790bdd00da937845b38467abfebdea09acfea0e Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:10 -0300 Subject: [media] stv090x: Optional external lock routine Card driver may supply its own lock routine now. Signed-off-by: Oliver Endriss Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index e26bfd7..eccad06 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -767,8 +767,12 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) * In case of any error, the lock is unlocked and exit within the * relevant operations themselves. */ - if (enable) - mutex_lock(&state->internal->tuner_lock); + if (enable) { + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 1); + else + mutex_lock(&state->internal->tuner_lock); + } reg = STV090x_READ_DEMOD(state, I2CRPT); if (enable) { @@ -784,13 +788,20 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) goto err; } - if (!enable) - mutex_unlock(&state->internal->tuner_lock); + if (!enable) { + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 0); + else + mutex_unlock(&state->internal->tuner_lock); + } return 0; err: dprintk(FE_ERROR, 1, "I/O error"); - mutex_unlock(&state->internal->tuner_lock); + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 0); + else + mutex_unlock(&state->internal->tuner_lock); return -1; } diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h index a3fefa7..29cdc2b 100644 --- a/drivers/media/dvb/frontends/stv090x.h +++ b/drivers/media/dvb/frontends/stv090x.h @@ -100,6 +100,7 @@ struct stv090x_config { int (*tuner_get_bbgain) (struct dvb_frontend *fe, u32 *gain); int (*tuner_set_refclk) (struct dvb_frontend *fe, u32 refclk); int (*tuner_get_status) (struct dvb_frontend *fe, u32 *status); + void (*tuner_i2c_lock) (struct dvb_frontend *fe, int lock); }; #if defined(CONFIG_DVB_STV090x) || (defined(CONFIG_DVB_STV090x_MODULE) && defined(MODULE)) -- cgit v0.10.2 From 09961aae7b3ea8640051e464b346ed3984521de8 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:11 -0300 Subject: [media] ngene: Firmware 18 support Small modifications for firmware 18. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index dc073bd..cd5b1f3 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1257,6 +1257,10 @@ static int ngene_load_firm(struct ngene *dev) fw_name = "ngene_17.fw"; dev->cmd_timeout_workaround = true; break; + case 18: + size = 0; + fw_name = "ngene_18.fw"; + break; } if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) { @@ -1266,6 +1270,8 @@ static int ngene_load_firm(struct ngene *dev) ": Copy %s to your hotplug directory!\n", fw_name); return -1; } + if (size == 0) + size = fw->size; if (size != fw->size) { printk(KERN_ERR DEVICE_NAME ": Firmware %s has invalid size!", fw_name); @@ -1365,7 +1371,7 @@ static int ngene_start(struct ngene *dev) if (stat < 0) goto fail; - if (dev->card_info->fw_version == 17) { + if (dev->card_info->fw_version >= 17) { u8 tsin4_config[6] = { 3072 / 64, 3072 / 64, 0, 3072 / 64, 3072 / 64, 0}; u8 default_config[6] = { @@ -1374,7 +1380,7 @@ static int ngene_start(struct ngene *dev) if (dev->card_info->io_type[3] == NGENE_IO_TSIN) bconf = tsin4_config; - dprintk(KERN_DEBUG DEVICE_NAME ": FW 17 buffer config\n"); + dprintk(KERN_DEBUG DEVICE_NAME ": FW 17+ buffer config\n"); stat = ngene_command_config_free_buf(dev, bconf); } else { int bconf = BUFFER_CONFIG_4422; -- cgit v0.10.2 From ac9725d224544954a8d3413a2a66bdf49f735d8b Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:12 -0300 Subject: [media] ngene: Fixes for TS input over I2S Fix TS input over I2S. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index cd5b1f3..8764647 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -143,7 +143,7 @@ static void demux_tasklet(unsigned long data) } } else { if (chan->HWState == HWSTATE_RUN) { - u32 Flags = 0; + u32 Flags = chan->DataFormatFlags; IBufferExchange *exch1 = chan->pBufferExchange; IBufferExchange *exch2 = chan->pBufferExchange2; if (Cur->ngeneBuffer.SR.Flags & 0x01) @@ -474,7 +474,7 @@ static u8 SPDIFConfiguration[10] = { /* Set NGENE I2S Config to transport stream compatible mode */ -static u8 TS_I2SConfiguration[4] = { 0x3E, 0x1A, 0x00, 0x00 }; /*3e 18 00 00 ?*/ +static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 }; static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x20, 0x00, 0x00 }; diff --git a/drivers/media/dvb/ngene/ngene-dvb.c b/drivers/media/dvb/ngene/ngene-dvb.c index 3832e59..8bb1593 100644 --- a/drivers/media/dvb/ngene/ngene-dvb.c +++ b/drivers/media/dvb/ngene/ngene-dvb.c @@ -65,8 +65,11 @@ void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) struct ngene_channel *chan = priv; - if (chan->users > 0) + if (chan->users > 0) { + if (flags & DF_SWAP32) + swap_buffer(buf, len); dvb_dmx_swfilter(&chan->demux, buf, len); + } return NULL; } -- cgit v0.10.2 From 5fec18574fec3eaffcab596c1a4e943e2d413cc3 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:13 -0300 Subject: [media] ngene: Support up to 4 tuners Support up to 4 tuners for cineS2 v5, duoflex & mystique v2. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c index 4692a41..5e6b85c 100644 --- a/drivers/media/dvb/ngene/ngene-cards.c +++ b/drivers/media/dvb/ngene/ngene-cards.c @@ -48,20 +48,27 @@ static int tuner_attach_stv6110(struct ngene_channel *chan) { + struct i2c_adapter *i2c; struct stv090x_config *feconf = (struct stv090x_config *) chan->dev->card_info->fe_config[chan->number]; struct stv6110x_config *tunerconf = (struct stv6110x_config *) chan->dev->card_info->tuner_config[chan->number]; struct stv6110x_devctl *ctl; - ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, - &chan->i2c_adapter); + /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ + if (chan->number < 2) + i2c = &chan->dev->channel[0].i2c_adapter; + else + i2c = &chan->dev->channel[1].i2c_adapter; + + ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c); if (ctl == NULL) { printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n"); return -ENODEV; } feconf->tuner_init = ctl->tuner_init; + feconf->tuner_sleep = ctl->tuner_sleep; feconf->tuner_set_mode = ctl->tuner_set_mode; feconf->tuner_set_frequency = ctl->tuner_set_frequency; feconf->tuner_get_frequency = ctl->tuner_get_frequency; @@ -78,20 +85,31 @@ static int tuner_attach_stv6110(struct ngene_channel *chan) static int demod_attach_stv0900(struct ngene_channel *chan) { + struct i2c_adapter *i2c; struct stv090x_config *feconf = (struct stv090x_config *) chan->dev->card_info->fe_config[chan->number]; - chan->fe = dvb_attach(stv090x_attach, - feconf, - &chan->i2c_adapter, - chan->number == 0 ? STV090x_DEMODULATOR_0 : - STV090x_DEMODULATOR_1); + /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ + /* Note: Both adapters share the same i2c bus, but the demod */ + /* driver requires that each demod has its own i2c adapter */ + if (chan->number < 2) + i2c = &chan->dev->channel[0].i2c_adapter; + else + i2c = &chan->dev->channel[1].i2c_adapter; + + chan->fe = dvb_attach(stv090x_attach, feconf, i2c, + (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0 + : STV090x_DEMODULATOR_1); if (chan->fe == NULL) { printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n"); return -ENODEV; } - if (!dvb_attach(lnbh24_attach, chan->fe, &chan->i2c_adapter, 0, + /* store channel info */ + if (feconf->tuner_i2c_lock) + chan->fe->analog_demod_priv = chan; + + if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0, 0, chan->dev->card_info->lnb[chan->number])) { printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); dvb_frontend_detach(chan->fe); @@ -101,6 +119,71 @@ static int demod_attach_stv0900(struct ngene_channel *chan) return 0; } +static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock) +{ + struct ngene_channel *chan = fe->analog_demod_priv; + + if (lock) + down(&chan->dev->pll_mutex); + else + up(&chan->dev->pll_mutex); +} + +static int cineS2_probe(struct ngene_channel *chan) +{ + struct i2c_adapter *i2c; + struct stv090x_config *fe_conf; + u8 buf[3]; + struct i2c_msg i2c_msg = { .flags = 0, .buf = buf }; + int rc; + + /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ + if (chan->number < 2) + i2c = &chan->dev->channel[0].i2c_adapter; + else + i2c = &chan->dev->channel[1].i2c_adapter; + + fe_conf = chan->dev->card_info->fe_config[chan->number]; + i2c_msg.addr = fe_conf->address; + + /* probe demod */ + i2c_msg.len = 2; + buf[0] = 0xf1; + buf[1] = 0x00; + rc = i2c_transfer(i2c, &i2c_msg, 1); + if (rc != 1) + return -ENODEV; + + /* demod found, attach it */ + rc = demod_attach_stv0900(chan); + if (rc < 0 || chan->number < 2) + return rc; + + /* demod #2: reprogram outputs DPN1 & DPN2 */ + i2c_msg.len = 3; + buf[0] = 0xf1; + switch (chan->number) { + case 2: + buf[1] = 0x5c; + buf[2] = 0xc2; + break; + case 3: + buf[1] = 0x61; + buf[2] = 0xcc; + break; + default: + return -ENODEV; + } + rc = i2c_transfer(i2c, &i2c_msg, 1); + if (rc != 1) { + printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n"); + return -EIO; + } + + return 0; +} + + static struct lgdt330x_config aver_m780 = { .demod_address = 0xb2 >> 1, .demod_chip = LGDT3303, @@ -151,6 +234,29 @@ static struct stv090x_config fe_cineS2 = { .adc2_range = STV090x_ADC_1Vpp, .diseqc_envelope_mode = true, + + .tuner_i2c_lock = cineS2_tuner_i2c_lock, +}; + +static struct stv090x_config fe_cineS2_2 = { + .device = STV0900, + .demod_mode = STV090x_DUAL, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 27000000, + .address = 0x69, + + .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + + .repeater_level = STV090x_RPTLEVEL_16, + + .adc1_range = STV090x_ADC_1Vpp, + .adc2_range = STV090x_ADC_1Vpp, + + .diseqc_envelope_mode = true, + + .tuner_i2c_lock = cineS2_tuner_i2c_lock, }; static struct stv6110x_config tuner_cineS2_0 = { @@ -175,7 +281,8 @@ static struct ngene_info ngene_info_cineS2 = { .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, .lnb = {0x0b, 0x08}, .tsf = {3, 3}, - .fw_version = 15, + .fw_version = 18, + .msi_supported = true, }; static struct ngene_info ngene_info_satixS2 = { @@ -188,46 +295,51 @@ static struct ngene_info ngene_info_satixS2 = { .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, .lnb = {0x0b, 0x08}, .tsf = {3, 3}, - .fw_version = 15, + .fw_version = 18, + .msi_supported = true, }; static struct ngene_info ngene_info_satixS2v2 = { .type = NGENE_SIDEWINDER, .name = "Mystique SaTiX-S2 Dual (v2)", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, - .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, - .fe_config = {&fe_cineS2, &fe_cineS2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0a, 0x08}, + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0a, 0x08, 0x0b, 0x09}, .tsf = {3, 3}, - .fw_version = 15, + .fw_version = 18, + .msi_supported = true, }; static struct ngene_info ngene_info_cineS2v5 = { .type = NGENE_SIDEWINDER, .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, - .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, - .fe_config = {&fe_cineS2, &fe_cineS2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0a, 0x08}, + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0a, 0x08, 0x0b, 0x09}, .tsf = {3, 3}, - .fw_version = 15, + .fw_version = 18, + .msi_supported = true, }; + static struct ngene_info ngene_info_duoFlexS2 = { .type = NGENE_SIDEWINDER, .name = "Digital Devices DuoFlex S2 miniPCIe", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, - .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, - .fe_config = {&fe_cineS2, &fe_cineS2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0a, 0x08}, + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0a, 0x08, 0x0b, 0x09}, .tsf = {3, 3}, - .fw_version = 15, + .fw_version = 18, + .msi_supported = true, }; static struct ngene_info ngene_info_m780 = { -- cgit v0.10.2 From 8a484719c790772bc51c87c56323611752361bef Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:14 -0300 Subject: [media] ngene: Clean-up driver initialisation (part 1) If tuner initialisation failed, the frontend node was not removed. When the frontend was opened, a kernel oops occurred... This is the first step to improve error handling during initialisation. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c index 5e6b85c..fc93d10 100644 --- a/drivers/media/dvb/ngene/ngene-cards.c +++ b/drivers/media/dvb/ngene/ngene-cards.c @@ -113,6 +113,7 @@ static int demod_attach_stv0900(struct ngene_channel *chan) 0, chan->dev->card_info->lnb[chan->number])) { printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); dvb_frontend_detach(chan->fe); + chan->fe = NULL; return -ENODEV; } diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 8764647..831b758 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1482,23 +1482,32 @@ static int init_channel(struct ngene_channel *chan) if (io & NGENE_IO_TSIN) { chan->fe = NULL; - if (ni->demod_attach[nr]) - ni->demod_attach[nr](chan); + if (ni->demod_attach[nr]) { + ret = ni->demod_attach[nr](chan); + if (ret < 0) + goto err_fe; + } + if (chan->fe && ni->tuner_attach[nr]) { + ret = ni->tuner_attach[nr](chan); + if (ret < 0) + goto err_fe; + } if (chan->fe) { - if (dvb_register_frontend(adapter, chan->fe) < 0) { - if (chan->fe->ops.release) - chan->fe->ops.release(chan->fe); - chan->fe = NULL; - } + if (dvb_register_frontend(adapter, chan->fe) < 0) + goto err_fe; } - if (chan->fe && ni->tuner_attach[nr]) - if (ni->tuner_attach[nr] (chan) < 0) { - printk(KERN_ERR DEVICE_NAME - ": Tuner attach failed on channel %d!\n", - nr); - } } return ret; + +err_fe: + if (chan->fe) { + dvb_frontend_detach(chan->fe); + chan->fe = NULL; + } +/* FIXME: this causes an oops... */ +/* release_channel(chan); */ +/* return ret; */ + return 0; } static int init_channels(struct ngene *dev) -- cgit v0.10.2 From 0f0b270f905bbb0c8e75988ceaf10ff9a401e712 Mon Sep 17 00:00:00 2001 From: Ralph Metzler Date: Mon, 10 Jan 2011 06:36:15 -0300 Subject: [media] ngene: CXD2099AR Common Interface driver Driver for the Common Interface Controller CXD2099AR. Supports the CI of the cineS2 DVB-S2. For now, data is passed through '/dev/dvb/adapterX/sec0': - Encrypted data must be written to 'sec0'. - Decrypted data can be read from 'sec0'. - Setup the CAM using device 'ca0'. Signed-off-by: Ralph Metzler Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile index 0608aab..00d12d6 100644 --- a/drivers/media/dvb/ngene/Makefile +++ b/drivers/media/dvb/ngene/Makefile @@ -2,7 +2,7 @@ # Makefile for the nGene device driver # -ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o +ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o cxd2099.o obj-$(CONFIG_DVB_NGENE) += ngene.o diff --git a/drivers/media/dvb/ngene/cxd2099.c b/drivers/media/dvb/ngene/cxd2099.c new file mode 100644 index 0000000..b49186c --- /dev/null +++ b/drivers/media/dvb/ngene/cxd2099.c @@ -0,0 +1,574 @@ +/* + * cxd2099.c: Driver for the CXD2099AR Common Interface Controller + * + * Copyright (C) 2010 DigitalDevices UG + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cxd2099.h" + +#define MAX_BUFFER_SIZE 248 + +struct cxd { + struct dvb_ca_en50221 en; + + struct i2c_adapter *i2c; + u8 adr; + u8 regs[0x23]; + u8 lastaddress; + u8 clk_reg_f; + u8 clk_reg_b; + int mode; + u32 bitrate; + int ready; + int dr; + int slot_stat; + + u8 amem[1024]; + int amem_read; + + int cammode; + struct mutex lock; +}; + +static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 data) +{ + u8 m[2] = {reg, data}; + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", + reg, adr); + return -1; + } + return 0; +} + +static int i2c_write(struct i2c_adapter *adapter, u8 adr, + u8 *data, u8 len) +{ + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR "Failed to write to I2C!\n"); + return -1; + } + return 0; +} + +static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 *val) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = ®, .len = 1 }, + {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1 } }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + printk(KERN_ERR "error in i2c_read_reg\n"); + return -1; + } + return 0; +} + +static int i2c_read(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 *data, u8 n) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = ®, .len = 1 }, + {.addr = adr, .flags = I2C_M_RD, + .buf = data, .len = n } }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + printk(KERN_ERR "error in i2c_read\n"); + return -1; + } + return 0; +} + +static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n) +{ + int status; + + status = i2c_write_reg(ci->i2c, ci->adr, 0, adr); + if (!status) { + ci->lastaddress = adr; + status = i2c_read(ci->i2c, ci->adr, 1, data, n); + } + return status; +} + +static int read_reg(struct cxd *ci, u8 reg, u8 *val) +{ + return read_block(ci, reg, val, 1); +} + + +static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) + status = i2c_read(ci->i2c, ci->adr, 3, data, n); + return status; +} + +static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) { + u8 buf[256] = {3}; + memcpy(buf+1, data, n); + status = i2c_write(ci->i2c, ci->adr, buf, n+1); + } + return status; +} + +static int read_io(struct cxd *ci, u16 address, u8 *val) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) + status = i2c_read(ci->i2c, ci->adr, 3, val, 1); + return status; +} + +static int write_io(struct cxd *ci, u16 address, u8 val) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + u8 buf[2] = { 3, val }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) + status = i2c_write(ci->i2c, ci->adr, buf, 2); + + return status; +} + + +static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask) +{ + int status; + + status = i2c_write_reg(ci->i2c, ci->adr, 0, reg); + if (!status && reg >= 6 && reg <= 8 && mask != 0xff) + status = i2c_read_reg(ci->i2c, ci->adr, 1, &ci->regs[reg]); + ci->regs[reg] = (ci->regs[reg]&(~mask))|val; + if (!status) { + ci->lastaddress = reg; + status = i2c_write_reg(ci->i2c, ci->adr, 1, ci->regs[reg]); + } + if (reg == 0x20) + ci->regs[reg] &= 0x7f; + return status; +} + +static int write_reg(struct cxd *ci, u8 reg, u8 val) +{ + return write_regm(ci, reg, val, 0xff); +} + +#ifdef BUFFER_MODE +static int write_block(struct cxd *ci, u8 adr, u8 *data, int n) +{ + int status; + u8 buf[256] = {1}; + + status = i2c_write_reg(ci->i2c, ci->adr, 0, adr); + if (!status) { + ci->lastaddress = adr; + memcpy(buf+1, data, n); + status = i2c_write(ci->i2c, ci->adr, buf, n+1); + } + return status; +} +#endif + +static void set_mode(struct cxd *ci, int mode) +{ + if (mode == ci->mode) + return; + + switch (mode) { + case 0x00: /* IO mem */ + write_regm(ci, 0x06, 0x00, 0x07); + break; + case 0x01: /* ATT mem */ + write_regm(ci, 0x06, 0x02, 0x07); + break; + default: + break; + } + ci->mode = mode; +} + +static void cam_mode(struct cxd *ci, int mode) +{ + if (mode == ci->cammode) + return; + + switch (mode) { + case 0x00: + write_regm(ci, 0x20, 0x80, 0x80); + break; + case 0x01: + printk(KERN_INFO "enable cam buffer mode\n"); + /* write_reg(ci, 0x0d, 0x00); */ + /* write_reg(ci, 0x0e, 0x01); */ + write_regm(ci, 0x08, 0x40, 0x40); + /* read_reg(ci, 0x12, &dummy); */ + write_regm(ci, 0x08, 0x80, 0x80); + break; + default: + break; + } + ci->cammode = mode; +} + + + +#define CHK_ERROR(s) if ((status = s)) break + +static int init(struct cxd *ci) +{ + int status; + + mutex_lock(&ci->lock); + ci->mode = -1; + do { + CHK_ERROR(write_reg(ci, 0x00, 0x00)); + CHK_ERROR(write_reg(ci, 0x01, 0x00)); + CHK_ERROR(write_reg(ci, 0x02, 0x10)); + CHK_ERROR(write_reg(ci, 0x03, 0x00)); + CHK_ERROR(write_reg(ci, 0x05, 0xFF)); + CHK_ERROR(write_reg(ci, 0x06, 0x1F)); + CHK_ERROR(write_reg(ci, 0x07, 0x1F)); + CHK_ERROR(write_reg(ci, 0x08, 0x28)); + CHK_ERROR(write_reg(ci, 0x14, 0x20)); + + CHK_ERROR(write_reg(ci, 0x09, 0x4D)); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */ + CHK_ERROR(write_reg(ci, 0x0A, 0xA7)); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */ + + /* Sync detector */ + CHK_ERROR(write_reg(ci, 0x0B, 0x33)); + CHK_ERROR(write_reg(ci, 0x0C, 0x33)); + + CHK_ERROR(write_regm(ci, 0x14, 0x00, 0x0F)); + CHK_ERROR(write_reg(ci, 0x15, ci->clk_reg_b)); + CHK_ERROR(write_regm(ci, 0x16, 0x00, 0x0F)); + CHK_ERROR(write_reg(ci, 0x17, ci->clk_reg_f)); + + CHK_ERROR(write_reg(ci, 0x20, 0x28)); /* Integer Divider, Falling Edge, Internal Sync, */ + CHK_ERROR(write_reg(ci, 0x21, 0x00)); /* MCLKI = TICLK/8 */ + CHK_ERROR(write_reg(ci, 0x22, 0x07)); /* MCLKI = TICLK/8 */ + + + CHK_ERROR(write_regm(ci, 0x20, 0x80, 0x80)); /* Reset CAM state machine */ + + CHK_ERROR(write_regm(ci, 0x03, 0x02, 02)); /* Enable IREQA Interrupt */ + CHK_ERROR(write_reg(ci, 0x01, 0x04)); /* Enable CD Interrupt */ + CHK_ERROR(write_reg(ci, 0x00, 0x31)); /* Enable TS1,Hot Swap,Slot A */ + CHK_ERROR(write_regm(ci, 0x09, 0x08, 0x08)); /* Put TS in bypass */ + ci->cammode = -1; +#ifdef BUFFER_MODE + cam_mode(ci, 0); +#endif + } while (0); + mutex_unlock(&ci->lock); + + return 0; +} + + +static int read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address) +{ + struct cxd *ci = ca->data; + u8 val; + mutex_lock(&ci->lock); + set_mode(ci, 1); + read_pccard(ci, address, &val, 1); + mutex_unlock(&ci->lock); + return val; +} + + +static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, + int address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 1); + write_pccard(ci, address, &value, 1); + mutex_unlock(&ci->lock); + return 0; +} + +static int read_cam_control(struct dvb_ca_en50221 *ca, + int slot, u8 address) +{ + struct cxd *ci = ca->data; + u8 val; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + read_io(ci, address, &val); + mutex_unlock(&ci->lock); + return val; +} + +static int write_cam_control(struct dvb_ca_en50221 *ca, int slot, + u8 address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + write_io(ci, address, value); + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + cam_mode(ci, 0); + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + write_reg(ci, 0x00, 0x31); + write_regm(ci, 0x20, 0x80, 0x80); + write_reg(ci, 0x03, 0x02); + ci->ready = 0; + ci->mode = -1; + { + int i; + for (i = 0; i < 100; i++) { + msleep(10); + if (ci->ready) + break; + } + } + mutex_unlock(&ci->lock); + /* msleep(500); */ + return 0; +} + +static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + printk(KERN_INFO "slot_shutdown\n"); + mutex_lock(&ci->lock); + /* write_regm(ci, 0x09, 0x08, 0x08); */ + write_regm(ci, 0x20, 0x80, 0x80); + write_regm(ci, 0x06, 0x07, 0x07); + ci->mode = -1; + mutex_unlock(&ci->lock); + return 0; /* shutdown(ci); */ +} + +static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + write_regm(ci, 0x09, 0x00, 0x08); + set_mode(ci, 0); +#ifdef BUFFER_MODE + cam_mode(ci, 1); +#endif + mutex_unlock(&ci->lock); + return 0; +} + + +static int campoll(struct cxd *ci) +{ + u8 istat; + + read_reg(ci, 0x04, &istat); + if (!istat) + return 0; + write_reg(ci, 0x05, istat); + + if (istat&0x40) { + ci->dr = 1; + printk(KERN_INFO "DR\n"); + } + if (istat&0x20) + printk(KERN_INFO "WC\n"); + + if (istat&2) { + u8 slotstat; + + read_reg(ci, 0x01, &slotstat); + if (!(2&slotstat)) { + if (!ci->slot_stat) { + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_PRESENT; + write_regm(ci, 0x03, 0x08, 0x08); + } + + } else { + if (ci->slot_stat) { + ci->slot_stat = 0; + write_regm(ci, 0x03, 0x00, 0x08); + printk(KERN_INFO "NO CAM\n"); + ci->ready = 0; + } + } + if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { + ci->ready = 1; + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; + printk(KERN_INFO "READY\n"); + } + } + return 0; +} + + +static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct cxd *ci = ca->data; + u8 slotstat; + + mutex_lock(&ci->lock); + campoll(ci); + read_reg(ci, 0x01, &slotstat); + mutex_unlock(&ci->lock); + + return ci->slot_stat; +} + +#ifdef BUFFER_MODE +static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + u8 msb, lsb; + u16 len; + + mutex_lock(&ci->lock); + campoll(ci); + mutex_unlock(&ci->lock); + + printk(KERN_INFO "read_data\n"); + if (!ci->dr) + return 0; + + mutex_lock(&ci->lock); + read_reg(ci, 0x0f, &msb); + read_reg(ci, 0x10, &lsb); + len = (msb<<8)|lsb; + read_block(ci, 0x12, ebuf, len); + ci->dr = 0; + mutex_unlock(&ci->lock); + + return len; +} + +static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + printk(KERN_INFO "write_data %d\n", ecount); + write_reg(ci, 0x0d, ecount>>8); + write_reg(ci, 0x0e, ecount&0xff); + write_block(ci, 0x11, ebuf, ecount); + mutex_unlock(&ci->lock); + return ecount; +} +#endif + +static struct dvb_ca_en50221 en_templ = { + .read_attribute_mem = read_attribute_mem, + .write_attribute_mem = write_attribute_mem, + .read_cam_control = read_cam_control, + .write_cam_control = write_cam_control, + .slot_reset = slot_reset, + .slot_shutdown = slot_shutdown, + .slot_ts_enable = slot_ts_enable, + .poll_slot_status = poll_slot_status, +#ifdef BUFFER_MODE + .read_data = read_data, + .write_data = write_data, +#endif + +}; + +struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, + struct i2c_adapter *i2c) +{ + struct cxd *ci = 0; + u32 bitrate = 62000000; + u8 val; + + if (i2c_read_reg(i2c, adr, 0, &val) < 0) { + printk(KERN_ERR "No CXD2099 detected at %02x\n", adr); + return 0; + } + + ci = kmalloc(sizeof(struct cxd), GFP_KERNEL); + if (!ci) + return 0; + memset(ci, 0, sizeof(*ci)); + + mutex_init(&ci->lock); + ci->i2c = i2c; + ci->adr = adr; + ci->lastaddress = 0xff; + ci->clk_reg_b = 0x4a; + ci->clk_reg_f = 0x1b; + ci->bitrate = bitrate; + + memcpy(&ci->en, &en_templ, sizeof(en_templ)); + ci->en.data = ci; + init(ci); + printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->adr); + return &ci->en; +} +EXPORT_SYMBOL(cxd2099_attach); + +MODULE_DESCRIPTION("cxd2099"); +MODULE_AUTHOR("Ralph Metzler "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ngene/cxd2099.h b/drivers/media/dvb/ngene/cxd2099.h new file mode 100644 index 0000000..f71b807 --- /dev/null +++ b/drivers/media/dvb/ngene/cxd2099.h @@ -0,0 +1,32 @@ +/* + * cxd2099.h: Driver for the CXD2099AR Common Interface Controller + * + * Copyright (C) 2010 DigitalDevices UG + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _CXD2099_H_ +#define _CXD2099_H_ + +#include + +struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c); + +#endif diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c index fc93d10..93e6f9e 100644 --- a/drivers/media/dvb/ngene/ngene-cards.c +++ b/drivers/media/dvb/ngene/ngene-cards.c @@ -317,7 +317,8 @@ static struct ngene_info ngene_info_satixS2v2 = { static struct ngene_info ngene_info_cineS2v5 = { .type = NGENE_SIDEWINDER, .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN}, + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, + NGENE_IO_TSOUT}, .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110}, .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, @@ -332,7 +333,8 @@ static struct ngene_info ngene_info_cineS2v5 = { static struct ngene_info ngene_info_duoFlexS2 = { .type = NGENE_SIDEWINDER, .name = "Digital Devices DuoFlex S2 miniPCIe", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN}, + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, + NGENE_IO_TSOUT}, .demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe}, .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110}, .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 831b758..deaf72a 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -45,7 +45,6 @@ static int one_adapter = 1; module_param(one_adapter, int, 0444); MODULE_PARM_DESC(one_adapter, "Use only one adapter."); - static int debug; module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "Print debugging information."); @@ -476,7 +475,7 @@ static u8 SPDIFConfiguration[10] = { static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 }; -static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x20, 0x00, 0x00 }; +static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 }; static u8 ITUDecoderSetup[4][16] = { {0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */ @@ -749,13 +748,11 @@ void set_transfer(struct ngene_channel *chan, int state) if (chan->mode & NGENE_IO_TSOUT) { chan->pBufferExchange = tsout_exchange; /* 0x66666666 = 50MHz *2^33 /250MHz */ - chan->AudioDTOValue = 0x66666666; - /* set_dto(chan, 38810700+1000); */ - /* set_dto(chan, 19392658); */ + chan->AudioDTOValue = 0x80000000; + chan->AudioDTOUpdated = 1; } if (chan->mode & NGENE_IO_TSIN) chan->pBufferExchange = tsin_exchange; - /* ngwritel(0, 0x9310); */ spin_unlock_irq(&chan->state_lock); } else ;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", @@ -1168,6 +1165,7 @@ static void ngene_release_buffers(struct ngene *dev) iounmap(dev->iomem); free_common_buffers(dev); vfree(dev->tsout_buf); + vfree(dev->tsin_buf); vfree(dev->ain_buf); vfree(dev->vin_buf); vfree(dev); @@ -1184,6 +1182,13 @@ static int ngene_get_buffers(struct ngene *dev) dvb_ringbuffer_init(&dev->tsout_rbuf, dev->tsout_buf, TSOUT_BUF_SIZE); } + if (dev->card_info->io_type[2]&NGENE_IO_TSIN) { + dev->tsin_buf = vmalloc(TSIN_BUF_SIZE); + if (!dev->tsin_buf) + return -ENOMEM; + dvb_ringbuffer_init(&dev->tsin_rbuf, + dev->tsin_buf, TSIN_BUF_SIZE); + } if (dev->card_info->io_type[2] & NGENE_IO_AIN) { dev->ain_buf = vmalloc(AIN_BUF_SIZE); if (!dev->ain_buf) @@ -1307,6 +1312,35 @@ static void ngene_stop(struct ngene *dev) #endif } +static int ngene_buffer_config(struct ngene *dev) +{ + int stat; + + if (dev->card_info->fw_version >= 17) { + u8 tsin12_config[6] = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }; + u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 }; + u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 }; + u8 *bconf = tsin12_config; + + if (dev->card_info->io_type[2]&NGENE_IO_TSIN && + dev->card_info->io_type[3]&NGENE_IO_TSIN) { + bconf = tsin1234_config; + if (dev->card_info->io_type[4]&NGENE_IO_TSOUT && + dev->ci.en) + bconf = tsio1235_config; + } + stat = ngene_command_config_free_buf(dev, bconf); + } else { + int bconf = BUFFER_CONFIG_4422; + + if (dev->card_info->io_type[3] == NGENE_IO_TSIN) + bconf = BUFFER_CONFIG_3333; + stat = ngene_command_config_buf(dev, bconf); + } + return stat; +} + + static int ngene_start(struct ngene *dev) { int stat; @@ -1371,23 +1405,6 @@ static int ngene_start(struct ngene *dev) if (stat < 0) goto fail; - if (dev->card_info->fw_version >= 17) { - u8 tsin4_config[6] = { - 3072 / 64, 3072 / 64, 0, 3072 / 64, 3072 / 64, 0}; - u8 default_config[6] = { - 4096 / 64, 4096 / 64, 0, 2048 / 64, 2048 / 64, 0}; - u8 *bconf = default_config; - - if (dev->card_info->io_type[3] == NGENE_IO_TSIN) - bconf = tsin4_config; - dprintk(KERN_DEBUG DEVICE_NAME ": FW 17+ buffer config\n"); - stat = ngene_command_config_free_buf(dev, bconf); - } else { - int bconf = BUFFER_CONFIG_4422; - if (dev->card_info->io_type[3] == NGENE_IO_TSIN) - bconf = BUFFER_CONFIG_3333; - stat = ngene_command_config_buf(dev, bconf); - } if (!stat) return stat; @@ -1403,9 +1420,6 @@ fail2: return stat; } - - - /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ @@ -1422,7 +1436,12 @@ static void release_channel(struct ngene_channel *chan) tasklet_kill(&chan->demux_tasklet); + if (chan->number >= 2 && chan->number <= 3 && dev->ci.en) + return; + if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { + if (chan->ci_dev) + dvb_unregister_device(chan->ci_dev); if (chan->fe) { dvb_unregister_frontend(chan->fe); dvb_frontend_detach(chan->fe); @@ -1458,6 +1477,9 @@ static int init_channel(struct ngene_channel *chan) if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { if (nr >= STREAM_AUDIOIN1) chan->DataFormatFlags = DF_SWAP32; + + if (nr >= 2 && nr <= 3 && dev->ci.en) + return 0; if (nr == 0 || !one_adapter || dev->first_adapter == NULL) { adapter = &dev->adapter[nr]; ret = dvb_register_adapter(adapter, "nGene", @@ -1478,6 +1500,15 @@ static int init_channel(struct ngene_channel *chan) ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux, &chan->hw_frontend, &chan->mem_frontend, adapter); + if (dev->ci.en && (io&NGENE_IO_TSOUT)) { + dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1); + set_transfer(chan, 1); + set_transfer(&chan->dev->channel[2], 1); + + dvb_register_device(adapter, &chan->ci_dev, + &ngene_dvbdev_ci, (void *) chan, + DVB_DEVICE_SEC); + } } if (io & NGENE_IO_TSIN) { @@ -1525,6 +1556,24 @@ static int init_channels(struct ngene *dev) return 0; } +static void cxd_attach(struct ngene *dev) +{ + struct ngene_ci *ci = &dev->ci; + + ci->en = cxd2099_attach(0x40, dev, &dev->channel[0].i2c_adapter); + ci->dev = dev; + return; +} + +static void cxd_detach(struct ngene *dev) +{ + struct ngene_ci *ci = &dev->ci; + + dvb_ca_en50221_release(ci->en); + kfree(ci->en); + ci->en = 0; +} + /****************************************************************************/ /* device probe/remove calls ************************************************/ /****************************************************************************/ @@ -1537,6 +1586,8 @@ void __devexit ngene_remove(struct pci_dev *pdev) tasklet_kill(&dev->event_tasklet); for (i = MAX_STREAM - 1; i >= 0; i--) release_channel(&dev->channel[i]); + if (dev->ci.en) + cxd_detach(dev); ngene_stop(dev); ngene_release_buffers(dev); pci_set_drvdata(pdev, NULL); @@ -1572,6 +1623,13 @@ int __devinit ngene_probe(struct pci_dev *pci_dev, if (stat < 0) goto fail1; + cxd_attach(dev); + + stat = ngene_buffer_config(dev); + if (stat < 0) + goto fail1; + + dev->i2c_current_bus = -1; /* Register DVB adapters and devices for both channels */ diff --git a/drivers/media/dvb/ngene/ngene-dvb.c b/drivers/media/dvb/ngene/ngene-dvb.c index 8bb1593..0b49432 100644 --- a/drivers/media/dvb/ngene/ngene-dvb.c +++ b/drivers/media/dvb/ngene/ngene-dvb.c @@ -47,6 +47,64 @@ /* COMMAND API interface ****************************************************/ /****************************************************************************/ +static ssize_t ts_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + + if (wait_event_interruptible(dev->tsout_rbuf.queue, + dvb_ringbuffer_free + (&dev->tsout_rbuf) >= count) < 0) + return 0; + + dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count); + + return count; +} + +static ssize_t ts_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + int left, avail; + + left = count; + while (left) { + if (wait_event_interruptible( + dev->tsin_rbuf.queue, + dvb_ringbuffer_avail(&dev->tsin_rbuf) > 0) < 0) + return -EAGAIN; + avail = dvb_ringbuffer_avail(&dev->tsin_rbuf); + if (avail > left) + avail = left; + dvb_ringbuffer_read_user(&dev->tsin_rbuf, buf, avail); + left -= avail; + buf += avail; + } + return count; +} + +static const struct file_operations ci_fops = { + .owner = THIS_MODULE, + .read = ts_read, + .write = ts_write, + .open = dvb_generic_open, + .release = dvb_generic_release, +}; + +struct dvb_device ngene_dvbdev_ci = { + .priv = 0, + .readers = -1, + .writers = -1, + .users = -1, + .fops = &ci_fops, +}; + + /****************************************************************************/ /* DVB functions and API interface ******************************************/ /****************************************************************************/ @@ -63,11 +121,19 @@ static void swap_buffer(u32 *p, u32 len) void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) { struct ngene_channel *chan = priv; + struct ngene *dev = chan->dev; + if (flags & DF_SWAP32) + swap_buffer(buf, len); + if (dev->ci.en && chan->number == 2) { + if (dvb_ringbuffer_free(&dev->tsin_rbuf) > len) { + dvb_ringbuffer_write(&dev->tsin_rbuf, buf, len); + wake_up_interruptible(&dev->tsin_rbuf.queue); + } + return 0; + } if (chan->users > 0) { - if (flags & DF_SWAP32) - swap_buffer(buf, len); dvb_dmx_swfilter(&chan->demux, buf, len); } return NULL; diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index 8fb4200..f783406 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -36,8 +36,10 @@ #include "dmxdev.h" #include "dvbdev.h" #include "dvb_demux.h" +#include "dvb_ca_en50221.h" #include "dvb_frontend.h" #include "dvb_ringbuffer.h" +#include "cxd2099.h" #define DEVICE_NAME "ngene" @@ -644,6 +646,7 @@ struct ngene_channel { struct dmx_frontend mem_frontend; int users; struct video_device *v4l_dev; + struct dvb_device *ci_dev; struct tasklet_struct demux_tasklet; struct SBufferHeader *nextBuffer; @@ -710,6 +713,15 @@ struct ngene_channel { int running; }; + +struct ngene_ci { + struct device device; + struct i2c_adapter i2c_adapter; + + struct ngene *dev; + struct dvb_ca_en50221 *en; +}; + struct ngene; typedef void (rx_cb_t)(struct ngene *, u32, u8); @@ -774,6 +786,10 @@ struct ngene { #define TSOUT_BUF_SIZE (512*188*8) struct dvb_ringbuffer tsout_rbuf; + u8 *tsin_buf; +#define TSIN_BUF_SIZE (512*188*8) + struct dvb_ringbuffer tsin_rbuf; + u8 *ain_buf; #define AIN_BUF_SIZE (128*1024) struct dvb_ringbuffer ain_rbuf; @@ -785,6 +801,8 @@ struct ngene { unsigned long exp_val; int prev_cmd; + + struct ngene_ci ci; }; struct ngene_info { @@ -872,6 +890,7 @@ void FillTSBuffer(void *Buffer, int Length, u32 Flags); int ngene_i2c_init(struct ngene *dev, int dev_nr); /* Provided by ngene-dvb.c */ +extern struct dvb_device ngene_dvbdev_ci; void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed); -- cgit v0.10.2 From 1b7c41ef33f38c88dcf9a1b7544d9e4eaa045e6f Mon Sep 17 00:00:00 2001 From: Ralph Metzler Date: Mon, 10 Jan 2011 06:36:16 -0300 Subject: [media] ngene: Shutdown workaround Implement shutdown workaround for some chipsets. Signed-off-by: Ralph Metzler Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c index 93e6f9e..6d87427 100644 --- a/drivers/media/dvb/ngene/ngene-cards.c +++ b/drivers/media/dvb/ngene/ngene-cards.c @@ -436,6 +436,7 @@ static struct pci_driver ngene_pci_driver = { .probe = ngene_probe, .remove = __devexit_p(ngene_remove), .err_handler = &ngene_errors, + .shutdown = ngene_shutdown, }; static __init int module_init_ngene(void) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index deaf72a..422f83b 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -45,6 +45,10 @@ static int one_adapter = 1; module_param(one_adapter, int, 0444); MODULE_PARM_DESC(one_adapter, "Use only one adapter."); +static int shutdown_workaround; +module_param(shutdown_workaround, int, 0644); +MODULE_PARM_DESC(one_adapter, "Activate workaround for shutdown problem with some chipsets."); + static int debug; module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "Print debugging information."); @@ -1574,6 +1578,39 @@ static void cxd_detach(struct ngene *dev) ci->en = 0; } +/***********************************/ +/* workaround for shutdown failure */ +/***********************************/ + +static void ngene_unlink(struct ngene *dev) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_MEM_WRITE; + com.cmd.hdr.Length = 3; + com.cmd.MemoryWrite.address = 0x910c; + com.cmd.MemoryWrite.data = 0xff; + com.in_len = 3; + com.out_len = 1; + + down(&dev->cmd_mutex); + ngwritel(0, NGENE_INT_ENABLE); + ngene_command_mutex(dev, &com); + up(&dev->cmd_mutex); +} + +void ngene_shutdown(struct pci_dev *pdev) +{ + struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev); + + if (!dev || !shutdown_workaround) + return; + + printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n"); + ngene_unlink(dev); + pci_disable_device(pdev); +} + /****************************************************************************/ /* device probe/remove calls ************************************************/ /****************************************************************************/ diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index f783406..c1bb810 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -881,6 +881,7 @@ struct ngene_buffer { int __devinit ngene_probe(struct pci_dev *pci_dev, const struct pci_device_id *id); void __devexit ngene_remove(struct pci_dev *pdev); +void ngene_shutdown(struct pci_dev *pdev); int ngene_command(struct ngene *dev, struct ngene_command *com); int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level); void set_transfer(struct ngene_channel *chan, int state); -- cgit v0.10.2 From cce33c34937dec038567681d4755e75affed9742 Mon Sep 17 00:00:00 2001 From: Ralph Metzler Date: Mon, 10 Jan 2011 06:36:17 -0300 Subject: [media] ngene: Add net device Add dvb net device. Note that the physical address must be setup manually. Signed-off-by: Ralph Metzler Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 422f83b..6a097e4 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1451,6 +1451,7 @@ static void release_channel(struct ngene_channel *chan) dvb_frontend_detach(chan->fe); chan->fe = NULL; } + dvb_net_release(&chan->dvbnet); dvbdemux->dmx.close(&dvbdemux->dmx); dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &chan->hw_frontend); @@ -1504,6 +1505,8 @@ static int init_channel(struct ngene_channel *chan) ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux, &chan->hw_frontend, &chan->mem_frontend, adapter); + ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx); + if (dev->ci.en && (io&NGENE_IO_TSOUT)) { dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1); set_transfer(chan, 1); diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index c1bb810..5894abd 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -39,6 +39,7 @@ #include "dvb_ca_en50221.h" #include "dvb_frontend.h" #include "dvb_ringbuffer.h" +#include "dvb_net.h" #include "cxd2099.h" #define DEVICE_NAME "ngene" @@ -642,6 +643,7 @@ struct ngene_channel { struct dvb_frontend *fe; struct dmxdev dmxdev; struct dvb_demux demux; + struct dvb_net dvbnet; struct dmx_frontend hw_frontend; struct dmx_frontend mem_frontend; int users; -- cgit v0.10.2 From f164396ff54b95d709a5ee5ffc601cf9cd85f298 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:18 -0300 Subject: [media] ngene: Enable CI for Mystique SaTiX-S2 Dual (v2) Enable CI for Mystique SaTiX-S2 Dual (v2). Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c index 6d87427..fcf4be9 100644 --- a/drivers/media/dvb/ngene/ngene-cards.c +++ b/drivers/media/dvb/ngene/ngene-cards.c @@ -303,7 +303,8 @@ static struct ngene_info ngene_info_satixS2 = { static struct ngene_info ngene_info_satixS2v2 = { .type = NGENE_SIDEWINDER, .name = "Mystique SaTiX-S2 Dual (v2)", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN}, + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, + NGENE_IO_TSOUT}, .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110}, .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, -- cgit v0.10.2 From f33de4aa24646b445f4dd52ebd57e322730644b2 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:19 -0300 Subject: [media] get_dvb_firmware: ngene_18.fw added Add download link for ngene firmware rev 18. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 59690de..3348d31 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -556,6 +556,9 @@ sub ngene { my $hash1 = "d798d5a757121174f0dbc5f2833c0c85"; my $file2 = "ngene_17.fw"; my $hash2 = "26b687136e127b8ac24b81e0eeafc20b"; + my $url2 = "http://l4m-daten.de/downloads/firmware/dvb-s2/linux/all/"; + my $file3 = "ngene_18.fw"; + my $hash3 = "ebce3ea769a53e3e0b0197c3b3f127e3"; checkstandard(); @@ -565,7 +568,10 @@ sub ngene { wgetfile($file2, $url . $file2); verify($file2, $hash2); - "$file1, $file2"; + wgetfile($file3, $url2 . $file3); + verify($file3, $hash3); + + "$file1, $file2, $file3"; } sub az6027{ -- cgit v0.10.2 From e549bedf8fd9887529adf1a511501d4c036a2cfe Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:20 -0300 Subject: [media] ngene: Fix copy-paste error Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 6a097e4..a14b69c 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -47,7 +47,7 @@ MODULE_PARM_DESC(one_adapter, "Use only one adapter."); static int shutdown_workaround; module_param(shutdown_workaround, int, 0644); -MODULE_PARM_DESC(one_adapter, "Activate workaround for shutdown problem with some chipsets."); +MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets."); static int debug; module_param(debug, int, 0444); -- cgit v0.10.2 From 908f668bfbe4d268462352b9227bfe4cb458feb6 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:21 -0300 Subject: [media] stv090x: Fixed typos in register macros Fixed typos in register macros. Signed-off-by: Oliver Endriss Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv090x_reg.h b/drivers/media/dvb/frontends/stv090x_reg.h index 2502855..93741ee 100644 --- a/drivers/media/dvb/frontends/stv090x_reg.h +++ b/drivers/media/dvb/frontends/stv090x_reg.h @@ -1327,10 +1327,10 @@ #define STV090x_WIDTH_Px_NOSPLHT_UNNORMED_FIELD 8 #define STV090x_Px_NOSPLHy(__x, __y) (0xf48f - (__x - 1) * 0x200 - __y * 0x1) -#define STv090x_P1_NOSPLH0 STV090x_Px_NOSPLHy(1, 0) -#define STv090x_P1_NOSPLH1 STV090x_Px_NOSPLHy(1, 1) -#define STv090x_P2_NOSPLH0 STV090x_Px_NOSPLHy(2, 0) -#define STv090x_P2_NOSPLH1 STV090x_Px_NOSPLHy(2, 1) +#define STV090x_P1_NOSPLH0 STV090x_Px_NOSPLHy(1, 0) +#define STV090x_P1_NOSPLH1 STV090x_Px_NOSPLHy(1, 1) +#define STV090x_P2_NOSPLH0 STV090x_Px_NOSPLHy(2, 0) +#define STV090x_P2_NOSPLH1 STV090x_Px_NOSPLHy(2, 1) #define STV090x_OFFST_Px_NOSPLH_UNNORMED_FIELD 0 #define STV090x_WIDTH_Px_NOSPLH_UNNORMED_FIELD 8 @@ -1406,7 +1406,7 @@ #define STV090x_Px_BCLC2S28(__x) (0xf49d - (__x - 1) * 0x200) #define STV090x_P1_BCLC2S28 STV090x_Px_BCLC2S28(1) -#define STV090x_P2_BCLC2S28 STV090x_Px_BCLC2S28(1) +#define STV090x_P2_BCLC2S28 STV090x_Px_BCLC2S28(2) #define STV090x_OFFST_Px_CAR2S2_8_BETA_M_FIELD 4 #define STV090x_WIDTH_Px_CAR2S2_8_BETA_M_FIELD 2 #define STV090x_OFFST_Px_CAR2S2_8_BETA_E_FIELD 0 @@ -1414,7 +1414,7 @@ #define STV090x_Px_BCLC2S216A(__x) (0xf49e - (__x - 1) * 0x200) #define STV090x_P1_BCLC2S216A STV090x_Px_BCLC2S216A(1) -#define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(1) +#define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(2) #define STV090x_OFFST_Px_CAR2S2_16A_BETA_M_FIELD 4 #define STV090x_WIDTH_Px_CAR2S2_16A_BETA_M_FIELD 2 #define STV090x_OFFST_Px_CAR2S2_16A_BETA_E_FIELD 0 @@ -1422,7 +1422,7 @@ #define STV090x_Px_BCLC2S232A(__x) (0xf49f - (__x - 1) * 0x200) #define STV090x_P1_BCLC2S232A STV090x_Px_BCLC2S232A(1) -#define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(1) +#define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(2) #define STV090x_OFFST_Px_CAR2S2_32A_BETA_M_FIELD 4 #define STV090x_WIDTH_Px_CAR2S2_32A_BETA_M_FIELD 2 #define STV090x_OFFST_Px_CAR2S2_32A_BETA_E_FIELD 0 @@ -1602,7 +1602,7 @@ #define STV090x_Px_CCIACC(__x) (0xf4c4 - (__x - 1) * 0x200) #define STV090x_P1_CCIACC STV090x_Px_CCIACC(1) -#define STV090x_P2_CCIACC STV090x_Px_CCIACC(1) +#define STV090x_P2_CCIACC STV090x_Px_CCIACC(2) #define STV090x_OFFST_Px_CCI_VALUE_FIELD 0 #define STV090x_WIDTH_Px_CCI_VALUE_FIELD 8 -- cgit v0.10.2 From 179fd15a99175e8e22516edbb1894c3348842f3a Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:22 -0300 Subject: [media] stv090x: Fix losing lock in dual DVB-S2 mode Do not clear registers ACLC/BCLC in DVB-S2 mode for Cut <= 20. Otherwise, the demod could lose the lock periodically. Verified with cineS2 and Duoflex. Signed-off-by: Oliver Endriss Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index eccad06..e3442a1 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -2894,10 +2894,12 @@ static int stv090x_optimize_track(struct stv090x_state *state) STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, ACLC, 0) < 0) - goto err; - if (STV090x_WRITE_DEMOD(state, BCLC, 0) < 0) - goto err; + if (state->internal->dev_ver >= 0x30) { + if (STV090x_WRITE_DEMOD(state, ACLC, 0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, BCLC, 0) < 0) + goto err; + } if (state->frame_len == STV090x_LONG_FRAME) { reg = STV090x_READ_DEMOD(state, DMDMODCOD); modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD); -- cgit v0.10.2 From 3d0cef2c3c7e19a2425f8e1379a535c46427951b Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:23 -0300 Subject: [media] ngene: Improved channel initialisation and release Refactored code for channel initialisation and release: - Do not create device nodes which are not required. - Better error handling. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index a14b69c..175a0f6 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1432,25 +1432,24 @@ static void release_channel(struct ngene_channel *chan) { struct dvb_demux *dvbdemux = &chan->demux; struct ngene *dev = chan->dev; - struct ngene_info *ni = dev->card_info; - int io = ni->io_type[chan->number]; - if (chan->dev->cmd_timeout_workaround && chan->running) + if (chan->running) set_transfer(chan, 0); tasklet_kill(&chan->demux_tasklet); - if (chan->number >= 2 && chan->number <= 3 && dev->ci.en) - return; + if (chan->ci_dev) { + dvb_unregister_device(chan->ci_dev); + chan->ci_dev = NULL; + } - if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { - if (chan->ci_dev) - dvb_unregister_device(chan->ci_dev); - if (chan->fe) { - dvb_unregister_frontend(chan->fe); - dvb_frontend_detach(chan->fe); - chan->fe = NULL; - } + if (chan->fe) { + dvb_unregister_frontend(chan->fe); + dvb_frontend_detach(chan->fe); + chan->fe = NULL; + } + + if (chan->has_demux) { dvb_net_release(&chan->dvbnet); dvbdemux->dmx.close(&dvbdemux->dmx); dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, @@ -1459,9 +1458,12 @@ static void release_channel(struct ngene_channel *chan) &chan->mem_frontend); dvb_dmxdev_release(&chan->dmxdev); dvb_dmx_release(&chan->demux); + chan->has_demux = false; + } - if (chan->number == 0 || !one_adapter) - dvb_unregister_adapter(&dev->adapter[chan->number]); + if (chan->has_adapter) { + dvb_unregister_adapter(&dev->adapter[chan->number]); + chan->has_adapter = false; } } @@ -1479,12 +1481,27 @@ static int init_channel(struct ngene_channel *chan) chan->type = io; chan->mode = chan->type; /* for now only one mode */ + if (io & NGENE_IO_TSIN) { + chan->fe = NULL; + if (ni->demod_attach[nr]) { + ret = ni->demod_attach[nr](chan); + if (ret < 0) + goto err; + } + if (chan->fe && ni->tuner_attach[nr]) { + ret = ni->tuner_attach[nr](chan); + if (ret < 0) + goto err; + } + } + + if (!dev->ci.en && (io & NGENE_IO_TSOUT)) + return 0; + if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { if (nr >= STREAM_AUDIOIN1) chan->DataFormatFlags = DF_SWAP32; - if (nr >= 2 && nr <= 3 && dev->ci.en) - return 0; if (nr == 0 || !one_adapter || dev->first_adapter == NULL) { adapter = &dev->adapter[nr]; ret = dvb_register_adapter(adapter, "nGene", @@ -1492,13 +1509,32 @@ static int init_channel(struct ngene_channel *chan) &chan->dev->pci_dev->dev, adapter_nr); if (ret < 0) - return ret; + goto err; if (dev->first_adapter == NULL) dev->first_adapter = adapter; - } else { + chan->has_adapter = true; + } else adapter = dev->first_adapter; - } + } + + if (dev->ci.en && (io & NGENE_IO_TSOUT)) { + dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1); + set_transfer(chan, 1); + set_transfer(&chan->dev->channel[2], 1); + dvb_register_device(adapter, &chan->ci_dev, + &ngene_dvbdev_ci, (void *) chan, + DVB_DEVICE_SEC); + if (!chan->ci_dev) + goto err; + } + if (chan->fe) { + if (dvb_register_frontend(adapter, chan->fe) < 0) + goto err; + chan->has_demux = true; + } + + if (chan->has_demux) { ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", ngene_start_feed, ngene_stop_feed, chan); @@ -1506,45 +1542,16 @@ static int init_channel(struct ngene_channel *chan) &chan->hw_frontend, &chan->mem_frontend, adapter); ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx); - - if (dev->ci.en && (io&NGENE_IO_TSOUT)) { - dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1); - set_transfer(chan, 1); - set_transfer(&chan->dev->channel[2], 1); - - dvb_register_device(adapter, &chan->ci_dev, - &ngene_dvbdev_ci, (void *) chan, - DVB_DEVICE_SEC); - } } - if (io & NGENE_IO_TSIN) { - chan->fe = NULL; - if (ni->demod_attach[nr]) { - ret = ni->demod_attach[nr](chan); - if (ret < 0) - goto err_fe; - } - if (chan->fe && ni->tuner_attach[nr]) { - ret = ni->tuner_attach[nr](chan); - if (ret < 0) - goto err_fe; - } - if (chan->fe) { - if (dvb_register_frontend(adapter, chan->fe) < 0) - goto err_fe; - } - } return ret; -err_fe: +err: if (chan->fe) { dvb_frontend_detach(chan->fe); chan->fe = NULL; } -/* FIXME: this causes an oops... */ -/* release_channel(chan); */ -/* return ret; */ + release_channel(chan); return 0; } diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index 5894abd..40fce9e 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -639,6 +639,8 @@ struct ngene_channel { int number; int type; int mode; + bool has_adapter; + bool has_demux; struct dvb_frontend *fe; struct dmxdev dmxdev; -- cgit v0.10.2 From 4862d6b2e23f0573ef60c784341e05e1ac0804eb Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 10 Jan 2011 06:36:24 -0300 Subject: [media] stv090x: 22kHz workaround must also be performed for the 2nd frontend 22kHz workaround must also be performed for the 2nd frontend. Signed-off-by: Oliver Endriss Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index e3442a1..d3362d0 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -4696,10 +4696,6 @@ static int stv090x_setup(struct dvb_frontend *fe) if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0) goto err; - /* workaround for stuck DiSEqC output */ - if (config->diseqc_envelope_mode) - stv090x_send_diseqc_burst(fe, SEC_MINI_A); - return 0; err: dprintk(FE_ERROR, 1, "I/O error"); @@ -4784,11 +4780,6 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, state->internal = temp_int->internal; state->internal->num_used++; dprintk(FE_INFO, 1, "Found Internal Structure!"); - dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", - state->device == STV0900 ? "STV0900" : "STV0903", - demod, - state->internal->dev_ver); - return &state->frontend; } else { state->internal = kmalloc(sizeof(struct stv090x_internal), GFP_KERNEL); @@ -4799,15 +4790,20 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, state->internal->i2c_adap = state->i2c; state->internal->i2c_addr = state->config->address; dprintk(FE_INFO, 1, "Create New Internal Structure!"); - } - mutex_init(&state->internal->demod_lock); - mutex_init(&state->internal->tuner_lock); + mutex_init(&state->internal->demod_lock); + mutex_init(&state->internal->tuner_lock); - if (stv090x_setup(&state->frontend) < 0) { - dprintk(FE_ERROR, 1, "Error setting up device"); - goto error; + if (stv090x_setup(&state->frontend) < 0) { + dprintk(FE_ERROR, 1, "Error setting up device"); + goto error; + } } + + /* workaround for stuck DiSEqC output */ + if (config->diseqc_envelope_mode) + stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A); + dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", state->device == STV0900 ? "STV0900" : "STV0903", demod, -- cgit v0.10.2 From 8112248a1b66440f6c562c2669aba6950ef48199 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 17 Jan 2011 14:20:49 -0200 Subject: [media] Move CI cxd2099 driver to staging This driver is abusing the kernel<=>userspace API, due to the lack of a proper solution for it. A discussion were done at: http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html But there's not a solution for it yet. So, move the driver to staging, while we don't have a final solution. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile index 00d12d6..2bc9687 100644 --- a/drivers/media/dvb/ngene/Makefile +++ b/drivers/media/dvb/ngene/Makefile @@ -2,10 +2,13 @@ # Makefile for the nGene device driver # -ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o cxd2099.o +ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o obj-$(CONFIG_DVB_NGENE) += ngene.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/ EXTRA_CFLAGS += -Idrivers/media/common/tuners/ + +# For the staging CI driver cxd2099 +EXTRA_CFLAGS += -Idrivers/staging/cxd2099/ diff --git a/drivers/media/dvb/ngene/cxd2099.c b/drivers/media/dvb/ngene/cxd2099.c deleted file mode 100644 index b49186c..0000000 --- a/drivers/media/dvb/ngene/cxd2099.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * cxd2099.c: Driver for the CXD2099AR Common Interface Controller - * - * Copyright (C) 2010 DigitalDevices UG - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cxd2099.h" - -#define MAX_BUFFER_SIZE 248 - -struct cxd { - struct dvb_ca_en50221 en; - - struct i2c_adapter *i2c; - u8 adr; - u8 regs[0x23]; - u8 lastaddress; - u8 clk_reg_f; - u8 clk_reg_b; - int mode; - u32 bitrate; - int ready; - int dr; - int slot_stat; - - u8 amem[1024]; - int amem_read; - - int cammode; - struct mutex lock; -}; - -static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, - u8 reg, u8 data) -{ - u8 m[2] = {reg, data}; - struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; - - if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", - reg, adr); - return -1; - } - return 0; -} - -static int i2c_write(struct i2c_adapter *adapter, u8 adr, - u8 *data, u8 len) -{ - struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; - - if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C!\n"); - return -1; - } - return 0; -} - -static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, - u8 reg, u8 *val) -{ - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = ®, .len = 1 }, - {.addr = adr, .flags = I2C_M_RD, - .buf = val, .len = 1 } }; - - if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read_reg\n"); - return -1; - } - return 0; -} - -static int i2c_read(struct i2c_adapter *adapter, u8 adr, - u8 reg, u8 *data, u8 n) -{ - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = ®, .len = 1 }, - {.addr = adr, .flags = I2C_M_RD, - .buf = data, .len = n } }; - - if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read\n"); - return -1; - } - return 0; -} - -static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n) -{ - int status; - - status = i2c_write_reg(ci->i2c, ci->adr, 0, adr); - if (!status) { - ci->lastaddress = adr; - status = i2c_read(ci->i2c, ci->adr, 1, data, n); - } - return status; -} - -static int read_reg(struct cxd *ci, u8 reg, u8 *val) -{ - return read_block(ci, reg, val, 1); -} - - -static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) -{ - int status; - u8 addr[3] = { 2, address&0xff, address>>8 }; - - status = i2c_write(ci->i2c, ci->adr, addr, 3); - if (!status) - status = i2c_read(ci->i2c, ci->adr, 3, data, n); - return status; -} - -static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) -{ - int status; - u8 addr[3] = { 2, address&0xff, address>>8 }; - - status = i2c_write(ci->i2c, ci->adr, addr, 3); - if (!status) { - u8 buf[256] = {3}; - memcpy(buf+1, data, n); - status = i2c_write(ci->i2c, ci->adr, buf, n+1); - } - return status; -} - -static int read_io(struct cxd *ci, u16 address, u8 *val) -{ - int status; - u8 addr[3] = { 2, address&0xff, address>>8 }; - - status = i2c_write(ci->i2c, ci->adr, addr, 3); - if (!status) - status = i2c_read(ci->i2c, ci->adr, 3, val, 1); - return status; -} - -static int write_io(struct cxd *ci, u16 address, u8 val) -{ - int status; - u8 addr[3] = { 2, address&0xff, address>>8 }; - u8 buf[2] = { 3, val }; - - status = i2c_write(ci->i2c, ci->adr, addr, 3); - if (!status) - status = i2c_write(ci->i2c, ci->adr, buf, 2); - - return status; -} - - -static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask) -{ - int status; - - status = i2c_write_reg(ci->i2c, ci->adr, 0, reg); - if (!status && reg >= 6 && reg <= 8 && mask != 0xff) - status = i2c_read_reg(ci->i2c, ci->adr, 1, &ci->regs[reg]); - ci->regs[reg] = (ci->regs[reg]&(~mask))|val; - if (!status) { - ci->lastaddress = reg; - status = i2c_write_reg(ci->i2c, ci->adr, 1, ci->regs[reg]); - } - if (reg == 0x20) - ci->regs[reg] &= 0x7f; - return status; -} - -static int write_reg(struct cxd *ci, u8 reg, u8 val) -{ - return write_regm(ci, reg, val, 0xff); -} - -#ifdef BUFFER_MODE -static int write_block(struct cxd *ci, u8 adr, u8 *data, int n) -{ - int status; - u8 buf[256] = {1}; - - status = i2c_write_reg(ci->i2c, ci->adr, 0, adr); - if (!status) { - ci->lastaddress = adr; - memcpy(buf+1, data, n); - status = i2c_write(ci->i2c, ci->adr, buf, n+1); - } - return status; -} -#endif - -static void set_mode(struct cxd *ci, int mode) -{ - if (mode == ci->mode) - return; - - switch (mode) { - case 0x00: /* IO mem */ - write_regm(ci, 0x06, 0x00, 0x07); - break; - case 0x01: /* ATT mem */ - write_regm(ci, 0x06, 0x02, 0x07); - break; - default: - break; - } - ci->mode = mode; -} - -static void cam_mode(struct cxd *ci, int mode) -{ - if (mode == ci->cammode) - return; - - switch (mode) { - case 0x00: - write_regm(ci, 0x20, 0x80, 0x80); - break; - case 0x01: - printk(KERN_INFO "enable cam buffer mode\n"); - /* write_reg(ci, 0x0d, 0x00); */ - /* write_reg(ci, 0x0e, 0x01); */ - write_regm(ci, 0x08, 0x40, 0x40); - /* read_reg(ci, 0x12, &dummy); */ - write_regm(ci, 0x08, 0x80, 0x80); - break; - default: - break; - } - ci->cammode = mode; -} - - - -#define CHK_ERROR(s) if ((status = s)) break - -static int init(struct cxd *ci) -{ - int status; - - mutex_lock(&ci->lock); - ci->mode = -1; - do { - CHK_ERROR(write_reg(ci, 0x00, 0x00)); - CHK_ERROR(write_reg(ci, 0x01, 0x00)); - CHK_ERROR(write_reg(ci, 0x02, 0x10)); - CHK_ERROR(write_reg(ci, 0x03, 0x00)); - CHK_ERROR(write_reg(ci, 0x05, 0xFF)); - CHK_ERROR(write_reg(ci, 0x06, 0x1F)); - CHK_ERROR(write_reg(ci, 0x07, 0x1F)); - CHK_ERROR(write_reg(ci, 0x08, 0x28)); - CHK_ERROR(write_reg(ci, 0x14, 0x20)); - - CHK_ERROR(write_reg(ci, 0x09, 0x4D)); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */ - CHK_ERROR(write_reg(ci, 0x0A, 0xA7)); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */ - - /* Sync detector */ - CHK_ERROR(write_reg(ci, 0x0B, 0x33)); - CHK_ERROR(write_reg(ci, 0x0C, 0x33)); - - CHK_ERROR(write_regm(ci, 0x14, 0x00, 0x0F)); - CHK_ERROR(write_reg(ci, 0x15, ci->clk_reg_b)); - CHK_ERROR(write_regm(ci, 0x16, 0x00, 0x0F)); - CHK_ERROR(write_reg(ci, 0x17, ci->clk_reg_f)); - - CHK_ERROR(write_reg(ci, 0x20, 0x28)); /* Integer Divider, Falling Edge, Internal Sync, */ - CHK_ERROR(write_reg(ci, 0x21, 0x00)); /* MCLKI = TICLK/8 */ - CHK_ERROR(write_reg(ci, 0x22, 0x07)); /* MCLKI = TICLK/8 */ - - - CHK_ERROR(write_regm(ci, 0x20, 0x80, 0x80)); /* Reset CAM state machine */ - - CHK_ERROR(write_regm(ci, 0x03, 0x02, 02)); /* Enable IREQA Interrupt */ - CHK_ERROR(write_reg(ci, 0x01, 0x04)); /* Enable CD Interrupt */ - CHK_ERROR(write_reg(ci, 0x00, 0x31)); /* Enable TS1,Hot Swap,Slot A */ - CHK_ERROR(write_regm(ci, 0x09, 0x08, 0x08)); /* Put TS in bypass */ - ci->cammode = -1; -#ifdef BUFFER_MODE - cam_mode(ci, 0); -#endif - } while (0); - mutex_unlock(&ci->lock); - - return 0; -} - - -static int read_attribute_mem(struct dvb_ca_en50221 *ca, - int slot, int address) -{ - struct cxd *ci = ca->data; - u8 val; - mutex_lock(&ci->lock); - set_mode(ci, 1); - read_pccard(ci, address, &val, 1); - mutex_unlock(&ci->lock); - return val; -} - - -static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, - int address, u8 value) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); - set_mode(ci, 1); - write_pccard(ci, address, &value, 1); - mutex_unlock(&ci->lock); - return 0; -} - -static int read_cam_control(struct dvb_ca_en50221 *ca, - int slot, u8 address) -{ - struct cxd *ci = ca->data; - u8 val; - - mutex_lock(&ci->lock); - set_mode(ci, 0); - read_io(ci, address, &val); - mutex_unlock(&ci->lock); - return val; -} - -static int write_cam_control(struct dvb_ca_en50221 *ca, int slot, - u8 address, u8 value) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); - set_mode(ci, 0); - write_io(ci, address, value); - mutex_unlock(&ci->lock); - return 0; -} - -static int slot_reset(struct dvb_ca_en50221 *ca, int slot) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); - cam_mode(ci, 0); - write_reg(ci, 0x00, 0x21); - write_reg(ci, 0x06, 0x1F); - write_reg(ci, 0x00, 0x31); - write_regm(ci, 0x20, 0x80, 0x80); - write_reg(ci, 0x03, 0x02); - ci->ready = 0; - ci->mode = -1; - { - int i; - for (i = 0; i < 100; i++) { - msleep(10); - if (ci->ready) - break; - } - } - mutex_unlock(&ci->lock); - /* msleep(500); */ - return 0; -} - -static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) -{ - struct cxd *ci = ca->data; - - printk(KERN_INFO "slot_shutdown\n"); - mutex_lock(&ci->lock); - /* write_regm(ci, 0x09, 0x08, 0x08); */ - write_regm(ci, 0x20, 0x80, 0x80); - write_regm(ci, 0x06, 0x07, 0x07); - ci->mode = -1; - mutex_unlock(&ci->lock); - return 0; /* shutdown(ci); */ -} - -static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); - write_regm(ci, 0x09, 0x00, 0x08); - set_mode(ci, 0); -#ifdef BUFFER_MODE - cam_mode(ci, 1); -#endif - mutex_unlock(&ci->lock); - return 0; -} - - -static int campoll(struct cxd *ci) -{ - u8 istat; - - read_reg(ci, 0x04, &istat); - if (!istat) - return 0; - write_reg(ci, 0x05, istat); - - if (istat&0x40) { - ci->dr = 1; - printk(KERN_INFO "DR\n"); - } - if (istat&0x20) - printk(KERN_INFO "WC\n"); - - if (istat&2) { - u8 slotstat; - - read_reg(ci, 0x01, &slotstat); - if (!(2&slotstat)) { - if (!ci->slot_stat) { - ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_PRESENT; - write_regm(ci, 0x03, 0x08, 0x08); - } - - } else { - if (ci->slot_stat) { - ci->slot_stat = 0; - write_regm(ci, 0x03, 0x00, 0x08); - printk(KERN_INFO "NO CAM\n"); - ci->ready = 0; - } - } - if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { - ci->ready = 1; - ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; - printk(KERN_INFO "READY\n"); - } - } - return 0; -} - - -static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) -{ - struct cxd *ci = ca->data; - u8 slotstat; - - mutex_lock(&ci->lock); - campoll(ci); - read_reg(ci, 0x01, &slotstat); - mutex_unlock(&ci->lock); - - return ci->slot_stat; -} - -#ifdef BUFFER_MODE -static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) -{ - struct cxd *ci = ca->data; - u8 msb, lsb; - u16 len; - - mutex_lock(&ci->lock); - campoll(ci); - mutex_unlock(&ci->lock); - - printk(KERN_INFO "read_data\n"); - if (!ci->dr) - return 0; - - mutex_lock(&ci->lock); - read_reg(ci, 0x0f, &msb); - read_reg(ci, 0x10, &lsb); - len = (msb<<8)|lsb; - read_block(ci, 0x12, ebuf, len); - ci->dr = 0; - mutex_unlock(&ci->lock); - - return len; -} - -static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); - printk(KERN_INFO "write_data %d\n", ecount); - write_reg(ci, 0x0d, ecount>>8); - write_reg(ci, 0x0e, ecount&0xff); - write_block(ci, 0x11, ebuf, ecount); - mutex_unlock(&ci->lock); - return ecount; -} -#endif - -static struct dvb_ca_en50221 en_templ = { - .read_attribute_mem = read_attribute_mem, - .write_attribute_mem = write_attribute_mem, - .read_cam_control = read_cam_control, - .write_cam_control = write_cam_control, - .slot_reset = slot_reset, - .slot_shutdown = slot_shutdown, - .slot_ts_enable = slot_ts_enable, - .poll_slot_status = poll_slot_status, -#ifdef BUFFER_MODE - .read_data = read_data, - .write_data = write_data, -#endif - -}; - -struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, - struct i2c_adapter *i2c) -{ - struct cxd *ci = 0; - u32 bitrate = 62000000; - u8 val; - - if (i2c_read_reg(i2c, adr, 0, &val) < 0) { - printk(KERN_ERR "No CXD2099 detected at %02x\n", adr); - return 0; - } - - ci = kmalloc(sizeof(struct cxd), GFP_KERNEL); - if (!ci) - return 0; - memset(ci, 0, sizeof(*ci)); - - mutex_init(&ci->lock); - ci->i2c = i2c; - ci->adr = adr; - ci->lastaddress = 0xff; - ci->clk_reg_b = 0x4a; - ci->clk_reg_f = 0x1b; - ci->bitrate = bitrate; - - memcpy(&ci->en, &en_templ, sizeof(en_templ)); - ci->en.data = ci; - init(ci); - printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->adr); - return &ci->en; -} -EXPORT_SYMBOL(cxd2099_attach); - -MODULE_DESCRIPTION("cxd2099"); -MODULE_AUTHOR("Ralph Metzler "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ngene/cxd2099.h b/drivers/media/dvb/ngene/cxd2099.h deleted file mode 100644 index f71b807..0000000 --- a/drivers/media/dvb/ngene/cxd2099.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * cxd2099.h: Driver for the CXD2099AR Common Interface Controller - * - * Copyright (C) 2010 DigitalDevices UG - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -#ifndef _CXD2099_H_ -#define _CXD2099_H_ - -#include - -struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c); - -#endif diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5c8fcfc..4a88e69 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -51,6 +51,8 @@ source "drivers/staging/cx25821/Kconfig" source "drivers/staging/tm6000/Kconfig" +source "drivers/staging/cxd2099/Kconfig" + source "drivers/staging/dabusb/Kconfig" source "drivers/staging/se401/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index d538863..80ee49a 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ +obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_USB_DABUSB) += dabusb/ obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_USB_SE401) += se401/ diff --git a/drivers/staging/cxd2099/Kconfig b/drivers/staging/cxd2099/Kconfig new file mode 100644 index 0000000..9d638c3 --- /dev/null +++ b/drivers/staging/cxd2099/Kconfig @@ -0,0 +1,11 @@ +config DVB_CXD2099 + tristate "CXD2099AR Common Interface driver" + depends on DVB_CORE && PCI && I2C && DVB_NGENE + ---help--- + Support for the CI module found on cineS2 DVB-S2, supported by + the Micronas PCIe device driver (ngene). + + For now, data is passed through '/dev/dvb/adapterX/sec0': + - Encrypted data must be written to 'sec0'. + - Decrypted data can be read from 'sec0'. + - Setup the CAM using device 'ca0'. diff --git a/drivers/staging/cxd2099/Makefile b/drivers/staging/cxd2099/Makefile new file mode 100644 index 0000000..72b1455 --- /dev/null +++ b/drivers/staging/cxd2099/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_DVB_CXD2099) += cxd2099.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/ +EXTRA_CFLAGS += -Idrivers/media/common/tuners/ diff --git a/drivers/staging/cxd2099/TODO b/drivers/staging/cxd2099/TODO new file mode 100644 index 0000000..375bb6f --- /dev/null +++ b/drivers/staging/cxd2099/TODO @@ -0,0 +1,12 @@ +For now, data is passed through '/dev/dvb/adapterX/sec0': + - Encrypted data must be written to 'sec0'. + - Decrypted data can be read from 'sec0'. + - Setup the CAM using device 'ca0'. + +But this is wrong. There are some discussions about the proper way for +doing it, as seen at: + http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html + +While there's no proper fix for it, the driver should be kept in staging. + +Patches should be submitted to: linux-media@vger.kernel.org. diff --git a/drivers/staging/cxd2099/cxd2099.c b/drivers/staging/cxd2099/cxd2099.c new file mode 100644 index 0000000..b49186c --- /dev/null +++ b/drivers/staging/cxd2099/cxd2099.c @@ -0,0 +1,574 @@ +/* + * cxd2099.c: Driver for the CXD2099AR Common Interface Controller + * + * Copyright (C) 2010 DigitalDevices UG + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cxd2099.h" + +#define MAX_BUFFER_SIZE 248 + +struct cxd { + struct dvb_ca_en50221 en; + + struct i2c_adapter *i2c; + u8 adr; + u8 regs[0x23]; + u8 lastaddress; + u8 clk_reg_f; + u8 clk_reg_b; + int mode; + u32 bitrate; + int ready; + int dr; + int slot_stat; + + u8 amem[1024]; + int amem_read; + + int cammode; + struct mutex lock; +}; + +static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 data) +{ + u8 m[2] = {reg, data}; + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", + reg, adr); + return -1; + } + return 0; +} + +static int i2c_write(struct i2c_adapter *adapter, u8 adr, + u8 *data, u8 len) +{ + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR "Failed to write to I2C!\n"); + return -1; + } + return 0; +} + +static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 *val) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = ®, .len = 1 }, + {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1 } }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + printk(KERN_ERR "error in i2c_read_reg\n"); + return -1; + } + return 0; +} + +static int i2c_read(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 *data, u8 n) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = ®, .len = 1 }, + {.addr = adr, .flags = I2C_M_RD, + .buf = data, .len = n } }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + printk(KERN_ERR "error in i2c_read\n"); + return -1; + } + return 0; +} + +static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n) +{ + int status; + + status = i2c_write_reg(ci->i2c, ci->adr, 0, adr); + if (!status) { + ci->lastaddress = adr; + status = i2c_read(ci->i2c, ci->adr, 1, data, n); + } + return status; +} + +static int read_reg(struct cxd *ci, u8 reg, u8 *val) +{ + return read_block(ci, reg, val, 1); +} + + +static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) + status = i2c_read(ci->i2c, ci->adr, 3, data, n); + return status; +} + +static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) { + u8 buf[256] = {3}; + memcpy(buf+1, data, n); + status = i2c_write(ci->i2c, ci->adr, buf, n+1); + } + return status; +} + +static int read_io(struct cxd *ci, u16 address, u8 *val) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) + status = i2c_read(ci->i2c, ci->adr, 3, val, 1); + return status; +} + +static int write_io(struct cxd *ci, u16 address, u8 val) +{ + int status; + u8 addr[3] = { 2, address&0xff, address>>8 }; + u8 buf[2] = { 3, val }; + + status = i2c_write(ci->i2c, ci->adr, addr, 3); + if (!status) + status = i2c_write(ci->i2c, ci->adr, buf, 2); + + return status; +} + + +static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask) +{ + int status; + + status = i2c_write_reg(ci->i2c, ci->adr, 0, reg); + if (!status && reg >= 6 && reg <= 8 && mask != 0xff) + status = i2c_read_reg(ci->i2c, ci->adr, 1, &ci->regs[reg]); + ci->regs[reg] = (ci->regs[reg]&(~mask))|val; + if (!status) { + ci->lastaddress = reg; + status = i2c_write_reg(ci->i2c, ci->adr, 1, ci->regs[reg]); + } + if (reg == 0x20) + ci->regs[reg] &= 0x7f; + return status; +} + +static int write_reg(struct cxd *ci, u8 reg, u8 val) +{ + return write_regm(ci, reg, val, 0xff); +} + +#ifdef BUFFER_MODE +static int write_block(struct cxd *ci, u8 adr, u8 *data, int n) +{ + int status; + u8 buf[256] = {1}; + + status = i2c_write_reg(ci->i2c, ci->adr, 0, adr); + if (!status) { + ci->lastaddress = adr; + memcpy(buf+1, data, n); + status = i2c_write(ci->i2c, ci->adr, buf, n+1); + } + return status; +} +#endif + +static void set_mode(struct cxd *ci, int mode) +{ + if (mode == ci->mode) + return; + + switch (mode) { + case 0x00: /* IO mem */ + write_regm(ci, 0x06, 0x00, 0x07); + break; + case 0x01: /* ATT mem */ + write_regm(ci, 0x06, 0x02, 0x07); + break; + default: + break; + } + ci->mode = mode; +} + +static void cam_mode(struct cxd *ci, int mode) +{ + if (mode == ci->cammode) + return; + + switch (mode) { + case 0x00: + write_regm(ci, 0x20, 0x80, 0x80); + break; + case 0x01: + printk(KERN_INFO "enable cam buffer mode\n"); + /* write_reg(ci, 0x0d, 0x00); */ + /* write_reg(ci, 0x0e, 0x01); */ + write_regm(ci, 0x08, 0x40, 0x40); + /* read_reg(ci, 0x12, &dummy); */ + write_regm(ci, 0x08, 0x80, 0x80); + break; + default: + break; + } + ci->cammode = mode; +} + + + +#define CHK_ERROR(s) if ((status = s)) break + +static int init(struct cxd *ci) +{ + int status; + + mutex_lock(&ci->lock); + ci->mode = -1; + do { + CHK_ERROR(write_reg(ci, 0x00, 0x00)); + CHK_ERROR(write_reg(ci, 0x01, 0x00)); + CHK_ERROR(write_reg(ci, 0x02, 0x10)); + CHK_ERROR(write_reg(ci, 0x03, 0x00)); + CHK_ERROR(write_reg(ci, 0x05, 0xFF)); + CHK_ERROR(write_reg(ci, 0x06, 0x1F)); + CHK_ERROR(write_reg(ci, 0x07, 0x1F)); + CHK_ERROR(write_reg(ci, 0x08, 0x28)); + CHK_ERROR(write_reg(ci, 0x14, 0x20)); + + CHK_ERROR(write_reg(ci, 0x09, 0x4D)); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */ + CHK_ERROR(write_reg(ci, 0x0A, 0xA7)); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */ + + /* Sync detector */ + CHK_ERROR(write_reg(ci, 0x0B, 0x33)); + CHK_ERROR(write_reg(ci, 0x0C, 0x33)); + + CHK_ERROR(write_regm(ci, 0x14, 0x00, 0x0F)); + CHK_ERROR(write_reg(ci, 0x15, ci->clk_reg_b)); + CHK_ERROR(write_regm(ci, 0x16, 0x00, 0x0F)); + CHK_ERROR(write_reg(ci, 0x17, ci->clk_reg_f)); + + CHK_ERROR(write_reg(ci, 0x20, 0x28)); /* Integer Divider, Falling Edge, Internal Sync, */ + CHK_ERROR(write_reg(ci, 0x21, 0x00)); /* MCLKI = TICLK/8 */ + CHK_ERROR(write_reg(ci, 0x22, 0x07)); /* MCLKI = TICLK/8 */ + + + CHK_ERROR(write_regm(ci, 0x20, 0x80, 0x80)); /* Reset CAM state machine */ + + CHK_ERROR(write_regm(ci, 0x03, 0x02, 02)); /* Enable IREQA Interrupt */ + CHK_ERROR(write_reg(ci, 0x01, 0x04)); /* Enable CD Interrupt */ + CHK_ERROR(write_reg(ci, 0x00, 0x31)); /* Enable TS1,Hot Swap,Slot A */ + CHK_ERROR(write_regm(ci, 0x09, 0x08, 0x08)); /* Put TS in bypass */ + ci->cammode = -1; +#ifdef BUFFER_MODE + cam_mode(ci, 0); +#endif + } while (0); + mutex_unlock(&ci->lock); + + return 0; +} + + +static int read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address) +{ + struct cxd *ci = ca->data; + u8 val; + mutex_lock(&ci->lock); + set_mode(ci, 1); + read_pccard(ci, address, &val, 1); + mutex_unlock(&ci->lock); + return val; +} + + +static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, + int address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 1); + write_pccard(ci, address, &value, 1); + mutex_unlock(&ci->lock); + return 0; +} + +static int read_cam_control(struct dvb_ca_en50221 *ca, + int slot, u8 address) +{ + struct cxd *ci = ca->data; + u8 val; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + read_io(ci, address, &val); + mutex_unlock(&ci->lock); + return val; +} + +static int write_cam_control(struct dvb_ca_en50221 *ca, int slot, + u8 address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + write_io(ci, address, value); + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + cam_mode(ci, 0); + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + write_reg(ci, 0x00, 0x31); + write_regm(ci, 0x20, 0x80, 0x80); + write_reg(ci, 0x03, 0x02); + ci->ready = 0; + ci->mode = -1; + { + int i; + for (i = 0; i < 100; i++) { + msleep(10); + if (ci->ready) + break; + } + } + mutex_unlock(&ci->lock); + /* msleep(500); */ + return 0; +} + +static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + printk(KERN_INFO "slot_shutdown\n"); + mutex_lock(&ci->lock); + /* write_regm(ci, 0x09, 0x08, 0x08); */ + write_regm(ci, 0x20, 0x80, 0x80); + write_regm(ci, 0x06, 0x07, 0x07); + ci->mode = -1; + mutex_unlock(&ci->lock); + return 0; /* shutdown(ci); */ +} + +static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + write_regm(ci, 0x09, 0x00, 0x08); + set_mode(ci, 0); +#ifdef BUFFER_MODE + cam_mode(ci, 1); +#endif + mutex_unlock(&ci->lock); + return 0; +} + + +static int campoll(struct cxd *ci) +{ + u8 istat; + + read_reg(ci, 0x04, &istat); + if (!istat) + return 0; + write_reg(ci, 0x05, istat); + + if (istat&0x40) { + ci->dr = 1; + printk(KERN_INFO "DR\n"); + } + if (istat&0x20) + printk(KERN_INFO "WC\n"); + + if (istat&2) { + u8 slotstat; + + read_reg(ci, 0x01, &slotstat); + if (!(2&slotstat)) { + if (!ci->slot_stat) { + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_PRESENT; + write_regm(ci, 0x03, 0x08, 0x08); + } + + } else { + if (ci->slot_stat) { + ci->slot_stat = 0; + write_regm(ci, 0x03, 0x00, 0x08); + printk(KERN_INFO "NO CAM\n"); + ci->ready = 0; + } + } + if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { + ci->ready = 1; + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; + printk(KERN_INFO "READY\n"); + } + } + return 0; +} + + +static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct cxd *ci = ca->data; + u8 slotstat; + + mutex_lock(&ci->lock); + campoll(ci); + read_reg(ci, 0x01, &slotstat); + mutex_unlock(&ci->lock); + + return ci->slot_stat; +} + +#ifdef BUFFER_MODE +static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + u8 msb, lsb; + u16 len; + + mutex_lock(&ci->lock); + campoll(ci); + mutex_unlock(&ci->lock); + + printk(KERN_INFO "read_data\n"); + if (!ci->dr) + return 0; + + mutex_lock(&ci->lock); + read_reg(ci, 0x0f, &msb); + read_reg(ci, 0x10, &lsb); + len = (msb<<8)|lsb; + read_block(ci, 0x12, ebuf, len); + ci->dr = 0; + mutex_unlock(&ci->lock); + + return len; +} + +static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + printk(KERN_INFO "write_data %d\n", ecount); + write_reg(ci, 0x0d, ecount>>8); + write_reg(ci, 0x0e, ecount&0xff); + write_block(ci, 0x11, ebuf, ecount); + mutex_unlock(&ci->lock); + return ecount; +} +#endif + +static struct dvb_ca_en50221 en_templ = { + .read_attribute_mem = read_attribute_mem, + .write_attribute_mem = write_attribute_mem, + .read_cam_control = read_cam_control, + .write_cam_control = write_cam_control, + .slot_reset = slot_reset, + .slot_shutdown = slot_shutdown, + .slot_ts_enable = slot_ts_enable, + .poll_slot_status = poll_slot_status, +#ifdef BUFFER_MODE + .read_data = read_data, + .write_data = write_data, +#endif + +}; + +struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, + struct i2c_adapter *i2c) +{ + struct cxd *ci = 0; + u32 bitrate = 62000000; + u8 val; + + if (i2c_read_reg(i2c, adr, 0, &val) < 0) { + printk(KERN_ERR "No CXD2099 detected at %02x\n", adr); + return 0; + } + + ci = kmalloc(sizeof(struct cxd), GFP_KERNEL); + if (!ci) + return 0; + memset(ci, 0, sizeof(*ci)); + + mutex_init(&ci->lock); + ci->i2c = i2c; + ci->adr = adr; + ci->lastaddress = 0xff; + ci->clk_reg_b = 0x4a; + ci->clk_reg_f = 0x1b; + ci->bitrate = bitrate; + + memcpy(&ci->en, &en_templ, sizeof(en_templ)); + ci->en.data = ci; + init(ci); + printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->adr); + return &ci->en; +} +EXPORT_SYMBOL(cxd2099_attach); + +MODULE_DESCRIPTION("cxd2099"); +MODULE_AUTHOR("Ralph Metzler "); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/cxd2099/cxd2099.h b/drivers/staging/cxd2099/cxd2099.h new file mode 100644 index 0000000..a313dc2 --- /dev/null +++ b/drivers/staging/cxd2099/cxd2099.h @@ -0,0 +1,41 @@ +/* + * cxd2099.h: Driver for the CXD2099AR Common Interface Controller + * + * Copyright (C) 2010 DigitalDevices UG + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _CXD2099_H_ +#define _CXD2099_H_ + +#include + +#if defined(CONFIG_DVB_CXD2099) || \ + (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE)) +struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c); +#else +struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif -- cgit v0.10.2 From dbf05b279b8ed398a8e92b0c1187882f6df2bf7f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 17 Jan 2011 22:18:02 -0200 Subject: [media] ngene: Fix compilation when cxd2099 is not enabled drivers/media/dvb/ngene/ngene-i2c.o: In function `cxd2099_attach': ngene-i2c.c:(.text+0x195): multiple definition of `cxd2099_attach' drivers/media/dvb/ngene/ngene-core.o:ngene-core.c:(.text+0x100): first defined here drivers/media/dvb/ngene/ngene-cards.o: In function `cxd2099_attach': ngene-cards.c:(.text+0x116): multiple definition of `cxd2099_attach' drivers/media/dvb/ngene/ngene-core.o:ngene-core.c:(.text+0x100): first defined here drivers/media/dvb/ngene/ngene-dvb.o: In function `cxd2099_attach': ngene-dvb.c:(.text+0x7ad): multiple definition of `cxd2099_attach' drivers/media/dvb/ngene/ngene-core.o:ngene-core.c:(.text+0x100): first defined here Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/cxd2099/cxd2099.h b/drivers/staging/cxd2099/cxd2099.h index a313dc2..bed54ff 100644 --- a/drivers/staging/cxd2099/cxd2099.h +++ b/drivers/staging/cxd2099/cxd2099.h @@ -31,7 +31,7 @@ (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE)) struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c); #else -struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c) +static inline struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; -- cgit v0.10.2 From e007a32526e9c5fc2746f0b84a32d3355f129634 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Wed, 19 Jan 2011 13:02:29 -0200 Subject: [media] v4l: vivi: port to videobuf2 Make vivi use videobuf2 in place of videobuf. Signed-off-by: Pawel Osciak Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park CC: Pawel Osciak Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index f86e407..d40a8fc 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -549,7 +549,7 @@ config VIDEO_VIVI depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE select FONT_8x16 - select VIDEOBUF_VMALLOC + select VIDEOBUF2_VMALLOC default n ---help--- Enables a virtual video driver. This device shows a color bar diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index c49c393..9ed3831 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -7,6 +7,9 @@ * John Sokol * http://v4l.videotechnology.com/ * + * Conversion to videobuf2 by Pawel Osciak & Marek Szyprowski + * Copyright (c) 2010 Samsung Electronics + * * This program is free software; you can redistribute it and/or modify * it under the terms of the BSD Licence, GNU General Public License * as published by the Free Software Foundation; either version 2 of the @@ -23,10 +26,8 @@ #include #include #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) #include -#endif -#include +#include #include #include #include @@ -42,7 +43,7 @@ #define MAX_HEIGHT 1200 #define VIVI_MAJOR_VERSION 0 -#define VIVI_MINOR_VERSION 7 +#define VIVI_MINOR_VERSION 8 #define VIVI_RELEASE 0 #define VIVI_VERSION \ KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE) @@ -133,16 +134,11 @@ static struct vivi_fmt *get_format(struct v4l2_format *f) return &formats[k]; } -struct sg_to_addr { - int pos; - struct scatterlist *sg; -}; - /* buffer for one video frame */ struct vivi_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - + struct vb2_buffer vb; + struct list_head list; struct vivi_fmt *fmt; }; @@ -190,9 +186,11 @@ struct vivi_dev { /* video capture */ struct vivi_fmt *fmt; unsigned int width, height; - struct videobuf_queue vb_vidq; + struct vb2_queue vb_vidq; + enum v4l2_field field; + unsigned int field_count; - unsigned long generating; + unsigned int open_count; u8 bars[9][3]; u8 line[MAX_WIDTH * 4]; }; @@ -443,10 +441,10 @@ static void gen_text(struct vivi_dev *dev, char *basep, static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { - int hmax = buf->vb.height; - int wmax = buf->vb.width; + int wmax = dev->width; + int hmax = dev->height; struct timeval ts; - void *vbuf = videobuf_to_vmalloc(&buf->vb); + void *vbuf = vb2_plane_vaddr(&buf->vb, 0); unsigned ms; char str[100]; int h, line = 1; @@ -483,11 +481,11 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->mv_count += 2; - /* Advice that buffer was filled */ - buf->vb.field_count++; + buf->vb.v4l2_buf.field = dev->field; + dev->field_count++; + buf->vb.v4l2_buf.sequence = dev->field_count >> 1; do_gettimeofday(&ts); - buf->vb.ts = ts; - buf->vb.state = VIDEOBUF_DONE; + buf->vb.v4l2_buf.timestamp = ts; } static void vivi_thread_tick(struct vivi_dev *dev) @@ -504,23 +502,17 @@ static void vivi_thread_tick(struct vivi_dev *dev) goto unlock; } - buf = list_entry(dma_q->active.next, - struct vivi_buffer, vb.queue); - - /* Nobody is waiting on this buffer, return */ - if (!waitqueue_active(&buf->vb.done)) - goto unlock; + buf = list_entry(dma_q->active.next, struct vivi_buffer, list); + list_del(&buf->list); - list_del(&buf->vb.queue); - - do_gettimeofday(&buf->vb.ts); + do_gettimeofday(&buf->vb.v4l2_buf.timestamp); /* Fill buffer */ vivi_fillbuff(dev, buf); dprintk(dev, 1, "filled buffer %p\n", buf); - wake_up(&buf->vb.done); - dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); unlock: spin_unlock_irqrestore(&dev->slock, flags); } @@ -571,17 +563,12 @@ static int vivi_thread(void *data) return 0; } -static void vivi_start_generating(struct file *file) +static int vivi_start_generating(struct vivi_dev *dev) { - struct vivi_dev *dev = video_drvdata(file); struct vivi_dmaqueue *dma_q = &dev->vidq; dprintk(dev, 1, "%s\n", __func__); - if (test_and_set_bit(0, &dev->generating)) - return; - file->private_data = dev; - /* Resets frame counters */ dev->ms = 0; dev->mv_count = 0; @@ -593,146 +580,200 @@ static void vivi_start_generating(struct file *file) if (IS_ERR(dma_q->kthread)) { v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); - clear_bit(0, &dev->generating); - return; + return PTR_ERR(dma_q->kthread); } /* Wakes thread */ wake_up_interruptible(&dma_q->wq); dprintk(dev, 1, "returning from %s\n", __func__); + return 0; } -static void vivi_stop_generating(struct file *file) +static void vivi_stop_generating(struct vivi_dev *dev) { - struct vivi_dev *dev = video_drvdata(file); struct vivi_dmaqueue *dma_q = &dev->vidq; dprintk(dev, 1, "%s\n", __func__); - if (!file->private_data) - return; - if (!test_and_clear_bit(0, &dev->generating)) - return; - /* shutdown control thread */ if (dma_q->kthread) { kthread_stop(dma_q->kthread); dma_q->kthread = NULL; } - videobuf_stop(&dev->vb_vidq); - videobuf_mmap_free(&dev->vb_vidq); -} -static int vivi_is_generating(struct vivi_dev *dev) -{ - return test_bit(0, &dev->generating); + /* + * Typical driver might need to wait here until dma engine stops. + * In this case we can abort imiedetly, so it's just a noop. + */ + + /* Release all active buffers */ + while (!list_empty(&dma_q->active)) { + struct vivi_buffer *buf; + buf = list_entry(dma_q->active.next, struct vivi_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); + } } - /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned long sizes[], + void *alloc_ctxs[]) { - struct vivi_dev *dev = vq->priv_data; + struct vivi_dev *dev = vb2_get_drv_priv(vq); + unsigned long size; + + size = dev->width * dev->height * 2; - *size = dev->width * dev->height * 2; + if (0 == *nbuffers) + *nbuffers = 32; - if (0 == *count) - *count = 32; + while (size * *nbuffers > vid_limit * 1024 * 1024) + (*nbuffers)--; - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; + *nplanes = 1; - dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__, - *count, *size); + sizes[0] = size; + + /* + * videobuf2-vmalloc allocator is context-less so no need to set + * alloc_ctxs array. + */ + + dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__, + *nbuffers, size); return 0; } -static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf) +static int buffer_init(struct vb2_buffer *vb) { - struct vivi_dev *dev = vq->priv_data; + struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state); + BUG_ON(NULL == dev->fmt); - videobuf_vmalloc_free(&buf->vb); - dprintk(dev, 1, "free_buffer: freed\n"); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + /* + * This callback is called once per buffer, after its allocation. + * + * Vivi does not allow changing format during streaming, but it is + * possible to do so when streaming is paused (i.e. in streamoff state). + * Buffers however are not freed when going into streamoff and so + * buffer size verification has to be done in buffer_prepare, on each + * qbuf. + * It would be best to move verification code here to buf_init and + * s_fmt though. + */ + + return 0; } -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) +static int buffer_prepare(struct vb2_buffer *vb) { - struct vivi_dev *dev = vq->priv_data; + struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); - int rc; + unsigned long size; - dprintk(dev, 1, "%s, field=%d\n", __func__, field); + dprintk(dev, 1, "%s, field=%d\n", __func__, vb->v4l2_buf.field); BUG_ON(NULL == dev->fmt); + /* + * Theses properties only change when queue is idle, see s_fmt. + * The below checks should not be performed here, on each + * buffer_prepare (i.e. on each qbuf). Most of the code in this function + * should thus be moved to buffer_init and s_fmt. + */ if (dev->width < 48 || dev->width > MAX_WIDTH || dev->height < 32 || dev->height > MAX_HEIGHT) return -EINVAL; - buf->vb.size = dev->width * dev->height * 2; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + size = dev->width * dev->height * 2; + if (vb2_plane_size(vb, 0) < size) { + dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); return -EINVAL; + } + + vb2_set_plane_payload(&buf->vb, 0, size); - /* These properties only change when queue is idle, see s_fmt */ - buf->fmt = dev->fmt; - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; + buf->fmt = dev->fmt; precalculate_bars(dev); precalculate_line(dev); - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } + return 0; +} - buf->vb.state = VIDEOBUF_PREPARED; +static int buffer_finish(struct vb2_buffer *vb) +{ + struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + dprintk(dev, 1, "%s\n", __func__); return 0; +} + +static void buffer_cleanup(struct vb2_buffer *vb) +{ + struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + dprintk(dev, 1, "%s\n", __func__); -fail: - free_buffer(vq, buf); - return rc; } -static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +static void buffer_queue(struct vb2_buffer *vb) { - struct vivi_dev *dev = vq->priv_data; + struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); struct vivi_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; dprintk(dev, 1, "%s\n", __func__); - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&dev->slock, flags); } -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static int start_streaming(struct vb2_queue *vq) { - struct vivi_dev *dev = vq->priv_data; - struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); + struct vivi_dev *dev = vb2_get_drv_priv(vq); + dprintk(dev, 1, "%s\n", __func__); + return vivi_start_generating(dev); +} +/* abort streaming and wait for last buffer */ +static int stop_streaming(struct vb2_queue *vq) +{ + struct vivi_dev *dev = vb2_get_drv_priv(vq); dprintk(dev, 1, "%s\n", __func__); + vivi_stop_generating(dev); + return 0; +} + +static void vivi_lock(struct vb2_queue *vq) +{ + struct vivi_dev *dev = vb2_get_drv_priv(vq); + mutex_lock(&dev->mutex); +} - free_buffer(vq, buf); +static void vivi_unlock(struct vb2_queue *vq) +{ + struct vivi_dev *dev = vb2_get_drv_priv(vq); + mutex_unlock(&dev->mutex); } -static struct videobuf_queue_ops vivi_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, + +static struct vb2_ops vivi_video_qops = { + .queue_setup = queue_setup, + .buf_init = buffer_init, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_cleanup = buffer_cleanup, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .wait_prepare = vivi_unlock, + .wait_finish = vivi_lock, }; /* ------------------------------------------------------------------ @@ -774,7 +815,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; - f->fmt.pix.field = dev->vb_vidq.field; + f->fmt.pix.field = dev->field; f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = (f->fmt.pix.width * dev->fmt->depth) >> 3; @@ -820,82 +861,60 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_dev *dev = video_drvdata(file); + struct vb2_queue *q = &dev->vb_vidq; int ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret < 0) return ret; - if (vivi_is_generating(dev)) { + if (vb2_is_streaming(q)) { dprintk(dev, 1, "%s device busy\n", __func__); - ret = -EBUSY; - goto out; + return -EBUSY; } dev->fmt = get_format(f); dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; - dev->vb_vidq.field = f->fmt.pix.field; - ret = 0; -out: - return ret; + dev->field = f->fmt.pix.field; + + return 0; } static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { struct vivi_dev *dev = video_drvdata(file); - - return videobuf_reqbufs(&dev->vb_vidq, p); + return vb2_reqbufs(&dev->vb_vidq, p); } static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct vivi_dev *dev = video_drvdata(file); - - return videobuf_querybuf(&dev->vb_vidq, p); + return vb2_querybuf(&dev->vb_vidq, p); } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct vivi_dev *dev = video_drvdata(file); - - return videobuf_qbuf(&dev->vb_vidq, p); + return vb2_qbuf(&dev->vb_vidq, p); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct vivi_dev *dev = video_drvdata(file); - - return videobuf_dqbuf(&dev->vb_vidq, p, - file->f_flags & O_NONBLOCK); + return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK); } static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct vivi_dev *dev = video_drvdata(file); - int ret; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - ret = videobuf_streamon(&dev->vb_vidq); - if (ret) - return ret; - - vivi_start_generating(file); - return 0; + return vb2_streamon(&dev->vb_vidq, i); } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { struct vivi_dev *dev = video_drvdata(file); - int ret; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - ret = videobuf_streamoff(&dev->vb_vidq); - if (!ret) - vivi_stop_generating(file); - return ret; + return vb2_streamoff(&dev->vb_vidq, i); } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) @@ -1018,26 +1037,33 @@ static int vidioc_s_ctrl(struct file *file, void *priv, File operations for the device ------------------------------------------------------------------*/ +static int vivi_open(struct file *file) +{ + struct vivi_dev *dev = video_drvdata(file); + + dprintk(dev, 1, "%s, %p\n", __func__, file); + dev->open_count++; + return 0; +} + static ssize_t vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { struct vivi_dev *dev = video_drvdata(file); - vivi_start_generating(file); - return videobuf_read_stream(&dev->vb_vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); + dprintk(dev, 1, "read called\n"); + return vb2_read(&dev->vb_vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); } static unsigned int vivi_poll(struct file *file, struct poll_table_struct *wait) { struct vivi_dev *dev = video_drvdata(file); - struct videobuf_queue *q = &dev->vb_vidq; + struct vb2_queue *q = &dev->vb_vidq; dprintk(dev, 1, "%s\n", __func__); - - vivi_start_generating(file); - return videobuf_poll_stream(file, q, wait); + return vb2_poll(q, file, wait); } static int vivi_close(struct file *file) @@ -1045,10 +1071,11 @@ static int vivi_close(struct file *file) struct video_device *vdev = video_devdata(file); struct vivi_dev *dev = video_drvdata(file); - vivi_stop_generating(file); + dprintk(dev, 1, "close called (dev=%s), file %p\n", + video_device_node_name(vdev), file); - dprintk(dev, 1, "close called (dev=%s)\n", - video_device_node_name(vdev)); + if (--dev->open_count == 0) + vb2_queue_release(&dev->vb_vidq); return 0; } @@ -1059,8 +1086,7 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma) dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); - ret = videobuf_mmap_mapper(&dev->vb_vidq, vma); - + ret = vb2_mmap(&dev->vb_vidq, vma); dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, @@ -1070,6 +1096,7 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma) static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, + .open = vivi_open, .release = vivi_close, .read = vivi_read, .poll = vivi_poll, @@ -1136,6 +1163,7 @@ static int __init vivi_create_instance(int inst) { struct vivi_dev *dev; struct video_device *vfd; + struct vb2_queue *q; int ret; dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -1159,12 +1187,20 @@ static int __init vivi_create_instance(int inst) /* initialize locks */ spin_lock_init(&dev->slock); - mutex_init(&dev->mutex); - videobuf_queue_vmalloc_init(&dev->vb_vidq, &vivi_video_qops, - NULL, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct vivi_buffer), dev, &dev->mutex); + /* initialize queue */ + q = &dev->vb_vidq; + memset(q, 0, sizeof(dev->vb_vidq)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivi_buffer); + q->ops = &vivi_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + + vb2_queue_init(q); + + mutex_init(&dev->mutex); /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); @@ -1178,6 +1214,11 @@ static int __init vivi_create_instance(int inst) *vfd = vivi_template; vfd->debug = debug; vfd->v4l2_dev = &dev->v4l2_dev; + + /* + * Provide a mutex to v4l2 core. It will be used to protect + * all fops and v4l2 ioctls. + */ vfd->lock = &dev->mutex; ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); -- cgit v0.10.2 From db4d56835e07ec793f143431170ba2fc17bf0191 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 16 Jan 2011 17:21:02 -0300 Subject: [media] DocBook/v4l: fix validation errors Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/planar-apis.xml b/Documentation/DocBook/v4l/planar-apis.xml index 8be7552..7f3f552 100644 --- a/Documentation/DocBook/v4l/planar-apis.xml +++ b/Documentation/DocBook/v4l/planar-apis.xml @@ -53,28 +53,28 @@ &VIDIOC-QUERYCAP; - Two additional multi-planar capabilities are added. They can + Two additional multi-planar capabilities are added. They can be set together with non-multi-planar ones for devices that handle - both single- and multi-planar formats. + both single- and multi-planar formats. &VIDIOC-G-FMT;, &VIDIOC-S-FMT;, &VIDIOC-TRY-FMT; - New structures for describing multi-planar formats are added: + New structures for describing multi-planar formats are added: &v4l2-pix-format-mplane; and &v4l2-plane-pix-format;. Drivers may define new multi-planar formats, which have distinct FourCC codes from - the existing single-planar ones. + the existing single-planar ones. &VIDIOC-QBUF;, &VIDIOC-DQBUF;, &VIDIOC-QUERYBUF; - A new &v4l2-plane; structure for describing planes is added. + A new &v4l2-plane; structure for describing planes is added. Arrays of this structure are passed in the new - m.planes field of &v4l2-buffer;. + m.planes field of &v4l2-buffer;. &VIDIOC-REQBUFS; - Will allocate multi-planar buffers as requested. + Will allocate multi-planar buffers as requested. diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml index 9c7a175..c3d6991 100644 --- a/Documentation/DocBook/v4l/v4l2.xml +++ b/Documentation/DocBook/v4l/v4l2.xml @@ -129,6 +129,7 @@ applications. --> 2.6.38 + 2011-01-16 po Added the multi-planar API. -- cgit v0.10.2 From 0cda12556bf3ebe47ee98eb6767a085c204ee35e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Jan 2011 16:05:30 -0200 Subject: [media] tuner-simple: add support for Tena TNF5337 MFD Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c index 58a513b..afba6dc 100644 --- a/drivers/media/common/tuners/tuner-types.c +++ b/drivers/media/common/tuners/tuner-types.c @@ -971,6 +971,22 @@ static struct tuner_params tuner_tena_9533_di_params[] = { }, }; +/* ------------ TUNER_TENA_TNF_5337 - Tena tnf5337MFD STD M/N ------------ */ + +static struct tuner_range tuner_tena_tnf_5337_ntsc_ranges[] = { + { 16 * 166.25 /*MHz*/, 0x86, 0x01, }, + { 16 * 466.25 /*MHz*/, 0x86, 0x02, }, + { 16 * 999.99 , 0x86, 0x08, }, +}; + +static struct tuner_params tuner_tena_tnf_5337_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tena_tnf_5337_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tena_tnf_5337_ntsc_ranges), + }, +}; + /* ------------ TUNER_PHILIPS_FMD1216ME(X)_MK3 - Philips PAL ------------ */ static struct tuner_range tuner_philips_fmd1216me_mk3_pal_ranges[] = { @@ -1842,6 +1858,11 @@ struct tunertype tuners[] = { .params = tuner_philips_fq1236_mk5_params, .count = ARRAY_SIZE(tuner_philips_fq1236_mk5_params), }, + [TUNER_TENA_TNF_5337] = { /* Tena 5337 MFD */ + .name = "Tena TNF5337 MFD", + .params = tuner_tena_tnf_5337_params, + .count = ARRAY_SIZE(tuner_tena_tnf_5337_params), + }, }; EXPORT_SYMBOL(tuners); diff --git a/include/media/tuner.h b/include/media/tuner.h index 51811eac..5eec529 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -131,6 +131,7 @@ #define TUNER_NXP_TDA18271 83 #define TUNER_SONY_BTF_PXN01Z 84 #define TUNER_PHILIPS_FQ1236_MK5 85 /* NTSC, TDA9885, no FM radio */ +#define TUNER_TENA_TNF_5337 86 /* tv card specific */ #define TDA9887_PRESENT (1<<0) -- cgit v0.10.2 From 99fb079aad031712283d555aa87251706aa60212 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Jan 2011 16:11:58 -0200 Subject: [media] saa7134: Properly report when a board doesn't have eeprom When a device is not detected, the driver prints a warning message. That's ok, except for the fact that, even on newer devices that have eeprom but aren't yet supported, it prints a message saying that the device doesn't have eeprom. Fix it by having a separate message for devices with eeprom, but not yet known by the driver. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 6abeecf..41f836f 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -752,19 +752,28 @@ static int saa7134_hwfini(struct saa7134_dev *dev) return 0; } -static void __devinit must_configure_manually(void) +static void __devinit must_configure_manually(int has_eeprom) { unsigned int i,p; - printk(KERN_WARNING - "saa7134: \n" - "saa7134: Congratulations! Your TV card vendor saved a few\n" - "saa7134: cents for a eeprom, thus your pci board has no\n" - "saa7134: subsystem ID and I can't identify it automatically\n" - "saa7134: \n" - "saa7134: I feel better now. Ok, here are the good news:\n" - "saa7134: You can use the card= insmod option to specify\n" - "saa7134: which board do you have. The list:\n"); + if (!has_eeprom) + printk(KERN_WARNING + "saa7134: \n" + "saa7134: Congratulations! Your TV card vendor saved a few\n" + "saa7134: cents for a eeprom, thus your pci board has no\n" + "saa7134: subsystem ID and I can't identify it automatically\n" + "saa7134: \n" + "saa7134: I feel better now. Ok, here are the good news:\n" + "saa7134: You can use the card= insmod option to specify\n" + "saa7134: which board do you have. The list:\n"); + else + printk(KERN_WARNING + "saa7134: Board is currently unknown. You might try to use the card=\n" + "saa7134: insmod option to specify which board do you have, but this is\n" + "saa7134: somewhat risky, as might damage your card. It is better to ask\n" + "saa7134: for support at linux-media@vger.kernel.org.\n" + "saa7134: The supported cards are:\n"); + for (i = 0; i < saa7134_bcount; i++) { printk(KERN_WARNING "saa7134: card=%d -> %-40.40s", i,saa7134_boards[i].name); @@ -936,8 +945,10 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, if (card[dev->nr] >= 0 && card[dev->nr] < saa7134_bcount) dev->board = card[dev->nr]; - if (SAA7134_BOARD_NOAUTO == dev->board) { - must_configure_manually(); + if (SAA7134_BOARD_UNKNOWN == dev->board) + must_configure_manually(0); + else if (SAA7134_BOARD_NOAUTO == dev->board) { + must_configure_manually(1); dev->board = SAA7134_BOARD_UNKNOWN; } dev->autodetected = card[dev->nr] != dev->board; -- cgit v0.10.2 From d642f2f3c5be4737ec021c6d6e9cda99262564f8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Jan 2011 16:25:17 -0200 Subject: [media] add support for Encore FM3 Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index deb8fcf..74467c1 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3620,6 +3620,38 @@ struct saa7134_board saa7134_boards[] = { .amux = 0, }, }, + [SAA7134_BOARD_ENCORE_ENLTV_FM3] = { + .name = "Encore ENLTV-FM 3", + .audio_clock = 0x02187de7, + .tuner_type = TUNER_TENA_TNF_5337, + .radio_type = TUNER_TEA5767, + .tuner_addr = 0x61, + .radio_addr = 0x60, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .vmux = 1, + .amux = LINE1, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + .gpio = 0x43000, + }, + }, [SAA7134_BOARD_CINERGY_HT_PCI] = { .name = "Terratec Cinergy HT PCI", .audio_clock = 0x00187de7, @@ -6387,6 +6419,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53, }, { .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1a7f, + .subdevice = 0x2108, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM3, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x153b, .subdevice = 0x1175, @@ -7102,6 +7140,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_ENCORE_ENLTV: case SAA7134_BOARD_ENCORE_ENLTV_FM: case SAA7134_BOARD_ENCORE_ENLTV_FM53: + case SAA7134_BOARD_ENCORE_ENLTV_FM3: case SAA7134_BOARD_10MOONSTVMASTER3: case SAA7134_BOARD_BEHOLD_401: case SAA7134_BOARD_BEHOLD_403: diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index dc646e6..c9eff03 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -681,6 +681,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) polling = 50; // ms break; case SAA7134_BOARD_ENCORE_ENLTV_FM53: + case SAA7134_BOARD_ENCORE_ENLTV_FM3: ir_codes = RC_MAP_ENCORE_ENLTV_FM53; mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ mask_keyup = 0x0040000; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 5b0a347..f96cd5d 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -327,6 +327,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181 #define SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG 182 #define SAA7134_BOARD_VIDEOMATE_M1F 183 +#define SAA7134_BOARD_ENCORE_ENLTV_FM3 184 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 -- cgit v0.10.2 From 1f075d1f1bfb7101be9a651a4d087193b4f05b23 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 9 Nov 2010 22:31:40 -0200 Subject: [media] technisat-usb2: CodingStyle cleanups Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/dvb/dvb-usb/technisat-usb2.c index 10151aa..08f8842 100644 --- a/drivers/media/dvb/dvb-usb/technisat-usb2.c +++ b/drivers/media/dvb/dvb-usb/technisat-usb2.c @@ -331,11 +331,10 @@ static int technisat_usb2_identify_state(struct usb_device *udev, u8 version[3]; /* first select the interface */ - if (usb_set_interface(udev, 0, 1) != 0) { + if (usb_set_interface(udev, 0, 1) != 0) err("could not set alternate setting to 0"); - } else { + else info("set alternate setting"); - } *cold = 0; /* by default do not download a firmware - just in case something is wrong */ @@ -378,7 +377,7 @@ static void technisat_usb2_eeprom_dump(struct dvb_usb_device *d) { u8 reg; u8 b[16]; - int i,j; + int i, j; /* full EEPROM dump */ for (j = 0; j < 256 * 4; j += 16) { @@ -496,9 +495,9 @@ static struct stv090x_config technisat_usb2_stv090x_config = { }; static struct stv6110x_config technisat_usb2_stv6110x_config = { - .addr = 0x60, - .refclk = 16000000, - .clk_div = 2, + .addr = 0x60, + .refclk = 16000000, + .clk_div = 2, }; static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a) -- cgit v0.10.2 From 34a078da8e20ca40a09bfa635ff2c4e2c4b648b1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Dec 2010 07:53:28 -0300 Subject: [media] cs5345: use the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c index 9358fe7..5909f25 100644 --- a/drivers/media/video/cs5345.c +++ b/drivers/media/video/cs5345.c @@ -25,6 +25,7 @@ #include #include #include +#include MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC"); MODULE_AUTHOR("Hans Verkuil"); @@ -36,6 +37,20 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); +struct cs5345_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; +}; + +static inline struct cs5345_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cs5345_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cs5345_state, hdl)->sd; +} /* ----------------------------------------------------------------------- */ @@ -65,33 +80,20 @@ static int cs5345_s_routing(struct v4l2_subdev *sd, return 0; } -static int cs5345_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int cs5345_s_ctrl(struct v4l2_ctrl *ctrl) { - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - ctrl->value = (cs5345_read(sd, 0x04) & 0x08) != 0; - return 0; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - ctrl->value = cs5345_read(sd, 0x07) & 0x3f; - if (ctrl->value >= 32) - ctrl->value = ctrl->value - 64; - return 0; -} + struct v4l2_subdev *sd = to_sd(ctrl); -static int cs5345_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - cs5345_write(sd, 0x04, ctrl->value ? 0x80 : 0); + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + cs5345_write(sd, 0x04, ctrl->val ? 0x80 : 0); + return 0; + case V4L2_CID_AUDIO_VOLUME: + cs5345_write(sd, 0x07, ((u8)ctrl->val) & 0x3f); + cs5345_write(sd, 0x08, ((u8)ctrl->val) & 0x3f); return 0; } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - if (ctrl->value > 24 || ctrl->value < -24) - return -EINVAL; - cs5345_write(sd, 0x07, ((u8)ctrl->value) & 0x3f); - cs5345_write(sd, 0x08, ((u8)ctrl->value) & 0x3f); - return 0; + return -EINVAL; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -144,11 +146,20 @@ static int cs5345_log_status(struct v4l2_subdev *sd) /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops cs5345_ctrl_ops = { + .s_ctrl = cs5345_s_ctrl, +}; + static const struct v4l2_subdev_core_ops cs5345_core_ops = { .log_status = cs5345_log_status, .g_chip_ident = cs5345_g_chip_ident, - .g_ctrl = cs5345_g_ctrl, - .s_ctrl = cs5345_s_ctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = cs5345_g_register, .s_register = cs5345_s_register, @@ -169,6 +180,7 @@ static const struct v4l2_subdev_ops cs5345_ops = { static int cs5345_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct cs5345_state *state; struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ @@ -178,11 +190,28 @@ static int cs5345_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) + state = kzalloc(sizeof(struct cs5345_state), GFP_KERNEL); + if (state == NULL) return -ENOMEM; + sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &cs5345_ops); + v4l2_ctrl_handler_init(&state->hdl, 2); + v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -24, 24, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + /* set volume/mute */ + v4l2_ctrl_handler_setup(&state->hdl); + cs5345_write(sd, 0x02, 0x00); cs5345_write(sd, 0x04, 0x01); cs5345_write(sd, 0x09, 0x01); @@ -194,9 +223,11 @@ static int cs5345_probe(struct i2c_client *client, static int cs5345_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cs5345_state *state = to_state(sd); v4l2_device_unregister_subdev(sd); - kfree(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); return 0; } -- cgit v0.10.2 From a75b9be1c2fb52dee765d35f29031dd788d522eb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 31 Dec 2010 10:22:52 -0300 Subject: [media] cx18: Use the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c index 43d09a2..4a24ffb 100644 --- a/drivers/media/video/cx18/cx18-av-audio.c +++ b/drivers/media/video/cx18/cx18-av-audio.c @@ -342,17 +342,6 @@ void cx18_av_audio_set_path(struct cx18 *cx) } } -static int get_volume(struct cx18 *cx) -{ - /* Volume runs +18dB to -96dB in 1/2dB steps - * change to fit the msp3400 -114dB to +12dB range */ - - /* check PATH1_VOLUME */ - int vol = 228 - cx18_av_read(cx, 0x8d4); - vol = (vol / 2) + 23; - return vol << 9; -} - static void set_volume(struct cx18 *cx, int volume) { /* First convert the volume to msp3400 values (0-127) */ @@ -369,52 +358,18 @@ static void set_volume(struct cx18 *cx, int volume) cx18_av_write(cx, 0x8d4, 228 - (vol * 2)); } -static int get_bass(struct cx18 *cx) -{ - /* bass is 49 steps +12dB to -12dB */ - - /* check PATH1_EQ_BASS_VOL */ - int bass = cx18_av_read(cx, 0x8d9) & 0x3f; - bass = (((48 - bass) * 0xffff) + 47) / 48; - return bass; -} - static void set_bass(struct cx18 *cx, int bass) { /* PATH1_EQ_BASS_VOL */ cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff)); } -static int get_treble(struct cx18 *cx) -{ - /* treble is 49 steps +12dB to -12dB */ - - /* check PATH1_EQ_TREBLE_VOL */ - int treble = cx18_av_read(cx, 0x8db) & 0x3f; - treble = (((48 - treble) * 0xffff) + 47) / 48; - return treble; -} - static void set_treble(struct cx18 *cx, int treble) { /* PATH1_EQ_TREBLE_VOL */ cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff)); } -static int get_balance(struct cx18 *cx) -{ - /* balance is 7 bit, 0 to -96dB */ - - /* check PATH1_BAL_LEVEL */ - int balance = cx18_av_read(cx, 0x8d5) & 0x7f; - /* check PATH1_BAL_LEFT */ - if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0) - balance = 0x80 - balance; - else - balance = 0x80 + balance; - return balance << 8; -} - static void set_balance(struct cx18 *cx, int balance) { int bal = balance >> 8; @@ -431,12 +386,6 @@ static void set_balance(struct cx18 *cx, int balance) } } -static int get_mute(struct cx18 *cx) -{ - /* check SRC1_MUTE_EN */ - return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0; -} - static void set_mute(struct cx18 *cx, int mute) { struct cx18_av_state *state = &cx->av_state; @@ -490,50 +439,33 @@ int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) return retval; } -int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl) +static int cx18_av_audio_s_ctrl(struct v4l2_ctrl *ctrl) { - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = get_volume(cx); - break; - case V4L2_CID_AUDIO_BASS: - ctrl->value = get_bass(cx); - break; - case V4L2_CID_AUDIO_TREBLE: - ctrl->value = get_treble(cx); - break; - case V4L2_CID_AUDIO_BALANCE: - ctrl->value = get_balance(cx); - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = get_mute(cx); - break; - default: - return -EINVAL; - } - return 0; -} + struct v4l2_subdev *sd = to_sd(ctrl); + struct cx18 *cx = v4l2_get_subdevdata(sd); -int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl) -{ switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: - set_volume(cx, ctrl->value); + set_volume(cx, ctrl->val); break; case V4L2_CID_AUDIO_BASS: - set_bass(cx, ctrl->value); + set_bass(cx, ctrl->val); break; case V4L2_CID_AUDIO_TREBLE: - set_treble(cx, ctrl->value); + set_treble(cx, ctrl->val); break; case V4L2_CID_AUDIO_BALANCE: - set_balance(cx, ctrl->value); + set_balance(cx, ctrl->val); break; case V4L2_CID_AUDIO_MUTE: - set_mute(cx, ctrl->value); + set_mute(cx, ctrl->val); break; default: return -EINVAL; } return 0; } + +const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops = { + .s_ctrl = cx18_av_audio_s_ctrl, +}; diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index a41951c..f164b7f 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -129,6 +129,7 @@ static void cx18_av_initialize(struct v4l2_subdev *sd) { struct cx18_av_state *state = to_cx18_av_state(sd); struct cx18 *cx = v4l2_get_subdevdata(sd); + int default_volume; u32 v; cx18_av_loadfw(cx); @@ -247,8 +248,23 @@ static void cx18_av_initialize(struct v4l2_subdev *sd) /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */ /* } */ cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F); - state->default_volume = 228 - cx18_av_read(cx, 0x8d4); - state->default_volume = ((state->default_volume / 2) + 23) << 9; + default_volume = cx18_av_read(cx, 0x8d4); + /* + * Enforce the legacy volume scale mapping limits to avoid + * -ERANGE errors when initializing the volume control + */ + if (default_volume > 228) { + /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */ + default_volume = 228; + cx18_av_write(cx, 0x8d4, 228); + } else if (default_volume < 20) { + /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */ + default_volume = 20; + cx18_av_write(cx, 0x8d4, 20); + } + default_volume = (((228 - default_volume) >> 1) + 23) << 9; + state->volume->cur.val = state->volume->default_value = default_volume; + v4l2_ctrl_handler_setup(&state->hdl); } static int cx18_av_reset(struct v4l2_subdev *sd, u32 val) @@ -901,126 +917,35 @@ static int cx18_av_s_radio(struct v4l2_subdev *sd) return 0; } -static int cx18_av_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct cx18 *cx = v4l2_get_subdevdata(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - if (ctrl->value < 0 || ctrl->value > 255) { - CX18_ERR_DEV(sd, "invalid brightness setting %d\n", - ctrl->value); - return -ERANGE; - } - - cx18_av_write(cx, 0x414, ctrl->value - 128); + cx18_av_write(cx, 0x414, ctrl->val - 128); break; case V4L2_CID_CONTRAST: - if (ctrl->value < 0 || ctrl->value > 127) { - CX18_ERR_DEV(sd, "invalid contrast setting %d\n", - ctrl->value); - return -ERANGE; - } - - cx18_av_write(cx, 0x415, ctrl->value << 1); + cx18_av_write(cx, 0x415, ctrl->val << 1); break; case V4L2_CID_SATURATION: - if (ctrl->value < 0 || ctrl->value > 127) { - CX18_ERR_DEV(sd, "invalid saturation setting %d\n", - ctrl->value); - return -ERANGE; - } - - cx18_av_write(cx, 0x420, ctrl->value << 1); - cx18_av_write(cx, 0x421, ctrl->value << 1); + cx18_av_write(cx, 0x420, ctrl->val << 1); + cx18_av_write(cx, 0x421, ctrl->val << 1); break; case V4L2_CID_HUE: - if (ctrl->value < -128 || ctrl->value > 127) { - CX18_ERR_DEV(sd, "invalid hue setting %d\n", - ctrl->value); - return -ERANGE; - } - - cx18_av_write(cx, 0x422, ctrl->value); + cx18_av_write(cx, 0x422, ctrl->val); break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_MUTE: - return cx18_av_audio_s_ctrl(cx, ctrl); - - default: - return -EINVAL; - } - return 0; -} - -static int cx18_av_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct cx18 *cx = v4l2_get_subdevdata(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128; - break; - case V4L2_CID_CONTRAST: - ctrl->value = cx18_av_read(cx, 0x415) >> 1; - break; - case V4L2_CID_SATURATION: - ctrl->value = cx18_av_read(cx, 0x420) >> 1; - break; - case V4L2_CID_HUE: - ctrl->value = (s8)cx18_av_read(cx, 0x422); - break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_MUTE: - return cx18_av_audio_g_ctrl(cx, ctrl); default: return -EINVAL; } return 0; } -static int cx18_av_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - struct cx18_av_state *state = to_cx18_av_state(sd); - - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - default: - break; - } - - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, - 65535 / 100, state->default_volume); - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - default: - return -EINVAL; - } - return -EINVAL; -} - static int cx18_av_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) { struct cx18_av_state *state = to_cx18_av_state(sd); @@ -1356,14 +1281,22 @@ static int cx18_av_s_register(struct v4l2_subdev *sd, } #endif +static const struct v4l2_ctrl_ops cx18_av_ctrl_ops = { + .s_ctrl = cx18_av_s_ctrl, +}; + static const struct v4l2_subdev_core_ops cx18_av_general_ops = { .g_chip_ident = cx18_av_g_chip_ident, .log_status = cx18_av_log_status, .load_fw = cx18_av_load_fw, .reset = cx18_av_reset, - .queryctrl = cx18_av_queryctrl, - .g_ctrl = cx18_av_g_ctrl, - .s_ctrl = cx18_av_s_ctrl, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = cx18_av_s_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = cx18_av_g_register, @@ -1427,8 +1360,42 @@ int cx18_av_probe(struct cx18 *cx) snprintf(sd->name, sizeof(sd->name), "%s %03x", cx->v4l2_dev.name, (state->rev >> 4)); sd->grp_id = CX18_HW_418_AV; + v4l2_ctrl_handler_init(&state->hdl, 9); + v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + + state->volume = v4l2_ctrl_new_std(&state->hdl, + &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, + 0, 65535, 65535 / 100, 0); + v4l2_ctrl_new_std(&state->hdl, + &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops, + V4L2_CID_AUDIO_BASS, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, + 0, 65535, 65535 / 100, 32768); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + return err; + } err = v4l2_device_register_subdev(&cx->v4l2_dev, sd); - if (!err) + if (err) + v4l2_ctrl_handler_free(&state->hdl); + else cx18_av_init(cx); return err; } diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index 1956991..188c9c3 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -26,6 +26,7 @@ #define _CX18_AV_CORE_H_ #include +#include struct cx18; @@ -95,13 +96,14 @@ enum cx18_av_audio_input { struct cx18_av_state { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *volume; int radio; v4l2_std_id std; enum cx18_av_video_input vid_input; enum cx18_av_audio_input aud_input; u32 audclk_freq; int audmode; - int default_volume; u32 id; u32 rev; int is_initialized; @@ -347,6 +349,11 @@ static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd) return container_of(sd, struct cx18_av_state, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cx18_av_state, hdl)->sd; +} + /* ----------------------------------------------------------------------- */ /* cx18_av-core.c */ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); @@ -369,10 +376,9 @@ int cx18_av_loadfw(struct cx18 *cx); /* ----------------------------------------------------------------------- */ /* cx18_av-audio.c */ -int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl); -int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl); int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq); void cx18_av_audio_set_path(struct cx18 *cx); +extern const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops; /* ----------------------------------------------------------------------- */ /* cx18_av-vbi.c */ diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 97d7b7e..282a3d2 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -30,152 +30,11 @@ #include "cx18-mailbox.h" #include "cx18-controls.h" -/* Must be sorted from low to high control ID! */ -static const u32 user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUDIO_VOLUME, - V4L2_CID_AUDIO_BALANCE, - V4L2_CID_AUDIO_BASS, - V4L2_CID_AUDIO_TREBLE, - V4L2_CID_AUDIO_MUTE, - V4L2_CID_AUDIO_LOUDNESS, - 0 -}; - -static const u32 *ctrl_classes[] = { - user_ctrls, - cx2341x_mpeg_ctrls, - NULL -}; - -int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) -{ - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - const char *name; - - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (qctrl->id == 0) - return -EINVAL; - - switch (qctrl->id) { - /* Standard V4L2 controls */ - case V4L2_CID_USER_CLASS: - return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_HUE: - case V4L2_CID_SATURATION: - case V4L2_CID_CONTRAST: - if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_LOUDNESS: - if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - - default: - if (cx2341x_ctrl_query(&cx->params, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - } - strncpy(qctrl->name, name, sizeof(qctrl->name) - 1); - qctrl->name[sizeof(qctrl->name) - 1] = 0; - return 0; -} - -int cx18_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu) +static int cx18_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - struct v4l2_queryctrl qctrl; - - qctrl.id = qmenu->id; - cx18_queryctrl(file, fh, &qctrl); - return v4l2_ctrl_query_menu(qmenu, &qctrl, - cx2341x_ctrl_get_menu(&cx->params, qmenu->id)); -} - -static int cx18_try_ctrl(struct file *file, void *fh, - struct v4l2_ext_control *vctrl) -{ - struct v4l2_queryctrl qctrl; - const char * const *menu_items = NULL; - int err; - - qctrl.id = vctrl->id; - err = cx18_queryctrl(file, fh, &qctrl); - if (err) - return err; - if (qctrl.type == V4L2_CTRL_TYPE_MENU) - menu_items = v4l2_ctrl_get_menu(qctrl.id); - return v4l2_ctrl_check(vctrl, &qctrl, menu_items); -} - -static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) -{ - switch (vctrl->id) { - /* Standard V4L2 controls */ - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_HUE: - case V4L2_CID_SATURATION: - case V4L2_CID_CONTRAST: - return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl); - - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_LOUDNESS: - return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl); - - default: - CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); - return -EINVAL; - } - return 0; -} - -static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) -{ - switch (vctrl->id) { - /* Standard V4L2 controls */ - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_HUE: - case V4L2_CID_SATURATION: - case V4L2_CID_CONTRAST: - return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl); - - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_LOUDNESS: - return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl); + struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); + int type = cxhdl->stream_type->val; - default: - CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); - return -EINVAL; - } - return 0; -} - -static int cx18_setup_vbi_fmt(struct cx18 *cx, - enum v4l2_mpeg_stream_vbi_fmt fmt, - enum v4l2_mpeg_stream_type type) -{ - if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE)) - return -EINVAL; if (atomic_read(&cx->ana_capturing) > 0) return -EBUSY; @@ -230,121 +89,43 @@ static int cx18_setup_vbi_fmt(struct cx18 *cx, return 0; } -int cx18_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - struct v4l2_control ctrl; - - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err = cx18_g_ctrl(cx, &ctrl); - c->controls[i].value = ctrl.value; - if (err) { - c->error_idx = i; - break; - } - } - return err; - } - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) - return cx2341x_ext_ctrls(&cx->params, 0, c, VIDIOC_G_EXT_CTRLS); - return -EINVAL; + struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); + int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + struct v4l2_mbus_framefmt fmt; + + /* fix videodecoder resolution */ + fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1); + fmt.height = cxhdl->height; + fmt.code = V4L2_MBUS_FMT_FIXED; + v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt); + return 0; } -int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +static int cx18_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx) { - struct cx18_open_id *id = fh; - struct cx18 *cx = id->cx; - int ret; - struct v4l2_control ctrl; - - ret = v4l2_prio_check(&cx->prio, id->prio); - if (ret) - return ret; - - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err = cx18_s_ctrl(cx, &ctrl); - c->controls[i].value = ctrl.value; - if (err) { - c->error_idx = i; - break; - } - } - return err; - } - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - static u32 freqs[3] = { 44100, 48000, 32000 }; - struct cx18_api_func_private priv; - struct cx2341x_mpeg_params p = cx->params; - int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), - c, VIDIOC_S_EXT_CTRLS); - unsigned int idx; - - if (err) - return err; + static const u32 freqs[3] = { 44100, 48000, 32000 }; + struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); - if (p.video_encoding != cx->params.video_encoding) { - int is_mpeg1 = p.video_encoding == - V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - struct v4l2_mbus_framefmt fmt; - - /* fix videodecoder resolution */ - fmt.width = cx->params.width / (is_mpeg1 ? 2 : 1); - fmt.height = cx->params.height; - fmt.code = V4L2_MBUS_FMT_FIXED; - v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt); - } - priv.cx = cx; - priv.s = &cx->streams[id->type]; - err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p); - if (!err && - (cx->params.stream_vbi_fmt != p.stream_vbi_fmt || - cx->params.stream_type != p.stream_type)) - err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt, - p.stream_type); - cx->params = p; - cx->dualwatch_stereo_mode = p.audio_properties & 0x0300; - idx = p.audio_properties & 0x03; - /* The audio clock of the digitizer must match the codec sample - rate otherwise you get some very strange effects. */ - if (idx < ARRAY_SIZE(freqs)) - cx18_call_all(cx, audio, s_clock_freq, freqs[idx]); - return err; - } - return -EINVAL; + /* The audio clock of the digitizer must match the codec sample + rate otherwise you get some very strange effects. */ + if (idx < ARRAY_SIZE(freqs)) + cx18_call_all(cx, audio, s_clock_freq, freqs[idx]); + return 0; } -int cx18_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +static int cx18_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - err = cx18_try_ctrl(file, fh, &c->controls[i]); - if (err) { - c->error_idx = i; - break; - } - } - return err; - } - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) - return cx2341x_ext_ctrls(&cx->params, - atomic_read(&cx->ana_capturing), - c, VIDIOC_TRY_EXT_CTRLS); - return -EINVAL; + cx->dualwatch_stereo_mode = val; + return 0; } + +struct cx2341x_handler_ops cx18_cxhdl_ops = { + .s_audio_mode = cx18_s_audio_mode, + .s_audio_sampling_freq = cx18_s_audio_sampling_freq, + .s_video_encoding = cx18_s_video_encoding, + .s_stream_vbi_fmt = cx18_s_stream_vbi_fmt, +}; diff --git a/drivers/media/video/cx18/cx18-controls.h b/drivers/media/video/cx18/cx18-controls.h index e463237..cb5dfc7 100644 --- a/drivers/media/video/cx18/cx18-controls.h +++ b/drivers/media/video/cx18/cx18-controls.h @@ -21,9 +21,4 @@ * 02111-1307 USA */ -int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a); -int cx18_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); -int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); -int cx18_try_ext_ctrls(struct file *file, void *fh, - struct v4l2_ext_controls *a); -int cx18_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a); +extern struct cx2341x_handler_ops cx18_cxhdl_ops; diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index b1c3cbd..b988ec6 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -36,6 +36,7 @@ #include "cx18-scb.h" #include "cx18-mailbox.h" #include "cx18-ioctl.h" +#include "cx18-controls.h" #include "tuner-xc2028.h" #include @@ -729,15 +730,21 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) cx->open_id = 1; /* Initial settings */ - cx2341x_fill_defaults(&cx->params); - cx->temporal_strength = cx->params.video_temporal_filter; - cx->spatial_strength = cx->params.video_spatial_filter; - cx->filter_mode = cx->params.video_spatial_filter_mode | - (cx->params.video_temporal_filter_mode << 1) | - (cx->params.video_median_filter_type << 2); - cx->params.port = CX2341X_PORT_MEMORY; - cx->params.capabilities = - CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI; + cx->cxhdl.port = CX2341X_PORT_MEMORY; + cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI; + cx->cxhdl.ops = &cx18_cxhdl_ops; + cx->cxhdl.func = cx18_api_func; + ret = cx2341x_handler_init(&cx->cxhdl, 50); + if (ret) + return ret; + cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl; + + cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val; + cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val; + cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val | + (cx->cxhdl.video_temporal_filter_mode->cur.val << 1) | + (cx->cxhdl.video_median_filter_type->cur.val << 2); + init_waitqueue_head(&cx->cap_w); init_waitqueue_head(&cx->mb_apu_waitq); init_waitqueue_head(&cx->mb_cpu_waitq); @@ -1049,7 +1056,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, else cx->is_50hz = 1; - cx->params.video_gop_size = cx->is_60hz ? 15 : 12; + cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz); if (cx->options.radio > 0) cx->v4l2_cap |= V4L2_CAP_RADIO; @@ -1095,7 +1102,6 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, /* Load cx18 submodules (cx18-alsa) */ request_modules(cx); - return 0; free_streams: @@ -1278,6 +1284,8 @@ static void cx18_remove(struct pci_dev *pci_dev) for (i = 0; i < CX18_VBI_FRAMES; i++) kfree(cx->vbi.sliced_mpeg_data[i]); + v4l2_ctrl_handler_free(&cx->av_state.hdl); + CX18_INFO("Removed %s\n", cx->card_name); v4l2_device_unregister(v4l2_dev); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index f736679..de24577 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -565,7 +565,7 @@ struct cx18 { struct cx18_av_state av_state; /* codec settings */ - struct cx2341x_mpeg_params params; + struct cx2341x_handler cxhdl; u32 filter_mode; u32 temporal_strength; u32 spatial_strength; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 9f23b90..98ef33e 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -160,13 +160,10 @@ EXPORT_SYMBOL(cx18_release_stream); static void cx18_dualwatch(struct cx18 *cx) { struct v4l2_tuner vt; - u32 new_bitmap; u32 new_stereo_mode; - const u32 stereo_mask = 0x0300; const u32 dual = 0x0200; - u32 h; - new_stereo_mode = cx->params.audio_properties & stereo_mask; + new_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode); memset(&vt, 0, sizeof(vt)); cx18_call_all(cx, tuner, g_tuner, &vt); if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && @@ -176,25 +173,10 @@ static void cx18_dualwatch(struct cx18 *cx) if (new_stereo_mode == cx->dualwatch_stereo_mode) return; - new_bitmap = new_stereo_mode - | (cx->params.audio_properties & ~stereo_mask); - - CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. " - "new audio_bitmask=0x%ux\n", - cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); - - h = cx18_find_handle(cx); - if (h == CX18_INVALID_TASK_HANDLE) { - CX18_DEBUG_INFO("dualwatch: can't find valid task handle\n"); - return; - } - - if (cx18_vapi(cx, - CX18_CPU_SET_AUDIO_PARAMETERS, 2, h, new_bitmap) == 0) { - cx->dualwatch_stereo_mode = new_stereo_mode; - return; - } - CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); + CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n", + cx->dualwatch_stereo_mode, new_stereo_mode); + if (v4l2_ctrl_s_ctrl(cx->cxhdl.audio_mode, new_stereo_mode)) + CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); } @@ -724,8 +706,8 @@ int cx18_v4l2_close(struct file *filp) if (atomic_read(&cx->ana_capturing) > 0) { /* Undo video mute */ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, - cx->params.video_mute | - (cx->params.video_mute_yuv << 8)); + (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute) | + (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8))); } /* Done! Unmute and continue. */ cx18_unmute(cx); diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 7150195..36b018c 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -152,8 +152,8 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, struct cx18 *cx = id->cx; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - pixfmt->width = cx->params.width; - pixfmt->height = cx->params.height; + pixfmt->width = cx->cxhdl.width; + pixfmt->height = cx->cxhdl.height; pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; pixfmt->field = V4L2_FIELD_INTERLACED; pixfmt->priv = 0; @@ -287,14 +287,14 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, w = fmt->fmt.pix.width; h = fmt->fmt.pix.height; - if (cx->params.width == w && cx->params.height == h) + if (cx->cxhdl.width == w && cx->cxhdl.height == h) return 0; if (atomic_read(&cx->ana_capturing) > 0) return -EBUSY; - mbus_fmt.width = cx->params.width = w; - mbus_fmt.height = cx->params.height = h; + mbus_fmt.width = cx->cxhdl.width = w; + mbus_fmt.height = cx->cxhdl.height = h; mbus_fmt.code = V4L2_MBUS_FMT_FIXED; v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt); return cx18_g_fmt_vid_cap(file, fh, fmt); @@ -696,9 +696,10 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) cx->std = *std; cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; - cx->params.is_50hz = cx->is_50hz = !cx->is_60hz; - cx->params.width = 720; - cx->params.height = cx->is_50hz ? 576 : 480; + cx->is_50hz = !cx->is_60hz; + cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz); + cx->cxhdl.width = 720; + cx->cxhdl.height = cx->is_50hz ? 576 : 480; cx->vbi.count = cx->is_50hz ? 18 : 12; cx->vbi.start[0] = cx->is_50hz ? 6 : 10; cx->vbi.start[1] = cx->is_50hz ? 318 : 273; @@ -1035,7 +1036,7 @@ static int cx18_log_status(struct file *file, void *fh) mutex_unlock(&cx->gpio_lock); CX18_INFO("Tuner: %s\n", test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV"); - cx2341x_log_status(&cx->params, cx->v4l2_dev.name); + v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name); CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); for (i = 0; i < CX18_MAX_STREAMS; i++) { struct cx18_stream *s = &cx->streams[i]; @@ -1136,11 +1137,6 @@ static const struct v4l2_ioctl_ops cx18_ioctl_ops = { .vidioc_s_register = cx18_s_register, #endif .vidioc_default = cx18_default, - .vidioc_queryctrl = cx18_queryctrl, - .vidioc_querymenu = cx18_querymenu, - .vidioc_g_ext_ctrls = cx18_g_ext_ctrls, - .vidioc_s_ext_ctrls = cx18_s_ext_ctrls, - .vidioc_try_ext_ctrls = cx18_try_ext_ctrls, }; void cx18_set_funcs(struct video_device *vdev) diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index c545f3b..9605d54 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -716,9 +716,8 @@ static int cx18_set_filter_param(struct cx18_stream *s) int cx18_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) { - struct cx18_api_func_private *api_priv = priv; - struct cx18 *cx = api_priv->cx; - struct cx18_stream *s = api_priv->s; + struct cx18_stream *s = priv; + struct cx18 *cx = s->cx; switch (cmd) { case CX2341X_ENC_SET_OUTPUT_PORT: diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index 077952f..05fe6bd 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -81,11 +81,6 @@ struct cx18_mailbox { struct cx18_stream; -struct cx18_api_func_private { - struct cx18 *cx; - struct cx18_stream *s; -}; - int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]); int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, int args, ...); diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 94f5d79..2d24856 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -572,7 +572,7 @@ static void cx18_stream_configure_mdls(struct cx18_stream *s) * Set the MDL size to the exact size needed for one frame. * Use enough buffers per MDL to cover the MDL size */ - s->mdl_size = 720 * s->cx->params.height * 3 / 2; + s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2; s->bufs_per_mdl = s->mdl_size / s->buf_size; if (s->mdl_size % s->buf_size) s->bufs_per_mdl++; @@ -607,7 +607,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) u32 data[MAX_MB_ARGUMENTS]; struct cx18 *cx = s->cx; int captype = 0; - struct cx18_api_func_private priv; struct cx18_stream *s_idx; if (!cx18_stream_enabled(s)) @@ -620,7 +619,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) captype = CAPTURE_CHANNEL_TYPE_MPEG; cx->mpg_data_received = cx->vbi_data_inserted = 0; cx->dualwatch_jiffies = jiffies; - cx->dualwatch_stereo_mode = cx->params.audio_properties & 0x300; + cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode); cx->search_pack_header = 0; break; @@ -710,21 +709,21 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) s->handle, cx18_stream_enabled(s_idx) ? 7 : 0); /* Call out to the common CX2341x API setup for user controls */ - priv.cx = cx; - priv.s = s; - cx2341x_update(&priv, cx18_api_func, NULL, &cx->params); + cx->cxhdl.priv = s; + cx2341x_handler_setup(&cx->cxhdl); /* * When starting a capture and we're set for radio, * ensure the video is muted, despite the user control. */ - if (!cx->params.video_mute && + if (!cx->cxhdl.video_mute && test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, - (cx->params.video_mute_yuv << 8) | 1); + (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1); } if (atomic_read(&cx->tot_capturing) == 0) { + cx2341x_handler_set_busy(&cx->cxhdl, 1); clear_bit(CX18_F_I_EOS, &cx->i_flags); cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK); } @@ -826,6 +825,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) if (atomic_read(&cx->tot_capturing) > 0) return 0; + cx2341x_handler_set_busy(&cx->cxhdl, 0); cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); wake_up(&s->waitq); -- cgit v0.10.2 From ceed52d67e1cf7866fa96514525c615554a0e44e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Dec 2010 08:19:16 -0300 Subject: [media] adv7343: use control framework Also fixed a memory leak in the probe function if an error occurred. The gain control range was also fixed (a proper range from -64 to 64). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c index 41b2930..021fab2 100644 --- a/drivers/media/video/adv7343.c +++ b/drivers/media/video/adv7343.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "adv7343_regs.h" @@ -41,15 +42,13 @@ MODULE_PARM_DESC(debug, "Debug level 0-1"); struct adv7343_state { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; u8 reg00; u8 reg01; u8 reg02; u8 reg35; u8 reg80; u8 reg82; - int bright; - int hue; - int gain; u32 output; v4l2_std_id std; }; @@ -59,6 +58,11 @@ static inline struct adv7343_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct adv7343_state, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7343_state, hdl)->sd; +} + static inline int adv7343_write(struct v4l2_subdev *sd, u8 reg, u8 value) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -268,111 +272,22 @@ static int adv7343_log_status(struct v4l2_subdev *sd) return 0; } -static int adv7343_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, ADV7343_BRIGHTNESS_MIN, - ADV7343_BRIGHTNESS_MAX, 1, - ADV7343_BRIGHTNESS_DEF); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, ADV7343_HUE_MIN, - ADV7343_HUE_MAX, 1 , - ADV7343_HUE_DEF); - case V4L2_CID_GAIN: - return v4l2_ctrl_query_fill(qc, ADV7343_GAIN_MIN, - ADV7343_GAIN_MAX, 1, - ADV7343_GAIN_DEF); - default: - break; - } - - return 0; -} - -static int adv7343_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct adv7343_state *state = to_state(sd); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value < ADV7343_BRIGHTNESS_MIN || - ctrl->value > ADV7343_BRIGHTNESS_MAX) { - v4l2_dbg(1, debug, sd, - "invalid brightness settings %d\n", - ctrl->value); - return -ERANGE; - } - - state->bright = ctrl->value; - err = adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS, - state->bright); - break; - - case V4L2_CID_HUE: - if (ctrl->value < ADV7343_HUE_MIN || - ctrl->value > ADV7343_HUE_MAX) { - v4l2_dbg(1, debug, sd, "invalid hue settings %d\n", - ctrl->value); - return -ERANGE; - } - - state->hue = ctrl->value; - err = adv7343_write(sd, ADV7343_SD_HUE_REG, state->hue); - break; - - case V4L2_CID_GAIN: - if (ctrl->value < ADV7343_GAIN_MIN || - ctrl->value > ADV7343_GAIN_MAX) { - v4l2_dbg(1, debug, sd, "invalid gain settings %d\n", - ctrl->value); - return -ERANGE; - } - - if ((ctrl->value > POSITIVE_GAIN_MAX) && - (ctrl->value < NEGATIVE_GAIN_MIN)) { - v4l2_dbg(1, debug, sd, - "gain settings not within the specified range\n"); - return -ERANGE; - } - - state->gain = ctrl->value; - err = adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, state->gain); - break; - - default: - return -EINVAL; - } - - if (err < 0) - v4l2_err(sd, "Failed to set the encoder controls\n"); - - return err; -} - -static int adv7343_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int adv7343_s_ctrl(struct v4l2_ctrl *ctrl) { - struct adv7343_state *state = to_state(sd); + struct v4l2_subdev *sd = to_sd(ctrl); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = state->bright; - break; + return adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS, + ctrl->val); case V4L2_CID_HUE: - ctrl->value = state->hue; - break; + return adv7343_write(sd, ADV7343_SD_HUE_REG, ctrl->val); case V4L2_CID_GAIN: - ctrl->value = state->gain; - break; - - default: - return -EINVAL; + return adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, ctrl->val); } - - return 0; + return -EINVAL; } static int adv7343_g_chip_ident(struct v4l2_subdev *sd, @@ -383,12 +298,20 @@ static int adv7343_g_chip_ident(struct v4l2_subdev *sd, return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0); } +static const struct v4l2_ctrl_ops adv7343_ctrl_ops = { + .s_ctrl = adv7343_s_ctrl, +}; + static const struct v4l2_subdev_core_ops adv7343_core_ops = { - .log_status = adv7343_log_status, - .g_chip_ident = adv7343_g_chip_ident, - .g_ctrl = adv7343_g_ctrl, - .s_ctrl = adv7343_s_ctrl, - .queryctrl = adv7343_queryctrl, + .log_status = adv7343_log_status, + .g_chip_ident = adv7343_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, }; static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) @@ -468,6 +391,7 @@ static int adv7343_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adv7343_state *state; + int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -490,15 +414,46 @@ static int adv7343_probe(struct i2c_client *client, state->std = V4L2_STD_NTSC; v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops); - return adv7343_initialize(&state->sd); + + v4l2_ctrl_handler_init(&state->hdl, 2); + v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7343_BRIGHTNESS_MIN, + ADV7343_BRIGHTNESS_MAX, 1, + ADV7343_BRIGHTNESS_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, + V4L2_CID_HUE, ADV7343_HUE_MIN, + ADV7343_HUE_MAX, 1, + ADV7343_HUE_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, + V4L2_CID_GAIN, ADV7343_GAIN_MIN, + ADV7343_GAIN_MAX, 1, + ADV7343_GAIN_DEF); + state->sd.ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + + err = adv7343_initialize(&state->sd); + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + } + return err; } static int adv7343_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7343_state *state = to_state(sd); v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); return 0; } diff --git a/drivers/media/video/adv7343_regs.h b/drivers/media/video/adv7343_regs.h index 3431045..4466067 100644 --- a/drivers/media/video/adv7343_regs.h +++ b/drivers/media/video/adv7343_regs.h @@ -102,10 +102,6 @@ struct adv7343_std_info { /* Bit masks for DAC output levels */ #define DAC_OUTPUT_LEVEL_MASK (0xFF) -#define POSITIVE_GAIN_MAX (0x40) -#define POSITIVE_GAIN_MIN (0x00) -#define NEGATIVE_GAIN_MAX (0xFF) -#define NEGATIVE_GAIN_MIN (0xC0) /* Bit masks for soft reset register */ #define SOFT_RESET (0x02) @@ -178,8 +174,8 @@ struct adv7343_std_info { #define ADV7343_HUE_MAX (255) #define ADV7343_HUE_MIN (0) #define ADV7343_HUE_DEF (127) -#define ADV7343_GAIN_MAX (255) -#define ADV7343_GAIN_MIN (0) +#define ADV7343_GAIN_MAX (64) +#define ADV7343_GAIN_MIN (-64) #define ADV7343_GAIN_DEF (0) #endif -- cgit v0.10.2 From bd84a65aefbd669bcd2ff7bcee96f0f666d7a820 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Dec 2010 08:19:41 -0300 Subject: [media] bt819: use control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index c38300f..f872044 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -37,6 +37,7 @@ #include #include #include +#include #include MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); @@ -52,16 +53,13 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); struct bt819 { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; unsigned char reg[32]; v4l2_std_id norm; int ident; int input; int enable; - int bright; - int contrast; - int hue; - int sat; }; static inline struct bt819 *to_bt819(struct v4l2_subdev *sd) @@ -69,6 +67,11 @@ static inline struct bt819 *to_bt819(struct v4l2_subdev *sd) return container_of(sd, struct bt819, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct bt819, hdl)->sd; +} + struct timing { int hactive; int hdelay; @@ -333,71 +336,35 @@ static int bt819_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int bt819_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - break; - - case V4L2_CID_CONTRAST: - v4l2_ctrl_query_fill(qc, 0, 511, 1, 256); - break; - - case V4L2_CID_SATURATION: - v4l2_ctrl_query_fill(qc, 0, 511, 1, 256); - break; - - case V4L2_CID_HUE: - v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int bt819_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int bt819_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct bt819 *decoder = to_bt819(sd); int temp; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - if (decoder->bright == ctrl->value) - break; - decoder->bright = ctrl->value; - bt819_write(decoder, 0x0a, decoder->bright); + bt819_write(decoder, 0x0a, ctrl->val); break; case V4L2_CID_CONTRAST: - if (decoder->contrast == ctrl->value) - break; - decoder->contrast = ctrl->value; - bt819_write(decoder, 0x0c, decoder->contrast & 0xff); - bt819_setbit(decoder, 0x0b, 2, ((decoder->contrast >> 8) & 0x01)); + bt819_write(decoder, 0x0c, ctrl->val & 0xff); + bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01)); break; case V4L2_CID_SATURATION: - if (decoder->sat == ctrl->value) - break; - decoder->sat = ctrl->value; - bt819_write(decoder, 0x0d, (decoder->sat >> 7) & 0xff); - bt819_setbit(decoder, 0x0b, 1, ((decoder->sat >> 15) & 0x01)); + bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff); + bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01)); /* Ratio between U gain and V gain must stay the same as the ratio between the default U and V gain values. */ - temp = (decoder->sat * 180) / 254; + temp = (ctrl->val * 180) / 254; bt819_write(decoder, 0x0e, (temp >> 7) & 0xff); bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01); break; case V4L2_CID_HUE: - if (decoder->hue == ctrl->value) - break; - decoder->hue = ctrl->value; - bt819_write(decoder, 0x0f, decoder->hue); + bt819_write(decoder, 0x0f, ctrl->val); break; default: @@ -406,29 +373,6 @@ static int bt819_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return 0; } -static int bt819_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct bt819 *decoder = to_bt819(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = decoder->bright; - break; - case V4L2_CID_CONTRAST: - ctrl->value = decoder->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = decoder->sat; - break; - case V4L2_CID_HUE: - ctrl->value = decoder->hue; - break; - default: - return -EINVAL; - } - return 0; -} - static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct bt819 *decoder = to_bt819(sd); @@ -439,11 +383,19 @@ static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops bt819_ctrl_ops = { + .s_ctrl = bt819_s_ctrl, +}; + static const struct v4l2_subdev_core_ops bt819_core_ops = { .g_chip_ident = bt819_g_chip_ident, - .g_ctrl = bt819_g_ctrl, - .s_ctrl = bt819_s_ctrl, - .queryctrl = bt819_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = bt819_s_std, }; @@ -505,23 +457,40 @@ static int bt819_probe(struct i2c_client *client, decoder->norm = V4L2_STD_NTSC; decoder->input = 0; decoder->enable = 1; - decoder->bright = 0; - decoder->contrast = 0xd8; /* 100% of original signal */ - decoder->hue = 0; - decoder->sat = 0xfe; /* 100% of original signal */ i = bt819_init(sd); if (i < 0) v4l2_dbg(1, debug, sd, "init status %d\n", i); + + v4l2_ctrl_handler_init(&decoder->hdl, 4); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_CONTRAST, 0, 511, 1, 0xd8); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_SATURATION, 0, 511, 1, 0xfe); + v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); return 0; } static int bt819_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct bt819 *decoder = to_bt819(sd); v4l2_device_unregister_subdev(sd); - kfree(to_bt819(sd)); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); return 0; } -- cgit v0.10.2 From 4744ebf631bc9276f7c10e37801f05c8ceea7bab Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Dec 2010 08:27:48 -0300 Subject: [media] saa7110: use control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index 7913f93..9966420 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -36,6 +36,7 @@ #include #include #include +#include MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); MODULE_AUTHOR("Pauline Middelink"); @@ -53,15 +54,12 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); struct saa7110 { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; u8 reg[SAA7110_NR_REG]; v4l2_std_id norm; int input; int enable; - int bright; - int contrast; - int hue; - int sat; wait_queue_head_t wq; }; @@ -71,6 +69,11 @@ static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd) return container_of(sd, struct saa7110, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa7110, hdl)->sd; +} + /* ----------------------------------------------------------------------- */ /* I2C support functions */ /* ----------------------------------------------------------------------- */ @@ -326,73 +329,22 @@ static int saa7110_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int saa7110_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - default: - return -EINVAL; - } - return 0; -} - -static int saa7110_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct saa7110 *decoder = to_saa7110(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = decoder->bright; - break; - case V4L2_CID_CONTRAST: - ctrl->value = decoder->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = decoder->sat; - break; - case V4L2_CID_HUE: - ctrl->value = decoder->hue; - break; - default: - return -EINVAL; - } - return 0; -} - -static int saa7110_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int saa7110_s_ctrl(struct v4l2_ctrl *ctrl) { - struct saa7110 *decoder = to_saa7110(sd); + struct v4l2_subdev *sd = to_sd(ctrl); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - if (decoder->bright != ctrl->value) { - decoder->bright = ctrl->value; - saa7110_write(sd, 0x19, decoder->bright); - } + saa7110_write(sd, 0x19, ctrl->val); break; case V4L2_CID_CONTRAST: - if (decoder->contrast != ctrl->value) { - decoder->contrast = ctrl->value; - saa7110_write(sd, 0x13, decoder->contrast); - } + saa7110_write(sd, 0x13, ctrl->val); break; case V4L2_CID_SATURATION: - if (decoder->sat != ctrl->value) { - decoder->sat = ctrl->value; - saa7110_write(sd, 0x12, decoder->sat); - } + saa7110_write(sd, 0x12, ctrl->val); break; case V4L2_CID_HUE: - if (decoder->hue != ctrl->value) { - decoder->hue = ctrl->value; - saa7110_write(sd, 0x07, decoder->hue); - } + saa7110_write(sd, 0x07, ctrl->val); break; default: return -EINVAL; @@ -409,11 +361,19 @@ static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ide /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops saa7110_ctrl_ops = { + .s_ctrl = saa7110_s_ctrl, +}; + static const struct v4l2_subdev_core_ops saa7110_core_ops = { .g_chip_ident = saa7110_g_chip_ident, - .g_ctrl = saa7110_g_ctrl, - .s_ctrl = saa7110_s_ctrl, - .queryctrl = saa7110_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = saa7110_s_std, }; @@ -454,10 +414,25 @@ static int saa7110_probe(struct i2c_client *client, decoder->norm = V4L2_STD_PAL; decoder->input = 0; decoder->enable = 1; - decoder->bright = 32768; - decoder->contrast = 32768; - decoder->hue = 32768; - decoder->sat = 32768; + v4l2_ctrl_handler_init(&decoder->hdl, 2); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + init_waitqueue_head(&decoder->wq); rv = saa7110_write_block(sd, initseq, sizeof(initseq)); @@ -490,9 +465,11 @@ static int saa7110_probe(struct i2c_client *client, static int saa7110_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa7110 *decoder = to_saa7110(sd); v4l2_device_unregister_subdev(sd); - kfree(to_saa7110(sd)); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); return 0; } -- cgit v0.10.2 From e3d5ef0410806d94cf58afd87a753ad5932ca8a8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Dec 2010 08:45:04 -0300 Subject: [media] tlv320aic23b: use control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c index dfc4dd7..286ec7e 100644 --- a/drivers/media/video/tlv320aic23b.c +++ b/drivers/media/video/tlv320aic23b.c @@ -31,6 +31,7 @@ #include #include #include +#include MODULE_DESCRIPTION("tlv320aic23b driver"); MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil"); @@ -41,7 +42,7 @@ MODULE_LICENSE("GPL"); struct tlv320aic23b_state { struct v4l2_subdev sd; - u8 muted; + struct v4l2_ctrl_handler hdl; }; static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd) @@ -49,6 +50,11 @@ static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct tlv320aic23b_state, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tlv320aic23b_state, hdl)->sd; +} + static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -85,44 +91,44 @@ static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) return 0; } -static int tlv320aic23b_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct tlv320aic23b_state *state = to_state(sd); - - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - ctrl->value = state->muted; - return 0; -} - -static int tlv320aic23b_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int tlv320aic23b_s_ctrl(struct v4l2_ctrl *ctrl) { - struct tlv320aic23b_state *state = to_state(sd); - - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - state->muted = ctrl->value; - tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */ - /* set gain on both channels to +3.0 dB */ - if (!state->muted) - tlv320aic23b_write(sd, 0, 0x119); - return 0; + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */ + /* set gain on both channels to +3.0 dB */ + if (!ctrl->val) + tlv320aic23b_write(sd, 0, 0x119); + return 0; + } + return -EINVAL; } static int tlv320aic23b_log_status(struct v4l2_subdev *sd) { struct tlv320aic23b_state *state = to_state(sd); - v4l2_info(sd, "Input: %s\n", state->muted ? "muted" : "active"); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); return 0; } /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = { + .s_ctrl = tlv320aic23b_s_ctrl, +}; + static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { .log_status = tlv320aic23b_log_status, - .g_ctrl = tlv320aic23b_g_ctrl, - .s_ctrl = tlv320aic23b_s_ctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = { @@ -161,7 +167,6 @@ static int tlv320aic23b_probe(struct i2c_client *client, return -ENOMEM; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops); - state->muted = 0; /* Initialize tlv320aic23b */ @@ -177,15 +182,30 @@ static int tlv320aic23b_probe(struct i2c_client *client, tlv320aic23b_write(sd, 8, 0x000); /* activate digital interface */ tlv320aic23b_write(sd, 9, 0x001); + + v4l2_ctrl_handler_init(&state->hdl, 1); + v4l2_ctrl_new_std(&state->hdl, &tlv320aic23b_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); return 0; } static int tlv320aic23b_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tlv320aic23b_state *state = to_state(sd); v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); return 0; } -- cgit v0.10.2 From cf6832afe59f37b154bf0e41bf5e59cfd7f46d24 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Dec 2010 08:45:22 -0300 Subject: [media] tvp514x: use the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index 45bcf03..9b3e828 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include "tvp514x_regs.h" @@ -97,6 +98,7 @@ static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable); */ struct tvp514x_decoder { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; const struct tvp514x_platform_data *pdata; @@ -238,6 +240,11 @@ static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) return container_of(sd, struct tvp514x_decoder, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tvp514x_decoder, hdl)->sd; +} + /** * tvp514x_read_reg() - Read a value from a register in an TVP5146/47. @@ -719,213 +726,54 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, } /** - * tvp514x_queryctrl() - V4L2 decoder interface handler for queryctrl - * @sd: pointer to standard V4L2 sub-device structure - * @qctrl: standard V4L2 v4l2_queryctrl structure - * - * If the requested control is supported, returns the control information. - * Otherwise, returns -EINVAL if the control is not supported. - */ -static int -tvp514x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) -{ - int err = -EINVAL; - - if (qctrl == NULL) - return err; - - switch (qctrl->id) { - case V4L2_CID_BRIGHTNESS: - /* Brightness supported is (0-255), */ - err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); - break; - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - /** - * Saturation and Contrast supported is - - * Contrast: 0 - 255 (Default - 128) - * Saturation: 0 - 255 (Default - 128) - */ - err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); - break; - case V4L2_CID_HUE: - /* Hue Supported is - - * Hue - -180 - +180 (Default - 0, Step - +180) - */ - err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0); - break; - case V4L2_CID_AUTOGAIN: - /** - * Auto Gain supported is - - * 0 - 1 (Default - 1) - */ - err = v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); - break; - default: - v4l2_err(sd, "invalid control id %d\n", qctrl->id); - return err; - } - - v4l2_dbg(1, debug, sd, "Query Control:%s: Min - %d, Max - %d, Def - %d\n", - qctrl->name, qctrl->minimum, qctrl->maximum, - qctrl->default_value); - - return err; -} - -/** - * tvp514x_g_ctrl() - V4L2 decoder interface handler for g_ctrl - * @sd: pointer to standard V4L2 sub-device structure - * @ctrl: pointer to v4l2_control structure - * - * If the requested control is supported, returns the control's current - * value from the decoder. Otherwise, returns -EINVAL if the control is not - * supported. - */ -static int -tvp514x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - - if (ctrl == NULL) - return -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = decoder->tvp514x_regs[REG_BRIGHTNESS].val; - break; - case V4L2_CID_CONTRAST: - ctrl->value = decoder->tvp514x_regs[REG_CONTRAST].val; - break; - case V4L2_CID_SATURATION: - ctrl->value = decoder->tvp514x_regs[REG_SATURATION].val; - break; - case V4L2_CID_HUE: - ctrl->value = decoder->tvp514x_regs[REG_HUE].val; - if (ctrl->value == 0x7F) - ctrl->value = 180; - else if (ctrl->value == 0x80) - ctrl->value = -180; - else - ctrl->value = 0; - - break; - case V4L2_CID_AUTOGAIN: - ctrl->value = decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val; - if ((ctrl->value & 0x3) == 3) - ctrl->value = 1; - else - ctrl->value = 0; - - break; - default: - v4l2_err(sd, "invalid control id %d\n", ctrl->id); - return -EINVAL; - } - - v4l2_dbg(1, debug, sd, "Get Control: ID - %d - %d\n", - ctrl->id, ctrl->value); - return 0; -} - -/** * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl - * @sd: pointer to standard V4L2 sub-device structure - * @ctrl: pointer to v4l2_control structure + * @ctrl: pointer to v4l2_ctrl structure * * If the requested control is supported, sets the control's current * value in HW. Otherwise, returns -EINVAL if the control is not supported. */ -static int -tvp514x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct tvp514x_decoder *decoder = to_decoder(sd); int err = -EINVAL, value; - if (ctrl == NULL) - return err; - - value = ctrl->value; + value = ctrl->val; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - if (ctrl->value < 0 || ctrl->value > 255) { - v4l2_err(sd, "invalid brightness setting %d\n", - ctrl->value); - return -ERANGE; - } - err = tvp514x_write_reg(sd, REG_BRIGHTNESS, - value); - if (err) - return err; - - decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; + err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value); + if (!err) + decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; break; case V4L2_CID_CONTRAST: - if (ctrl->value < 0 || ctrl->value > 255) { - v4l2_err(sd, "invalid contrast setting %d\n", - ctrl->value); - return -ERANGE; - } err = tvp514x_write_reg(sd, REG_CONTRAST, value); - if (err) - return err; - - decoder->tvp514x_regs[REG_CONTRAST].val = value; + if (!err) + decoder->tvp514x_regs[REG_CONTRAST].val = value; break; case V4L2_CID_SATURATION: - if (ctrl->value < 0 || ctrl->value > 255) { - v4l2_err(sd, "invalid saturation setting %d\n", - ctrl->value); - return -ERANGE; - } err = tvp514x_write_reg(sd, REG_SATURATION, value); - if (err) - return err; - - decoder->tvp514x_regs[REG_SATURATION].val = value; + if (!err) + decoder->tvp514x_regs[REG_SATURATION].val = value; break; case V4L2_CID_HUE: if (value == 180) value = 0x7F; else if (value == -180) value = 0x80; - else if (value == 0) - value = 0; - else { - v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); - return -ERANGE; - } err = tvp514x_write_reg(sd, REG_HUE, value); - if (err) - return err; - - decoder->tvp514x_regs[REG_HUE].val = value; + if (!err) + decoder->tvp514x_regs[REG_HUE].val = value; break; case V4L2_CID_AUTOGAIN: - if (value == 1) - value = 0x0F; - else if (value == 0) - value = 0x0C; - else { - v4l2_err(sd, "invalid auto gain setting %d\n", - ctrl->value); - return -ERANGE; - } - err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value); - if (err) - return err; - - decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; + err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c); + if (!err) + decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; break; - default: - v4l2_err(sd, "invalid control id %d\n", ctrl->id); - return err; } v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n", - ctrl->id, ctrl->value); - + ctrl->id, ctrl->val); return err; } @@ -1104,10 +952,18 @@ static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) return err; } -static const struct v4l2_subdev_core_ops tvp514x_core_ops = { - .queryctrl = tvp514x_queryctrl, - .g_ctrl = tvp514x_g_ctrl, +static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { .s_ctrl = tvp514x_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tvp514x_core_ops = { + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = tvp514x_s_std, }; @@ -1190,6 +1046,27 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) sd = &decoder->sd; v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); + v4l2_ctrl_handler_init(&decoder->hdl, 5); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_HUE, -180, 180, 180, 0); + v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); + v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); return 0; @@ -1209,6 +1086,7 @@ static int tvp514x_remove(struct i2c_client *client) struct tvp514x_decoder *decoder = to_decoder(sd); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); kfree(decoder); return 0; } -- cgit v0.10.2 From 6c45ec718e3b641da41654b31f35ae3e13306264 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Dec 2010 08:45:43 -0300 Subject: [media] tvp5150: use the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 5892766..959d690 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "tvp5150_reg.h" @@ -24,58 +25,14 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-2)"); -/* supported controls */ -static struct v4l2_queryctrl tvp5150_qctrl[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 128, - .flags = 0, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 128, - .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 0x1, - .default_value = 0, - .flags = 0, - } -}; - struct tvp5150 { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; v4l2_std_id norm; /* Current set standard */ u32 input; u32 output; int enable; - int bright; - int contrast; - int hue; - int sat; }; static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) @@ -83,6 +40,11 @@ static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) return container_of(sd, struct tvp5150, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tvp5150, hdl)->sd; +} + static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) { struct i2c_client *c = v4l2_get_subdevdata(sd); @@ -810,64 +772,28 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) tvp5150_write_inittab(sd, tvp5150_init_enable); /* Initialize image preferences */ - tvp5150_write(sd, TVP5150_BRIGHT_CTL, decoder->bright); - tvp5150_write(sd, TVP5150_CONTRAST_CTL, decoder->contrast); - tvp5150_write(sd, TVP5150_SATURATION_CTL, decoder->contrast); - tvp5150_write(sd, TVP5150_HUE_CTL, decoder->hue); + v4l2_ctrl_handler_setup(&decoder->hdl); tvp5150_set_std(sd, decoder->norm); return 0; }; -static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - v4l2_dbg(1, debug, sd, "g_ctrl called\n"); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = tvp5150_read(sd, TVP5150_BRIGHT_CTL); - return 0; - case V4L2_CID_CONTRAST: - ctrl->value = tvp5150_read(sd, TVP5150_CONTRAST_CTL); - return 0; - case V4L2_CID_SATURATION: - ctrl->value = tvp5150_read(sd, TVP5150_SATURATION_CTL); - return 0; - case V4L2_CID_HUE: - ctrl->value = tvp5150_read(sd, TVP5150_HUE_CTL); - return 0; - } - return -EINVAL; -} - -static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) { - u8 i, n; - n = ARRAY_SIZE(tvp5150_qctrl); - - for (i = 0; i < n; i++) { - if (ctrl->id != tvp5150_qctrl[i].id) - continue; - if (ctrl->value < tvp5150_qctrl[i].minimum || - ctrl->value > tvp5150_qctrl[i].maximum) - return -ERANGE; - v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", - ctrl->id, ctrl->value); - break; - } + struct v4l2_subdev *sd = to_sd(ctrl); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val); return 0; case V4L2_CID_CONTRAST: - tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val); return 0; case V4L2_CID_SATURATION: - tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val); return 0; case V4L2_CID_HUE: - tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val); return 0; } return -EINVAL; @@ -995,29 +921,21 @@ static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - int i; - - v4l2_dbg(1, debug, sd, "queryctrl called\n"); - - for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++) - if (qc->id && qc->id == tvp5150_qctrl[i].id) { - memcpy(qc, &(tvp5150_qctrl[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} - /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { + .s_ctrl = tvp5150_s_ctrl, +}; + static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .log_status = tvp5150_log_status, - .g_ctrl = tvp5150_g_ctrl, - .s_ctrl = tvp5150_s_ctrl, - .queryctrl = tvp5150_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = tvp5150_s_std, .reset = tvp5150_reset, .g_chip_ident = tvp5150_g_chip_ident, @@ -1077,10 +995,25 @@ static int tvp5150_probe(struct i2c_client *c, core->norm = V4L2_STD_ALL; /* Default is autodetect */ core->input = TVP5150_COMPOSITE1; core->enable = 1; - core->bright = 128; - core->contrast = 128; - core->hue = 0; - core->sat = 128; + + v4l2_ctrl_handler_init(&core->hdl, 4); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + sd->ctrl_handler = &core->hdl; + if (core->hdl.error) { + int err = core->hdl.error; + + v4l2_ctrl_handler_free(&core->hdl); + kfree(core); + return err; + } + v4l2_ctrl_handler_setup(&core->hdl); if (debug > 1) tvp5150_log_status(sd); @@ -1090,12 +1023,14 @@ static int tvp5150_probe(struct i2c_client *c, static int tvp5150_remove(struct i2c_client *c) { struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct tvp5150 *decoder = to_tvp5150(sd); v4l2_dbg(1, debug, sd, "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", c->addr << 1); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&decoder->hdl); kfree(to_tvp5150(sd)); return 0; } -- cgit v0.10.2 From 9a775323ee0cbc93b5a1801c7b9264226fb0885c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Dec 2010 08:45:59 -0300 Subject: [media] vpx3220: use control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index 91a01b3..75301d1 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -28,6 +28,7 @@ #include #include #include +#include MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); MODULE_AUTHOR("Laurent Pinchart"); @@ -44,16 +45,13 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); struct vpx3220 { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; unsigned char reg[255]; v4l2_std_id norm; int ident; int input; int enable; - int bright; - int contrast; - int hue; - int sat; }; static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) @@ -61,6 +59,11 @@ static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) return container_of(sd, struct vpx3220, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct vpx3220, hdl)->sd; +} + static char *inputs[] = { "internal", "composite", "svideo" }; /* ----------------------------------------------------------------------- */ @@ -417,88 +420,26 @@ static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int vpx3220_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - break; - - case V4L2_CID_CONTRAST: - v4l2_ctrl_query_fill(qc, 0, 63, 1, 32); - break; - - case V4L2_CID_SATURATION: - v4l2_ctrl_query_fill(qc, 0, 4095, 1, 2048); - break; - - case V4L2_CID_HUE: - v4l2_ctrl_query_fill(qc, -512, 511, 1, 0); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int vpx3220_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl) { - struct vpx3220 *decoder = to_vpx3220(sd); + struct v4l2_subdev *sd = to_sd(ctrl); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = decoder->bright; - break; + vpx3220_write(sd, 0xe6, ctrl->val); + return 0; case V4L2_CID_CONTRAST: - ctrl->value = decoder->contrast; - break; + /* Bit 7 and 8 is for noise shaping */ + vpx3220_write(sd, 0xe7, ctrl->val + 192); + return 0; case V4L2_CID_SATURATION: - ctrl->value = decoder->sat; - break; + vpx3220_fp_write(sd, 0xa0, ctrl->val); + return 0; case V4L2_CID_HUE: - ctrl->value = decoder->hue; - break; - default: - return -EINVAL; + vpx3220_fp_write(sd, 0x1c, ctrl->val); + return 0; } - return 0; -} - -static int vpx3220_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct vpx3220 *decoder = to_vpx3220(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (decoder->bright != ctrl->value) { - decoder->bright = ctrl->value; - vpx3220_write(sd, 0xe6, decoder->bright); - } - break; - case V4L2_CID_CONTRAST: - if (decoder->contrast != ctrl->value) { - /* Bit 7 and 8 is for noise shaping */ - decoder->contrast = ctrl->value; - vpx3220_write(sd, 0xe7, decoder->contrast + 192); - } - break; - case V4L2_CID_SATURATION: - if (decoder->sat != ctrl->value) { - decoder->sat = ctrl->value; - vpx3220_fp_write(sd, 0xa0, decoder->sat); - } - break; - case V4L2_CID_HUE: - if (decoder->hue != ctrl->value) { - decoder->hue = ctrl->value; - vpx3220_fp_write(sd, 0x1c, decoder->hue); - } - break; - default: - return -EINVAL; - } - return 0; + return -EINVAL; } static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) @@ -511,12 +452,20 @@ static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ide /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = { + .s_ctrl = vpx3220_s_ctrl, +}; + static const struct v4l2_subdev_core_ops vpx3220_core_ops = { .g_chip_ident = vpx3220_g_chip_ident, .init = vpx3220_init, - .g_ctrl = vpx3220_g_ctrl, - .s_ctrl = vpx3220_s_ctrl, - .queryctrl = vpx3220_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = vpx3220_s_std, }; @@ -558,10 +507,24 @@ static int vpx3220_probe(struct i2c_client *client, decoder->norm = V4L2_STD_PAL; decoder->input = 0; decoder->enable = 1; - decoder->bright = 32768; - decoder->contrast = 32768; - decoder->hue = 32768; - decoder->sat = 32768; + v4l2_ctrl_handler_init(&decoder->hdl, 4); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, 32); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_SATURATION, 0, 4095, 1, 2048); + v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, + V4L2_CID_HUE, -512, 511, 1, 0); + sd->ctrl_handler = &decoder->hdl; + if (decoder->hdl.error) { + int err = decoder->hdl.error; + + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); + return err; + } + v4l2_ctrl_handler_setup(&decoder->hdl); ver = i2c_smbus_read_byte_data(client, 0x00); pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + @@ -599,9 +562,11 @@ static int vpx3220_probe(struct i2c_client *client, static int vpx3220_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vpx3220 *decoder = to_vpx3220(sd); v4l2_device_unregister_subdev(sd); - kfree(to_vpx3220(sd)); + v4l2_ctrl_handler_free(&decoder->hdl); + kfree(decoder); return 0; } -- cgit v0.10.2 From 0eb73de019da80facad559c8a5ef21a4b357d8d5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Dec 2010 08:46:15 -0300 Subject: [media] tvp7002: use control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index c799e4e..b799851 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "tvp7002_reg.h" MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); @@ -421,13 +422,13 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { /* Device definition */ struct tvp7002 { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; const struct tvp7002_config *pdata; int ver; int streaming; const struct tvp7002_preset_definition *current_preset; - u8 gain; }; /* @@ -441,6 +442,11 @@ static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) return container_of(sd, struct tvp7002, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct tvp7002, hdl)->sd; +} + /* * tvp7002_read - Read a value from a register in an TVP7002 * @sd: ptr to v4l2_subdev struct @@ -606,78 +612,25 @@ static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, } /* - * tvp7002_g_ctrl() - Get a control - * @sd: ptr to v4l2_subdev struct - * @ctrl: ptr to v4l2_control struct - * - * Get a control for a TVP7002 decoder device. - * Returns zero when successful or -EINVAL if register access fails. - */ -static int tvp7002_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct tvp7002 *device = to_tvp7002(sd); - - switch (ctrl->id) { - case V4L2_CID_GAIN: - ctrl->value = device->gain; - return 0; - default: - return -EINVAL; - } -} - -/* * tvp7002_s_ctrl() - Set a control - * @sd: ptr to v4l2_subdev struct - * @ctrl: ptr to v4l2_control struct + * @ctrl: ptr to v4l2_ctrl struct * * Set a control in TVP7002 decoder device. * Returns zero when successful or -EINVAL if register access fails. */ -static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) { - struct tvp7002 *device = to_tvp7002(sd); + struct v4l2_subdev *sd = to_sd(ctrl); int error = 0; switch (ctrl->id) { case V4L2_CID_GAIN: - tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, - ctrl->value & 0xff, &error); - tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, - ctrl->value & 0xff, &error); - tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, - ctrl->value & 0xff, &error); - - if (error < 0) - return error; - - /* Set only after knowing there is no error */ - device->gain = ctrl->value & 0xff; - return 0; - default: - return -EINVAL; - } -} - -/* - * tvp7002_queryctrl() - Query a control - * @sd: ptr to v4l2_subdev struct - * @qc: ptr to v4l2_queryctrl struct - * - * Query a control of a TVP7002 decoder device. - * Returns zero when successful or -EINVAL if register read fails. - */ -static int tvp7002_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_GAIN: - /* - * Gain is supported [0-255, default=0, step=1] - */ - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0); - default: - return -EINVAL; + tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error); + tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error); + tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error); + return error; } + return -EINVAL; } /* @@ -924,7 +877,7 @@ static int tvp7002_log_status(struct v4l2_subdev *sd) device->streaming ? "yes" : "no"); /* Print the current value of the gain control */ - v4l2_info(sd, "Gain: %u\n", device->gain); + v4l2_ctrl_handler_log_status(&device->hdl, sd->name); return 0; } @@ -946,13 +899,21 @@ static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); } +static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { + .s_ctrl = tvp7002_s_ctrl, +}; + /* V4L2 core operation handlers */ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { .g_chip_ident = tvp7002_g_chip_ident, .log_status = tvp7002_log_status, - .g_ctrl = tvp7002_g_ctrl, - .s_ctrl = tvp7002_s_ctrl, - .queryctrl = tvp7002_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tvp7002_g_register, .s_register = tvp7002_s_register, @@ -977,12 +938,6 @@ static const struct v4l2_subdev_ops tvp7002_ops = { .video = &tvp7002_video_ops, }; -static struct tvp7002 tvp7002_dev = { - .streaming = 0, - .current_preset = tvp7002_presets, - .gain = 0, -}; - /* * tvp7002_probe - Probe a TVP7002 device * @c: ptr to i2c_client struct @@ -1013,14 +968,14 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) return -ENODEV; } - device = kmalloc(sizeof(struct tvp7002), GFP_KERNEL); + device = kzalloc(sizeof(struct tvp7002), GFP_KERNEL); if (!device) return -ENOMEM; - *device = tvp7002_dev; sd = &device->sd; device->pdata = c->dev.platform_data; + device->current_preset = tvp7002_presets; /* Tell v4l2 the device is ready */ v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); @@ -1060,6 +1015,19 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) preset.preset = device->current_preset->preset; error = tvp7002_s_dv_preset(sd, &preset); + v4l2_ctrl_handler_init(&device->hdl, 1); + v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 0); + sd->ctrl_handler = &device->hdl; + if (device->hdl.error) { + int err = device->hdl.error; + + v4l2_ctrl_handler_free(&device->hdl); + kfree(device); + return err; + } + v4l2_ctrl_handler_setup(&device->hdl); + found_error: if (error < 0) kfree(device); @@ -1083,6 +1051,7 @@ static int tvp7002_remove(struct i2c_client *c) "on address 0x%x\n", c->addr); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&device->hdl); kfree(device); return 0; } -- cgit v0.10.2 From 7e996afa81f71ade7870eb26b1b17350b4395646 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 23 Jan 2011 12:33:16 -0200 Subject: [media] vivi: convert to the control framework and add test controls Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 9ed3831..66ce69e 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #define VIVI_MODULE_NAME "vivi" @@ -158,13 +159,20 @@ static LIST_HEAD(vivi_devlist); struct vivi_dev { struct list_head vivi_devlist; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; /* controls */ - int brightness; - int contrast; - int saturation; - int hue; - int volume; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + struct v4l2_ctrl *volume; + struct v4l2_ctrl *button; + struct v4l2_ctrl *boolean; + struct v4l2_ctrl *int32; + struct v4l2_ctrl *int64; + struct v4l2_ctrl *menu; + struct v4l2_ctrl *string; spinlock_t slock; struct mutex mutex; @@ -177,6 +185,7 @@ struct vivi_dev { /* Several counters */ unsigned ms; unsigned long jiffies; + unsigned button_pressed; int mv_count; /* Controls bars movement */ @@ -470,14 +479,30 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->width, dev->height, dev->input); gen_text(dev, vbuf, line++ * 16, 16, str); + mutex_lock(&dev->ctrl_handler.lock); snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ", - dev->brightness, - dev->contrast, - dev->saturation, - dev->hue); + dev->brightness->cur.val, + dev->contrast->cur.val, + dev->saturation->cur.val, + dev->hue->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); - snprintf(str, sizeof(str), " volume %3d ", dev->volume); + snprintf(str, sizeof(str), " volume %3d ", dev->volume->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); + snprintf(str, sizeof(str), " int32 %d, int64 %lld ", + dev->int32->cur.val, + dev->int64->cur.val64); + gen_text(dev, vbuf, line++ * 16, 16, str); + snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ", + dev->boolean->cur.val, + dev->menu->qmenu[dev->menu->cur.val], + dev->string->cur.string); + mutex_unlock(&dev->ctrl_handler.lock); + gen_text(dev, vbuf, line++ * 16, 16, str); + if (dev->button_pressed) { + dev->button_pressed--; + snprintf(str, sizeof(str), " button pressed!"); + gen_text(dev, vbuf, line++ * 16, 16, str); + } dev->mv_count += 2; @@ -957,80 +982,14 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) } /* --- controls ---------------------------------------------- */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 200); - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 16); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - } - return -EINVAL; -} -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int vivi_s_ctrl(struct v4l2_ctrl *ctrl) { - struct vivi_dev *dev = video_drvdata(file); + struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler); - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = dev->volume; - return 0; - case V4L2_CID_BRIGHTNESS: - ctrl->value = dev->brightness; - return 0; - case V4L2_CID_CONTRAST: - ctrl->value = dev->contrast; - return 0; - case V4L2_CID_SATURATION: - ctrl->value = dev->saturation; - return 0; - case V4L2_CID_HUE: - ctrl->value = dev->hue; - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct vivi_dev *dev = video_drvdata(file); - struct v4l2_queryctrl qc; - int err; - - qc.id = ctrl->id; - err = vidioc_queryctrl(file, priv, &qc); - if (err < 0) - return err; - if (ctrl->value < qc.minimum || ctrl->value > qc.maximum) - return -ERANGE; - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; - return 0; - case V4L2_CID_BRIGHTNESS: - dev->brightness = ctrl->value; - return 0; - case V4L2_CID_CONTRAST: - dev->contrast = ctrl->value; - return 0; - case V4L2_CID_SATURATION: - dev->saturation = ctrl->value; - return 0; - case V4L2_CID_HUE: - dev->hue = ctrl->value; - return 0; - } - return -EINVAL; + if (ctrl == dev->button) + dev->button_pressed = 30; + return 0; } /* ------------------------------------------------------------------ @@ -1094,6 +1053,79 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma) return ret; } +static const struct v4l2_ctrl_ops vivi_ctrl_ops = { + .s_ctrl = vivi_s_ctrl, +}; + +#define VIVI_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) + +static const struct v4l2_ctrl_config vivi_ctrl_button = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 0, + .name = "Button", + .type = V4L2_CTRL_TYPE_BUTTON, +}; + +static const struct v4l2_ctrl_config vivi_ctrl_boolean = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 1, + .name = "Boolean", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static const struct v4l2_ctrl_config vivi_ctrl_int32 = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 2, + .name = "Integer 32 Bits", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = -2147483648, + .max = 2147483647, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivi_ctrl_int64 = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 3, + .name = "Integer 64 Bits", + .type = V4L2_CTRL_TYPE_INTEGER64, +}; + +static const char * const vivi_ctrl_menu_strings[] = { + "Menu Item 0 (Skipped)", + "Menu Item 1", + "Menu Item 2 (Skipped)", + "Menu Item 3", + "Menu Item 4", + "Menu Item 5 (Skipped)", + NULL, +}; + +static const struct v4l2_ctrl_config vivi_ctrl_menu = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 4, + .name = "Menu", + .type = V4L2_CTRL_TYPE_MENU, + .min = 1, + .max = 4, + .def = 3, + .menu_skip_mask = 0x04, + .qmenu = vivi_ctrl_menu_strings, +}; + +static const struct v4l2_ctrl_config vivi_ctrl_string = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 5, + .name = "String", + .type = V4L2_CTRL_TYPE_STRING, + .min = 2, + .max = 4, + .step = 1, +}; + static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, .open = vivi_open, @@ -1120,9 +1152,6 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_s_input = vidioc_s_input, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, }; static struct video_device vivi_template = { @@ -1153,6 +1182,7 @@ static int vivi_release(void) video_device_node_name(dev->vfd)); video_unregister_device(dev->vfd); v4l2_device_unregister(&dev->v4l2_dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); kfree(dev); } @@ -1163,6 +1193,7 @@ static int __init vivi_create_instance(int inst) { struct vivi_dev *dev; struct video_device *vfd; + struct v4l2_ctrl_handler *hdl; struct vb2_queue *q; int ret; @@ -1179,11 +1210,29 @@ static int __init vivi_create_instance(int inst) dev->fmt = &formats[0]; dev->width = 640; dev->height = 480; - dev->volume = 200; - dev->brightness = 127; - dev->contrast = 16; - dev->saturation = 127; - dev->hue = 0; + hdl = &dev->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 11); + dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200); + dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 16); + dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 127); + dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL); + dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL); + dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL); + dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL); + dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL); + dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL); + if (hdl->error) { + ret = hdl->error; + goto unreg_dev; + } + dev->v4l2_dev.ctrl_handler = hdl; /* initialize locks */ spin_lock_init(&dev->slock); @@ -1241,6 +1290,7 @@ static int __init vivi_create_instance(int inst) rel_vdev: video_device_release(vfd); unreg_dev: + v4l2_ctrl_handler_free(hdl); v4l2_device_unregister(&dev->v4l2_dev); free_dev: kfree(dev); -- cgit v0.10.2 From 5b2830298dc0534eb33277a31f353bb524e2cc2e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Jan 2011 17:32:28 -0300 Subject: [media] vivi: fix compiler warning drivers/media/video/vivi.c:1059: warning: this decimal constant is unsigned only in ISO C90 Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 66ce69e..bd104d0 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -1082,8 +1082,8 @@ static const struct v4l2_ctrl_config vivi_ctrl_int32 = { .id = VIVI_CID_CUSTOM_BASE + 2, .name = "Integer 32 Bits", .type = V4L2_CTRL_TYPE_INTEGER, - .min = -2147483648, - .max = 2147483647, + .min = 0x80000000, + .max = 0x7fffffff, .step = 1, }; -- cgit v0.10.2 From b577f962d27c055571f72ddd73ae3aded39a6261 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 13 Jan 2011 05:05:36 -0300 Subject: [media] pwc: convert to core-assisted locking Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index bd1519a..4aa70fa 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -1098,7 +1098,6 @@ static int pwc_video_open(struct file *file) return -EBUSY; } - mutex_lock(&pdev->modlock); pwc_construct(pdev); /* set min/max sizes correct */ if (!pdev->usb_init) { PWC_DEBUG_OPEN("Doing first time initialization.\n"); @@ -1130,7 +1129,6 @@ static int pwc_video_open(struct file *file) if (i < 0) { PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n"); pwc_free_buffers(pdev); - mutex_unlock(&pdev->modlock); return i; } @@ -1171,7 +1169,6 @@ static int pwc_video_open(struct file *file) if (i) { PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n"); pwc_free_buffers(pdev); - mutex_unlock(&pdev->modlock); return i; } @@ -1181,7 +1178,6 @@ static int pwc_video_open(struct file *file) pdev->vopen++; file->private_data = vdev; - mutex_unlock(&pdev->modlock); PWC_DEBUG_OPEN("<< video_open() returns 0.\n"); return 0; } @@ -1210,7 +1206,6 @@ static int pwc_video_close(struct file *file) PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); pdev = video_get_drvdata(vdev); - mutex_lock(&pdev->modlock); if (pdev->vopen == 0) PWC_DEBUG_MODULE("video_close() called on closed device?\n"); @@ -1248,7 +1243,6 @@ static int pwc_video_close(struct file *file) if (device_hint[hint].pdev == pdev) device_hint[hint].pdev = NULL; } - mutex_unlock(&pdev->modlock); return 0; } @@ -1283,7 +1277,6 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, if (pdev == NULL) return -EFAULT; - mutex_lock(&pdev->modlock); if (pdev->error_status) { rv = -pdev->error_status; /* Something happened, report what. */ goto err_out; @@ -1318,8 +1311,10 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, rv = -ERESTARTSYS; goto err_out; } + mutex_unlock(&pdev->modlock); schedule(); set_current_state(TASK_INTERRUPTIBLE); + mutex_lock(&pdev->modlock); } remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); @@ -1352,10 +1347,8 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, pdev->image_read_pos = 0; pwc_next_image(pdev); } - mutex_unlock(&pdev->modlock); return count; err_out: - mutex_unlock(&pdev->modlock); return rv; } @@ -1372,9 +1365,7 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait) return -EFAULT; /* Start the stream (if not already started) */ - mutex_lock(&pdev->modlock); ret = pwc_isoc_init(pdev); - mutex_unlock(&pdev->modlock); if (ret) return ret; @@ -1398,10 +1389,8 @@ static long pwc_video_ioctl(struct file *file, goto out; pdev = video_get_drvdata(vdev); - mutex_lock(&pdev->modlock); if (!pdev->unplugged) r = video_usercopy(file, cmd, arg, pwc_video_do_ioctl); - mutex_unlock(&pdev->modlock); out: return r; } @@ -1754,6 +1743,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id } memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); pdev->vdev->parent = &intf->dev; + pdev->vdev->lock = &pdev->modlock; strcpy(pdev->vdev->name, name); video_set_drvdata(pdev->vdev, pdev); diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 8ca4d22..8a7e52c 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -830,8 +830,10 @@ long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) set_current_state(TASK_RUNNING); return -ERESTARTSYS; } + mutex_unlock(&pdev->modlock); schedule(); set_current_state(TASK_INTERRUPTIBLE); + mutex_lock(&pdev->modlock); } remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); -- cgit v0.10.2 From afa38521614dcdfe12c765ff76d4c137a056e905 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 22 Jan 2011 06:34:55 -0300 Subject: [media] pwc: convert to video_ioctl2 Tested with a Logitech QuickCam Pro 4000. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 4aa70fa..780af5f 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -151,8 +151,6 @@ static int pwc_video_close(struct file *file); static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); static unsigned int pwc_video_poll(struct file *file, poll_table *wait); -static long pwc_video_ioctl(struct file *file, - unsigned int ioctlnr, unsigned long arg); static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); static const struct v4l2_file_operations pwc_fops = { @@ -162,7 +160,7 @@ static const struct v4l2_file_operations pwc_fops = { .read = pwc_video_read, .poll = pwc_video_poll, .mmap = pwc_video_mmap, - .unlocked_ioctl = pwc_video_ioctl, + .unlocked_ioctl = video_ioctl2, }; static struct video_device pwc_template = { .name = "Philips Webcam", /* Filled in later */ @@ -1378,23 +1376,6 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait) return 0; } -static long pwc_video_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - long r = -ENODEV; - - if (!vdev) - goto out; - pdev = video_get_drvdata(vdev); - - if (!pdev->unplugged) - r = video_usercopy(file, cmd, arg, pwc_video_do_ioctl); -out: - return r; -} - static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) { struct video_device *vdev = file->private_data; @@ -1744,6 +1725,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); pdev->vdev->parent = &intf->dev; pdev->vdev->lock = &pdev->modlock; + pdev->vdev->ioctl_ops = &pwc_ioctl_ops; strcpy(pdev->vdev->name, name); video_set_drvdata(pdev->vdev, pdev); diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 8a7e52c..68a5313 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -341,606 +341,554 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) } -long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { struct video_device *vdev = video_devdata(file); - struct pwc_device *pdev; - DECLARE_WAITQUEUE(wait, current); - - if (vdev == NULL) - return -EFAULT; - pdev = video_get_drvdata(vdev); - if (pdev == NULL) - return -EFAULT; + struct pwc_device *pdev = video_drvdata(file); + + strcpy(cap->driver, PWC_NAME); + strlcpy(cap->card, vdev->name, sizeof(cap->card)); + usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->version = PWC_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + return 0; +} -#ifdef CONFIG_USB_PWC_DEBUG - if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) { - v4l_printk_ioctl(cmd); - printk("\n"); - } -#endif - - - switch (cmd) { - /* V4L2 Layer */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCAP) This application "\ - "try to use the v4l2 layer\n"); - strcpy(cap->driver,PWC_NAME); - strlcpy(cap->card, vdev->name, sizeof(cap->card)); - usb_make_path(pdev->udev,cap->bus_info,sizeof(cap->bus_info)); - cap->version = PWC_VERSION_CODE; - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - return 0; - } +static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index) /* Only one INPUT is supported */ + return -EINVAL; - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; + strcpy(i->name, "usb"); + return 0; +} - if ( i->index ) /* Only one INPUT is supported */ - return -EINVAL; +static int pwc_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} - memset(i, 0, sizeof(struct v4l2_input)); - strcpy(i->name, "usb"); - return 0; - } +static int pwc_s_input(struct file *file, void *fh, unsigned int i) +{ + return i ? -EINVAL : 0; +} - case VIDIOC_G_INPUT: - { - int *i = arg; - *i = 0; /* Only one INPUT is supported */ - return 0; - } - case VIDIOC_S_INPUT: - { - int *i = arg; +static int pwc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) +{ + int i; - if ( *i ) { /* Only one INPUT is supported */ - PWC_DEBUG_IOCTL("Only one input source is"\ - " supported with this webcam.\n"); - return -EINVAL; - } + for (i = 0; i < sizeof(pwc_controls) / sizeof(struct v4l2_queryctrl); i++) { + if (pwc_controls[i].id == c->id) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n"); + memcpy(c, &pwc_controls[i], sizeof(struct v4l2_queryctrl)); return 0; } + } + return -EINVAL; +} - /* TODO: */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *c = arg; - int i; - - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) query id=%d\n", c->id); - for (i=0; iid) { - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n"); - memcpy(c,&pwc_controls[i],sizeof(struct v4l2_queryctrl)); - return 0; - } - } - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) not found\n"); +static int pwc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) +{ + struct pwc_device *pdev = video_drvdata(file); + int ret; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = pwc_get_brightness(pdev); + if (c->value < 0) return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *c = arg; - int ret; - - switch (c->id) - { - case V4L2_CID_BRIGHTNESS: - c->value = pwc_get_brightness(pdev); - if (c->value<0) - return -EINVAL; - return 0; - case V4L2_CID_CONTRAST: - c->value = pwc_get_contrast(pdev); - if (c->value<0) - return -EINVAL; - return 0; - case V4L2_CID_SATURATION: - ret = pwc_get_saturation(pdev, &c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_GAMMA: - c->value = pwc_get_gamma(pdev); - if (c->value<0) - return -EINVAL; - return 0; - case V4L2_CID_RED_BALANCE: - ret = pwc_get_red_gain(pdev, &c->value); - if (ret<0) - return -EINVAL; - c->value >>= 8; - return 0; - case V4L2_CID_BLUE_BALANCE: - ret = pwc_get_blue_gain(pdev, &c->value); - if (ret<0) - return -EINVAL; - c->value >>= 8; - return 0; - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = pwc_get_awb(pdev); - if (ret<0) - return -EINVAL; - c->value = (ret == PWC_WB_MANUAL)?0:1; - return 0; - case V4L2_CID_GAIN: - ret = pwc_get_agc(pdev, &c->value); - if (ret<0) - return -EINVAL; - c->value >>= 8; - return 0; - case V4L2_CID_AUTOGAIN: - ret = pwc_get_agc(pdev, &c->value); - if (ret<0) - return -EINVAL; - c->value = (c->value < 0)?1:0; - return 0; - case V4L2_CID_EXPOSURE: - ret = pwc_get_shutter_speed(pdev, &c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_COLOUR_MODE: - ret = pwc_get_colour_mode(pdev, &c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_AUTOCONTOUR: - ret = pwc_get_contour(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value=(c->value == -1?1:0); - return 0; - case V4L2_CID_PRIVATE_CONTOUR: - ret = pwc_get_contour(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value >>= 10; - return 0; - case V4L2_CID_PRIVATE_BACKLIGHT: - ret = pwc_get_backlight(pdev, &c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_FLICKERLESS: - ret = pwc_get_flicker(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value=(c->value?1:0); - return 0; - case V4L2_CID_PRIVATE_NOISE_REDUCTION: - ret = pwc_get_dynamic_noise(pdev, &c->value); - if (ret < 0) - return -EINVAL; - return 0; - - case V4L2_CID_PRIVATE_SAVE_USER: - case V4L2_CID_PRIVATE_RESTORE_USER: - case V4L2_CID_PRIVATE_RESTORE_FACTORY: - return -EINVAL; - } + return 0; + case V4L2_CID_CONTRAST: + c->value = pwc_get_contrast(pdev); + if (c->value < 0) return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *c = arg; - int ret; - - switch (c->id) - { - case V4L2_CID_BRIGHTNESS: - c->value <<= 9; - ret = pwc_set_brightness(pdev, c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_CONTRAST: - c->value <<= 10; - ret = pwc_set_contrast(pdev, c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_SATURATION: - ret = pwc_set_saturation(pdev, c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_GAMMA: - c->value <<= 11; - ret = pwc_set_gamma(pdev, c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_RED_BALANCE: - c->value <<= 8; - ret = pwc_set_red_gain(pdev, c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_BLUE_BALANCE: - c->value <<= 8; - ret = pwc_set_blue_gain(pdev, c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_AUTO_WHITE_BALANCE: - c->value = (c->value == 0)?PWC_WB_MANUAL:PWC_WB_AUTO; - ret = pwc_set_awb(pdev, c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_EXPOSURE: - c->value <<= 8; - ret = pwc_set_shutter_speed(pdev, c->value?0:1, c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_AUTOGAIN: - /* autogain off means nothing without a gain */ - if (c->value == 0) - return 0; - ret = pwc_set_agc(pdev, c->value, 0); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_GAIN: - c->value <<= 8; - ret = pwc_set_agc(pdev, 0, c->value); - if (ret<0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_SAVE_USER: - if (pwc_save_user(pdev)) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_RESTORE_USER: - if (pwc_restore_user(pdev)) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_RESTORE_FACTORY: - if (pwc_restore_factory(pdev)) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_COLOUR_MODE: - ret = pwc_set_colour_mode(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_AUTOCONTOUR: - c->value=(c->value == 1)?-1:0; - ret = pwc_set_contour(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_CONTOUR: - c->value <<= 10; - ret = pwc_set_contour(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_BACKLIGHT: - ret = pwc_set_backlight(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_FLICKERLESS: - ret = pwc_set_flicker(pdev, c->value); - if (ret < 0) - return -EINVAL; - case V4L2_CID_PRIVATE_NOISE_REDUCTION: - ret = pwc_set_dynamic_noise(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - - } + return 0; + case V4L2_CID_SATURATION: + ret = pwc_get_saturation(pdev, &c->value); + if (ret < 0) return -EINVAL; - } + return 0; + case V4L2_CID_GAMMA: + c->value = pwc_get_gamma(pdev); + if (c->value < 0) + return -EINVAL; + return 0; + case V4L2_CID_RED_BALANCE: + ret = pwc_get_red_gain(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value >>= 8; + return 0; + case V4L2_CID_BLUE_BALANCE: + ret = pwc_get_blue_gain(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value >>= 8; + return 0; + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = pwc_get_awb(pdev); + if (ret < 0) + return -EINVAL; + c->value = (ret == PWC_WB_MANUAL) ? 0 : 1; + return 0; + case V4L2_CID_GAIN: + ret = pwc_get_agc(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value >>= 8; + return 0; + case V4L2_CID_AUTOGAIN: + ret = pwc_get_agc(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value = (c->value < 0) ? 1 : 0; + return 0; + case V4L2_CID_EXPOSURE: + ret = pwc_get_shutter_speed(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_COLOUR_MODE: + ret = pwc_get_colour_mode(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_AUTOCONTOUR: + ret = pwc_get_contour(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value = (c->value == -1 ? 1 : 0); + return 0; + case V4L2_CID_PRIVATE_CONTOUR: + ret = pwc_get_contour(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value >>= 10; + return 0; + case V4L2_CID_PRIVATE_BACKLIGHT: + ret = pwc_get_backlight(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_FLICKERLESS: + ret = pwc_get_flicker(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value = (c->value ? 1 : 0); + return 0; + case V4L2_CID_PRIVATE_NOISE_REDUCTION: + ret = pwc_get_dynamic_noise(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *f = arg; - int index; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* We only support two format: the raw format, and YUV */ - index = f->index; - memset(f,0,sizeof(struct v4l2_fmtdesc)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->index = index; - switch(index) - { - case 0: - /* RAW format */ - f->pixelformat = pdev->type<=646?V4L2_PIX_FMT_PWC1:V4L2_PIX_FMT_PWC2; - f->flags = V4L2_FMT_FLAG_COMPRESSED; - strlcpy(f->description,"Raw Philips Webcam",sizeof(f->description)); - break; - case 1: - f->pixelformat = V4L2_PIX_FMT_YUV420; - strlcpy(f->description,"4:2:0, planar, Y-Cb-Cr",sizeof(f->description)); - break; - default: - return -EINVAL; - } - return 0; - } + case V4L2_CID_PRIVATE_SAVE_USER: + case V4L2_CID_PRIVATE_RESTORE_USER: + case V4L2_CID_PRIVATE_RESTORE_FACTORY: + return -EINVAL; + } + return -EINVAL; +} - case VIDIOC_G_FMT: - { - struct v4l2_format *f = arg; +static int pwc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +{ + struct pwc_device *pdev = video_drvdata(file); + int ret; + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value <<= 9; + ret = pwc_set_brightness(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_CONTRAST: + c->value <<= 10; + ret = pwc_set_contrast(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_SATURATION: + ret = pwc_set_saturation(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_GAMMA: + c->value <<= 11; + ret = pwc_set_gamma(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_RED_BALANCE: + c->value <<= 8; + ret = pwc_set_red_gain(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_BLUE_BALANCE: + c->value <<= 8; + ret = pwc_set_blue_gain(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_AUTO_WHITE_BALANCE: + c->value = (c->value == 0) ? PWC_WB_MANUAL : PWC_WB_AUTO; + ret = pwc_set_awb(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_EXPOSURE: + c->value <<= 8; + ret = pwc_set_shutter_speed(pdev, c->value ? 0 : 1, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_AUTOGAIN: + /* autogain off means nothing without a gain */ + if (c->value == 0) + return 0; + ret = pwc_set_agc(pdev, c->value, 0); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_GAIN: + c->value <<= 8; + ret = pwc_set_agc(pdev, 0, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_SAVE_USER: + if (pwc_save_user(pdev)) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_RESTORE_USER: + if (pwc_restore_user(pdev)) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_RESTORE_FACTORY: + if (pwc_restore_factory(pdev)) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_COLOUR_MODE: + ret = pwc_set_colour_mode(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_AUTOCONTOUR: + c->value = (c->value == 1) ? -1 : 0; + ret = pwc_set_contour(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_CONTOUR: + c->value <<= 10; + ret = pwc_set_contour(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_BACKLIGHT: + ret = pwc_set_backlight(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_FLICKERLESS: + ret = pwc_set_flicker(pdev, c->value); + if (ret < 0) + return -EINVAL; + case V4L2_CID_PRIVATE_NOISE_REDUCTION: + ret = pwc_set_dynamic_noise(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; - PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",pdev->image.x,pdev->image.y); - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + } + return -EINVAL; +} - pwc_vidioc_fill_fmt(pdev, f); +static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct pwc_device *pdev = video_drvdata(file); + + /* We only support two format: the raw format, and YUV */ + switch (f->index) { + case 0: + /* RAW format */ + f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strlcpy(f->description, "Raw Philips Webcam", sizeof(f->description)); + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_YUV420; + strlcpy(f->description, "4:2:0, planar, Y-Cb-Cr", sizeof(f->description)); + break; + default: + return -EINVAL; + } + return 0; +} - return 0; - } +static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct pwc_device *pdev = video_drvdata(file); - case VIDIOC_TRY_FMT: - return pwc_vidioc_try_fmt(pdev, arg); + PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n", + pdev->image.x, pdev->image.y); + pwc_vidioc_fill_fmt(pdev, f); + return 0; +} - case VIDIOC_S_FMT: - return pwc_vidioc_set_fmt(pdev, arg); +static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct pwc_device *pdev = video_drvdata(file); - case VIDIOC_G_STD: - { - v4l2_std_id *std = arg; - *std = V4L2_STD_UNKNOWN; - return 0; - } + return pwc_vidioc_try_fmt(pdev, f); +} - case VIDIOC_S_STD: - { - v4l2_std_id *std = arg; - if (*std != V4L2_STD_UNKNOWN) - return -EINVAL; - return 0; - } +static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct pwc_device *pdev = video_drvdata(file); - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *std = arg; - if (std->index != 0) - return -EINVAL; - std->id = V4L2_STD_UNKNOWN; - strlcpy(std->name, "webcam", sizeof(std->name)); - return 0; - } + return pwc_vidioc_set_fmt(pdev, f); +} - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *rb = arg; - int nbuffers; +static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) +{ + int nbuffers; - PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n",rb->count); - if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (rb->memory != V4L2_MEMORY_MMAP) - return -EINVAL; + PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n", rb->count); + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; - nbuffers = rb->count; - if (nbuffers < 2) - nbuffers = 2; - else if (nbuffers > pwc_mbufs) - nbuffers = pwc_mbufs; - /* Force to use our # of buffers */ - rb->count = pwc_mbufs; - return 0; - } + nbuffers = rb->count; + if (nbuffers < 2) + nbuffers = 2; + else if (nbuffers > pwc_mbufs) + nbuffers = pwc_mbufs; + /* Force to use our # of buffers */ + rb->count = pwc_mbufs; + return 0; +} - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *buf = arg; - int index; +static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct pwc_device *pdev = video_drvdata(file); + int index; - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n",buf->index); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n"); - return -EINVAL; - } - if (buf->memory != V4L2_MEMORY_MMAP) { - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad memory type\n"); - return -EINVAL; - } - index = buf->index; - if (index < 0 || index >= pwc_mbufs) { - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index); - return -EINVAL; - } + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n", buf->index); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n"); + return -EINVAL; + } + index = buf->index; + if (index < 0 || index >= pwc_mbufs) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index); + return -EINVAL; + } - memset(buf, 0, sizeof(struct v4l2_buffer)); - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->index = index; - buf->m.offset = index * pdev->len_per_image; - if (pdev->pixfmt != V4L2_PIX_FMT_YUV420) - buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); - else - buf->bytesused = pdev->view.size; - buf->field = V4L2_FIELD_NONE; - buf->memory = V4L2_MEMORY_MMAP; - //buf->flags = V4L2_BUF_FLAG_MAPPED; - buf->length = pdev->len_per_image; - - PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n",buf->index); - PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n",buf->m.offset); - PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n",buf->bytesused); + buf->m.offset = index * pdev->len_per_image; + if (pdev->pixfmt != V4L2_PIX_FMT_YUV420) + buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); + else + buf->bytesused = pdev->view.size; + buf->field = V4L2_FIELD_NONE; + buf->memory = V4L2_MEMORY_MMAP; + /*buf->flags = V4L2_BUF_FLAG_MAPPED;*/ + buf->length = pdev->len_per_image; - return 0; - } + PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n", buf->index); + PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n", buf->m.offset); + PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n", buf->bytesused); - case VIDIOC_QBUF: - { - struct v4l2_buffer *buf = arg; + return 0; +} - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n",buf->index); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - if (buf->index >= pwc_mbufs) - return -EINVAL; +static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n", buf->index); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + if (buf->index >= pwc_mbufs) + return -EINVAL; - buf->flags |= V4L2_BUF_FLAG_QUEUED; - buf->flags &= ~V4L2_BUF_FLAG_DONE; + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->flags &= ~V4L2_BUF_FLAG_DONE; - return 0; - } + return 0; +} - case VIDIOC_DQBUF: - { - struct v4l2_buffer *buf = arg; - int ret; +static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + DECLARE_WAITQUEUE(wait, current); + struct pwc_device *pdev = video_drvdata(file); + int ret; - PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n"); + PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n"); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - /* Add ourselves to the frame wait-queue. - - FIXME: needs auditing for safety. - QUESTION: In what respect? I think that using the - frameq is safe now. - */ - add_wait_queue(&pdev->frameq, &wait); - while (pdev->full_frames == NULL) { - if (pdev->error_status) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -pdev->error_status; - } + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status; + } - if (signal_pending(current)) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -ERESTARTSYS; - } - mutex_unlock(&pdev->modlock); - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - mutex_lock(&pdev->modlock); - } + if (signal_pending(current)) { remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + mutex_unlock(&pdev->modlock); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + mutex_lock(&pdev->modlock); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); - PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n"); - /* Decompress data in pdev->images[pdev->fill_image] */ - ret = pwc_handle_frame(pdev); - if (ret) - return -EFAULT; - PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n"); - - buf->index = pdev->fill_image; - if (pdev->pixfmt != V4L2_PIX_FMT_YUV420) - buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); - else - buf->bytesused = pdev->view.size; - buf->flags = V4L2_BUF_FLAG_MAPPED; - buf->field = V4L2_FIELD_NONE; - do_gettimeofday(&buf->timestamp); - buf->sequence = 0; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = pdev->fill_image * pdev->len_per_image; - buf->length = pdev->len_per_image; - pwc_next_image(pdev); - - PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index); - PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n",buf->length); - PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n",buf->m.offset); - PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n",buf->bytesused); - PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n"); - return 0; + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n"); + /* Decompress data in pdev->images[pdev->fill_image] */ + ret = pwc_handle_frame(pdev); + if (ret) + return -EFAULT; + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n"); + + buf->index = pdev->fill_image; + if (pdev->pixfmt != V4L2_PIX_FMT_YUV420) + buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); + else + buf->bytesused = pdev->view.size; + buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->field = V4L2_FIELD_NONE; + do_gettimeofday(&buf->timestamp); + buf->sequence = 0; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = pdev->fill_image * pdev->len_per_image; + buf->length = pdev->len_per_image; + pwc_next_image(pdev); + + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n", buf->index); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n", buf->length); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n", buf->m.offset); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n", buf->bytesused); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n"); + return 0; - } +} - case VIDIOC_STREAMON: - { - return pwc_isoc_init(pdev); - } +static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct pwc_device *pdev = video_drvdata(file); - case VIDIOC_STREAMOFF: - { - pwc_isoc_cleanup(pdev); - return 0; - } + return pwc_isoc_init(pdev); +} - case VIDIOC_ENUM_FRAMESIZES: - { - struct v4l2_frmsizeenum *fsize = arg; - unsigned int i = 0, index = fsize->index; - - if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) { - for (i = 0; i < PSZ_MAX; i++) { - if (pdev->image_mask & (1UL << i)) { - if (!index--) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = pwc_image_sizes[i].x; - fsize->discrete.height = pwc_image_sizes[i].y; - return 0; - } - } - } - } else if (fsize->index == 0 && - ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) || - (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) { - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = pdev->abs_max.x; - fsize->discrete.height = pdev->abs_max.y; - return 0; - } - return -EINVAL; - } +static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct pwc_device *pdev = video_drvdata(file); - case VIDIOC_ENUM_FRAMEINTERVALS: - { - struct v4l2_frmivalenum *fival = arg; - int size = -1; - unsigned int i; - - for (i = 0; i < PSZ_MAX; i++) { - if (pwc_image_sizes[i].x == fival->width && - pwc_image_sizes[i].y == fival->height) { - size = i; - break; + pwc_isoc_cleanup(pdev); + return 0; +} + +static int pwc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct pwc_device *pdev = video_drvdata(file); + unsigned int i = 0, index = fsize->index; + + if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) { + for (i = 0; i < PSZ_MAX; i++) { + if (pdev->image_mask & (1UL << i)) { + if (!index--) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = pwc_image_sizes[i].x; + fsize->discrete.height = pwc_image_sizes[i].y; + return 0; } } + } + } else if (fsize->index == 0 && + ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) || + (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) { + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = pdev->abs_max.x; + fsize->discrete.height = pdev->abs_max.y; + return 0; + } + return -EINVAL; +} - /* TODO: Support raw format */ - if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) { - return -EINVAL; - } +static int pwc_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct pwc_device *pdev = video_drvdata(file); + int size = -1; + unsigned int i; + + for (i = 0; i < PSZ_MAX; i++) { + if (pwc_image_sizes[i].x == fival->width && + pwc_image_sizes[i].y == fival->height) { + size = i; + break; + } + } - i = pwc_get_fps(pdev, fival->index, size); - if (!i) - return -EINVAL; + /* TODO: Support raw format */ + if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) + return -EINVAL; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete.numerator = 1; - fival->discrete.denominator = i; + i = pwc_get_fps(pdev, fival->index, size); + if (!i) + return -EINVAL; - return 0; - } + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1; + fival->discrete.denominator = i; - default: - return pwc_ioctl(pdev, cmd, arg); - } /* ..switch */ return 0; } +static long pwc_default(struct file *file, void *fh, int cmd, void *arg) +{ + struct pwc_device *pdev = video_drvdata(file); + + return pwc_ioctl(pdev, cmd, arg); +} + +const struct v4l2_ioctl_ops pwc_ioctl_ops = { + .vidioc_querycap = pwc_querycap, + .vidioc_enum_input = pwc_enum_input, + .vidioc_g_input = pwc_g_input, + .vidioc_s_input = pwc_s_input, + .vidioc_enum_fmt_vid_cap = pwc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap, + .vidioc_queryctrl = pwc_queryctrl, + .vidioc_g_ctrl = pwc_g_ctrl, + .vidioc_s_ctrl = pwc_s_ctrl, + .vidioc_reqbufs = pwc_reqbufs, + .vidioc_querybuf = pwc_querybuf, + .vidioc_qbuf = pwc_qbuf, + .vidioc_dqbuf = pwc_dqbuf, + .vidioc_streamon = pwc_streamon, + .vidioc_streamoff = pwc_streamoff, + .vidioc_enum_framesizes = pwc_enum_framesizes, + .vidioc_enum_frameintervals = pwc_enum_frameintervals, + .vidioc_default = pwc_default, +}; + + /* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 16bbc6d..e947766 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -339,8 +339,7 @@ extern int pwc_camera_power(struct pwc_device *pdev, int power); /* Private ioctl()s; see pwc-ioctl.h */ extern long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); -/** Functions in pwc-v4l.c */ -extern long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); +extern const struct v4l2_ioctl_ops pwc_ioctl_ops; /** pwc-uncompress.c */ /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ -- cgit v0.10.2 From cbfb3daa66bc43f4e2d4f2063db7472a8b2837f5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 22 Jan 2011 06:36:02 -0300 Subject: [media] cpia2: convert to video_ioctl2 Unable to test this due to lack of hardware. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 9bad398..363fe09 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -395,10 +395,14 @@ static int sync(struct camera_data *cam, int frame_nr) * *****************************************************************************/ -static int ioctl_set_gpio(void *arg, struct camera_data *cam) +static long cpia2_default(struct file *file, void *fh, int cmd, void *arg) { + struct camera_data *cam = video_drvdata(file); __u32 gpio_val; + if (cmd != CPIA2_CID_GPIO) + return -EINVAL; + gpio_val = *(__u32*) arg; if (gpio_val &~ 0xFFU) @@ -415,11 +419,10 @@ static int ioctl_set_gpio(void *arg, struct camera_data *cam) * *****************************************************************************/ -static int ioctl_querycap(void *arg, struct camera_data *cam) +static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc) { - struct v4l2_capability *vc = arg; + struct camera_data *cam = video_drvdata(file); - memset(vc, 0, sizeof(*vc)); strcpy(vc->driver, "cpia2"); if (cam->params.pnp_id.product == 0x151) @@ -479,22 +482,26 @@ static int ioctl_querycap(void *arg, struct camera_data *cam) * *****************************************************************************/ -static int ioctl_input(unsigned int ioclt_nr,void *arg,struct camera_data *cam) +static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *i) { - struct v4l2_input *i = arg; - - if(ioclt_nr != VIDIOC_G_INPUT) { - if (i->index != 0) - return -EINVAL; - } - - memset(i, 0, sizeof(*i)); + if (i->index) + return -EINVAL; strcpy(i->name, "Camera"); i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} +static int cpia2_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; return 0; } +static int cpia2_s_input(struct file *file, void *fh, unsigned int i) +{ + return i ? -EINVAL : 0; +} + /****************************************************************************** * * ioctl_enum_fmt @@ -503,9 +510,9 @@ static int ioctl_input(unsigned int ioclt_nr,void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_enum_fmt(void *arg,struct camera_data *cam) +static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) { - struct v4l2_fmtdesc *f = arg; int index = f->index; if (index < 0 || index > 1) @@ -539,12 +546,10 @@ static int ioctl_enum_fmt(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_try_fmt(void *arg,struct camera_data *cam) +static int cpia2_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) { - struct v4l2_format *f = arg; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + struct camera_data *cam = video_drvdata(file); if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG && f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) @@ -603,12 +608,17 @@ static int ioctl_try_fmt(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_set_fmt(void *arg,struct camera_data *cam, struct cpia2_fh *fh) +static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh, + struct v4l2_format *f) { - struct v4l2_format *f = arg; + struct camera_data *cam = video_drvdata(file); + struct cpia2_fh *fh = _fh; int err, frame; - err = ioctl_try_fmt(arg, cam); + err = v4l2_prio_check(&cam->prio, fh->prio); + if (err) + return err; + err = cpia2_try_fmt_vid_cap(file, _fh, f); if(err != 0) return err; @@ -658,12 +668,10 @@ static int ioctl_set_fmt(void *arg,struct camera_data *cam, struct cpia2_fh *fh) * *****************************************************************************/ -static int ioctl_get_fmt(void *arg,struct camera_data *cam) +static int cpia2_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) { - struct v4l2_format *f = arg; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + struct camera_data *cam = video_drvdata(file); f->fmt.pix.width = cam->width; f->fmt.pix.height = cam->height; @@ -686,9 +694,9 @@ static int ioctl_get_fmt(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_cropcap(void *arg,struct camera_data *cam) +static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c) { - struct v4l2_cropcap *c = arg; + struct camera_data *cam = video_drvdata(file); if (c->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -715,9 +723,9 @@ static int ioctl_cropcap(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_queryctrl(void *arg,struct camera_data *cam) +static int cpia2_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) { - struct v4l2_queryctrl *c = arg; + struct camera_data *cam = video_drvdata(file); int i; for(i=0; iname, 0, sizeof(m->name)); - m->reserved = 0; + struct camera_data *cam = video_drvdata(file); switch(m->id) { case CPIA2_CID_FLICKER_MODE: @@ -837,9 +842,9 @@ static int ioctl_querymenu(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_g_ctrl(void *arg,struct camera_data *cam) +static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) { - struct v4l2_control *c = arg; + struct camera_data *cam = video_drvdata(file); switch(c->id) { case V4L2_CID_BRIGHTNESS: @@ -955,9 +960,9 @@ static int ioctl_g_ctrl(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_s_ctrl(void *arg,struct camera_data *cam) +static int cpia2_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) { - struct v4l2_control *c = arg; + struct camera_data *cam = video_drvdata(file); int i; int retval = 0; @@ -1031,9 +1036,9 @@ static int ioctl_s_ctrl(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_g_jpegcomp(void *arg,struct camera_data *cam) +static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) { - struct v4l2_jpegcompression *parms = arg; + struct camera_data *cam = video_drvdata(file); memset(parms, 0, sizeof(*parms)); @@ -1072,9 +1077,9 @@ static int ioctl_g_jpegcomp(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_s_jpegcomp(void *arg,struct camera_data *cam) +static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) { - struct v4l2_jpegcompression *parms = arg; + struct camera_data *cam = video_drvdata(file); DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n", parms->APP_len, parms->COM_len); @@ -1121,9 +1126,9 @@ static int ioctl_s_jpegcomp(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_reqbufs(void *arg,struct camera_data *cam) +static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) { - struct v4l2_requestbuffers *req = arg; + struct camera_data *cam = video_drvdata(file); if(req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || req->memory != V4L2_MEMORY_MMAP) @@ -1144,9 +1149,9 @@ static int ioctl_reqbufs(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_querybuf(void *arg,struct camera_data *cam) +static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { - struct v4l2_buffer *buf = arg; + struct camera_data *cam = video_drvdata(file); if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || buf->index > cam->num_frames) @@ -1192,9 +1197,9 @@ static int ioctl_querybuf(void *arg,struct camera_data *cam) * *****************************************************************************/ -static int ioctl_qbuf(void *arg,struct camera_data *cam) +static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { - struct v4l2_buffer *buf = arg; + struct camera_data *cam = video_drvdata(file); if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || buf->memory != V4L2_MEMORY_MMAP || @@ -1248,9 +1253,9 @@ static int find_earliest_filled_buffer(struct camera_data *cam) * *****************************************************************************/ -static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file) +static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { - struct v4l2_buffer *buf = arg; + struct camera_data *cam = video_drvdata(file); int frame; if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || @@ -1296,210 +1301,56 @@ static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file) return 0; } -/****************************************************************************** - * - * cpia2_ioctl - * - *****************************************************************************/ -static long cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int cpia2_g_priority(struct file *file, void *_fh, enum v4l2_priority *p) { - struct camera_data *cam = video_drvdata(file); - long retval = 0; - - if (!cam) - return -ENOTTY; - - if (!cam->present) - return -ENODEV; - - /* Priority check */ - switch (cmd) { - case VIDIOC_S_FMT: - { - struct cpia2_fh *fh = file->private_data; - retval = v4l2_prio_check(&cam->prio, fh->prio); - if (retval) - return retval; - break; - } - default: - break; - } - - switch (cmd) { - /* CPIA2 extension to Video4Linux API */ - case CPIA2_IOC_SET_GPIO: - retval = ioctl_set_gpio(arg, cam); - break; - case VIDIOC_QUERYCAP: - retval = ioctl_querycap(arg,cam); - break; - - case VIDIOC_ENUMINPUT: - case VIDIOC_G_INPUT: - case VIDIOC_S_INPUT: - retval = ioctl_input(cmd, arg, cam); - break; - - case VIDIOC_ENUM_FMT: - retval = ioctl_enum_fmt(arg,cam); - break; - case VIDIOC_TRY_FMT: - retval = ioctl_try_fmt(arg,cam); - break; - case VIDIOC_G_FMT: - retval = ioctl_get_fmt(arg,cam); - break; - case VIDIOC_S_FMT: - retval = ioctl_set_fmt(arg,cam,file->private_data); - break; + struct cpia2_fh *fh = _fh; - case VIDIOC_CROPCAP: - retval = ioctl_cropcap(arg,cam); - break; - case VIDIOC_G_CROP: - case VIDIOC_S_CROP: - // TODO: I think cropping can be implemented - SJB - retval = -EINVAL; - break; - - case VIDIOC_QUERYCTRL: - retval = ioctl_queryctrl(arg,cam); - break; - case VIDIOC_QUERYMENU: - retval = ioctl_querymenu(arg,cam); - break; - case VIDIOC_G_CTRL: - retval = ioctl_g_ctrl(arg,cam); - break; - case VIDIOC_S_CTRL: - retval = ioctl_s_ctrl(arg,cam); - break; - - case VIDIOC_G_JPEGCOMP: - retval = ioctl_g_jpegcomp(arg,cam); - break; - case VIDIOC_S_JPEGCOMP: - retval = ioctl_s_jpegcomp(arg,cam); - break; - - case VIDIOC_G_PRIORITY: - { - struct cpia2_fh *fh = file->private_data; - *(enum v4l2_priority*)arg = fh->prio; - break; - } - case VIDIOC_S_PRIORITY: - { - struct cpia2_fh *fh = file->private_data; - enum v4l2_priority prio; - prio = *(enum v4l2_priority*)arg; - if(cam->streaming && - prio != fh->prio && - fh->prio == V4L2_PRIORITY_RECORD) { - /* Can't drop record priority while streaming */ - retval = -EBUSY; - } else if(prio == V4L2_PRIORITY_RECORD && - prio != fh->prio && - v4l2_prio_max(&cam->prio) == V4L2_PRIORITY_RECORD) { - /* Only one program can record at a time */ - retval = -EBUSY; - } else { - retval = v4l2_prio_change(&cam->prio, &fh->prio, prio); - } - break; - } - - case VIDIOC_REQBUFS: - retval = ioctl_reqbufs(arg,cam); - break; - case VIDIOC_QUERYBUF: - retval = ioctl_querybuf(arg,cam); - break; - case VIDIOC_QBUF: - retval = ioctl_qbuf(arg,cam); - break; - case VIDIOC_DQBUF: - retval = ioctl_dqbuf(arg,cam,file); - break; - case VIDIOC_STREAMON: - { - int type; - DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); - type = *(int*)arg; - if(!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - retval = -EINVAL; - - if(!cam->streaming) { - retval = cpia2_usb_stream_start(cam, - cam->params.camera_state.stream_mode); - } else { - retval = -EINVAL; - } - - break; - } - case VIDIOC_STREAMOFF: - { - int type; - DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); - type = *(int*)arg; - if(!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - retval = -EINVAL; - - if(cam->streaming) { - retval = cpia2_usb_stream_stop(cam); - } else { - retval = -EINVAL; - } - - break; - } - - case VIDIOC_ENUMOUTPUT: - case VIDIOC_G_OUTPUT: - case VIDIOC_S_OUTPUT: - case VIDIOC_G_MODULATOR: - case VIDIOC_S_MODULATOR: - - case VIDIOC_ENUMAUDIO: - case VIDIOC_G_AUDIO: - case VIDIOC_S_AUDIO: + *p = fh->prio; + return 0; +} - case VIDIOC_ENUMAUDOUT: - case VIDIOC_G_AUDOUT: - case VIDIOC_S_AUDOUT: +static int cpia2_s_priority(struct file *file, void *_fh, enum v4l2_priority prio) +{ + struct camera_data *cam = video_drvdata(file); + struct cpia2_fh *fh = fh; - case VIDIOC_ENUMSTD: - case VIDIOC_QUERYSTD: - case VIDIOC_G_STD: - case VIDIOC_S_STD: + if (cam->streaming && prio != fh->prio && + fh->prio == V4L2_PRIORITY_RECORD) + /* Can't drop record priority while streaming */ + return -EBUSY; - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: + if (prio == V4L2_PRIORITY_RECORD && prio != fh->prio && + v4l2_prio_max(&cam->prio) == V4L2_PRIORITY_RECORD) + /* Only one program can record at a time */ + return -EBUSY; + return v4l2_prio_change(&cam->prio, &fh->prio, prio); +} - case VIDIOC_OVERLAY: - case VIDIOC_G_FBUF: - case VIDIOC_S_FBUF: +static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct camera_data *cam = video_drvdata(file); - case VIDIOC_G_PARM: - case VIDIOC_S_PARM: - retval = -EINVAL; - break; - default: - retval = -ENOIOCTLCMD; - break; - } + DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); + if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - return retval; + if (!cam->streaming) + return cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + return -EINVAL; } -static long cpia2_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) +static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { - return video_usercopy(file, cmd, arg, cpia2_do_ioctl); + struct camera_data *cam = video_drvdata(file); + + DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); + if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->streaming) + return cpia2_usb_stream_stop(cam); + return -EINVAL; } /****************************************************************************** @@ -1550,6 +1401,33 @@ static void reset_camera_struct_v4l(struct camera_data *cam) v4l2_prio_init(&cam->prio); } +static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { + .vidioc_querycap = cpia2_querycap, + .vidioc_enum_input = cpia2_enum_input, + .vidioc_g_input = cpia2_g_input, + .vidioc_s_input = cpia2_s_input, + .vidioc_enum_fmt_vid_cap = cpia2_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap, + .vidioc_queryctrl = cpia2_queryctrl, + .vidioc_querymenu = cpia2_querymenu, + .vidioc_g_ctrl = cpia2_g_ctrl, + .vidioc_s_ctrl = cpia2_s_ctrl, + .vidioc_g_jpegcomp = cpia2_g_jpegcomp, + .vidioc_s_jpegcomp = cpia2_s_jpegcomp, + .vidioc_cropcap = cpia2_cropcap, + .vidioc_reqbufs = cpia2_reqbufs, + .vidioc_querybuf = cpia2_querybuf, + .vidioc_qbuf = cpia2_qbuf, + .vidioc_dqbuf = cpia2_dqbuf, + .vidioc_streamon = cpia2_streamon, + .vidioc_streamoff = cpia2_streamoff, + .vidioc_g_priority = cpia2_g_priority, + .vidioc_s_priority = cpia2_s_priority, + .vidioc_default = cpia2_default, +}; + /*** * The v4l video device structure initialized for this device ***/ @@ -1559,7 +1437,7 @@ static const struct v4l2_file_operations cpia2_fops = { .release = cpia2_close, .read = cpia2_v4l_read, .poll = cpia2_v4l_poll, - .unlocked_ioctl = cpia2_ioctl, + .unlocked_ioctl = video_ioctl2, .mmap = cpia2_mmap, }; @@ -1567,6 +1445,7 @@ static struct video_device cpia2_template = { /* I could not find any place for the old .initialize initializer?? */ .name = "CPiA2 Camera", .fops = &cpia2_fops, + .ioctl_ops = &cpia2_ioctl_ops, .release = video_device_release, }; -- cgit v0.10.2 From 8e030cabf1bb8601ad3efe09b9dc65a441a294ec Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Thu, 20 Jan 2011 03:05:08 -0300 Subject: [media] tm6000: add/rework reg.defines Rework registers defines. Add TM6000 specific registers defines. Add marks and comments for TM6010 specific registers. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/tm6000/tm6000-regs.h b/drivers/staging/tm6000/tm6000-regs.h index 1f0ced8..5375a83 100644 --- a/drivers/staging/tm6000/tm6000-regs.h +++ b/drivers/staging/tm6000/tm6000-regs.h @@ -97,6 +97,34 @@ enum { TM6000_URB_MSG_ERR, }; +/* Define specific TM6000 Video decoder registers */ +#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8 +#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9 +#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda +#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb +#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc +#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd +#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde +#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf +#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0 +#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1 +#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2 +#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3 +#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4 +#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5 +#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6 +#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7 +#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8 +#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9 +#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea +#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb +#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec +#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed +#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee +#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef +#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd +#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe + /* Define TM6000/TM6010 Video decoder registers */ #define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00 #define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01 @@ -241,6 +269,7 @@ enum { #define TM6010_REQ07_RC9_VEND1 0x07, 0xc9 #define TM6010_REQ07_RCA_VEND0 0x07, 0xca #define TM6010_REQ07_RCB_DELAY 0x07, 0xcb +/* ONLY for TM6010 */ #define TM6010_REQ07_RCC_ACTIVE_VIDEO_IF 0x07, 0xcc #define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0 #define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1 @@ -250,32 +279,59 @@ enum { #define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5 #define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6 #define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7 +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR 0x07, 0xd8 +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_BSIZE 0x07, 0xd9 +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_WAKEUP_SEL 0x07, 0xda +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_WAKEUP_ADD 0x07, 0xdb +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_LEADER1 0x07, 0xdc +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_LEADER0 0x07, 0xdd +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_PULSE_CNT1 0x07, 0xde +/* ONLY for TM6010 */ #define TM6010_REQ07_RD8_IR_PULSE_CNT0 0x07, 0xdf +/* ONLY for TM6010 */ #define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8 +/* ONLY for TM6010 */ #define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9 +/* ONLY for TM6010 */ #define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea +/* ONLY for TM6010 */ #define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6 +/* ONLY for TM6010 */ #define TM6010_REQ07_RF7_BIST 0x07, 0xf7 +/* ONLY for TM6010 */ #define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe #define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff @@ -477,7 +533,8 @@ enum { #define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8 #define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc -/* Define TM6000/TM6010 Audio decoder registers */ +/* Define TM6010 Audio decoder registers */ +/* This core available only in TM6010 */ #define TM6010_REQ08_R00_A_VERSION 0x08, 0x00 #define TM6010_REQ08_R01_A_INIT 0x08, 0x01 #define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02 @@ -518,7 +575,7 @@ enum { #define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27 #define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28 -/* Define TM6000/TM6010 Video ADC registers */ +/* Define TM6010 Video ADC registers */ #define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0 #define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1 #define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2 @@ -534,7 +591,7 @@ enum { #define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec #define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed -/* Define TM6000/TM6010 Audio ADC registers */ +/* Define TM6010 Audio ADC registers */ #define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0 #define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1 #define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2 -- cgit v0.10.2 From 2b97e2201eb76d56c4901d39e8ed276f2de0e2e9 Mon Sep 17 00:00:00 2001 From: iceberg Date: Fri, 21 Jan 2011 15:00:25 -0300 Subject: [media] double mutex lock in drivers/media/radio/si470x/radio-si470x- KERNEL_VERSION: 2.6.37 common.c in function ssize_t si470x_fops_read. 1. First mutex_lock on &radio->lock in line 441 2. Second in line 462 I think that mutex in line 462 is not needed. 433static ssize_t si470x_fops_read(struct file *file, char __user *buf, 434 size_t count, loff_t *ppos) 435{ .... 441 mutex_lock(&radio->lock); 442 if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) 443 si470x_rds_on(radio); 444 445 /* block if no new data available */ 446 while (radio->wr_index == radio->rd_index) { 447 if (file->f_flags & O_NONBLOCK) { 448 retval = -EWOULDBLOCK; 449 goto done; 450 } 451 if (wait_event_interruptible(radio->read_queue, 452 radio->wr_index != radio->rd_index) < 0) { 453 retval = -EINTR; 454 goto done; 455 } 456 } 457 458 /* calculate block count from byte count */ 459 count /= 3; 460 461 /* copy RDS block out of internal buffer and to user buffer */ 462 mutex_lock(&radio->lock); Found by Linux Device Drivers Verification Project Remove second mutex. Signed-off-by: Alexander Strakh Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 60c176f..38ae6cd 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -460,7 +460,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, count /= 3; /* copy RDS block out of internal buffer and to user buffer */ - mutex_lock(&radio->lock); while (block_count < count) { if (radio->rd_index == radio->wr_index) break; -- cgit v0.10.2 From b8bc77db1e23c6e141734511db873902c66bec8c Mon Sep 17 00:00:00 2001 From: Alina Friedrichsen Date: Sun, 23 Jan 2011 12:27:05 -0300 Subject: [media] tuner-xc2028: More firmware loading retries My Hauppauge WinTV HVR-1400 needs sometimes more then only one retry to load the firmware successfully. Signed-off-by: Alina Friedrichsen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index b6ce528..d95f3b2 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -685,7 +685,7 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type, { struct xc2028_data *priv = fe->tuner_priv; struct firmware_properties new_fw; - int rc = 0, is_retry = 0; + int rc = 0, retry_count = 0; u16 version, hwmodel; v4l2_std_id std0; @@ -855,9 +855,9 @@ read_not_reliable: fail: memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); - if (!is_retry) { + if (retry_count < 8) { msleep(50); - is_retry = 1; + retry_count++; tuner_dbg("Retrying firmware load\n"); goto retry; } -- cgit v0.10.2 From 8bb84227d1ba41d6c1ec9350a4424ece5fe00e33 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 29 Jan 2011 22:10:56 -0300 Subject: [media] cx231xx: Simplify interface checking logic at probe Just a cleanup patch. Removes one indent level by moving the return -ENODEV to happen before the device register logic, if the interface is not the audio/video (int 1). Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index 588f3e8..ca2b24b 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -844,110 +844,110 @@ static int cx231xx_usb_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); ifnum = interface->altsetting[0].desc.bInterfaceNumber; - if (ifnum == 1) { - /* - * Interface number 0 - IR interface - */ - /* Check to see next free device and mark as used */ - nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS); - cx231xx_devused |= 1 << nr; - - if (nr >= CX231XX_MAXBOARDS) { - cx231xx_err(DRIVER_NAME - ": Supports only %i cx231xx boards.\n", CX231XX_MAXBOARDS); - cx231xx_devused &= ~(1 << nr); - return -ENOMEM; - } - - /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - cx231xx_err(DRIVER_NAME ": out of memory!\n"); - cx231xx_devused &= ~(1 << nr); - return -ENOMEM; - } - - snprintf(dev->name, 29, "cx231xx #%d", nr); - dev->devno = nr; - dev->model = id->driver_info; - dev->video_mode.alt = -1; - dev->interface_count++; - - /* reset gpio dir and value */ - dev->gpio_dir = 0; - dev->gpio_val = 0; - dev->xc_fw_load_done = 0; - dev->has_alsa_audio = 1; - dev->power_mode = -1; - atomic_set(&dev->devlist_count, 0); - - /* 0 - vbi ; 1 -sliced cc mode */ - dev->vbi_or_sliced_cc_mode = 0; - - /* get maximum no.of IAD interfaces */ - assoc_desc = udev->actconfig->intf_assoc[0]; - dev->max_iad_interface_count = assoc_desc->bInterfaceCount; - - /* init CIR module TBD */ + /* + * Interface number 0 - IR interface (handled by mceusb driver) + * Interface number 1 - AV interface (handled by this driver) + */ + if (ifnum != 1) + return -ENODEV; - /* store the current interface */ - lif = interface; + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS); + cx231xx_devused |= 1 << nr; - /*mode_tv: digital=1 or analog=0*/ - dev->mode_tv = 0; + if (nr >= CX231XX_MAXBOARDS) { + cx231xx_err(DRIVER_NAME + ": Supports only %i cx231xx boards.\n", CX231XX_MAXBOARDS); + cx231xx_devused &= ~(1 << nr); + return -ENOMEM; + } - dev->USE_ISO = transfer_mode; + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + cx231xx_err(DRIVER_NAME ": out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + return -ENOMEM; + } - switch (udev->speed) { - case USB_SPEED_LOW: - speed = "1.5"; - break; - case USB_SPEED_UNKNOWN: - case USB_SPEED_FULL: - speed = "12"; - break; - case USB_SPEED_HIGH: - speed = "480"; - break; - default: - speed = "unknown"; - } + snprintf(dev->name, 29, "cx231xx #%d", nr); + dev->devno = nr; + dev->model = id->driver_info; + dev->video_mode.alt = -1; + + dev->interface_count++; + /* reset gpio dir and value */ + dev->gpio_dir = 0; + dev->gpio_val = 0; + dev->xc_fw_load_done = 0; + dev->has_alsa_audio = 1; + dev->power_mode = -1; + atomic_set(&dev->devlist_count, 0); + + /* 0 - vbi ; 1 -sliced cc mode */ + dev->vbi_or_sliced_cc_mode = 0; + + /* get maximum no.of IAD interfaces */ + assoc_desc = udev->actconfig->intf_assoc[0]; + dev->max_iad_interface_count = assoc_desc->bInterfaceCount; + + /* init CIR module TBD */ + + /* store the current interface */ + lif = interface; + + /*mode_tv: digital=1 or analog=0*/ + dev->mode_tv = 0; + + dev->USE_ISO = transfer_mode; + + switch (udev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } - if (udev->manufacturer) - strlcpy(descr, udev->manufacturer, sizeof(descr)); + 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 (udev->product) { if (*descr) strlcat(descr, " ", sizeof(descr)); - - cx231xx_info("New device %s@ %s Mbps " - "(%04x:%04x) with %d interfaces\n", - descr, - speed, - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - dev->max_iad_interface_count); - - /* store the interface 0 back */ - lif = udev->actconfig->interface[0]; - - /* increment interface count */ - dev->interface_count++; - - /* get device number */ - nr = dev->devno; - - assoc_desc = udev->actconfig->intf_assoc[0]; - if (assoc_desc->bFirstInterface != ifnum) { - cx231xx_err(DRIVER_NAME ": Not found " - "matching IAD interface\n"); - return -ENODEV; - } - } else { + strlcat(descr, udev->product, sizeof(descr)); + } + if (*descr) + strlcat(descr, " ", sizeof(descr)); + + cx231xx_info("New device %s@ %s Mbps " + "(%04x:%04x) with %d interfaces\n", + descr, + speed, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + dev->max_iad_interface_count); + + /* store the interface 0 back */ + lif = udev->actconfig->interface[0]; + + /* increment interface count */ + dev->interface_count++; + + /* get device number */ + nr = dev->devno; + + assoc_desc = udev->actconfig->intf_assoc[0]; + if (assoc_desc->bFirstInterface != ifnum) { + cx231xx_err(DRIVER_NAME ": Not found " + "matching IAD interface\n"); return -ENODEV; } -- cgit v0.10.2 From 3f25ffa2cd946333d29a668993f5aa78e9b56a02 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 30 Jan 2011 09:03:24 -0300 Subject: [media] cx231xx: Use a generic check for TUNER_XC5000 The check for xc5000 assumes that the tuner will always be using the same bus and will have the same address. As those are configurable via dev->board, it should use, instead, the values defined there. Also, a similar type of test will be needed by other tuners (for example, for TUNER_XC2028) Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c index 8356706..925f3a0 100644 --- a/drivers/media/video/cx231xx/cx231xx-i2c.c +++ b/drivers/media/video/cx231xx/cx231xx-i2c.c @@ -54,6 +54,21 @@ do { \ } \ } while (0) +static inline bool is_tuner(struct cx231xx *dev, struct cx231xx_i2c *bus, + const struct i2c_msg *msg, int tuner_type) +{ + if (bus->nr != dev->board.tuner_i2c_master) + return false; + + if (msg->addr != dev->board.tuner_addr) + return false; + + if (dev->tuner_type != tuner_type) + return false; + + return true; +} + /* * cx231xx_i2c_send_bytes() */ @@ -71,9 +86,7 @@ int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap, u16 saddr = 0; u8 need_gpio = 0; - if ((bus->nr == 1) && (msg->addr == 0x61) - && (dev->tuner_type == TUNER_XC5000)) { - + if (is_tuner(dev, bus, msg, TUNER_XC5000)) { size = msg->len; if (size == 2) { /* register write sub addr */ @@ -180,9 +193,7 @@ static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap, u16 saddr = 0; u8 need_gpio = 0; - if ((bus->nr == 1) && (msg->addr == 0x61) - && dev->tuner_type == TUNER_XC5000) { - + if (is_tuner(dev, bus, msg, TUNER_XC5000)) { if (msg->len == 2) saddr = msg->buf[0] << 8 | msg->buf[1]; else if (msg->len == 1) @@ -274,9 +285,7 @@ static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap, else if (msg1->len == 1) saddr = msg1->buf[0]; - if ((bus->nr == 1) && (msg2->addr == 0x61) - && dev->tuner_type == TUNER_XC5000) { - + if (is_tuner(dev, bus, msg2, TUNER_XC5000)) { if ((msg2->len < 16)) { dprintk1(1, @@ -454,8 +463,8 @@ static char *i2c_devs[128] = { [0x32 >> 1] = "GeminiIII", [0x02 >> 1] = "Aquarius", [0xa0 >> 1] = "eeprom", - [0xc0 >> 1] = "tuner/XC3028", - [0xc2 >> 1] = "tuner/XC5000", + [0xc0 >> 1] = "tuner", + [0xc2 >> 1] = "tuner", }; /* -- cgit v0.10.2 From 2f86138706d3b5c85a69e72ca2959717372386dd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 31 Jan 2011 22:12:15 -0300 Subject: [media] cx231xx: Use parameters to describe some board variants Instead of per-model tests all over the code, use some parameters at the board entries to describe the model variants for: - devices with 417 MPEG encoder; - devices that use external AV; - devices where vbi VANC endpoint doesn't work; - devices with xc5000 that require different IF initialization (and probably will cover also xc3028). - devices with xceive tuner that require a reset during init. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index c53e972..b80bccf 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -759,11 +759,8 @@ int cx231xx_set_decoder_video_input(struct cx231xx *dev, case CX231XX_VMUX_TELEVISION: case CX231XX_VMUX_CABLE: default: - switch (dev->model) { - case CX231XX_BOARD_CNXT_CARRAERA: - case CX231XX_BOARD_CNXT_RDE_250: - case CX231XX_BOARD_CNXT_SHELBY: - case CX231XX_BOARD_CNXT_RDU_250: + /* TODO: Test if this is also needed for xc2028/xc3028 */ + if (dev->board.tuner_type == TUNER_XC5000) { /* Disable the use of DIF */ status = vid_blk_read_word(dev, AFE_CTRL, &value); @@ -820,8 +817,7 @@ int cx231xx_set_decoder_video_input(struct cx231xx *dev, MODE_CTRL, FLD_INPUT_MODE, cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_CVBS_0)); - break; - default: + } else { /* Enable the DIF for the tuner */ /* Reinitialize the DIF */ @@ -2550,7 +2546,7 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) case 4: /* ts1 */ cx231xx_info("%s: set ts1 registers", __func__); - if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) { + if (dev->board.has_417) { cx231xx_info(" MPEG\n"); value &= 0xFFFFFFFC; value |= 0x3; diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index ca2b24b..e04c955 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -261,6 +261,9 @@ struct cx231xx_board cx231xx_boards[] = { .agc_analog_digital_select_gpio = 0x1c, .gpio_pin_status_mask = 0x4001000, .norm = V4L2_STD_PAL, + .no_alt_vanc = 1, + .external_av = 1, + .has_417 = 1, .input = {{ .type = CX231XX_VMUX_COMPOSITE1, @@ -382,6 +385,8 @@ struct cx231xx_board cx231xx_boards[] = { .agc_analog_digital_select_gpio = 0x0c, .gpio_pin_status_mask = 0x4001000, .norm = V4L2_STD_NTSC, + .no_alt_vanc = 1, + .external_av = 1, .input = {{ .type = CX231XX_VMUX_COMPOSITE1, .vmux = CX231XX_VIN_2_1, @@ -772,7 +777,7 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, /* Reset other chips required if they are tied up with GPIO pins */ cx231xx_add_into_devlist(dev); - if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) { + if (dev->board.has_417) { printk(KERN_INFO "attach 417 %d\n", dev->model); if (cx231xx_417_register(dev) < 0) { printk(KERN_ERR diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c index 7d62d58..abe500f 100644 --- a/drivers/media/video/cx231xx/cx231xx-core.c +++ b/drivers/media/video/cx231xx/cx231xx-core.c @@ -571,6 +571,8 @@ int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt) alt]; break; case INDEX_VANC: + if (dev->board.no_alt_vanc) + return 0; usb_interface_index = dev->current_pcb_config.hs_config_info[0].interface_info. vanc_index + 1; @@ -600,8 +602,7 @@ int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt) usb_interface_index, alt); /*To workaround error number=-71 on EP0 for videograbber, need add following codes.*/ - if (dev->model != CX231XX_BOARD_CNXT_VIDEO_GRABBER && - dev->model != CX231XX_BOARD_HAUPPAUGE_USBLIVE2) + if (dev->board.no_alt_vanc) return -1; } @@ -1301,8 +1302,7 @@ int cx231xx_dev_init(struct cx231xx *dev) /* init hardware */ /* Note : with out calling set power mode function, afe can not be set up correctly */ - if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER || - dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) { + if (dev->board.external_av) { errCode = cx231xx_set_power_mode(dev, POLARIS_AVMODE_ENXTERNAL_AV); if (errCode < 0) { @@ -1322,11 +1322,9 @@ int cx231xx_dev_init(struct cx231xx *dev) } } - /* reset the Tuner */ - if ((dev->model == CX231XX_BOARD_CNXT_CARRAERA) || - (dev->model == CX231XX_BOARD_CNXT_RDE_250) || - (dev->model == CX231XX_BOARD_CNXT_SHELBY) || - (dev->model == CX231XX_BOARD_CNXT_RDU_250)) + /* reset the Tuner, if it is a Xceive tuner */ + if ((dev->board.tuner_type == TUNER_XC5000) || + (dev->board.tuner_type == TUNER_XC2028)) cx231xx_gpio_set(dev, dev->board.tuner_gpio); /* initialize Colibri block */ diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 7e3e8c4..ffd5af9 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -2190,8 +2190,7 @@ static int cx231xx_v4l2_open(struct file *filp) dev->height = norm_maxh(dev); /* Power up in Analog TV mode */ - if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER || - dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) + if (dev->board.external_av) cx231xx_set_power_mode(dev, POLARIS_AVMODE_ENXTERNAL_AV); else @@ -2231,9 +2230,7 @@ static int cx231xx_v4l2_open(struct file *filp) if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { /* Set the required alternate setting VBI interface works in Bulk mode only */ - if (dev->model != CX231XX_BOARD_CNXT_VIDEO_GRABBER && - dev->model != CX231XX_BOARD_HAUPPAUGE_USBLIVE2) - cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops, NULL, &dev->vbi_mode.slock, @@ -2275,7 +2272,7 @@ void cx231xx_release_analog_resources(struct cx231xx *dev) cx231xx_info("V4L2 device %s deregistered\n", video_device_node_name(dev->vdev)); - if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) + if (dev->board.has_417) cx231xx_417_unregister(dev); if (video_is_registered(dev->vdev)) @@ -2302,10 +2299,13 @@ static int cx231xx_v4l2_close(struct file *filp) if (res_check(fh)) res_free(fh); - /*To workaround error number=-71 on EP0 for VideoGrabber, - need exclude following.*/ - if (dev->model != CX231XX_BOARD_CNXT_VIDEO_GRABBER && - dev->model != CX231XX_BOARD_HAUPPAUGE_USBLIVE2) + /* + * To workaround error number=-71 on EP0 for VideoGrabber, + * need exclude following. + * FIXME: It is probably safe to remove most of these, as we're + * now avoiding the alternate setting for INDEX_VANC + */ + if (!dev->board.no_alt_vanc) if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { videobuf_stop(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vidq); diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index 72bbea2..b72503d 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -353,7 +353,10 @@ struct cx231xx_board { unsigned int max_range_640_480:1; unsigned int has_dvb:1; + unsigned int has_417:1; unsigned int valid:1; + unsigned int no_alt_vanc:1; + unsigned int external_av:1; unsigned char xclk, i2c_speed; -- cgit v0.10.2 From 38f5ddc1bf12887c0abe287de359e853e12fcc19 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 31 Jan 2011 16:25:39 -0300 Subject: [media] cx231xx: Allow some boards to not use I2C port 3 Some devices don't need to use it. So allow to just disable this logic. Having it enabled on some devices cause power management to complain, generating error -71. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index b80bccf..62843d3 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -1271,6 +1271,8 @@ int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3) int status = 0; bool current_is_port_3; + if (dev->board.dont_use_port_3) + is_port_3 = false; status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, 4); if (status < 0) diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index b72503d..e1c222b 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -357,6 +357,7 @@ struct cx231xx_board { unsigned int valid:1; unsigned int no_alt_vanc:1; unsigned int external_av:1; + unsigned int dont_use_port_3:1; unsigned char xclk, i2c_speed; -- cgit v0.10.2 From 4e105039da20f57958a2f538048a0f1bfb576b8b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 31 Jan 2011 22:18:21 -0300 Subject: [media] cx231xx: Add support for PV Xcapture USB Adds support for Pixelviex Xcapture USB grabber device. This device has one composite and one s-video entry only, plus a button. For now, the button is not supported. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index e04c955..6540b8d 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -440,6 +440,35 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = 0, } }, }, + [CX231XX_BOARD_PV_XCAPTURE_USB] = { + .name = "Pixelview Xcapture USB", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_NTSC, + .no_alt_vanc = 1, + .external_av = 1, + .dont_use_port_3 = 1, + + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } + }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -469,6 +498,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_HAUPPAUGE_USBLIVE2}, {USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x4000, 0x4001), .driver_info = CX231XX_BOARD_PV_PLAYTV_USB_HYBRID}, + {USB_DEVICE(USB_VID_PIXELVIEW, 0x5014), + .driver_info = CX231XX_BOARD_PV_XCAPTURE_USB}, {}, }; diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index e1c222b..2d5ab0c 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -64,6 +64,7 @@ #define CX231XX_BOARD_HAUPPAUGE_EXETER 8 #define CX231XX_BOARD_HAUPPAUGE_USBLIVE2 9 #define CX231XX_BOARD_PV_PLAYTV_USB_HYBRID 10 +#define CX231XX_BOARD_PV_XCAPTURE_USB 11 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 -- cgit v0.10.2 From b618726414f3eb7b0b122e459efe3c5edc36b2fc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 13 Feb 2011 21:52:02 -0300 Subject: [media] cx88: use unlocked_ioctl for cx88-video. cx88-video has locks. don't use the locked ioctl version, as it is not needed. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 508dabb..e2fc455 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1672,7 +1672,7 @@ static const struct v4l2_file_operations video_fops = .read = video_read, .poll = video_poll, .mmap = video_mmap, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { @@ -1722,7 +1722,7 @@ static const struct v4l2_file_operations radio_fops = .owner = THIS_MODULE, .open = video_open, .release = video_release, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { -- cgit v0.10.2 From 121ec13267fabcf2c81e4584f1505069e234b30d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Feb 2011 07:01:51 -0300 Subject: [media] cx88: Don't allow opening a device while it is not ready After registering the cdev, it would be possible do have an open on it. In a matter of fact, some versions of udev do this. So, move registration to the end and protect it with a mutex. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index e2fc455..f814886 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1882,6 +1882,15 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, request_module("ir-kbd-i2c"); } + /* Sets device info at pci_dev */ + pci_set_drvdata(pci_dev, dev); + + /* initial device configuration */ + mutex_lock(&core->lock); + cx88_set_tvnorm(core, core->tvnorm); + init_controls(core); + cx88_video_mux(core, 0); + /* register v4l devices */ dev->video_dev = cx88_vdev_init(core,dev->pci, &cx8800_video_template,"video"); @@ -1923,16 +1932,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, core->name, video_device_node_name(dev->radio_dev)); } - /* everything worked */ - pci_set_drvdata(pci_dev,dev); - - /* initial device configuration */ - mutex_lock(&core->lock); - cx88_set_tvnorm(core,core->tvnorm); - init_controls(core); - cx88_video_mux(core,0); - mutex_unlock(&core->lock); - /* start tvaudio thread */ if (core->board.tuner_type != TUNER_ABSENT) { core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio"); @@ -1942,11 +1941,14 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, core->name, err); } } + mutex_unlock(&core->lock); + return 0; fail_unreg: cx8800_unregister_video(dev); free_irq(pci_dev->irq, dev); + mutex_unlock(&core->lock); fail_core: cx88_core_put(core,dev->pci); fail_free: -- cgit v0.10.2 From 5620094a2eaa6527d7f038ad3d65ce6a76c8272a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 3 Feb 2011 22:05:23 -0300 Subject: [media] tuner-core: Remove V4L1/V4L2 API switch V4L1 was removed. So, the code there is just dead code. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 1cec122..6041c7d 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -80,7 +80,6 @@ struct tuner { struct i2c_client *i2c; struct v4l2_subdev sd; struct list_head list; - unsigned int using_v4l2:1; /* keep track of the current settings */ v4l2_std_id std; @@ -717,19 +716,6 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, return 0; } -#define switch_v4l2() if (!t->using_v4l2) \ - tuner_dbg("switching to v4l2\n"); \ - t->using_v4l2 = 1; - -static inline int check_v4l2(struct tuner *t) -{ - /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for - TV, v4l1 for radio), until that is fixed this code is disabled. - Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2) - first. */ - return 0; -} - static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type) { struct tuner *t = to_tuner(sd); @@ -803,8 +789,6 @@ static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "s_std") == -EINVAL) return 0; - switch_v4l2(); - t->std = std; tuner_fixup_std(t); if (t->tv_freq) @@ -819,7 +803,6 @@ static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) if (set_mode(client, t, f->type, "s_frequency") == -EINVAL) return 0; - switch_v4l2(); set_freq(client, f->frequency); return 0; @@ -832,7 +815,6 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) if (check_mode(t, "g_frequency") == -EINVAL) return 0; - switch_v4l2(); f->type = t->mode; if (fe_tuner_ops->get_frequency) { u32 abs_freq; @@ -856,7 +838,6 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) if (check_mode(t, "g_tuner") == -EINVAL) return 0; - switch_v4l2(); vt->type = t->mode; if (analog_ops->get_afc) @@ -906,8 +887,6 @@ static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) if (check_mode(t, "s_tuner") == -EINVAL) return 0; - switch_v4l2(); - /* do nothing unless we're a radio tuner */ if (t->mode != V4L2_TUNER_RADIO) return 0; -- cgit v0.10.2 From 04f590e82bdd5fa6fc36d7abf806006d27ea9e5d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 3 Feb 2011 22:18:14 -0300 Subject: [media] tuner-core: remove the legacy is_stereo() call Nobody is using this legacy call. Just remove it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index f9f19be..3b86050 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -239,7 +239,6 @@ struct analog_demod_ops { void (*set_params)(struct dvb_frontend *fe, struct analog_parameters *params); int (*has_signal)(struct dvb_frontend *fe); - int (*is_stereo)(struct dvb_frontend *fe); int (*get_afc)(struct dvb_frontend *fe); void (*tuner_status)(struct dvb_frontend *fe); void (*standby)(struct dvb_frontend *fe); diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 6041c7d..912d8e8 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -682,9 +682,6 @@ static void tuner_status(struct dvb_frontend *fe) if (analog_ops->has_signal) tuner_info("Signal strength: %d\n", analog_ops->has_signal(fe)); - if (analog_ops->is_stereo) - tuner_info("Stereo: %s\n", - analog_ops->is_stereo(fe) ? "yes" : "no"); } /* ---------------------------------------------------------------------- */ @@ -861,13 +858,6 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) (tuner_status & TUNER_STATUS_STEREO) ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; - } else { - if (analog_ops->is_stereo) { - vt->rxsubchans = - analog_ops->is_stereo(&t->fe) ? - V4L2_TUNER_SUB_STEREO : - V4L2_TUNER_SUB_MONO; - } } if (analog_ops->has_signal) vt->signal = analog_ops->has_signal(&t->fe); -- cgit v0.10.2 From b77bdb028807bb1f78b5b50127456e1f664bd501 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 3 Feb 2011 23:03:19 -0300 Subject: [media] tuner-core: move some messages to the proper place Move the frequency set debug printk's to the code that actually are changing it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 912d8e8..f497f52 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -246,6 +246,9 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) freq = tv_range[1] * 16; } params.frequency = freq; + tuner_dbg("tv freq set to %lu.%02lu\n", + freq / 16, freq % 16 * 100 / 16); + t->tv_freq = freq; analog_ops->set_params(&t->fe, ¶ms); } @@ -281,6 +284,9 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) freq = radio_range[1] * 16000; } params.frequency = freq; + tuner_dbg("radio freq set to %lu.%02lu\n", + freq / 16000, freq % 16000 * 100 / 16000); + t->radio_freq = freq; analog_ops->set_params(&t->fe, ¶ms); } @@ -291,17 +297,11 @@ static void set_freq(struct i2c_client *c, unsigned long freq) switch (t->mode) { case V4L2_TUNER_RADIO: - tuner_dbg("radio freq set to %lu.%02lu\n", - freq / 16000, freq % 16000 * 100 / 16000); set_radio_freq(c, freq); - t->radio_freq = freq; break; case V4L2_TUNER_ANALOG_TV: case V4L2_TUNER_DIGITAL_TV: - tuner_dbg("tv freq set to %lu.%02lu\n", - freq / 16, freq % 16 * 100 / 16); set_tv_freq(c, freq); - t->tv_freq = freq; break; default: tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode); -- cgit v0.10.2 From 9f3f71ef6d9a6179a16f82287f062cbf3f112ec6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 3 Feb 2011 23:32:07 -0300 Subject: [media] tuner-core: Reorganize the functions internally This is a big patch with no functional changes. It just rearranges everything inside the driver, and prepares to break TV and Radio into two separate fops groups. Currently, it has an heuristics logic to determine if the call came from radio or video. However, the caller driver knows for sure, so tuner-core shouldn't try to guess it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index f497f52..5c3da21 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -34,6 +34,99 @@ #define PREFIX t->i2c->driver->driver.name +/* + * Driver modprobe parameters + */ + +/* insmod options used at init time => read/only */ +static unsigned int addr; +static unsigned int no_autodetect; +static unsigned int show_i2c; + +module_param(addr, int, 0444); +module_param(no_autodetect, int, 0444); +module_param(show_i2c, int, 0444); + +/* insmod options used at runtime => read/write */ +static int tuner_debug; +static unsigned int tv_range[2] = { 44, 958 }; +static unsigned int radio_range[2] = { 65, 108 }; +static char pal[] = "--"; +static char secam[] = "--"; +static char ntsc[] = "-"; + +module_param_named(debug,tuner_debug, int, 0644); +module_param_array(tv_range, int, NULL, 0644); +module_param_array(radio_range, int, NULL, 0644); +module_param_string(pal, pal, sizeof(pal), 0644); +module_param_string(secam, secam, sizeof(secam), 0644); +module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); + +/* + * Static vars + */ + +static struct xc5000_config xc5000_cfg; +static LIST_HEAD(tuner_list); + +/* + * Debug macros + */ + +#define tuner_warn(fmt, arg...) do { \ + printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \ + i2c_adapter_id(t->i2c->adapter), \ + t->i2c->addr, ##arg); \ + } while (0) + +#define tuner_info(fmt, arg...) do { \ + printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \ + i2c_adapter_id(t->i2c->adapter), \ + t->i2c->addr, ##arg); \ + } while (0) + +#define tuner_err(fmt, arg...) do { \ + printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \ + i2c_adapter_id(t->i2c->adapter), \ + t->i2c->addr, ##arg); \ + } while (0) + +#define tuner_dbg(fmt, arg...) do { \ + if (tuner_debug) \ + printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \ + i2c_adapter_id(t->i2c->adapter), \ + t->i2c->addr, ##arg); \ + } while (0) + +/* + * Internal struct used inside the driver + */ + +struct tuner { + /* device */ + struct dvb_frontend fe; + struct i2c_client *i2c; + struct v4l2_subdev sd; + struct list_head list; + + /* keep track of the current settings */ + v4l2_std_id std; + unsigned int tv_freq; + unsigned int radio_freq; + unsigned int audmode; + + unsigned int mode; + unsigned int mode_mask; /* Combination of allowable modes */ + + unsigned int type; /* chip type id */ + unsigned int config; + const char *name; +}; + +/* + * tuner attach/detach logic + */ + /** This macro allows us to probe dynamically, avoiding static links */ #ifdef CONFIG_MEDIA_ATTACH #define tuner_symbol_probe(FUNCTION, ARGS...) ({ \ @@ -74,91 +167,15 @@ static void tuner_detach(struct dvb_frontend *fe) } #endif -struct tuner { - /* device */ - struct dvb_frontend fe; - struct i2c_client *i2c; - struct v4l2_subdev sd; - struct list_head list; - - /* keep track of the current settings */ - v4l2_std_id std; - unsigned int tv_freq; - unsigned int radio_freq; - unsigned int audmode; - - unsigned int mode; - unsigned int mode_mask; /* Combination of allowable modes */ - - unsigned int type; /* chip type id */ - unsigned int config; - const char *name; -}; static inline struct tuner *to_tuner(struct v4l2_subdev *sd) { return container_of(sd, struct tuner, sd); } - -/* insmod options used at init time => read/only */ -static unsigned int addr; -static unsigned int no_autodetect; -static unsigned int show_i2c; - -/* insmod options used at runtime => read/write */ -static int tuner_debug; - -#define tuner_warn(fmt, arg...) do { \ - printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(t->i2c->adapter), \ - t->i2c->addr, ##arg); \ - } while (0) - -#define tuner_info(fmt, arg...) do { \ - printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(t->i2c->adapter), \ - t->i2c->addr, ##arg); \ - } while (0) - -#define tuner_err(fmt, arg...) do { \ - printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(t->i2c->adapter), \ - t->i2c->addr, ##arg); \ - } while (0) - -#define tuner_dbg(fmt, arg...) do { \ - if (tuner_debug) \ - printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(t->i2c->adapter), \ - t->i2c->addr, ##arg); \ - } while (0) - -/* ------------------------------------------------------------------------ */ - -static unsigned int tv_range[2] = { 44, 958 }; -static unsigned int radio_range[2] = { 65, 108 }; - -static char pal[] = "--"; -static char secam[] = "--"; -static char ntsc[] = "-"; - - -module_param(addr, int, 0444); -module_param(no_autodetect, int, 0444); -module_param(show_i2c, int, 0444); -module_param_named(debug,tuner_debug, int, 0644); -module_param_string(pal, pal, sizeof(pal), 0644); -module_param_string(secam, secam, sizeof(secam), 0644); -module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); -module_param_array(tv_range, int, NULL, 0644); -module_param_array(radio_range, int, NULL, 0644); - -MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners"); -MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); -MODULE_LICENSE("GPL"); - -/* ---------------------------------------------------------------------- */ +/* + * struct analog_demod_ops callbacks + */ static void fe_set_params(struct dvb_frontend *fe, struct analog_parameters *params) @@ -214,101 +231,13 @@ static struct analog_demod_ops tuner_analog_ops = { .tuner_status = tuner_status }; -/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */ -static void set_tv_freq(struct i2c_client *c, unsigned int freq) -{ - struct tuner *t = to_tuner(i2c_get_clientdata(c)); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - struct analog_parameters params = { - .mode = t->mode, - .audmode = t->audmode, - .std = t->std - }; - - if (t->type == UNSET) { - tuner_warn ("tuner type not set\n"); - return; - } - if (NULL == analog_ops->set_params) { - tuner_warn ("Tuner has no way to set tv freq\n"); - return; - } - if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) { - tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n", - freq / 16, freq % 16 * 100 / 16, tv_range[0], - tv_range[1]); - /* V4L2 spec: if the freq is not possible then the closest - possible value should be selected */ - if (freq < tv_range[0] * 16) - freq = tv_range[0] * 16; - else - freq = tv_range[1] * 16; - } - params.frequency = freq; - tuner_dbg("tv freq set to %lu.%02lu\n", - freq / 16, freq % 16 * 100 / 16); - t->tv_freq = freq; - - analog_ops->set_params(&t->fe, ¶ms); -} - -static void set_radio_freq(struct i2c_client *c, unsigned int freq) -{ - struct tuner *t = to_tuner(i2c_get_clientdata(c)); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - struct analog_parameters params = { - .mode = t->mode, - .audmode = t->audmode, - .std = t->std - }; - - if (t->type == UNSET) { - tuner_warn ("tuner type not set\n"); - return; - } - if (NULL == analog_ops->set_params) { - tuner_warn ("tuner has no way to set radio frequency\n"); - return; - } - if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) { - tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n", - freq / 16000, freq % 16000 * 100 / 16000, - radio_range[0], radio_range[1]); - /* V4L2 spec: if the freq is not possible then the closest - possible value should be selected */ - if (freq < radio_range[0] * 16000) - freq = radio_range[0] * 16000; - else - freq = radio_range[1] * 16000; - } - params.frequency = freq; - tuner_dbg("radio freq set to %lu.%02lu\n", - freq / 16000, freq % 16000 * 100 / 16000); - t->radio_freq = freq; - - analog_ops->set_params(&t->fe, ¶ms); -} - -static void set_freq(struct i2c_client *c, unsigned long freq) -{ - struct tuner *t = to_tuner(i2c_get_clientdata(c)); - - switch (t->mode) { - case V4L2_TUNER_RADIO: - set_radio_freq(c, freq); - break; - case V4L2_TUNER_ANALOG_TV: - case V4L2_TUNER_DIGITAL_TV: - set_tv_freq(c, freq); - break; - default: - tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode); - } -} +/* + * Functions that are common to both TV and radio + */ -static struct xc5000_config xc5000_cfg; +static void set_tv_freq(struct i2c_client *c, unsigned int freq); +static void set_radio_freq(struct i2c_client *c, unsigned int freq); +static const struct v4l2_subdev_ops tuner_ops; static void set_type(struct i2c_client *c, unsigned int type, unsigned int new_mode_mask, unsigned int new_config, @@ -429,103 +358,329 @@ static void set_type(struct i2c_client *c, unsigned int type, .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, }; - if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr, - t->i2c->adapter, &cfg)) - goto attach_failed; - tune_now = 0; - break; + if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr, + t->i2c->adapter, &cfg)) + goto attach_failed; + tune_now = 0; + break; + } + default: + if (!dvb_attach(simple_tuner_attach, &t->fe, + t->i2c->adapter, t->i2c->addr, t->type)) + goto attach_failed; + + break; + } + + if ((NULL == analog_ops->set_params) && + (fe_tuner_ops->set_analog_params)) { + + t->name = fe_tuner_ops->info.name; + + t->fe.analog_demod_priv = t; + memcpy(analog_ops, &tuner_analog_ops, + sizeof(struct analog_demod_ops)); + + } else { + t->name = analog_ops->info.name; + } + + tuner_dbg("type set to %s\n", t->name); + + if (t->mode_mask == T_UNINITIALIZED) + t->mode_mask = new_mode_mask; + + /* Some tuners require more initialization setup before use, + such as firmware download or device calibration. + trying to set a frequency here will just fail + FIXME: better to move set_freq to the tuner code. This is needed + on analog tuners for PLL to properly work + */ + if (tune_now) { + if (V4L2_TUNER_RADIO == t->mode) + set_radio_freq(c, t->radio_freq); + else + set_tv_freq(c, t->tv_freq); + } + + tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n", + c->adapter->name, c->driver->driver.name, c->addr << 1, type, + t->mode_mask); + return; + +attach_failed: + tuner_dbg("Tuner attach for type = %d failed.\n", t->type); + t->type = TUNER_ABSENT; + t->mode_mask = T_UNINITIALIZED; + + return; +} + +/* + * This function apply tuner config to tuner specified + * by tun_setup structure. I addr is unset, then admin status + * and tun addr status is more precise then current status, + * it's applied. Otherwise status and type are applied only to + * tuner with exactly the same addr. +*/ + +static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) +{ + struct tuner *t = to_tuner(i2c_get_clientdata(c)); + + if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && + (t->mode_mask & tun_setup->mode_mask))) || + (tun_setup->addr == c->addr)) { + set_type(c, tun_setup->type, tun_setup->mode_mask, + tun_setup->config, tun_setup->tuner_callback); + } else + tuner_dbg("set addr discarded for type %i, mask %x. " + "Asked to change tuner at addr 0x%02x, with mask %x\n", + t->type, t->mode_mask, + tun_setup->addr, tun_setup->mode_mask); +} + +static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", + type->type, + type->addr, + type->mode_mask, + type->config); + + set_addr(client, type); + return 0; +} + +static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + + if (t->type != cfg->tuner) + return 0; + + if (analog_ops->set_config) { + analog_ops->set_config(&t->fe, cfg->priv); + return 0; + } + + tuner_dbg("Tuner frontend module has no way to set config\n"); + return 0; +} + +/* Search for existing radio and/or TV tuners on the given I2C adapter. + Note that when this function is called from tuner_probe you can be + certain no other devices will be added/deleted at the same time, I2C + core protects against that. */ +static void tuner_lookup(struct i2c_adapter *adap, + struct tuner **radio, struct tuner **tv) +{ + struct tuner *pos; + + *radio = NULL; + *tv = NULL; + + list_for_each_entry(pos, &tuner_list, list) { + int mode_mask; + + if (pos->i2c->adapter != adap || + strcmp(pos->i2c->driver->driver.name, "tuner")) + continue; + + mode_mask = pos->mode_mask & ~T_STANDBY; + if (*radio == NULL && mode_mask == T_RADIO) + *radio = pos; + /* Note: currently TDA9887 is the only demod-only + device. If other devices appear then we need to + make this test more general. */ + else if (*tv == NULL && pos->type != TUNER_TDA9887 && + (pos->mode_mask & (T_ANALOG_TV | T_DIGITAL_TV))) + *tv = pos; + } +} + +/* During client attach, set_type is called by adapter's attach_inform callback. + set_type must then be completed by tuner_probe. + */ +static int tuner_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tuner *t; + struct tuner *radio; + struct tuner *tv; + + t = kzalloc(sizeof(struct tuner), GFP_KERNEL); + if (NULL == t) + return -ENOMEM; + v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops); + t->i2c = client; + t->name = "(tuner unset)"; + t->type = UNSET; + t->audmode = V4L2_TUNER_MODE_STEREO; + t->mode_mask = T_UNINITIALIZED; + + if (show_i2c) { + unsigned char buffer[16]; + int i, rc; + + memset(buffer, 0, sizeof(buffer)); + rc = i2c_master_recv(client, buffer, sizeof(buffer)); + tuner_info("I2C RECV = "); + for (i = 0; i < rc; i++) + printk(KERN_CONT "%02x ", buffer[i]); + printk("\n"); + } + + /* autodetection code based on the i2c addr */ + if (!no_autodetect) { + switch (client->addr) { + case 0x10: + if (tuner_symbol_probe(tea5761_autodetection, + t->i2c->adapter, + t->i2c->addr) >= 0) { + t->type = TUNER_TEA5761; + t->mode_mask = T_RADIO; + t->mode = T_STANDBY; + /* Sets freq to FM range */ + t->radio_freq = 87.5 * 16000; + tuner_lookup(t->i2c->adapter, &radio, &tv); + if (tv) + tv->mode_mask &= ~T_RADIO; + + goto register_client; + } + kfree(t); + return -ENODEV; + case 0x42: + case 0x43: + case 0x4a: + case 0x4b: + /* If chip is not tda8290, don't register. + since it can be tda9887*/ + if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter, + t->i2c->addr) >= 0) { + tuner_dbg("tda829x detected\n"); + } else { + /* Default is being tda9887 */ + t->type = TUNER_TDA9887; + t->mode_mask = T_RADIO | T_ANALOG_TV | + T_DIGITAL_TV; + t->mode = T_STANDBY; + goto register_client; + } + break; + case 0x60: + if (tuner_symbol_probe(tea5767_autodetection, + t->i2c->adapter, t->i2c->addr) + >= 0) { + t->type = TUNER_TEA5767; + t->mode_mask = T_RADIO; + t->mode = T_STANDBY; + /* Sets freq to FM range */ + t->radio_freq = 87.5 * 16000; + tuner_lookup(t->i2c->adapter, &radio, &tv); + if (tv) + tv->mode_mask &= ~T_RADIO; + + goto register_client; + } + break; + } } - default: - if (!dvb_attach(simple_tuner_attach, &t->fe, - t->i2c->adapter, t->i2c->addr, t->type)) - goto attach_failed; - break; + /* Initializes only the first TV tuner on this adapter. Why only the + first? Because there are some devices (notably the ones with TI + tuners) that have more than one i2c address for the *same* device. + Experience shows that, except for just one case, the first + address is the right one. The exception is a Russian tuner + (ACORP_Y878F). So, the desired behavior is just to enable the + first found TV tuner. */ + tuner_lookup(t->i2c->adapter, &radio, &tv); + if (tv == NULL) { + t->mode_mask = T_ANALOG_TV | T_DIGITAL_TV; + if (radio == NULL) + t->mode_mask |= T_RADIO; + tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask); + t->tv_freq = 400 * 16; /* Sets freq to VHF High */ + t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */ } - if ((NULL == analog_ops->set_params) && - (fe_tuner_ops->set_analog_params)) { - - t->name = fe_tuner_ops->info.name; - - t->fe.analog_demod_priv = t; - memcpy(analog_ops, &tuner_analog_ops, - sizeof(struct analog_demod_ops)); + /* Should be just before return */ +register_client: + tuner_info("chip found @ 0x%x (%s)\n", client->addr << 1, + client->adapter->name); + /* Sets a default mode */ + if (t->mode_mask & T_ANALOG_TV) { + t->mode = V4L2_TUNER_ANALOG_TV; + } else if (t->mode_mask & T_RADIO) { + t->mode = V4L2_TUNER_RADIO; } else { - t->name = analog_ops->info.name; + t->mode = V4L2_TUNER_DIGITAL_TV; } + set_type(client, t->type, t->mode_mask, t->config, t->fe.callback); + list_add_tail(&t->list, &tuner_list); + return 0; +} - tuner_dbg("type set to %s\n", t->name); - - if (t->mode_mask == T_UNINITIALIZED) - t->mode_mask = new_mode_mask; - - /* Some tuners require more initialization setup before use, - such as firmware download or device calibration. - trying to set a frequency here will just fail - FIXME: better to move set_freq to the tuner code. This is needed - on analog tuners for PLL to properly work - */ - if (tune_now) - set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? - t->radio_freq : t->tv_freq); - - tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n", - c->adapter->name, c->driver->driver.name, c->addr << 1, type, - t->mode_mask); - return; +static int tuner_remove(struct i2c_client *client) +{ + struct tuner *t = to_tuner(i2c_get_clientdata(client)); -attach_failed: - tuner_dbg("Tuner attach for type = %d failed.\n", t->type); - t->type = TUNER_ABSENT; - t->mode_mask = T_UNINITIALIZED; + v4l2_device_unregister_subdev(&t->sd); + tuner_detach(&t->fe); + t->fe.analog_demod_priv = NULL; - return; + list_del(&t->list); + kfree(t); + return 0; } /* - * This function apply tuner config to tuner specified - * by tun_setup structure. I addr is unset, then admin status - * and tun addr status is more precise then current status, - * it's applied. Otherwise status and type are applied only to - * tuner with exactly the same addr. -*/ + * Functions that are specific for TV mode + */ -static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) +/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */ +static void set_tv_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = to_tuner(i2c_get_clientdata(c)); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && - (t->mode_mask & tun_setup->mode_mask))) || - (tun_setup->addr == c->addr)) { - set_type(c, tun_setup->type, tun_setup->mode_mask, - tun_setup->config, tun_setup->tuner_callback); - } else - tuner_dbg("set addr discarded for type %i, mask %x. " - "Asked to change tuner at addr 0x%02x, with mask %x\n", - t->type, t->mode_mask, - tun_setup->addr, tun_setup->mode_mask); -} + struct analog_parameters params = { + .mode = t->mode, + .audmode = t->audmode, + .std = t->std + }; -static inline int check_mode(struct tuner *t, char *cmd) -{ - if ((1 << t->mode & t->mode_mask) == 0) { - return -EINVAL; + if (t->type == UNSET) { + tuner_warn ("tuner type not set\n"); + return; } - - switch (t->mode) { - case V4L2_TUNER_RADIO: - tuner_dbg("Cmd %s accepted for radio\n", cmd); - break; - case V4L2_TUNER_ANALOG_TV: - tuner_dbg("Cmd %s accepted for analog TV\n", cmd); - break; - case V4L2_TUNER_DIGITAL_TV: - tuner_dbg("Cmd %s accepted for digital TV\n", cmd); - break; + if (NULL == analog_ops->set_params) { + tuner_warn ("Tuner has no way to set tv freq\n"); + return; } - return 0; + if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) { + tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n", + freq / 16, freq % 16 * 100 / 16, tv_range[0], + tv_range[1]); + /* V4L2 spec: if the freq is not possible then the closest + possible value should be selected */ + if (freq < tv_range[0] * 16) + freq = tv_range[0] * 16; + else + freq = tv_range[1] * 16; + } + params.frequency = freq; + tuner_dbg("tv freq set to %d.%02d\n", + freq / 16, freq % 16 * 100 / 16); + t->tv_freq = freq; + + analog_ops->set_params(&t->fe, ¶ms); } /* get more precise norm info from insmod option */ @@ -641,7 +796,117 @@ static int tuner_fixup_std(struct tuner *t) break; } } - return 0; + return 0; +} + +/* + * Functions that are specific for Radio mode + */ + +static void set_radio_freq(struct i2c_client *c, unsigned int freq) +{ + struct tuner *t = to_tuner(i2c_get_clientdata(c)); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + + struct analog_parameters params = { + .mode = t->mode, + .audmode = t->audmode, + .std = t->std + }; + + if (t->type == UNSET) { + tuner_warn ("tuner type not set\n"); + return; + } + if (NULL == analog_ops->set_params) { + tuner_warn ("tuner has no way to set radio frequency\n"); + return; + } + if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) { + tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n", + freq / 16000, freq % 16000 * 100 / 16000, + radio_range[0], radio_range[1]); + /* V4L2 spec: if the freq is not possible then the closest + possible value should be selected */ + if (freq < radio_range[0] * 16000) + freq = radio_range[0] * 16000; + else + freq = radio_range[1] * 16000; + } + params.frequency = freq; + tuner_dbg("radio freq set to %d.%02d\n", + freq / 16000, freq % 16000 * 100 / 16000); + t->radio_freq = freq; + + analog_ops->set_params(&t->fe, ¶ms); +} + +/* + * Functions that should be broken into separate radio/TV functions + */ + +static inline int check_mode(struct tuner *t, char *cmd) +{ + if ((1 << t->mode & t->mode_mask) == 0) { + return -EINVAL; + } + + switch (t->mode) { + case V4L2_TUNER_RADIO: + tuner_dbg("Cmd %s accepted for radio\n", cmd); + break; + case V4L2_TUNER_ANALOG_TV: + tuner_dbg("Cmd %s accepted for analog TV\n", cmd); + break; + case V4L2_TUNER_DIGITAL_TV: + tuner_dbg("Cmd %s accepted for digital TV\n", cmd); + break; + } + return 0; +} + +/* + * Switch tuner to other mode. If tuner support both tv and radio, + * set another frequency to some value (This is needed for some pal + * tuners to avoid locking). Otherwise, just put second tuner in + * standby mode. + */ + +static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd) +{ + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + + if (mode == t->mode) + return 0; + + t->mode = mode; + + if (check_mode(t, cmd) == -EINVAL) { + tuner_dbg("Tuner doesn't support this mode. " + "Putting tuner to sleep\n"); + t->mode = T_STANDBY; + if (analog_ops->standby) + analog_ops->standby(&t->fe); + return -EINVAL; + } + return 0; +} + +static void set_freq(struct i2c_client *c, unsigned long freq) +{ + struct tuner *t = to_tuner(i2c_get_clientdata(c)); + + switch (t->mode) { + case V4L2_TUNER_RADIO: + set_radio_freq(c, freq); + break; + case V4L2_TUNER_ANALOG_TV: + case V4L2_TUNER_DIGITAL_TV: + set_tv_freq(c, freq); + break; + default: + tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode); + } } static void tuner_status(struct dvb_frontend *fe) @@ -684,62 +949,6 @@ static void tuner_status(struct dvb_frontend *fe) analog_ops->has_signal(fe)); } -/* ---------------------------------------------------------------------- */ - -/* - * Switch tuner to other mode. If tuner support both tv and radio, - * set another frequency to some value (This is needed for some pal - * tuners to avoid locking). Otherwise, just put second tuner in - * standby mode. - */ - -static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd) -{ - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - if (mode == t->mode) - return 0; - - t->mode = mode; - - if (check_mode(t, cmd) == -EINVAL) { - tuner_dbg("Tuner doesn't support this mode. " - "Putting tuner to sleep\n"); - t->mode = T_STANDBY; - if (analog_ops->standby) - analog_ops->standby(&t->fe); - return -EINVAL; - } - return 0; -} - -static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type) -{ - struct tuner *t = to_tuner(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", - type->type, - type->addr, - type->mode_mask, - type->config); - - set_addr(client, type); - return 0; -} - -static int tuner_s_radio(struct v4l2_subdev *sd) -{ - struct tuner *t = to_tuner(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (set_mode(client, t, V4L2_TUNER_RADIO, "s_radio") == -EINVAL) - return 0; - if (t->radio_freq) - set_freq(client, t->radio_freq); - return 0; -} - static int tuner_s_power(struct v4l2_subdev *sd, int on) { struct tuner *t = to_tuner(sd); @@ -758,20 +967,18 @@ static int tuner_s_power(struct v4l2_subdev *sd, int on) return 0; } -static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) +/* ---------------------------------------------------------------------- */ + + +static int tuner_s_radio(struct v4l2_subdev *sd) { struct tuner *t = to_tuner(sd); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - if (t->type != cfg->tuner) - return 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (analog_ops->set_config) { - analog_ops->set_config(&t->fe, cfg->priv); + if (set_mode(client, t, V4L2_TUNER_RADIO, "s_radio") == -EINVAL) return 0; - } - - tuner_dbg("Tuner frontend module has no way to set config\n"); + if (t->radio_freq) + set_freq(client, t->radio_freq); return 0; } @@ -956,178 +1163,6 @@ static const struct v4l2_subdev_ops tuner_ops = { .tuner = &tuner_tuner_ops, }; -/* ---------------------------------------------------------------------- */ - -static LIST_HEAD(tuner_list); - -/* Search for existing radio and/or TV tuners on the given I2C adapter. - Note that when this function is called from tuner_probe you can be - certain no other devices will be added/deleted at the same time, I2C - core protects against that. */ -static void tuner_lookup(struct i2c_adapter *adap, - struct tuner **radio, struct tuner **tv) -{ - struct tuner *pos; - - *radio = NULL; - *tv = NULL; - - list_for_each_entry(pos, &tuner_list, list) { - int mode_mask; - - if (pos->i2c->adapter != adap || - strcmp(pos->i2c->driver->driver.name, "tuner")) - continue; - - mode_mask = pos->mode_mask & ~T_STANDBY; - if (*radio == NULL && mode_mask == T_RADIO) - *radio = pos; - /* Note: currently TDA9887 is the only demod-only - device. If other devices appear then we need to - make this test more general. */ - else if (*tv == NULL && pos->type != TUNER_TDA9887 && - (pos->mode_mask & (T_ANALOG_TV | T_DIGITAL_TV))) - *tv = pos; - } -} - -/* During client attach, set_type is called by adapter's attach_inform callback. - set_type must then be completed by tuner_probe. - */ -static int tuner_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct tuner *t; - struct tuner *radio; - struct tuner *tv; - - t = kzalloc(sizeof(struct tuner), GFP_KERNEL); - if (NULL == t) - return -ENOMEM; - v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops); - t->i2c = client; - t->name = "(tuner unset)"; - t->type = UNSET; - t->audmode = V4L2_TUNER_MODE_STEREO; - t->mode_mask = T_UNINITIALIZED; - - if (show_i2c) { - unsigned char buffer[16]; - int i, rc; - - memset(buffer, 0, sizeof(buffer)); - rc = i2c_master_recv(client, buffer, sizeof(buffer)); - tuner_info("I2C RECV = "); - for (i = 0; i < rc; i++) - printk(KERN_CONT "%02x ", buffer[i]); - printk("\n"); - } - - /* autodetection code based on the i2c addr */ - if (!no_autodetect) { - switch (client->addr) { - case 0x10: - if (tuner_symbol_probe(tea5761_autodetection, - t->i2c->adapter, - t->i2c->addr) >= 0) { - t->type = TUNER_TEA5761; - t->mode_mask = T_RADIO; - t->mode = T_STANDBY; - /* Sets freq to FM range */ - t->radio_freq = 87.5 * 16000; - tuner_lookup(t->i2c->adapter, &radio, &tv); - if (tv) - tv->mode_mask &= ~T_RADIO; - - goto register_client; - } - kfree(t); - return -ENODEV; - case 0x42: - case 0x43: - case 0x4a: - case 0x4b: - /* If chip is not tda8290, don't register. - since it can be tda9887*/ - if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter, - t->i2c->addr) >= 0) { - tuner_dbg("tda829x detected\n"); - } else { - /* Default is being tda9887 */ - t->type = TUNER_TDA9887; - t->mode_mask = T_RADIO | T_ANALOG_TV | - T_DIGITAL_TV; - t->mode = T_STANDBY; - goto register_client; - } - break; - case 0x60: - if (tuner_symbol_probe(tea5767_autodetection, - t->i2c->adapter, t->i2c->addr) - >= 0) { - t->type = TUNER_TEA5767; - t->mode_mask = T_RADIO; - t->mode = T_STANDBY; - /* Sets freq to FM range */ - t->radio_freq = 87.5 * 16000; - tuner_lookup(t->i2c->adapter, &radio, &tv); - if (tv) - tv->mode_mask &= ~T_RADIO; - - goto register_client; - } - break; - } - } - - /* Initializes only the first TV tuner on this adapter. Why only the - first? Because there are some devices (notably the ones with TI - tuners) that have more than one i2c address for the *same* device. - Experience shows that, except for just one case, the first - address is the right one. The exception is a Russian tuner - (ACORP_Y878F). So, the desired behavior is just to enable the - first found TV tuner. */ - tuner_lookup(t->i2c->adapter, &radio, &tv); - if (tv == NULL) { - t->mode_mask = T_ANALOG_TV | T_DIGITAL_TV; - if (radio == NULL) - t->mode_mask |= T_RADIO; - tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask); - t->tv_freq = 400 * 16; /* Sets freq to VHF High */ - t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */ - } - - /* Should be just before return */ -register_client: - tuner_info("chip found @ 0x%x (%s)\n", client->addr << 1, - client->adapter->name); - - /* Sets a default mode */ - if (t->mode_mask & T_ANALOG_TV) { - t->mode = V4L2_TUNER_ANALOG_TV; - } else if (t->mode_mask & T_RADIO) { - t->mode = V4L2_TUNER_RADIO; - } else { - t->mode = V4L2_TUNER_DIGITAL_TV; - } - set_type(client, t->type, t->mode_mask, t->config, t->fe.callback); - list_add_tail(&t->list, &tuner_list); - return 0; -} - -static int tuner_remove(struct i2c_client *client) -{ - struct tuner *t = to_tuner(i2c_get_clientdata(client)); - - v4l2_device_unregister_subdev(&t->sd); - tuner_detach(&t->fe); - t->fe.analog_demod_priv = NULL; - - list_del(&t->list); - kfree(t); - return 0; -} - /* ----------------------------------------------------------------------- */ /* This driver supports many devices and the idea is to let the driver @@ -1165,10 +1200,6 @@ static __exit void exit_tuner(void) module_init(init_tuner); module_exit(exit_tuner); -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ +MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners"); +MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From e2d25a2474a8c0178bac4919729ab092783bd916 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Feb 2011 10:09:07 -0300 Subject: [media] tuner-core: Some cleanups at check_mode/set_mode Properly document those functions and do some cleanups around that. There's just one behavior change on this patchset: it will now restore TV frequency when changing from radio to TV mode. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 5c3da21..7cdfe3a 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -841,38 +841,39 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) analog_ops->set_params(&t->fe, ¶ms); } -/* - * Functions that should be broken into separate radio/TV functions +/** + * check_mode - Verify if tuner supports the requested mode + * @t - a pointer to the module's internal struct_tuner + * + * This function checks if the tuner is capable of tuning analog TV, + * digital TV or radio, depending on what the caller wants. If the + * tuner can't support that mode, it returns -EINVAL. Otherwise, it + * returns 0. + * This function is needed for boards that have a separate tuner for + * radio (like devices with tea5767). */ - -static inline int check_mode(struct tuner *t, char *cmd) +static inline int check_mode(struct tuner *t) { if ((1 << t->mode & t->mode_mask) == 0) { return -EINVAL; } - - switch (t->mode) { - case V4L2_TUNER_RADIO: - tuner_dbg("Cmd %s accepted for radio\n", cmd); - break; - case V4L2_TUNER_ANALOG_TV: - tuner_dbg("Cmd %s accepted for analog TV\n", cmd); - break; - case V4L2_TUNER_DIGITAL_TV: - tuner_dbg("Cmd %s accepted for digital TV\n", cmd); - break; - } return 0; } -/* - * Switch tuner to other mode. If tuner support both tv and radio, - * set another frequency to some value (This is needed for some pal - * tuners to avoid locking). Otherwise, just put second tuner in - * standby mode. +/** + * set_mode - Switch tuner to other mode. + * @client - struct i2c_client pointer + * @t - a pointer to the module's internal struct_tuner + * @mode - enum v4l2_type + T_STANDBY mode + * @cmd - string for the command to be executed (for debug messages) + * + * If tuner doesn't support the needed mode (radio or TV), prints a + * debug message and returns -EINVAL, changing internal state to T_STANDBY. + * Otherwise, changes the state and sets frequency to the last value, if + * the tuner can sleep or if it supports both Radio and TV. */ - -static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd) +static inline int set_mode(struct i2c_client *client, struct tuner *t, + int mode, char *cmd) { struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; @@ -881,7 +882,7 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, t->mode = mode; - if (check_mode(t, cmd) == -EINVAL) { + if (check_mode(t) == -EINVAL) { tuner_dbg("Tuner doesn't support this mode. " "Putting tuner to sleep\n"); t->mode = T_STANDBY; @@ -889,9 +890,22 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, analog_ops->standby(&t->fe); return -EINVAL; } + + if (t->mode == V4L2_TUNER_RADIO) { + if (t->radio_freq) + set_radio_freq(client, t->radio_freq); + } else { + if (t->tv_freq) + set_tv_freq(client, t->tv_freq); + } + return 0; } +/* + * Functions that should be broken into separate radio/TV functions + */ + static void set_freq(struct i2c_client *c, unsigned long freq) { struct tuner *t = to_tuner(i2c_get_clientdata(c)); @@ -959,7 +973,7 @@ static int tuner_s_power(struct v4l2_subdev *sd, int on) tuner_dbg("Putting tuner to sleep\n"); - if (check_mode(t, "s_power") == -EINVAL) + if (check_mode(t) == -EINVAL) return 0; t->mode = T_STANDBY; if (analog_ops->standby) @@ -977,8 +991,6 @@ static int tuner_s_radio(struct v4l2_subdev *sd) if (set_mode(client, t, V4L2_TUNER_RADIO, "s_radio") == -EINVAL) return 0; - if (t->radio_freq) - set_freq(client, t->radio_freq); return 0; } @@ -1017,7 +1029,7 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) struct tuner *t = to_tuner(sd); struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - if (check_mode(t, "g_frequency") == -EINVAL) + if (check_mode(t) == -EINVAL) return 0; f->type = t->mode; if (fe_tuner_ops->get_frequency) { @@ -1040,7 +1052,7 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - if (check_mode(t, "g_tuner") == -EINVAL) + if (check_mode(t) == -EINVAL) return 0; vt->type = t->mode; @@ -1081,7 +1093,7 @@ static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (check_mode(t, "s_tuner") == -EINVAL) + if (check_mode(t) == -EINVAL) return 0; /* do nothing unless we're a radio tuner */ -- cgit v0.10.2 From cbde689823776d187ba1b307a171625dbc02dd4f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Feb 2011 10:42:09 -0300 Subject: [media] tuner-core: Better implement standby mode In the past, T_STANDBY were used on devices with a separate radio tuner to mark a tuner that were disabled. With the time, it got newer meanings. Also, due to a bug at the logic, the driver might incorrectly return T_STANDBY to userspace. So, instead of keeping the abuse, just use a boolean for storing such information. We can't remove T_STANDBY yet, as this is used on two other drivers. A latter patch will address its usage outside tuner-core. Thanks-to: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 7cdfe3a..e6855a4 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -115,9 +115,11 @@ struct tuner { unsigned int radio_freq; unsigned int audmode; - unsigned int mode; + enum v4l2_tuner_type mode; unsigned int mode_mask; /* Combination of allowable modes */ + bool standby; /* Standby mode */ + unsigned int type; /* chip type id */ unsigned int config; const char *name; @@ -262,12 +264,6 @@ static void set_type(struct i2c_client *c, unsigned int type, t->fe.callback = tuner_callback; } - if (t->mode == T_UNINITIALIZED) { - tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr); - - return; - } - /* discard private data, in case set_type() was previously called */ tuner_detach(&t->fe); t->fe.analog_demod_priv = NULL; @@ -387,8 +383,7 @@ static void set_type(struct i2c_client *c, unsigned int type, tuner_dbg("type set to %s\n", t->name); - if (t->mode_mask == T_UNINITIALIZED) - t->mode_mask = new_mode_mask; + t->mode_mask = new_mode_mask; /* Some tuners require more initialization setup before use, such as firmware download or device calibration. @@ -411,7 +406,6 @@ static void set_type(struct i2c_client *c, unsigned int type, attach_failed: tuner_dbg("Tuner attach for type = %d failed.\n", t->type); t->type = TUNER_ABSENT; - t->mode_mask = T_UNINITIALIZED; return; } @@ -429,10 +423,10 @@ static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) struct tuner *t = to_tuner(i2c_get_clientdata(c)); if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && - (t->mode_mask & tun_setup->mode_mask))) || - (tun_setup->addr == c->addr)) { - set_type(c, tun_setup->type, tun_setup->mode_mask, - tun_setup->config, tun_setup->tuner_callback); + (t->mode_mask & tun_setup->mode_mask))) || + (tun_setup->addr == c->addr)) { + set_type(c, tun_setup->type, tun_setup->mode_mask, + tun_setup->config, tun_setup->tuner_callback); } else tuner_dbg("set addr discarded for type %i, mask %x. " "Asked to change tuner at addr 0x%02x, with mask %x\n", @@ -491,7 +485,8 @@ static void tuner_lookup(struct i2c_adapter *adap, strcmp(pos->i2c->driver->driver.name, "tuner")) continue; - mode_mask = pos->mode_mask & ~T_STANDBY; + mode_mask = pos->mode_mask; + pos->standby = 1; if (*radio == NULL && mode_mask == T_RADIO) *radio = pos; /* Note: currently TDA9887 is the only demod-only @@ -521,7 +516,9 @@ static int tuner_probe(struct i2c_client *client, t->name = "(tuner unset)"; t->type = UNSET; t->audmode = V4L2_TUNER_MODE_STEREO; - t->mode_mask = T_UNINITIALIZED; + t->standby = 1; + t->radio_freq = 87.5 * 16000; /* Initial freq range */ + t->tv_freq = 400 * 16; /* Sets freq to VHF High - needed for some PLL's to properly start */ if (show_i2c) { unsigned char buffer[16]; @@ -544,9 +541,6 @@ static int tuner_probe(struct i2c_client *client, t->i2c->addr) >= 0) { t->type = TUNER_TEA5761; t->mode_mask = T_RADIO; - t->mode = T_STANDBY; - /* Sets freq to FM range */ - t->radio_freq = 87.5 * 16000; tuner_lookup(t->i2c->adapter, &radio, &tv); if (tv) tv->mode_mask &= ~T_RADIO; @@ -569,7 +563,6 @@ static int tuner_probe(struct i2c_client *client, t->type = TUNER_TDA9887; t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV; - t->mode = T_STANDBY; goto register_client; } break; @@ -579,9 +572,7 @@ static int tuner_probe(struct i2c_client *client, >= 0) { t->type = TUNER_TEA5767; t->mode_mask = T_RADIO; - t->mode = T_STANDBY; /* Sets freq to FM range */ - t->radio_freq = 87.5 * 16000; tuner_lookup(t->i2c->adapter, &radio, &tv); if (tv) tv->mode_mask &= ~T_RADIO; @@ -605,15 +596,10 @@ static int tuner_probe(struct i2c_client *client, if (radio == NULL) t->mode_mask |= T_RADIO; tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask); - t->tv_freq = 400 * 16; /* Sets freq to VHF High */ - t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */ } /* Should be just before return */ register_client: - tuner_info("chip found @ 0x%x (%s)\n", client->addr << 1, - client->adapter->name); - /* Sets a default mode */ if (t->mode_mask & T_ANALOG_TV) { t->mode = V4L2_TUNER_ANALOG_TV; @@ -624,6 +610,12 @@ register_client: } set_type(client, t->type, t->mode_mask, t->config, t->fe.callback); list_add_tail(&t->list, &tuner_list); + + tuner_info("Tuner %d found with type(s)%s%s%s.\n", + t->type, + t->mode_mask & T_RADIO ? " radio" : "", + t->mode_mask & T_ANALOG_TV ? " TV" : "", + t->mode_mask & T_ANALOG_TV ? " DTV" : ""); return 0; } @@ -679,6 +671,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) tuner_dbg("tv freq set to %d.%02d\n", freq / 16, freq % 16 * 100 / 16); t->tv_freq = freq; + t->standby = false; analog_ops->set_params(&t->fe, ¶ms); } @@ -837,13 +830,14 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) tuner_dbg("radio freq set to %d.%02d\n", freq / 16000, freq % 16000 * 100 / 16000); t->radio_freq = freq; + t->standby = false; analog_ops->set_params(&t->fe, ¶ms); } /** * check_mode - Verify if tuner supports the requested mode - * @t - a pointer to the module's internal struct_tuner + * @t: a pointer to the module's internal struct_tuner * * This function checks if the tuner is capable of tuning analog TV, * digital TV or radio, depending on what the caller wants. If the @@ -852,51 +846,51 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) * This function is needed for boards that have a separate tuner for * radio (like devices with tea5767). */ -static inline int check_mode(struct tuner *t) +static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode) { - if ((1 << t->mode & t->mode_mask) == 0) { + if ((1 << mode & t->mode_mask) == 0) { return -EINVAL; } return 0; } /** - * set_mode - Switch tuner to other mode. - * @client - struct i2c_client pointer - * @t - a pointer to the module's internal struct_tuner - * @mode - enum v4l2_type + T_STANDBY mode - * @cmd - string for the command to be executed (for debug messages) + * set_mode_freq - Switch tuner to other mode. + * @client: struct i2c_client pointer + * @t: a pointer to the module's internal struct_tuner + * @mode: enum v4l2_type (radio or TV) + * @freq: frequency to set (0 means to use the previous one) * * If tuner doesn't support the needed mode (radio or TV), prints a * debug message and returns -EINVAL, changing internal state to T_STANDBY. * Otherwise, changes the state and sets frequency to the last value, if * the tuner can sleep or if it supports both Radio and TV. */ -static inline int set_mode(struct i2c_client *client, struct tuner *t, - int mode, char *cmd) +static int set_mode_freq(struct i2c_client *client, struct tuner *t, + enum v4l2_tuner_type mode, unsigned int freq) { struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (mode == t->mode) - return 0; - - t->mode = mode; - - if (check_mode(t) == -EINVAL) { - tuner_dbg("Tuner doesn't support this mode. " - "Putting tuner to sleep\n"); - t->mode = T_STANDBY; - if (analog_ops->standby) - analog_ops->standby(&t->fe); - return -EINVAL; + if (mode != t->mode) { + if (check_mode(t, mode) == -EINVAL) { + tuner_dbg("Tuner doesn't support mode %d. " + "Putting tuner to sleep\n", mode); + t->standby = true; + if (analog_ops->standby) + analog_ops->standby(&t->fe); + return -EINVAL; + } + t->mode = mode; + tuner_dbg("Changing to mode %d\n", mode); } - if (t->mode == V4L2_TUNER_RADIO) { - if (t->radio_freq) - set_radio_freq(client, t->radio_freq); + if (freq) + t->radio_freq = freq; + set_radio_freq(client, t->radio_freq); } else { - if (t->tv_freq) - set_tv_freq(client, t->tv_freq); + if (freq) + t->tv_freq = freq; + set_tv_freq(client, t->tv_freq); } return 0; @@ -923,6 +917,13 @@ static void set_freq(struct i2c_client *c, unsigned long freq) } } +/** + * tuner_status - Dumps the current tuner status at dmesg + * @fe: pointer to struct dvb_frontend + * + * This callback is used only for driver debug purposes, answering to + * VIDIOC_LOG_STATUS. No changes should happen on this call. + */ static void tuner_status(struct dvb_frontend *fe) { struct tuner *t = fe->analog_demod_priv; @@ -932,10 +933,16 @@ static void tuner_status(struct dvb_frontend *fe) const char *p; switch (t->mode) { - case V4L2_TUNER_RADIO: p = "radio"; break; - case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break; - case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break; - default: p = "undefined"; break; + case V4L2_TUNER_RADIO: + p = "radio"; + break; + case V4L2_TUNER_DIGITAL_TV: + p = "digital TV"; + break; + case V4L2_TUNER_ANALOG_TV: + default: + p = "analog TV"; + break; } if (t->mode == V4L2_TUNER_RADIO) { freq = t->radio_freq / 16000; @@ -944,7 +951,8 @@ static void tuner_status(struct dvb_frontend *fe) freq = t->tv_freq / 16; freq_fraction = (t->tv_freq % 16) * 100 / 16; } - tuner_info("Tuner mode: %s\n", p); + tuner_info("Tuner mode: %s%s\n", p, + t->standby ? " on standby mode" : ""); tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction); tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std); if (t->mode != V4L2_TUNER_RADIO) @@ -963,19 +971,22 @@ static void tuner_status(struct dvb_frontend *fe) analog_ops->has_signal(fe)); } +/** + * tuner_s_power - controls the power state of the tuner + * @sd: pointer to struct v4l2_subdev + * @on: a zero value puts the tuner to sleep + */ static int tuner_s_power(struct v4l2_subdev *sd, int on) { struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + /* FIXME: Why this function don't wake the tuner if on != 0 ? */ if (on) return 0; tuner_dbg("Putting tuner to sleep\n"); - - if (check_mode(t) == -EINVAL) - return 0; - t->mode = T_STANDBY; + t->standby = true; if (analog_ops->standby) analog_ops->standby(&t->fe); return 0; @@ -983,13 +994,12 @@ static int tuner_s_power(struct v4l2_subdev *sd, int on) /* ---------------------------------------------------------------------- */ - static int tuner_s_radio(struct v4l2_subdev *sd) { struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (set_mode(client, t, V4L2_TUNER_RADIO, "s_radio") == -EINVAL) + if (set_mode_freq(client, t, V4L2_TUNER_RADIO, 0) == -EINVAL) return 0; return 0; } @@ -1002,13 +1012,12 @@ static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "s_std") == -EINVAL) + if (set_mode_freq(client, t, V4L2_TUNER_ANALOG_TV, 0) == -EINVAL) return 0; t->std = std; tuner_fixup_std(t); - if (t->tv_freq) - set_freq(client, t->tv_freq); + return 0; } @@ -1017,9 +1026,8 @@ static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (set_mode(client, t, f->type, "s_frequency") == -EINVAL) + if (set_mode_freq(client, t, f->type, f->frequency) == -EINVAL) return 0; - set_freq(client, f->frequency); return 0; } @@ -1029,20 +1037,20 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) struct tuner *t = to_tuner(sd); struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - if (check_mode(t) == -EINVAL) + if (check_mode(t, f->type) == -EINVAL) return 0; f->type = t->mode; - if (fe_tuner_ops->get_frequency) { + if (fe_tuner_ops->get_frequency && !t->standby) { u32 abs_freq; fe_tuner_ops->get_frequency(&t->fe, &abs_freq); f->frequency = (V4L2_TUNER_RADIO == t->mode) ? DIV_ROUND_CLOSEST(abs_freq * 2, 125) : DIV_ROUND_CLOSEST(abs_freq, 62500); - return 0; + } else { + f->frequency = (V4L2_TUNER_RADIO == t->mode) ? + t->radio_freq : t->tv_freq; } - f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - t->radio_freq : t->tv_freq; return 0; } @@ -1052,9 +1060,8 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - if (check_mode(t) == -EINVAL) + if (check_mode(t, vt->type) == -EINVAL) return 0; - vt->type = t->mode; if (analog_ops->get_afc) vt->afc = analog_ops->get_afc(&t->fe); @@ -1067,8 +1074,7 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) } /* radio mode */ - vt->rxsubchans = - V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; if (fe_tuner_ops->get_status) { u32 tuner_status; @@ -1080,11 +1086,11 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) } if (analog_ops->has_signal) vt->signal = analog_ops->has_signal(&t->fe); - vt->capability |= - V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; vt->audmode = t->audmode; vt->rangelow = radio_range[0] * 16000; vt->rangehigh = radio_range[1] * 16000; + return 0; } @@ -1093,14 +1099,12 @@ static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (check_mode(t) == -EINVAL) + if (set_mode_freq(client, t, vt->type, 0) == -EINVAL) return 0; - /* do nothing unless we're a radio tuner */ - if (t->mode != V4L2_TUNER_RADIO) - return 0; - t->audmode = vt->audmode; - set_radio_freq(client, t->radio_freq); + if (t->mode == V4L2_TUNER_RADIO) + t->audmode = vt->audmode; + return 0; } @@ -1128,13 +1132,10 @@ static int tuner_resume(struct i2c_client *c) struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("resume\n"); - if (V4L2_TUNER_RADIO == t->mode) { - if (t->radio_freq) - set_freq(c, t->radio_freq); - } else { - if (t->tv_freq) - set_freq(c, t->tv_freq); - } + if (V4L2_TUNER_RADIO == t->mode) + set_freq(c, t->radio_freq); + else + set_freq(c, t->tv_freq); return 0; } diff --git a/include/media/tuner.h b/include/media/tuner.h index 5eec529..1d59642 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -160,7 +160,6 @@ #ifdef __KERNEL__ enum tuner_mode { - T_UNINITIALIZED = 0, T_RADIO = 1 << V4L2_TUNER_RADIO, T_ANALOG_TV = 1 << V4L2_TUNER_ANALOG_TV, T_DIGITAL_TV = 1 << V4L2_TUNER_DIGITAL_TV, -- cgit v0.10.2 From e2f63d9ba51efe8930bf1c952c66b971047d355d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Feb 2011 11:15:21 -0300 Subject: [media] tuner-core: do the right thing for suspend/resume Power down tuners at suspend. At resume, if the tuner is in standby, calls set_mode, that will turn it on and set the latest frequencies. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index e6855a4..e6b63e9 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -1121,9 +1121,13 @@ static int tuner_log_status(struct v4l2_subdev *sd) static int tuner_suspend(struct i2c_client *c, pm_message_t state) { struct tuner *t = to_tuner(i2c_get_clientdata(c)); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; tuner_dbg("suspend\n"); - /* FIXME: power down ??? */ + + if (!t->standby && analog_ops->standby) + analog_ops->standby(&t->fe); + return 0; } @@ -1132,10 +1136,10 @@ static int tuner_resume(struct i2c_client *c) struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("resume\n"); - if (V4L2_TUNER_RADIO == t->mode) - set_freq(c, t->radio_freq); - else - set_freq(c, t->tv_freq); + + if (!t->standby) + set_mode_freq(c, t, t->type, 0); + return 0; } -- cgit v0.10.2 From 7d275bf840a8f390b8fd3d686545e09ef01f7915 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Feb 2011 11:28:00 -0300 Subject: [media] tuner-core: CodingStyle cleanups Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index e6b63e9..70ff416 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -32,7 +32,7 @@ #define UNSET (-1U) -#define PREFIX t->i2c->driver->driver.name +#define PREFIX (t->i2c->driver->driver.name) /* * Driver modprobe parameters @@ -55,7 +55,7 @@ static char pal[] = "--"; static char secam[] = "--"; static char ntsc[] = "-"; -module_param_named(debug,tuner_debug, int, 0644); +module_param_named(debug, tuner_debug, int, 0644); module_param_array(tv_range, int, NULL, 0644); module_param_array(radio_range, int, NULL, 0644); module_param_string(pal, pal, sizeof(pal), 0644); @@ -252,7 +252,7 @@ static void set_type(struct i2c_client *c, unsigned int type, int tune_now = 1; if (type == UNSET || type == TUNER_ABSENT) { - tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr); + tuner_dbg("tuner 0x%02x: Tuner type absent\n", c->addr); return; } @@ -422,9 +422,9 @@ static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) { struct tuner *t = to_tuner(i2c_get_clientdata(c)); - if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && - (t->mode_mask & tun_setup->mode_mask))) || - (tun_setup->addr == c->addr)) { + if ((t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && + (t->mode_mask & tun_setup->mode_mask))) || + (tun_setup->addr == c->addr)) { set_type(c, tun_setup->type, tun_setup->mode_mask, tun_setup->config, tun_setup->tuner_callback); } else @@ -449,7 +449,8 @@ static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type) return 0; } -static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) +static int tuner_s_config(struct v4l2_subdev *sd, + const struct v4l2_priv_tun_config *cfg) { struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; @@ -601,13 +602,12 @@ static int tuner_probe(struct i2c_client *client, /* Should be just before return */ register_client: /* Sets a default mode */ - if (t->mode_mask & T_ANALOG_TV) { + if (t->mode_mask & T_ANALOG_TV) t->mode = V4L2_TUNER_ANALOG_TV; - } else if (t->mode_mask & T_RADIO) { + else if (t->mode_mask & T_RADIO) t->mode = V4L2_TUNER_RADIO; - } else { + else t->mode = V4L2_TUNER_DIGITAL_TV; - } set_type(client, t->type, t->mode_mask, t->config, t->fe.callback); list_add_tail(&t->list, &tuner_list); @@ -649,15 +649,15 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) }; if (t->type == UNSET) { - tuner_warn ("tuner type not set\n"); + tuner_warn("tuner type not set\n"); return; } if (NULL == analog_ops->set_params) { - tuner_warn ("Tuner has no way to set tv freq\n"); + tuner_warn("Tuner has no way to set tv freq\n"); return; } if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) { - tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n", + tuner_dbg("TV freq (%d.%02d) out of range (%d-%d)\n", freq / 16, freq % 16 * 100 / 16, tv_range[0], tv_range[1]); /* V4L2 spec: if the freq is not possible then the closest @@ -682,31 +682,31 @@ static int tuner_fixup_std(struct tuner *t) if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) { switch (pal[0]) { case '6': - tuner_dbg ("insmod fixup: PAL => PAL-60\n"); + tuner_dbg("insmod fixup: PAL => PAL-60\n"); t->std = V4L2_STD_PAL_60; break; case 'b': case 'B': case 'g': case 'G': - tuner_dbg ("insmod fixup: PAL => PAL-BG\n"); + tuner_dbg("insmod fixup: PAL => PAL-BG\n"); t->std = V4L2_STD_PAL_BG; break; case 'i': case 'I': - tuner_dbg ("insmod fixup: PAL => PAL-I\n"); + tuner_dbg("insmod fixup: PAL => PAL-I\n"); t->std = V4L2_STD_PAL_I; break; case 'd': case 'D': case 'k': case 'K': - tuner_dbg ("insmod fixup: PAL => PAL-DK\n"); + tuner_dbg("insmod fixup: PAL => PAL-DK\n"); t->std = V4L2_STD_PAL_DK; break; case 'M': case 'm': - tuner_dbg ("insmod fixup: PAL => PAL-M\n"); + tuner_dbg("insmod fixup: PAL => PAL-M\n"); t->std = V4L2_STD_PAL_M; break; case 'N': @@ -715,7 +715,7 @@ static int tuner_fixup_std(struct tuner *t) tuner_dbg("insmod fixup: PAL => PAL-Nc\n"); t->std = V4L2_STD_PAL_Nc; } else { - tuner_dbg ("insmod fixup: PAL => PAL-N\n"); + tuner_dbg("insmod fixup: PAL => PAL-N\n"); t->std = V4L2_STD_PAL_N; } break; @@ -723,7 +723,7 @@ static int tuner_fixup_std(struct tuner *t) /* default parameter, do nothing */ break; default: - tuner_warn ("pal= argument not recognised\n"); + tuner_warn("pal= argument not recognised\n"); break; } } @@ -736,22 +736,24 @@ static int tuner_fixup_std(struct tuner *t) case 'h': case 'H': tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n"); - t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + t->std = V4L2_STD_SECAM_B | + V4L2_STD_SECAM_G | + V4L2_STD_SECAM_H; break; case 'd': case 'D': case 'k': case 'K': - tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n"); + tuner_dbg("insmod fixup: SECAM => SECAM-DK\n"); t->std = V4L2_STD_SECAM_DK; break; case 'l': case 'L': - if ((secam[1]=='C')||(secam[1]=='c')) { - tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n"); + if ((secam[1] == 'C') || (secam[1] == 'c')) { + tuner_dbg("insmod fixup: SECAM => SECAM-L'\n"); t->std = V4L2_STD_SECAM_LC; } else { - tuner_dbg ("insmod fixup: SECAM => SECAM-L\n"); + tuner_dbg("insmod fixup: SECAM => SECAM-L\n"); t->std = V4L2_STD_SECAM_L; } break; @@ -759,7 +761,7 @@ static int tuner_fixup_std(struct tuner *t) /* default parameter, do nothing */ break; default: - tuner_warn ("secam= argument not recognised\n"); + tuner_warn("secam= argument not recognised\n"); break; } } @@ -808,15 +810,15 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) }; if (t->type == UNSET) { - tuner_warn ("tuner type not set\n"); + tuner_warn("tuner type not set\n"); return; } if (NULL == analog_ops->set_params) { - tuner_warn ("tuner has no way to set radio frequency\n"); + tuner_warn("tuner has no way to set radio frequency\n"); return; } if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) { - tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n", + tuner_dbg("radio freq (%d.%02d) out of range (%d-%d)\n", freq / 16000, freq % 16000 * 100 / 16000, radio_range[0], radio_range[1]); /* V4L2 spec: if the freq is not possible then the closest @@ -848,9 +850,9 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) */ static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode) { - if ((1 << mode & t->mode_mask) == 0) { + if ((1 << mode & t->mode_mask) == 0) return -EINVAL; - } + return 0; } @@ -867,7 +869,7 @@ static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode) * the tuner can sleep or if it supports both Radio and TV. */ static int set_mode_freq(struct i2c_client *client, struct tuner *t, - enum v4l2_tuner_type mode, unsigned int freq) + enum v4l2_tuner_type mode, unsigned int freq) { struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; @@ -913,7 +915,7 @@ static void set_freq(struct i2c_client *c, unsigned long freq) set_tv_freq(c, freq); break; default: - tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode); + tuner_dbg("freq set: unknown mode: 0x%04x!\n", t->mode); } } @@ -933,16 +935,16 @@ static void tuner_status(struct dvb_frontend *fe) const char *p; switch (t->mode) { - case V4L2_TUNER_RADIO: - p = "radio"; - break; - case V4L2_TUNER_DIGITAL_TV: - p = "digital TV"; - break; - case V4L2_TUNER_ANALOG_TV: - default: - p = "analog TV"; - break; + case V4L2_TUNER_RADIO: + p = "radio"; + break; + case V4L2_TUNER_DIGITAL_TV: + p = "digital TV"; + break; + case V4L2_TUNER_ANALOG_TV: + default: + p = "analog TV"; + break; } if (t->mode == V4L2_TUNER_RADIO) { freq = t->radio_freq / 16000; @@ -956,7 +958,7 @@ static void tuner_status(struct dvb_frontend *fe) tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction); tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std); if (t->mode != V4L2_TUNER_RADIO) - return; + return; if (fe_tuner_ops->get_status) { u32 tuner_status; -- cgit v0.10.2 From 900f734b3bafb4e111b9ec5d865c448a9911b2ab Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Feb 2011 12:56:39 -0300 Subject: [media] tuner-core: Don't use a static var for xc5000_cfg A static var is evil, especially if a device has two boards with xc5000. Instead, just like the other drivers, use stack to store its config during setup. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 70ff416..16939ca 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -66,7 +66,6 @@ module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); * Static vars */ -static struct xc5000_config xc5000_cfg; static LIST_HEAD(tuner_list); /* @@ -338,9 +337,12 @@ static void set_type(struct i2c_client *c, unsigned int type, break; case TUNER_XC5000: { - xc5000_cfg.i2c_address = t->i2c->addr; - /* if_khz will be set when the digital dvb_attach() occurs */ - xc5000_cfg.if_khz = 0; + struct xc5000_config xc5000_cfg = { + .i2c_address = t->i2c->addr, + /* if_khz will be set at dvb_attach() */ + .if_khz = 0, + }; + if (!dvb_attach(xc5000_attach, &t->fe, t->i2c->adapter, &xc5000_cfg)) goto attach_failed; -- cgit v0.10.2 From a34ec5f3743950cadb8f512c884b8c9dbaa9a7a5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 15 Feb 2011 00:55:18 -0300 Subject: [media] tuner-core: dead code removal Remove the now obsolete set_freq. Also merge set_addr and set_type_addr. In the past, it used to have two different setup calls, one to set just the tuner type to any tuner found, and another to set the type only if the address matches. Those two internal calls were grouped together, but the functions weren't merged, making the code uglier. No functional changes are done in this patch. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 16939ca..2a0bea1 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -419,10 +419,17 @@ attach_failed: * it's applied. Otherwise status and type are applied only to * tuner with exactly the same addr. */ - -static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) +static int tuner_s_type_addr(struct v4l2_subdev *sd, + struct tuner_setup *tun_setup) { - struct tuner *t = to_tuner(i2c_get_clientdata(c)); + struct tuner *t = to_tuner(sd); + struct i2c_client *c = v4l2_get_subdevdata(sd); + + tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", + tun_setup->type, + tun_setup->addr, + tun_setup->mode_mask, + tun_setup->config); if ((t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && (t->mode_mask & tun_setup->mode_mask))) || @@ -434,20 +441,7 @@ static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) "Asked to change tuner at addr 0x%02x, with mask %x\n", t->type, t->mode_mask, tun_setup->addr, tun_setup->mode_mask); -} - -static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type) -{ - struct tuner *t = to_tuner(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", - type->type, - type->addr, - type->mode_mask, - type->config); - - set_addr(client, type); return 0; } @@ -900,27 +894,6 @@ static int set_mode_freq(struct i2c_client *client, struct tuner *t, return 0; } -/* - * Functions that should be broken into separate radio/TV functions - */ - -static void set_freq(struct i2c_client *c, unsigned long freq) -{ - struct tuner *t = to_tuner(i2c_get_clientdata(c)); - - switch (t->mode) { - case V4L2_TUNER_RADIO: - set_radio_freq(c, freq); - break; - case V4L2_TUNER_ANALOG_TV: - case V4L2_TUNER_DIGITAL_TV: - set_tv_freq(c, freq); - break; - default: - tuner_dbg("freq set: unknown mode: 0x%04x!\n", t->mode); - } -} - /** * tuner_status - Dumps the current tuner status at dmesg * @fe: pointer to struct dvb_frontend -- cgit v0.10.2 From 0ae79d993cd5ce425bedebcbd2f8c4fa310acfe6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 15 Feb 2011 01:10:20 -0300 Subject: [media] tuner-core: Fix a few comments on it Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 2a0bea1..dcf03fa 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -128,7 +128,7 @@ struct tuner { * tuner attach/detach logic */ -/** This macro allows us to probe dynamically, avoiding static links */ +/* This macro allows us to probe dynamically, avoiding static links */ #ifdef CONFIG_MEDIA_ATTACH #define tuner_symbol_probe(FUNCTION, ARGS...) ({ \ int __r = -EINVAL; \ @@ -412,13 +412,20 @@ attach_failed: return; } -/* - * This function apply tuner config to tuner specified - * by tun_setup structure. I addr is unset, then admin status - * and tun addr status is more precise then current status, - * it's applied. Otherwise status and type are applied only to - * tuner with exactly the same addr. -*/ +/** + * tuner_s_type_addr - Sets the tuner type for a device + * + * @sd: subdev descriptor + * @tun_setup: type to be associated to a given tuner i2c address + * + * This function applys the tuner config to tuner specified + * by tun_setup structure. + * If tuner I2C address is UNSET, then it will only set the device + * if the tuner supports the mode specified in the call. + * If the address is specified, the change will be applied only if + * tuner I2C address matches. + * The call can change the tuner number and the tuner mode. + */ static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *tun_setup) { -- cgit v0.10.2 From 2d351df443b0b324a440b5fd053074562d36a7f5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Feb 2011 18:53:12 -0200 Subject: [media] Remove the remaining usages for T_STANDBY tda9887 used to use the T_STANDBY type internally, while tea5761 used it to put the device to sleep. Fix the code for it to work properly with the tuner core changes and remove this flag from tuner.h. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c index bf14bd7..cdb645d 100644 --- a/drivers/media/common/tuners/tda9887.c +++ b/drivers/media/common/tuners/tda9887.c @@ -36,6 +36,8 @@ struct tda9887_priv { unsigned int mode; unsigned int audmode; v4l2_std_id std; + + bool standby; }; /* ---------------------------------------------------------------------- */ @@ -568,7 +570,7 @@ static void tda9887_configure(struct dvb_frontend *fe) tda9887_do_config(fe); tda9887_set_insmod(fe); - if (priv->mode == T_STANDBY) + if (priv->standby) priv->data[1] |= cForcedMuteAudioON; tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", @@ -616,7 +618,7 @@ static void tda9887_standby(struct dvb_frontend *fe) { struct tda9887_priv *priv = fe->analog_demod_priv; - priv->mode = T_STANDBY; + priv->standby = true; tda9887_configure(fe); } @@ -626,6 +628,7 @@ static void tda9887_set_params(struct dvb_frontend *fe, { struct tda9887_priv *priv = fe->analog_demod_priv; + priv->standby = false; priv->mode = params->mode; priv->audmode = params->audmode; priv->std = params->std; @@ -686,7 +689,7 @@ struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, return NULL; case 1: fe->analog_demod_priv = priv; - priv->mode = T_STANDBY; + priv->standby = true; tuner_info("tda988[5/6/7] found\n"); break; default: diff --git a/drivers/media/common/tuners/tea5761.c b/drivers/media/common/tuners/tea5761.c index 925399d..bf78cb9 100644 --- a/drivers/media/common/tuners/tea5761.c +++ b/drivers/media/common/tuners/tea5761.c @@ -23,6 +23,7 @@ struct tea5761_priv { struct tuner_i2c_props i2c_props; u32 frequency; + bool standby; }; /*****************************************************************************/ @@ -135,18 +136,19 @@ static void tea5761_status_dump(unsigned char *buffer) } /* Freq should be specifyed at 62.5 Hz */ -static int set_radio_freq(struct dvb_frontend *fe, - struct analog_parameters *params) +static int __set_radio_freq(struct dvb_frontend *fe, + unsigned int freq, + bool mono) { struct tea5761_priv *priv = fe->tuner_priv; - unsigned int frq = params->frequency; + unsigned int frq = freq; unsigned char buffer[7] = {0, 0, 0, 0, 0, 0, 0 }; unsigned div; int rc; tuner_dbg("radio freq counter %d\n", frq); - if (params->mode == T_STANDBY) { + if (priv->standby) { tuner_dbg("TEA5761 set to standby mode\n"); buffer[5] |= TEA5761_TNCTRL_MU; } else { @@ -154,7 +156,7 @@ static int set_radio_freq(struct dvb_frontend *fe, } - if (params->audmode == V4L2_TUNER_MODE_MONO) { + if (mono) { tuner_dbg("TEA5761 set to mono\n"); buffer[5] |= TEA5761_TNCTRL_MST; } else { @@ -176,6 +178,26 @@ static int set_radio_freq(struct dvb_frontend *fe, return 0; } +static int set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tea5761_priv *priv = fe->analog_demod_priv; + + priv->standby = false; + + return __set_radio_freq(fe, params->frequency, + params->audmode == V4L2_TUNER_MODE_MONO); +} + +static int set_radio_sleep(struct dvb_frontend *fe) +{ + struct tea5761_priv *priv = fe->analog_demod_priv; + + priv->standby = true; + + return __set_radio_freq(fe, priv->frequency, false); +} + static int tea5761_read_status(struct dvb_frontend *fe, char *buffer) { struct tea5761_priv *priv = fe->tuner_priv; @@ -284,6 +306,7 @@ static struct dvb_tuner_ops tea5761_tuner_ops = { .name = "tea5761", // Philips TEA5761HN FM Radio }, .set_analog_params = set_radio_freq, + .sleep = set_radio_sleep, .release = tea5761_release, .get_frequency = tea5761_get_frequency, .get_status = tea5761_get_status, diff --git a/include/media/tuner.h b/include/media/tuner.h index 1d59642..32dfd5f 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -21,6 +21,7 @@ #ifndef _TUNER_H #define _TUNER_H +#ifdef __KERNEL__ #include @@ -157,13 +158,10 @@ #define TDA9887_GAIN_NORMAL (1<<20) #define TDA9887_RIF_41_3 (1<<21) /* radio IF1 41.3 vs 33.3 */ -#ifdef __KERNEL__ - enum tuner_mode { T_RADIO = 1 << V4L2_TUNER_RADIO, T_ANALOG_TV = 1 << V4L2_TUNER_ANALOG_TV, T_DIGITAL_TV = 1 << V4L2_TUNER_DIGITAL_TV, - T_STANDBY = 1 << 31 }; /* Older boards only had a single tuner device. Nowadays multiple tuner @@ -193,11 +191,3 @@ struct tuner_setup { #endif /* __KERNEL__ */ #endif /* _TUNER_H */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v0.10.2 From ad020dc2fe9039628cf6cef42cd1b76531ee8411 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 15 Feb 2011 09:30:50 -0200 Subject: [media] tuner-core: remove usage of DIGITAL_TV tuner-core has no business to do with digital TV. So, don't use T_DIGITAL_TV on it, as it has no code to distinguish between them, and nobody fills T_DIGITAL_TV right. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index 01be89f..39fc923 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -185,8 +185,7 @@ void au0828_card_setup(struct au0828_dev *dev) static u8 eeprom[256]; struct tuner_setup tun_setup; struct v4l2_subdev *sd; - unsigned int mode_mask = T_ANALOG_TV | - T_DIGITAL_TV; + unsigned int mode_mask = T_ANALOG_TV; dprintk(1, "%s()\n", __func__); diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 7f58756..242f0d5 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -3616,7 +3616,7 @@ void __devinit bttv_init_tuner(struct bttv *btv) &btv->c.i2c_adap, "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); - tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; + tun_setup.mode_mask = T_ANALOG_TV; tun_setup.type = btv->tuner_type; tun_setup.addr = addr; diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 4e6ee55..8128b936 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -3165,9 +3165,7 @@ static void cx88_card_setup(struct cx88_core *core) { static u8 eeprom[256]; struct tuner_setup tun_setup; - unsigned int mode_mask = T_RADIO | - T_ANALOG_TV | - T_DIGITAL_TV; + unsigned int mode_mask = T_RADIO | T_ANALOG_TV; memset(&tun_setup, 0, sizeof(tun_setup)); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 74467c1..61c6007 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -7333,9 +7333,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) static void saa7134_tuner_setup(struct saa7134_dev *dev) { struct tuner_setup tun_setup; - unsigned int mode_mask = T_RADIO | - T_ANALOG_TV | - T_DIGITAL_TV; + unsigned int mode_mask = T_RADIO | T_ANALOG_TV; memset(&tun_setup, 0, sizeof(tun_setup)); tun_setup.tuner_callback = saa7134_tuner_callback; diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index dcf03fa..5e1437c 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -497,7 +497,7 @@ static void tuner_lookup(struct i2c_adapter *adap, device. If other devices appear then we need to make this test more general. */ else if (*tv == NULL && pos->type != TUNER_TDA9887 && - (pos->mode_mask & (T_ANALOG_TV | T_DIGITAL_TV))) + (pos->mode_mask & T_ANALOG_TV)) *tv = pos; } } @@ -565,8 +565,7 @@ static int tuner_probe(struct i2c_client *client, } else { /* Default is being tda9887 */ t->type = TUNER_TDA9887; - t->mode_mask = T_RADIO | T_ANALOG_TV | - T_DIGITAL_TV; + t->mode_mask = T_RADIO | T_ANALOG_TV; goto register_client; } break; @@ -596,7 +595,7 @@ static int tuner_probe(struct i2c_client *client, first found TV tuner. */ tuner_lookup(t->i2c->adapter, &radio, &tv); if (tv == NULL) { - t->mode_mask = T_ANALOG_TV | T_DIGITAL_TV; + t->mode_mask = T_ANALOG_TV; if (radio == NULL) t->mode_mask |= T_RADIO; tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask); @@ -607,18 +606,15 @@ register_client: /* Sets a default mode */ if (t->mode_mask & T_ANALOG_TV) t->mode = V4L2_TUNER_ANALOG_TV; - else if (t->mode_mask & T_RADIO) - t->mode = V4L2_TUNER_RADIO; else - t->mode = V4L2_TUNER_DIGITAL_TV; + t->mode = V4L2_TUNER_RADIO; set_type(client, t->type, t->mode_mask, t->config, t->fe.callback); list_add_tail(&t->list, &tuner_list); - tuner_info("Tuner %d found with type(s)%s%s%s.\n", + tuner_info("Tuner %d found with type(s)%s%s.\n", t->type, - t->mode_mask & T_RADIO ? " radio" : "", - t->mode_mask & T_ANALOG_TV ? " TV" : "", - t->mode_mask & T_ANALOG_TV ? " DTV" : ""); + t->mode_mask & T_RADIO ? " Radio" : "", + t->mode_mask & T_ANALOG_TV ? " TV" : ""); return 0; } -- cgit v0.10.2 From a2894e3f8ed691b4873c8122e66902fbf0e78e0b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 15 Feb 2011 10:15:19 -0200 Subject: [media] tuner-core: Improve function documentation This driver is complex, and used by everyone. Better to have it properly documented. No functional changes are done in this patch. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 5e1437c..2c7fe18 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -1,7 +1,17 @@ /* - * * i2c tv tuner chip device driver * core core, i.e. kernel interfaces, registering and so on + * + * Copyright(c) by Ralph Metzler, Gerd Knorr, Gunther Mayer + * + * Copyright(c) 2005-2011 by Mauro Carvalho Chehab + * - Added support for a separate Radio tuner + * - Major rework and cleanups at the code + * + * This driver supports many devices and the idea is to let the driver + * detect which device is present. So rather than listing all supported + * devices here, we pretend to support a single, fake device type that will + * handle both radio and analog TV tuning. */ #include @@ -67,6 +77,7 @@ module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); */ static LIST_HEAD(tuner_list); +static const struct v4l2_subdev_ops tuner_ops; /* * Debug macros @@ -125,6 +136,13 @@ struct tuner { }; /* + * Function prototypes + */ + +static void set_tv_freq(struct i2c_client *c, unsigned int freq); +static void set_radio_freq(struct i2c_client *c, unsigned int freq); + +/* * tuner attach/detach logic */ @@ -233,13 +251,24 @@ static struct analog_demod_ops tuner_analog_ops = { }; /* - * Functions that are common to both TV and radio + * Functions to select between radio and TV and tuner probe functions */ -static void set_tv_freq(struct i2c_client *c, unsigned int freq); -static void set_radio_freq(struct i2c_client *c, unsigned int freq); -static const struct v4l2_subdev_ops tuner_ops; - +/** + * set_type - Sets the tuner type for a given device + * + * @c: i2c_client descriptoy + * @type: type of the tuner (e. g. tuner number) + * @new_mode_mask: Indicates if tuner supports TV and/or Radio + * @new_config: an optional parameter ranging from 0-255 used by + a few tuners to adjust an internal parameter, + like LNA mode + * @tuner_callback: an optional function to be called when switching + * to analog mode + * + * This function applys the tuner config to tuner specified + * by tun_setup structure. It contains several per-tuner initialization "magic" + */ static void set_type(struct i2c_client *c, unsigned int type, unsigned int new_mode_mask, unsigned int new_config, int (*tuner_callback) (void *dev, int component, int cmd, int arg)) @@ -452,6 +481,15 @@ static int tuner_s_type_addr(struct v4l2_subdev *sd, return 0; } +/** + * tuner_s_config - Sets tuner configuration + * + * @sd: subdev descriptor + * @cfg: tuner configuration + * + * Calls tuner set_config() private function to set some tuner-internal + * parameters + */ static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) { @@ -470,10 +508,20 @@ static int tuner_s_config(struct v4l2_subdev *sd, return 0; } -/* Search for existing radio and/or TV tuners on the given I2C adapter. - Note that when this function is called from tuner_probe you can be - certain no other devices will be added/deleted at the same time, I2C - core protects against that. */ +/** + * tuner_lookup - Seek for tuner adapters + * + * @adap: i2c_adapter struct + * @radio: pointer to be filled if the adapter is radio + * @tv: pointer to be filled if the adapter is TV + * + * Search for existing radio and/or TV tuners on the given I2C adapter, + * discarding demod-only adapters (tda9887). + * + * Note that when this function is called from tuner_probe you can be + * certain no other devices will be added/deleted at the same time, I2C + * core protects against that. + */ static void tuner_lookup(struct i2c_adapter *adap, struct tuner **radio, struct tuner **tv) { @@ -502,8 +550,20 @@ static void tuner_lookup(struct i2c_adapter *adap, } } -/* During client attach, set_type is called by adapter's attach_inform callback. - set_type must then be completed by tuner_probe. +/** + *tuner_probe - Probes the existing tuners on an I2C bus + * + * @client: i2c_client descriptor + * @id: not used + * + * This routine probes for tuners at the expected I2C addresses. On most + * cases, if a device answers to a given I2C address, it assumes that the + * device is a tuner. On a few cases, however, an additional logic is needed + * to double check if the device is really a tuner, or to identify the tuner + * type, like on tea5767/5761 devices. + * + * During client attach, set_type is called by adapter's attach_inform callback. + * set_type must then be completed by tuner_probe. */ static int tuner_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -618,6 +678,12 @@ register_client: return 0; } +/** + * tuner_remove - detaches a tuner + * + * @client: i2c_client descriptor + */ + static int tuner_remove(struct i2c_client *client) { struct tuner *t = to_tuner(i2c_get_clientdata(client)); @@ -635,7 +701,12 @@ static int tuner_remove(struct i2c_client *client) * Functions that are specific for TV mode */ -/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */ +/** + * set_tv_freq - Set tuner frequency, freq in Units of 62.5 kHz = 1/16MHz + * + * @c: i2c_client descriptor + * @freq: frequency + */ static void set_tv_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = to_tuner(i2c_get_clientdata(c)); @@ -675,7 +746,19 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) analog_ops->set_params(&t->fe, ¶ms); } -/* get more precise norm info from insmod option */ +/** + * tuner_fixup_std - force a given video standard variant + * + * @t: tuner internal struct + * + * A few devices or drivers have problem to detect some standard variations. + * On other operational systems, the drivers generally have a per-country + * code, and some logic to apply per-country hacks. V4L2 API doesn't provide + * such hacks. Instead, it relies on a proper video standard selection from + * the userspace application. However, as some apps are buggy, not allowing + * to distinguish all video standard variations, a modprobe parameter can + * be used to force a video standard match. + */ static int tuner_fixup_std(struct tuner *t) { if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) { @@ -797,6 +880,12 @@ static int tuner_fixup_std(struct tuner *t) * Functions that are specific for Radio mode */ +/** + * set_radio_freq - Set tuner frequency, freq in Units of 62.5 Hz = 1/16kHz + * + * @c: i2c_client descriptor + * @freq: frequency + */ static void set_radio_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = to_tuner(i2c_get_clientdata(c)); @@ -972,7 +1061,9 @@ static int tuner_s_power(struct v4l2_subdev *sd, int on) return 0; } -/* ---------------------------------------------------------------------- */ +/* + * Function to splicitly change mode to radio. Probably not needed anymore + */ static int tuner_s_radio(struct v4l2_subdev *sd) { @@ -984,9 +1075,10 @@ static int tuner_s_radio(struct v4l2_subdev *sd) return 0; } -/* --- v4l ioctls --- */ -/* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ +/* + * Tuner callbacks to handle userspace ioctl's + */ + static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct tuner *t = to_tuner(sd); @@ -1137,7 +1229,9 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) return -ENOIOCTLCMD; } -/* ----------------------------------------------------------------------- */ +/* + * Callback structs + */ static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, @@ -1160,11 +1254,10 @@ static const struct v4l2_subdev_ops tuner_ops = { .tuner = &tuner_tuner_ops, }; -/* ----------------------------------------------------------------------- */ +/* + * I2C structs and module init functions + */ -/* This driver supports many devices and the idea is to let the driver - detect which device is present. So rather than listing all supported - devices here, we pretend to support a single, fake device type. */ static const struct i2c_device_id tuner_id[] = { { "tuner", }, /* autodetect */ { } -- cgit v0.10.2 From 0eec66c0dfc1b066e6addbc6af755c779f65fd87 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 15 Feb 2011 10:27:03 -0200 Subject: [media] tuner-core: Rearrange some functions to better document Group a few functions together and add/fix comments for each block of the driver. This is just a cleanup patch meant to improve driver readability. No functional changes in this patch. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 2c7fe18..a91a299 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -251,7 +251,7 @@ static struct analog_demod_ops tuner_analog_ops = { }; /* - * Functions to select between radio and TV and tuner probe functions + * Functions to select between radio and TV and tuner probe/remove functions */ /** @@ -698,6 +698,75 @@ static int tuner_remove(struct i2c_client *client) } /* + * Functions to switch between Radio and TV + * + * A few cards have a separate I2C tuner for radio. Those routines + * take care of switching between TV/Radio mode, filtering only the + * commands that apply to the Radio or TV tuner. + */ + +/** + * check_mode - Verify if tuner supports the requested mode + * @t: a pointer to the module's internal struct_tuner + * + * This function checks if the tuner is capable of tuning analog TV, + * digital TV or radio, depending on what the caller wants. If the + * tuner can't support that mode, it returns -EINVAL. Otherwise, it + * returns 0. + * This function is needed for boards that have a separate tuner for + * radio (like devices with tea5767). + */ +static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode) +{ + if ((1 << mode & t->mode_mask) == 0) + return -EINVAL; + + return 0; +} + +/** + * set_mode_freq - Switch tuner to other mode. + * @client: struct i2c_client pointer + * @t: a pointer to the module's internal struct_tuner + * @mode: enum v4l2_type (radio or TV) + * @freq: frequency to set (0 means to use the previous one) + * + * If tuner doesn't support the needed mode (radio or TV), prints a + * debug message and returns -EINVAL, changing its state to standby. + * Otherwise, changes the state and sets frequency to the last value, if + * the tuner can sleep or if it supports both Radio and TV. + */ +static int set_mode_freq(struct i2c_client *client, struct tuner *t, + enum v4l2_tuner_type mode, unsigned int freq) +{ + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + + if (mode != t->mode) { + if (check_mode(t, mode) == -EINVAL) { + tuner_dbg("Tuner doesn't support mode %d. " + "Putting tuner to sleep\n", mode); + t->standby = true; + if (analog_ops->standby) + analog_ops->standby(&t->fe); + return -EINVAL; + } + t->mode = mode; + tuner_dbg("Changing to mode %d\n", mode); + } + if (t->mode == V4L2_TUNER_RADIO) { + if (freq) + t->radio_freq = freq; + set_radio_freq(client, t->radio_freq); + } else { + if (freq) + t->tv_freq = freq; + set_tv_freq(client, t->tv_freq); + } + + return 0; +} + +/* * Functions that are specific for TV mode */ @@ -925,66 +994,9 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) analog_ops->set_params(&t->fe, ¶ms); } -/** - * check_mode - Verify if tuner supports the requested mode - * @t: a pointer to the module's internal struct_tuner - * - * This function checks if the tuner is capable of tuning analog TV, - * digital TV or radio, depending on what the caller wants. If the - * tuner can't support that mode, it returns -EINVAL. Otherwise, it - * returns 0. - * This function is needed for boards that have a separate tuner for - * radio (like devices with tea5767). - */ -static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode) -{ - if ((1 << mode & t->mode_mask) == 0) - return -EINVAL; - - return 0; -} - -/** - * set_mode_freq - Switch tuner to other mode. - * @client: struct i2c_client pointer - * @t: a pointer to the module's internal struct_tuner - * @mode: enum v4l2_type (radio or TV) - * @freq: frequency to set (0 means to use the previous one) - * - * If tuner doesn't support the needed mode (radio or TV), prints a - * debug message and returns -EINVAL, changing internal state to T_STANDBY. - * Otherwise, changes the state and sets frequency to the last value, if - * the tuner can sleep or if it supports both Radio and TV. +/* + * Debug function for reporting tuner status to userspace */ -static int set_mode_freq(struct i2c_client *client, struct tuner *t, - enum v4l2_tuner_type mode, unsigned int freq) -{ - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - if (mode != t->mode) { - if (check_mode(t, mode) == -EINVAL) { - tuner_dbg("Tuner doesn't support mode %d. " - "Putting tuner to sleep\n", mode); - t->standby = true; - if (analog_ops->standby) - analog_ops->standby(&t->fe); - return -EINVAL; - } - t->mode = mode; - tuner_dbg("Changing to mode %d\n", mode); - } - if (t->mode == V4L2_TUNER_RADIO) { - if (freq) - t->radio_freq = freq; - set_radio_freq(client, t->radio_freq); - } else { - if (freq) - t->tv_freq = freq; - set_tv_freq(client, t->tv_freq); - } - - return 0; -} /** * tuner_status - Dumps the current tuner status at dmesg @@ -1040,6 +1052,24 @@ static void tuner_status(struct dvb_frontend *fe) analog_ops->has_signal(fe)); } +/* + * Function to splicitly change mode to radio. Probably not needed anymore + */ + +static int tuner_s_radio(struct v4l2_subdev *sd) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (set_mode_freq(client, t, V4L2_TUNER_RADIO, 0) == -EINVAL) + return 0; + return 0; +} + +/* + * Tuner callbacks to handle userspace ioctl's + */ + /** * tuner_s_power - controls the power state of the tuner * @sd: pointer to struct v4l2_subdev @@ -1061,24 +1091,6 @@ static int tuner_s_power(struct v4l2_subdev *sd, int on) return 0; } -/* - * Function to splicitly change mode to radio. Probably not needed anymore - */ - -static int tuner_s_radio(struct v4l2_subdev *sd) -{ - struct tuner *t = to_tuner(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (set_mode_freq(client, t, V4L2_TUNER_RADIO, 0) == -EINVAL) - return 0; - return 0; -} - -/* - * Tuner callbacks to handle userspace ioctl's - */ - static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct tuner *t = to_tuner(sd); -- cgit v0.10.2 From 550df5a450928172e284cb0f857ae73e5e705305 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 15 Feb 2011 10:31:01 -0200 Subject: [media] tuner-core: Don't touch at standby during tuner_lookup It makes no sense that tuner_lookup would touch at the standby state. Remove it. Thanks-to: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index a91a299..9363ed9 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -538,7 +538,6 @@ static void tuner_lookup(struct i2c_adapter *adap, continue; mode_mask = pos->mode_mask; - pos->standby = 1; if (*radio == NULL && mode_mask == T_RADIO) *radio = pos; /* Note: currently TDA9887 is the only demod-only -- cgit v0.10.2 From 437f5fa34225db0d981536692678afc590e5c87f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Feb 2011 21:03:59 -0300 Subject: [media] tuner: Remove remaining usages of T_DIGITAL_TV A few places used T_DIGITAL_TV internally. Remove the usage of this obsolete mode mask. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index d95f3b2..efcbc3e 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -933,7 +933,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, * that xc2028 will be in a safe state. * Maybe this might also be needed for DTV. */ - if (new_mode == T_ANALOG_TV) { + if (new_mode == V4L2_TUNER_ANALOG_TV) { rc = send_seq(priv, {0x00, 0x00}); /* Analog modes require offset = 0 */ @@ -1054,7 +1054,7 @@ static int xc2028_set_analog_freq(struct dvb_frontend *fe, if (priv->ctrl.input1) type |= INPUT1; return generic_set_freq(fe, (625l * p->frequency) / 10, - T_RADIO, type, 0, 0); + V4L2_TUNER_RADIO, type, 0, 0); } /* if std is not defined, choose one */ @@ -1069,7 +1069,7 @@ static int xc2028_set_analog_freq(struct dvb_frontend *fe, p->std |= parse_audio_std_option(); return generic_set_freq(fe, 62500l * p->frequency, - T_ANALOG_TV, type, p->std, 0); + V4L2_TUNER_ANALOG_TV, type, p->std, 0); } static int xc2028_set_params(struct dvb_frontend *fe, @@ -1174,7 +1174,7 @@ static int xc2028_set_params(struct dvb_frontend *fe, } return generic_set_freq(fe, p->frequency, - T_DIGITAL_TV, type, 0, demod); + V4L2_TUNER_DIGITAL_TV, type, 0, demod); } static int xc2028_sleep(struct dvb_frontend *fe) diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index 455038b..22a2222 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -588,8 +588,6 @@ static void tm6000_config_tuner(struct tm6000_core *dev) tun_setup.mode_mask = 0; if (dev->caps.has_tuner) tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO); - if (dev->caps.has_dvb) - tun_setup.mode_mask |= T_DIGITAL_TV; switch (dev->tuner_type) { case TUNER_XC2028: diff --git a/include/media/tuner.h b/include/media/tuner.h index 32dfd5f..963e334 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -161,7 +161,7 @@ enum tuner_mode { T_RADIO = 1 << V4L2_TUNER_RADIO, T_ANALOG_TV = 1 << V4L2_TUNER_ANALOG_TV, - T_DIGITAL_TV = 1 << V4L2_TUNER_DIGITAL_TV, + /* Don't need to map V4L2_TUNER_DIGITAL_TV, as tuner-core won't use it */ }; /* Older boards only had a single tuner device. Nowadays multiple tuner -- cgit v0.10.2 From 0e09a3c92bfd54a6ac03f8225c4fd10f50111beb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Feb 2011 00:10:22 -0300 Subject: [media] tvp5150: device detection should be done only once Having the device detection happening at reset is bad, as every time, it will produce a message like: tvp5150 2-005c: tvp5150am1 detected. This only polutes the log and for an accidental kernel hacker, it looks like a real problem. So, move those printk's to happen during device probe. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 959d690..e927d25 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -737,27 +737,6 @@ static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) { struct tvp5150 *decoder = to_tvp5150(sd); - u8 msb_id, lsb_id, msb_rom, lsb_rom; - - msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID); - lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID); - msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER); - lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER); - - if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */ - v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id); - - /* ITU-T BT.656.4 timing */ - tvp5150_write(sd, TVP5150_REV_SELECT, 0); - } else { - if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */ - v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id); - } else { - v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", - msb_id, lsb_id); - v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom); - } - } /* Initializes TVP5150 to its default values */ tvp5150_write_inittab(sd, tvp5150_init_default); @@ -977,6 +956,7 @@ static int tvp5150_probe(struct i2c_client *c, { struct tvp5150 *core; struct v4l2_subdev *sd; + u8 msb_id, lsb_id, msb_rom, lsb_rom; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(c->adapter, @@ -992,6 +972,26 @@ static int tvp5150_probe(struct i2c_client *c, v4l_info(c, "chip found @ 0x%02x (%s)\n", c->addr << 1, c->adapter->name); + msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID); + lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID); + msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER); + lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER); + + if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */ + v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id); + + /* ITU-T BT.656.4 timing */ + tvp5150_write(sd, TVP5150_REV_SELECT, 0); + } else { + if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */ + v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id); + } else { + v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", + msb_id, lsb_id); + v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom); + } + } + core->norm = V4L2_STD_ALL; /* Default is autodetect */ core->input = TVP5150_COMPOSITE1; core->enable = 1; -- cgit v0.10.2 From 78e51566f0c56daa58f7bbe2591336b7d478c148 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Feb 2011 00:27:41 -0300 Subject: [media] em28xx: Fix return value for s_ctrl On some cases, driver returns 1. This should be OK, but qv4l2 is too strict about return values. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index f34d524..1ddd3ee 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1452,7 +1452,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, rc = em28xx_audio_analog_set(dev); } } - return rc; + return (rc < 0) ? rc : 0; } static int vidioc_g_tuner(struct file *file, void *priv, -- cgit v0.10.2 From fb8decfabdf841a199b297076ff9f890dee55e53 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Feb 2011 01:00:58 -0300 Subject: [media] em28xx: properly handle subdev controls Subdev controls return codes are evil, as they return -EINVAL to mean both unsupported and invalid arguments. Due to that, we need to use a trick to identify what controls are supported by a subdev. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 1ddd3ee..a83131b 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1387,6 +1387,27 @@ static int vidioc_queryctrl(struct file *file, void *priv, return -EINVAL; } +/* + * FIXME: This is an indirect way to check if a control exists at a + * subdev. Instead of that hack, maybe the better would be to change all + * subdevs to return -ENOIOCTLCMD, if an ioctl is not supported. + */ +static int check_subdev_ctrl(struct em28xx *dev, int id) +{ + struct v4l2_queryctrl qc; + + memset(&qc, 0, sizeof(qc)); + qc.id = id; + + /* enumerate V4L2 device controls */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, &qc); + + if (qc.type) + return 0; + else + return -EINVAL; +} + static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { @@ -1399,7 +1420,6 @@ static int vidioc_g_ctrl(struct file *file, void *priv, return rc; rc = 0; - /* Set an AC97 control */ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) rc = ac97_get_ctrl(dev, ctrl); @@ -1408,6 +1428,9 @@ static int vidioc_g_ctrl(struct file *file, void *priv, /* It were not an AC97 control. Sends it to the v4l2 dev interface */ if (rc == 1) { + if (check_subdev_ctrl(dev, ctrl->id)) + return -EINVAL; + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); rc = 0; } @@ -1434,8 +1457,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv, /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ if (rc == 1) { - rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); - + rc = check_subdev_ctrl(dev, ctrl->id); + if (!rc) + v4l2_device_call_all(&dev->v4l2_dev, 0, + core, s_ctrl, ctrl); /* * In the case of non-AC97 volume controls, we still need * to do some setups at em28xx, in order to mute/unmute -- cgit v0.10.2 From 87a0c94ce616b231f3c0bd09d7dbd39d43b0557a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Feb 2011 12:31:07 -0300 Subject: [media] v4l2-ctrls: Fix control enumeration for multiple subdevs with ctrl v4l2-ctl and qv4l2 enumerate controls using V4L2_CTRL_FLAG_NEXT_CTRL, falling back to the old method if the flag isn't supported. The v4l2_subdev_queryctrl function will currently handle that flag, but for the controls of the subdev only. This isn't right, it should refuse this flag, otherwise v4l2-ctl will only see the controls of the first subdev. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index ef66d2a..2412f08 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -1364,6 +1364,8 @@ EXPORT_SYMBOL(v4l2_queryctrl); int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { + if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) + return -EINVAL; return v4l2_queryctrl(sd->ctrl_handler, qc); } EXPORT_SYMBOL(v4l2_subdev_queryctrl); -- cgit v0.10.2 From d39155d91d034e2b2c9154892e29c8a8eb193b7f Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Sun, 16 Jan 2011 13:53:31 -0300 Subject: [media] Remove compatibility layer from multi-planar API documentation This feature will probably be moved to libv4l2. Signed-off-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/planar-apis.xml b/Documentation/DocBook/v4l/planar-apis.xml index 7f3f552..878ce20 100644 --- a/Documentation/DocBook/v4l/planar-apis.xml +++ b/Documentation/DocBook/v4l/planar-apis.xml @@ -2,10 +2,10 @@ Single- and multi-planar APIs Some devices require data for each input or output video frame - to be placed in discontiguous memory buffers. In such cases one + to be placed in discontiguous memory buffers. In such cases, one video frame has to be addressed using more than one memory address, i.e. one - pointer per "plane". A plane is a sub-buffer of current frame. For examples - of such formats see . + pointer per "plane". A plane is a sub-buffer of the current frame. For + examples of such formats see . Initially, V4L2 API did not support multi-planar buffers and a set of extensions has been introduced to handle them. Those extensions constitute @@ -14,8 +14,8 @@ Some of the V4L2 API calls and structures are interpreted differently, depending on whether single- or multi-planar API is being used. An application can choose whether to use one or the other by passing a corresponding buffer - type to its ioctl calls. Multi-planar versions of buffer types are suffixed with - an `_MPLANE' string. For a list of available multi-planar buffer types + type to its ioctl calls. Multi-planar versions of buffer types are suffixed + with an `_MPLANE' string. For a list of available multi-planar buffer types see &v4l2-buf-type;. @@ -24,28 +24,9 @@ Multi-planar API introduces new multi-planar formats. Those formats use a separate set of FourCC codes. It is important to distinguish between the multi-planar API and a multi-planar format. Multi-planar API calls can - handle all single-planar formats as well, while the single-planar API cannot - handle multi-planar formats. Applications do not have to switch between APIs - when handling both single- and multi-planar devices and should use the - multi-planar API version for both single- and multi-planar formats. - Drivers that do not support multi-planar API can still be handled with it, - utilizing a compatibility layer built into standard V4L2 ioctl handling. - - - -
- Single and multi-planar API compatibility layer - In most casesThe compatibility layer does not cover - drivers that do not use video_ioctl2() call., applications - can use the multi-planar API with older drivers that support - only its single-planar version and vice versa. Appropriate conversion is - done seamlessly for both applications and drivers in the V4L2 core. The - general rule of thumb is: as long as an application uses formats that - a driver supports, it can use either API (although use of multi-planar - formats is only possible with the multi-planar API). The list of formats - supported by a driver can be obtained using the &VIDIOC-ENUM-FMT; call. - It is possible, but discouraged, for a driver or an application to support - and use both versions of the API. + handle all single-planar formats as well (as long as they are passed in + multi-planar API structures), while the single-planar API cannot + handle multi-planar formats.
diff --git a/Documentation/DocBook/v4l/vidioc-querycap.xml b/Documentation/DocBook/v4l/vidioc-querycap.xml index 9369976..f29f1b8 100644 --- a/Documentation/DocBook/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/v4l/vidioc-querycap.xml @@ -142,30 +142,28 @@ this array to zero. V4L2_CAP_VIDEO_CAPTURE 0x00000001 - The device supports single-planar formats through the Video Capture interface. An application can use either -the single or the multi-planar API. + The device supports the single-planar API through the Video Capture interface. V4L2_CAP_VIDEO_CAPTURE_MPLANE 0x00001000 - The device supports multi-planar formats through the Video Capture interface. An application has to use the -multi-planar API. + The device supports the + multi-planar API through the + Video Capture interface. V4L2_CAP_VIDEO_OUTPUT 0x00000002 - The device supports single-planar formats through the Video Output interface. An application can use either -the single or the multi-planar API. + The device supports the single-planar API through the Video Output interface. V4L2_CAP_VIDEO_OUTPUT_MPLANE 0x00002000 - The device supports multi-planar formats through the Video Output interface. An application has to use the -multi-planar API. + The device supports the + multi-planar API through the + Video Output interface. V4L2_CAP_VIDEO_OVERLAY -- cgit v0.10.2 From 0f77f40c300c18c15e7aeb2f9eee510aa96d58fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 07:01:30 -0300 Subject: [media] gspca - sonixj: Move the avg lum computation to a separate function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index d6f39ce..c414707 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -2757,6 +2757,29 @@ static void do_autogain(struct gspca_dev *gspca_dev) } } +/* set the average luminosity from an isoc marker */ +static void set_lum(struct sd *sd, + u8 *data) +{ + int avg_lum; + + /* w0 w1 w2 + * w3 w4 w5 + * w6 w7 w8 + */ + avg_lum = (data[27] << 8) + data[28] /* w3 */ + + + (data[31] << 8) + data[32] /* w5 */ + + + (data[23] << 8) + data[24] /* w1 */ + + + (data[35] << 8) + data[36] /* w7 */ + + + (data[29] << 10) + (data[30] << 2); /* w4 * 4 */ + avg_lum >>= 10; + atomic_set(&sd->avg_lum, avg_lum); +} + /* scan the URB packets */ /* This function is run at interrupt level. */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -2764,7 +2787,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - int sof, avg_lum; + int sof; /* the image ends on a 64 bytes block starting with * ff d9 ff ff 00 c4 c4 96 @@ -2795,23 +2818,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* end of image found - remove the trailing data */ gspca_dev->image_len = sof + 2; gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - if (sd->ag_cnt < 0) - return; -/* w1 w2 w3 */ -/* w4 w5 w6 */ -/* w7 w8 */ -/* w4 */ - avg_lum = ((data[sof + 29] << 8) | data[sof + 30]) >> 6; -/* w6 */ - avg_lum += ((data[sof + 33] << 8) | data[sof + 34]) >> 6; -/* w2 */ - avg_lum += ((data[sof + 25] << 8) | data[sof + 26]) >> 6; -/* w8 */ - avg_lum += ((data[sof + 37] << 8) | data[sof + 38]) >> 6; -/* w5 */ - avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4; - avg_lum >>= 4; - atomic_set(&sd->avg_lum, avg_lum); + if (sd->ag_cnt >= 0) + set_lum(sd, data + sof + 2); } static int sd_set_jcomp(struct gspca_dev *gspca_dev, -- cgit v0.10.2 From a4a3076ffcdbcf40358edc2068aa4d9103fe0a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 07:15:24 -0300 Subject: [media] gspca - sonixj: Better scanning of isochronous packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A marker 'ff ff 00 c4 c4 96' indicates an end of frame. It is 62 bytes long and may be splitted on 2 packets. It contains a flag 'USB full' which indicates that the frame is truncated. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index c414707..21f0330 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -57,6 +57,8 @@ struct sd { atomic_t avg_lum; u32 exposure; + s8 short_mark; + u8 quality; /* image quality */ #define QUALITY_MIN 60 #define QUALITY_MAX 95 @@ -2787,39 +2789,104 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - int sof; - - /* the image ends on a 64 bytes block starting with - * ff d9 ff ff 00 c4 c4 96 - * and followed by various information including luminosity */ - /* this block may be splitted between two packets */ - /* a new image always starts in a new packet */ - switch (gspca_dev->last_packet_type) { - case DISCARD_PACKET: /* restart image building */ - sof = len - 64; - if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9) - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - return; - case LAST_PACKET: /* put the JPEG 422 header */ + int i; + + /* + * A frame ends on the marker + * ff ff 00 c4 c4 96 .. + * which is 62 bytes long and is followed by various information + * including statuses and luminosity. + * + * A marker may be splitted on two packets. + * + * The 6th byte of a marker contains the bits: + * 0x08: USB full + * 0xc0: frame sequence + * When the bit 'USB full' is set, the frame must be discarded; + * this is also the case when the 2 bytes before the marker are + * not the JPEG end of frame ('ff d9'). + */ + +/*fixme: assumption about the following code: + * - there can be only one marker in a packet + */ + + /* skip the remaining bytes of a short marker */ + i = sd->short_mark; + if (i != 0) { + sd->short_mark = 0; + if (i < 0 /* if 'ff' at end of previous packet */ + && data[0] == 0xff + && data[1] == 0x00) + goto marker_found; + if (data[0] == 0xff && data[1] == 0xff) { + i = 0; + goto marker_found; + } + len -= i; + if (len <= 0) + return; + data += i; + } + + /* search backwards if there is a marker in the packet */ + for (i = len - 1; --i >= 0; ) { + if (data[i] != 0xff) { + i--; + continue; + } + if (data[i + 1] == 0xff) { + + /* (there may be 'ff ff' inside a marker) */ + if (i + 2 >= len || data[i + 2] == 0x00) + goto marker_found; + } + } + + /* no marker found */ + /* add the JPEG header if first fragment */ + if (data[len - 1] == 0xff) + sd->short_mark = -1; + if (gspca_dev->last_packet_type == LAST_PACKET) gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); - break; - } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + return; + + /* marker found */ + /* if some error, discard the frame */ +marker_found: + if (i > 2) { + if (data[i - 2] != 0xff || data[i - 1] != 0xd9) { + gspca_dev->last_packet_type = DISCARD_PACKET; + } + } else if (i + 6 < len) { + if (data[i + 6] & 0x08) { + gspca_dev->last_packet_type = DISCARD_PACKET; + } + } - data = gspca_dev->image; - if (data == NULL) - return; - sof = gspca_dev->image_len - 64; - if (data[sof] != 0xff - || data[sof + 1] != 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, data, i); + + /* if the marker is smaller than 62 bytes, + * memorize the number of bytes to skip in the next packet */ + if (i + 62 > len) { /* no more usable data */ + sd->short_mark = i + 62 - len; return; + } - /* end of image found - remove the trailing data */ - gspca_dev->image_len = sof + 2; - gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); if (sd->ag_cnt >= 0) - set_lum(sd, data + sof + 2); + set_lum(sd, data + i); + + /* if more data, start a new frame */ + i += 62; + if (i < len) { + data += i; + len -= i; + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + } } static int sd_set_jcomp(struct gspca_dev *gspca_dev, -- cgit v0.10.2 From dccdcccdd36bf7570967ff53411c7401b8e645d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 07:30:03 -0300 Subject: [media] gspca - sonixj: Have the same JPEG quality for encoding and decoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 21f0330..d465022 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -63,7 +63,6 @@ struct sd { #define QUALITY_MIN 60 #define QUALITY_MAX 95 #define QUALITY_DEF 80 - u8 jpegqual; /* webcam quality */ u8 reg01; u8 reg17; @@ -1786,7 +1785,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->ag_cnt = -1; sd->quality = QUALITY_DEF; - sd->jpegqual = 80; return 0; } @@ -2273,18 +2271,12 @@ static void setfreq(struct gspca_dev *gspca_dev) static void setjpegqual(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i, sc; - if (sd->jpegqual < 50) - sc = 5000 / sd->jpegqual; - else - sc = 200 - sd->jpegqual * 2; + jpeg_set_qual(sd->jpeg_hdr, sd->quality); #if USB_BUF_SZ < 64 #error "No room enough in usb_buf for quantization table" #endif - for (i = 0; i < 64; i++) - gspca_dev->usb_buf[i] = - (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0x08, @@ -2292,9 +2284,7 @@ static void setjpegqual(struct gspca_dev *gspca_dev) 0x0100, 0, gspca_dev->usb_buf, 64, 500); - for (i = 0; i < 64; i++) - gspca_dev->usb_buf[i] = - (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0x08, @@ -2340,7 +2330,6 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ - jpeg_set_qual(sd->jpeg_hdr, sd->quality); /* initialize the bridge */ sn9c1xx = sn_tb[sd->sensor]; @@ -2901,7 +2890,7 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev, else sd->quality = jcomp->quality; if (gspca_dev->streaming) - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + setjpegqual(gspca_dev); return 0; } -- cgit v0.10.2 From b96cfc33e74f5ee5a206b2d43bf0bc3898c5a572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 07:38:58 -0300 Subject: [media] gspca - sonixj: Update the JPEG quality for best image transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During image capture, the filling rate of the isoc packets is computed. It is then used by a work queue to update the current JPEG quality. Tested-by: Franck Bourdonnec Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index d465022..2bfb8df 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -57,12 +57,18 @@ struct sd { atomic_t avg_lum; u32 exposure; + struct work_struct work; + struct workqueue_struct *work_thread; + + u32 pktsz; /* (used by pkt_scan) */ + u16 npkt; + u8 nchg; s8 short_mark; u8 quality; /* image quality */ -#define QUALITY_MIN 60 -#define QUALITY_MAX 95 -#define QUALITY_DEF 80 +#define QUALITY_MIN 25 +#define QUALITY_MAX 90 +#define QUALITY_DEF 70 u8 reg01; u8 reg17; @@ -100,6 +106,8 @@ enum sensors { SENSOR_SP80708, }; +static void qual_upd(struct work_struct *work); + /* device flags */ #define F_PDN_INV 0x01 /* inverse pin S_PWR_DN / sn_xxx tables */ #define F_ILLUM 0x02 /* presence of illuminator */ @@ -1786,6 +1794,8 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->ag_cnt = -1; sd->quality = QUALITY_DEF; + INIT_WORK(&sd->work, qual_upd); + return 0; } @@ -2297,6 +2307,19 @@ static void setjpegqual(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x18, sd->reg18); } +/* 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->quality); + setjpegqual(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); +} + /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { @@ -2610,6 +2633,11 @@ static int sd_start(struct gspca_dev *gspca_dev) setcolors(gspca_dev); setautogain(gspca_dev); setfreq(gspca_dev); + + sd->pktsz = sd->npkt = 0; + sd->nchg = sd->short_mark = 0; + sd->work_thread = create_singlethread_workqueue(MODULE_NAME); + return gspca_dev->usb_err; } @@ -2686,6 +2714,20 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* reg_w1(gspca_dev, 0xf1, 0x01); */ } +/* 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_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2778,7 +2820,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - int i; + int i, new_qual; /* * A frame ends on the marker @@ -2818,6 +2860,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += i; } + /* count the packets and their size */ + sd->npkt++; + sd->pktsz += len; + /* search backwards if there is a marker in the packet */ for (i = len - 1; --i >= 0; ) { if (data[i] != 0xff) { @@ -2843,27 +2889,60 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, return; /* marker found */ - /* if some error, discard the frame */ + /* if some error, discard the frame and decrease the quality */ marker_found: + new_qual = 0; if (i > 2) { if (data[i - 2] != 0xff || data[i - 1] != 0xd9) { gspca_dev->last_packet_type = DISCARD_PACKET; + new_qual = -3; } } else if (i + 6 < len) { if (data[i + 6] & 0x08) { gspca_dev->last_packet_type = DISCARD_PACKET; + new_qual = -5; } } gspca_frame_add(gspca_dev, LAST_PACKET, data, i); + /* compute the filling rate and a new JPEG quality */ + if (new_qual == 0) { + int r; + + 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->quality; + if (new_qual < QUALITY_MIN) + new_qual = QUALITY_MIN; + else if (new_qual > QUALITY_MAX) + new_qual = QUALITY_MAX; + if (new_qual != sd->quality) { + sd->quality = new_qual; + queue_work(sd->work_thread, &sd->work); + } + } + } else { + sd->nchg = 0; + } + sd->pktsz = sd->npkt = 0; + /* if the marker is smaller than 62 bytes, * memorize the number of bytes to skip in the next packet */ if (i + 62 > len) { /* no more usable data */ sd->short_mark = i + 62 - len; return; } - if (sd->ag_cnt >= 0) set_lum(sd, data + i); @@ -2878,22 +2957,6 @@ marker_found: } } -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) - setjpegqual(gspca_dev); - return 0; -} - static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { @@ -2955,10 +3018,10 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, .get_jcomp = sd_get_jcomp, - .set_jcomp = sd_set_jcomp, .querymenu = sd_querymenu, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) .int_pkt_scan = sd_int_pkt_scan, -- cgit v0.10.2 From 804632201f2d749b7cb35fce42fc28552ad595c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 07:44:59 -0300 Subject: [media] gspca - sonixj: Fix start sequence of sensor mt9v111 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 2bfb8df..01ee39b 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -856,18 +856,8 @@ static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */ {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */ {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, /* op mode ctrl */ - {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x03, 0x01, 0xe1, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x04, 0x02, 0x81, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x05, 0x00, 0x04, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */ - {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x03, 0x01, 0xe6, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x04, 0x02, 0x86, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x05, 0x00, 0x04, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */ - {0xb1, 0x5c, 0x0e, 0x00, 0x08, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, /* col start */ {0xb1, 0x5c, 0x03, 0x01, 0xe7, 0x00, 0x00, 0x10}, /* window height */ {0xb1, 0x5c, 0x04, 0x02, 0x87, 0x00, 0x00, 0x10}, /* window width */ @@ -881,15 +871,10 @@ static const u8 mt9v111_sensor_init[][8] = { {} }; static const u8 mt9v111_sensor_param1[][8] = { - {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10}, - {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10}, /* green1 gain */ - {0xd1, 0x5c, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10}, /* red gain */ - /*******/ - {0xb1, 0x5c, 0x06, 0x00, 0x1e, 0x00, 0x00, 0x10}, /* vert blanking */ - {0xb1, 0x5c, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10}, /* horiz blanking */ - {0xd1, 0x5c, 0x2c, 0x00, 0xad, 0x00, 0xad, 0x10}, /* blue gain */ + {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xad, 0x10}, /* G1 and B gains */ + {0xd1, 0x5c, 0x2d, 0x00, 0xad, 0x00, 0x33, 0x10}, /* R and G2 gains */ + {0xb1, 0x5c, 0x06, 0x00, 0x40, 0x00, 0x00, 0x10}, /* vert blanking */ + {0xb1, 0x5c, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10}, /* horiz blanking */ {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */ {} }; -- cgit v0.10.2 From 222036765ccbdb1ff4feef22d8e2fc687a9cdcfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 07:49:32 -0300 Subject: [media] gspca - sonixj: Adjust autogain for sensor mt9v111 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gain/expo adjustment was too long. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 01ee39b..b7a7e96 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1939,10 +1939,10 @@ static u32 setexposure(struct gspca_dev *gspca_dev, u8 expo_c1[] = { 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 }; - if (expo > 0x0280) - expo = 0x0280; - else if (expo < 0x0040) - expo = 0x0040; + if (expo > 0x0390) + expo = 0x0390; + else if (expo < 0x0060) + expo = 0x0060; expo_c1[3] = expo >> 8; expo_c1[4] = expo; i2c_w8(gspca_dev, expo_c1); @@ -1999,10 +1999,13 @@ static void setbrightness(struct gspca_dev *gspca_dev) sd->exposure = setexposure(gspca_dev, expo); break; case SENSOR_GC0307: - case SENSOR_MT9V111: expo = brightness; sd->exposure = setexposure(gspca_dev, expo); return; /* don't set the Y offset */ + case SENSOR_MT9V111: + expo = brightness << 2; + sd->exposure = setexposure(gspca_dev, expo); + return; /* don't set the Y offset */ case SENSOR_OM6802: expo = brightness << 2; sd->exposure = setexposure(gspca_dev, expo); @@ -2750,6 +2753,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) (unsigned int) (expotimes << 8)); break; case SENSOR_OM6802: + case SENSOR_MT9V111: expotimes = sd->exposure; expotimes += (luma_mean - delta) >> 2; if (expotimes < 0) @@ -2762,7 +2766,6 @@ static void do_autogain(struct gspca_dev *gspca_dev) /* case SENSOR_MO4000: */ /* case SENSOR_MI0360: */ /* case SENSOR_MI0360B: */ -/* case SENSOR_MT9V111: */ expotimes = sd->exposure; expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) -- cgit v0.10.2 From e6a92a7445cc33bbfe2775eb50473fb43bb3a2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 07:52:58 -0300 Subject: [media] gspca - sonixj: Simplify GPIO setting when audio present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index b7a7e96..bfc09b9 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1789,7 +1789,7 @@ static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; const u8 *sn9c1xx; - u8 regGpio[] = { 0x29, 0x74 }; /* with audio */ + u8 regGpio[] = { 0x29, 0x70 }; /* no audio */ u8 regF1; /* setup a selector by bridge */ @@ -1801,6 +1801,8 @@ static int sd_init(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) return gspca_dev->usb_err; PDEBUG(D_PROBE, "Sonix chip id: %02x", regF1); + if (gspca_dev->audio) + regGpio[1] |= 0x04; /* with audio */ switch (sd->bridge) { case BRIDGE_SN9C102P: case BRIDGE_SN9C105: @@ -1840,7 +1842,6 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x02, 0x62); break; case BRIDGE_SN9C120: - regGpio[1] = 0x70; /* no audio */ reg_w(gspca_dev, 0x01, regGpio, 2); break; } -- cgit v0.10.2 From 336d23ef88fd71564fb07fdc1d1d871f8e1eeadd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 08:00:40 -0300 Subject: [media] gspca - sonixj: Same init for all bridges but the sn9c102p MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index bfc09b9..571d086 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1835,13 +1835,7 @@ static int sd_init(struct gspca_dev *gspca_dev) case BRIDGE_SN9C102P: reg_w1(gspca_dev, 0x02, regGpio[1]); break; - case BRIDGE_SN9C105: - reg_w(gspca_dev, 0x01, regGpio, 2); - break; - case BRIDGE_SN9C110: - reg_w1(gspca_dev, 0x02, 0x62); - break; - case BRIDGE_SN9C120: + default: reg_w(gspca_dev, 0x01, regGpio, 2); break; } -- cgit v0.10.2 From 5c95606b1d94a6dc62df0af2bc0d3e0e9a2e6cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 08:03:24 -0300 Subject: [media] gspca - sonixj: Set both pins for infrared of mt9v111 webcams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids to define the webcam model. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 571d086..eb7b261 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -29,8 +29,6 @@ MODULE_AUTHOR("Jean-François Moine "); MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); MODULE_LICENSE("GPL"); -static int starcam; - /* controls */ enum e_ctrl { BRIGHTNESS, @@ -2192,14 +2190,11 @@ static void setillum(struct gspca_dev *gspca_dev) sd->ctrls[ILLUM].val ? 0x64 : 0x60); break; case SENSOR_MT9V111: - if (starcam) - reg_w1(gspca_dev, 0x02, - sd->ctrls[ILLUM].val ? - 0x55 : 0x54); /* 370i */ - else - reg_w1(gspca_dev, 0x02, - sd->ctrls[ILLUM].val ? - 0x66 : 0x64); /* Clip */ + reg_w1(gspca_dev, 0x02, + sd->ctrls[ILLUM].val ? 0x77 : 0x74); +/* should have been: */ +/* 0x55 : 0x54); * 370i */ +/* 0x66 : 0x64); * Clip */ break; } } @@ -3114,7 +3109,3 @@ static void __exit sd_mod_exit(void) module_init(sd_mod_init); module_exit(sd_mod_exit); - -module_param(starcam, int, 0644); -MODULE_PARM_DESC(starcam, - "StarCam model. 0: Clip, 1: 370i"); -- cgit v0.10.2 From 062909f64981678899a1abbe16a616b0e92fc615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 08:10:50 -0300 Subject: [media] gspca - sonixj, zc3xx: Let some bandwidth for audio when USB 1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index eb7b261..a1f82a9 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1777,6 +1777,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->ag_cnt = -1; sd->quality = QUALITY_DEF; + /* if USB 1.1, let some bandwidth for the audio device */ + if (gspca_dev->audio && gspca_dev->dev->speed < USB_SPEED_HIGH) + gspca_dev->nbalt--; + INIT_WORK(&sd->work, qual_upd); return 0; diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 47236a5..b201491 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6414,6 +6414,10 @@ static int sd_config(struct gspca_dev *gspca_dev, gspca_dev->cam.ctrls = sd->ctrls; sd->quality = QUALITY_DEF; + /* if USB 1.1, let some bandwidth for the audio device */ + if (gspca_dev->audio && gspca_dev->dev->speed < USB_SPEED_HIGH) + gspca_dev->nbalt--; + return 0; } -- cgit v0.10.2 From 228dd2660c4cee10fda47aa105b0152f8de7a766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 09:34:57 -0300 Subject: [media] gspca - ov534: Use the new control mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 04da228..ee6c1df 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -49,54 +49,51 @@ MODULE_AUTHOR("Antonio Ospite "); MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver"); MODULE_LICENSE("GPL"); +/* controls */ +enum e_ctrl { + BRIGHTNESS, + CONTRAST, + GAIN, + EXPOSURE, + AGC, + AWB, + AEC, + SHARPNESS, + HFLIP, + VFLIP, + LIGHTFREQ, + NCTRLS /* number of controls */ +}; + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct gspca_ctrl ctrls[NCTRLS]; + __u32 last_pts; u16 last_fid; u8 frame_rate; - u8 brightness; - u8 contrast; - u8 gain; - u8 exposure; - u8 agc; - u8 awb; - u8 aec; - s8 sharpness; - u8 hflip; - u8 vflip; - u8 freqfltr; }; /* V4L2 controls supported by the driver */ -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 setbrightness(struct gspca_dev *gspca_dev); +static void setcontrast(struct gspca_dev *gspca_dev); +static void setgain(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); static int sd_setagc(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getagc(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getsharpness(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_setawb(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setaec(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getaec(struct gspca_dev *gspca_dev, __s32 *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_setfreqfltr(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getfreqfltr(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_querymenu(struct gspca_dev *gspca_dev, - struct v4l2_querymenu *menu); +static void setawb(struct gspca_dev *gspca_dev); +static void setaec(struct gspca_dev *gspca_dev); +static void setsharpness(struct gspca_dev *gspca_dev); +static void sethvflip(struct gspca_dev *gspca_dev); +static void setlightfreq(struct gspca_dev *gspca_dev); + +static int sd_start(struct gspca_dev *gspca_dev); +static void sd_stopN(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[] = { - { /* 0 */ +[BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -104,13 +101,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 255, .step = 1, -#define BRIGHTNESS_DEF 0 - .default_value = BRIGHTNESS_DEF, + .default_value = 0, }, - .set = sd_setbrightness, - .get = sd_getbrightness, + .set_control = setbrightness }, - { /* 1 */ +[CONTRAST] = { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, @@ -118,13 +113,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 255, .step = 1, -#define CONTRAST_DEF 32 - .default_value = CONTRAST_DEF, + .default_value = 32, }, - .set = sd_setcontrast, - .get = sd_getcontrast, + .set_control = setcontrast }, - { /* 2 */ +[GAIN] = { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, @@ -132,13 +125,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 63, .step = 1, -#define GAIN_DEF 20 - .default_value = GAIN_DEF, + .default_value = 20, }, - .set = sd_setgain, - .get = sd_getgain, + .set_control = setgain }, - { /* 3 */ +[EXPOSURE] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -146,13 +137,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 255, .step = 1, -#define EXPO_DEF 120 - .default_value = EXPO_DEF, + .default_value = 120, }, - .set = sd_setexposure, - .get = sd_getexposure, + .set_control = setexposure }, - { /* 4 */ +[AGC] = { { .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -160,14 +149,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define AGC_DEF 1 - .default_value = AGC_DEF, + .default_value = 1, }, - .set = sd_setagc, - .get = sd_getagc, + .set = sd_setagc }, -#define AWB_IDX 5 - { /* 5 */ +[AWB] = { { .id = V4L2_CID_AUTO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -175,13 +161,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define AWB_DEF 1 - .default_value = AWB_DEF, + .default_value = 1, }, - .set = sd_setawb, - .get = sd_getawb, + .set_control = setawb }, - { /* 6 */ +[AEC] = { { .id = V4L2_CID_EXPOSURE_AUTO, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -189,13 +173,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define AEC_DEF 1 - .default_value = AEC_DEF, + .default_value = 1, }, - .set = sd_setaec, - .get = sd_getaec, + .set_control = setaec }, - { /* 7 */ +[SHARPNESS] = { { .id = V4L2_CID_SHARPNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -203,13 +185,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 63, .step = 1, -#define SHARPNESS_DEF 0 - .default_value = SHARPNESS_DEF, + .default_value = 0, }, - .set = sd_setsharpness, - .get = sd_getsharpness, + .set_control = setsharpness }, - { /* 8 */ +[HFLIP] = { { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -217,13 +197,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 }, - { /* 9 */ +[VFLIP] = { { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -231,13 +209,11 @@ 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 }, - { /* 10 */ +[LIGHTFREQ] = { { .id = V4L2_CID_POWER_LINE_FREQUENCY, .type = V4L2_CTRL_TYPE_MENU, @@ -245,11 +221,9 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define FREQFLTR_DEF 0 - .default_value = FREQFLTR_DEF, + .default_value = 0, }, - .set = sd_setfreqfltr, - .get = sd_getfreqfltr, + .set_control = setlightfreq }, }; @@ -670,14 +644,14 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sccb_reg_write(gspca_dev, 0x9b, sd->brightness); + sccb_reg_write(gspca_dev, 0x9b, sd->ctrls[BRIGHTNESS].val); } static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sccb_reg_write(gspca_dev, 0x9c, sd->contrast); + sccb_reg_write(gspca_dev, 0x9c, sd->ctrls[CONTRAST].val); } static void setgain(struct gspca_dev *gspca_dev) @@ -685,10 +659,10 @@ static void setgain(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; - if (sd->agc) + if (sd->ctrls[AGC].val) return; - val = sd->gain; + val = sd->ctrls[GAIN].val; switch (val & 0x30) { case 0x00: val &= 0x0f; @@ -715,7 +689,7 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; - if (sd->aec) + if (sd->ctrls[AEC].val) return; /* 'val' is one byte and represents half of the exposure value we are @@ -724,7 +698,7 @@ static void setexposure(struct gspca_dev *gspca_dev) * MSB: ((u16) val << 1) >> 8 == val >> 7 * LSB: ((u16) val << 1) & 0xff == val << 1 */ - val = sd->exposure; + val = sd->ctrls[EXPOSURE].val; sccb_reg_write(gspca_dev, 0x08, val >> 7); sccb_reg_write(gspca_dev, 0x10, val << 1); } @@ -733,7 +707,7 @@ static void setagc(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->agc) { + if (sd->ctrls[AGC].val) { sccb_reg_write(gspca_dev, 0x13, sccb_reg_read(gspca_dev, 0x13) | 0x04); sccb_reg_write(gspca_dev, 0x64, @@ -752,7 +726,7 @@ static void setawb(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->awb) { + if (sd->ctrls[AWB].val) { sccb_reg_write(gspca_dev, 0x13, sccb_reg_read(gspca_dev, 0x13) | 0x02); sccb_reg_write(gspca_dev, 0x63, @@ -769,7 +743,7 @@ static void setaec(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->aec) + if (sd->ctrls[AEC].val) sccb_reg_write(gspca_dev, 0x13, sccb_reg_read(gspca_dev, 0x13) | 0x01); else { @@ -784,40 +758,30 @@ static void setsharpness(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; - val = sd->sharpness; + val = sd->ctrls[SHARPNESS].val; sccb_reg_write(gspca_dev, 0x91, val); /* Auto de-noise threshold */ sccb_reg_write(gspca_dev, 0x8e, val); /* De-noise threshold */ } -static void sethflip(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->hflip == 0) - sccb_reg_write(gspca_dev, 0x0c, - sccb_reg_read(gspca_dev, 0x0c) | 0x40); - else - sccb_reg_write(gspca_dev, 0x0c, - sccb_reg_read(gspca_dev, 0x0c) & ~0x40); -} - -static void setvflip(struct gspca_dev *gspca_dev) +static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 val; - if (sd->vflip == 0) - sccb_reg_write(gspca_dev, 0x0c, - sccb_reg_read(gspca_dev, 0x0c) | 0x80); - else - sccb_reg_write(gspca_dev, 0x0c, - sccb_reg_read(gspca_dev, 0x0c) & ~0x80); + val = sccb_reg_read(gspca_dev, 0x0c); + val &= ~0xc0; + if (sd->ctrls[HFLIP].val == 0) + val |= 0x40; + if (sd->ctrls[VFLIP].val == 0) + val |= 0x80; + sccb_reg_write(gspca_dev, 0x0c, val); } -static void setfreqfltr(struct gspca_dev *gspca_dev) +static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->freqfltr == 0) + if (sd->ctrls[LIGHTFREQ].val == 0) sccb_reg_write(gspca_dev, 0x2b, 0x00); else sccb_reg_write(gspca_dev, 0x2b, 0x9e); @@ -833,6 +797,12 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; + cam->ctrls = sd->ctrls; + + /* the auto white balance control works only when auto gain is set */ + if (sd_ctrls[AGC].qctrl.default_value == 0) + gspca_dev->ctrl_inac |= (1 << AWB); + cam->cam_mode = ov772x_mode; cam->nmodes = ARRAY_SIZE(ov772x_mode); cam->mode_framerates = ov772x_framerates; @@ -843,22 +813,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->frame_rate = 30; - sd->brightness = BRIGHTNESS_DEF; - sd->contrast = CONTRAST_DEF; - sd->gain = GAIN_DEF; - sd->exposure = EXPO_DEF; -#if AGC_DEF != 0 - sd->agc = AGC_DEF; -#else - gspca_dev->ctrl_inac |= (1 << AWB_IDX); -#endif - sd->awb = AWB_DEF; - sd->aec = AEC_DEF; - sd->sharpness = SHARPNESS_DEF; - sd->hflip = HFLIP_DEF; - sd->vflip = VFLIP_DEF; - sd->freqfltr = FREQFLTR_DEF; - return 0; } @@ -917,17 +871,19 @@ static int sd_start(struct gspca_dev *gspca_dev) } set_frame_rate(gspca_dev); - setagc(gspca_dev); + if (!(gspca_dev->ctrl_dis & (1 << AGC))) + setagc(gspca_dev); setawb(gspca_dev); setaec(gspca_dev); - setgain(gspca_dev); + if (!(gspca_dev->ctrl_dis & (1 << GAIN))) + setgain(gspca_dev); setexposure(gspca_dev); setbrightness(gspca_dev); setcontrast(gspca_dev); - setsharpness(gspca_dev); - setvflip(gspca_dev); - sethflip(gspca_dev); - setfreqfltr(gspca_dev); + if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS))) + setsharpness(gspca_dev); + sethvflip(gspca_dev); + setlightfreq(gspca_dev); ov534_set_led(gspca_dev, 1); ov534_reg_write(gspca_dev, 0xe0, 0x00); @@ -1026,212 +982,27 @@ scan_next: } while (remaining_len > 0); } -/* controls */ -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - setgain(gspca_dev); - return 0; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->gain; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - setexposure(gspca_dev); - return 0; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->exposure; - return 0; -} - -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightness(gspca_dev); - return 0; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; -} - -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); - return 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_setagc(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->agc = val; + sd->ctrls[AGC].val = val; - if (gspca_dev->streaming) { - - /* the auto white balance control works only - * when auto gain is set */ - if (val) - gspca_dev->ctrl_inac &= ~(1 << AWB_IDX); - else - gspca_dev->ctrl_inac |= (1 << AWB_IDX); - setagc(gspca_dev); + /* the auto white balance control works only + * when auto gain is set */ + if (val) { + gspca_dev->ctrl_inac &= ~(1 << AWB); + } else { + gspca_dev->ctrl_inac |= (1 << AWB); + if (sd->ctrls[AWB].val) { + sd->ctrls[AWB].val = 0; + if (gspca_dev->streaming) + setawb(gspca_dev); + } } - return 0; -} - -static int sd_getagc(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->agc; - return 0; -} - -static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->awb = val; - if (gspca_dev->streaming) - setawb(gspca_dev); - return 0; -} - -static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->awb; - return 0; -} - -static int sd_setaec(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->aec = val; - if (gspca_dev->streaming) - setaec(gspca_dev); - return 0; -} - -static int sd_getaec(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->aec; - return 0; -} - -static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->sharpness = val; - if (gspca_dev->streaming) - setsharpness(gspca_dev); - return 0; -} - -static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->sharpness; - 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) - sethflip(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) - setvflip(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_setfreqfltr(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->freqfltr = val; if (gspca_dev->streaming) - setfreqfltr(gspca_dev); - return 0; -} - -static int sd_getfreqfltr(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->freqfltr; - return 0; + setagc(gspca_dev); + return gspca_dev->usb_err; } static int sd_querymenu(struct gspca_dev *gspca_dev, -- cgit v0.10.2 From 458efe2d558b51fff38026e8ede9374899340e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 10:11:04 -0300 Subject: [media] gspca - ov534: Add the webcam 06f8:3002 and sensor ov767x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 261776e..dc72fff 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -199,6 +199,7 @@ spca500 06bd:0404 Agfa CL20 spca500 06be:0800 Optimedia sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom spca506 06e1:a190 ADS Instant VCD +ov534 06f8:3002 Hercules Blog Webcam ov534_9 06f8:3003 Hercules Dualpix HD Weblog sonixj 06f8:3004 Hercules Classic Silver sonixj 06f8:3008 Hercules Deluxe Optical Glass diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index ee6c1df..8dada2b 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -1,5 +1,5 @@ /* - * ov534-ov772x gspca driver + * ov534-ov7xxx gspca driver * * Copyright (C) 2008 Antonio Ospite * Copyright (C) 2008 Jim Paris @@ -75,6 +75,12 @@ struct sd { u16 last_fid; u8 frame_rate; + u8 sensor; +}; +enum sensors { + SENSOR_OV767x, + SENSOR_OV772x, + NSENSORS }; /* V4L2 controls supported by the driver */ @@ -239,6 +245,16 @@ static const struct v4l2_pix_format ov772x_mode[] = { .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; +static const struct v4l2_pix_format ov767x_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; static const u8 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30}; static const u8 vga_rates[] = {60, 50, 40, 30, 15}; @@ -254,7 +270,288 @@ static const struct framerates ov772x_framerates[] = { }, }; -static const u8 bridge_init[][2] = { +struct reg_array { + const u8 (*val)[2]; + int len; +}; + +static const u8 bridge_init_767x[][2] = { +/* comments from the ms-win file apollo7670.set */ +/* str1 */ + {0xf1, 0x42}, + {0x88, 0xf8}, + {0x89, 0xff}, + {0x76, 0x03}, + {0x92, 0x03}, + {0x95, 0x10}, + {0xe2, 0x00}, + {0xe7, 0x3e}, + {0x8d, 0x1c}, + {0x8e, 0x00}, + {0x8f, 0x00}, + {0x1f, 0x00}, + {0xc3, 0xf9}, + {0x89, 0xff}, + {0x88, 0xf8}, + {0x76, 0x03}, + {0x92, 0x01}, + {0x93, 0x18}, + {0x1c, 0x00}, + {0x1d, 0x48}, + {0x1d, 0x00}, + {0x1d, 0xff}, + {0x1d, 0x02}, + {0x1d, 0x58}, + {0x1d, 0x00}, + {0x1c, 0x0a}, + {0x1d, 0x0a}, + {0x1d, 0x0e}, + {0xc0, 0x50}, /* HSize 640 */ + {0xc1, 0x3c}, /* VSize 480 */ + {0x34, 0x05}, /* enable Audio Suspend mode */ + {0xc2, 0x0c}, /* Input YUV */ + {0xc3, 0xf9}, /* enable PRE */ + {0x34, 0x05}, /* enable Audio Suspend mode */ + {0xe7, 0x2e}, /* this solves failure of "SuspendResumeTest" */ + {0x31, 0xf9}, /* enable 1.8V Suspend */ + {0x35, 0x02}, /* turn on JPEG */ + {0xd9, 0x10}, + {0x25, 0x42}, /* GPIO[8]:Input */ + {0x94, 0x11}, /* If the default setting is loaded when + * system boots up, this flag is closed here */ +}; +static const u8 sensor_init_767x[][2] = { + {0x12, 0x80}, + {0x11, 0x03}, + {0x3a, 0x04}, + {0x12, 0x00}, + {0x17, 0x13}, + {0x18, 0x01}, + {0x32, 0xb6}, + {0x19, 0x02}, + {0x1a, 0x7a}, + {0x03, 0x0a}, + {0x0c, 0x00}, + {0x3e, 0x00}, + {0x70, 0x3a}, + {0x71, 0x35}, + {0x72, 0x11}, + {0x73, 0xf0}, + {0xa2, 0x02}, + {0x7a, 0x2a}, /* set Gamma=1.6 below */ + {0x7b, 0x12}, + {0x7c, 0x1d}, + {0x7d, 0x2d}, + {0x7e, 0x45}, + {0x7f, 0x50}, + {0x80, 0x59}, + {0x81, 0x62}, + {0x82, 0x6b}, + {0x83, 0x73}, + {0x84, 0x7b}, + {0x85, 0x8a}, + {0x86, 0x98}, + {0x87, 0xb2}, + {0x88, 0xca}, + {0x89, 0xe0}, + {0x13, 0xe0}, + {0x00, 0x00}, + {0x10, 0x00}, + {0x0d, 0x40}, + {0x14, 0x38}, /* gain max 16x */ + {0xa5, 0x05}, + {0xab, 0x07}, + {0x24, 0x95}, + {0x25, 0x33}, + {0x26, 0xe3}, + {0x9f, 0x78}, + {0xa0, 0x68}, + {0xa1, 0x03}, + {0xa6, 0xd8}, + {0xa7, 0xd8}, + {0xa8, 0xf0}, + {0xa9, 0x90}, + {0xaa, 0x94}, + {0x13, 0xe5}, + {0x0e, 0x61}, + {0x0f, 0x4b}, + {0x16, 0x02}, + {0x21, 0x02}, + {0x22, 0x91}, + {0x29, 0x07}, + {0x33, 0x0b}, + {0x35, 0x0b}, + {0x37, 0x1d}, + {0x38, 0x71}, + {0x39, 0x2a}, + {0x3c, 0x78}, + {0x4d, 0x40}, + {0x4e, 0x20}, + {0x69, 0x00}, + {0x6b, 0x4a}, + {0x74, 0x10}, + {0x8d, 0x4f}, + {0x8e, 0x00}, + {0x8f, 0x00}, + {0x90, 0x00}, + {0x91, 0x00}, + {0x96, 0x00}, + {0x9a, 0x80}, + {0xb0, 0x84}, + {0xb1, 0x0c}, + {0xb2, 0x0e}, + {0xb3, 0x82}, + {0xb8, 0x0a}, + {0x43, 0x0a}, + {0x44, 0xf0}, + {0x45, 0x34}, + {0x46, 0x58}, + {0x47, 0x28}, + {0x48, 0x3a}, + {0x59, 0x88}, + {0x5a, 0x88}, + {0x5b, 0x44}, + {0x5c, 0x67}, + {0x5d, 0x49}, + {0x5e, 0x0e}, + {0x6c, 0x0a}, + {0x6d, 0x55}, + {0x6e, 0x11}, + {0x6f, 0x9f}, + {0x6a, 0x40}, + {0x01, 0x40}, + {0x02, 0x40}, + {0x13, 0xe7}, + {0x4f, 0x80}, + {0x50, 0x80}, + {0x51, 0x00}, + {0x52, 0x22}, + {0x53, 0x5e}, + {0x54, 0x80}, + {0x58, 0x9e}, + {0x41, 0x08}, + {0x3f, 0x00}, + {0x75, 0x04}, + {0x76, 0xe1}, + {0x4c, 0x00}, + {0x77, 0x01}, + {0x3d, 0xc2}, + {0x4b, 0x09}, + {0xc9, 0x60}, + {0x41, 0x38}, /* jfm: auto sharpness + auto de-noise */ + {0x56, 0x40}, + {0x34, 0x11}, + {0x3b, 0xc2}, + {0xa4, 0x8a}, /* Night mode trigger point */ + {0x96, 0x00}, + {0x97, 0x30}, + {0x98, 0x20}, + {0x99, 0x20}, + {0x9a, 0x84}, + {0x9b, 0x29}, + {0x9c, 0x03}, + {0x9d, 0x4c}, + {0x9e, 0x3f}, + {0x78, 0x04}, + {0x79, 0x01}, + {0xc8, 0xf0}, + {0x79, 0x0f}, + {0xc8, 0x00}, + {0x79, 0x10}, + {0xc8, 0x7e}, + {0x79, 0x0a}, + {0xc8, 0x80}, + {0x79, 0x0b}, + {0xc8, 0x01}, + {0x79, 0x0c}, + {0xc8, 0x0f}, + {0x79, 0x0d}, + {0xc8, 0x20}, + {0x79, 0x09}, + {0xc8, 0x80}, + {0x79, 0x02}, + {0xc8, 0xc0}, + {0x79, 0x03}, + {0xc8, 0x20}, + {0x79, 0x26}, +}; +static const u8 bridge_start_vga_767x[][2] = { +/* str59 JPG */ + {0x94, 0xaa}, + {0xf1, 0x42}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0xc2, 0x0c}, + {0x35, 0x02}, /* turn on JPEG */ + {0xd9, 0x10}, + {0xda, 0x00}, /* for higher clock rate(30fps) */ + {0x34, 0x05}, /* enable Audio Suspend mode */ + {0xc3, 0xf9}, /* enable PRE */ + {0x8c, 0x00}, /* CIF VSize LSB[2:0] */ + {0x8d, 0x1c}, /* output YUV */ +/* {0x34, 0x05}, * enable Audio Suspend mode (?) */ + {0x50, 0x00}, /* H/V divider=0 */ + {0x51, 0xa0}, /* input H=640/4 */ + {0x52, 0x3c}, /* input V=480/4 */ + {0x53, 0x00}, /* offset X=0 */ + {0x54, 0x00}, /* offset Y=0 */ + {0x55, 0x00}, /* H/V size[8]=0 */ + {0x57, 0x00}, /* H-size[9]=0 */ + {0x5c, 0x00}, /* output size[9:8]=0 */ + {0x5a, 0xa0}, /* output H=640/4 */ + {0x5b, 0x78}, /* output V=480/4 */ + {0x1c, 0x0a}, + {0x1d, 0x0a}, + {0x94, 0x11}, +}; +static const u8 sensor_start_vga_767x[][2] = { + {0x11, 0x01}, + {0x1e, 0x04}, + {0x19, 0x02}, + {0x1a, 0x7a}, +}; +static const u8 bridge_start_qvga_767x[][2] = { +/* str86 JPG */ + {0x94, 0xaa}, + {0xf1, 0x42}, + {0xe5, 0x04}, + {0xc0, 0x80}, + {0xc1, 0x60}, + {0xc2, 0x0c}, + {0x35, 0x02}, /* turn on JPEG */ + {0xd9, 0x10}, + {0xc0, 0x50}, /* CIF HSize 640 */ + {0xc1, 0x3c}, /* CIF VSize 480 */ + {0x8c, 0x00}, /* CIF VSize LSB[2:0] */ + {0x8d, 0x1c}, /* output YUV */ + {0x34, 0x05}, /* enable Audio Suspend mode */ + {0xc2, 0x4c}, /* output YUV and Enable DCW */ + {0xc3, 0xf9}, /* enable PRE */ + {0x1c, 0x00}, /* indirect addressing */ + {0x1d, 0x48}, /* output YUV422 */ + {0x50, 0x89}, /* H/V divider=/2; plus DCW AVG */ + {0x51, 0xa0}, /* DCW input H=640/4 */ + {0x52, 0x78}, /* DCW input V=480/4 */ + {0x53, 0x00}, /* offset X=0 */ + {0x54, 0x00}, /* offset Y=0 */ + {0x55, 0x00}, /* H/V size[8]=0 */ + {0x57, 0x00}, /* H-size[9]=0 */ + {0x5c, 0x00}, /* DCW output size[9:8]=0 */ + {0x5a, 0x50}, /* DCW output H=320/4 */ + {0x5b, 0x3c}, /* DCW output V=240/4 */ + {0x1c, 0x0a}, + {0x1d, 0x0a}, + {0x94, 0x11}, +}; +static const u8 sensor_start_qvga_767x[][2] = { + {0x11, 0x01}, + {0x1e, 0x04}, + {0x19, 0x02}, + {0x1a, 0x7a}, +}; + +static const u8 bridge_init_772x[][2] = { { 0xc2, 0x0c }, { 0x88, 0xf8 }, { 0xc3, 0x69 }, @@ -312,7 +609,7 @@ static const u8 bridge_init[][2] = { { 0xc1, 0x3c }, { 0xc2, 0x0c }, }; -static const u8 sensor_init[][2] = { +static const u8 sensor_init_772x[][2] = { { 0x12, 0x80 }, { 0x11, 0x01 }, /*fixme: better have a delay?*/ @@ -405,7 +702,7 @@ static const u8 sensor_init[][2] = { { 0x8e, 0x00 }, /* De-noise threshold */ { 0x0c, 0xd0 } }; -static const u8 bridge_start_vga[][2] = { +static const u8 bridge_start_vga_772x[][2] = { {0x1c, 0x00}, {0x1d, 0x40}, {0x1d, 0x02}, @@ -416,7 +713,7 @@ static const u8 bridge_start_vga[][2] = { {0xc0, 0x50}, {0xc1, 0x3c}, }; -static const u8 sensor_start_vga[][2] = { +static const u8 sensor_start_vga_772x[][2] = { {0x12, 0x00}, {0x17, 0x26}, {0x18, 0xa0}, @@ -426,7 +723,7 @@ static const u8 sensor_start_vga[][2] = { {0x2c, 0xf0}, {0x65, 0x20}, }; -static const u8 bridge_start_qvga[][2] = { +static const u8 bridge_start_qvga_772x[][2] = { {0x1c, 0x00}, {0x1d, 0x40}, {0x1d, 0x02}, @@ -437,7 +734,7 @@ static const u8 bridge_start_qvga[][2] = { {0xc0, 0x28}, {0xc1, 0x1e}, }; -static const u8 sensor_start_qvga[][2] = { +static const u8 sensor_start_qvga_772x[][2] = { {0x12, 0x40}, {0x17, 0x3f}, {0x18, 0x50}, @@ -620,6 +917,8 @@ static void set_frame_rate(struct gspca_dev *gspca_dev) {30, 0x04, 0x41, 0x04}, }; + if (sd->sensor != SENSOR_OV772x) + return; if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) { r = rate_0; i = ARRAY_SIZE(rate_0); @@ -643,15 +942,28 @@ static void set_frame_rate(struct gspca_dev *gspca_dev) static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int val; - sccb_reg_write(gspca_dev, 0x9b, sd->ctrls[BRIGHTNESS].val); + val = sd->ctrls[BRIGHTNESS].val; + if (sd->sensor == SENSOR_OV767x) { + if (val < 0) + val = 0x80 - val; + sccb_reg_write(gspca_dev, 0x55, val); /* bright */ + } else { + sccb_reg_write(gspca_dev, 0x9b, val); + } } static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 val; - sccb_reg_write(gspca_dev, 0x9c, sd->ctrls[CONTRAST].val); + val = sd->ctrls[CONTRAST].val; + if (sd->sensor == SENSOR_OV767x) + sccb_reg_write(gspca_dev, 0x56, val); /* contras */ + else + sccb_reg_write(gspca_dev, 0x9c, val); } static void setgain(struct gspca_dev *gspca_dev) @@ -692,15 +1004,22 @@ static void setexposure(struct gspca_dev *gspca_dev) if (sd->ctrls[AEC].val) return; - /* 'val' is one byte and represents half of the exposure value we are - * going to set into registers, a two bytes value: - * - * MSB: ((u16) val << 1) >> 8 == val >> 7 - * LSB: ((u16) val << 1) & 0xff == val << 1 - */ val = sd->ctrls[EXPOSURE].val; - sccb_reg_write(gspca_dev, 0x08, val >> 7); - sccb_reg_write(gspca_dev, 0x10, val << 1); + if (sd->sensor == SENSOR_OV767x) { + + /* set only aec[9:2] */ + sccb_reg_write(gspca_dev, 0x10, val); /* aech */ + } else { + + /* 'val' is one byte and represents half of the exposure value + * we are going to set into registers, a two bytes value: + * + * MSB: ((u16) val << 1) >> 8 == val >> 7 + * LSB: ((u16) val << 1) & 0xff == val << 1 + */ + sccb_reg_write(gspca_dev, 0x08, val >> 7); + sccb_reg_write(gspca_dev, 0x10, val << 1); + } } static void setagc(struct gspca_dev *gspca_dev) @@ -729,12 +1048,14 @@ static void setawb(struct gspca_dev *gspca_dev) if (sd->ctrls[AWB].val) { sccb_reg_write(gspca_dev, 0x13, sccb_reg_read(gspca_dev, 0x13) | 0x02); - sccb_reg_write(gspca_dev, 0x63, + if (sd->sensor == SENSOR_OV772x) + sccb_reg_write(gspca_dev, 0x63, sccb_reg_read(gspca_dev, 0x63) | 0xc0); } else { sccb_reg_write(gspca_dev, 0x13, sccb_reg_read(gspca_dev, 0x13) & ~0x02); - sccb_reg_write(gspca_dev, 0x63, + if (sd->sensor == SENSOR_OV772x) + sccb_reg_write(gspca_dev, 0x63, sccb_reg_read(gspca_dev, 0x63) & ~0xc0); } } @@ -742,14 +1063,22 @@ static void setawb(struct gspca_dev *gspca_dev) static void setaec(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 data; + data = sd->sensor == SENSOR_OV767x ? + 0x05 : /* agc + aec */ + 0x01; /* agc */ if (sd->ctrls[AEC].val) sccb_reg_write(gspca_dev, 0x13, - sccb_reg_read(gspca_dev, 0x13) | 0x01); + sccb_reg_read(gspca_dev, 0x13) | data); else { sccb_reg_write(gspca_dev, 0x13, - sccb_reg_read(gspca_dev, 0x13) & ~0x01); - setexposure(gspca_dev); + sccb_reg_read(gspca_dev, 0x13) & ~data); + if (sd->sensor == SENSOR_OV767x) + sd->ctrls[EXPOSURE].val = + sccb_reg_read(gspca_dev, 10); /* aech */ + else + setexposure(gspca_dev); } } @@ -768,23 +1097,37 @@ static void sethvflip(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; - val = sccb_reg_read(gspca_dev, 0x0c); - val &= ~0xc0; - if (sd->ctrls[HFLIP].val == 0) - val |= 0x40; - if (sd->ctrls[VFLIP].val == 0) - val |= 0x80; - sccb_reg_write(gspca_dev, 0x0c, val); + if (sd->sensor == SENSOR_OV767x) { + val = sccb_reg_read(gspca_dev, 0x1e); /* mvfp */ + val &= ~0x30; + if (sd->ctrls[HFLIP].val) + val |= 0x20; + if (sd->ctrls[VFLIP].val) + val |= 0x10; + sccb_reg_write(gspca_dev, 0x1e, val); + } else { + val = sccb_reg_read(gspca_dev, 0x0c); + val &= ~0xc0; + if (sd->ctrls[HFLIP].val == 0) + val |= 0x40; + if (sd->ctrls[VFLIP].val == 0) + val |= 0x80; + sccb_reg_write(gspca_dev, 0x0c, val); + } } static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 val; - if (sd->ctrls[LIGHTFREQ].val == 0) - sccb_reg_write(gspca_dev, 0x2b, 0x00); - else - sccb_reg_write(gspca_dev, 0x2b, 0x9e); + val = sd->ctrls[LIGHTFREQ].val ? 0x9e : 0x00; + if (sd->sensor == SENSOR_OV767x) { + sccb_reg_write(gspca_dev, 0x2a, 0x00); + if (val) + val = 0x9d; /* insert dummy to 25fps for 50Hz */ + } + sccb_reg_write(gspca_dev, 0x2b, val); } @@ -805,11 +1148,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = ov772x_mode; cam->nmodes = ARRAY_SIZE(ov772x_mode); - cam->mode_framerates = ov772x_framerates; - - cam->bulk = 1; - cam->bulk_size = 16384; - cam->bulk_nurbs = 2; sd->frame_rate = 30; @@ -819,7 +1157,16 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u16 sensor_id; + static const struct reg_array bridge_init[NSENSORS] = { + [SENSOR_OV767x] = {bridge_init_767x, ARRAY_SIZE(bridge_init_767x)}, + [SENSOR_OV772x] = {bridge_init_772x, ARRAY_SIZE(bridge_init_772x)}, + }; + static const struct reg_array sensor_init[NSENSORS] = { + [SENSOR_OV767x] = {sensor_init_767x, ARRAY_SIZE(sensor_init_767x)}, + [SENSOR_OV772x] = {sensor_init_772x, ARRAY_SIZE(sensor_init_772x)}, + }; /* reset bridge */ ov534_reg_write(gspca_dev, 0xe7, 0x3a); @@ -840,35 +1187,82 @@ static int sd_init(struct gspca_dev *gspca_dev) sensor_id |= sccb_reg_read(gspca_dev, 0x0b); PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); + if ((sensor_id & 0xfff0) == 0x7670) { + sd->sensor = SENSOR_OV767x; + gspca_dev->ctrl_dis = (1 << GAIN) | + (1 << AGC) | + (1 << SHARPNESS); /* auto */ + sd->ctrls[BRIGHTNESS].min = -127; + sd->ctrls[BRIGHTNESS].max = 127; + sd->ctrls[BRIGHTNESS].def = 0; + sd->ctrls[CONTRAST].max = 0x80; + sd->ctrls[CONTRAST].def = 0x40; + sd->ctrls[EXPOSURE].min = 0x08; + sd->ctrls[EXPOSURE].max = 0x60; + sd->ctrls[EXPOSURE].def = 0x13; + sd->ctrls[SHARPNESS].max = 9; + sd->ctrls[SHARPNESS].def = 4; + sd->ctrls[HFLIP].def = 1; + gspca_dev->cam.cam_mode = ov767x_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode); + } else { + sd->sensor = SENSOR_OV772x; + gspca_dev->cam.bulk = 1; + gspca_dev->cam.bulk_size = 16384; + gspca_dev->cam.bulk_nurbs = 2; + gspca_dev->cam.mode_framerates = ov772x_framerates; + } + /* initialize */ - reg_w_array(gspca_dev, bridge_init, - ARRAY_SIZE(bridge_init)); + reg_w_array(gspca_dev, bridge_init[sd->sensor].val, + bridge_init[sd->sensor].len); ov534_set_led(gspca_dev, 1); - sccb_w_array(gspca_dev, sensor_init, - ARRAY_SIZE(sensor_init)); - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); - set_frame_rate(gspca_dev); + sccb_w_array(gspca_dev, sensor_init[sd->sensor].val, + sensor_init[sd->sensor].len); + if (sd->sensor == SENSOR_OV767x) + sd_start(gspca_dev); + sd_stopN(gspca_dev); +/* set_frame_rate(gspca_dev); */ return gspca_dev->usb_err; } static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int mode; + static const struct reg_array bridge_start[NSENSORS][2] = { + [SENSOR_OV767x] = {{bridge_start_qvga_767x, + ARRAY_SIZE(bridge_start_qvga_767x)}, + {bridge_start_vga_767x, + ARRAY_SIZE(bridge_start_vga_767x)}}, + [SENSOR_OV772x] = {{bridge_start_qvga_772x, + ARRAY_SIZE(bridge_start_qvga_772x)}, + {bridge_start_vga_772x, + ARRAY_SIZE(bridge_start_vga_772x)}}, + }; + static const struct reg_array sensor_start[NSENSORS][2] = { + [SENSOR_OV767x] = {{sensor_start_qvga_767x, + ARRAY_SIZE(sensor_start_qvga_767x)}, + {sensor_start_vga_767x, + ARRAY_SIZE(sensor_start_vga_767x)}}, + [SENSOR_OV772x] = {{sensor_start_qvga_772x, + ARRAY_SIZE(sensor_start_qvga_772x)}, + {sensor_start_vga_772x, + ARRAY_SIZE(sensor_start_vga_772x)}}, + }; + + /* (from ms-win trace) */ + if (sd->sensor == SENSOR_OV767x) + sccb_reg_write(gspca_dev, 0x1e, 0x04); + /* black sun enable ? */ + + mode = gspca_dev->curr_mode; /* 0: 320x240, 1: 640x480 */ + reg_w_array(gspca_dev, bridge_start[sd->sensor][mode].val, + bridge_start[sd->sensor][mode].len); + sccb_w_array(gspca_dev, sensor_start[sd->sensor][mode].val, + sensor_start[sd->sensor][mode].len); - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - if (mode != 0) { /* 320x240 */ - reg_w_array(gspca_dev, bridge_start_qvga, - ARRAY_SIZE(bridge_start_qvga)); - sccb_w_array(gspca_dev, sensor_start_qvga, - ARRAY_SIZE(sensor_start_qvga)); - } else { /* 640x480 */ - reg_w_array(gspca_dev, bridge_start_vga, - ARRAY_SIZE(bridge_start_vga)); - sccb_w_array(gspca_dev, sensor_start_vga, - ARRAY_SIZE(sensor_start_vga)); - } set_frame_rate(gspca_dev); if (!(gspca_dev->ctrl_dis & (1 << AGC))) @@ -913,9 +1307,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u32 this_pts; u16 this_fid; int remaining_len = len; + int payload_len; + payload_len = gspca_dev->cam.bulk ? 2048 : 2040; do { - len = min(remaining_len, 2048); + len = min(remaining_len, payload_len); /* Payloads are prefixed with a UVC-style header. We consider a frame to start when the FID toggles, or the PTS @@ -955,8 +1351,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* If this packet is marked as EOF, end the frame */ } else if (data[1] & UVC_STREAM_EOF) { sd->last_pts = 0; - if (gspca_dev->image_len + len - 12 != - gspca_dev->width * gspca_dev->height * 2) { + if (gspca_dev->pixfmt == V4L2_PIX_FMT_YUYV + && gspca_dev->image_len + len - 12 != + gspca_dev->width * gspca_dev->height * 2) { PDEBUG(D_PACK, "wrong sized frame"); goto discard; } @@ -1073,6 +1470,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x1415, 0x2000)}, + {USB_DEVICE(0x06f8, 0x3002)}, {} }; -- cgit v0.10.2 From d57747f1e738cf1aa4a743ea10e0fb640b502c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 10:15:45 -0300 Subject: [media] gspca - ov534: Add saturation control for ov767x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 8dada2b..0c6369b 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -61,6 +61,7 @@ enum e_ctrl { SHARPNESS, HFLIP, VFLIP, + COLORS, LIGHTFREQ, NCTRLS /* number of controls */ }; @@ -93,6 +94,7 @@ static void setawb(struct gspca_dev *gspca_dev); static void setaec(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); static void sethvflip(struct gspca_dev *gspca_dev); +static void setcolors(struct gspca_dev *gspca_dev); static void setlightfreq(struct gspca_dev *gspca_dev); static int sd_start(struct gspca_dev *gspca_dev); @@ -219,6 +221,18 @@ static const struct ctrl sd_ctrls[] = { }, .set_control = sethvflip }, +[COLORS] = { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 6, + .step = 1, + .default_value = 3, + }, + .set_control = setcolors + }, [LIGHTFREQ] = { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -1116,6 +1130,26 @@ static void sethvflip(struct gspca_dev *gspca_dev) } } +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + int i; + static u8 color_tb[][6] = { + {0x42, 0x42, 0x00, 0x11, 0x30, 0x41}, + {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52}, + {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66}, + {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80}, + {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a}, + {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8}, + {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd}, + }; + + val = sd->ctrls[COLORS].val; + for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++) + sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]); +} + static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1207,6 +1241,7 @@ static int sd_init(struct gspca_dev *gspca_dev) gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode); } else { sd->sensor = SENSOR_OV772x; + gspca_dev->ctrl_dis = (1 << COLORS); gspca_dev->cam.bulk = 1; gspca_dev->cam.bulk_size = 16384; gspca_dev->cam.bulk_nurbs = 2; @@ -1277,6 +1312,8 @@ static int sd_start(struct gspca_dev *gspca_dev) if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS))) setsharpness(gspca_dev); sethvflip(gspca_dev); + if (!(gspca_dev->ctrl_dis & (1 << COLORS))) + setcolors(gspca_dev); setlightfreq(gspca_dev); ov534_set_led(gspca_dev, 1); -- cgit v0.10.2 From 20c957b360f85c293fbb6bcec9d1ec11b241ef1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 13:18:58 -0300 Subject: [media] gspca - sonixj: The pin S_PWR_DN is inverted for sensor mi0360 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index a1f82a9..6415aff 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -408,7 +408,7 @@ static const u8 sn_hv7131[0x1c] = { static const u8 sn_mi0360[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, + 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ -- cgit v0.10.2 From 07c6c9c6d0e797590a7cc76fc8f0b740f34d21a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 13:32:22 -0300 Subject: [media] gspca - ov519: Add the sensor ov2610ae MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 8ab2c45..cda7ee2 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -118,6 +118,7 @@ struct sd { }; enum sensors { SEN_OV2610, + SEN_OV2610AE, SEN_OV3610, SEN_OV6620, SEN_OV6630, @@ -239,6 +240,8 @@ static const struct ctrl sd_ctrls[] = { static const unsigned ctrl_dis[] = { [SEN_OV2610] = (1 << NCTRL) - 1, /* no control */ +[SEN_OV2610AE] = (1 << NCTRL) - 1, /* no control */ + [SEN_OV3610] = (1 << NCTRL) - 1, /* no control */ [SEN_OV6620] = (1 << HFLIP) | @@ -656,6 +659,24 @@ static const struct ov_i2c_regvals norm_2610[] = { { 0x12, 0x80 }, /* reset */ }; +static const struct ov_i2c_regvals norm_2610ae[] = { + {0x12, 0x80}, /* reset */ + {0x13, 0xcd}, + {0x09, 0x01}, + {0x0d, 0x00}, + {0x11, 0x80}, + {0x12, 0x20}, /* 1600x1200 */ + {0x33, 0x0c}, + {0x35, 0x90}, + {0x36, 0x37}, +/* ms-win traces */ + {0x11, 0x83}, /* clock / 3 ? */ + {0x2d, 0x00}, /* 60 Hz filter */ + {0x24, 0xb0}, /* normal colors */ + {0x25, 0x90}, + {0x10, 0x43}, +}; + static const struct ov_i2c_regvals norm_3620b[] = { /* * From the datasheet: "Note that after writing to register COMH @@ -2621,6 +2642,9 @@ static void ov_hires_configure(struct sd *sd) if (high == 0x96 && low == 0x40) { PDEBUG(D_PROBE, "Sensor is an OV2610"); sd->sensor = SEN_OV2610; + } else if (high == 0x96 && low == 0x41) { + PDEBUG(D_PROBE, "Sensor is an OV2610AE"); + sd->sensor = SEN_OV2610AE; } else if (high == 0x36 && (low & 0x0f) == 0x00) { PDEBUG(D_PROBE, "Sensor is an OV3610"); sd->sensor = SEN_OV3610; @@ -3295,15 +3319,22 @@ static int sd_init(struct gspca_dev *gspca_dev) } break; case BRIDGE_OVFX2: - if (sd->sensor == SEN_OV2610) { + switch (sd->sensor) { + case SEN_OV2610: + case SEN_OV2610AE: cam->cam_mode = ovfx2_ov2610_mode; cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode); - } else if (sd->sensor == SEN_OV3610) { + break; + case SEN_OV3610: cam->cam_mode = ovfx2_ov3610_mode; cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode); - } else if (sd->sif) { - cam->cam_mode = ov519_sif_mode; - cam->nmodes = ARRAY_SIZE(ov519_sif_mode); + break; + default: + if (sd->sif) { + cam->cam_mode = ov519_sif_mode; + cam->nmodes = ARRAY_SIZE(ov519_sif_mode); + } + break; } break; case BRIDGE_W9968CF: @@ -3325,6 +3356,12 @@ static int sd_init(struct gspca_dev *gspca_dev) /* Enable autogain, autoexpo, awb, bandfilter */ i2c_w_mask(sd, 0x13, 0x27, 0x27); break; + case SEN_OV2610AE: + write_i2c_regvals(sd, norm_2610ae, ARRAY_SIZE(norm_2610ae)); + + /* enable autoexpo */ + i2c_w_mask(sd, 0x13, 0x05, 0x05); + break; case SEN_OV3610: write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b)); @@ -3827,6 +3864,25 @@ static void mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); return; + case SEN_OV2610AE: { + u8 v; + + /* frame rates: + * 10fps / 5 fps for 1600x1200 + * 40fps / 20fps for 800x600 + */ + v = 80; + if (qvga) { + if (sd->frame_rate < 25) + v = 0x81; + } else { + if (sd->frame_rate < 10) + v = 0x81; + } + i2c_w(sd, 0x11, v); + i2c_w(sd, 0x12, qvga ? 0x60 : 0x20); + return; + } case SEN_OV3610: if (qvga) { xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4); @@ -3975,6 +4031,7 @@ static void set_ov_sensor_window(struct sd *sd) /* mode setup is fully handled in mode_init_ov_sensor_regs for these */ switch (sd->sensor) { case SEN_OV2610: + case SEN_OV2610AE: case SEN_OV3610: case SEN_OV7670: mode_init_ov_sensor_regs(sd); -- cgit v0.10.2 From c42cedbb658bde9b7bea0ce24e2388b7c717fdb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 10 Feb 2011 13:37:48 -0300 Subject: [media] gspca - ov519: Add the 800x600 resolution for sensors ov2610/2610ae MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index cda7ee2..42670ec 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -431,6 +431,11 @@ static const struct v4l2_pix_format ovfx2_cif_mode[] = { .priv = 0}, }; static const struct v4l2_pix_format ovfx2_ov2610_mode[] = { + {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 1600, .sizeimage = 1600 * 1200, @@ -547,6 +552,7 @@ static const struct v4l2_pix_format ovfx2_ov3610_mode[] = { * buffers, there are some pretty strict real time constraints for * isochronous transfer for larger frame sizes). */ +/*jfm: this value works well for 1600x1200, but not 800x600 - see isoc_init */ #define OVFX2_BULK_SIZE (13 * 4096) /* I2C registers */ @@ -3434,6 +3440,22 @@ error: return -EINVAL; } +/* function called at start time before URB creation */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + case BRIDGE_OVFX2: + if (gspca_dev->width == 1600) + gspca_dev->cam.bulk_size = OVFX2_BULK_SIZE; + else + gspca_dev->cam.bulk_size = 7 * 4096; + break; + } + return 0; +} + /* Set up the OV511/OV511+ with the given image parameters. * * Do not put any sensor-specific code in here (including I2C I/O functions) @@ -4788,6 +4810,7 @@ static const struct sd_desc sd_desc = { .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .isoc_init = sd_isoc_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, -- cgit v0.10.2 From 9839029b052f094b9c14cb60e7c729950dd28712 Mon Sep 17 00:00:00 2001 From: Stefan Ringel Date: Tue, 25 Jan 2011 13:40:55 -0300 Subject: [media] tm6000: relabeling any registers relabeling any registers Signed-off-by: Stefan Ringel Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/staging/tm6000/tm6000-core.c index 96aed4a..5162cd4 100644 --- a/drivers/staging/tm6000/tm6000-core.c +++ b/drivers/staging/tm6000/tm6000-core.c @@ -268,11 +268,11 @@ int tm6000_init_analog_mode(struct tm6000_core *dev) tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23); tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f); /* AP Software reset */ tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); @@ -284,8 +284,8 @@ int tm6000_init_analog_mode(struct tm6000_core *dev) tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); /* E3: Select input 0 - TV tuner */ - tm6000_set_reg(dev, TM6010_REQ07_RE3_OUT_SEL1, 0x00); - tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x60); + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x60); /* This controls input */ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_2, 0x0); @@ -344,21 +344,21 @@ int tm6000_init_digital_mode(struct tm6000_core *dev) tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x08); - tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c); - tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff); - tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0xd8); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08); + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8); tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x37); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37); tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); - tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c); - tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff); - tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0x08); + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08); msleep(50); tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); @@ -388,18 +388,19 @@ struct reg_init { /* The meaning of those initializations are unknown */ struct reg_init tm6000_init_tab[] = { /* REG VALUE */ - { TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f }, + { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f }, { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, - { TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23 }, - { TM6010_REQ07_RD8_IR_WAKEUP_ADD, 0x08 }, - { TM6010_REQ07_RE2_OUT_SEL2, 0x00 }, - { TM6010_REQ07_RE3_OUT_SEL1, 0x10 }, - { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0x00 }, - { TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0x00 }, - { REQ_07_SET_GET_AVREG, 0xeb, 0x64 }, /* 48000 bits/sample, external input */ - { REQ_07_SET_GET_AVREG, 0xee, 0xc2 }, + { TM6000_REQ07_RDA_CLK_SEL, 0x23 }, + { TM6000_REQ07_RDB_OUT_SEL, 0x08 }, + { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 }, + { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 }, + { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 }, + { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 }, + { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */ + { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 }, + { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, @@ -592,6 +593,7 @@ int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) { int val; + /* enable I2S, if we use sif or external I2S device */ if (dev->dev_type == TM6010) { val = tm6000_get_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0); if (val < 0) @@ -602,9 +604,17 @@ int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) return val; } - val = tm6000_get_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x0); - if (val < 0) - return val; + /* different reg's to set audio bitrate */ + if (dev->dev_type == TM6010) { + val = tm6000_get_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x0); + if (val < 0) + return val; + } else { + val = tm6000_get_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x0); + if (val < 0) + return val; + } val &= 0x0f; /* Preserve the audio input control bits */ switch (bitrate) { @@ -617,7 +627,11 @@ int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) dev->audio_bitrate = bitrate; break; } - val = tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, val); + if (dev->dev_type == TM6010) + val = tm6000_set_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + val); + else + val = tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, val); return val; } -- cgit v0.10.2 From 8bc50f354047f4afb31fc85f0efb6bfe28ce9e57 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 16 Feb 2011 07:11:14 -0300 Subject: [media] gspca_sn9c20x: Fix colored borders with ov7660 sensor Tested with a camera with usb-id: 0c45:62bb Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index fcf2989..40e316a 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -884,6 +884,9 @@ static struct i2c_reg_u8 ov7660_init[] = { {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, + /* HDG Set hstart and hstop, datasheet default 0x11, 0x61, using + 0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */ + {0x17, 0x10}, {0x18, 0x61}, {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43}, {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6}, {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, @@ -1334,8 +1337,8 @@ static int ov7660_init_sensor(struct gspca_dev *gspca_dev) } /* disable hflip and vflip */ gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); - sd->hstart = 1; - sd->vstart = 1; + sd->hstart = 3; + sd->vstart = 3; return 0; } -- cgit v0.10.2 From 779b51f7a5ec0e95dcc3690cd68c2631a971a65e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 16 Feb 2011 08:17:36 -0300 Subject: [media] gspca_sn9c20x: Add hflip and vflip controls for the ov7660 sensor Tested with a camera with usb-id: 0c45:62bb Also enable dmi based flopped laptop detection for the 0c45:627b ov7660 model. Some of the MSI laptops with upside down camera have this one instead of the 0c45:624f. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 40e316a..ccc89c1 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -1335,8 +1335,6 @@ static int ov7660_init_sensor(struct gspca_dev *gspca_dev) return -ENODEV; } } - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); sd->hstart = 3; sd->vstart = 3; return 0; @@ -1611,6 +1609,18 @@ static int set_hvflip(struct gspca_dev *gspca_dev) } switch (sd->sensor) { + case SENSOR_OV7660: + value = 0x01; + if (hflip) + value |= 0x20; + if (vflip) { + value |= 0x10; + sd->vstart = 2; + } else + sd->vstart = 3; + reg_w1(gspca_dev, 0x1182, sd->vstart); + i2c_w1(gspca_dev, 0x1e, value); + break; case SENSOR_OV9650: i2c_r1(gspca_dev, 0x1e, &value); value &= ~0x30; @@ -2485,7 +2495,7 @@ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)}, {USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)}, - {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, FLIP_DETECT)}, {USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)}, {USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)}, -- cgit v0.10.2 From b39e0cb9018bf95348078ad74b3e0171e985389a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 16 Feb 2011 08:33:16 -0300 Subject: [media] gspca_sn9c20x: Add LED_REVERSE flag for 0c45:62bb This fixes the led not working on this camera Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index ccc89c1..52f44a5 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -2507,7 +2507,7 @@ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)}, {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)}, {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, LED_REVERSE)}, {USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)}, {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)}, -- cgit v0.10.2 From 6899a9c7778a6d19edc74e2ef7dfb6daf21dc366 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 16 Feb 2011 08:37:54 -0300 Subject: [media] gspca_sn9c20x: Make buffers slightly larger for JPEG frames In good lighting conditions (lots of details visible) the current buffers are sometimes too small. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 52f44a5..f51579c 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -369,7 +369,7 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = SCALE_160x120}, {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, + .sizeimage = 320 * 240 * 4 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = SCALE_320x240 | MODE_JPEG}, {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, @@ -384,7 +384,7 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = SCALE_320x240}, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, + .sizeimage = 640 * 480 * 4 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = SCALE_640x480 | MODE_JPEG}, {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, @@ -417,7 +417,7 @@ static const struct v4l2_pix_format sxga_mode[] = { .priv = SCALE_160x120}, {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, + .sizeimage = 320 * 240 * 4 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = SCALE_320x240 | MODE_JPEG}, {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, @@ -432,7 +432,7 @@ static const struct v4l2_pix_format sxga_mode[] = { .priv = SCALE_320x240}, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, + .sizeimage = 640 * 480 * 4 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = SCALE_640x480 | MODE_JPEG}, {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, -- cgit v0.10.2 From bcc6f669066d490f10484f44561f9c2ebb38c25b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Feb 2011 06:27:57 -0300 Subject: [media] gspca_sn9c20x: Add another MSI laptop to the sn9c20x upside down list Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index f51579c..c431900 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -152,6 +152,13 @@ static const struct dmi_system_id flip_dmi_table[] = { } }, { + .ident = "MSI MS-1633X", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1633X") + } + }, + { .ident = "MSI MS-1635X", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), -- cgit v0.10.2 From 49b61ec9b5afdb739e07bc085ed12678bc932a22 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 21 Feb 2011 11:06:29 -0300 Subject: [media] gspca: Add new vicam subdriver This is a complete rewrite of the old v4l1 vicam subdriver, featuring multiple resolutions, gain + exposure control and still has less code. Oh and it is a v4l2 driver rather then v4l1 ofcourse :) Many thanks to Devin Heitmueller for donating his 3com homeconnect to me, which made this rewrite possible. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index dda56ff..a20f6ae 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -346,6 +346,16 @@ config USB_GSPCA_VC032X To compile this driver as a module, choose M here: the module will be called gspca_vc032x. +config USB_GSPCA_VICAM + tristate "ViCam USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for the 3com homeconnect camera + (vicam). + + To compile this driver as a module, choose M here: the + module will be called gspca_vicam. + config USB_GSPCA_XIRLINK_CIT tristate "Xirlink C-It USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 24e695b..a0dbcfb 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o +obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o @@ -73,6 +74,7 @@ gspca_sunplus-objs := sunplus.o gspca_t613-objs := t613.o gspca_tv8532-objs := tv8532.o gspca_vc032x-objs := vc032x.o +gspca_vicam-objs := vicam.o gspca_xirlink_cit-objs := xirlink_cit.o gspca_zc3xx-objs := zc3xx.o diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c new file mode 100644 index 0000000..84dfbab --- /dev/null +++ b/drivers/media/video/gspca/vicam.c @@ -0,0 +1,381 @@ +/* + * gspca ViCam subdriver + * + * Copyright (C) 2011 Hans de Goede + * + * Based on the usbvideo vicam driver, which is: + * + * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), + * Christopher L Cheney (ccheney@cheney.cx), + * Pavel Machek (pavel@ucw.cz), + * John Tyner (jtyner@cs.ucr.edu), + * Monroe Williams (monroe@pobox.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 + * 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 + */ + +#define MODULE_NAME "vicam" +#define HEADER_SIZE 64 + +#include +#include +#include +#include +#include "gspca.h" + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); +MODULE_LICENSE("GPL"); + +enum e_ctrl { + GAIN, + EXPOSURE, + NCTRL /* number of controls */ +}; + +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct work_struct work_struct; + struct workqueue_struct *work_thread; + struct gspca_ctrl ctrls[NCTRL]; +}; + +/* The vicam sensor has a resolution of 512 x 244, with I believe square + pixels, but this is forced to a 4:3 ratio by optics. So it has + non square pixels :( */ +static struct v4l2_pix_format vicam_mode[] = { + { 256, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 256, + .sizeimage = 256 * 122, + .colorspace = V4L2_COLORSPACE_SRGB,}, + /* 2 modes with somewhat more square pixels */ + { 256, 200, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 256, + .sizeimage = 256 * 200, + .colorspace = V4L2_COLORSPACE_SRGB,}, + { 256, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 256, + .sizeimage = 256 * 240, + .colorspace = V4L2_COLORSPACE_SRGB,}, +#if 0 /* This mode has extremely non square pixels, testing use only */ + { 512, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 512, + .sizeimage = 512 * 122, + .colorspace = V4L2_COLORSPACE_SRGB,}, +#endif + { 512, 244, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 512, + .sizeimage = 512 * 244, + .colorspace = V4L2_COLORSPACE_SRGB,}, +}; + +static const struct ctrl sd_ctrls[] = { +[GAIN] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 200, + }, + }, +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 2047, + .step = 1, + .default_value = 256, + }, + }, +}; + +static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request, + u16 value, u16 index, u8 *data, u16 len) +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, data, len, 1000); + if (ret < 0) + err("control msg req %02X error %d", request, ret); + + return ret; +} + +static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state) +{ + int ret; + + ret = vicam_control_msg(gspca_dev, 0x50, state, 0, NULL, 0); + if (ret < 0) + return ret; + + if (state) + ret = vicam_control_msg(gspca_dev, 0x55, 1, 0, NULL, 0); + + return ret; +} + +/* + * request and read a block of data - see warning on vicam_command. + */ +static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) +{ + struct sd *sd = (struct sd *)gspca_dev; + int ret, unscaled_height, act_len = 0; + u8 *req_data = gspca_dev->usb_buf; + + memset(req_data, 0, 16); + req_data[0] = sd->ctrls[GAIN].val; + if (gspca_dev->width == 256) + req_data[1] |= 0x01; /* low nibble x-scale */ + if (gspca_dev->height <= 122) { + req_data[1] |= 0x10; /* high nibble y-scale */ + unscaled_height = gspca_dev->height * 2; + } else + unscaled_height = gspca_dev->height; + req_data[2] = 0x90; /* unknown, does not seem to do anything */ + if (unscaled_height <= 200) + req_data[3] = 0x06; /* vend? */ + else if (unscaled_height <= 242) /* Yes 242 not 240 */ + req_data[3] = 0x07; /* vend? */ + else /* Up to 244 lines with req_data[3] == 0x08 */ + req_data[3] = 0x08; /* vend? */ + + if (sd->ctrls[EXPOSURE].val < 256) { + /* Frame rate maxed out, use partial frame expo time */ + req_data[4] = 255 - sd->ctrls[EXPOSURE].val; + req_data[5] = 0x00; + req_data[6] = 0x00; + req_data[7] = 0x01; + } else { + /* Modify frame rate */ + req_data[4] = 0x00; + req_data[5] = 0x00; + req_data[6] = sd->ctrls[EXPOSURE].val & 0xFF; + req_data[7] = sd->ctrls[EXPOSURE].val >> 8; + } + req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */ + /* bytes 9-15 do not seem to affect exposure or image quality */ + + mutex_lock(&gspca_dev->usb_lock); + ret = vicam_control_msg(gspca_dev, 0x51, 0x80, 0, req_data, 16); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + return ret; + + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + data, size, &act_len, 10000); + /* successful, it returns 0, otherwise negative */ + if (ret < 0 || act_len != size) { + err("bulk read fail (%d) len %d/%d", + ret, act_len, size); + return -EIO; + } + return 0; +} + +/* This function is called as a workqueue function and runs whenever the camera + * is streaming data. Because it is a workqueue function it is allowed to sleep + * so we can use synchronous USB calls. To avoid possible collisions with other + * threads attempting to use the camera's USB interface we take the gspca + * usb_lock when performing USB operations. In practice the only thing we need + * to protect against is the usb_set_interface call that gspca makes during + * stream_off as the camera doesn't provide any controls that the user could try + * to change. + */ +static void vicam_dostream(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work_struct); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + int ret, frame_sz; + u8 *buffer; + + frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + + HEADER_SIZE; + buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA); + if (!buffer) { + err("Couldn't allocate USB buffer"); + goto exit; + } + + while (gspca_dev->present && gspca_dev->streaming) { + ret = vicam_read_frame(gspca_dev, buffer, frame_sz); + if (ret < 0) + break; + + /* Note the frame header contents seem to be completely + constant, they do not change with either image, or + settings. So we simply discard it. The frames have + a very similar 64 byte footer, which we don't even + bother reading from the cam */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + buffer + HEADER_SIZE, + frame_sz - HEADER_SIZE); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + } +exit: + kfree(buffer); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + struct sd *sd = (struct sd *)gspca_dev; + + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk = 1; + cam->bulk_size = 64; + cam->cam_mode = vicam_mode; + cam->nmodes = ARRAY_SIZE(vicam_mode); + cam->ctrls = sd->ctrls; + + INIT_WORK(&sd->work_struct, vicam_dostream); + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + int ret; + const struct ihex_binrec *rec; + const struct firmware *uninitialized_var(fw); + u8 *firmware_buf; + + ret = request_ihex_firmware(&fw, "vicam/firmware.fw", + &gspca_dev->dev->dev); + if (ret) { + err("Failed to load \"vicam/firmware.fw\": %d\n", ret); + return ret; + } + + firmware_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!firmware_buf) { + ret = -ENOMEM; + goto exit; + } + for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { + memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len)); + ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf, + be16_to_cpu(rec->len)); + if (ret < 0) + break; + } + + kfree(firmware_buf); +exit: + release_firmware(fw); + return ret; +} + +/* Set up for getting frames. */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + int ret; + + ret = vicam_set_camera_power(gspca_dev, 1); + if (ret < 0) + return ret; + + /* Start the workqueue function to do the streaming */ + sd->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(sd->work_thread, &sd->work_struct); + + return 0; +} + +/* 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 *dev = (struct sd *)gspca_dev; + + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + /* This waits for vicam_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); + + vicam_set_camera_power(gspca_dev, 0); +} + +/* Table of supported USB devices */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x04c1, 0x009d)}, + {USB_DEVICE(0x0602, 0x1001)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, +}; + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc, + sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + return usb_register(&sd_driver); +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); -- cgit v0.10.2 From 76fafe78df9e4062b9b6595f3225a2c0c2847ae5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 21 Feb 2011 11:30:30 -0300 Subject: [media] gspca_cpia1: Don't allow the framerate divisor to go above 2 Having a framerate divisor of 3 is not really necesarry and leads to various troubles (frames going missing, compression issues) when allowed. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c index 4bf2cab..2a4a428 100644 --- a/drivers/media/video/gspca/cpia1.c +++ b/drivers/media/video/gspca/cpia1.c @@ -1,7 +1,7 @@ /* * cpia CPiA (1) gspca driver * - * Copyright (C) 2010 Hans de Goede + * Copyright (C) 2010-2011 Hans de Goede * * This module is adapted from the in kernel v4l1 cpia driver which is : * @@ -1400,7 +1400,7 @@ static void monitor_exposure(struct gspca_dev *gspca_dev) if ((sd->exposure_status == EXPOSURE_VERY_DARK || sd->exposure_status == EXPOSURE_DARK) && sd->exposure_count >= DARK_TIME * framerate && - sd->params.sensorFps.divisor < 3) { + sd->params.sensorFps.divisor < 2) { /* dark for too long */ ++sd->params.sensorFps.divisor; @@ -1456,7 +1456,7 @@ static void monitor_exposure(struct gspca_dev *gspca_dev) if ((sd->exposure_status == EXPOSURE_VERY_DARK || sd->exposure_status == EXPOSURE_DARK) && sd->exposure_count >= DARK_TIME * framerate && - sd->params.sensorFps.divisor < 3) { + sd->params.sensorFps.divisor < 2) { /* dark for too long */ ++sd->params.sensorFps.divisor; -- cgit v0.10.2 From 37d803ed0b86a3252457da971e0e9a8a937c2ba4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 22 Feb 2011 13:39:36 -0300 Subject: [media] staging-usbvideo: remove With the new gspca v4l2 vicam driver, there is no more reason to keep the old v4l1 usbvideo vicam driver around, and since that is the last usbvideo framework using driver, the old usbvideo framework itself can go too. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 4a88e69..a3f340b 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -57,8 +57,6 @@ source "drivers/staging/dabusb/Kconfig" source "drivers/staging/se401/Kconfig" -source "drivers/staging/usbvideo/Kconfig" - source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 80ee49a..532331c 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_USB_DABUSB) += dabusb/ -obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_USB_SE401) += se401/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ diff --git a/drivers/staging/usbvideo/Kconfig b/drivers/staging/usbvideo/Kconfig deleted file mode 100644 index 566d659..0000000 --- a/drivers/staging/usbvideo/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config VIDEO_USBVIDEO - tristate - -config USB_VICAM - tristate "USB 3com HomeConnect (aka vicam) support (DEPRECATED)" - depends on VIDEO_DEV && VIDEO_V4L2_COMMON && USB - select VIDEO_USBVIDEO - ---help--- - Say Y here if you have 3com homeconnect camera (vicam). - - This driver uses the deprecated V4L1 API and will be removed in - 2.6.39, unless someone converts it to the V4L2 API. - - To compile this driver as a module, choose M here: the - module will be called vicam. diff --git a/drivers/staging/usbvideo/Makefile b/drivers/staging/usbvideo/Makefile deleted file mode 100644 index 3c99a9a..0000000 --- a/drivers/staging/usbvideo/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o -obj-$(CONFIG_USB_VICAM) += vicam.o diff --git a/drivers/staging/usbvideo/TODO b/drivers/staging/usbvideo/TODO deleted file mode 100644 index 3b2c038..0000000 --- a/drivers/staging/usbvideo/TODO +++ /dev/null @@ -1,5 +0,0 @@ -This is an obsolete driver for some old webcams that still use V4L1 API. -As V4L1 support is being removed from kernel, if nobody take care on it, -the driver will be removed for 2.6.39. - -Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/usbvideo/usbvideo.c b/drivers/staging/usbvideo/usbvideo.c deleted file mode 100644 index f1fcf97..0000000 --- a/drivers/staging/usbvideo/usbvideo.c +++ /dev/null @@ -1,2230 +0,0 @@ -/* - * 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, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "usbvideo.h" - -#if defined(MAP_NR) -#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */ -#endif - -static int video_nr = -1; -module_param(video_nr, int, 0); - -/* - * Local prototypes. - */ -static void usbvideo_Disconnect(struct usb_interface *intf); -static void usbvideo_CameraRelease(struct uvd *uvd); - -static long usbvideo_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg); -static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma); -static int usbvideo_v4l_open(struct file *file); -static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos); -static int usbvideo_v4l_close(struct file *file); - -static int usbvideo_StartDataPump(struct uvd *uvd); -static void usbvideo_StopDataPump(struct uvd *uvd); -static int usbvideo_GetFrame(struct uvd *uvd, int frameNum); -static int usbvideo_NewFrame(struct uvd *uvd, int framenum); -static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, - struct usbvideo_frame *frame); - -/*******************************/ -/* Memory management functions */ -/*******************************/ -static void *usbvideo_rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void usbvideo_rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -static void RingQueue_Initialize(struct RingQueue *rq) -{ - assert(rq != NULL); - init_waitqueue_head(&rq->wqh); -} - -static void RingQueue_Allocate(struct RingQueue *rq, int rqLen) -{ - /* Make sure the requested size is a power of 2 and - round up if necessary. This allows index wrapping - using masks rather than modulo */ - - int i = 1; - assert(rq != NULL); - assert(rqLen > 0); - - while(rqLen >> i) - i++; - if(rqLen != 1 << (i-1)) - rqLen = 1 << i; - - rq->length = rqLen; - rq->ri = rq->wi = 0; - rq->queue = usbvideo_rvmalloc(rq->length); - assert(rq->queue != NULL); -} - -static int RingQueue_IsAllocated(const struct RingQueue *rq) -{ - if (rq == NULL) - return 0; - return (rq->queue != NULL) && (rq->length > 0); -} - -static void RingQueue_Free(struct RingQueue *rq) -{ - assert(rq != NULL); - if (RingQueue_IsAllocated(rq)) { - usbvideo_rvfree(rq->queue, rq->length); - rq->queue = NULL; - rq->length = 0; - } -} - -int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len) -{ - int rql, toread; - - assert(rq != NULL); - assert(dst != NULL); - - rql = RingQueue_GetLength(rq); - if(!rql) - return 0; - - /* Clip requested length to available data */ - if(len > rql) - len = rql; - - toread = len; - if(rq->ri > rq->wi) { - /* Read data from tail */ - int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri; - memcpy(dst, rq->queue + rq->ri, read); - toread -= read; - dst += read; - rq->ri = (rq->ri + read) & (rq->length-1); - } - if(toread) { - /* Read data from head */ - memcpy(dst, rq->queue + rq->ri, toread); - rq->ri = (rq->ri + toread) & (rq->length-1); - } - return len; -} - -EXPORT_SYMBOL(RingQueue_Dequeue); - -int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n) -{ - int enqueued = 0; - - assert(rq != NULL); - assert(cdata != NULL); - assert(rq->length > 0); - while (n > 0) { - int m, q_avail; - - /* Calculate the largest chunk that fits the tail of the ring */ - q_avail = rq->length - rq->wi; - if (q_avail <= 0) { - rq->wi = 0; - q_avail = rq->length; - } - m = n; - assert(q_avail > 0); - if (m > q_avail) - m = q_avail; - - memcpy(rq->queue + rq->wi, cdata, m); - RING_QUEUE_ADVANCE_INDEX(rq, wi, m); - cdata += m; - enqueued += m; - n -= m; - } - return enqueued; -} - -EXPORT_SYMBOL(RingQueue_Enqueue); - -static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq) -{ - assert(rq != NULL); - interruptible_sleep_on(&rq->wqh); -} - -void RingQueue_WakeUpInterruptible(struct RingQueue *rq) -{ - assert(rq != NULL); - if (waitqueue_active(&rq->wqh)) - wake_up_interruptible(&rq->wqh); -} - -EXPORT_SYMBOL(RingQueue_WakeUpInterruptible); - -void RingQueue_Flush(struct RingQueue *rq) -{ - assert(rq != NULL); - rq->ri = 0; - rq->wi = 0; -} - -EXPORT_SYMBOL(RingQueue_Flush); - - -/* - * usbvideo_VideosizeToString() - * - * This procedure converts given videosize value to readable string. - * - * History: - * 07-Aug-2000 Created. - * 19-Oct-2000 Reworked for usbvideo module. - */ -static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs) -{ - char tmp[40]; - int n; - - n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs)); - assert(n < sizeof(tmp)); - if ((buf == NULL) || (bufLen < n)) - err("usbvideo_VideosizeToString: buffer is too small."); - else - memmove(buf, tmp, n); -} - -/* - * usbvideo_OverlayChar() - * - * History: - * 01-Feb-2000 Created. - */ -static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame, - int x, int y, int ch) -{ - static const unsigned short digits[16] = { - 0xF6DE, /* 0 */ - 0x2492, /* 1 */ - 0xE7CE, /* 2 */ - 0xE79E, /* 3 */ - 0xB792, /* 4 */ - 0xF39E, /* 5 */ - 0xF3DE, /* 6 */ - 0xF492, /* 7 */ - 0xF7DE, /* 8 */ - 0xF79E, /* 9 */ - 0x77DA, /* a */ - 0xD75C, /* b */ - 0xF24E, /* c */ - 0xD6DC, /* d */ - 0xF34E, /* e */ - 0xF348 /* f */ - }; - unsigned short digit; - int ix, iy; - int value; - - if ((uvd == NULL) || (frame == NULL)) - return; - - value = hex_to_bin(ch); - if (value < 0) - return; - digit = digits[value]; - - for (iy=0; iy < 5; iy++) { - for (ix=0; ix < 3; ix++) { - if (digit & 0x8000) { - if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) { -/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); - } - } - digit = digit << 1; - } - } -} - -/* - * usbvideo_OverlayString() - * - * History: - * 01-Feb-2000 Created. - */ -static void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame, - int x, int y, const char *str) -{ - while (*str) { - usbvideo_OverlayChar(uvd, frame, x, y, *str); - str++; - x += 4; /* 3 pixels character + 1 space */ - } -} - -/* - * usbvideo_OverlayStats() - * - * Overlays important debugging information. - * - * History: - * 01-Feb-2000 Created. - */ -static void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame) -{ - const int y_diff = 8; - char tmp[16]; - int x = 10, y=10; - long i, j, barLength; - const int qi_x1 = 60, qi_y1 = 10; - const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10; - - /* Call the user callback, see if we may proceed after that */ - if (VALID_CALLBACK(uvd, overlayHook)) { - if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0) - return; - } - - /* - * We draw a (mostly) hollow rectangle with qi_xxx coordinates. - * Left edge symbolizes the queue index 0; right edge symbolizes - * the full capacity of the queue. - */ - barLength = qi_x2 - qi_x1 - 2; - if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) { -/* TODO */ long u_lo, u_hi, q_used; - long m_ri, m_wi, m_lo, m_hi; - - /* - * Determine fill zones (used areas of the queue): - * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length - * - * if u_lo < 0 then there is no first filler. - */ - - q_used = RingQueue_GetLength(&uvd->dp); - if ((uvd->dp.ri + q_used) >= uvd->dp.length) { - u_hi = uvd->dp.length; - u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1); - } else { - u_hi = (q_used + uvd->dp.ri); - u_lo = -1; - } - - /* Convert byte indices into screen units */ - m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length); - m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length); - m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1; - m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length); - - for (j=qi_y1; j < (qi_y1 + qi_h); j++) { - for (i=qi_x1; i < qi_x2; i++) { - /* Draw border lines */ - if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) || - (i == qi_x1) || (i == (qi_x2 - 1))) { - RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF); - continue; - } - /* For all other points the Y coordinate does not matter */ - if ((i >= m_ri) && (i <= (m_ri + 3))) { - RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00); - } else if ((i >= m_wi) && (i <= (m_wi + 3))) { - RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00); - } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi))) - RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF); - } - } - } - - sprintf(tmp, "%8lx", uvd->stats.frame_num); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.urb_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.urb_length); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.data_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.header_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.iso_skip_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.iso_err_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.colour); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.hue); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.brightness >> 8); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.contrast >> 12); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; -} - -/* - * usbvideo_ReportStatistics() - * - * This procedure prints packet and transfer statistics. - * - * History: - * 14-Jan-2000 Corrected default multiplier. - */ -static void usbvideo_ReportStatistics(const struct uvd *uvd) -{ - if ((uvd != NULL) && (uvd->stats.urb_count > 0)) { - unsigned long allPackets, badPackets, goodPackets, percent; - allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES; - badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count; - goodPackets = allPackets - badPackets; - /* Calculate percentage wisely, remember integer limits */ - assert(allPackets != 0); - if (goodPackets < (((unsigned long)-1)/100)) - percent = (100 * goodPackets) / allPackets; - else - percent = goodPackets / (allPackets / 100); - dev_info(&uvd->dev->dev, - "Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%\n", - allPackets, badPackets, percent); - if (uvd->iso_packet_len > 0) { - unsigned long allBytes, xferBytes; - char multiplier = ' '; - allBytes = allPackets * uvd->iso_packet_len; - xferBytes = uvd->stats.data_count; - assert(allBytes != 0); - if (xferBytes < (((unsigned long)-1)/100)) - percent = (100 * xferBytes) / allBytes; - else - percent = xferBytes / (allBytes / 100); - /* Scale xferBytes for easy reading */ - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'K'; - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'M'; - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'G'; - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'T'; - } - } - } - } - dev_info(&uvd->dev->dev, - "Transfer Statistics: Transferred=%lu%cB Usage=%lu%%\n", - xferBytes, multiplier, percent); - } - } -} - -/* - * usbvideo_TestPattern() - * - * Procedure forms a test pattern (yellow grid on blue background). - * - * Parameters: - * fullframe: if TRUE then entire frame is filled, otherwise the procedure - * continues from the current scanline. - * pmode 0: fill the frame with solid blue color (like on VCR or TV) - * 1: Draw a colored grid - * - * History: - * 01-Feb-2000 Created. - */ -void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode) -{ - struct usbvideo_frame *frame; - int num_cell = 0; - int scan_length = 0; - static int num_pass; - - if (uvd == NULL) { - err("%s: uvd == NULL", __func__); - return; - } - if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { - err("%s: uvd->curframe=%d.", __func__, uvd->curframe); - return; - } - - /* Grab the current frame */ - frame = &uvd->frame[uvd->curframe]; - - /* Optionally start at the beginning */ - if (fullframe) { - frame->curline = 0; - frame->seqRead_Length = 0; - } -#if 0 - { /* For debugging purposes only */ - char tmp[20]; - usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request); - dev_info(&uvd->dev->dev, "testpattern: frame=%s\n", tmp); - } -#endif - /* Form every scan line */ - for (; frame->curline < VIDEOSIZE_Y(frame->request); frame->curline++) { - int i; - unsigned char *f = frame->data + - (VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL * frame->curline); - for (i=0; i < VIDEOSIZE_X(frame->request); i++) { - unsigned char cb=0x80; - unsigned char cg = 0; - unsigned char cr = 0; - - if (pmode == 1) { - if (frame->curline % 32 == 0) - cb = 0, cg = cr = 0xFF; - else if (i % 32 == 0) { - if (frame->curline % 32 == 1) - num_cell++; - cb = 0, cg = cr = 0xFF; - } else { - cb = ((num_cell*7) + num_pass) & 0xFF; - cg = ((num_cell*5) + num_pass*2) & 0xFF; - cr = ((num_cell*3) + num_pass*3) & 0xFF; - } - } else { - /* Just the blue screen */ - } - - *f++ = cb; - *f++ = cg; - *f++ = cr; - scan_length += 3; - } - } - - frame->frameState = FrameState_Done; - frame->seqRead_Length += scan_length; - ++num_pass; - - /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ - usbvideo_OverlayStats(uvd, frame); -} - -EXPORT_SYMBOL(usbvideo_TestPattern); - - -#ifdef DEBUG -/* - * usbvideo_HexDump() - * - * A debugging tool. Prints hex dumps. - * - * History: - * 29-Jul-2000 Added printing of offsets. - */ -void usbvideo_HexDump(const unsigned char *data, int len) -{ - const int bytes_per_line = 32; - char tmp[128]; /* 32*3 + 5 */ - int i, k; - - for (i=k=0; len > 0; i++, len--) { - if (i > 0 && ((i % bytes_per_line) == 0)) { - printk("%s\n", tmp); - k=0; - } - if ((i % bytes_per_line) == 0) - k += sprintf(&tmp[k], "%04x: ", i); - k += sprintf(&tmp[k], "%02x ", data[i]); - } - if (k > 0) - printk("%s\n", tmp); -} - -EXPORT_SYMBOL(usbvideo_HexDump); - -#endif - -/* ******************************************************************** */ - -/* XXX: this piece of crap really wants some error handling.. */ -static int usbvideo_ClientIncModCount(struct uvd *uvd) -{ - if (uvd == NULL) { - err("%s: uvd == NULL", __func__); - return -EINVAL; - } - if (uvd->handle == NULL) { - err("%s: uvd->handle == NULL", __func__); - return -EINVAL; - } - if (!try_module_get(uvd->handle->md_module)) { - err("%s: try_module_get() == 0", __func__); - return -ENODEV; - } - return 0; -} - -static void usbvideo_ClientDecModCount(struct uvd *uvd) -{ - if (uvd == NULL) { - err("%s: uvd == NULL", __func__); - return; - } - if (uvd->handle == NULL) { - err("%s: uvd->handle == NULL", __func__); - return; - } - if (uvd->handle->md_module == NULL) { - err("%s: uvd->handle->md_module == NULL", __func__); - return; - } - module_put(uvd->handle->md_module); -} - -int usbvideo_register( - struct usbvideo **pCams, - const int num_cams, - const int num_extra, - const char *driverName, - const struct usbvideo_cb *cbTbl, - struct module *md, - const struct usb_device_id *id_table) -{ - struct usbvideo *cams; - int i, base_size, result; - - /* Check parameters for sanity */ - if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) { - err("%s: Illegal call", __func__); - return -EINVAL; - } - - /* Check registration callback - must be set! */ - if (cbTbl->probe == NULL) { - err("%s: probe() is required!", __func__); - return -EINVAL; - } - - base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo); - cams = kzalloc(base_size, GFP_KERNEL); - if (cams == NULL) { - err("Failed to allocate %d. bytes for usbvideo struct", base_size); - return -ENOMEM; - } - dbg("%s: Allocated $%p (%d. bytes) for %d. cameras", - __func__, cams, base_size, num_cams); - - /* Copy callbacks, apply defaults for those that are not set */ - memmove(&cams->cb, cbTbl, sizeof(cams->cb)); - if (cams->cb.getFrame == NULL) - cams->cb.getFrame = usbvideo_GetFrame; - if (cams->cb.disconnect == NULL) - cams->cb.disconnect = usbvideo_Disconnect; - if (cams->cb.startDataPump == NULL) - cams->cb.startDataPump = usbvideo_StartDataPump; - if (cams->cb.stopDataPump == NULL) - cams->cb.stopDataPump = usbvideo_StopDataPump; - - cams->num_cameras = num_cams; - cams->cam = (struct uvd *) &cams[1]; - cams->md_module = md; - mutex_init(&cams->lock); /* to 1 == available */ - - for (i = 0; i < num_cams; i++) { - struct uvd *up = &cams->cam[i]; - - up->handle = cams; - - /* Allocate user_data separately because of kmalloc's limits */ - if (num_extra > 0) { - up->user_size = num_cams * num_extra; - up->user_data = kmalloc(up->user_size, GFP_KERNEL); - if (up->user_data == NULL) { - err("%s: Failed to allocate user_data (%d. bytes)", - __func__, up->user_size); - while (i) { - up = &cams->cam[--i]; - kfree(up->user_data); - } - kfree(cams); - return -ENOMEM; - } - dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)", - __func__, i, up->user_data, up->user_size); - } - } - - /* - * Register ourselves with USB stack. - */ - strcpy(cams->drvName, (driverName != NULL) ? driverName : "Unknown"); - cams->usbdrv.name = cams->drvName; - cams->usbdrv.probe = cams->cb.probe; - cams->usbdrv.disconnect = cams->cb.disconnect; - cams->usbdrv.id_table = id_table; - - /* - * Update global handle to usbvideo. This is very important - * because probe() can be called before usb_register() returns. - * If the handle is not yet updated then the probe() will fail. - */ - *pCams = cams; - result = usb_register(&cams->usbdrv); - if (result) { - for (i = 0; i < num_cams; i++) { - struct uvd *up = &cams->cam[i]; - kfree(up->user_data); - } - kfree(cams); - } - - return result; -} - -EXPORT_SYMBOL(usbvideo_register); - -/* - * usbvideo_Deregister() - * - * Procedure frees all usbvideo and user data structures. Be warned that - * if you had some dynamically allocated components in ->user field then - * you should free them before calling here. - */ -void usbvideo_Deregister(struct usbvideo **pCams) -{ - struct usbvideo *cams; - int i; - - if (pCams == NULL) { - err("%s: pCams == NULL", __func__); - return; - } - cams = *pCams; - if (cams == NULL) { - err("%s: cams == NULL", __func__); - return; - } - - dbg("%s: Deregistering %s driver.", __func__, cams->drvName); - usb_deregister(&cams->usbdrv); - - dbg("%s: Deallocating cams=$%p (%d. cameras)", __func__, cams, cams->num_cameras); - for (i=0; i < cams->num_cameras; i++) { - struct uvd *up = &cams->cam[i]; - int warning = 0; - - if (up->user_data != NULL) { - if (up->user_size <= 0) - ++warning; - } else { - if (up->user_size > 0) - ++warning; - } - if (warning) { - err("%s: Warning: user_data=$%p user_size=%d.", - __func__, up->user_data, up->user_size); - } else { - dbg("%s: Freeing %d. $%p->user_data=$%p", - __func__, i, up, up->user_data); - kfree(up->user_data); - } - } - /* Whole array was allocated in one chunk */ - dbg("%s: Freed %d uvd structures", - __func__, cams->num_cameras); - kfree(cams); - *pCams = NULL; -} - -EXPORT_SYMBOL(usbvideo_Deregister); - -/* - * usbvideo_Disconnect() - * - * This procedure stops all driver activity. Deallocation of - * the interface-private structure (pointed by 'ptr') is done now - * (if we don't have any open files) or later, when those files - * are closed. After that driver should be removable. - * - * This code handles surprise removal. The uvd->user is a counter which - * increments on open() and decrements on close(). If we see here that - * this counter is not 0 then we have a client who still has us opened. - * We set uvd->remove_pending flag as early as possible, and after that - * all access to the camera will gracefully fail. These failures should - * prompt client to (eventually) close the video device, and then - in - * usbvideo_v4l_close() - we decrement uvd->uvd_used and usage counter. - * - * History: - * 22-Jan-2000 Added polling of MOD_IN_USE to delay removal until all users gone. - * 27-Jan-2000 Reworked to allow pending disconnects; see xxx_close() - * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). - * 19-Oct-2000 Moved to usbvideo module. - */ -static void usbvideo_Disconnect(struct usb_interface *intf) -{ - struct uvd *uvd = usb_get_intfdata (intf); - int i; - - if (uvd == NULL) { - err("%s($%p): Illegal call.", __func__, intf); - return; - } - - usb_set_intfdata (intf, NULL); - - usbvideo_ClientIncModCount(uvd); - if (uvd->debug > 0) - dev_info(&intf->dev, "%s(%p.)\n", __func__, intf); - - mutex_lock(&uvd->lock); - uvd->remove_pending = 1; /* Now all ISO data will be ignored */ - - /* At this time we ask to cancel outstanding URBs */ - GET_CALLBACK(uvd, stopDataPump)(uvd); - - for (i=0; i < USBVIDEO_NUMSBUF; i++) - usb_free_urb(uvd->sbuf[i].urb); - - usb_put_dev(uvd->dev); - uvd->dev = NULL; /* USB device is no more */ - - video_unregister_device(&uvd->vdev); - if (uvd->debug > 0) - dev_info(&intf->dev, "%s: Video unregistered.\n", __func__); - - if (uvd->user) - dev_info(&intf->dev, "%s: In use, disconnect pending.\n", - __func__); - else - usbvideo_CameraRelease(uvd); - mutex_unlock(&uvd->lock); - dev_info(&intf->dev, "USB camera disconnected.\n"); - - usbvideo_ClientDecModCount(uvd); -} - -/* - * usbvideo_CameraRelease() - * - * This code does final release of uvd. This happens - * after the device is disconnected -and- all clients - * closed their files. - * - * History: - * 27-Jan-2000 Created. - */ -static void usbvideo_CameraRelease(struct uvd *uvd) -{ - if (uvd == NULL) { - err("%s: Illegal call", __func__); - return; - } - - RingQueue_Free(&uvd->dp); - if (VALID_CALLBACK(uvd, userFree)) - GET_CALLBACK(uvd, userFree)(uvd); - uvd->uvd_used = 0; /* This is atomic, no need to take mutex */ -} - -/* - * usbvideo_find_struct() - * - * This code searches the array of preallocated (static) structures - * and returns index of the first one that isn't in use. Returns -1 - * if there are no free structures. - * - * History: - * 27-Jan-2000 Created. - */ -static int usbvideo_find_struct(struct usbvideo *cams) -{ - int u, rv = -1; - - if (cams == NULL) { - err("No usbvideo handle?"); - return -1; - } - mutex_lock(&cams->lock); - for (u = 0; u < cams->num_cameras; u++) { - struct uvd *uvd = &cams->cam[u]; - if (!uvd->uvd_used) /* This one is free */ - { - uvd->uvd_used = 1; /* In use now */ - mutex_init(&uvd->lock); /* to 1 == available */ - uvd->dev = NULL; - rv = u; - break; - } - } - mutex_unlock(&cams->lock); - return rv; -} - -static const struct v4l2_file_operations usbvideo_fops = { - .owner = THIS_MODULE, - .open = usbvideo_v4l_open, - .release =usbvideo_v4l_close, - .read = usbvideo_v4l_read, - .mmap = usbvideo_v4l_mmap, - .ioctl = usbvideo_v4l_ioctl, -}; -static const struct video_device usbvideo_template = { - .fops = &usbvideo_fops, -}; - -struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams) -{ - int i, devnum; - struct uvd *uvd = NULL; - - if (cams == NULL) { - err("No usbvideo handle?"); - return NULL; - } - - devnum = usbvideo_find_struct(cams); - if (devnum == -1) { - err("IBM USB camera driver: Too many devices!"); - return NULL; - } - uvd = &cams->cam[devnum]; - dbg("Device entry #%d. at $%p", devnum, uvd); - - /* Not relying upon caller we increase module counter ourselves */ - usbvideo_ClientIncModCount(uvd); - - mutex_lock(&uvd->lock); - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (uvd->sbuf[i].urb == NULL) { - err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC); - uvd->uvd_used = 0; - uvd = NULL; - goto allocate_done; - } - } - uvd->user=0; - uvd->remove_pending = 0; - uvd->last_error = 0; - RingQueue_Initialize(&uvd->dp); - - /* Initialize video device structure */ - uvd->vdev = usbvideo_template; - sprintf(uvd->vdev.name, "%.20s USB Camera", cams->drvName); - /* - * The client is free to overwrite those because we - * return control to the client's probe function right now. - */ -allocate_done: - mutex_unlock(&uvd->lock); - usbvideo_ClientDecModCount(uvd); - return uvd; -} - -EXPORT_SYMBOL(usbvideo_AllocateDevice); - -int usbvideo_RegisterVideoDevice(struct uvd *uvd) -{ - char tmp1[20], tmp2[20]; /* Buffers for printing */ - - if (uvd == NULL) { - err("%s: Illegal call.", __func__); - return -EINVAL; - } - if (uvd->video_endp == 0) { - dev_info(&uvd->dev->dev, - "%s: No video endpoint specified; data pump disabled.\n", - __func__); - } - if (uvd->paletteBits == 0) { - err("%s: No palettes specified!", __func__); - return -EINVAL; - } - if (uvd->defaultPalette == 0) { - dev_info(&uvd->dev->dev, "%s: No default palette!\n", - __func__); - } - - uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) * - VIDEOSIZE_Y(uvd->canvas) * V4L_BYTES_PER_PIXEL; - usbvideo_VideosizeToString(tmp1, sizeof(tmp1), uvd->videosize); - usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas); - - if (uvd->debug > 0) { - dev_info(&uvd->dev->dev, - "%s: iface=%d. endpoint=$%02x paletteBits=$%08lx\n", - __func__, uvd->iface, uvd->video_endp, - uvd->paletteBits); - } - if (uvd->dev == NULL) { - err("%s: uvd->dev == NULL", __func__); - return -EINVAL; - } - uvd->vdev.parent = &uvd->dev->dev; - uvd->vdev.release = video_device_release_empty; - if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - err("%s: video_register_device failed", __func__); - return -EPIPE; - } - if (uvd->debug > 1) { - dev_info(&uvd->dev->dev, - "%s: video_register_device() successful\n", __func__); - } - - dev_info(&uvd->dev->dev, "%s on %s: canvas=%s videosize=%s\n", - (uvd->handle != NULL) ? uvd->handle->drvName : "???", - video_device_node_name(&uvd->vdev), tmp2, tmp1); - - usb_get_dev(uvd->dev); - return 0; -} - -EXPORT_SYMBOL(usbvideo_RegisterVideoDevice); - -/* ******************************************************************** */ - -static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct uvd *uvd = file->private_data; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return -EFAULT; - - if (size > (((USBVIDEO_NUMFRAMES * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) - return -EINVAL; - - pos = (unsigned long) uvd->fbuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return 0; -} - -/* - * usbvideo_v4l_open() - * - * This is part of Video 4 Linux API. The driver can be opened by one - * client only (checks internal counter 'uvdser'). The procedure - * then allocates buffers needed for video processing. - * - * History: - * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the - * camera is also initialized here (once per connect), at - * expense of V4L client (it waits on open() call). - * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. - * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). - */ -static int usbvideo_v4l_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct uvd *uvd = (struct uvd *) dev; - const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len; - int i, errCode = 0; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); - - if (usbvideo_ClientIncModCount(uvd) < 0) - return -ENODEV; - mutex_lock(&uvd->lock); - - if (uvd->user) { - err("%s: Someone tried to open an already opened device!", __func__); - errCode = -EBUSY; - } else { - /* Clear statistics */ - memset(&uvd->stats, 0, sizeof(uvd->stats)); - - /* Clean pointers so we know if we allocated something */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) - uvd->sbuf[i].data = NULL; - - /* Allocate memory for the frame buffers */ - uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size; - uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size); - RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE); - if ((uvd->fbuf == NULL) || - (!RingQueue_IsAllocated(&uvd->dp))) { - err("%s: Failed to allocate fbuf or dp", __func__); - errCode = -ENOMEM; - } else { - /* Allocate all buffers */ - for (i=0; i < USBVIDEO_NUMFRAMES; i++) { - uvd->frame[i].frameState = FrameState_Unused; - uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size); - /* - * Set default sizes in case IOCTL (VIDIOCMCAPTURE) - * is not used (using read() instead). - */ - uvd->frame[i].canvas = uvd->canvas; - uvd->frame[i].seqRead_Index = 0; - } - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); - if (uvd->sbuf[i].data == NULL) { - errCode = -ENOMEM; - break; - } - } - } - if (errCode != 0) { - /* Have to free all that memory */ - if (uvd->fbuf != NULL) { - usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); - uvd->fbuf = NULL; - } - RingQueue_Free(&uvd->dp); - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - kfree(uvd->sbuf[i].data); - uvd->sbuf[i].data = NULL; - } - } - } - - /* If so far no errors then we shall start the camera */ - if (errCode == 0) { - /* Start data pump if we have valid endpoint */ - if (uvd->video_endp != 0) - errCode = GET_CALLBACK(uvd, startDataPump)(uvd); - if (errCode == 0) { - if (VALID_CALLBACK(uvd, setupOnOpen)) { - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, - "%s: setupOnOpen callback\n", - __func__); - errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd); - if (errCode < 0) { - err("%s: setupOnOpen callback failed (%d.).", - __func__, errCode); - } else if (uvd->debug > 1) { - dev_info(&uvd->dev->dev, - "%s: setupOnOpen callback successful\n", - __func__); - } - } - if (errCode == 0) { - uvd->settingsAdjusted = 0; - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, - "%s: Open succeeded.\n", - __func__); - uvd->user++; - file->private_data = uvd; - } - } - } - mutex_unlock(&uvd->lock); - if (errCode != 0) - usbvideo_ClientDecModCount(uvd); - if (uvd->debug > 0) - dev_info(&uvd->dev->dev, "%s: Returning %d.\n", __func__, - errCode); - return errCode; -} - -/* - * usbvideo_v4l_close() - * - * This is part of Video 4 Linux API. The procedure - * stops streaming and deallocates all buffers that were earlier - * allocated in usbvideo_v4l_open(). - * - * History: - * 22-Jan-2000 Moved scratch buffer deallocation here. - * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. - * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep. - */ -static int usbvideo_v4l_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct uvd *uvd = (struct uvd *) dev; - int i; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); - - mutex_lock(&uvd->lock); - GET_CALLBACK(uvd, stopDataPump)(uvd); - usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); - uvd->fbuf = NULL; - RingQueue_Free(&uvd->dp); - - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - kfree(uvd->sbuf[i].data); - uvd->sbuf[i].data = NULL; - } - -#if USBVIDEO_REPORT_STATS - usbvideo_ReportStatistics(uvd); -#endif - - uvd->user--; - if (uvd->remove_pending) { - if (uvd->debug > 0) - dev_info(&uvd->dev->dev, "%s: Final disconnect.\n", - __func__); - usbvideo_CameraRelease(uvd); - } - mutex_unlock(&uvd->lock); - usbvideo_ClientDecModCount(uvd); - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s: Completed.\n", __func__); - file->private_data = NULL; - return 0; -} - -/* - * usbvideo_v4l_ioctl() - * - * This is part of Video 4 Linux API. The procedure handles ioctl() calls. - * - * History: - * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. - */ -static long usbvideo_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct uvd *uvd = file->private_data; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return -EIO; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - *b = uvd->vcap; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - *v = uvd->vchan; - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - if (v->channel != 0) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *pic = arg; - *pic = uvd->vpic; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *pic = arg; - /* - * Use temporary 'video_picture' structure to preserve our - * own settings (such as color depth, palette) that we - * aren't allowing everyone (V4L client) to change. - */ - uvd->vpic.brightness = pic->brightness; - uvd->vpic.hue = pic->hue; - uvd->vpic.colour = pic->colour; - uvd->vpic.contrast = pic->contrast; - uvd->settingsAdjusted = 0; /* Will force new settings */ - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - - if(VALID_CALLBACK(uvd, setVideoMode)) { - return GET_CALLBACK(uvd, setVideoMode)(uvd, vw); - } - - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; - if (vw->width != VIDEOSIZE_X(uvd->canvas)) - return -EINVAL; - if (vw->height != VIDEOSIZE_Y(uvd->canvas)) - return -EINVAL; - - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - vw->x = 0; - vw->y = 0; - vw->width = VIDEOSIZE_X(uvd->videosize); - vw->height = VIDEOSIZE_Y(uvd->videosize); - vw->chromakey = 0; - if (VALID_CALLBACK(uvd, getFPS)) - vw->flags = GET_CALLBACK(uvd, getFPS)(uvd); - else - vw->flags = 10; /* FIXME: do better! */ - return 0; - } - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - memset(vm, 0, sizeof(*vm)); - vm->size = uvd->max_frame_size * USBVIDEO_NUMFRAMES; - vm->frames = USBVIDEO_NUMFRAMES; - for(i = 0; i < USBVIDEO_NUMFRAMES; i++) - vm->offsets[i] = i * uvd->max_frame_size; - - return 0; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - - if (uvd->debug >= 1) { - dev_info(&uvd->dev->dev, - "VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.\n", - vm->frame, vm->width, vm->height, vm->format); - } - /* - * Check if the requested size is supported. If the requestor - * requests too big a frame then we may be tricked into accessing - * outside of own preallocated frame buffer (in uvd->frame). - * This will cause oops or a security hole. Theoretically, we - * could only clamp the size down to acceptable bounds, but then - * we'd need to figure out how to insert our smaller buffer into - * larger caller's buffer... this is not an easy question. So we - * here just flatly reject too large requests, assuming that the - * caller will resubmit with smaller size. Callers should know - * what size we support (returned by VIDIOCGCAP). However vidcat, - * for one, does not care and allows to ask for any size. - */ - if ((vm->width > VIDEOSIZE_X(uvd->canvas)) || - (vm->height > VIDEOSIZE_Y(uvd->canvas))) { - if (uvd->debug > 0) { - dev_info(&uvd->dev->dev, - "VIDIOCMCAPTURE: Size=%dx%d " - "too large; allowed only up " - "to %ldx%ld\n", vm->width, - vm->height, - VIDEOSIZE_X(uvd->canvas), - VIDEOSIZE_Y(uvd->canvas)); - } - return -EINVAL; - } - /* Check if the palette is supported */ - if (((1L << vm->format) & uvd->paletteBits) == 0) { - if (uvd->debug > 0) { - dev_info(&uvd->dev->dev, - "VIDIOCMCAPTURE: format=%d. " - "not supported " - "(paletteBits=$%08lx)\n", - vm->format, uvd->paletteBits); - } - return -EINVAL; - } - if ((vm->frame < 0) || (vm->frame >= USBVIDEO_NUMFRAMES)) { - err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm->frame, USBVIDEO_NUMFRAMES-1); - return -EINVAL; - } - if (uvd->frame[vm->frame].frameState == FrameState_Grabbing) { - /* Not an error - can happen */ - } - uvd->frame[vm->frame].request = VIDEOSIZE(vm->width, vm->height); - uvd->frame[vm->frame].palette = vm->format; - - /* Mark it as ready */ - uvd->frame[vm->frame].frameState = FrameState_Ready; - - return usbvideo_NewFrame(uvd, vm->frame); - } - case VIDIOCSYNC: - { - int *frameNum = arg; - int ret; - - if (*frameNum < 0 || *frameNum >= USBVIDEO_NUMFRAMES) - return -EINVAL; - - if (uvd->debug >= 1) - dev_info(&uvd->dev->dev, - "VIDIOCSYNC: syncing to frame %d.\n", - *frameNum); - if (uvd->flags & FLAGS_NO_DECODING) - ret = usbvideo_GetFrame(uvd, *frameNum); - else if (VALID_CALLBACK(uvd, getFrame)) { - ret = GET_CALLBACK(uvd, getFrame)(uvd, *frameNum); - if ((ret < 0) && (uvd->debug >= 1)) { - err("VIDIOCSYNC: getFrame() returned %d.", ret); - } - } else { - err("VIDIOCSYNC: getFrame is not set"); - ret = -EFAULT; - } - - /* - * The frame is in FrameState_Done_Hold state. Release it - * right now because its data is already mapped into - * the user space and it's up to the application to - * make use of it until it asks for another frame. - */ - uvd->frame[*frameNum].frameState = FrameState_Unused; - return ret; - } - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - memset(vb, 0, sizeof(*vb)); - return 0; - } - case VIDIOCKEY: - return 0; - - case VIDIOCCAPTURE: - return -EINVAL; - - case VIDIOCSFBUF: - - case VIDIOCGTUNER: - case VIDIOCSTUNER: - - case VIDIOCGFREQ: - case VIDIOCSFREQ: - - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static long usbvideo_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, usbvideo_v4l_do_ioctl); -} - -/* - * usbvideo_v4l_read() - * - * This is mostly boring stuff. We simply ask for a frame and when it - * arrives copy all the video data from it into user space. There is - * no obvious need to override this method. - * - * History: - * 20-Oct-2000 Created. - * 01-Nov-2000 Added mutex (uvd->lock). - */ -static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct uvd *uvd = file->private_data; - int noblock = file->f_flags & O_NONBLOCK; - int frmx = -1, i; - struct usbvideo_frame *frame; - - if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL)) - return -EFAULT; - - if (uvd->debug >= 1) - dev_info(&uvd->dev->dev, - "%s: %Zd. bytes, noblock=%d.\n", - __func__, count, noblock); - - mutex_lock(&uvd->lock); - - /* See if a frame is completed, then use it. */ - for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { - if ((uvd->frame[i].frameState == FrameState_Done) || - (uvd->frame[i].frameState == FrameState_Done_Hold) || - (uvd->frame[i].frameState == FrameState_Error)) { - frmx = i; - break; - } - } - - /* FIXME: If we don't start a frame here then who ever does? */ - if (noblock && (frmx == -1)) { - count = -EAGAIN; - goto read_done; - } - - /* - * If no FrameState_Done, look for a FrameState_Grabbing state. - * See if a frame is in process (grabbing), then use it. - * We will need to wait until it becomes cooked, of course. - */ - if (frmx == -1) { - for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { - if (uvd->frame[i].frameState == FrameState_Grabbing) { - frmx = i; - break; - } - } - } - - /* - * If no frame is active, start one. We don't care which one - * it will be, so #0 is as good as any. - * In read access mode we don't have convenience of VIDIOCMCAPTURE - * to specify the requested palette (video format) on per-frame - * basis. This means that we have to return data in -some- format - * and just hope that the client knows what to do with it. - * The default format is configured in uvd->defaultPalette field - * as one of VIDEO_PALETTE_xxx values. We stuff it into the new - * frame and initiate the frame filling process. - */ - if (frmx == -1) { - if (uvd->defaultPalette == 0) { - err("%s: No default palette; don't know what to do!", __func__); - count = -EFAULT; - goto read_done; - } - frmx = 0; - /* - * We have no per-frame control over video size. - * Therefore we only can use whatever size was - * specified as default. - */ - uvd->frame[frmx].request = uvd->videosize; - uvd->frame[frmx].palette = uvd->defaultPalette; - uvd->frame[frmx].frameState = FrameState_Ready; - usbvideo_NewFrame(uvd, frmx); - /* Now frame 0 is supposed to start filling... */ - } - - /* - * Get a pointer to the active frame. It is either previously - * completed frame or frame in progress but not completed yet. - */ - frame = &uvd->frame[frmx]; - - /* - * Sit back & wait until the frame gets filled and postprocessed. - * If we fail to get the picture [in time] then return the error. - * In this call we specify that we want the frame to be waited for, - * postprocessed and switched into FrameState_Done_Hold state. This - * state is used to hold the frame as "fully completed" between - * subsequent partial reads of the same frame. - */ - if (frame->frameState != FrameState_Done_Hold) { - long rv = -EFAULT; - if (uvd->flags & FLAGS_NO_DECODING) - rv = usbvideo_GetFrame(uvd, frmx); - else if (VALID_CALLBACK(uvd, getFrame)) - rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx); - else - err("getFrame is not set"); - if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) { - count = rv; - goto read_done; - } - } - - /* - * Copy bytes to user space. We allow for partial reads, which - * means that the user application can request read less than - * the full frame size. It is up to the application to issue - * subsequent calls until entire frame is read. - * - * First things first, make sure we don't copy more than we - * have - even if the application wants more. That would be - * a big security embarassment! - */ - if ((count + frame->seqRead_Index) > frame->seqRead_Length) - count = frame->seqRead_Length - frame->seqRead_Index; - - /* - * Copy requested amount of data to user space. We start - * copying from the position where we last left it, which - * will be zero for a new frame (not read before). - */ - if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) { - count = -EFAULT; - goto read_done; - } - - /* Update last read position */ - frame->seqRead_Index += count; - if (uvd->debug >= 1) { - err("%s: {copy} count used=%Zd, new seqRead_Index=%ld", - __func__, count, frame->seqRead_Index); - } - - /* Finally check if the frame is done with and "release" it */ - if (frame->seqRead_Index >= frame->seqRead_Length) { - /* All data has been read */ - frame->seqRead_Index = 0; - - /* Mark it as available to be used again. */ - uvd->frame[frmx].frameState = FrameState_Unused; - if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) { - err("%s: usbvideo_NewFrame failed.", __func__); - } - } -read_done: - mutex_unlock(&uvd->lock); - return count; -} - -/* - * Make all of the blocks of data contiguous - */ -static int usbvideo_CompressIsochronous(struct uvd *uvd, struct urb *urb) -{ - char *cdata; - int i, totlen = 0; - - for (i = 0; i < urb->number_of_packets; i++) { - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - /* Detect and ignore errored packets */ - if (st < 0) { - if (uvd->debug >= 1) - err("Data error: packet=%d. len=%d. status=%d.", i, n, st); - uvd->stats.iso_err_count++; - continue; - } - - /* Detect and ignore empty packets */ - if (n <= 0) { - uvd->stats.iso_skip_count++; - continue; - } - totlen += n; /* Little local accounting */ - RingQueue_Enqueue(&uvd->dp, cdata, n); - } - return totlen; -} - -static void usbvideo_IsocIrq(struct urb *urb) -{ - int i, ret, len; - struct uvd *uvd = urb->context; - - /* We don't want to do anything if we are about to be removed! */ - if (!CAMERA_IS_OPERATIONAL(uvd)) - return; -#if 0 - if (urb->actual_length > 0) { - dev_info(&uvd->dev->dev, - "urb=$%p status=%d. errcount=%d. length=%d.\n", - urb, urb->status, urb->error_count, - urb->actual_length); - } else { - static int c = 0; - if (c++ % 100 == 0) - dev_info(&uvd->dev->dev, "No Isoc data\n"); - } -#endif - - if (!uvd->streaming) { - if (uvd->debug >= 1) - dev_info(&uvd->dev->dev, - "Not streaming, but interrupt!\n"); - return; - } - - uvd->stats.urb_count++; - if (urb->actual_length <= 0) - goto urb_done_with; - - /* Copy the data received into ring queue */ - len = usbvideo_CompressIsochronous(uvd, urb); - uvd->stats.urb_length = len; - if (len <= 0) - goto urb_done_with; - - /* Here we got some data */ - uvd->stats.data_count += len; - RingQueue_WakeUpInterruptible(&uvd->dp); - -urb_done_with: - for (i = 0; i < FRAMES_PER_DESC; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - urb->status = 0; - urb->dev = uvd->dev; - ret = usb_submit_urb (urb, GFP_KERNEL); - if(ret) - err("usb_submit_urb error (%d)", ret); - return; -} - -/* - * usbvideo_StartDataPump() - * - * History: - * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead - * of hardcoded values. Simplified by using for loop, - * allowed any number of URBs. - */ -static int usbvideo_StartDataPump(struct uvd *uvd) -{ - struct usb_device *dev = uvd->dev; - int i, errFlag; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd); - - if (!CAMERA_IS_OPERATIONAL(uvd)) { - err("%s: Camera is not operational", __func__); - return -EFAULT; - } - uvd->curframe = -1; - - /* Alternate interface 1 is is the biggest frame size */ - i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); - if (i < 0) { - err("%s: usb_set_interface error", __func__); - uvd->last_error = i; - return -EBUSY; - } - if (VALID_CALLBACK(uvd, videoStart)) - GET_CALLBACK(uvd, videoStart)(uvd); - else - err("%s: videoStart not set", __func__); - - /* We double buffer the Iso lists */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - int j, k; - struct urb *urb = uvd->sbuf[i].urb; - urb->dev = dev; - urb->context = uvd; - urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); - urb->interval = 1; - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = uvd->sbuf[i].data; - urb->complete = usbvideo_IsocIrq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; - for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = uvd->iso_packet_len; - } - } - - /* Submit all URBs */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); - if (errFlag) - err("%s: usb_submit_isoc(%d) ret %d", __func__, i, errFlag); - } - - uvd->streaming = 1; - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, - "%s: streaming=1 video_endp=$%02x\n", __func__, - uvd->video_endp); - return 0; -} - -/* - * usbvideo_StopDataPump() - * - * This procedure stops streaming and deallocates URBs. Then it - * activates zero-bandwidth alt. setting of the video interface. - * - * History: - * 22-Jan-2000 Corrected order of actions to work after surprise removal. - * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values. - */ -static void usbvideo_StopDataPump(struct uvd *uvd) -{ - int i, j; - - if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) - return; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd); - - /* Unschedule all of the iso td's */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - usb_kill_urb(uvd->sbuf[i].urb); - } - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s: streaming=0\n", __func__); - uvd->streaming = 0; - - if (!uvd->remove_pending) { - /* Invoke minidriver's magic to stop the camera */ - if (VALID_CALLBACK(uvd, videoStop)) - GET_CALLBACK(uvd, videoStop)(uvd); - else - err("%s: videoStop not set", __func__); - - /* Set packet size to 0 */ - j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); - if (j < 0) { - err("%s: usb_set_interface() error %d.", __func__, j); - uvd->last_error = j; - } - } -} - -/* - * usbvideo_NewFrame() - * - * History: - * 29-Mar-00 Added copying of previous frame into the current one. - * 6-Aug-00 Added model 3 video sizes, removed redundant width, height. - */ -static int usbvideo_NewFrame(struct uvd *uvd, int framenum) -{ - struct usbvideo_frame *frame; - int n; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "usbvideo_NewFrame($%p,%d.)\n", uvd, - framenum); - - /* If we're not grabbing a frame right now and the other frame is */ - /* ready to be grabbed into, then use it instead */ - if (uvd->curframe != -1) - return 0; - - /* If necessary we adjust picture settings between frames */ - if (!uvd->settingsAdjusted) { - if (VALID_CALLBACK(uvd, adjustPicture)) - GET_CALLBACK(uvd, adjustPicture)(uvd); - uvd->settingsAdjusted = 1; - } - - n = (framenum + 1) % USBVIDEO_NUMFRAMES; - if (uvd->frame[n].frameState == FrameState_Ready) - framenum = n; - - frame = &uvd->frame[framenum]; - - frame->frameState = FrameState_Grabbing; - frame->scanstate = ScanState_Scanning; - frame->seqRead_Length = 0; /* Accumulated in xxx_parse_data() */ - frame->deinterlace = Deinterlace_None; - frame->flags = 0; /* No flags yet, up to minidriver (or us) to set them */ - uvd->curframe = framenum; - - /* - * Normally we would want to copy previous frame into the current one - * before we even start filling it with data; this allows us to stop - * filling at any moment; top portion of the frame will be new and - * bottom portion will stay as it was in previous frame. If we don't - * do that then missing chunks of video stream will result in flickering - * portions of old data whatever it was before. - * - * If we choose not to copy previous frame (to, for example, save few - * bus cycles - the frame can be pretty large!) then we have an option - * to clear the frame before using. If we experience losses in this - * mode then missing picture will be black (no flickering). - * - * Finally, if user chooses not to clean the current frame before - * filling it with data then the old data will be visible if we fail - * to refill entire frame with new data. - */ - if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) { - /* This copies previous frame into this one to mask losses */ - int prev = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES; - memmove(frame->data, uvd->frame[prev].data, uvd->max_frame_size); - } else { - if (uvd->flags & FLAGS_CLEAN_FRAMES) { - /* This provides a "clean" frame but slows things down */ - memset(frame->data, 0, uvd->max_frame_size); - } - } - return 0; -} - -/* - * usbvideo_CollectRawData() - * - * This procedure can be used instead of 'processData' callback if you - * only want to dump the raw data from the camera into the output - * device (frame buffer). You can look at it with V4L client, but the - * image will be unwatchable. The main purpose of this code and of the - * mode FLAGS_NO_DECODING is debugging and capturing of datastreams from - * new, unknown cameras. This procedure will be automatically invoked - * instead of the specified callback handler when uvd->flags has bit - * FLAGS_NO_DECODING set. Therefore, any regular build of any driver - * based on usbvideo can use this feature at any time. - */ -static void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame) -{ - int n; - - assert(uvd != NULL); - assert(frame != NULL); - - /* Try to move data from queue into frame buffer */ - n = RingQueue_GetLength(&uvd->dp); - if (n > 0) { - int m; - /* See how much space we have left */ - m = uvd->max_frame_size - frame->seqRead_Length; - if (n > m) - n = m; - /* Now move that much data into frame buffer */ - RingQueue_Dequeue( - &uvd->dp, - frame->data + frame->seqRead_Length, - m); - frame->seqRead_Length += m; - } - /* See if we filled the frame */ - if (frame->seqRead_Length >= uvd->max_frame_size) { - frame->frameState = FrameState_Done; - uvd->curframe = -1; - uvd->stats.frame_num++; - } -} - -static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) -{ - struct usbvideo_frame *frame = &uvd->frame[frameNum]; - - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, "%s($%p,%d.)\n", __func__, uvd, - frameNum); - - switch (frame->frameState) { - case FrameState_Unused: - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, "%s: FrameState_Unused\n", - __func__); - return -EINVAL; - case FrameState_Ready: - case FrameState_Grabbing: - case FrameState_Error: - { - int ntries, signalPending; - redo: - if (!CAMERA_IS_OPERATIONAL(uvd)) { - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Camera is not operational (1)\n", - __func__); - return -EIO; - } - ntries = 0; - do { - RingQueue_InterruptibleSleepOn(&uvd->dp); - signalPending = signal_pending(current); - if (!CAMERA_IS_OPERATIONAL(uvd)) { - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Camera is not " - "operational (2)\n", __func__); - return -EIO; - } - assert(uvd->fbuf != NULL); - if (signalPending) { - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Signal=$%08x\n", __func__, - signalPending); - if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) { - usbvideo_TestPattern(uvd, 1, 0); - uvd->curframe = -1; - uvd->stats.frame_num++; - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Forced test " - "pattern screen\n", - __func__); - return 0; - } else { - /* Standard answer: Interrupted! */ - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Interrupted!\n", - __func__); - return -EINTR; - } - } else { - /* No signals - we just got new data in dp queue */ - if (uvd->flags & FLAGS_NO_DECODING) - usbvideo_CollectRawData(uvd, frame); - else if (VALID_CALLBACK(uvd, processData)) - GET_CALLBACK(uvd, processData)(uvd, frame); - else - err("%s: processData not set", __func__); - } - } while (frame->frameState == FrameState_Grabbing); - if (uvd->debug >= 2) { - dev_info(&uvd->dev->dev, - "%s: Grabbing done; state=%d. (%lu. bytes)\n", - __func__, frame->frameState, - frame->seqRead_Length); - } - if (frame->frameState == FrameState_Error) { - int ret = usbvideo_NewFrame(uvd, frameNum); - if (ret < 0) { - err("%s: usbvideo_NewFrame() failed (%d.)", __func__, ret); - return ret; - } - goto redo; - } - /* Note that we fall through to meet our destiny below */ - } - case FrameState_Done: - /* - * Do all necessary postprocessing of data prepared in - * "interrupt" code and the collecting code above. The - * frame gets marked as FrameState_Done by queue parsing code. - * This status means that we collected enough data and - * most likely processed it as we went through. However - * the data may need postprocessing, such as deinterlacing - * or picture adjustments implemented in software (horror!) - * - * As soon as the frame becomes "final" it gets promoted to - * FrameState_Done_Hold status where it will remain until the - * caller consumed all the video data from the frame. Then - * the empty shell of ex-frame is thrown out for dogs to eat. - * But we, worried about pets, will recycle the frame! - */ - uvd->stats.frame_num++; - if ((uvd->flags & FLAGS_NO_DECODING) == 0) { - if (VALID_CALLBACK(uvd, postProcess)) - GET_CALLBACK(uvd, postProcess)(uvd, frame); - if (frame->flags & USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST) - usbvideo_SoftwareContrastAdjustment(uvd, frame); - } - frame->frameState = FrameState_Done_Hold; - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Entered FrameState_Done_Hold state.\n", - __func__); - return 0; - - case FrameState_Done_Hold: - /* - * We stay in this state indefinitely until someone external, - * like ioctl() or read() call finishes digesting the frame - * data. Then it will mark the frame as FrameState_Unused and - * it will be released back into the wild to roam freely. - */ - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: FrameState_Done_Hold state.\n", - __func__); - return 0; - } - - /* Catch-all for other cases. We shall not be here. */ - err("%s: Invalid state %d.", __func__, frame->frameState); - frame->frameState = FrameState_Unused; - return 0; -} - -/* - * usbvideo_DeinterlaceFrame() - * - * This procedure deinterlaces the given frame. Some cameras produce - * only half of scanlines - sometimes only even lines, sometimes only - * odd lines. The deinterlacing method is stored in frame->deinterlace - * variable. - * - * Here we scan the frame vertically and replace missing scanlines with - * average between surrounding ones - before and after. If we have no - * line above then we just copy next line. Similarly, if we need to - * create a last line then preceding line is used. - */ -void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame) -{ - if ((uvd == NULL) || (frame == NULL)) - return; - - if ((frame->deinterlace == Deinterlace_FillEvenLines) || - (frame->deinterlace == Deinterlace_FillOddLines)) - { - const int v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; - int i = (frame->deinterlace == Deinterlace_FillEvenLines) ? 0 : 1; - - for (; i < VIDEOSIZE_Y(frame->request); i += 2) { - const unsigned char *fs1, *fs2; - unsigned char *fd; - int ip, in, j; /* Previous and next lines */ - - /* - * Need to average lines before and after 'i'. - * If we go out of bounds seeking those lines then - * we point back to existing line. - */ - ip = i - 1; /* First, get rough numbers */ - in = i + 1; - - /* Now validate */ - if (ip < 0) - ip = in; - if (in >= VIDEOSIZE_Y(frame->request)) - in = ip; - - /* Sanity check */ - if ((ip < 0) || (in < 0) || - (ip >= VIDEOSIZE_Y(frame->request)) || - (in >= VIDEOSIZE_Y(frame->request))) - { - err("Error: ip=%d. in=%d. req.height=%ld.", - ip, in, VIDEOSIZE_Y(frame->request)); - break; - } - - /* Now we need to average lines 'ip' and 'in' to produce line 'i' */ - fs1 = frame->data + (v4l_linesize * ip); - fs2 = frame->data + (v4l_linesize * in); - fd = frame->data + (v4l_linesize * i); - - /* Average lines around destination */ - for (j=0; j < v4l_linesize; j++) { - fd[j] = (unsigned char)((((unsigned) fs1[j]) + - ((unsigned)fs2[j])) >> 1); - } - } - } - - /* Optionally display statistics on the screen */ - if (uvd->flags & FLAGS_OVERLAY_STATS) - usbvideo_OverlayStats(uvd, frame); -} - -EXPORT_SYMBOL(usbvideo_DeinterlaceFrame); - -/* - * usbvideo_SoftwareContrastAdjustment() - * - * This code adjusts the contrast of the frame, assuming RGB24 format. - * As most software image processing, this job is CPU-intensive. - * Get a camera that supports hardware adjustment! - * - * History: - * 09-Feb-2001 Created. - */ -static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, - struct usbvideo_frame *frame) -{ - int i, j, v4l_linesize; - signed long adj; - const int ccm = 128; /* Color correction median - see below */ - - if ((uvd == NULL) || (frame == NULL)) { - err("%s: Illegal call.", __func__); - return; - } - adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ - RESTRICT_TO_RANGE(adj, -ccm, ccm+1); - if (adj == 0) { - /* In rare case of no adjustment */ - return; - } - v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; - for (i=0; i < VIDEOSIZE_Y(frame->request); i++) { - unsigned char *fd = frame->data + (v4l_linesize * i); - for (j=0; j < v4l_linesize; j++) { - signed long v = (signed long) fd[j]; - /* Magnify up to 2 times, reduce down to zero */ - v = 128 + ((ccm + adj) * (v - 128)) / ccm; - RESTRICT_TO_RANGE(v, 0, 0xFF); /* Must flatten tails */ - fd[j] = (unsigned char) v; - } - } -} - -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/usbvideo/usbvideo.h b/drivers/staging/usbvideo/usbvideo.h deleted file mode 100644 index 95638a0..0000000 --- a/drivers/staging/usbvideo/usbvideo.h +++ /dev/null @@ -1,395 +0,0 @@ -/* - * 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, 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 usbvideo_h -#define usbvideo_h - -#include "videodev.h" -#include -#include -#include -#include - -/* Most helpful debugging aid */ -#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) - -#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */ - -/* Bit flags (options) */ -#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) -#define FLAGS_MONOCHROME (1 << 1) -#define FLAGS_DISPLAY_HINTS (1 << 2) -#define FLAGS_OVERLAY_STATS (1 << 3) -#define FLAGS_FORCE_TESTPATTERN (1 << 4) -#define FLAGS_SEPARATE_FRAMES (1 << 5) -#define FLAGS_CLEAN_FRAMES (1 << 6) -#define FLAGS_NO_DECODING (1 << 7) - -/* Bit flags for frames (apply to the frame where they are specified) */ -#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0) - -/* Camera capabilities (maximum) */ -#define CAMERA_URB_FRAMES 32 -#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */ -#define FRAMES_PER_DESC (CAMERA_URB_FRAMES) -#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET) - -/* This macro restricts an int variable to an inclusive range */ -#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } - -#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */ - -/* - * Use this macro to construct constants for different video sizes. - * We have to deal with different video sizes that have to be - * configured in the device or compared against when we receive - * a data. Normally one would define a bunch of VIDEOSIZE_x_by_y - * #defines and that's the end of story. However this solution - * does not allow to convert between real pixel sizes and the - * constant (integer) value that may be used to tag a frame or - * whatever. The set of macros below constructs videosize constants - * from the pixel size and allows to reconstruct the pixel size - * from the combined value later. - */ -#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16)) -#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL) -#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL) -typedef unsigned long videosize_t; - -/* - * This macro checks if the camera is still operational. The 'uvd' - * pointer must be valid, uvd->dev must be valid, we are not - * removing the device and the device has not erred on us. - */ -#define CAMERA_IS_OPERATIONAL(uvd) (\ - (uvd != NULL) && \ - ((uvd)->dev != NULL) && \ - ((uvd)->last_error == 0) && \ - (!(uvd)->remove_pending)) - -/* - * We use macros to do YUV -> RGB conversion because this is - * very important for speed and totally unimportant for size. - * - * YUV -> RGB Conversion - * --------------------- - * - * B = 1.164*(Y-16) + 2.018*(V-128) - * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) - * R = 1.164*(Y-16) + 1.596*(U-128) - * - * If you fancy integer arithmetics (as you should), hear this: - * - * 65536*B = 76284*(Y-16) + 132252*(V-128) - * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) - * 65536*R = 76284*(Y-16) + 104595*(U-128) - * - * Make sure the output values are within [0..255] range. - */ -#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) -#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ - int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ - mm_y = (my) - 16; \ - mm_u = (mu) - 128; \ - mm_v = (mv) - 128; \ - mm_yc= mm_y * 76284; \ - mm_b = (mm_yc + 132252*mm_v ) >> 16; \ - mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ - mm_r = (mm_yc + 104595*mm_u ) >> 16; \ - mb = LIMIT_RGB(mm_b); \ - mg = LIMIT_RGB(mm_g); \ - mr = LIMIT_RGB(mm_r); \ -} - -#define RING_QUEUE_SIZE (128*1024) /* Must be a power of 2 */ -#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1) -#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n) -#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)]) - -struct RingQueue { - unsigned char *queue; /* Data from the Isoc data pump */ - int length; /* How many bytes allocated for the queue */ - int wi; /* That's where we write */ - int ri; /* Read from here until you hit write index */ - wait_queue_head_t wqh; /* Processes waiting */ -}; - -enum ScanState { - ScanState_Scanning, /* Scanning for header */ - ScanState_Lines /* Parsing lines */ -}; - -/* Completion states of the data parser */ -enum ParseState { - scan_Continue, /* Just parse next item */ - scan_NextFrame, /* Frame done, send it to V4L */ - scan_Out, /* Not enough data for frame */ - scan_EndParse /* End parsing */ -}; - -enum FrameState { - FrameState_Unused, /* Unused (no MCAPTURE) */ - FrameState_Ready, /* Ready to start grabbing */ - FrameState_Grabbing, /* In the process of being grabbed into */ - FrameState_Done, /* Finished grabbing, but not been synced yet */ - FrameState_Done_Hold, /* Are syncing or reading */ - FrameState_Error, /* Something bad happened while processing */ -}; - -/* - * Some frames may contain only even or odd lines. This type - * specifies what type of deinterlacing is required. - */ -enum Deinterlace { - Deinterlace_None=0, - Deinterlace_FillOddLines, - Deinterlace_FillEvenLines -}; - -#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */ -#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */ - -/* This structure represents one Isoc request - URB and buffer */ -struct usbvideo_sbuf { - char *data; - struct urb *urb; -}; - -struct usbvideo_frame { - char *data; /* Frame buffer */ - unsigned long header; /* Significant bits from the header */ - - videosize_t canvas; /* The canvas (max. image) allocated */ - videosize_t request; /* That's what the application asked for */ - unsigned short palette; /* The desired format */ - - enum FrameState frameState;/* State of grabbing */ - enum ScanState scanstate; /* State of scanning */ - enum Deinterlace deinterlace; - int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */ - - int curline; /* Line of frame we're working on */ - - long seqRead_Length; /* Raw data length of frame */ - long seqRead_Index; /* Amount of data that has been already read */ - - void *user; /* Additional data that user may need */ -}; - -/* Statistics that can be overlaid on screen */ -struct usbvideo_statistics { - unsigned long frame_num; /* Sequential number of the frame */ - unsigned long urb_count; /* How many URBs we received so far */ - unsigned long urb_length; /* Length of last URB */ - unsigned long data_count; /* How many bytes we received */ - unsigned long header_count; /* How many frame headers we found */ - unsigned long iso_skip_count; /* How many empty ISO packets received */ - unsigned long iso_err_count; /* How many bad ISO packets received */ -}; - -struct usbvideo; - -struct uvd { - struct video_device vdev; /* Must be the first field! */ - struct usb_device *dev; - struct usbvideo *handle; /* Points back to the struct usbvideo */ - void *user_data; /* Camera-dependent data */ - int user_size; /* Size of that camera-dependent data */ - int debug; /* Debug level for usbvideo */ - unsigned char iface; /* Video interface number */ - unsigned char video_endp; - unsigned char ifaceAltActive; - unsigned char ifaceAltInactive; /* Alt settings */ - unsigned long flags; /* FLAGS_USBVIDEO_xxx */ - unsigned long paletteBits; /* Which palettes we accept? */ - unsigned short defaultPalette; /* What palette to use for read() */ - struct mutex lock; - int user; /* user count for exclusive use */ - - videosize_t videosize; /* Current setting */ - videosize_t canvas; /* This is the width,height of the V4L canvas */ - int max_frame_size; /* Bytes in one video frame */ - - int uvd_used; /* Is this structure in use? */ - int streaming; /* Are we streaming Isochronous? */ - int grabbing; /* Are we grabbing? */ - int settingsAdjusted; /* Have we adjusted contrast etc.? */ - int last_error; /* What calamity struck us? */ - - char *fbuf; /* Videodev buffer area */ - int fbuf_size; /* Videodev buffer size */ - - int curframe; - int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */ - - struct RingQueue dp; /* Isoc data pump */ - struct usbvideo_frame frame[USBVIDEO_NUMFRAMES]; - struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF]; - - volatile int remove_pending; /* If set then about to exit */ - - struct video_picture vpic, vpic_old; /* Picture settings */ - struct video_capability vcap; /* Video capabilities */ - struct video_channel vchan; /* May be used for tuner support */ - struct usbvideo_statistics stats; - char videoName[32]; /* Holds name like "video7" */ -}; - -/* - * usbvideo callbacks (virtual methods). They are set when usbvideo - * services are registered. All of these default to NULL, except those - * that default to usbvideo-provided methods. - */ -struct usbvideo_cb { - int (*probe)(struct usb_interface *, const struct usb_device_id *); - void (*userFree)(struct uvd *); - void (*disconnect)(struct usb_interface *); - int (*setupOnOpen)(struct uvd *); - void (*videoStart)(struct uvd *); - void (*videoStop)(struct uvd *); - void (*processData)(struct uvd *, struct usbvideo_frame *); - void (*postProcess)(struct uvd *, struct usbvideo_frame *); - void (*adjustPicture)(struct uvd *); - int (*getFPS)(struct uvd *); - int (*overlayHook)(struct uvd *, struct usbvideo_frame *); - int (*getFrame)(struct uvd *, int); - int (*startDataPump)(struct uvd *uvd); - void (*stopDataPump)(struct uvd *uvd); - int (*setVideoMode)(struct uvd *uvd, struct video_window *vw); -}; - -struct usbvideo { - int num_cameras; /* As allocated */ - struct usb_driver usbdrv; /* Interface to the USB stack */ - char drvName[80]; /* Driver name */ - struct mutex lock; /* Mutex protecting camera structures */ - struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */ - struct video_device vdt; /* Video device template */ - struct uvd *cam; /* Array of camera structures */ - struct module *md_module; /* Minidriver module */ -}; - - -/* - * This macro retrieves callback address from the struct uvd object. - * No validity checks are done here, so be sure to check the - * callback beforehand with VALID_CALLBACK. - */ -#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName) - -/* - * This macro returns either callback pointer or NULL. This is safe - * macro, meaning that most of components of data structures involved - * may be NULL - this only results in NULL being returned. You may - * wish to use this macro to make sure that the callback is callable. - * However keep in mind that those checks take time. - */ -#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \ - ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL) - -int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len); -int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n); -void RingQueue_WakeUpInterruptible(struct RingQueue *rq); -void RingQueue_Flush(struct RingQueue *rq); - -static inline int RingQueue_GetLength(const struct RingQueue *rq) -{ - return (rq->wi - rq->ri + rq->length) & (rq->length-1); -} - -static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq) -{ - return rq->length - RingQueue_GetLength(rq); -} - -void usbvideo_DrawLine( - struct usbvideo_frame *frame, - int x1, int y1, - int x2, int y2, - unsigned char cr, unsigned char cg, unsigned char cb); -void usbvideo_HexDump(const unsigned char *data, int len); -void usbvideo_SayAndWait(const char *what); -void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode); - -/* Memory allocation routines */ -unsigned long usbvideo_kvirt_to_pa(unsigned long adr); - -int usbvideo_register( - struct usbvideo **pCams, - const int num_cams, - const int num_extra, - const char *driverName, - const struct usbvideo_cb *cbTable, - struct module *md, - const struct usb_device_id *id_table); -struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams); -int usbvideo_RegisterVideoDevice(struct uvd *uvd); -void usbvideo_Deregister(struct usbvideo **uvt); - -int usbvideo_v4l_initialize(struct video_device *dev); - -void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame); - -/* - * This code performs bounds checking - use it when working with - * new formats, or else you may get oopses all over the place. - * If pixel falls out of bounds then it gets shoved back (as close - * to place of offence as possible) and is painted bright red. - * - * There are two important concepts: frame width, height and - * V4L canvas width, height. The former is the area requested by - * the application -for this very frame-. The latter is the largest - * possible frame that we can serve (we advertise that via V4L ioctl). - * The frame data is expected to be formatted as lines of length - * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines. - */ -static inline void RGB24_PUTPIXEL( - struct usbvideo_frame *fr, - int ix, int iy, - unsigned char vr, - unsigned char vg, - unsigned char vb) -{ - register unsigned char *pf; - int limiter = 0, mx, my; - mx = ix; - my = iy; - if (mx < 0) { - mx=0; - limiter++; - } else if (mx >= VIDEOSIZE_X((fr)->request)) { - mx= VIDEOSIZE_X((fr)->request) - 1; - limiter++; - } - if (my < 0) { - my = 0; - limiter++; - } else if (my >= VIDEOSIZE_Y((fr)->request)) { - my = VIDEOSIZE_Y((fr)->request) - 1; - limiter++; - } - pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix)); - if (limiter) { - *pf++ = 0; - *pf++ = 0; - *pf++ = 0xFF; - } else { - *pf++ = (vb); - *pf++ = (vg); - *pf++ = (vr); - } -} - -#endif /* usbvideo_h */ diff --git a/drivers/staging/usbvideo/vicam.c b/drivers/staging/usbvideo/vicam.c deleted file mode 100644 index ecdb121..0000000 --- a/drivers/staging/usbvideo/vicam.c +++ /dev/null @@ -1,952 +0,0 @@ -/* - * USB ViCam WebCam driver - * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), - * Christopher L Cheney (ccheney@cheney.cx), - * Pavel Machek (pavel@ucw.cz), - * John Tyner (jtyner@cs.ucr.edu), - * Monroe Williams (monroe@pobox.com) - * - * Supports 3COM HomeConnect PC Digital WebCam - * Supports Compro PS39U WebCam - * - * 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. - * - * This source code is based heavily on the CPiA webcam driver which was - * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt - * - * Portions of this code were also copied from usbvideo.c - * - * Special thanks to the whole team at Sourceforge for help making - * this driver become a reality. Notably: - * Andy Armstrong who reverse engineered the color encoding and - * Pavel Machek and Chris Cheney who worked on reverse engineering the - * camera controls and wrote the first generation driver. - */ - -#include -#include -#include -#include "videodev.h" -#include -#include -#include -#include -#include -#include -#include -#include "usbvideo.h" - -// #define VICAM_DEBUG - -#ifdef VICAM_DEBUG -#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __func__, lineno, ##args) -#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args) -#else -#define DBG(fmn,args...) do {} while(0) -#endif - -#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org" -#define DRIVER_DESC "ViCam WebCam Driver" - -/* Define these values to match your device */ -#define USB_VICAM_VENDOR_ID 0x04c1 -#define USB_VICAM_PRODUCT_ID 0x009d -#define USB_COMPRO_VENDOR_ID 0x0602 -#define USB_COMPRO_PRODUCT_ID 0x1001 - -#define VICAM_BYTES_PER_PIXEL 3 -#define VICAM_MAX_READ_SIZE (512*242+128) -#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240) -#define VICAM_FRAMES 2 - -#define VICAM_HEADER_SIZE 64 - -/* rvmalloc / rvfree copied from usbvideo.c - * - * Not sure why these are not yet non-statics which I can reference through - * usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime - * in the future. - * -*/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -struct vicam_camera { - u16 shutter_speed; // capture shutter speed - u16 gain; // capture gain - - u8 *raw_image; // raw data captured from the camera - u8 *framebuf; // processed data in RGB24 format - u8 *cntrlbuf; // area used to send control msgs - - struct video_device vdev; // v4l video device - struct usb_device *udev; // usb device - - /* guard against simultaneous accesses to the camera */ - struct mutex cam_lock; - - int is_initialized; - u8 open_count; - u8 bulkEndpoint; - int needsDummyRead; -}; - -static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id); -static void vicam_disconnect(struct usb_interface *intf); -static void read_frame(struct vicam_camera *cam, int framenum); -static void vicam_decode_color(const u8 *, u8 *); - -static int __send_control_msg(struct vicam_camera *cam, - u8 request, - u16 value, - u16 index, - unsigned char *cp, - u16 size) -{ - int status; - - /* cp must be memory that has been allocated by kmalloc */ - - status = usb_control_msg(cam->udev, - usb_sndctrlpipe(cam->udev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, value, index, - cp, size, 1000); - - status = min(status, 0); - - if (status < 0) { - printk(KERN_INFO "Failed sending control message, error %d.\n", - status); - } - - return status; -} - -static int send_control_msg(struct vicam_camera *cam, - u8 request, - u16 value, - u16 index, - unsigned char *cp, - u16 size) -{ - int status = -ENODEV; - mutex_lock(&cam->cam_lock); - if (cam->udev) { - status = __send_control_msg(cam, request, value, - index, cp, size); - } - mutex_unlock(&cam->cam_lock); - return status; -} -static int -initialize_camera(struct vicam_camera *cam) -{ - int err; - const struct ihex_binrec *rec; - const struct firmware *uninitialized_var(fw); - - err = request_ihex_firmware(&fw, "vicam/firmware.fw", &cam->udev->dev); - if (err) { - printk(KERN_ERR "Failed to load \"vicam/firmware.fw\": %d\n", - err); - return err; - } - - for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { - memcpy(cam->cntrlbuf, rec->data, be16_to_cpu(rec->len)); - - err = send_control_msg(cam, 0xff, 0, 0, - cam->cntrlbuf, be16_to_cpu(rec->len)); - if (err) - break; - } - - release_firmware(fw); - - return err; -} - -static int -set_camera_power(struct vicam_camera *cam, int state) -{ - int status; - - if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0) - return status; - - if (state) { - send_control_msg(cam, 0x55, 1, 0, NULL, 0); - } - - return 0; -} - -static long -vicam_ioctl(struct file *file, unsigned int ioctlnr, unsigned long arg) -{ - void __user *user_arg = (void __user *)arg; - struct vicam_camera *cam = file->private_data; - long retval = 0; - - if (!cam) - return -ENODEV; - - switch (ioctlnr) { - /* query capabilities */ - case VIDIOCGCAP: - { - struct video_capability b; - - DBG("VIDIOCGCAP\n"); - memset(&b, 0, sizeof(b)); - strcpy(b.name, "ViCam-based Camera"); - b.type = VID_TYPE_CAPTURE; - b.channels = 1; - b.audios = 0; - b.maxwidth = 320; /* VIDEOSIZE_CIF */ - b.maxheight = 240; - b.minwidth = 320; /* VIDEOSIZE_48_48 */ - b.minheight = 240; - - if (copy_to_user(user_arg, &b, sizeof(b))) - retval = -EFAULT; - - break; - } - /* get/set video source - we are a camera and nothing else */ - case VIDIOCGCHAN: - { - struct video_channel v; - - DBG("VIDIOCGCHAN\n"); - if (copy_from_user(&v, user_arg, sizeof(v))) { - retval = -EFAULT; - break; - } - if (v.channel != 0) { - retval = -EINVAL; - break; - } - - v.channel = 0; - strcpy(v.name, "Camera"); - v.tuners = 0; - v.flags = 0; - v.type = VIDEO_TYPE_CAMERA; - v.norm = 0; - - if (copy_to_user(user_arg, &v, sizeof(v))) - retval = -EFAULT; - break; - } - - case VIDIOCSCHAN: - { - int v; - - if (copy_from_user(&v, user_arg, sizeof(v))) - retval = -EFAULT; - DBG("VIDIOCSCHAN %d\n", v); - - if (retval == 0 && v != 0) - retval = -EINVAL; - - break; - } - - /* image properties */ - case VIDIOCGPICT: - { - struct video_picture vp; - DBG("VIDIOCGPICT\n"); - memset(&vp, 0, sizeof (struct video_picture)); - vp.brightness = cam->gain << 8; - vp.depth = 24; - vp.palette = VIDEO_PALETTE_RGB24; - if (copy_to_user(user_arg, &vp, sizeof (struct video_picture))) - retval = -EFAULT; - break; - } - - case VIDIOCSPICT: - { - struct video_picture vp; - - if (copy_from_user(&vp, user_arg, sizeof(vp))) { - retval = -EFAULT; - break; - } - - DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth, - vp.palette); - - cam->gain = vp.brightness >> 8; - - if (vp.depth != 24 - || vp.palette != VIDEO_PALETTE_RGB24) - retval = -EINVAL; - - break; - } - - /* get/set capture window */ - case VIDIOCGWIN: - { - struct video_window vw; - vw.x = 0; - vw.y = 0; - vw.width = 320; - vw.height = 240; - vw.chromakey = 0; - vw.flags = 0; - vw.clips = NULL; - vw.clipcount = 0; - - DBG("VIDIOCGWIN\n"); - - if (copy_to_user(user_arg, (void *)&vw, sizeof(vw))) - retval = -EFAULT; - - // I'm not sure what the deal with a capture window is, it is very poorly described - // in the doc. So I won't support it now. - break; - } - - case VIDIOCSWIN: - { - - struct video_window vw; - - if (copy_from_user(&vw, user_arg, sizeof(vw))) { - retval = -EFAULT; - break; - } - - DBG("VIDIOCSWIN %d x %d\n", vw.width, vw.height); - - if ( vw.width != 320 || vw.height != 240 ) - retval = -EFAULT; - - break; - } - - /* mmap interface */ - case VIDIOCGMBUF: - { - struct video_mbuf vm; - int i; - - DBG("VIDIOCGMBUF\n"); - memset(&vm, 0, sizeof (vm)); - vm.size = - VICAM_MAX_FRAME_SIZE * VICAM_FRAMES; - vm.frames = VICAM_FRAMES; - for (i = 0; i < VICAM_FRAMES; i++) - vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i; - - if (copy_to_user(user_arg, (void *)&vm, sizeof(vm))) - retval = -EFAULT; - - break; - } - - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - // int video_size; - - if (copy_from_user((void *)&vm, user_arg, sizeof(vm))) { - retval = -EFAULT; - break; - } - - DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format); - - if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 ) - retval = -EINVAL; - - // in theory right here we'd start the image capturing - // (fill in a bulk urb and submit it asynchronously) - // - // Instead we're going to do a total hack job for now and - // retrieve the frame in VIDIOCSYNC - - break; - } - - case VIDIOCSYNC: - { - int frame; - - if (copy_from_user((void *)&frame, user_arg, sizeof(int))) { - retval = -EFAULT; - break; - } - DBG("VIDIOCSYNC: %d\n", frame); - - read_frame(cam, frame); - vicam_decode_color(cam->raw_image, - cam->framebuf + - frame * VICAM_MAX_FRAME_SIZE ); - - break; - } - - /* pointless to implement overlay with this camera */ - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCKEY: - retval = -EINVAL; - break; - - /* tuner interface - we have none */ - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - retval = -EINVAL; - break; - - /* audio interface - we have none */ - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - retval = -EINVAL; - break; - default: - retval = -ENOIOCTLCMD; - break; - } - - return retval; -} - -static int -vicam_open(struct file *file) -{ - struct vicam_camera *cam = video_drvdata(file); - - DBG("open\n"); - - if (!cam) { - printk(KERN_ERR - "vicam video_device improperly initialized"); - return -EINVAL; - } - - /* cam_lock/open_count protects us from simultaneous opens - * ... for now. we probably shouldn't rely on this fact forever. - */ - - mutex_lock(&cam->cam_lock); - if (cam->open_count > 0) { - printk(KERN_INFO - "vicam_open called on already opened camera"); - mutex_unlock(&cam->cam_lock); - return -EBUSY; - } - - cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL); - if (!cam->raw_image) { - mutex_unlock(&cam->cam_lock); - return -ENOMEM; - } - - cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - if (!cam->framebuf) { - kfree(cam->raw_image); - mutex_unlock(&cam->cam_lock); - return -ENOMEM; - } - - cam->cntrlbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!cam->cntrlbuf) { - kfree(cam->raw_image); - rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - mutex_unlock(&cam->cam_lock); - return -ENOMEM; - } - - cam->needsDummyRead = 1; - cam->open_count++; - - file->private_data = cam; - mutex_unlock(&cam->cam_lock); - - - // First upload firmware, then turn the camera on - - if (!cam->is_initialized) { - initialize_camera(cam); - - cam->is_initialized = 1; - } - - set_camera_power(cam, 1); - - return 0; -} - -static int -vicam_close(struct file *file) -{ - struct vicam_camera *cam = file->private_data; - int open_count; - struct usb_device *udev; - - DBG("close\n"); - - /* it's not the end of the world if - * we fail to turn the camera off. - */ - - set_camera_power(cam, 0); - - kfree(cam->raw_image); - rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - kfree(cam->cntrlbuf); - - mutex_lock(&cam->cam_lock); - - cam->open_count--; - open_count = cam->open_count; - udev = cam->udev; - - mutex_unlock(&cam->cam_lock); - - if (!open_count && !udev) { - kfree(cam); - } - - return 0; -} - -static void vicam_decode_color(const u8 *data, u8 *rgb) -{ - /* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB - * Copyright (C) 2002 Monroe Williams (monroe@pobox.com) - */ - - int i, prevY, nextY; - - prevY = 512; - nextY = 512; - - data += VICAM_HEADER_SIZE; - - for( i = 0; i < 240; i++, data += 512 ) { - const int y = ( i * 242 ) / 240; - - int j, prevX, nextX; - int Y, Cr, Cb; - - if ( y == 242 - 1 ) { - nextY = -512; - } - - prevX = 1; - nextX = 1; - - for ( j = 0; j < 320; j++, rgb += 3 ) { - const int x = ( j * 512 ) / 320; - const u8 * const src = &data[x]; - - if ( x == 512 - 1 ) { - nextX = -1; - } - - Cr = ( src[prevX] - src[0] ) + - ( src[nextX] - src[0] ); - Cr /= 2; - - Cb = ( src[prevY] - src[prevX + prevY] ) + - ( src[prevY] - src[nextX + prevY] ) + - ( src[nextY] - src[prevX + nextY] ) + - ( src[nextY] - src[nextX + nextY] ); - Cb /= 4; - - Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 ); - - if ( i & 1 ) { - int Ct = Cr; - Cr = Cb; - Cb = Ct; - } - - if ( ( x ^ i ) & 1 ) { - Cr = -Cr; - Cb = -Cb; - } - - rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) + - 500 ) / 900, 0, 255 ); - rgb[1] = clamp( ( ( Y - ( 392 * Cb ) - - ( 813 * Cr ) ) + - 500 ) / 1000, 0, 255 ); - rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) + - 500 ) / 1300, 0, 255 ); - - prevX = -1; - } - - prevY = -512; - } -} - -static void -read_frame(struct vicam_camera *cam, int framenum) -{ - unsigned char *request = cam->cntrlbuf; - int realShutter; - int n; - int actual_length; - - if (cam->needsDummyRead) { - cam->needsDummyRead = 0; - read_frame(cam, framenum); - } - - memset(request, 0, 16); - request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain - - request[1] = 0; // 512x242 capture - - request[2] = 0x90; // the function of these two bytes - request[3] = 0x07; // is not yet understood - - if (cam->shutter_speed > 60) { - // Short exposure - realShutter = - ((-15631900 / cam->shutter_speed) + 260533) / 1000; - request[4] = realShutter & 0xFF; - request[5] = (realShutter >> 8) & 0xFF; - request[6] = 0x03; - request[7] = 0x01; - } else { - // Long exposure - realShutter = 15600 / cam->shutter_speed - 1; - request[4] = 0; - request[5] = 0; - request[6] = realShutter & 0xFF; - request[7] = realShutter >> 8; - } - - // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0 - request[8] = 0; - // bytes 9-15 do not seem to affect exposure or image quality - - mutex_lock(&cam->cam_lock); - - if (!cam->udev) { - goto done; - } - - n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16); - - if (n < 0) { - printk(KERN_ERR - " Problem sending frame capture control message"); - goto done; - } - - n = usb_bulk_msg(cam->udev, - usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint), - cam->raw_image, - 512 * 242 + 128, &actual_length, 10000); - - if (n < 0) { - printk(KERN_ERR "Problem during bulk read of frame data: %d\n", - n); - } - - done: - mutex_unlock(&cam->cam_lock); -} - -static ssize_t -vicam_read( struct file *file, char __user *buf, size_t count, loff_t *ppos ) -{ - struct vicam_camera *cam = file->private_data; - - DBG("read %d bytes.\n", (int) count); - - if (*ppos >= VICAM_MAX_FRAME_SIZE) { - *ppos = 0; - return 0; - } - - if (*ppos == 0) { - read_frame(cam, 0); - vicam_decode_color(cam->raw_image, - cam->framebuf + - 0 * VICAM_MAX_FRAME_SIZE); - } - - count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos); - - if (copy_to_user(buf, &cam->framebuf[*ppos], count)) { - count = -EFAULT; - } else { - *ppos += count; - } - - if (count == VICAM_MAX_FRAME_SIZE) { - *ppos = 0; - } - - return count; -} - - -static int -vicam_mmap(struct file *file, struct vm_area_struct *vma) -{ - // TODO: allocate the raw frame buffer if necessary - unsigned long page, pos; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - struct vicam_camera *cam = file->private_data; - - if (!cam) - return -ENODEV; - - DBG("vicam_mmap: %ld\n", size); - - /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes - * to the size the application requested for mmap and it was screwing apps up. - if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE) - return -EINVAL; - */ - - pos = (unsigned long)cam->framebuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return 0; -} - -static const struct v4l2_file_operations vicam_fops = { - .owner = THIS_MODULE, - .open = vicam_open, - .release = vicam_close, - .read = vicam_read, - .mmap = vicam_mmap, - .ioctl = vicam_ioctl, -}; - -static struct video_device vicam_template = { - .name = "ViCam-based USB Camera", - .fops = &vicam_fops, - .release = video_device_release_empty, -}; - -/* table of devices that work with this driver */ -static struct usb_device_id vicam_table[] = { - {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)}, - {USB_DEVICE(USB_COMPRO_VENDOR_ID, USB_COMPRO_PRODUCT_ID)}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, vicam_table); - -static struct usb_driver vicam_driver = { - .name = "vicam", - .probe = vicam_probe, - .disconnect = vicam_disconnect, - .id_table = vicam_table -}; - -/** - * vicam_probe - * @intf: the interface - * @id: the device id - * - * Called by the usb core when a new device is connected that it thinks - * this driver might be interested in. - */ -static int -vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - int bulkEndpoint = 0; - const struct usb_host_interface *interface; - const struct usb_endpoint_descriptor *endpoint; - struct vicam_camera *cam; - - printk(KERN_INFO "ViCam based webcam connected\n"); - - interface = intf->cur_altsetting; - - DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n", - interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints)); - endpoint = &interface->endpoint[0].desc; - - if (usb_endpoint_is_bulk_in(endpoint)) { - /* we found a bulk in endpoint */ - bulkEndpoint = endpoint->bEndpointAddress; - } else { - printk(KERN_ERR - "No bulk in endpoint was found ?! (this is bad)\n"); - } - - if ((cam = - kzalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) { - printk(KERN_WARNING - "could not allocate kernel memory for vicam_camera struct\n"); - return -ENOMEM; - } - - - cam->shutter_speed = 15; - - mutex_init(&cam->cam_lock); - - memcpy(&cam->vdev, &vicam_template, sizeof(vicam_template)); - video_set_drvdata(&cam->vdev, cam); - - cam->udev = dev; - cam->bulkEndpoint = bulkEndpoint; - - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) < 0) { - kfree(cam); - printk(KERN_WARNING "video_register_device failed\n"); - return -EIO; - } - - printk(KERN_INFO "ViCam webcam driver now controlling device %s\n", - video_device_node_name(&cam->vdev)); - - usb_set_intfdata (intf, cam); - - return 0; -} - -static void -vicam_disconnect(struct usb_interface *intf) -{ - int open_count; - struct vicam_camera *cam = usb_get_intfdata (intf); - usb_set_intfdata (intf, NULL); - - /* we must unregister the device before taking its - * cam_lock. This is because the video open call - * holds the same lock as video unregister. if we - * unregister inside of the cam_lock and open also - * uses the cam_lock, we get deadlock. - */ - - video_unregister_device(&cam->vdev); - - /* stop the camera from being used */ - - mutex_lock(&cam->cam_lock); - - /* mark the camera as gone */ - - cam->udev = NULL; - - /* the only thing left to do is synchronize with - * our close/release function on who should release - * the camera memory. if there are any users using the - * camera, it's their job. if there are no users, - * it's ours. - */ - - open_count = cam->open_count; - - mutex_unlock(&cam->cam_lock); - - if (!open_count) { - kfree(cam); - } - - printk(KERN_DEBUG "ViCam-based WebCam disconnected\n"); -} - -/* - */ -static int __init -usb_vicam_init(void) -{ - int retval; - DBG(KERN_INFO "ViCam-based WebCam driver startup\n"); - retval = usb_register(&vicam_driver); - if (retval) - printk(KERN_WARNING "usb_register failed!\n"); - return retval; -} - -static void __exit -usb_vicam_exit(void) -{ - DBG(KERN_INFO - "ViCam-based WebCam driver shutdown\n"); - - usb_deregister(&vicam_driver); -} - -module_init(usb_vicam_init); -module_exit(usb_vicam_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("vicam/firmware.fw"); diff --git a/drivers/staging/usbvideo/videodev.h b/drivers/staging/usbvideo/videodev.h deleted file mode 100644 index f11efbe..0000000 --- a/drivers/staging/usbvideo/videodev.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Video for Linux version 1 - OBSOLETE - * - * Header file for v4l1 drivers and applications, for - * Linux kernels 2.2.x or 2.4.x. - * - * Provides header for legacy drivers and applications - * - * See http://linuxtv.org for more info - * - */ -#ifndef __LINUX_VIDEODEV_H -#define __LINUX_VIDEODEV_H - -#include -#include -#include - -#define VID_TYPE_CAPTURE 1 /* Can capture */ -#define VID_TYPE_TUNER 2 /* Can tune */ -#define VID_TYPE_TELETEXT 4 /* Does teletext */ -#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ -#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ -#define VID_TYPE_CLIPPING 32 /* Can clip */ -#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ -#define VID_TYPE_SCALES 128 /* Scalable */ -#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ -#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ -#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ -#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ -#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ -#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ - -struct video_capability -{ - char name[32]; - int type; - int channels; /* Num channels */ - int audios; /* Num audio devices */ - int maxwidth; /* Supported width */ - int maxheight; /* And height */ - int minwidth; /* Supported width */ - int minheight; /* And height */ -}; - - -struct video_channel -{ - int channel; - char name[32]; - int tuners; - __u32 flags; -#define VIDEO_VC_TUNER 1 /* Channel has a tuner */ -#define VIDEO_VC_AUDIO 2 /* Channel has audio */ - __u16 type; -#define VIDEO_TYPE_TV 1 -#define VIDEO_TYPE_CAMERA 2 - __u16 norm; /* Norm set by channel */ -}; - -struct video_tuner -{ - int tuner; - char name[32]; - unsigned long rangelow, rangehigh; /* Tuner range */ - __u32 flags; -#define VIDEO_TUNER_PAL 1 -#define VIDEO_TUNER_NTSC 2 -#define VIDEO_TUNER_SECAM 4 -#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ -#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ -#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ -#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ -#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ - __u16 mode; /* PAL/NTSC/SECAM/OTHER */ -#define VIDEO_MODE_PAL 0 -#define VIDEO_MODE_NTSC 1 -#define VIDEO_MODE_SECAM 2 -#define VIDEO_MODE_AUTO 3 - __u16 signal; /* Signal strength 16bit scale */ -}; - -struct video_picture -{ - __u16 brightness; - __u16 hue; - __u16 colour; - __u16 contrast; - __u16 whiteness; /* Black and white only */ - __u16 depth; /* Capture depth */ - __u16 palette; /* Palette in use */ -#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ -#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ -#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ -#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ -#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ -#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ -#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ -#define VIDEO_PALETTE_YUYV 8 -#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ -#define VIDEO_PALETTE_YUV420 10 -#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ -#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ -#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ -#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ -#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ -#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ -#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ -#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ -}; - -struct video_audio -{ - int audio; /* Audio channel */ - __u16 volume; /* If settable */ - __u16 bass, treble; - __u32 flags; -#define VIDEO_AUDIO_MUTE 1 -#define VIDEO_AUDIO_MUTABLE 2 -#define VIDEO_AUDIO_VOLUME 4 -#define VIDEO_AUDIO_BASS 8 -#define VIDEO_AUDIO_TREBLE 16 -#define VIDEO_AUDIO_BALANCE 32 - char name[16]; -#define VIDEO_SOUND_MONO 1 -#define VIDEO_SOUND_STEREO 2 -#define VIDEO_SOUND_LANG1 4 -#define VIDEO_SOUND_LANG2 8 - __u16 mode; - __u16 balance; /* Stereo balance */ - __u16 step; /* Step actual volume uses */ -}; - -struct video_clip -{ - __s32 x,y; - __s32 width, height; - struct video_clip *next; /* For user use/driver use only */ -}; - -struct video_window -{ - __u32 x,y; /* Position of window */ - __u32 width,height; /* Its size */ - __u32 chromakey; - __u32 flags; - struct video_clip __user *clips; /* Set only */ - int clipcount; -#define VIDEO_WINDOW_INTERLACE 1 -#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ -#define VIDEO_CLIP_BITMAP -1 -/* bitmap is 1024x625, a '1' bit represents a clipped pixel */ -#define VIDEO_CLIPMAP_SIZE (128 * 625) -}; - -struct video_capture -{ - __u32 x,y; /* Offsets into image */ - __u32 width, height; /* Area to capture */ - __u16 decimation; /* Decimation divider */ - __u16 flags; /* Flags for capture */ -#define VIDEO_CAPTURE_ODD 0 /* Temporal */ -#define VIDEO_CAPTURE_EVEN 1 -}; - -struct video_buffer -{ - void *base; - int height,width; - int depth; - int bytesperline; -}; - -struct video_mmap -{ - unsigned int frame; /* Frame (0 - n) for double buffer */ - int height,width; - unsigned int format; /* should be VIDEO_PALETTE_* */ -}; - -struct video_key -{ - __u8 key[8]; - __u32 flags; -}; - -struct video_mbuf -{ - int size; /* Total memory to map */ - int frames; /* Frames */ - int offsets[VIDEO_MAX_FRAME]; -}; - -#define VIDEO_NO_UNIT (-1) - -struct video_unit -{ - int video; /* Video minor */ - int vbi; /* VBI minor */ - int radio; /* Radio minor */ - int audio; /* Audio minor */ - int teletext; /* Teletext minor */ -}; - -struct vbi_format { - __u32 sampling_rate; /* in Hz */ - __u32 samples_per_line; - __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */ - __s32 start[2]; /* starting line for each frame */ - __u32 count[2]; /* count of lines for each frame */ - __u32 flags; -#define VBI_UNSYNC 1 /* can distingues between top/bottom field */ -#define VBI_INTERLACED 2 /* lines are interlaced */ -}; - -/* video_info is biased towards hardware mpeg encode/decode */ -/* but it could apply generically to any hardware compressor/decompressor */ -struct video_info -{ - __u32 frame_count; /* frames output since decode/encode began */ - __u32 h_size; /* current unscaled horizontal size */ - __u32 v_size; /* current unscaled veritcal size */ - __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */ - __u32 picture_type; /* current picture type */ - __u32 temporal_reference; /* current temporal reference */ - __u8 user_data[256]; /* user data last found in compressed stream */ - /* user_data[0] contains user data flags, user_data[1] has count */ -}; - -/* generic structure for setting playback modes */ -struct video_play_mode -{ - int mode; - int p1; - int p2; -}; - -/* for loading microcode / fpga programming */ -struct video_code -{ - char loadwhat[16]; /* name or tag of file being passed */ - int datasize; - __u8 *data; -}; - -#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ -#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ -#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ -#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ -#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ -#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ -#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ -#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ -#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ -#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ -#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ -#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ -#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */ -#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ -#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ -#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ -#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ -#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ -#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ -#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ -#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */ -#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */ -#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */ -#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */ -#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */ -#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */ -#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */ -#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ -#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ - - -#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */ - -/* VIDIOCSWRITEMODE */ -#define VID_WRITE_MPEG_AUD 0 -#define VID_WRITE_MPEG_VID 1 -#define VID_WRITE_OSD 2 -#define VID_WRITE_TTX 3 -#define VID_WRITE_CC 4 -#define VID_WRITE_MJPEG 5 - -/* VIDIOCSPLAYMODE */ -#define VID_PLAY_VID_OUT_MODE 0 - /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */ -#define VID_PLAY_GENLOCK 1 - /* p1: 0 = OFF, 1 = ON */ - /* p2: GENLOCK FINE DELAY value */ -#define VID_PLAY_NORMAL 2 -#define VID_PLAY_PAUSE 3 -#define VID_PLAY_SINGLE_FRAME 4 -#define VID_PLAY_FAST_FORWARD 5 -#define VID_PLAY_SLOW_MOTION 6 -#define VID_PLAY_IMMEDIATE_NORMAL 7 -#define VID_PLAY_SWITCH_CHANNELS 8 -#define VID_PLAY_FREEZE_FRAME 9 -#define VID_PLAY_STILL_MODE 10 -#define VID_PLAY_MASTER_MODE 11 - /* p1: see below */ -#define VID_PLAY_MASTER_NONE 1 -#define VID_PLAY_MASTER_VIDEO 2 -#define VID_PLAY_MASTER_AUDIO 3 -#define VID_PLAY_ACTIVE_SCANLINES 12 - /* p1 = first active; p2 = last active */ -#define VID_PLAY_RESET 13 -#define VID_PLAY_END_MARK 14 - -#endif /* __LINUX_VIDEODEV_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v0.10.2 From b287db119edb92548b53f63f05fd593c43cba200 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 5 Feb 2011 09:25:02 -0300 Subject: [media] se401: remove last V4L1 driver This driver is for obsolete hardware that the old maintainer didn't care (or not have the hardware anymore), and that no other developer could find any hardware to buy. The V4L1 API is no longer supported, and since nobody stepped in to convert them to V4L2 the decision was made to remove them. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index b3f35e5..08e0df1 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -97,21 +97,6 @@ Who: Pavel Machek --------------------------- -What: Video4Linux obsolete drivers using V4L1 API -When: kernel 2.6.39 -Files: drivers/staging/se401/* drivers/staging/usbvideo/* -Check: drivers/staging/se401/se401.c drivers/staging/usbvideo/usbvideo.c -Why: There are some drivers still using V4L1 API, despite all efforts we've done - to migrate. Those drivers are for obsolete hardware that the old maintainer - didn't care (or not have the hardware anymore), and that no other developer - could find any hardware to buy. They probably have no practical usage today, - and people with such old hardware could probably keep using an older version - of the kernel. Those drivers will be moved to staging on 2.6.38 and, if nobody - cares enough to port and test them with V4L2 API, they'll be removed on 2.6.39. -Who: Mauro Carvalho Chehab - ---------------------------- - What: Video4Linux: Remove obsolete ioctl's When: kernel 2.6.39 Files: include/media/videodev2.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index a3f340b..fa5c007e 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -55,8 +55,6 @@ source "drivers/staging/cxd2099/Kconfig" source "drivers/staging/dabusb/Kconfig" -source "drivers/staging/se401/Kconfig" - source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 532331c..2bdd703 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_USB_DABUSB) += dabusb/ -obj-$(CONFIG_USB_SE401) += se401/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/se401/Kconfig b/drivers/staging/se401/Kconfig deleted file mode 100644 index b7f8222..0000000 --- a/drivers/staging/se401/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config USB_SE401 - tristate "USB SE401 Camera support (DEPRECATED)" - depends on VIDEO_DEV && VIDEO_V4L2_COMMON && USB - ---help--- - Say Y here if you want to connect this type of camera to your - computer's USB port. See - for more information and for a list of supported cameras. - - This driver uses the deprecated V4L1 API and will be removed in - 2.6.39, unless someone converts it to the V4L2 API. - - To compile this driver as a module, choose M here: the - module will be called se401. diff --git a/drivers/staging/se401/Makefile b/drivers/staging/se401/Makefile deleted file mode 100644 index b465d49..0000000 --- a/drivers/staging/se401/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_USB_SE401) += se401.o diff --git a/drivers/staging/se401/TODO b/drivers/staging/se401/TODO deleted file mode 100644 index 3b2c038..0000000 --- a/drivers/staging/se401/TODO +++ /dev/null @@ -1,5 +0,0 @@ -This is an obsolete driver for some old webcams that still use V4L1 API. -As V4L1 support is being removed from kernel, if nobody take care on it, -the driver will be removed for 2.6.39. - -Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/se401/se401.c b/drivers/staging/se401/se401.c deleted file mode 100644 index 41360d7..0000000 --- a/drivers/staging/se401/se401.c +++ /dev/null @@ -1,1492 +0,0 @@ -/* - * Endpoints (formerly known as AOX) se401 USB Camera Driver - * - * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) - * - * Still somewhat based on the Linux ov511 driver. - * - * 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. - * - * - * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on - * their chipset available and supporting me while writing this driver. - * - Jeroen Vreeken - */ - -static const char version[] = "0.24"; - -#include -#include -#include -#include -#include -#include -#include "se401.h" - -static int flickerless; -static int video_nr = -1; - -static struct usb_device_id device_table[] = { - { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */ - { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */ - { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */ - { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */ - { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */ - { } -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -MODULE_AUTHOR("Jeroen Vreeken "); -MODULE_DESCRIPTION("SE401 USB Camera Driver"); -MODULE_LICENSE("GPL"); -module_param(flickerless, int, 0); -MODULE_PARM_DESC(flickerless, - "Net frequency to adjust exposure time to (0/50/60)"); -module_param(video_nr, int, 0); - -static struct usb_driver se401_driver; - - -/********************************************************************** - * - * Memory management - * - **********************************************************************/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - - - -/**************************************************************************** - * - * se401 register read/write functions - * - ***************************************************************************/ - -static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req, - unsigned short value, unsigned char *cp, int size) -{ - return usb_control_msg( - se401->dev, - set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0), - req, - (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - 0, - cp, - size, - 1000 - ); -} - -static int se401_set_feature(struct usb_se401 *se401, unsigned short selector, - unsigned short param) -{ - /* specs say that the selector (address) should go in the value field - and the param in index, but in the logs of the windows driver they do - this the other way around... - */ - return usb_control_msg( - se401->dev, - usb_sndctrlpipe(se401->dev, 0), - SE401_REQ_SET_EXT_FEATURE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - param, - selector, - NULL, - 0, - 1000 - ); -} - -static unsigned short se401_get_feature(struct usb_se401 *se401, - unsigned short selector) -{ - /* For 'set' the selecetor should be in index, not sure if the spec is - wrong here to.... - */ - unsigned char cp[2]; - usb_control_msg( - se401->dev, - usb_rcvctrlpipe(se401->dev, 0), - SE401_REQ_GET_EXT_FEATURE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, - selector, - cp, - 2, - 1000 - ); - return cp[0]+cp[1]*256; -} - -/**************************************************************************** - * - * Camera control - * - ***************************************************************************/ - - -static int se401_send_pict(struct usb_se401 *se401) -{ - /* integration time low */ - se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l); - /* integration time mid */ - se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m); - /* integration time mid */ - se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h); - /* reset level value */ - se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); - /* red color gain */ - se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain); - /* green color gain */ - se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain); - /* blue color gain */ - se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain); - - return 0; -} - -static void se401_set_exposure(struct usb_se401 *se401, int brightness) -{ - int integration = brightness << 5; - - if (flickerless == 50) - integration = integration-integration % 106667; - if (flickerless == 60) - integration = integration-integration % 88889; - se401->brightness = integration >> 5; - se401->expose_h = (integration >> 16) & 0xff; - se401->expose_m = (integration >> 8) & 0xff; - se401->expose_l = integration & 0xff; -} - -static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p) -{ - p->brightness = se401->brightness; - if (se401->enhance) - p->whiteness = 32768; - else - p->whiteness = 0; - - p->colour = 65535; - p->contrast = 65535; - p->hue = se401->rgain << 10; - p->palette = se401->palette; - p->depth = 3; /* rgb24 */ - return 0; -} - - -static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p) -{ - if (p->palette != VIDEO_PALETTE_RGB24) - return 1; - se401->palette = p->palette; - if (p->hue != se401->hue) { - se401->rgain = p->hue >> 10; - se401->bgain = 0x40-(p->hue >> 10); - se401->hue = p->hue; - } - if (p->brightness != se401->brightness) - se401_set_exposure(se401, p->brightness); - - if (p->whiteness >= 32768) - se401->enhance = 1; - else - se401->enhance = 0; - se401_send_pict(se401); - se401_send_pict(se401); - return 0; -} - -/* - Hyundai have some really nice docs about this and other sensor related - stuff on their homepage: www.hei.co.kr -*/ -static void se401_auto_resetlevel(struct usb_se401 *se401) -{ - unsigned int ahrc, alrc; - int oldreset = se401->resetlevel; - - /* For some reason this normally read-only register doesn't get reset - to zero after reading them just once... - */ - se401_get_feature(se401, HV7131_REG_HIREFNOH); - se401_get_feature(se401, HV7131_REG_HIREFNOL); - se401_get_feature(se401, HV7131_REG_LOREFNOH); - se401_get_feature(se401, HV7131_REG_LOREFNOL); - ahrc = 256*se401_get_feature(se401, HV7131_REG_HIREFNOH) + - se401_get_feature(se401, HV7131_REG_HIREFNOL); - alrc = 256*se401_get_feature(se401, HV7131_REG_LOREFNOH) + - se401_get_feature(se401, HV7131_REG_LOREFNOL); - - /* Not an exact science, but it seems to work pretty well... */ - if (alrc > 10) { - while (alrc >= 10 && se401->resetlevel < 63) { - se401->resetlevel++; - alrc /= 2; - } - } else if (ahrc > 20) { - while (ahrc >= 20 && se401->resetlevel > 0) { - se401->resetlevel--; - ahrc /= 2; - } - } - if (se401->resetlevel != oldreset) - se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); - - return; -} - -/* irq handler for snapshot button */ -static void se401_button_irq(struct urb *urb) -{ - struct usb_se401 *se401 = urb->context; - int status; - - if (!se401->dev) { - dev_info(&urb->dev->dev, "device vapourished\n"); - return; - } - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __func__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", - __func__, urb->status); - goto exit; - } - - if (urb->actual_length >= 2) - if (se401->button) - se401->buttonpressed = 1; -exit: - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) - err("%s - usb_submit_urb failed with result %d", - __func__, status); -} - -static void se401_video_irq(struct urb *urb) -{ - struct usb_se401 *se401 = urb->context; - int length = urb->actual_length; - - /* ohoh... */ - if (!se401->streaming) - return; - - if (!se401->dev) { - dev_info(&urb->dev->dev, "device vapourished\n"); - return; - } - - /* 0 sized packets happen if we are to fast, but sometimes the camera - keeps sending them forever... - */ - if (length && !urb->status) { - se401->nullpackets = 0; - switch (se401->scratch[se401->scratch_next].state) { - case BUFFER_READY: - case BUFFER_BUSY: - se401->dropped++; - break; - case BUFFER_UNUSED: - memcpy(se401->scratch[se401->scratch_next].data, - (unsigned char *)urb->transfer_buffer, length); - se401->scratch[se401->scratch_next].state - = BUFFER_READY; - se401->scratch[se401->scratch_next].offset - = se401->bayeroffset; - se401->scratch[se401->scratch_next].length = length; - if (waitqueue_active(&se401->wq)) - wake_up_interruptible(&se401->wq); - se401->scratch_overflow = 0; - se401->scratch_next++; - if (se401->scratch_next >= SE401_NUMSCRATCH) - se401->scratch_next = 0; - break; - } - se401->bayeroffset += length; - if (se401->bayeroffset >= se401->cheight * se401->cwidth) - se401->bayeroffset = 0; - } else { - se401->nullpackets++; - if (se401->nullpackets > SE401_MAX_NULLPACKETS) - if (waitqueue_active(&se401->wq)) - wake_up_interruptible(&se401->wq); - } - - /* Resubmit urb for new data */ - urb->status = 0; - urb->dev = se401->dev; - if (usb_submit_urb(urb, GFP_KERNEL)) - dev_info(&urb->dev->dev, "urb burned down\n"); - return; -} - -static void se401_send_size(struct usb_se401 *se401, int width, int height) -{ - int i = 0; - int mode = 0x03; /* No compression */ - int sendheight = height; - int sendwidth = width; - - /* JangGu compression can only be used with the camera supported sizes, - but bayer seems to work with any size that fits on the sensor. - We check if we can use compression with the current size with either - 4 or 16 times subcapturing, if not we use uncompressed bayer data - but this will result in cutouts of the maximum size.... - */ - while (i < se401->sizes && !(se401->width[i] == width && - se401->height[i] == height)) - i++; - while (i < se401->sizes) { - if (se401->width[i] == width * 2 && - se401->height[i] == height * 2) { - sendheight = se401->height[i]; - sendwidth = se401->width[i]; - mode = 0x40; - } - if (se401->width[i] == width * 4 && - se401->height[i] == height * 4) { - sendheight = se401->height[i]; - sendwidth = se401->width[i]; - mode = 0x42; - } - i++; - } - - se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0); - se401_set_feature(se401, SE401_OPERATINGMODE, mode); - - if (mode == 0x03) - se401->format = FMT_BAYER; - else - se401->format = FMT_JANGGU; -} - -/* - In this function se401_send_pict is called several times, - for some reason (depending on the state of the sensor and the phase of - the moon :) doing this only in either place doesn't always work... -*/ -static int se401_start_stream(struct usb_se401 *se401) -{ - struct urb *urb; - int err = 0, i; - se401->streaming = 1; - - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); - - /* Set picture settings */ - /* windowed + pix intg */ - se401_set_feature(se401, HV7131_REG_MODE_B, 0x05); - se401_send_pict(se401); - - se401_send_size(se401, se401->cwidth, se401->cheight); - - se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, - 0, NULL, 0); - - /* Do some memory allocation */ - for (i = 0; i < SE401_NUMFRAMES; i++) { - se401->frame[i].data = se401->fbuf + i * se401->maxframesize; - se401->frame[i].curpix = 0; - } - for (i = 0; i < SE401_NUMSBUF; i++) { - se401->sbuf[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); - if (!se401->sbuf[i].data) { - for (i = i - 1; i >= 0; i--) { - kfree(se401->sbuf[i].data); - se401->sbuf[i].data = NULL; - } - return -ENOMEM; - } - } - - se401->bayeroffset = 0; - se401->scratch_next = 0; - se401->scratch_use = 0; - se401->scratch_overflow = 0; - for (i = 0; i < SE401_NUMSCRATCH; i++) { - se401->scratch[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); - if (!se401->scratch[i].data) { - for (i = i - 1; i >= 0; i--) { - kfree(se401->scratch[i].data); - se401->scratch[i].data = NULL; - } - goto nomem_sbuf; - } - se401->scratch[i].state = BUFFER_UNUSED; - } - - for (i = 0; i < SE401_NUMSBUF; i++) { - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - for (i = i - 1; i >= 0; i--) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i] = NULL; - } - goto nomem_scratch; - } - - usb_fill_bulk_urb(urb, se401->dev, - usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT), - se401->sbuf[i].data, SE401_PACKETSIZE, - se401_video_irq, - se401); - - se401->urb[i] = urb; - - err = usb_submit_urb(se401->urb[i], GFP_KERNEL); - if (err) - err("urb burned down"); - } - - se401->framecount = 0; - - return 0; - - nomem_scratch: - for (i = 0; i < SE401_NUMSCRATCH; i++) { - kfree(se401->scratch[i].data); - se401->scratch[i].data = NULL; - } - nomem_sbuf: - for (i = 0; i < SE401_NUMSBUF; i++) { - kfree(se401->sbuf[i].data); - se401->sbuf[i].data = NULL; - } - return -ENOMEM; -} - -static int se401_stop_stream(struct usb_se401 *se401) -{ - int i; - - if (!se401->streaming || !se401->dev) - return 1; - - se401->streaming = 0; - - se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0); - - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); - - for (i = 0; i < SE401_NUMSBUF; i++) - if (se401->urb[i]) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i] = NULL; - kfree(se401->sbuf[i].data); - } - for (i = 0; i < SE401_NUMSCRATCH; i++) { - kfree(se401->scratch[i].data); - se401->scratch[i].data = NULL; - } - - return 0; -} - -static int se401_set_size(struct usb_se401 *se401, int width, int height) -{ - int wasstreaming = se401->streaming; - /* Check to see if we need to change */ - if (se401->cwidth == width && se401->cheight == height) - return 0; - - /* Check for a valid mode */ - if (!width || !height) - return 1; - if ((width & 1) || (height & 1)) - return 1; - if (width > se401->width[se401->sizes-1]) - return 1; - if (height > se401->height[se401->sizes-1]) - return 1; - - /* Stop a current stream and start it again at the new size */ - if (wasstreaming) - se401_stop_stream(se401); - se401->cwidth = width; - se401->cheight = height; - if (wasstreaming) - se401_start_stream(se401); - return 0; -} - - -/**************************************************************************** - * - * Video Decoding - * - ***************************************************************************/ - -/* - This shouldn't really be done in a v4l driver.... - But it does make the image look a lot more usable. - Basically it lifts the dark pixels more than the light pixels. -*/ -static inline void enhance_picture(unsigned char *frame, int len) -{ - while (len--) { - *frame = (((*frame^255)*(*frame^255))/255)^255; - frame++; - } -} - -static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data) -{ - struct se401_frame *frame = &se401->frame[se401->curframe]; - int linelength = se401->cwidth * 3; - - if (frame->curlinepix >= linelength) { - frame->curlinepix = 0; - frame->curline += linelength; - } - - /* First three are absolute, all others relative. - * Format is rgb from right to left (mirrorred image), - * we flip it to get bgr from left to right. */ - if (frame->curlinepix < 3) - *(frame->curline-frame->curlinepix) = 1 + data * 4; - else - *(frame->curline-frame->curlinepix) = - *(frame->curline-frame->curlinepix + 3) + data * 4; - frame->curlinepix++; -} - -static inline void decode_JangGu_vlc(struct usb_se401 *se401, - unsigned char *data, int bit_exp, int packetlength) -{ - int pos = 0; - int vlc_cod = 0; - int vlc_size = 0; - int vlc_data = 0; - int bit_cur; - int bit; - data += 4; - while (pos < packetlength) { - bit_cur = 8; - while (bit_cur && bit_exp) { - bit = ((*data) >> (bit_cur-1))&1; - if (!vlc_cod) { - if (bit) { - vlc_size++; - } else { - if (!vlc_size) - decode_JangGu_integrate(se401, 0); - else { - vlc_cod = 2; - vlc_data = 0; - } - } - } else { - if (vlc_cod == 2) { - if (!bit) - vlc_data = -(1 << vlc_size) + 1; - vlc_cod--; - } - vlc_size--; - vlc_data += bit << vlc_size; - if (!vlc_size) { - decode_JangGu_integrate(se401, vlc_data); - vlc_cod = 0; - } - } - bit_cur--; - bit_exp--; - } - pos++; - data++; - } -} - -static inline void decode_JangGu(struct usb_se401 *se401, - struct se401_scratch *buffer) -{ - unsigned char *data = buffer->data; - int len = buffer->length; - int bit_exp = 0, pix_exp = 0, frameinfo = 0, packetlength = 0, size; - int datapos = 0; - - /* New image? */ - if (!se401->frame[se401->curframe].curpix) { - se401->frame[se401->curframe].curlinepix = 0; - se401->frame[se401->curframe].curline = - se401->frame[se401->curframe].data+ - se401->cwidth * 3 - 1; - if (se401->frame[se401->curframe].grabstate == FRAME_READY) - se401->frame[se401->curframe].grabstate = FRAME_GRABBING; - se401->vlcdatapos = 0; - } - while (datapos < len) { - size = 1024 - se401->vlcdatapos; - if (size+datapos > len) - size = len-datapos; - memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size); - se401->vlcdatapos += size; - packetlength = 0; - if (se401->vlcdatapos >= 4) { - bit_exp = se401->vlcdata[3] + (se401->vlcdata[2] << 8); - pix_exp = se401->vlcdata[1] + - ((se401->vlcdata[0] & 0x3f) << 8); - frameinfo = se401->vlcdata[0] & 0xc0; - packetlength = ((bit_exp + 47) >> 4) << 1; - if (packetlength > 1024) { - se401->vlcdatapos = 0; - datapos = len; - packetlength = 0; - se401->error++; - se401->frame[se401->curframe].curpix = 0; - } - } - if (packetlength && se401->vlcdatapos >= packetlength) { - decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, - packetlength); - se401->frame[se401->curframe].curpix += pix_exp * 3; - datapos += size-(se401->vlcdatapos-packetlength); - se401->vlcdatapos = 0; - if (se401->frame[se401->curframe].curpix >= se401->cwidth * se401->cheight * 3) { - if (se401->frame[se401->curframe].curpix == se401->cwidth * se401->cheight * 3) { - if (se401->frame[se401->curframe].grabstate == FRAME_GRABBING) { - se401->frame[se401->curframe].grabstate = FRAME_DONE; - se401->framecount++; - se401->readcount++; - } - if (se401->frame[(se401->curframe + 1) & (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) - se401->curframe = (se401->curframe + 1) & (SE401_NUMFRAMES - 1); - } else - se401->error++; - se401->frame[se401->curframe].curpix = 0; - datapos = len; - } - } else - datapos += size; - } -} - -static inline void decode_bayer(struct usb_se401 *se401, - struct se401_scratch *buffer) -{ - unsigned char *data = buffer->data; - int len = buffer->length; - int offset = buffer->offset; - int datasize = se401->cwidth * se401->cheight; - struct se401_frame *frame = &se401->frame[se401->curframe]; - unsigned char *framedata = frame->data, *curline, *nextline; - int width = se401->cwidth; - int blineoffset = 0, bline; - int linelength = width * 3, i; - - - if (frame->curpix == 0) { - if (frame->grabstate == FRAME_READY) - frame->grabstate = FRAME_GRABBING; - - frame->curline = framedata + linelength; - frame->curlinepix = 0; - } - - if (offset != frame->curpix) { - /* Regard frame as lost :( */ - frame->curpix = 0; - se401->error++; - return; - } - - /* Check if we have to much data */ - if (frame->curpix + len > datasize) - len = datasize-frame->curpix; - - if (se401->cheight % 4) - blineoffset = 1; - bline = frame->curpix / se401->cwidth+blineoffset; - - curline = frame->curline; - nextline = curline + linelength; - if (nextline >= framedata+datasize * 3) - nextline = curline; - while (len) { - if (frame->curlinepix >= width) { - frame->curlinepix -= width; - bline = frame->curpix / width + blineoffset; - curline += linelength*2; - nextline += linelength*2; - if (curline >= framedata+datasize * 3) { - frame->curlinepix++; - curline -= 3; - nextline -= 3; - len--; - data++; - frame->curpix++; - } - if (nextline >= framedata+datasize*3) - nextline = curline; - } - if (bline & 1) { - if (frame->curlinepix & 1) { - *(curline + 2) = *data; - *(curline - 1) = *data; - *(nextline + 2) = *data; - *(nextline - 1) = *data; - } else { - *(curline + 1) = - (*(curline + 1) + *data) / 2; - *(curline-2) = - (*(curline - 2) + *data) / 2; - *(nextline + 1) = *data; - *(nextline - 2) = *data; - } - } else { - if (frame->curlinepix & 1) { - *(curline + 1) = - (*(curline + 1) + *data) / 2; - *(curline - 2) = - (*(curline - 2) + *data) / 2; - *(nextline + 1) = *data; - *(nextline - 2) = *data; - } else { - *curline = *data; - *(curline - 3) = *data; - *nextline = *data; - *(nextline - 3) = *data; - } - } - frame->curlinepix++; - curline -= 3; - nextline -= 3; - len--; - data++; - frame->curpix++; - } - frame->curline = curline; - - if (frame->curpix >= datasize) { - /* Fix the top line */ - framedata += linelength; - for (i = 0; i < linelength; i++) { - framedata--; - *framedata = *(framedata + linelength); - } - /* Fix the left side (green is already present) */ - for (i = 0; i < se401->cheight; i++) { - *framedata = *(framedata + 3); - *(framedata + 1) = *(framedata + 4); - *(framedata + 2) = *(framedata + 5); - framedata += linelength; - } - frame->curpix = 0; - frame->grabstate = FRAME_DONE; - se401->framecount++; - se401->readcount++; - if (se401->frame[(se401->curframe + 1) & - (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) { - se401->curframe = (se401->curframe+1) & - (SE401_NUMFRAMES-1); - } - } -} - -static int se401_newframe(struct usb_se401 *se401, int framenr) -{ - DECLARE_WAITQUEUE(wait, current); - int errors = 0; - - while (se401->streaming && - (se401->frame[framenr].grabstate == FRAME_READY || - se401->frame[framenr].grabstate == FRAME_GRABBING)) { - if (!se401->frame[framenr].curpix) - errors++; - - wait_interruptible( - se401->scratch[se401->scratch_use].state != BUFFER_READY, - &se401->wq, &wait); - if (se401->nullpackets > SE401_MAX_NULLPACKETS) { - se401->nullpackets = 0; - dev_info(&se401->dev->dev, - "too many null length packets, restarting capture\n"); - se401_stop_stream(se401); - se401_start_stream(se401); - } else { - if (se401->scratch[se401->scratch_use].state != - BUFFER_READY) { - se401->frame[framenr].grabstate = FRAME_ERROR; - return -EIO; - } - se401->scratch[se401->scratch_use].state = BUFFER_BUSY; - if (se401->format == FMT_JANGGU) - decode_JangGu(se401, - &se401->scratch[se401->scratch_use]); - else - decode_bayer(se401, - &se401->scratch[se401->scratch_use]); - - se401->scratch[se401->scratch_use].state = - BUFFER_UNUSED; - se401->scratch_use++; - if (se401->scratch_use >= SE401_NUMSCRATCH) - se401->scratch_use = 0; - if (errors > SE401_MAX_ERRORS) { - errors = 0; - dev_info(&se401->dev->dev, - "too many errors, restarting capture\n"); - se401_stop_stream(se401); - se401_start_stream(se401); - } - } - } - - if (se401->frame[framenr].grabstate == FRAME_DONE) - if (se401->enhance) - enhance_picture(se401->frame[framenr].data, - se401->cheight * se401->cwidth * 3); - return 0; -} - -static void usb_se401_remove_disconnected(struct usb_se401 *se401) -{ - int i; - - se401->dev = NULL; - - for (i = 0; i < SE401_NUMSBUF; i++) - if (se401->urb[i]) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i] = NULL; - kfree(se401->sbuf[i].data); - } - - for (i = 0; i < SE401_NUMSCRATCH; i++) - kfree(se401->scratch[i].data); - - if (se401->inturb) { - usb_kill_urb(se401->inturb); - usb_free_urb(se401->inturb); - } - dev_info(&se401->dev->dev, "%s disconnected", se401->camera_name); - - /* Free the memory */ - kfree(se401->width); - kfree(se401->height); - kfree(se401); -} - - - -/**************************************************************************** - * - * Video4Linux - * - ***************************************************************************/ - - -static int se401_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_se401 *se401 = (struct usb_se401 *)dev; - int err = 0; - - mutex_lock(&se401->lock); - if (se401->user) { - mutex_unlock(&se401->lock); - return -EBUSY; - } - se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES); - if (se401->fbuf) - file->private_data = dev; - else - err = -ENOMEM; - se401->user = !err; - mutex_unlock(&se401->lock); - - return err; -} - -static int se401_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)dev; - int i; - - rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES); - if (se401->removed) { - dev_info(&se401->dev->dev, "device unregistered\n"); - usb_se401_remove_disconnected(se401); - } else { - for (i = 0; i < SE401_NUMFRAMES; i++) - se401->frame[i].grabstate = FRAME_UNUSED; - if (se401->streaming) - se401_stop_stream(se401); - se401->user = 0; - } - file->private_data = NULL; - return 0; -} - -static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *vdev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)vdev; - - if (!se401->dev) - return -EIO; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - strcpy(b->name, se401->camera_name); - b->type = VID_TYPE_CAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = se401->width[se401->sizes-1]; - b->maxheight = se401->height[se401->sizes-1]; - b->minwidth = se401->width[0]; - b->minheight = se401->height[0]; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Camera"); - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - - se401_get_pict(se401, p); - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - - if (se401_set_pict(se401, p)) - return -EINVAL; - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; - if (se401_set_size(se401, vw->width, vw->height)) - return -EINVAL; - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - vw->x = 0; /* FIXME */ - vw->y = 0; - vw->chromakey = 0; - vw->flags = 0; - vw->clipcount = 0; - vw->width = se401->cwidth; - vw->height = se401->cheight; - return 0; - } - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - memset(vm, 0, sizeof(*vm)); - vm->size = SE401_NUMFRAMES * se401->maxframesize; - vm->frames = SE401_NUMFRAMES; - for (i = 0; i < SE401_NUMFRAMES; i++) - vm->offsets[i] = se401->maxframesize * i; - return 0; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - - if (vm->format != VIDEO_PALETTE_RGB24) - return -EINVAL; - if (vm->frame >= SE401_NUMFRAMES) - return -EINVAL; - if (se401->frame[vm->frame].grabstate != FRAME_UNUSED) - return -EBUSY; - - /* Is this according to the v4l spec??? */ - if (se401_set_size(se401, vm->width, vm->height)) - return -EINVAL; - se401->frame[vm->frame].grabstate = FRAME_READY; - - if (!se401->streaming) - se401_start_stream(se401); - - /* Set the picture properties */ - if (se401->framecount == 0) - se401_send_pict(se401); - /* Calibrate the reset level after a few frames. */ - if (se401->framecount % 20 == 1) - se401_auto_resetlevel(se401); - - return 0; - } - case VIDIOCSYNC: - { - int *frame = arg; - int ret = 0; - - if (*frame < 0 || *frame >= SE401_NUMFRAMES) - return -EINVAL; - - ret = se401_newframe(se401, *frame); - se401->frame[*frame].grabstate = FRAME_UNUSED; - return ret; - } - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - memset(vb, 0, sizeof(*vb)); - return 0; - } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - return -EINVAL; - case VIDIOCSFBUF: - return -EINVAL; - case VIDIOCGTUNER: - case VIDIOCSTUNER: - return -EINVAL; - case VIDIOCGFREQ: - case VIDIOCSFREQ: - return -EINVAL; - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; - } /* end switch */ - - return 0; -} - -static long se401_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, se401_do_ioctl); -} - -static ssize_t se401_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - int realcount = count, ret = 0; - struct video_device *dev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)dev; - - - if (se401->dev == NULL) - return -EIO; - if (realcount > se401->cwidth*se401->cheight*3) - realcount = se401->cwidth*se401->cheight*3; - - /* Shouldn't happen: */ - if (se401->frame[0].grabstate == FRAME_GRABBING) - return -EBUSY; - se401->frame[0].grabstate = FRAME_READY; - se401->frame[1].grabstate = FRAME_UNUSED; - se401->curframe = 0; - - if (!se401->streaming) - se401_start_stream(se401); - - /* Set the picture properties */ - if (se401->framecount == 0) - se401_send_pict(se401); - /* Calibrate the reset level after a few frames. */ - if (se401->framecount%20 == 1) - se401_auto_resetlevel(se401); - - ret = se401_newframe(se401, 0); - - se401->frame[0].grabstate = FRAME_UNUSED; - if (ret) - return ret; - if (copy_to_user(buf, se401->frame[0].data, realcount)) - return -EFAULT; - - return realcount; -} - -static int se401_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)dev; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; - - mutex_lock(&se401->lock); - - if (se401->dev == NULL) { - mutex_unlock(&se401->lock); - return -EIO; - } - if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) - & ~(PAGE_SIZE - 1))) { - mutex_unlock(&se401->lock); - return -EINVAL; - } - pos = (unsigned long)se401->fbuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&se401->lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - mutex_unlock(&se401->lock); - - return 0; -} - -static const struct v4l2_file_operations se401_fops = { - .owner = THIS_MODULE, - .open = se401_open, - .release = se401_close, - .read = se401_read, - .mmap = se401_mmap, - .ioctl = se401_ioctl, -}; -static struct video_device se401_template = { - .name = "se401 USB camera", - .fops = &se401_fops, - .release = video_device_release_empty, -}; - - - -/***************************/ -static int se401_init(struct usb_se401 *se401, int button) -{ - int i = 0, rc; - unsigned char cp[0x40]; - char temp[200]; - int slen; - - /* led on */ - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); - - /* get camera descriptor */ - rc = se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, - cp, sizeof(cp)); - if (cp[1] != 0x41) { - err("Wrong descriptor type"); - return 1; - } - slen = snprintf(temp, 200, "ExtraFeatures: %d", cp[3]); - - se401->sizes = cp[4] + cp[5] * 256; - se401->width = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); - if (!se401->width) - return 1; - se401->height = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); - if (!se401->height) { - kfree(se401->width); - return 1; - } - for (i = 0; i < se401->sizes; i++) { - se401->width[i] = cp[6 + i * 4 + 0] + cp[6 + i*4 + 1] * 256; - se401->height[i] = cp[6 + i * 4 + 2] + cp[6 + i * 4 + 3] * 256; - } - slen += snprintf(temp + slen, 200 - slen, " Sizes:"); - for (i = 0; i < se401->sizes; i++) { - slen += snprintf(temp + slen, 200 - slen, - " %dx%d", se401->width[i], se401->height[i]); - } - dev_info(&se401->dev->dev, "%s\n", temp); - se401->maxframesize = se401->width[se401->sizes-1] * - se401->height[se401->sizes - 1] * 3; - - rc = se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp)); - se401->cwidth = cp[0]+cp[1]*256; - rc = se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); - se401->cheight = cp[0]+cp[1]*256; - - if (!(cp[2] & SE401_FORMAT_BAYER)) { - err("Bayer format not supported!"); - return 1; - } - /* set output mode (BAYER) */ - se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, - SE401_FORMAT_BAYER, NULL, 0); - - rc = se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); - se401->brightness = cp[0]+cp[1]*256; - /* some default values */ - se401->resetlevel = 0x2d; - se401->rgain = 0x20; - se401->ggain = 0x20; - se401->bgain = 0x20; - se401_set_exposure(se401, 20000); - se401->palette = VIDEO_PALETTE_RGB24; - se401->enhance = 1; - se401->dropped = 0; - se401->error = 0; - se401->framecount = 0; - se401->readcount = 0; - - /* Start interrupt transfers for snapshot button */ - if (button) { - se401->inturb = usb_alloc_urb(0, GFP_KERNEL); - if (!se401->inturb) { - dev_info(&se401->dev->dev, - "Allocation of inturb failed\n"); - return 1; - } - usb_fill_int_urb(se401->inturb, se401->dev, - usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT), - &se401->button, sizeof(se401->button), - se401_button_irq, - se401, - 8 - ); - if (usb_submit_urb(se401->inturb, GFP_KERNEL)) { - dev_info(&se401->dev->dev, "int urb burned down\n"); - return 1; - } - } else - se401->inturb = NULL; - - /* Flash the led */ - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); - - return 0; -} - -static int se401_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_interface_descriptor *interface; - struct usb_se401 *se401; - char *camera_name = NULL; - int button = 1; - - /* We don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - interface = &intf->cur_altsetting->desc; - - /* Is it an se401? */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 && - le16_to_cpu(dev->descriptor.idProduct) == 0x0004) { - camera_name = "Endpoints/Aox SE401"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 && - le16_to_cpu(dev->descriptor.idProduct) == 0x030b) { - camera_name = "Philips PCVC665K"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5001) { - camera_name = "Kensington VideoCAM 67014"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5002) { - camera_name = "Kensington VideoCAM 6701(5/7)"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5003) { - camera_name = "Kensington VideoCAM 67016"; - button = 0; - } else - return -ENODEV; - - /* Checking vendor/product should be enough, but what the hell */ - if (interface->bInterfaceClass != 0x00) - return -ENODEV; - if (interface->bInterfaceSubClass != 0x00) - return -ENODEV; - - /* We found one */ - dev_info(&intf->dev, "SE401 camera found: %s\n", camera_name); - - se401 = kzalloc(sizeof(*se401), GFP_KERNEL); - if (se401 == NULL) { - err("couldn't kmalloc se401 struct"); - return -ENOMEM; - } - - se401->dev = dev; - se401->iface = interface->bInterfaceNumber; - se401->camera_name = camera_name; - - dev_info(&intf->dev, "firmware version: %02x\n", - le16_to_cpu(dev->descriptor.bcdDevice) & 255); - - if (se401_init(se401, button)) { - kfree(se401); - return -EIO; - } - - memcpy(&se401->vdev, &se401_template, sizeof(se401_template)); - memcpy(se401->vdev.name, se401->camera_name, - strlen(se401->camera_name)); - init_waitqueue_head(&se401->wq); - mutex_init(&se401->lock); - wmb(); - - if (video_register_device(&se401->vdev, - VFL_TYPE_GRABBER, video_nr) < 0) { - kfree(se401); - err("video_register_device failed"); - return -EIO; - } - dev_info(&intf->dev, "registered new video device: %s\n", - video_device_node_name(&se401->vdev)); - - usb_set_intfdata(intf, se401); - return 0; -} - -static void se401_disconnect(struct usb_interface *intf) -{ - struct usb_se401 *se401 = usb_get_intfdata(intf); - - usb_set_intfdata(intf, NULL); - if (se401) { - video_unregister_device(&se401->vdev); - if (!se401->user) - usb_se401_remove_disconnected(se401); - else { - se401->frame[0].grabstate = FRAME_ERROR; - se401->frame[0].grabstate = FRAME_ERROR; - - se401->streaming = 0; - - wake_up_interruptible(&se401->wq); - se401->removed = 1; - } - } -} - -static struct usb_driver se401_driver = { - .name = "se401", - .id_table = device_table, - .probe = se401_probe, - .disconnect = se401_disconnect, -}; - - - -/**************************************************************************** - * - * Module routines - * - ***************************************************************************/ - -static int __init usb_se401_init(void) -{ - printk(KERN_INFO "SE401 usb camera driver version %s registering\n", - version); - if (flickerless) - if (flickerless != 50 && flickerless != 60) { - printk(KERN_ERR "Invallid flickerless value, use 0, 50 or 60.\n"); - return -1; - } - return usb_register(&se401_driver); -} - -static void __exit usb_se401_exit(void) -{ - usb_deregister(&se401_driver); - printk(KERN_INFO "SE401 driver deregistered\frame"); -} - -module_init(usb_se401_init); -module_exit(usb_se401_exit); diff --git a/drivers/staging/se401/se401.h b/drivers/staging/se401/se401.h deleted file mode 100644 index 2758f47..0000000 --- a/drivers/staging/se401/se401.h +++ /dev/null @@ -1,236 +0,0 @@ - -#ifndef __LINUX_se401_H -#define __LINUX_se401_H - -#include -#include "videodev.h" -#include -#include -#include - -#define se401_DEBUG /* Turn on debug messages */ - -#ifdef se401_DEBUG -# define PDEBUG(level, fmt, args...) \ -if (debug >= level) \ - info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) -#else -# define PDEBUG(level, fmt, args...) do {} while (0) -#endif - -/* An almost drop-in replacement for sleep_on_interruptible */ -#define wait_interruptible(test, queue, wait) \ -{ \ - add_wait_queue(queue, wait); \ - set_current_state(TASK_INTERRUPTIBLE); \ - if (test) \ - schedule(); \ - remove_wait_queue(queue, wait); \ - set_current_state(TASK_RUNNING); \ - if (signal_pending(current)) \ - break; \ -} - -#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06 -#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41 -#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42 -#define SE401_REQ_CAPTURE_FRAME 0x43 -#define SE401_REQ_GET_BRT 0x44 -#define SE401_REQ_SET_BRT 0x45 -#define SE401_REQ_GET_WIDTH 0x4c -#define SE401_REQ_SET_WIDTH 0x4d -#define SE401_REQ_GET_HEIGHT 0x4e -#define SE401_REQ_SET_HEIGHT 0x4f -#define SE401_REQ_GET_OUTPUT_MODE 0x50 -#define SE401_REQ_SET_OUTPUT_MODE 0x51 -#define SE401_REQ_GET_EXT_FEATURE 0x52 -#define SE401_REQ_SET_EXT_FEATURE 0x53 -#define SE401_REQ_CAMERA_POWER 0x56 -#define SE401_REQ_LED_CONTROL 0x57 -#define SE401_REQ_BIOS 0xff - -#define SE401_BIOS_READ 0x07 - -#define SE401_FORMAT_BAYER 0x40 - -/* Hyundai hv7131b registers - 7121 and 7141 should be the same (haven't really checked...) */ -/* Mode registers: */ -#define HV7131_REG_MODE_A 0x00 -#define HV7131_REG_MODE_B 0x01 -#define HV7131_REG_MODE_C 0x02 -/* Frame registers: */ -#define HV7131_REG_FRSU 0x10 -#define HV7131_REG_FRSL 0x11 -#define HV7131_REG_FCSU 0x12 -#define HV7131_REG_FCSL 0x13 -#define HV7131_REG_FWHU 0x14 -#define HV7131_REG_FWHL 0x15 -#define HV7131_REG_FWWU 0x16 -#define HV7131_REG_FWWL 0x17 -/* Timing registers: */ -#define HV7131_REG_THBU 0x20 -#define HV7131_REG_THBL 0x21 -#define HV7131_REG_TVBU 0x22 -#define HV7131_REG_TVBL 0x23 -#define HV7131_REG_TITU 0x25 -#define HV7131_REG_TITM 0x26 -#define HV7131_REG_TITL 0x27 -#define HV7131_REG_TMCD 0x28 -/* Adjust Registers: */ -#define HV7131_REG_ARLV 0x30 -#define HV7131_REG_ARCG 0x31 -#define HV7131_REG_AGCG 0x32 -#define HV7131_REG_ABCG 0x33 -#define HV7131_REG_APBV 0x34 -#define HV7131_REG_ASLP 0x54 -/* Offset Registers: */ -#define HV7131_REG_OFSR 0x50 -#define HV7131_REG_OFSG 0x51 -#define HV7131_REG_OFSB 0x52 -/* REset level statistics registers: */ -#define HV7131_REG_LOREFNOH 0x57 -#define HV7131_REG_LOREFNOL 0x58 -#define HV7131_REG_HIREFNOH 0x59 -#define HV7131_REG_HIREFNOL 0x5a - -/* se401 registers */ -#define SE401_OPERATINGMODE 0x2000 - - -/* size of usb transfers */ -#define SE401_PACKETSIZE 4096 -/* number of queued bulk transfers to use, should be about 8 */ -#define SE401_NUMSBUF 1 -/* read the usb specs for this one :) */ -#define SE401_VIDEO_ENDPOINT 1 -#define SE401_BUTTON_ENDPOINT 2 -/* number of frames supported by the v4l part */ -#define SE401_NUMFRAMES 2 -/* scratch buffers for passing data to the decoders */ -#define SE401_NUMSCRATCH 32 -/* maximum amount of data in a JangGu packet */ -#define SE401_VLCDATALEN 1024 -/* number of nul sized packets to receive before kicking the camera */ -#define SE401_MAX_NULLPACKETS 4000 -/* number of decoding errors before kicking the camera */ -#define SE401_MAX_ERRORS 200 - -struct usb_device; - -struct se401_sbuf { - unsigned char *data; -}; - -enum { - FRAME_UNUSED, /* Unused (no MCAPTURE) */ - FRAME_READY, /* Ready to start grabbing */ - FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_ERROR, /* Something bad happened while processing */ -}; - -enum { - FMT_BAYER, - FMT_JANGGU, -}; - -enum { - BUFFER_UNUSED, - BUFFER_READY, - BUFFER_BUSY, - BUFFER_DONE, -}; - -struct se401_scratch { - unsigned char *data; - volatile int state; - int offset; - int length; -}; - -struct se401_frame { - unsigned char *data; /* Frame buffer */ - - volatile int grabstate; /* State of grabbing */ - - unsigned char *curline; - int curlinepix; - int curpix; -}; - -struct usb_se401 { - struct video_device vdev; - - /* Device structure */ - struct usb_device *dev; - - unsigned char iface; - - char *camera_name; - - int change; - int brightness; - int hue; - int rgain; - int ggain; - int bgain; - int expose_h; - int expose_m; - int expose_l; - int resetlevel; - - int enhance; - - int format; - int sizes; - int *width; - int *height; - int cwidth; /* current width */ - int cheight; /* current height */ - int palette; - int maxframesize; - int cframesize; /* current framesize */ - - struct mutex lock; - int user; /* user count for exclusive use */ - int removed; /* device disconnected */ - - int streaming; /* Are we streaming video? */ - - char *fbuf; /* Videodev buffer area */ - - struct urb *urb[SE401_NUMSBUF]; - struct urb *inturb; - - int button; - int buttonpressed; - - int curframe; /* Current receiving frame */ - struct se401_frame frame[SE401_NUMFRAMES]; - int readcount; - int framecount; - int error; - int dropped; - - int scratch_next; - int scratch_use; - int scratch_overflow; - struct se401_scratch scratch[SE401_NUMSCRATCH]; - - /* Decoder specific data: */ - unsigned char vlcdata[SE401_VLCDATALEN]; - int vlcdatapos; - int bayeroffset; - - struct se401_sbuf sbuf[SE401_NUMSBUF]; - - wait_queue_head_t wq; /* Processes waiting */ - - int nullpackets; -}; - - - -#endif - diff --git a/drivers/staging/se401/videodev.h b/drivers/staging/se401/videodev.h deleted file mode 100644 index f11efbe..0000000 --- a/drivers/staging/se401/videodev.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Video for Linux version 1 - OBSOLETE - * - * Header file for v4l1 drivers and applications, for - * Linux kernels 2.2.x or 2.4.x. - * - * Provides header for legacy drivers and applications - * - * See http://linuxtv.org for more info - * - */ -#ifndef __LINUX_VIDEODEV_H -#define __LINUX_VIDEODEV_H - -#include -#include -#include - -#define VID_TYPE_CAPTURE 1 /* Can capture */ -#define VID_TYPE_TUNER 2 /* Can tune */ -#define VID_TYPE_TELETEXT 4 /* Does teletext */ -#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ -#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ -#define VID_TYPE_CLIPPING 32 /* Can clip */ -#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ -#define VID_TYPE_SCALES 128 /* Scalable */ -#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ -#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ -#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ -#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ -#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ -#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ - -struct video_capability -{ - char name[32]; - int type; - int channels; /* Num channels */ - int audios; /* Num audio devices */ - int maxwidth; /* Supported width */ - int maxheight; /* And height */ - int minwidth; /* Supported width */ - int minheight; /* And height */ -}; - - -struct video_channel -{ - int channel; - char name[32]; - int tuners; - __u32 flags; -#define VIDEO_VC_TUNER 1 /* Channel has a tuner */ -#define VIDEO_VC_AUDIO 2 /* Channel has audio */ - __u16 type; -#define VIDEO_TYPE_TV 1 -#define VIDEO_TYPE_CAMERA 2 - __u16 norm; /* Norm set by channel */ -}; - -struct video_tuner -{ - int tuner; - char name[32]; - unsigned long rangelow, rangehigh; /* Tuner range */ - __u32 flags; -#define VIDEO_TUNER_PAL 1 -#define VIDEO_TUNER_NTSC 2 -#define VIDEO_TUNER_SECAM 4 -#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ -#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ -#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ -#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ -#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ - __u16 mode; /* PAL/NTSC/SECAM/OTHER */ -#define VIDEO_MODE_PAL 0 -#define VIDEO_MODE_NTSC 1 -#define VIDEO_MODE_SECAM 2 -#define VIDEO_MODE_AUTO 3 - __u16 signal; /* Signal strength 16bit scale */ -}; - -struct video_picture -{ - __u16 brightness; - __u16 hue; - __u16 colour; - __u16 contrast; - __u16 whiteness; /* Black and white only */ - __u16 depth; /* Capture depth */ - __u16 palette; /* Palette in use */ -#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ -#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ -#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ -#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ -#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ -#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ -#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ -#define VIDEO_PALETTE_YUYV 8 -#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ -#define VIDEO_PALETTE_YUV420 10 -#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ -#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ -#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ -#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ -#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ -#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ -#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ -#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ -}; - -struct video_audio -{ - int audio; /* Audio channel */ - __u16 volume; /* If settable */ - __u16 bass, treble; - __u32 flags; -#define VIDEO_AUDIO_MUTE 1 -#define VIDEO_AUDIO_MUTABLE 2 -#define VIDEO_AUDIO_VOLUME 4 -#define VIDEO_AUDIO_BASS 8 -#define VIDEO_AUDIO_TREBLE 16 -#define VIDEO_AUDIO_BALANCE 32 - char name[16]; -#define VIDEO_SOUND_MONO 1 -#define VIDEO_SOUND_STEREO 2 -#define VIDEO_SOUND_LANG1 4 -#define VIDEO_SOUND_LANG2 8 - __u16 mode; - __u16 balance; /* Stereo balance */ - __u16 step; /* Step actual volume uses */ -}; - -struct video_clip -{ - __s32 x,y; - __s32 width, height; - struct video_clip *next; /* For user use/driver use only */ -}; - -struct video_window -{ - __u32 x,y; /* Position of window */ - __u32 width,height; /* Its size */ - __u32 chromakey; - __u32 flags; - struct video_clip __user *clips; /* Set only */ - int clipcount; -#define VIDEO_WINDOW_INTERLACE 1 -#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ -#define VIDEO_CLIP_BITMAP -1 -/* bitmap is 1024x625, a '1' bit represents a clipped pixel */ -#define VIDEO_CLIPMAP_SIZE (128 * 625) -}; - -struct video_capture -{ - __u32 x,y; /* Offsets into image */ - __u32 width, height; /* Area to capture */ - __u16 decimation; /* Decimation divider */ - __u16 flags; /* Flags for capture */ -#define VIDEO_CAPTURE_ODD 0 /* Temporal */ -#define VIDEO_CAPTURE_EVEN 1 -}; - -struct video_buffer -{ - void *base; - int height,width; - int depth; - int bytesperline; -}; - -struct video_mmap -{ - unsigned int frame; /* Frame (0 - n) for double buffer */ - int height,width; - unsigned int format; /* should be VIDEO_PALETTE_* */ -}; - -struct video_key -{ - __u8 key[8]; - __u32 flags; -}; - -struct video_mbuf -{ - int size; /* Total memory to map */ - int frames; /* Frames */ - int offsets[VIDEO_MAX_FRAME]; -}; - -#define VIDEO_NO_UNIT (-1) - -struct video_unit -{ - int video; /* Video minor */ - int vbi; /* VBI minor */ - int radio; /* Radio minor */ - int audio; /* Audio minor */ - int teletext; /* Teletext minor */ -}; - -struct vbi_format { - __u32 sampling_rate; /* in Hz */ - __u32 samples_per_line; - __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */ - __s32 start[2]; /* starting line for each frame */ - __u32 count[2]; /* count of lines for each frame */ - __u32 flags; -#define VBI_UNSYNC 1 /* can distingues between top/bottom field */ -#define VBI_INTERLACED 2 /* lines are interlaced */ -}; - -/* video_info is biased towards hardware mpeg encode/decode */ -/* but it could apply generically to any hardware compressor/decompressor */ -struct video_info -{ - __u32 frame_count; /* frames output since decode/encode began */ - __u32 h_size; /* current unscaled horizontal size */ - __u32 v_size; /* current unscaled veritcal size */ - __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */ - __u32 picture_type; /* current picture type */ - __u32 temporal_reference; /* current temporal reference */ - __u8 user_data[256]; /* user data last found in compressed stream */ - /* user_data[0] contains user data flags, user_data[1] has count */ -}; - -/* generic structure for setting playback modes */ -struct video_play_mode -{ - int mode; - int p1; - int p2; -}; - -/* for loading microcode / fpga programming */ -struct video_code -{ - char loadwhat[16]; /* name or tag of file being passed */ - int datasize; - __u8 *data; -}; - -#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ -#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ -#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ -#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ -#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ -#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ -#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ -#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ -#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ -#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ -#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ -#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ -#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */ -#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ -#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ -#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ -#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ -#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ -#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ -#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ -#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */ -#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */ -#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */ -#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */ -#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */ -#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */ -#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */ -#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ -#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ - - -#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */ - -/* VIDIOCSWRITEMODE */ -#define VID_WRITE_MPEG_AUD 0 -#define VID_WRITE_MPEG_VID 1 -#define VID_WRITE_OSD 2 -#define VID_WRITE_TTX 3 -#define VID_WRITE_CC 4 -#define VID_WRITE_MJPEG 5 - -/* VIDIOCSPLAYMODE */ -#define VID_PLAY_VID_OUT_MODE 0 - /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */ -#define VID_PLAY_GENLOCK 1 - /* p1: 0 = OFF, 1 = ON */ - /* p2: GENLOCK FINE DELAY value */ -#define VID_PLAY_NORMAL 2 -#define VID_PLAY_PAUSE 3 -#define VID_PLAY_SINGLE_FRAME 4 -#define VID_PLAY_FAST_FORWARD 5 -#define VID_PLAY_SLOW_MOTION 6 -#define VID_PLAY_IMMEDIATE_NORMAL 7 -#define VID_PLAY_SWITCH_CHANNELS 8 -#define VID_PLAY_FREEZE_FRAME 9 -#define VID_PLAY_STILL_MODE 10 -#define VID_PLAY_MASTER_MODE 11 - /* p1: see below */ -#define VID_PLAY_MASTER_NONE 1 -#define VID_PLAY_MASTER_VIDEO 2 -#define VID_PLAY_MASTER_AUDIO 3 -#define VID_PLAY_ACTIVE_SCANLINES 12 - /* p1 = first active; p2 = last active */ -#define VID_PLAY_RESET 13 -#define VID_PLAY_END_MARK 14 - -#endif /* __LINUX_VIDEODEV_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v0.10.2 From dae86ccbc3c185aebfc396e8e668aa3d73d748d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 5 Feb 2011 09:29:31 -0300 Subject: [media] dabusb: remove obsolete driver The dabusb driver was conceived as an experimental driver for a test device. The driver never supported any shipped product, and, while there were some updates for it in 2003, for an ancient product, those changes were never submitted upstream. Also, there's no DocBook for its API, nor any upstream discussion. So it was decided to remove it in 2.6.39. Future support for a DAB API should be discussed on the linux-media list first. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index fa5c007e..a2742d8 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -53,8 +53,6 @@ source "drivers/staging/tm6000/Kconfig" source "drivers/staging/cxd2099/Kconfig" -source "drivers/staging/dabusb/Kconfig" - source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 2bdd703..4ddd3ce 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ -obj-$(CONFIG_USB_DABUSB) += dabusb/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/dabusb/Kconfig b/drivers/staging/dabusb/Kconfig deleted file mode 100644 index 87bdc42..0000000 --- a/drivers/staging/dabusb/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -config USB_DABUSB - tristate "DABUSB driver" - depends on USB - ---help--- - A Digital Audio Broadcasting (DAB) Receiver for USB and Linux - brought to you by the DAB-Team - . This driver can be taken - as an example for URB-based bulk, control, and isochronous - transactions. URB's are explained in - . - - To compile this driver as a module, choose M here: the - module will be called dabusb. - diff --git a/drivers/staging/dabusb/Makefile b/drivers/staging/dabusb/Makefile deleted file mode 100644 index 2ff2f22..0000000 --- a/drivers/staging/dabusb/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -obj-$(CONFIG_USB_DABUSB) += dabusb.o - diff --git a/drivers/staging/dabusb/TODO b/drivers/staging/dabusb/TODO deleted file mode 100644 index f9c0314..0000000 --- a/drivers/staging/dabusb/TODO +++ /dev/null @@ -1,5 +0,0 @@ -This is a driver for an experimental sample developed in 2003. The driver -never supported any commercial product, nor had any known user. -If nobody takes care on it, the driver will be removed for 2.6.39. - -Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/dabusb/dabusb.c b/drivers/staging/dabusb/dabusb.c deleted file mode 100644 index f3e25e9..0000000 --- a/drivers/staging/dabusb/dabusb.c +++ /dev/null @@ -1,914 +0,0 @@ -/*****************************************************************************/ - -/* - * dabusb.c -- dab usb driver. - * - * Copyright (C) 1999 Deti Fliegl (deti@fliegl.de) - * - * 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. - * - * - * - * $Id: dabusb.c,v 1.54 2000/07/24 21:39:39 deti Exp $ - * - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dabusb.h" - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.54" -#define DRIVER_AUTHOR "Deti Fliegl, deti@fliegl.de" -#define DRIVER_DESC "DAB-USB Interface Driver for Linux (c)1999" - -/* --------------------------------------------------------------------- */ - -#ifdef CONFIG_USB_DYNAMIC_MINORS -#define NRDABUSB 256 -#else -#define NRDABUSB 4 -#endif - -/*-------------------------------------------------------------------*/ - -static dabusb_t dabusb[NRDABUSB]; -static int buffers = 256; -static struct usb_driver dabusb_driver; - -/*-------------------------------------------------------------------*/ - -static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src) -{ - unsigned long flags; - struct list_head *tmp; - int ret = 0; - - spin_lock_irqsave (&s->lock, flags); - - if (list_empty (src)) { - // no elements in source buffer - ret = -1; - goto err; - } - tmp = src->next; - list_move_tail (tmp, dst); - - err: spin_unlock_irqrestore (&s->lock, flags); - return ret; -} -/*-------------------------------------------------------------------*/ -#ifdef DEBUG -static void dump_urb (struct urb *urb) -{ - dbg("urb :%p", urb); - dbg("dev :%p", urb->dev); - dbg("pipe :%08X", urb->pipe); - dbg("status :%d", urb->status); - dbg("transfer_flags :%08X", urb->transfer_flags); - dbg("transfer_buffer :%p", urb->transfer_buffer); - dbg("transfer_buffer_length:%d", urb->transfer_buffer_length); - dbg("actual_length :%d", urb->actual_length); - dbg("setup_packet :%p", urb->setup_packet); - dbg("start_frame :%d", urb->start_frame); - dbg("number_of_packets :%d", urb->number_of_packets); - dbg("interval :%d", urb->interval); - dbg("error_count :%d", urb->error_count); - dbg("context :%p", urb->context); - dbg("complete :%p", urb->complete); -} -#endif -/*-------------------------------------------------------------------*/ -static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q) -{ - unsigned long flags; - pbuff_t b; - - dbg("dabusb_cancel_queue"); - - spin_lock_irqsave (&s->lock, flags); - - list_for_each_entry(b, q, buff_list) { -#ifdef DEBUG - dump_urb(b->purb); -#endif - usb_unlink_urb (b->purb); - } - spin_unlock_irqrestore (&s->lock, flags); - return 0; -} -/*-------------------------------------------------------------------*/ -static int dabusb_free_queue (struct list_head *q) -{ - struct list_head *tmp; - struct list_head *p; - pbuff_t b; - - dbg("dabusb_free_queue"); - for (p = q->next; p != q;) { - b = list_entry (p, buff_t, buff_list); - -#ifdef DEBUG - dump_urb(b->purb); -#endif - kfree(b->purb->transfer_buffer); - usb_free_urb(b->purb); - tmp = p->next; - list_del (p); - kfree (b); - p = tmp; - } - - return 0; -} -/*-------------------------------------------------------------------*/ -static int dabusb_free_buffers (pdabusb_t s) -{ - unsigned long flags; - dbg("dabusb_free_buffers"); - - spin_lock_irqsave(&s->lock, flags); - - dabusb_free_queue (&s->free_buff_list); - dabusb_free_queue (&s->rec_buff_list); - - spin_unlock_irqrestore(&s->lock, flags); - - s->got_mem = 0; - return 0; -} -/*-------------------------------------------------------------------*/ -static void dabusb_iso_complete (struct urb *purb) -{ - pbuff_t b = purb->context; - pdabusb_t s = b->s; - int i; - int len; - int dst = 0; - void *buf = purb->transfer_buffer; - - dbg("dabusb_iso_complete"); - - // process if URB was not killed - if (purb->status != -ENOENT) { - unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE); - int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe)); - for (i = 0; i < purb->number_of_packets; i++) - if (!purb->iso_frame_desc[i].status) { - len = purb->iso_frame_desc[i].actual_length; - if (len <= pipesize) { - memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len); - dst += len; - } - else - dev_err(&purb->dev->dev, - "dabusb_iso_complete: invalid len %d\n", len); - } - else - dev_warn(&purb->dev->dev, "dabusb_iso_complete: corrupted packet status: %d\n", purb->iso_frame_desc[i].status); - if (dst != purb->actual_length) - dev_err(&purb->dev->dev, - "dst!=purb->actual_length:%d!=%d\n", - dst, purb->actual_length); - } - - if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) { - s->overruns++; - dev_err(&purb->dev->dev, "overrun (%d)\n", s->overruns); - } - wake_up (&s->wait); -} -/*-------------------------------------------------------------------*/ -static int dabusb_alloc_buffers (pdabusb_t s) -{ - int transfer_len = 0; - pbuff_t b; - unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE); - int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe)); - int packets = _ISOPIPESIZE / pipesize; - int transfer_buffer_length = packets * pipesize; - int i; - - dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d", - pipesize, packets, transfer_buffer_length); - - while (transfer_len < (s->total_buffer_size << 10)) { - b = kzalloc(sizeof (buff_t), GFP_KERNEL); - if (!b) { - dev_err(&s->usbdev->dev, - "kzalloc(sizeof(buff_t))==NULL\n"); - goto err; - } - b->s = s; - b->purb = usb_alloc_urb(packets, GFP_KERNEL); - if (!b->purb) { - dev_err(&s->usbdev->dev, "usb_alloc_urb == NULL\n"); - kfree (b); - goto err; - } - - b->purb->transfer_buffer = kmalloc (transfer_buffer_length, GFP_KERNEL); - if (!b->purb->transfer_buffer) { - kfree (b->purb); - kfree (b); - dev_err(&s->usbdev->dev, - "kmalloc(%d)==NULL\n", transfer_buffer_length); - goto err; - } - - b->purb->transfer_buffer_length = transfer_buffer_length; - b->purb->number_of_packets = packets; - b->purb->complete = dabusb_iso_complete; - b->purb->context = b; - b->purb->dev = s->usbdev; - b->purb->pipe = pipe; - b->purb->transfer_flags = URB_ISO_ASAP; - - for (i = 0; i < packets; i++) { - b->purb->iso_frame_desc[i].offset = i * pipesize; - b->purb->iso_frame_desc[i].length = pipesize; - } - - transfer_len += transfer_buffer_length; - list_add_tail (&b->buff_list, &s->free_buff_list); - } - s->got_mem = transfer_len; - - return 0; - - err: - dabusb_free_buffers (s); - return -ENOMEM; -} -/*-------------------------------------------------------------------*/ -static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb) -{ - int ret; - unsigned int pipe; - int actual_length; - - dbg("dabusb_bulk"); - - if (!pb->pipe) - pipe = usb_rcvbulkpipe (s->usbdev, 2); - else - pipe = usb_sndbulkpipe (s->usbdev, 2); - - ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 100); - if(ret<0) { - dev_err(&s->usbdev->dev, - "usb_bulk_msg failed(%d)\n", ret); - - if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { - dev_err(&s->usbdev->dev, "set_interface failed\n"); - return -EINVAL; - } - - } - - if( ret == -EPIPE ) { - dev_warn(&s->usbdev->dev, "CLEAR_FEATURE request to remove STALL condition.\n"); - if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) - dev_err(&s->usbdev->dev, "request failed\n"); - } - - pb->size = actual_length; - return ret; -} -/* --------------------------------------------------------------------- */ -static int dabusb_writemem (pdabusb_t s, int pos, const unsigned char *data, - int len) -{ - int ret; - unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL); - - if (!transfer_buffer) { - dev_err(&s->usbdev->dev, - "dabusb_writemem: kmalloc(%d) failed.\n", len); - return -ENOMEM; - } - - memcpy (transfer_buffer, data, len); - - ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ), 0xa0, 0x40, pos, 0, transfer_buffer, len, 300); - - kfree (transfer_buffer); - return ret; -} -/* --------------------------------------------------------------------- */ -static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit) -{ - dbg("dabusb_8051_reset: %d",reset_bit); - return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1); -} -/* --------------------------------------------------------------------- */ -static int dabusb_loadmem (pdabusb_t s, const char *fname) -{ - int ret; - const struct ihex_binrec *rec; - const struct firmware *uninitialized_var(fw); - - dbg("Enter dabusb_loadmem (internal)"); - - ret = request_ihex_firmware(&fw, "dabusb/firmware.fw", &s->usbdev->dev); - if (ret) { - dev_err(&s->usbdev->dev, - "Failed to load \"dabusb/firmware.fw\": %d\n", ret); - goto out; - } - ret = dabusb_8051_reset (s, 1); - - for (rec = (const struct ihex_binrec *)fw->data; rec; - rec = ihex_next_binrec(rec)) { - dbg("dabusb_writemem: %04X %p %d)", be32_to_cpu(rec->addr), - rec->data, be16_to_cpu(rec->len)); - - ret = dabusb_writemem(s, be32_to_cpu(rec->addr), rec->data, - be16_to_cpu(rec->len)); - if (ret < 0) { - dev_err(&s->usbdev->dev, - "dabusb_writemem failed (%d %04X %p %d)\n", - ret, be32_to_cpu(rec->addr), - rec->data, be16_to_cpu(rec->len)); - break; - } - } - ret = dabusb_8051_reset (s, 0); - release_firmware(fw); - out: - dbg("dabusb_loadmem: exit"); - - return ret; -} -/* --------------------------------------------------------------------- */ -static int dabusb_fpga_clear (pdabusb_t s, pbulk_transfer_t b) -{ - b->size = 4; - b->data[0] = 0x2a; - b->data[1] = 0; - b->data[2] = 0; - b->data[3] = 0; - - dbg("dabusb_fpga_clear"); - - return dabusb_bulk (s, b); -} -/* --------------------------------------------------------------------- */ -static int dabusb_fpga_init (pdabusb_t s, pbulk_transfer_t b) -{ - b->size = 4; - b->data[0] = 0x2c; - b->data[1] = 0; - b->data[2] = 0; - b->data[3] = 0; - - dbg("dabusb_fpga_init"); - - return dabusb_bulk (s, b); -} -/* --------------------------------------------------------------------- */ -static int dabusb_fpga_download (pdabusb_t s, const char *fname) -{ - pbulk_transfer_t b = kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL); - const struct firmware *fw; - unsigned int blen, n; - int ret; - - dbg("Enter dabusb_fpga_download (internal)"); - - if (!b) { - dev_err(&s->usbdev->dev, - "kmalloc(sizeof(bulk_transfer_t))==NULL\n"); - return -ENOMEM; - } - - ret = request_firmware(&fw, "dabusb/bitstream.bin", &s->usbdev->dev); - if (ret) { - dev_err(&s->usbdev->dev, - "Failed to load \"dabusb/bitstream.bin\": %d\n", ret); - kfree(b); - return ret; - } - - b->pipe = 1; - ret = dabusb_fpga_clear (s, b); - mdelay (10); - blen = fw->data[73] + (fw->data[72] << 8); - - dbg("Bitstream len: %i", blen); - - b->data[0] = 0x2b; - b->data[1] = 0; - b->data[2] = 0; - b->data[3] = 60; - - for (n = 0; n <= blen + 60; n += 60) { - // some cclks for startup - b->size = 64; - memcpy (b->data + 4, fw->data + 74 + n, 60); - ret = dabusb_bulk (s, b); - if (ret < 0) { - dev_err(&s->usbdev->dev, "dabusb_bulk failed.\n"); - break; - } - mdelay (1); - } - - ret = dabusb_fpga_init (s, b); - kfree (b); - release_firmware(fw); - - dbg("exit dabusb_fpga_download"); - - return ret; -} - -static int dabusb_stop (pdabusb_t s) -{ - dbg("dabusb_stop"); - - s->state = _stopped; - dabusb_cancel_queue (s, &s->rec_buff_list); - - dbg("pending_io: %d", s->pending_io.counter); - - s->pending_io.counter = 0; - return 0; -} - -static int dabusb_startrek (pdabusb_t s) -{ - if (!s->got_mem && s->state != _started) { - - dbg("dabusb_startrek"); - - if (dabusb_alloc_buffers (s) < 0) - return -ENOMEM; - dabusb_stop (s); - s->state = _started; - s->readptr = 0; - } - - if (!list_empty (&s->free_buff_list)) { - pbuff_t end; - int ret; - - while (!dabusb_add_buf_tail (s, &s->rec_buff_list, &s->free_buff_list)) { - - dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list); - - end = list_entry (s->rec_buff_list.prev, buff_t, buff_list); - - ret = usb_submit_urb (end->purb, GFP_KERNEL); - if (ret) { - dev_err(&s->usbdev->dev, - "usb_submit_urb returned:%d\n", ret); - if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) - dev_err(&s->usbdev->dev, - "startrek: dabusb_add_buf_tail failed\n"); - break; - } - else - atomic_inc (&s->pending_io); - } - dbg("pending_io: %d",s->pending_io.counter); - } - - return 0; -} - -static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, loff_t * ppos) -{ - pdabusb_t s = (pdabusb_t) file->private_data; - unsigned long flags; - unsigned ret = 0; - int rem; - int cnt; - pbuff_t b; - struct urb *purb = NULL; - - dbg("dabusb_read"); - - if (*ppos) - return -ESPIPE; - - if (s->remove_pending) - return -EIO; - - - if (!s->usbdev) - return -EIO; - - while (count > 0) { - dabusb_startrek (s); - - spin_lock_irqsave (&s->lock, flags); - - if (list_empty (&s->rec_buff_list)) { - - spin_unlock_irqrestore(&s->lock, flags); - - dev_err(&s->usbdev->dev, - "error: rec_buf_list is empty\n"); - goto err; - } - - b = list_entry (s->rec_buff_list.next, buff_t, buff_list); - purb = b->purb; - - spin_unlock_irqrestore(&s->lock, flags); - - if (purb->status == -EINPROGRESS) { - if (file->f_flags & O_NONBLOCK) // return nonblocking - { - if (!ret) - ret = -EAGAIN; - goto err; - } - - interruptible_sleep_on (&s->wait); - - if (signal_pending (current)) { - if (!ret) - ret = -ERESTARTSYS; - goto err; - } - - spin_lock_irqsave (&s->lock, flags); - - if (list_empty (&s->rec_buff_list)) { - spin_unlock_irqrestore(&s->lock, flags); - dev_err(&s->usbdev->dev, - "error: still no buffer available.\n"); - goto err; - } - spin_unlock_irqrestore(&s->lock, flags); - s->readptr = 0; - } - if (s->remove_pending) { - ret = -EIO; - goto err; - } - - rem = purb->actual_length - s->readptr; // set remaining bytes to copy - - if (count >= rem) - cnt = rem; - else - cnt = count; - - dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt); - - if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) { - dev_err(&s->usbdev->dev, "read: copy_to_user failed\n"); - if (!ret) - ret = -EFAULT; - goto err; - } - - s->readptr += cnt; - count -= cnt; - buf += cnt; - ret += cnt; - - if (s->readptr == purb->actual_length) { - // finished, take next buffer - if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) - dev_err(&s->usbdev->dev, - "read: dabusb_add_buf_tail failed\n"); - s->readptr = 0; - } - } - err: //mutex_unlock(&s->mutex); - return ret; -} - -static int dabusb_open (struct inode *inode, struct file *file) -{ - int devnum = iminor(inode); - pdabusb_t s; - int r; - - if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB)) - return -EIO; - - s = &dabusb[devnum - DABUSB_MINOR]; - - dbg("dabusb_open"); - mutex_lock(&s->mutex); - - while (!s->usbdev || s->opened) { - mutex_unlock(&s->mutex); - - if (file->f_flags & O_NONBLOCK) - return -EBUSY; - msleep_interruptible(500); - - if (signal_pending (current)) - return -EAGAIN; - mutex_lock(&s->mutex); - } - if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { - mutex_unlock(&s->mutex); - dev_err(&s->usbdev->dev, "set_interface failed\n"); - return -EINVAL; - } - s->opened = 1; - mutex_unlock(&s->mutex); - - file->f_pos = 0; - file->private_data = s; - - r = nonseekable_open(inode, file); - return r; -} - -static int dabusb_release (struct inode *inode, struct file *file) -{ - pdabusb_t s = (pdabusb_t) file->private_data; - - dbg("dabusb_release"); - - mutex_lock(&s->mutex); - dabusb_stop (s); - dabusb_free_buffers (s); - mutex_unlock(&s->mutex); - - if (!s->remove_pending) { - if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) - dev_err(&s->usbdev->dev, "set_interface failed\n"); - } - else - wake_up (&s->remove_ok); - - s->opened = 0; - return 0; -} - -static long dabusb_ioctl (struct file *file, unsigned int cmd, unsigned long arg) -{ - pdabusb_t s = (pdabusb_t) file->private_data; - pbulk_transfer_t pbulk; - int ret = 0; - int version = DABUSB_VERSION; - - dbg("dabusb_ioctl"); - - if (s->remove_pending) - return -EIO; - - mutex_lock(&s->mutex); - - if (!s->usbdev) { - mutex_unlock(&s->mutex); - return -EIO; - } - - switch (cmd) { - - case IOCTL_DAB_BULK: - pbulk = memdup_user((void __user *)arg, - sizeof(bulk_transfer_t)); - - if (IS_ERR(pbulk)) { - ret = PTR_ERR(pbulk); - break; - } - - ret=dabusb_bulk (s, pbulk); - if(ret==0) - if (copy_to_user((void __user *)arg, pbulk, - sizeof(bulk_transfer_t))) - ret = -EFAULT; - kfree (pbulk); - break; - - case IOCTL_DAB_OVERRUNS: - ret = put_user (s->overruns, (unsigned int __user *) arg); - break; - - case IOCTL_DAB_VERSION: - ret = put_user (version, (unsigned int __user *) arg); - break; - - default: - ret = -ENOIOCTLCMD; - break; - } - mutex_unlock(&s->mutex); - return ret; -} - -static const struct file_operations dabusb_fops = -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = dabusb_read, - .unlocked_ioctl = dabusb_ioctl, - .open = dabusb_open, - .release = dabusb_release, -}; - -static char *dabusb_devnode(struct device *dev, mode_t *mode) -{ - return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); -} - -static struct usb_class_driver dabusb_class = { - .name = "dabusb%d", - .devnode = dabusb_devnode, - .fops = &dabusb_fops, - .minor_base = DABUSB_MINOR, -}; - - -/* --------------------------------------------------------------------- */ -static int dabusb_probe (struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *usbdev = interface_to_usbdev(intf); - int retval; - pdabusb_t s; - - dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", - le16_to_cpu(usbdev->descriptor.idVendor), - le16_to_cpu(usbdev->descriptor.idProduct), - intf->altsetting->desc.bInterfaceNumber); - - /* We don't handle multiple configurations */ - if (usbdev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - if (intf->altsetting->desc.bInterfaceNumber != _DABUSB_IF && - le16_to_cpu(usbdev->descriptor.idProduct) == 0x9999) - return -ENODEV; - - - - s = &dabusb[intf->minor]; - - mutex_lock(&s->mutex); - s->remove_pending = 0; - s->usbdev = usbdev; - s->devnum = intf->minor; - - if (usb_reset_configuration (usbdev) < 0) { - dev_err(&intf->dev, "reset_configuration failed\n"); - goto reject; - } - if (le16_to_cpu(usbdev->descriptor.idProduct) == 0x2131) { - dabusb_loadmem (s, NULL); - goto reject; - } - else { - dabusb_fpga_download (s, NULL); - - if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) { - dev_err(&intf->dev, "set_interface failed\n"); - goto reject; - } - } - dbg("bound to interface: %d", intf->altsetting->desc.bInterfaceNumber); - usb_set_intfdata (intf, s); - mutex_unlock(&s->mutex); - - retval = usb_register_dev(intf, &dabusb_class); - if (retval) { - usb_set_intfdata (intf, NULL); - return -ENOMEM; - } - - return 0; - - reject: - mutex_unlock(&s->mutex); - s->usbdev = NULL; - return -ENODEV; -} - -static void dabusb_disconnect (struct usb_interface *intf) -{ - wait_queue_t __wait; - pdabusb_t s = usb_get_intfdata (intf); - - dbg("dabusb_disconnect"); - - init_waitqueue_entry(&__wait, current); - - usb_set_intfdata (intf, NULL); - if (s) { - usb_deregister_dev (intf, &dabusb_class); - s->remove_pending = 1; - wake_up (&s->wait); - add_wait_queue(&s->remove_ok, &__wait); - set_current_state(TASK_UNINTERRUPTIBLE); - if (s->state == _started) - schedule(); - current->state = TASK_RUNNING; - remove_wait_queue(&s->remove_ok, &__wait); - - s->usbdev = NULL; - s->overruns = 0; - } -} - -static struct usb_device_id dabusb_ids [] = { - // { USB_DEVICE(0x0547, 0x2131) }, /* An2131 chip, no boot ROM */ - { USB_DEVICE(0x0547, 0x9999) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, dabusb_ids); - -static struct usb_driver dabusb_driver = { - .name = "dabusb", - .probe = dabusb_probe, - .disconnect = dabusb_disconnect, - .id_table = dabusb_ids, -}; - -/* --------------------------------------------------------------------- */ - -static int __init dabusb_init (void) -{ - int retval; - unsigned u; - - /* initialize struct */ - for (u = 0; u < NRDABUSB; u++) { - pdabusb_t s = &dabusb[u]; - memset (s, 0, sizeof (dabusb_t)); - mutex_init (&s->mutex); - s->usbdev = NULL; - s->total_buffer_size = buffers; - init_waitqueue_head (&s->wait); - init_waitqueue_head (&s->remove_ok); - spin_lock_init (&s->lock); - INIT_LIST_HEAD (&s->free_buff_list); - INIT_LIST_HEAD (&s->rec_buff_list); - } - - /* register misc device */ - retval = usb_register(&dabusb_driver); - if (retval) - goto out; - - dbg("dabusb_init: driver registered"); - - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); - -out: - return retval; -} - -static void __exit dabusb_cleanup (void) -{ - dbg("dabusb_cleanup"); - - usb_deregister (&dabusb_driver); -} - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("dabusb/firmware.fw"); -MODULE_FIRMWARE("dabusb/bitstream.bin"); - -module_param(buffers, int, 0); -MODULE_PARM_DESC (buffers, "Number of buffers (default=256)"); - -module_init (dabusb_init); -module_exit (dabusb_cleanup); - -/* --------------------------------------------------------------------- */ diff --git a/drivers/staging/dabusb/dabusb.h b/drivers/staging/dabusb/dabusb.h deleted file mode 100644 index 00eb34c..0000000 --- a/drivers/staging/dabusb/dabusb.h +++ /dev/null @@ -1,85 +0,0 @@ -#define _BULK_DATA_LEN 64 -typedef struct -{ - unsigned char data[_BULK_DATA_LEN]; - unsigned int size; - unsigned int pipe; -}bulk_transfer_t,*pbulk_transfer_t; - -#define DABUSB_MINOR 240 /* some unassigned USB minor */ -#define DABUSB_VERSION 0x1000 -#define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t) -#define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int) -#define IOCTL_DAB_VERSION _IOR('d', 0x3f, int) - -#ifdef __KERNEL__ - -typedef enum { _stopped=0, _started } driver_state_t; - -typedef struct -{ - struct mutex mutex; - struct usb_device *usbdev; - wait_queue_head_t wait; - wait_queue_head_t remove_ok; - spinlock_t lock; - atomic_t pending_io; - driver_state_t state; - int remove_pending; - int got_mem; - int total_buffer_size; - unsigned int overruns; - int readptr; - int opened; - int devnum; - struct list_head free_buff_list; - struct list_head rec_buff_list; -} dabusb_t,*pdabusb_t; - -typedef struct -{ - pdabusb_t s; - struct urb *purb; - struct list_head buff_list; -} buff_t,*pbuff_t; - -typedef struct -{ - wait_queue_head_t wait; -} bulk_completion_context_t, *pbulk_completion_context_t; - - -#define _DABUSB_IF 2 -#define _DABUSB_ISOPIPE 0x09 -#define _ISOPIPESIZE 16384 - -#define _BULK_DATA_LEN 64 -// Vendor specific request code for Anchor Upload/Download -// This one is implemented in the core -#define ANCHOR_LOAD_INTERNAL 0xA0 - -// EZ-USB Control and Status Register. Bit 0 controls 8051 reset -#define CPUCS_REG 0x7F92 -#define _TOTAL_BUFFERS 384 - -#define MAX_INTEL_HEX_RECORD_LENGTH 16 - -#ifndef _BYTE_DEFINED -#define _BYTE_DEFINED -typedef unsigned char BYTE; -#endif // !_BYTE_DEFINED - -#ifndef _WORD_DEFINED -#define _WORD_DEFINED -typedef unsigned short WORD; -#endif // !_WORD_DEFINED - -typedef struct _INTEL_HEX_RECORD -{ - BYTE Length; - WORD Address; - BYTE Type; - BYTE Data[MAX_INTEL_HEX_RECORD_LENGTH]; -} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD; - -#endif -- cgit v0.10.2 From 7ee40aadabd59b6cab60835f0ef9cdbe385df438 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 5 Feb 2011 10:10:38 -0300 Subject: [media] v4l: removal of old, obsolete ioctls Some ioctl's were defined wrong on 2.6.2 and 2.6.6, using the wrong type of R/W arguments. They were fixed, but the old ioctl names are still there, maintained to avoid breaking binary compatibility: There's no sense on preserving those forever, as it is very doubtful that someone would try to use a such old binary with a modern kernel. Removing them will allow us to remove some magic done at the V4L ioctl handler. Note that any application compiled with a videodev2.h from 2.6.7 or later will be using the correct ioctls. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 08e0df1..61fb823 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -97,27 +97,6 @@ Who: Pavel Machek --------------------------- -What: Video4Linux: Remove obsolete ioctl's -When: kernel 2.6.39 -Files: include/media/videodev2.h -Why: Some ioctl's were defined wrong on 2.6.2 and 2.6.6, using the wrong - type of R/W arguments. They were fixed, but the old ioctl names are - still there, maintained to avoid breaking binary compatibility: - #define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int) - #define VIDIOC_S_PARM_OLD _IOW('V', 22, struct v4l2_streamparm) - #define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control) - #define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct v4l2_audio) - #define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct v4l2_audioout) - #define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct v4l2_cropcap) - There's no sense on preserving those forever, as it is very doubtful - that someone would try to use a such old binary with a modern kernel. - Removing them will allow us to remove some magic done at the V4L ioctl - handler. - -Who: Mauro Carvalho Chehab - ---------------------------- - What: sys_sysctl When: September 2010 Option: CONFIG_SYSCTL_SYSCALL diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 810eef4..940b5db 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -59,7 +59,6 @@ #include #include #include -#define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include #include #include diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index c19208a..7c26947 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -14,7 +14,6 @@ */ #include -#define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include #include #include @@ -678,9 +677,6 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext #define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) #define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) -#ifdef __OLD_VIDIOC_ -#define VIDIOC_OVERLAY32_OLD _IOWR('V', 14, s32) -#endif #define VIDIOC_STREAMON32 _IOW ('V', 18, s32) #define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) #define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) @@ -720,9 +716,6 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; -#ifdef __OLD_VIDIOC_ - case VIDIOC_OVERLAY32_OLD: cmd = VIDIOC_OVERLAY; break; -#endif case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; @@ -856,14 +849,6 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) return ret; switch (cmd) { -#ifdef __OLD_VIDIOC_ - case VIDIOC_OVERLAY32_OLD: - case VIDIOC_S_PARM_OLD: - case VIDIOC_S_CTRL_OLD: - case VIDIOC_G_AUDIO_OLD: - case VIDIOC_G_AUDOUT_OLD: - case VIDIOC_CROPCAP_OLD: -#endif case VIDIOC_QUERYCAP: case VIDIOC_RESERVED: case VIDIOC_ENUM_FMT: diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 8360ed2..7a72074 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -17,7 +17,6 @@ #include #include -#define __OLD_VIDIOC_ /* To allow fixing old calls */ #include #include @@ -297,37 +296,6 @@ EXPORT_SYMBOL(v4l_printk_ioctl); /* * helper function -- handles userspace copying for ioctl arguments - */ - -#ifdef __OLD_VIDIOC_ -static unsigned int -video_fix_command(unsigned int cmd) -{ - switch (cmd) { - case VIDIOC_OVERLAY_OLD: - cmd = VIDIOC_OVERLAY; - break; - case VIDIOC_S_PARM_OLD: - cmd = VIDIOC_S_PARM; - break; - case VIDIOC_S_CTRL_OLD: - cmd = VIDIOC_S_CTRL; - break; - case VIDIOC_G_AUDIO_OLD: - cmd = VIDIOC_G_AUDIO; - break; - case VIDIOC_G_AUDOUT_OLD: - cmd = VIDIOC_G_AUDOUT; - break; - case VIDIOC_CROPCAP_OLD: - cmd = VIDIOC_CROPCAP; - break; - } - return cmd; -} -#endif - -/* * Obsolete usercopy function - Should be removed soon */ long @@ -342,9 +310,6 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, size_t ctrls_size = 0; void __user *user_ptr = NULL; -#ifdef __OLD_VIDIOC_ - cmd = video_fix_command(cmd); -#endif is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || cmd == VIDIOC_TRY_EXT_CTRLS); @@ -2379,9 +2344,6 @@ long video_ioctl2(struct file *file, void __user *user_ptr = NULL; void **kernel_ptr = NULL; -#ifdef __OLD_VIDIOC_ - cmd = video_fix_command(cmd); -#endif /* Copy arguments into temp kernel buffer */ if (_IOC_DIR(cmd) != _IOC_NONE) { if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { diff --git a/drivers/staging/easycap/easycap_ioctl.c b/drivers/staging/easycap/easycap_ioctl.c index 447953a..7ac43da 100644 --- a/drivers/staging/easycap/easycap_ioctl.c +++ b/drivers/staging/easycap/easycap_ioctl.c @@ -1399,11 +1399,6 @@ case VIDIOC_G_CTRL: { break; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -#if defined(VIDIOC_S_CTRL_OLD) -case VIDIOC_S_CTRL_OLD: { - JOM(8, "VIDIOC_S_CTRL_OLD required at least for xawtv\n"); -} -#endif /*VIDIOC_S_CTRL_OLD*/ case VIDIOC_S_CTRL: { struct v4l2_control v4l2_control; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 5122b26..a94c4d5 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1935,16 +1935,6 @@ struct v4l2_dbg_chip_ident { /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ -#ifdef __OLD_VIDIOC_ -/* for compatibility, will go away some day */ -#define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int) -#define VIDIOC_S_PARM_OLD _IOW('V', 22, struct v4l2_streamparm) -#define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control) -#define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct v4l2_audio) -#define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct v4l2_audioout) -#define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct v4l2_cropcap) -#endif - #define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */ #endif /* __LINUX_VIDEODEV2_H */ -- cgit v0.10.2 From e68044e2351ee937997c1e014fbbbe1c97b935cf Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 5 Feb 2011 10:07:39 -0300 Subject: [media] firedtv: remove obsolete ieee1394 backend code drivers/ieee1394/ has been removed in Linux 2.6.37. The corresponding backend code in firedtv is no longer built in now and can be deleted. Firedtv continues to work with drivers/firewire/. Also, fix a Kconfig menu comment: Removal of CONFIG_IEEE1394 made the "Supported FireWire (IEEE 1394) Adapters" comment disappear; bring it back with corrected dependency. Signed-off-by: Hans Verkuil Reviewed-by: Stefan Richter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 161ccfd..ee214c3 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -65,7 +65,7 @@ comment "Supported SDMC DM1105 Adapters" source "drivers/media/dvb/dm1105/Kconfig" comment "Supported FireWire (IEEE 1394) Adapters" - depends on DVB_CORE && IEEE1394 + depends on DVB_CORE && FIREWIRE source "drivers/media/dvb/firewire/Kconfig" comment "Supported Earthsoft PT1 Adapters" diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/dvb/firewire/Kconfig index 4afa292..f3e9448 100644 --- a/drivers/media/dvb/firewire/Kconfig +++ b/drivers/media/dvb/firewire/Kconfig @@ -1,6 +1,6 @@ config DVB_FIREDTV tristate "FireDTV and FloppyDTV" - depends on DVB_CORE && (FIREWIRE || IEEE1394) + depends on DVB_CORE && FIREWIRE help Support for DVB receivers from Digital Everywhere which are connected via IEEE 1394 (FireWire). @@ -13,12 +13,6 @@ config DVB_FIREDTV if DVB_FIREDTV -config DVB_FIREDTV_FIREWIRE - def_bool FIREWIRE = y || (FIREWIRE = m && DVB_FIREDTV = m) - -config DVB_FIREDTV_IEEE1394 - def_bool IEEE1394 = y || (IEEE1394 = m && DVB_FIREDTV = m) - config DVB_FIREDTV_INPUT def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m) diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile index da84203..357b3aa 100644 --- a/drivers/media/dvb/firewire/Makefile +++ b/drivers/media/dvb/firewire/Makefile @@ -1,9 +1,6 @@ obj-$(CONFIG_DVB_FIREDTV) += firedtv.o -firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o -firedtv-$(CONFIG_DVB_FIREDTV_FIREWIRE) += firedtv-fw.o -firedtv-$(CONFIG_DVB_FIREDTV_IEEE1394) += firedtv-1394.o +firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-$(CONFIG_DVB_FIREDTV_IEEE1394) += -Idrivers/ieee1394 diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c deleted file mode 100644 index b34ca7a..0000000 --- a/drivers/media/dvb/firewire/firedtv-1394.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * FireDTV driver -- ieee1394 I/O backend - * - * Copyright (C) 2004 Andreas Monitzer - * Copyright (C) 2007-2008 Ben Backx - * Copyright (C) 2008 Henrik Kurelid - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "firedtv.h" - -static LIST_HEAD(node_list); -static DEFINE_SPINLOCK(node_list_lock); - -#define CIP_HEADER_SIZE 8 -#define MPEG2_TS_HEADER_SIZE 4 -#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188) - -static void rawiso_activity_cb(struct hpsb_iso *iso) -{ - struct firedtv *f, *fdtv = NULL; - unsigned int i, num, packet; - unsigned char *buf; - unsigned long flags; - int count; - - spin_lock_irqsave(&node_list_lock, flags); - list_for_each_entry(f, &node_list, list) - if (f->backend_data == iso) { - fdtv = f; - break; - } - spin_unlock_irqrestore(&node_list_lock, flags); - - packet = iso->first_packet; - num = hpsb_iso_n_ready(iso); - - if (!fdtv) { - pr_err("received at unknown iso channel\n"); - goto out; - } - - for (i = 0; i < num; i++, packet = (packet + 1) % iso->buf_packets) { - buf = dma_region_i(&iso->data_buf, unsigned char, - iso->infos[packet].offset + CIP_HEADER_SIZE); - count = (iso->infos[packet].len - CIP_HEADER_SIZE) / - MPEG2_TS_SOURCE_PACKET_SIZE; - - /* ignore empty packet */ - if (iso->infos[packet].len <= CIP_HEADER_SIZE) - continue; - - while (count--) { - if (buf[MPEG2_TS_HEADER_SIZE] == 0x47) - dvb_dmx_swfilter_packets(&fdtv->demux, - &buf[MPEG2_TS_HEADER_SIZE], 1); - else - dev_err(fdtv->device, - "skipping invalid packet\n"); - buf += MPEG2_TS_SOURCE_PACKET_SIZE; - } - } -out: - hpsb_iso_recv_release_packets(iso, num); -} - -static inline struct node_entry *node_of(struct firedtv *fdtv) -{ - return container_of(fdtv->device, struct unit_directory, device)->ne; -} - -static int node_lock(struct firedtv *fdtv, u64 addr, void *data) -{ - quadlet_t *d = data; - int ret; - - ret = hpsb_node_lock(node_of(fdtv), addr, - EXTCODE_COMPARE_SWAP, &d[1], d[0]); - d[0] = d[1]; - - return ret; -} - -static int node_read(struct firedtv *fdtv, u64 addr, void *data) -{ - return hpsb_node_read(node_of(fdtv), addr, data, 4); -} - -static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) -{ - return hpsb_node_write(node_of(fdtv), addr, data, len); -} - -#define FDTV_ISO_BUFFER_PACKETS 256 -#define FDTV_ISO_BUFFER_SIZE (FDTV_ISO_BUFFER_PACKETS * 200) - -static int start_iso(struct firedtv *fdtv) -{ - struct hpsb_iso *iso_handle; - int ret; - - iso_handle = hpsb_iso_recv_init(node_of(fdtv)->host, - FDTV_ISO_BUFFER_SIZE, FDTV_ISO_BUFFER_PACKETS, - fdtv->isochannel, HPSB_ISO_DMA_DEFAULT, - -1, /* stat.config.irq_interval */ - rawiso_activity_cb); - if (iso_handle == NULL) { - dev_err(fdtv->device, "cannot initialize iso receive\n"); - return -ENOMEM; - } - fdtv->backend_data = iso_handle; - - ret = hpsb_iso_recv_start(iso_handle, -1, -1, 0); - if (ret != 0) { - dev_err(fdtv->device, "cannot start iso receive\n"); - hpsb_iso_shutdown(iso_handle); - fdtv->backend_data = NULL; - } - return ret; -} - -static void stop_iso(struct firedtv *fdtv) -{ - struct hpsb_iso *iso_handle = fdtv->backend_data; - - if (iso_handle != NULL) { - hpsb_iso_stop(iso_handle); - hpsb_iso_shutdown(iso_handle); - } - fdtv->backend_data = NULL; -} - -static const struct firedtv_backend fdtv_1394_backend = { - .lock = node_lock, - .read = node_read, - .write = node_write, - .start_iso = start_iso, - .stop_iso = stop_iso, -}; - -static void fcp_request(struct hpsb_host *host, int nodeid, int direction, - int cts, u8 *data, size_t length) -{ - struct firedtv *f, *fdtv = NULL; - unsigned long flags; - int su; - - if (length == 0 || (data[0] & 0xf0) != 0) - return; - - su = data[1] & 0x7; - - spin_lock_irqsave(&node_list_lock, flags); - list_for_each_entry(f, &node_list, list) - if (node_of(f)->host == host && - node_of(f)->nodeid == nodeid && - (f->subunit == su || (f->subunit == 0 && su == 0x7))) { - fdtv = f; - break; - } - spin_unlock_irqrestore(&node_list_lock, flags); - - if (fdtv) - avc_recv(fdtv, data, length); -} - -static int node_probe(struct device *dev) -{ - struct unit_directory *ud = - container_of(dev, struct unit_directory, device); - struct firedtv *fdtv; - int kv_len, err; - void *kv_str; - - if (ud->model_name_kv) { - kv_len = (ud->model_name_kv->value.leaf.len - 2) * 4; - kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); - } else { - kv_len = 0; - kv_str = NULL; - } - fdtv = fdtv_alloc(dev, &fdtv_1394_backend, kv_str, kv_len); - if (!fdtv) - return -ENOMEM; - - /* - * Work around a bug in udev's path_id script: Use the fw-host's dev - * instead of the unit directory's dev as parent of the input device. - */ - err = fdtv_register_rc(fdtv, dev->parent->parent); - if (err) - goto fail_free; - - spin_lock_irq(&node_list_lock); - list_add_tail(&fdtv->list, &node_list); - spin_unlock_irq(&node_list_lock); - - err = avc_identify_subunit(fdtv); - if (err) - goto fail; - - err = fdtv_dvb_register(fdtv); - if (err) - goto fail; - - avc_register_remote_control(fdtv); - - return 0; -fail: - spin_lock_irq(&node_list_lock); - list_del(&fdtv->list); - spin_unlock_irq(&node_list_lock); - fdtv_unregister_rc(fdtv); -fail_free: - kfree(fdtv); - - return err; -} - -static int node_remove(struct device *dev) -{ - struct firedtv *fdtv = dev_get_drvdata(dev); - - fdtv_dvb_unregister(fdtv); - - spin_lock_irq(&node_list_lock); - list_del(&fdtv->list); - spin_unlock_irq(&node_list_lock); - - fdtv_unregister_rc(fdtv); - kfree(fdtv); - - return 0; -} - -static int node_update(struct unit_directory *ud) -{ - struct firedtv *fdtv = dev_get_drvdata(&ud->device); - - if (fdtv->isochannel >= 0) - cmp_establish_pp_connection(fdtv, fdtv->subunit, - fdtv->isochannel); - return 0; -} - -static struct hpsb_protocol_driver fdtv_driver = { - .name = "firedtv", - .id_table = fdtv_id_table, - .update = node_update, - .driver = { - .probe = node_probe, - .remove = node_remove, - }, -}; - -static struct hpsb_highlevel fdtv_highlevel = { - .name = "firedtv", - .fcp_request = fcp_request, -}; - -int __init fdtv_1394_init(void) -{ - int ret; - - hpsb_register_highlevel(&fdtv_highlevel); - ret = hpsb_register_protocol(&fdtv_driver); - if (ret) { - printk(KERN_ERR "firedtv: failed to register protocol\n"); - hpsb_unregister_highlevel(&fdtv_highlevel); - } - return ret; -} - -void __exit fdtv_1394_exit(void) -{ - hpsb_unregister_protocol(&fdtv_driver); - hpsb_unregister_highlevel(&fdtv_highlevel); -} diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c index 079e8c5..86349db 100644 --- a/drivers/media/dvb/firewire/firedtv-dvb.c +++ b/drivers/media/dvb/firewire/firedtv-dvb.c @@ -351,16 +351,11 @@ static int __init fdtv_init(void) if (ret < 0) return ret; - ret = fdtv_1394_init(); - if (ret < 0) - fdtv_fw_exit(); - return ret; } static void __exit fdtv_exit(void) { - fdtv_1394_exit(); fdtv_fw_exit(); } diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h index 78cc28f..4a87049 100644 --- a/drivers/media/dvb/firewire/firedtv.h +++ b/drivers/media/dvb/firewire/firedtv.h @@ -118,15 +118,6 @@ struct firedtv { u8 avc_data[512]; }; -/* firedtv-1394.c */ -#ifdef CONFIG_DVB_FIREDTV_IEEE1394 -int fdtv_1394_init(void); -void fdtv_1394_exit(void); -#else -static inline int fdtv_1394_init(void) { return 0; } -static inline void fdtv_1394_exit(void) {} -#endif - /* firedtv-avc.c */ int avc_recv(struct firedtv *fdtv, void *data, size_t length); int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat); @@ -170,13 +161,8 @@ extern const struct ieee1394_device_id fdtv_id_table[]; void fdtv_frontend_init(struct firedtv *fdtv); /* firedtv-fw.c */ -#ifdef CONFIG_DVB_FIREDTV_FIREWIRE int fdtv_fw_init(void); void fdtv_fw_exit(void); -#else -static inline int fdtv_fw_init(void) { return 0; } -static inline void fdtv_fw_exit(void) {} -#endif /* firedtv-rc.c */ #ifdef CONFIG_DVB_FIREDTV_INPUT -- cgit v0.10.2 From d63044401a107ef22521e62c7f9b30c40b7c44ed Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 7 Feb 2011 04:37:38 -0300 Subject: [media] saa7134-empress: add missing MPEG controls Reported by, and based on a patch from, Martin Dauskardt. The AUDIO_AC3_BITRATE control is still missing from this list, but such optional controls are a bit hard to handle. This will be fixed automatically when we switch to the control framework. Signed-off-by: Hans Verkuil Reported-by: Martin Dauskardt Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 6b8459c7..18294db 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -373,6 +373,10 @@ static int empress_queryctrl(struct file *file, void *priv, static const u32 mpeg_ctrls[] = { V4L2_CID_MPEG_CLASS, V4L2_CID_MPEG_STREAM_TYPE, + V4L2_CID_MPEG_STREAM_PID_PMT, + V4L2_CID_MPEG_STREAM_PID_AUDIO, + V4L2_CID_MPEG_STREAM_PID_VIDEO, + V4L2_CID_MPEG_STREAM_PID_PCR, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, V4L2_CID_MPEG_AUDIO_ENCODING, V4L2_CID_MPEG_AUDIO_L2_BITRATE, -- cgit v0.10.2 From 03519b7e7ea531f5425be9d9bfe5f114e80951a0 Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Thu, 20 Jan 2011 05:19:40 -0300 Subject: [media] V4L: add enum_mbus_fsizes video operation Add enum_mbus_fsizes V4L2 subdevice video operation. Signed-off-by: Qing Xu Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index daf1e57..d4d74f9 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -271,6 +271,8 @@ struct v4l2_subdev_video_ops { struct v4l2_dv_timings *timings); int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code); + int (*enum_mbus_fsizes)(struct v4l2_subdev *sd, + struct v4l2_frmsizeenum *fsize); int (*g_mbus_fmt)(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt); int (*try_mbus_fmt)(struct v4l2_subdev *sd, -- cgit v0.10.2 From ed5b65dc362d488958075381d97931d865e77259 Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Thu, 20 Jan 2011 05:19:40 -0300 Subject: [media] V4L: soc-camera: add enum-frame-size ioctl add vidioc_enum_framesizes implementation, follow default_g_parm() and g_mbus_fmt() method Signed-off-by: Qing Xu Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index a66811b..a7820a5 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -191,6 +191,15 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) return v4l2_subdev_call(sd, core, s_std, *a); } +static int soc_camera_enum_fsizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + + return ici->ops->enum_fsizes(icd, fsize); +} + static int soc_camera_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { @@ -1175,6 +1184,31 @@ static int default_s_parm(struct soc_camera_device *icd, return v4l2_subdev_call(sd, video, s_parm, parm); } +static int default_enum_fsizes(struct soc_camera_device *icd, + struct v4l2_frmsizeenum *fsize) +{ + int ret; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + const struct soc_camera_format_xlate *xlate; + __u32 pixfmt = fsize->pixel_format; + struct v4l2_frmsizeenum fsize_mbus = *fsize; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) + return -EINVAL; + /* map xlate-code to pixel_format, sensor only handle xlate-code*/ + fsize_mbus.pixel_format = xlate->code; + + ret = v4l2_subdev_call(sd, video, enum_mbus_fsizes, &fsize_mbus); + if (ret < 0) + return ret; + + *fsize = fsize_mbus; + fsize->pixel_format = pixfmt; + + return 0; +} + static void soc_camera_device_init(struct device *dev, void *pdata) { dev->platform_data = pdata; @@ -1210,6 +1244,8 @@ int soc_camera_host_register(struct soc_camera_host *ici) ici->ops->set_parm = default_s_parm; if (!ici->ops->get_parm) ici->ops->get_parm = default_g_parm; + if (!ici->ops->enum_fsizes) + ici->ops->enum_fsizes = default_enum_fsizes; mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { @@ -1317,6 +1353,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_g_input = soc_camera_g_input, .vidioc_s_input = soc_camera_s_input, .vidioc_s_std = soc_camera_s_std, + .vidioc_enum_framesizes = soc_camera_enum_fsizes, .vidioc_reqbufs = soc_camera_reqbufs, .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, .vidioc_querybuf = soc_camera_querybuf, diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 9386db8..09b8271 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -85,6 +85,7 @@ struct soc_camera_host_ops { int (*set_ctrl)(struct soc_camera_device *, struct v4l2_control *); int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *); int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *); + int (*enum_fsizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *); unsigned int (*poll)(struct file *, poll_table *); const struct v4l2_queryctrl *controls; int num_controls; -- cgit v0.10.2 From 7fdbd85bce033e2d0f74e5d326ab43906653cb81 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 31 Jan 2011 09:19:32 -0300 Subject: [media] V4L: soc-camera: start stream after queueing the buffers Some camera systems have strong requirement for capturing an exact number of frames after starting the stream and do not tolerate losing captured frames. By starting the stream after the videobuf has queued the buffers, we ensure that no frame will be lost. Signed-off-by: Anatolij Gustschin Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index a7820a5..67611ad 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -655,10 +655,10 @@ static int soc_camera_streamon(struct file *file, void *priv, if (icd->streamer != file) return -EBUSY; - v4l2_subdev_call(sd, video, s_stream, 1); - /* This calls buf_queue from host driver's videobuf_queue_ops */ ret = videobuf_streamon(&icd->vb_vidq); + if (!ret) + v4l2_subdev_call(sd, video, s_stream, 1); return ret; } -- cgit v0.10.2 From 48a3c77338d725d4976fa792d822eb940ce5c296 Mon Sep 17 00:00:00 2001 From: Alberto Panizzo Date: Wed, 12 Jan 2011 08:16:19 -0300 Subject: [media] V4L: soc_mediabus: add a method to obtain the number of samples per pixel Add a function to calculate the number of samples on the media-bus, required to retrieve one pixel of a certain format. Signed-off-by: Alberto Panizzo Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index 9139121..73b4138 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -132,6 +132,20 @@ static const struct soc_mbus_pixelfmt mbus_fmt[] = { }, }; +int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf) +{ + switch (mf->packing) { + case SOC_MBUS_PACKING_NONE: + case SOC_MBUS_PACKING_EXTEND16: + return 1; + case SOC_MBUS_PACKING_2X8_PADHI: + case SOC_MBUS_PACKING_2X8_PADLO: + return 2; + } + return -EINVAL; +} +EXPORT_SYMBOL(soc_mbus_samples_per_pixel); + s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) { switch (mf->packing) { diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h index 037cd7b..f5522b3 100644 --- a/include/media/soc_mediabus.h +++ b/include/media/soc_mediabus.h @@ -61,5 +61,6 @@ struct soc_mbus_pixelfmt { const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( enum v4l2_mbus_pixelcode code); s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf); +int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf); #endif -- cgit v0.10.2 From 5bccd60852fc0a5ccc1980c10266896df99b8bda Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sun, 30 Jan 2011 07:05:58 -0300 Subject: [media] V4L: omap1_camera: fix use after free Even though clk_put() is a no-op on most architectures it is not for some ARM implementations. To not fail on those, release the clock timer before freeing the surrounding structure. This bug was spotted by the semantic patch tool coccinelle using the script found at scripts/coccinelle/free/kfree.cocci. More information about semantic patching is available at http://coccinelle.lip6.fr/ Signed-off-by: Mathias Krause Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c index 0a2fb2b..9ed1513 100644 --- a/drivers/media/video/omap1_camera.c +++ b/drivers/media/video/omap1_camera.c @@ -1664,10 +1664,10 @@ static int __exit omap1_cam_remove(struct platform_device *pdev) res = pcdev->res; release_mem_region(res->start, resource_size(res)); - kfree(pcdev); - clk_put(pcdev->clk); + kfree(pcdev); + dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n"); return 0; -- cgit v0.10.2 From 44facdc8c43a8b5172d6cbe995845c9889561aa0 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 31 Jan 2011 09:58:01 -0300 Subject: [media] V4L: mx3_camera: correct 'sizeimage' value reporting The 'pix->width' field may be updated in mx3_camera_set_fmt() to fulfill the IPU stride line alignment requirements. If this update takes place, the 'fmt.pix.sizeimage' field in the struct v4l2_format stucture returned by VIDIOC_S_FMT is wrong. We need to update the 'pix->sizeimage' field in the mx3_camera_set_fmt() function to fix this issue. Signed-off-by: Anatolij Gustschin Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index b9cb4a4..1cfff41 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -918,6 +918,12 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, pix->colorspace = mf.colorspace; icd->current_fmt = xlate; + pix->bytesperline = soc_mbus_bytes_per_line(pix->width, + xlate->host_fmt); + if (pix->bytesperline < 0) + return pix->bytesperline; + pix->sizeimage = pix->height * pix->bytesperline; + dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height); return ret; -- cgit v0.10.2 From a48be1d626ceb05b58a980a962214431af8041e8 Mon Sep 17 00:00:00 2001 From: Alberto Panizzo Date: Mon, 17 Jan 2011 06:52:10 -0300 Subject: [media] V4L: mx3_camera: fix capture issues for non 8-bit per pixel formats If the camera was set to output formats like RGB565 YUYV or SBGGR10, the resulting image was scrambled due to erroneous interpretations of horizontal parameter's units. This patch in fourcc_to_ipu_pix, eliminate also the pixel formats mappings that, first are not used within mainline code and second, standing at the datasheets, they will not work properly: The IPU internal bus support only the following data formatting (44.1.1.3 Data Flows and Formats): 1 YUV 4:4:4 or RGB-8 bits per color component 2 YUV 4:4:4 or RGB-10 bits per color component 3 Generic data (from sensor to the system memory only) And format conversions are done: - from memory: unpacking from other formats to IPU supported ones - to memory: packing in the inverse order. So, assigning a packing/unpacking strategy to the IPU for those formats will produce a packing to memory and not the inverse. Signed-off-by: Alberto Panizzo Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 1cfff41..c8c6c03 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -324,14 +324,10 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) { /* Add more formats as need arises and test possibilities appear... */ switch (fourcc) { - case V4L2_PIX_FMT_RGB565: - return IPU_PIX_FMT_RGB565; case V4L2_PIX_FMT_RGB24: return IPU_PIX_FMT_RGB24; - case V4L2_PIX_FMT_RGB332: - return IPU_PIX_FMT_RGB332; - case V4L2_PIX_FMT_YUV422P: - return IPU_PIX_FMT_YVU422P; + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_RGB565: default: return IPU_PIX_FMT_GENERIC; } @@ -359,9 +355,31 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, /* This is the configuration of one sg-element */ video->out_pixel_fmt = fourcc_to_ipu_pix(fourcc); - video->out_width = icd->user_width; - video->out_height = icd->user_height; - video->out_stride = icd->user_width; + + if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) { + /* + * If the IPU DMA channel is configured to transport + * generic 8-bit data, we have to set up correctly the + * geometry parameters upon the current pixel format. + * So, since the DMA horizontal parameters are expressed + * in bytes not pixels, convert these in the right unit. + */ + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + BUG_ON(bytes_per_line <= 0); + + video->out_width = bytes_per_line; + video->out_height = icd->user_height; + video->out_stride = bytes_per_line; + } else { + /* + * For IPU known formats the pixel unit will be managed + * successfully by the IPU code + */ + video->out_width = icd->user_width; + video->out_height = icd->user_height; + video->out_stride = icd->user_width; + } #ifdef DEBUG /* helps to see what DMA actually has written */ @@ -734,18 +752,36 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id if (xlate) { xlate->host_fmt = fmt; xlate->code = code; + dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n", + (fmt->fourcc >> (0*8)) & 0xFF, + (fmt->fourcc >> (1*8)) & 0xFF, + (fmt->fourcc >> (2*8)) & 0xFF, + (fmt->fourcc >> (3*8)) & 0xFF); xlate++; - dev_dbg(dev, "Providing format %x in pass-through mode\n", - xlate->host_fmt->fourcc); } return formats; } static void configure_geometry(struct mx3_camera_dev *mx3_cam, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height, + enum v4l2_mbus_pixelcode code) { u32 ctrl, width_field, height_field; + const struct soc_mbus_pixelfmt *fmt; + + fmt = soc_mbus_get_fmtdesc(code); + BUG_ON(!fmt); + + if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) { + /* + * As the CSI will be configured to output BAYER, here + * the width parameter count the number of samples to + * capture to complete the whole image width. + */ + width *= soc_mbus_samples_per_pixel(fmt); + BUG_ON(width < 0); + } /* Setup frame size - this cannot be changed on-the-fly... */ width_field = width - 1; @@ -854,7 +890,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, return ret; } - configure_geometry(mx3_cam, mf.width, mf.height); + configure_geometry(mx3_cam, mf.width, mf.height, mf.code); } dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n", @@ -897,7 +933,7 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, * mxc_v4l2_s_fmt() */ - configure_geometry(mx3_cam, pix->width, pix->height); + configure_geometry(mx3_cam, pix->width, pix->height, xlate->code); mf.width = pix->width; mf.height = pix->height; -- cgit v0.10.2 From 353c2129fdff2047237193932971e50734c64336 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 2 Feb 2011 17:38:22 -0300 Subject: [media] V4L: omap1_camera: join split format lines Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c index 9ed1513..eab31cb 100644 --- a/drivers/media/video/omap1_camera.c +++ b/drivers/media/video/omap1_camera.c @@ -811,8 +811,8 @@ static irqreturn_t cam_isr(int irq, void *data) spin_lock_irqsave(&pcdev->lock, flags); if (WARN_ON(!buf)) { - dev_warn(dev, "%s: unhandled camera interrupt, status == " - "%#x\n", __func__, it_status); + dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n", + __func__, it_status); suspend_capture(pcdev); disable_capture(pcdev); goto out; @@ -1088,15 +1088,15 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd, xlate->host_fmt = &omap1_cam_formats[code]; xlate->code = code; xlate++; - dev_dbg(dev, "%s: providing format %s " - "as byte swapped code #%d\n", __func__, - omap1_cam_formats[code].name, code); + dev_dbg(dev, + "%s: providing format %s as byte swapped code #%d\n", + __func__, omap1_cam_formats[code].name, code); } default: if (xlate) - dev_dbg(dev, "%s: providing format %s " - "in pass-through mode\n", __func__, - fmt->name); + dev_dbg(dev, + "%s: providing format %s in pass-through mode\n", + __func__, fmt->name); } formats++; if (xlate) { @@ -1139,29 +1139,29 @@ static int dma_align(int *width, int *height, return 1; } -#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...) \ -({ \ - struct soc_camera_sense sense = { \ - .master_clock = pcdev->camexclk, \ - .pixel_clock_max = 0, \ - }; \ - int __ret; \ - \ - if (pcdev->pdata) \ - sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \ - icd->sense = &sense; \ - __ret = v4l2_subdev_call(sd, video, function, ##args); \ - icd->sense = NULL; \ - \ - if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \ - if (sense.pixel_clock > sense.pixel_clock_max) { \ - dev_err(dev, "%s: pixel clock %lu " \ - "set by the camera too high!\n", \ - __func__, sense.pixel_clock); \ - __ret = -EINVAL; \ - } \ - } \ - __ret; \ +#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...) \ +({ \ + struct soc_camera_sense sense = { \ + .master_clock = pcdev->camexclk, \ + .pixel_clock_max = 0, \ + }; \ + int __ret; \ + \ + if (pcdev->pdata) \ + sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \ + icd->sense = &sense; \ + __ret = v4l2_subdev_call(sd, video, function, ##args); \ + icd->sense = NULL; \ + \ + if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \ + if (sense.pixel_clock > sense.pixel_clock_max) { \ + dev_err(dev, \ + "%s: pixel clock %lu set by the camera too high!\n", \ + __func__, sense.pixel_clock); \ + __ret = -EINVAL; \ + } \ + } \ + __ret; \ }) static int set_mbus_format(struct omap1_cam_dev *pcdev, struct device *dev, -- cgit v0.10.2 From bd94f588283b167f7fa8c2dc2164dcd2f4a4a7a5 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 28 Jan 2011 21:06:19 -0300 Subject: [media] V4L: add missing EXPORT_SYMBOL* statements to vb2 videobuf2-memops and videobuf2-core can be compiled as modules, in which case 3 more symbols from videobuf2-memops.c have to be exported. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c index 053c157..a3eb656 100644 --- a/drivers/media/video/videobuf2-memops.c +++ b/drivers/media/video/videobuf2-memops.c @@ -77,6 +77,7 @@ void vb2_put_vma(struct vm_area_struct *vma) kfree(vma); } +EXPORT_SYMBOL_GPL(vb2_put_vma); /** * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory @@ -141,6 +142,7 @@ done: up_read(&mm->mmap_sem); return ret; } +EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); /** * vb2_mmap_pfn_range() - map physical pages to userspace @@ -180,6 +182,7 @@ int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr, return 0; } +EXPORT_SYMBOL_GPL(vb2_mmap_pfn_range); /** * vb2_common_vm_open() - increase refcount of the vma -- cgit v0.10.2 From 592c2aba266ca5be0dce300d0400817d943d49af Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Sat, 29 Jan 2011 12:44:51 -0300 Subject: [media] V4L: soc-camera: extend to also support videobuf2 Extend soc-camera core to also support the videobuf2 API. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index d40a8fc..07c5baa 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -750,6 +750,7 @@ config SOC_CAMERA tristate "SoC camera support" depends on VIDEO_V4L2 && HAS_DMA && I2C select VIDEOBUF_GEN + select VIDEOBUF2_CORE help SoC Camera is a common API to several cameras, not connecting over a bus like PCI or USB. For example some i2c camera connected diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 67611ad..fa80a4a 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -34,6 +34,7 @@ #include #include #include +#include #include /* Default to VGA resolution */ @@ -212,11 +213,16 @@ static int soc_camera_reqbufs(struct file *file, void *priv, if (icd->streamer && icd->streamer != file) return -EBUSY; - ret = videobuf_reqbufs(&icd->vb_vidq, p); - if (ret < 0) - return ret; + if (ici->ops->init_videobuf) { + ret = videobuf_reqbufs(&icd->vb_vidq, p); + if (ret < 0) + return ret; + + ret = ici->ops->reqbufs(icd, p); + } else { + ret = vb2_reqbufs(&icd->vb2_vidq, p); + } - ret = ici->ops->reqbufs(icd, p); if (!ret && !icd->streamer) icd->streamer = file; @@ -227,36 +233,48 @@ static int soc_camera_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); WARN_ON(priv != file->private_data); - return videobuf_querybuf(&icd->vb_vidq, p); + if (ici->ops->init_videobuf) + return videobuf_querybuf(&icd->vb_vidq, p); + else + return vb2_querybuf(&icd->vb2_vidq, p); } static int soc_camera_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); WARN_ON(priv != file->private_data); if (icd->streamer != file) return -EBUSY; - return videobuf_qbuf(&icd->vb_vidq, p); + if (ici->ops->init_videobuf) + return videobuf_qbuf(&icd->vb_vidq, p); + else + return vb2_qbuf(&icd->vb2_vidq, p); } static int soc_camera_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); WARN_ON(priv != file->private_data); if (icd->streamer != file) return -EBUSY; - return videobuf_dqbuf(&icd->vb_vidq, p, file->f_flags & O_NONBLOCK); + if (ici->ops->init_videobuf) + return videobuf_dqbuf(&icd->vb_vidq, p, file->f_flags & O_NONBLOCK); + else + return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK); } /* Always entered with .video_lock held */ @@ -372,8 +390,9 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, icd->user_width = pix->width; icd->user_height = pix->height; icd->colorspace = pix->colorspace; - icd->vb_vidq.field = - icd->field = pix->field; + icd->field = pix->field; + if (ici->ops->init_videobuf) + icd->vb_vidq.field = pix->field; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", @@ -453,7 +472,13 @@ static int soc_camera_open(struct file *file) if (ret < 0) goto esfmt; - ici->ops->init_videobuf(&icd->vb_vidq, icd); + if (ici->ops->init_videobuf) { + ici->ops->init_videobuf(&icd->vb_vidq, icd); + } else { + ret = ici->ops->init_videobuf2(&icd->vb2_vidq, icd); + if (ret < 0) + goto einitvb; + } } file->private_data = icd; @@ -465,6 +490,7 @@ static int soc_camera_open(struct file *file) * First four errors are entered with the .video_lock held * and use_count == 1 */ +einitvb: esfmt: pm_runtime_disable(&icd->vdev->dev); eresume: @@ -491,6 +517,8 @@ static int soc_camera_close(struct file *file) pm_runtime_disable(&icd->vdev->dev); ici->ops->remove(icd); + if (ici->ops->init_videobuf2) + vb2_queue_release(&icd->vb2_vidq); soc_camera_power_set(icd, icl, 0); } @@ -519,6 +547,7 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf, static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) { struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); int err; dev_dbg(&icd->dev, "mmap called, vma=0x%08lx\n", (unsigned long)vma); @@ -526,7 +555,10 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) if (icd->streamer != file) return -EBUSY; - err = videobuf_mmap_mapper(&icd->vb_vidq, vma); + if (ici->ops->init_videobuf) + err = videobuf_mmap_mapper(&icd->vb_vidq, vma); + else + err = vb2_mmap(&icd->vb2_vidq, vma); dev_dbg(&icd->dev, "vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, @@ -544,7 +576,7 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt) if (icd->streamer != file) return -EBUSY; - if (list_empty(&icd->vb_vidq.stream)) { + if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) { dev_err(&icd->dev, "Trying to poll with no queued buffers!\n"); return POLLERR; } @@ -552,6 +584,20 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt) return ici->ops->poll(file, pt); } +void soc_camera_lock(struct vb2_queue *vq) +{ + struct soc_camera_device *icd = vb2_get_drv_priv(vq); + mutex_lock(&icd->video_lock); +} +EXPORT_SYMBOL(soc_camera_lock); + +void soc_camera_unlock(struct vb2_queue *vq) +{ + struct soc_camera_device *icd = vb2_get_drv_priv(vq); + mutex_unlock(&icd->video_lock); +} +EXPORT_SYMBOL(soc_camera_unlock); + static struct v4l2_file_operations soc_camera_fops = { .owner = THIS_MODULE, .open = soc_camera_open, @@ -615,7 +661,7 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, pix->width = icd->user_width; pix->height = icd->user_height; - pix->field = icd->vb_vidq.field; + pix->field = icd->field; pix->pixelformat = icd->current_fmt->host_fmt->fourcc; pix->bytesperline = soc_mbus_bytes_per_line(pix->width, icd->current_fmt->host_fmt); @@ -644,6 +690,7 @@ static int soc_camera_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct v4l2_subdev *sd = soc_camera_to_subdev(icd); int ret; @@ -656,7 +703,11 @@ static int soc_camera_streamon(struct file *file, void *priv, return -EBUSY; /* This calls buf_queue from host driver's videobuf_queue_ops */ - ret = videobuf_streamon(&icd->vb_vidq); + if (ici->ops->init_videobuf) + ret = videobuf_streamon(&icd->vb_vidq); + else + ret = vb2_streamon(&icd->vb2_vidq, i); + if (!ret) v4l2_subdev_call(sd, video, s_stream, 1); @@ -668,6 +719,7 @@ static int soc_camera_streamoff(struct file *file, void *priv, { struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); WARN_ON(priv != file->private_data); @@ -681,7 +733,10 @@ static int soc_camera_streamoff(struct file *file, void *priv, * This calls buf_release from host driver's videobuf_queue_ops for all * remaining buffers. When the last buffer is freed, stop capture */ - videobuf_streamoff(&icd->vb_vidq); + if (ici->ops->init_videobuf) + videobuf_streamoff(&icd->vb_vidq); + else + vb2_streamoff(&icd->vb2_vidq, i); v4l2_subdev_call(sd, video, s_stream, 0); @@ -1226,8 +1281,9 @@ int soc_camera_host_register(struct soc_camera_host *ici) !ici->ops->set_fmt || !ici->ops->set_bus_param || !ici->ops->querycap || - !ici->ops->init_videobuf || - !ici->ops->reqbufs || + ((!ici->ops->init_videobuf || + !ici->ops->reqbufs) && + !ici->ops->init_videobuf2) || !ici->ops->add || !ici->ops->remove || !ici->ops->poll || diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 09b8271..e29ff74 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -17,6 +17,7 @@ #include #include #include +#include #include extern struct bus_type soc_camera_bus_type; @@ -44,7 +45,10 @@ struct soc_camera_device { int use_count; struct mutex video_lock; /* Protects device data */ struct file *streamer; /* stream owner */ - struct videobuf_queue vb_vidq; + union { + struct videobuf_queue vb_vidq; + struct vb2_queue vb2_vidq; + }; }; struct soc_camera_host { @@ -78,6 +82,8 @@ struct soc_camera_host_ops { int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); void (*init_videobuf)(struct videobuf_queue *, struct soc_camera_device *); + int (*init_videobuf2)(struct vb2_queue *, + struct soc_camera_device *); int (*reqbufs)(struct soc_camera_device *, struct v4l2_requestbuffers *); int (*querycap)(struct soc_camera_host *, struct v4l2_capability *); int (*set_bus_param)(struct soc_camera_device *, __u32); @@ -300,4 +306,7 @@ static inline struct video_device *soc_camera_i2c_to_vdev(struct i2c_client *cli return icd->vdev; } +void soc_camera_lock(struct vb2_queue *vq); +void soc_camera_unlock(struct vb2_queue *vq); + #endif -- cgit v0.10.2 From 8eb4476d44b39fe2f5829de030328223bda2abaf Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 7 Feb 2011 18:09:30 -0300 Subject: [media] V4L: soc-camera: add helper functions for videobuf queue handling Add two helper inline functions to retrieve soc-camera device context from videobuf and videobuf2 queue pointers. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index e29ff74..6e96b26 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -306,6 +306,16 @@ static inline struct video_device *soc_camera_i2c_to_vdev(struct i2c_client *cli return icd->vdev; } +static inline struct soc_camera_device *soc_camera_from_vb2q(struct vb2_queue *vq) +{ + return container_of(vq, struct soc_camera_device, vb2_vidq); +} + +static inline struct soc_camera_device *soc_camera_from_vbq(struct videobuf_queue *vq) +{ + return container_of(vq, struct soc_camera_device, vb_vidq); +} + void soc_camera_lock(struct vb2_queue *vq); void soc_camera_unlock(struct vb2_queue *vq); -- cgit v0.10.2 From a616898d6df29b8ffaea5742f678b3ac223b307c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 18 Feb 2011 05:30:15 -0300 Subject: [media] V4L: sh_mobile_ceu_camera: convert to videobuf2 Convert the sh_mobile_ceu_camera driver to the videobuf2 API. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 07c5baa..cf81bd5 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -877,7 +877,7 @@ config VIDEO_SH_MOBILE_CSI2 config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK - select VIDEOBUF_DMA_CONTIG + select VIDEOBUF2_DMA_CONTIG ---help--- This is a v4l2 driver for the SuperH Mobile CEU Interface diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 954222b..325f50d 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include @@ -87,7 +87,8 @@ /* per video frame buffer */ struct sh_mobile_ceu_buffer { - struct videobuf_buffer vb; /* v4l buffer must be first */ + struct vb2_buffer vb; /* v4l buffer must be first */ + struct list_head queue; enum v4l2_mbus_pixelcode code; }; @@ -99,16 +100,17 @@ struct sh_mobile_ceu_dev { void __iomem *base; unsigned long video_limit; - /* lock used to protect videobuf */ - spinlock_t lock; + spinlock_t lock; /* Protects video buffer lists */ struct list_head capture; - struct videobuf_buffer *active; + struct vb2_buffer *active; + struct vb2_alloc_ctx *alloc_ctx; struct sh_mobile_ceu_info *pdata; u32 cflcr; enum v4l2_field field; + int sequence; unsigned int image_mode:1; unsigned int is_16bit:1; @@ -133,6 +135,11 @@ struct sh_mobile_ceu_cam { enum v4l2_mbus_pixelcode code; }; +static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb) +{ + return container_of(vb, struct sh_mobile_ceu_buffer, vb); +} + static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev) { unsigned long flags; @@ -205,11 +212,11 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) /* * Videobuf operations */ -static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, - unsigned int *count, - unsigned int *size) +static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, + unsigned int *count, unsigned int *num_planes, + unsigned long sizes[], void *alloc_ctxs[]) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, @@ -218,39 +225,25 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, if (bytes_per_line < 0) return bytes_per_line; - *size = bytes_per_line * icd->user_height; + *num_planes = 1; + + pcdev->sequence = 0; + sizes[0] = bytes_per_line * icd->user_height; + alloc_ctxs[0] = pcdev->alloc_ctx; - if (0 == *count) + if (!*count) *count = 2; if (pcdev->video_limit) { - if (PAGE_ALIGN(*size) * *count > pcdev->video_limit) - *count = pcdev->video_limit / PAGE_ALIGN(*size); + if (PAGE_ALIGN(sizes[0]) * *count > pcdev->video_limit) + *count = pcdev->video_limit / PAGE_ALIGN(sizes[0]); } - dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->dev.parent, "count=%d, size=%lu\n", *count, sizes[0]); return 0; } -static void free_buffer(struct videobuf_queue *vq, - struct sh_mobile_ceu_buffer *buf) -{ - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->dev.parent; - - dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, - &buf->vb, buf->vb.baddr, buf->vb.bsize); - - if (in_interrupt()) - BUG(); - - videobuf_waiton(vq, &buf->vb, 0, 0); - videobuf_dma_contig_free(vq, &buf->vb); - dev_dbg(dev, "%s freed\n", __func__); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - #define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ #define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ #define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ @@ -309,7 +302,10 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) bottom2 = CDBCR; } - phys_addr_top = videobuf_to_dma_contig(pcdev->active); + /* mem_ops->cookie must not be NULL */ + phys_addr_top = (dma_addr_t)icd->vb2_vidq.mem_ops->cookie(pcdev-> + active->planes[0].mem_priv); + ceu_write(pcdev, top1, phys_addr_top); if (V4L2_FIELD_NONE != pcdev->field) { phys_addr_bottom = phys_addr_top + icd->user_width; @@ -330,87 +326,67 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) } } - pcdev->active->state = VIDEOBUF_ACTIVE; ceu_write(pcdev, CAPSR, 0x1); /* start capture */ return ret; } -static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) +static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); struct sh_mobile_ceu_buffer *buf; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - int ret; + unsigned long size; if (bytes_per_line < 0) return bytes_per_line; - buf = container_of(vb, struct sh_mobile_ceu_buffer, vb); + buf = to_ceu_vb(vb); - dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); /* Added list head initialization on alloc */ - WARN_ON(!list_empty(&vb->queue)); + WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb); #ifdef DEBUG /* * This can be useful if you want to see if we actually fill * the buffer with something */ - memset((void *)vb->baddr, 0xaa, vb->bsize); + if (vb2_plane_vaddr(vb, 0)) + memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0)); #endif BUG_ON(NULL == icd->current_fmt); - 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; - } + size = icd->user_height * bytes_per_line; - vb->size = vb->height * bytes_per_line; - if (0 != vb->baddr && vb->bsize < vb->size) { - ret = -EINVAL; - goto out; + if (vb2_plane_size(vb, 0) < size) { + dev_err(icd->dev.parent, "Buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -ENOBUFS; } - if (vb->state == VIDEOBUF_NEEDS_INIT) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - vb->state = VIDEOBUF_PREPARED; - } + vb2_set_plane_payload(vb, 0, size); return 0; -fail: - free_buffer(vq, buf); -out: - return ret; } -/* Called under spinlock_irqsave(&pcdev->lock, ...) */ -static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; + struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); + unsigned long flags; - dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &pcdev->capture); + spin_lock_irqsave(&pcdev->lock, flags); + list_add_tail(&buf->queue, &pcdev->capture); if (!pcdev->active) { /* @@ -421,13 +397,14 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, pcdev->active = vb; sh_mobile_ceu_capture(pcdev); } + spin_unlock_irqrestore(&pcdev->lock, flags); } -static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); struct sh_mobile_ceu_dev *pcdev = ici->priv; unsigned long flags; @@ -439,53 +416,60 @@ static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, pcdev->active = NULL; } - if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && - !list_empty(&vb->queue)) { - vb->state = VIDEOBUF_ERROR; - list_del_init(&vb->queue); - } + /* Doesn't hurt also if the list is empty */ + list_del_init(&buf->queue); spin_unlock_irqrestore(&pcdev->lock, flags); +} - free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb)); +static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) +{ + /* This is for locking debugging only */ + INIT_LIST_HEAD(&to_ceu_vb(vb)->queue); + return 0; } -static struct videobuf_queue_ops sh_mobile_ceu_videobuf_ops = { - .buf_setup = sh_mobile_ceu_videobuf_setup, - .buf_prepare = sh_mobile_ceu_videobuf_prepare, - .buf_queue = sh_mobile_ceu_videobuf_queue, - .buf_release = sh_mobile_ceu_videobuf_release, +static struct vb2_ops sh_mobile_ceu_videobuf_ops = { + .queue_setup = sh_mobile_ceu_videobuf_setup, + .buf_prepare = sh_mobile_ceu_videobuf_prepare, + .buf_queue = sh_mobile_ceu_videobuf_queue, + .buf_cleanup = sh_mobile_ceu_videobuf_release, + .buf_init = sh_mobile_ceu_videobuf_init, + .wait_prepare = soc_camera_unlock, + .wait_finish = soc_camera_lock, }; static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) { struct sh_mobile_ceu_dev *pcdev = data; - struct videobuf_buffer *vb; - unsigned long flags; + struct vb2_buffer *vb; + int ret; - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock(&pcdev->lock); vb = pcdev->active; if (!vb) /* Stale interrupt from a released buffer */ goto out; - list_del_init(&vb->queue); + list_del_init(&to_ceu_vb(vb)->queue); if (!list_empty(&pcdev->capture)) - pcdev->active = list_entry(pcdev->capture.next, - struct videobuf_buffer, queue); + pcdev->active = &list_entry(pcdev->capture.next, + struct sh_mobile_ceu_buffer, queue)->vb; else pcdev->active = NULL; - vb->state = (sh_mobile_ceu_capture(pcdev) < 0) ? - VIDEOBUF_ERROR : VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); - vb->field_count++; - wake_up(&vb->done); + ret = sh_mobile_ceu_capture(pcdev); + do_gettimeofday(&vb->v4l2_buf.timestamp); + if (!ret) { + vb->v4l2_buf.field = pcdev->field; + vb->v4l2_buf.sequence = pcdev->sequence++; + } + vb2_buffer_done(vb, ret < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); out: - spin_unlock_irqrestore(&pcdev->lock, flags); + spin_unlock(&pcdev->lock); return IRQ_HANDLED; } @@ -529,9 +513,8 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) /* make sure active buffer is canceled */ spin_lock_irqsave(&pcdev->lock, flags); if (pcdev->active) { - list_del(&pcdev->active->queue); - pcdev->active->state = VIDEOBUF_ERROR; - wake_up_all(&pcdev->active->done); + list_del_init(&to_ceu_vb(pcdev->active)->queue); + vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); pcdev->active = NULL; } spin_unlock_irqrestore(&pcdev->lock, flags); @@ -686,6 +669,7 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) ceu_write(pcdev, CAPSR, capsr); } +/* Capture is not running, no interrupts, no locking needed */ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { @@ -1726,43 +1710,11 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, return ret; } -static int sh_mobile_ceu_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - int i; - - /* - * This is for locking debugging only. I removed spinlocks and now I - * check whether .prepare is ever called on a linked buffer, or whether - * a dma IRQ can occur for an in-work or unlinked buffer. Until now - * it hadn't triggered - */ - for (i = 0; i < p->count; i++) { - struct sh_mobile_ceu_buffer *buf; - - buf = container_of(icd->vb_vidq.bufs[i], - struct sh_mobile_ceu_buffer, vb); - INIT_LIST_HEAD(&buf->vb.queue); - } - - return 0; -} - static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt) { struct soc_camera_device *icd = file->private_data; - struct sh_mobile_ceu_buffer *buf; - - buf = list_entry(icd->vb_vidq.stream.next, - struct sh_mobile_ceu_buffer, vb.stream); - - poll_wait(file, &buf->vb.done, pt); - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return POLLIN|POLLRDNORM; - - return 0; + return vb2_poll(&icd->vb2_vidq, file, pt); } static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, @@ -1774,19 +1726,17 @@ static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, return 0; } -static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) +static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, + struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - videobuf_queue_dma_contig_init(q, - &sh_mobile_ceu_videobuf_ops, - icd->dev.parent, &pcdev->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - pcdev->field, - sizeof(struct sh_mobile_ceu_buffer), - icd, &icd->video_lock); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = icd; + q->ops = &sh_mobile_ceu_videobuf_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); + + return vb2_queue_init(q); } static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd, @@ -1850,11 +1800,10 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .try_fmt = sh_mobile_ceu_try_fmt, .set_ctrl = sh_mobile_ceu_set_ctrl, .get_ctrl = sh_mobile_ceu_get_ctrl, - .reqbufs = sh_mobile_ceu_reqbufs, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, .set_bus_param = sh_mobile_ceu_set_bus_param, - .init_videobuf = sh_mobile_ceu_init_videobuf, + .init_videobuf2 = sh_mobile_ceu_init_videobuf, .controls = sh_mobile_ceu_controls, .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), }; @@ -2005,12 +1954,20 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) } } + pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(pcdev->alloc_ctx)) { + err = PTR_ERR(pcdev->alloc_ctx); + goto exit_module_put; + } + err = soc_camera_host_register(&pcdev->ici); if (err) - goto exit_module_put; + goto exit_free_ctx; return 0; +exit_free_ctx: + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); exit_module_put: if (csi2 && csi2->driver) module_put(csi2->driver->owner); @@ -2041,6 +1998,7 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); iounmap(pcdev->base); + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); if (csi2 && csi2->driver) module_put(csi2->driver->owner); kfree(pcdev); -- cgit v0.10.2 From 379fa5d3567ca2c20843bdba06e456de51b195e9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 21 Feb 2011 07:14:01 -0300 Subject: [media] V4L: mx3_camera: convert to videobuf2 Now that soc-camera supports videobuf API v1 and v2, camera-host drivers can be converted to videobuf2 individually. This patch converts the mx3_camera driver. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index c8c6c03..502e2a4 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include @@ -62,10 +62,16 @@ #define MAX_VIDEO_MEM 16 +enum csi_buffer_state { + CSI_BUF_NEEDS_INIT, + CSI_BUF_PREPARED, +}; + struct mx3_camera_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - enum v4l2_mbus_pixelcode code; + struct vb2_buffer vb; + enum csi_buffer_state state; + struct list_head queue; /* One descriptot per scatterlist (per frame) */ struct dma_async_tx_descriptor *txd; @@ -108,6 +114,9 @@ struct mx3_camera_dev { struct list_head capture; spinlock_t lock; /* Protects video buffer lists */ struct mx3_camera_buffer *active; + struct vb2_alloc_ctx *alloc_ctx; + enum v4l2_field field; + int sequence; /* IDMAC / dmaengine interface */ struct idmac_channel *idmac_channel[1]; /* We need one channel */ @@ -130,6 +139,11 @@ static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg) __raw_writel(value, mx3->base + reg); } +static struct mx3_camera_buffer *to_mx3_vb(struct vb2_buffer *vb) +{ + return container_of(vb, struct mx3_camera_buffer, vb); +} + /* Called from the IPU IDMAC ISR */ static void mx3_cam_dma_done(void *arg) { @@ -137,20 +151,20 @@ static void mx3_cam_dma_done(void *arg) struct dma_chan *chan = desc->txd.chan; struct idmac_channel *ichannel = to_idmac_chan(chan); struct mx3_camera_dev *mx3_cam = ichannel->client; - struct videobuf_buffer *vb; dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n", desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0); spin_lock(&mx3_cam->lock); if (mx3_cam->active) { - vb = &mx3_cam->active->vb; - - list_del_init(&vb->queue); - vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); - vb->field_count++; - wake_up(&vb->done); + struct vb2_buffer *vb = &mx3_cam->active->vb; + struct mx3_camera_buffer *buf = to_mx3_vb(vb); + + list_del_init(&buf->queue); + do_gettimeofday(&vb->v4l2_buf.timestamp); + vb->v4l2_buf.field = mx3_cam->field; + vb->v4l2_buf.sequence = mx3_cam->sequence++; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } if (list_empty(&mx3_cam->capture)) { @@ -165,50 +179,22 @@ static void mx3_cam_dma_done(void *arg) } mx3_cam->active = list_entry(mx3_cam->capture.next, - struct mx3_camera_buffer, vb.queue); - mx3_cam->active->vb.state = VIDEOBUF_ACTIVE; + struct mx3_camera_buffer, queue); spin_unlock(&mx3_cam->lock); } -static void free_buffer(struct videobuf_queue *vq, struct mx3_camera_buffer *buf) -{ - struct soc_camera_device *icd = vq->priv_data; - struct videobuf_buffer *vb = &buf->vb; - struct dma_async_tx_descriptor *txd = buf->txd; - struct idmac_channel *ichan; - - BUG_ON(in_interrupt()); - - dev_dbg(icd->dev.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_QUEUED or STATE_ACTIVE - */ - videobuf_waiton(vq, vb, 0, 0); - if (txd) { - ichan = to_idmac_chan(txd->chan); - async_tx_ack(txd); - } - videobuf_dma_contig_free(vq, vb); - buf->txd = NULL; - - vb->state = VIDEOBUF_NEEDS_INIT; -} - /* * Videobuf operations */ /* * Calculate the __buffer__ (not data) size and number of buffers. - * Called with .vb_lock held */ -static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static int mx3_videobuf_setup(struct vb2_queue *vq, + unsigned int *count, unsigned int *num_planes, + unsigned long 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->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, @@ -220,104 +206,66 @@ static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (!mx3_cam->idmac_channel[0]) return -EINVAL; - *size = bytes_per_line * icd->user_height; + *num_planes = 1; + + mx3_cam->sequence = 0; + sizes[0] = bytes_per_line * icd->user_height; + alloc_ctxs[0] = mx3_cam->alloc_ctx; if (!*count) *count = 32; - if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = MAX_VIDEO_MEM * 1024 * 1024 / *size; + if (sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024) + *count = MAX_VIDEO_MEM * 1024 * 1024 / sizes[0]; return 0; } -/* Called with .vb_lock held */ -static int mx3_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) +static int mx3_videobuf_prepare(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->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = - container_of(vb, struct mx3_camera_buffer, vb); + struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; + struct scatterlist *sg; + struct mx3_camera_buffer *buf; size_t new_size; - int ret; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); if (bytes_per_line < 0) return bytes_per_line; - new_size = bytes_per_line * icd->user_height; - - /* - * I think, in buf_prepare you only have to protect global data, - * the actual buffer is yours - */ + buf = to_mx3_vb(vb); + sg = &buf->sg; - 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; - if (vb->state != VIDEOBUF_NEEDS_INIT) - free_buffer(vq, buf); - } + new_size = bytes_per_line * icd->user_height; - if (vb->baddr && vb->bsize < new_size) { - /* User provided buffer, but it is too small */ - ret = -ENOMEM; - goto out; + if (vb2_plane_size(vb, 0) < new_size) { + dev_err(icd->dev.parent, "Buffer too small (%lu < %zu)\n", + vb2_plane_size(vb, 0), new_size); + return -ENOBUFS; } - if (vb->state == VIDEOBUF_NEEDS_INIT) { - struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - struct scatterlist *sg = &buf->sg; - - /* - * The total size of video-buffers that will be allocated / mapped. - * *size that we calculated in videobuf_setup gets assigned to - * vb->bsize, and now we use the same calculation to get vb->size. - */ - vb->size = new_size; - - /* This actually (allocates and) maps buffers */ - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - - /* - * We will have to configure the IDMAC channel. It has two slots - * for DMA buffers, we shall enter the first two buffers there, - * and then submit new buffers in DMA-ready interrupts - */ - sg_init_table(sg, 1); - sg_dma_address(sg) = videobuf_to_dma_contig(vb); - sg_dma_len(sg) = vb->size; + if (buf->state == CSI_BUF_NEEDS_INIT) { + sg_dma_address(sg) = vb2_dma_contig_plane_paddr(vb, 0); + sg_dma_len(sg) = new_size; buf->txd = ichan->dma_chan.device->device_prep_slave_sg( &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT); - if (!buf->txd) { - ret = -EIO; - goto fail; - } + if (!buf->txd) + return -EIO; buf->txd->callback_param = buf->txd; buf->txd->callback = mx3_cam_dma_done; - vb->state = VIDEOBUF_PREPARED; + buf->state = CSI_BUF_PREPARED; } - return 0; + vb2_set_plane_payload(vb, 0, new_size); -fail: - free_buffer(vq, buf); -out: - return ret; + return 0; } static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) @@ -333,25 +281,18 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) } } -/* - * Called with .vb_lock mutex held and - * under spinlock_irqsave(&mx3_cam->lock, ...) - */ -static void mx3_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void mx3_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->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = - container_of(vb, struct mx3_camera_buffer, vb); + struct mx3_camera_buffer *buf = to_mx3_vb(vb); struct dma_async_tx_descriptor *txd = buf->txd; struct idmac_channel *ichan = to_idmac_chan(txd->chan); struct idmac_video_param *video = &ichan->params.video; dma_cookie_t cookie; u32 fourcc = icd->current_fmt->host_fmt->fourcc; - - BUG_ON(!irqs_disabled()); + unsigned long flags; /* This is the configuration of one sg-element */ video->out_pixel_fmt = fourcc_to_ipu_pix(fourcc); @@ -383,17 +324,15 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, #ifdef DEBUG /* helps to see what DMA actually has written */ - memset((void *)vb->baddr, 0xaa, vb->bsize); + if (vb2_plane_vaddr(vb, 0)) + memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0)); #endif - list_add_tail(&vb->queue, &mx3_cam->capture); + spin_lock_irqsave(&mx3_cam->lock, flags); + list_add_tail(&buf->queue, &mx3_cam->capture); - if (!mx3_cam->active) { + if (!mx3_cam->active) mx3_cam->active = buf; - vb->state = VIDEOBUF_ACTIVE; - } else { - vb->state = VIDEOBUF_QUEUED; - } spin_unlock_irq(&mx3_cam->lock); @@ -401,67 +340,87 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, dev_dbg(icd->dev.parent, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg)); - spin_lock_irq(&mx3_cam->lock); - if (cookie >= 0) return; - /* Submit error */ - vb->state = VIDEOBUF_PREPARED; + spin_lock_irq(&mx3_cam->lock); - list_del_init(&vb->queue); + /* Submit error */ + list_del_init(&buf->queue); if (mx3_cam->active == buf) mx3_cam->active = NULL; + + spin_unlock_irqrestore(&mx3_cam->lock, flags); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); } -/* Called with .vb_lock held */ -static void mx3_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void mx3_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->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = - container_of(vb, struct mx3_camera_buffer, vb); + struct mx3_camera_buffer *buf = to_mx3_vb(vb); + struct dma_async_tx_descriptor *txd = buf->txd; unsigned long flags; dev_dbg(icd->dev.parent, - "Release%s DMA 0x%08x (state %d), queue %sempty\n", + "Release%s DMA 0x%08x, queue %sempty\n", mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg), - vb->state, list_empty(&vb->queue) ? "" : "not "); + list_empty(&buf->queue) ? "" : "not "); + spin_lock_irqsave(&mx3_cam->lock, flags); - if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && - !list_empty(&vb->queue)) { - vb->state = VIDEOBUF_ERROR; - list_del_init(&vb->queue); - if (mx3_cam->active == buf) - mx3_cam->active = NULL; + if (mx3_cam->active == buf) + mx3_cam->active = NULL; + + /* Doesn't hurt also if the list is empty */ + list_del_init(&buf->queue); + buf->state = CSI_BUF_NEEDS_INIT; + + if (txd) { + buf->txd = NULL; + if (mx3_cam->idmac_channel[0]) + async_tx_ack(txd); } + spin_unlock_irqrestore(&mx3_cam->lock, flags); - free_buffer(vq, buf); } -static struct videobuf_queue_ops mx3_videobuf_ops = { - .buf_setup = mx3_videobuf_setup, - .buf_prepare = mx3_videobuf_prepare, - .buf_queue = mx3_videobuf_queue, - .buf_release = mx3_videobuf_release, +static int mx3_videobuf_init(struct vb2_buffer *vb) +{ + struct mx3_camera_buffer *buf = to_mx3_vb(vb); + /* This is for locking debugging only */ + INIT_LIST_HEAD(&buf->queue); + sg_init_table(&buf->sg, 1); + + buf->state = CSI_BUF_NEEDS_INIT; + buf->txd = NULL; + + return 0; +} + +static struct vb2_ops mx3_videobuf_ops = { + .queue_setup = mx3_videobuf_setup, + .buf_prepare = mx3_videobuf_prepare, + .buf_queue = mx3_videobuf_queue, + .buf_cleanup = mx3_videobuf_release, + .buf_init = mx3_videobuf_init, + .wait_prepare = soc_camera_unlock, + .wait_finish = soc_camera_lock, }; -static void mx3_camera_init_videobuf(struct videobuf_queue *q, +static int mx3_camera_init_videobuf(struct vb2_queue *q, struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - - videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, icd->dev.parent, - &mx3_cam->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, - sizeof(struct mx3_camera_buffer), icd, - &icd->video_lock); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = icd; + q->ops = &mx3_videobuf_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct mx3_camera_buffer); + + return vb2_queue_init(q); } /* First part of ipu_csi_init_interface() */ @@ -556,18 +515,6 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd) icd->devnum); } -static bool channel_change_requested(struct soc_camera_device *icd, - struct v4l2_rect *rect) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - - /* Do buffers have to be re-allocated or channel re-configured? */ - return ichan && rect->width * rect->height > - icd->user_width * icd->user_height; -} - static int test_platform_param(struct mx3_camera_dev *mx3_cam, unsigned char buswidth, unsigned long *flags) { @@ -808,18 +755,6 @@ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) struct dma_chan_request rq = {.mx3_cam = mx3_cam, .id = IDMAC_IC_7}; - if (*ichan) { - struct videobuf_buffer *vb, *_vb; - dma_release_channel(&(*ichan)->dma_chan); - *ichan = NULL; - mx3_cam->active = NULL; - list_for_each_entry_safe(vb, _vb, &mx3_cam->capture, queue) { - list_del_init(&vb->queue); - vb->state = VIDEOBUF_ERROR; - wake_up(&vb->done); - } - } - dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_PRIVATE, mask); @@ -879,19 +814,8 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, return ret; } - if (mf.width != icd->user_width || mf.height != icd->user_height) { - /* - * We now know pixel formats and can decide upon DMA-channel(s) - * So far only direct camera-to-memory is supported - */ - if (channel_change_requested(icd, rect)) { - ret = acquire_dma_channel(mx3_cam); - if (ret < 0) - return ret; - } - + if (mf.width != icd->user_width || mf.height != icd->user_height) configure_geometry(mx3_cam, mf.width, mf.height, mf.code); - } dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n", mf.width, mf.height); @@ -923,10 +847,6 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, stride_align(&pix->width); dev_dbg(icd->dev.parent, "Set format %dx%d\n", pix->width, pix->height); - ret = acquire_dma_channel(mx3_cam); - if (ret < 0) - return ret; - /* * Might have to perform a complete interface initialisation like in * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider @@ -948,9 +868,16 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, if (mf.code != xlate->code) return -EINVAL; + if (!mx3_cam->idmac_channel[0]) { + ret = acquire_dma_channel(mx3_cam); + if (ret < 0) + return ret; + } + pix->width = mf.width; pix->height = mf.height; pix->field = mf.field; + mx3_cam->field = mf.field; pix->colorspace = mf.colorspace; icd->current_fmt = xlate; @@ -1033,7 +960,7 @@ static unsigned int mx3_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 int mx3_camera_querycap(struct soc_camera_host *ici, @@ -1207,7 +1134,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = { .set_fmt = mx3_camera_set_fmt, .try_fmt = mx3_camera_try_fmt, .get_formats = mx3_camera_get_formats, - .init_videobuf = mx3_camera_init_videobuf, + .init_videobuf2 = mx3_camera_init_videobuf, .reqbufs = mx3_camera_reqbufs, .poll = mx3_camera_poll, .querycap = mx3_camera_querycap, @@ -1283,6 +1210,12 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev) soc_host->v4l2_dev.dev = &pdev->dev; soc_host->nr = pdev->id; + mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(mx3_cam->alloc_ctx)) { + err = PTR_ERR(mx3_cam->alloc_ctx); + goto eallocctx; + } + err = soc_camera_host_register(soc_host); if (err) goto ecamhostreg; @@ -1293,6 +1226,8 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev) return 0; ecamhostreg: + vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); +eallocctx: iounmap(base); eioremap: clk_put(mx3_cam->clk); @@ -1322,6 +1257,8 @@ static int __devexit mx3_camera_remove(struct platform_device *pdev) if (WARN_ON(mx3_cam->idmac_channel[0])) dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan); + vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); + vfree(mx3_cam); dmaengine_put(); -- cgit v0.10.2 From 787d0f9d5ca3e98f23af94037094a9a3fafec665 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 16 Feb 2011 18:42:24 -0300 Subject: [media] V4l: sh_mobile_ceu_camera: fix cropping offset calculation Use the correct scales to calculate cropping offsets. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 325f50d..61f3701 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -1348,7 +1348,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, struct device *dev = icd->dev.parent; struct v4l2_mbus_framefmt mf; unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v, - out_width, out_height, scale_h, scale_v; + out_width, out_height; int interm_width, interm_height; u32 capsr, cflcr; int ret; @@ -1406,10 +1406,6 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, scale_ceu_h = calc_scale(interm_width, &out_width); scale_ceu_v = calc_scale(interm_height, &out_height); - /* Calculate camera scales */ - scale_h = calc_generic_scale(cam_rect->width, out_width); - scale_v = calc_generic_scale(cam_rect->height, out_height); - dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); /* Apply CEU scales. */ @@ -1421,8 +1417,8 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, icd->user_width = out_width; icd->user_height = out_height; - cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_h) & ~1; - cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_v) & ~1; + cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1; + cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1; /* 6. Use CEU cropping to crop to the new window. */ sh_mobile_ceu_set_rect(icd); @@ -1433,7 +1429,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top); - /* Restore capture */ + /* Restore capture. The CE bit can be cleared by the hardware */ if (pcdev->active) capsr |= 1; capture_restore(pcdev, capsr); -- cgit v0.10.2 From 9bd060e492a2725c55404988ceed473a090bd04d Mon Sep 17 00:00:00 2001 From: Andrew Chew Date: Thu, 17 Feb 2011 19:14:33 -0300 Subject: [media] V4L: Initial submit of OV9740 driver This soc_camera driver is for Omnivision's OV9740 sensor. This initial submission provides support for YUV422 output at 1280x720 (720p), which is the sensor's native resolution. 640x480 (VGA) is also supported, with cropping and scaling performed by the sensor's ISP. This driver is heavily based off of the existing OV9640 driver. Signed-off-by: Andrew Chew Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index cf81bd5..e2f5a69 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -838,6 +838,12 @@ config SOC_CAMERA_OV9640 help This is a ov9640 camera driver +config SOC_CAMERA_OV9740 + tristate "ov9740 camera support" + depends on SOC_CAMERA && I2C + help + This is a ov9740 camera driver + config MX1_VIDEO bool diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 251b7ca..ac54652 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o +obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c new file mode 100644 index 0000000..4d4ee4f --- /dev/null +++ b/drivers/media/video/ov9740.c @@ -0,0 +1,1009 @@ +/* + * OmniVision OV9740 Camera Driver + * + * Copyright (C) 2011 NVIDIA Corporation + * + * Based on ov9640 camera driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev) + +/* General Status Registers */ +#define OV9740_MODEL_ID_HI 0x0000 +#define OV9740_MODEL_ID_LO 0x0001 +#define OV9740_REVISION_NUMBER 0x0002 +#define OV9740_MANUFACTURER_ID 0x0003 +#define OV9740_SMIA_VERSION 0x0004 + +/* General Setup Registers */ +#define OV9740_MODE_SELECT 0x0100 +#define OV9740_IMAGE_ORT 0x0101 +#define OV9740_SOFTWARE_RESET 0x0103 +#define OV9740_GRP_PARAM_HOLD 0x0104 +#define OV9740_MSK_CORRUP_FM 0x0105 + +/* Timing Setting */ +#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */ +#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */ +#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */ +#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */ +#define OV9740_X_ADDR_START_HI 0x0344 +#define OV9740_X_ADDR_START_LO 0x0345 +#define OV9740_Y_ADDR_START_HI 0x0346 +#define OV9740_Y_ADDR_START_LO 0x0347 +#define OV9740_X_ADDR_END_HI 0x0348 +#define OV9740_X_ADDR_END_LO 0x0349 +#define OV9740_Y_ADDR_END_HI 0x034A +#define OV9740_Y_ADDR_END_LO 0x034B +#define OV9740_X_OUTPUT_SIZE_HI 0x034C +#define OV9740_X_OUTPUT_SIZE_LO 0x034D +#define OV9740_Y_OUTPUT_SIZE_HI 0x034E +#define OV9740_Y_OUTPUT_SIZE_LO 0x034F + +/* IO Control Registers */ +#define OV9740_IO_CREL00 0x3002 +#define OV9740_IO_CREL01 0x3004 +#define OV9740_IO_CREL02 0x3005 +#define OV9740_IO_OUTPUT_SEL01 0x3026 +#define OV9740_IO_OUTPUT_SEL02 0x3027 + +/* AWB Registers */ +#define OV9740_AWB_MANUAL_CTRL 0x3406 + +/* Analog Control Registers */ +#define OV9740_ANALOG_CTRL01 0x3601 +#define OV9740_ANALOG_CTRL02 0x3602 +#define OV9740_ANALOG_CTRL03 0x3603 +#define OV9740_ANALOG_CTRL04 0x3604 +#define OV9740_ANALOG_CTRL10 0x3610 +#define OV9740_ANALOG_CTRL12 0x3612 +#define OV9740_ANALOG_CTRL20 0x3620 +#define OV9740_ANALOG_CTRL21 0x3621 +#define OV9740_ANALOG_CTRL22 0x3622 +#define OV9740_ANALOG_CTRL30 0x3630 +#define OV9740_ANALOG_CTRL31 0x3631 +#define OV9740_ANALOG_CTRL32 0x3632 +#define OV9740_ANALOG_CTRL33 0x3633 + +/* Sensor Control */ +#define OV9740_SENSOR_CTRL03 0x3703 +#define OV9740_SENSOR_CTRL04 0x3704 +#define OV9740_SENSOR_CTRL05 0x3705 +#define OV9740_SENSOR_CTRL07 0x3707 + +/* Timing Control */ +#define OV9740_TIMING_CTRL17 0x3817 +#define OV9740_TIMING_CTRL19 0x3819 +#define OV9740_TIMING_CTRL33 0x3833 +#define OV9740_TIMING_CTRL35 0x3835 + +/* Banding Filter */ +#define OV9740_AEC_MAXEXPO_60_H 0x3A02 +#define OV9740_AEC_MAXEXPO_60_L 0x3A03 +#define OV9740_AEC_B50_STEP_HI 0x3A08 +#define OV9740_AEC_B50_STEP_LO 0x3A09 +#define OV9740_AEC_B60_STEP_HI 0x3A0A +#define OV9740_AEC_B60_STEP_LO 0x3A0B +#define OV9740_AEC_CTRL0D 0x3A0D +#define OV9740_AEC_CTRL0E 0x3A0E +#define OV9740_AEC_MAXEXPO_50_H 0x3A14 +#define OV9740_AEC_MAXEXPO_50_L 0x3A15 + +/* AEC/AGC Control */ +#define OV9740_AEC_ENABLE 0x3503 +#define OV9740_GAIN_CEILING_01 0x3A18 +#define OV9740_GAIN_CEILING_02 0x3A19 +#define OV9740_AEC_HI_THRESHOLD 0x3A11 +#define OV9740_AEC_3A1A 0x3A1A +#define OV9740_AEC_CTRL1B_WPT2 0x3A1B +#define OV9740_AEC_CTRL0F_WPT 0x3A0F +#define OV9740_AEC_CTRL10_BPT 0x3A10 +#define OV9740_AEC_CTRL1E_BPT2 0x3A1E +#define OV9740_AEC_LO_THRESHOLD 0x3A1F + +/* BLC Control */ +#define OV9740_BLC_AUTO_ENABLE 0x4002 +#define OV9740_BLC_MODE 0x4005 + +/* VFIFO */ +#define OV9740_VFIFO_READ_START_HI 0x4608 +#define OV9740_VFIFO_READ_START_LO 0x4609 + +/* DVP Control */ +#define OV9740_DVP_VSYNC_CTRL02 0x4702 +#define OV9740_DVP_VSYNC_MODE 0x4704 +#define OV9740_DVP_VSYNC_CTRL06 0x4706 + +/* PLL Setting */ +#define OV9740_PLL_MODE_CTRL01 0x3104 +#define OV9740_PRE_PLL_CLK_DIV 0x0305 +#define OV9740_PLL_MULTIPLIER 0x0307 +#define OV9740_VT_SYS_CLK_DIV 0x0303 +#define OV9740_VT_PIX_CLK_DIV 0x0301 +#define OV9740_PLL_CTRL3010 0x3010 +#define OV9740_VFIFO_CTRL00 0x460E + +/* ISP Control */ +#define OV9740_ISP_CTRL00 0x5000 +#define OV9740_ISP_CTRL01 0x5001 +#define OV9740_ISP_CTRL03 0x5003 +#define OV9740_ISP_CTRL05 0x5005 +#define OV9740_ISP_CTRL12 0x5012 +#define OV9740_ISP_CTRL19 0x5019 +#define OV9740_ISP_CTRL1A 0x501A +#define OV9740_ISP_CTRL1E 0x501E +#define OV9740_ISP_CTRL1F 0x501F +#define OV9740_ISP_CTRL20 0x5020 +#define OV9740_ISP_CTRL21 0x5021 + +/* AWB */ +#define OV9740_AWB_CTRL00 0x5180 +#define OV9740_AWB_CTRL01 0x5181 +#define OV9740_AWB_CTRL02 0x5182 +#define OV9740_AWB_CTRL03 0x5183 +#define OV9740_AWB_ADV_CTRL01 0x5184 +#define OV9740_AWB_ADV_CTRL02 0x5185 +#define OV9740_AWB_ADV_CTRL03 0x5186 +#define OV9740_AWB_ADV_CTRL04 0x5187 +#define OV9740_AWB_ADV_CTRL05 0x5188 +#define OV9740_AWB_ADV_CTRL06 0x5189 +#define OV9740_AWB_ADV_CTRL07 0x518A +#define OV9740_AWB_ADV_CTRL08 0x518B +#define OV9740_AWB_ADV_CTRL09 0x518C +#define OV9740_AWB_ADV_CTRL10 0x518D +#define OV9740_AWB_ADV_CTRL11 0x518E +#define OV9740_AWB_CTRL0F 0x518F +#define OV9740_AWB_CTRL10 0x5190 +#define OV9740_AWB_CTRL11 0x5191 +#define OV9740_AWB_CTRL12 0x5192 +#define OV9740_AWB_CTRL13 0x5193 +#define OV9740_AWB_CTRL14 0x5194 + +/* MIPI Control */ +#define OV9740_MIPI_CTRL00 0x4800 +#define OV9740_MIPI_3837 0x3837 +#define OV9740_MIPI_CTRL01 0x4801 +#define OV9740_MIPI_CTRL03 0x4803 +#define OV9740_MIPI_CTRL05 0x4805 +#define OV9740_VFIFO_RD_CTRL 0x4601 +#define OV9740_MIPI_CTRL_3012 0x3012 +#define OV9740_SC_CMMM_MIPI_CTR 0x3014 + +/* supported resolutions */ +enum { + OV9740_VGA, + OV9740_720P, +}; + +struct ov9740_resolution { + unsigned int width; + unsigned int height; +}; + +static struct ov9740_resolution ov9740_resolutions[] = { + [OV9740_VGA] = { + .width = 640, + .height = 480, + }, + [OV9740_720P] = { + .width = 1280, + .height = 720, + }, +}; + +/* Misc. structures */ +struct ov9740_reg { + u16 reg; + u8 val; +}; + +struct ov9740_priv { + struct v4l2_subdev subdev; + + int ident; + u16 model; + u8 revision; + u8 manid; + u8 smiaver; + + bool flag_vflip; + bool flag_hflip; +}; + +static const struct ov9740_reg ov9740_defaults[] = { + /* Banding Filter */ + { OV9740_AEC_B50_STEP_HI, 0x00 }, + { OV9740_AEC_B50_STEP_LO, 0xe8 }, + { OV9740_AEC_CTRL0E, 0x03 }, + { OV9740_AEC_MAXEXPO_50_H, 0x15 }, + { OV9740_AEC_MAXEXPO_50_L, 0xc6 }, + { OV9740_AEC_B60_STEP_HI, 0x00 }, + { OV9740_AEC_B60_STEP_LO, 0xc0 }, + { OV9740_AEC_CTRL0D, 0x04 }, + { OV9740_AEC_MAXEXPO_60_H, 0x18 }, + { OV9740_AEC_MAXEXPO_60_L, 0x20 }, + + /* LC */ + { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 }, + { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc }, + + /* Un-documented OV9740 registers */ + { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 }, + { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c }, + { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580A, 0x0e }, { 0x580B, 0x16 }, + { 0x580C, 0x06 }, { 0x580D, 0x02 }, { 0x580E, 0x00 }, { 0x580F, 0x00 }, + { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 }, + { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 }, + { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581A, 0x07 }, { 0x581B, 0x08 }, + { 0x581C, 0x0b }, { 0x581D, 0x14 }, { 0x581E, 0x28 }, { 0x581F, 0x23 }, + { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a }, + { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f }, + { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582A, 0x8f }, { 0x582B, 0x9e }, + { 0x582C, 0x8f }, { 0x582D, 0x9f }, { 0x582E, 0x4f }, { 0x582F, 0x87 }, + { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f }, + { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf }, + { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583A, 0x9f }, { 0x583B, 0x7f }, + { 0x583C, 0x5f }, + + /* Y Gamma */ + { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e }, + { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 }, + { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548A, 0xa4 }, { 0x548B, 0xb1 }, + { 0x548C, 0xc6 }, { 0x548D, 0xd8 }, { 0x548E, 0xe9 }, + + /* UV Gamma */ + { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 }, + { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 }, + { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549A, 0x02 }, { 0x549B, 0xeb }, + { 0x549C, 0x02 }, { 0x549D, 0xa0 }, { 0x549E, 0x02 }, { 0x549F, 0x67 }, + { 0x54A0, 0x02 }, { 0x54A1, 0x3b }, { 0x54A2, 0x02 }, { 0x54A3, 0x18 }, + { 0x54A4, 0x01 }, { 0x54A5, 0xe7 }, { 0x54A6, 0x01 }, { 0x54A7, 0xc3 }, + { 0x54A8, 0x01 }, { 0x54A9, 0x94 }, { 0x54AA, 0x01 }, { 0x54AB, 0x72 }, + { 0x54AC, 0x01 }, { 0x54AD, 0x57 }, + + /* AWB */ + { OV9740_AWB_CTRL00, 0xf0 }, + { OV9740_AWB_CTRL01, 0x00 }, + { OV9740_AWB_CTRL02, 0x41 }, + { OV9740_AWB_CTRL03, 0x42 }, + { OV9740_AWB_ADV_CTRL01, 0x8a }, + { OV9740_AWB_ADV_CTRL02, 0x61 }, + { OV9740_AWB_ADV_CTRL03, 0xce }, + { OV9740_AWB_ADV_CTRL04, 0xa8 }, + { OV9740_AWB_ADV_CTRL05, 0x17 }, + { OV9740_AWB_ADV_CTRL06, 0x1f }, + { OV9740_AWB_ADV_CTRL07, 0x27 }, + { OV9740_AWB_ADV_CTRL08, 0x41 }, + { OV9740_AWB_ADV_CTRL09, 0x34 }, + { OV9740_AWB_ADV_CTRL10, 0xf0 }, + { OV9740_AWB_ADV_CTRL11, 0x10 }, + { OV9740_AWB_CTRL0F, 0xff }, + { OV9740_AWB_CTRL10, 0x00 }, + { OV9740_AWB_CTRL11, 0xff }, + { OV9740_AWB_CTRL12, 0x00 }, + { OV9740_AWB_CTRL13, 0xff }, + { OV9740_AWB_CTRL14, 0x00 }, + + /* CIP */ + { 0x530D, 0x12 }, + + /* CMX */ + { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 }, + { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 }, + { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538A, 0x00 }, { 0x538B, 0x20 }, + { 0x538C, 0x00 }, { 0x538D, 0x00 }, { 0x538E, 0x00 }, { 0x538F, 0x16 }, + { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 }, + { 0x5394, 0x18 }, + + /* 50/60 Detection */ + { 0x3C0A, 0x9c }, { 0x3C0B, 0x3f }, + + /* Output Select */ + { OV9740_IO_OUTPUT_SEL01, 0x00 }, + { OV9740_IO_OUTPUT_SEL02, 0x00 }, + { OV9740_IO_CREL00, 0x00 }, + { OV9740_IO_CREL01, 0x00 }, + { OV9740_IO_CREL02, 0x00 }, + + /* AWB Control */ + { OV9740_AWB_MANUAL_CTRL, 0x00 }, + + /* Analog Control */ + { OV9740_ANALOG_CTRL03, 0xaa }, + { OV9740_ANALOG_CTRL32, 0x2f }, + { OV9740_ANALOG_CTRL20, 0x66 }, + { OV9740_ANALOG_CTRL21, 0xc0 }, + { OV9740_ANALOG_CTRL31, 0x52 }, + { OV9740_ANALOG_CTRL33, 0x50 }, + { OV9740_ANALOG_CTRL30, 0xca }, + { OV9740_ANALOG_CTRL04, 0x0c }, + { OV9740_ANALOG_CTRL01, 0x40 }, + { OV9740_ANALOG_CTRL02, 0x16 }, + { OV9740_ANALOG_CTRL10, 0xa1 }, + { OV9740_ANALOG_CTRL12, 0x24 }, + { OV9740_ANALOG_CTRL22, 0x9f }, + + /* Sensor Control */ + { OV9740_SENSOR_CTRL03, 0x42 }, + { OV9740_SENSOR_CTRL04, 0x10 }, + { OV9740_SENSOR_CTRL05, 0x45 }, + { OV9740_SENSOR_CTRL07, 0x14 }, + + /* Timing Control */ + { OV9740_TIMING_CTRL33, 0x04 }, + { OV9740_TIMING_CTRL35, 0x02 }, + { OV9740_TIMING_CTRL19, 0x6e }, + { OV9740_TIMING_CTRL17, 0x94 }, + + /* AEC/AGC Control */ + { OV9740_AEC_ENABLE, 0x10 }, + { OV9740_GAIN_CEILING_01, 0x00 }, + { OV9740_GAIN_CEILING_02, 0x7f }, + { OV9740_AEC_HI_THRESHOLD, 0xa0 }, + { OV9740_AEC_3A1A, 0x05 }, + { OV9740_AEC_CTRL1B_WPT2, 0x50 }, + { OV9740_AEC_CTRL0F_WPT, 0x50 }, + { OV9740_AEC_CTRL10_BPT, 0x4c }, + { OV9740_AEC_CTRL1E_BPT2, 0x4c }, + { OV9740_AEC_LO_THRESHOLD, 0x26 }, + + /* BLC Control */ + { OV9740_BLC_AUTO_ENABLE, 0x45 }, + { OV9740_BLC_MODE, 0x18 }, + + /* DVP Control */ + { OV9740_DVP_VSYNC_CTRL02, 0x04 }, + { OV9740_DVP_VSYNC_MODE, 0x00 }, + { OV9740_DVP_VSYNC_CTRL06, 0x08 }, + + /* PLL Setting */ + { OV9740_PLL_MODE_CTRL01, 0x20 }, + { OV9740_PRE_PLL_CLK_DIV, 0x03 }, + { OV9740_PLL_MULTIPLIER, 0x4c }, + { OV9740_VT_SYS_CLK_DIV, 0x01 }, + { OV9740_VT_PIX_CLK_DIV, 0x08 }, + { OV9740_PLL_CTRL3010, 0x01 }, + { OV9740_VFIFO_CTRL00, 0x82 }, + + /* Timing Setting */ + /* VTS */ + { OV9740_FRM_LENGTH_LN_HI, 0x03 }, + { OV9740_FRM_LENGTH_LN_LO, 0x07 }, + /* HTS */ + { OV9740_LN_LENGTH_PCK_HI, 0x06 }, + { OV9740_LN_LENGTH_PCK_LO, 0x62 }, + + /* MIPI Control */ + { OV9740_MIPI_CTRL00, 0x44 }, + { OV9740_MIPI_3837, 0x01 }, + { OV9740_MIPI_CTRL01, 0x0f }, + { OV9740_MIPI_CTRL03, 0x05 }, + { OV9740_MIPI_CTRL05, 0x10 }, + { OV9740_VFIFO_RD_CTRL, 0x16 }, + { OV9740_MIPI_CTRL_3012, 0x70 }, + { OV9740_SC_CMMM_MIPI_CTR, 0x01 }, +}; + +static const struct ov9740_reg ov9740_regs_vga[] = { + { OV9740_X_ADDR_START_HI, 0x00 }, + { OV9740_X_ADDR_START_LO, 0xa0 }, + { OV9740_Y_ADDR_START_HI, 0x00 }, + { OV9740_Y_ADDR_START_LO, 0x00 }, + { OV9740_X_ADDR_END_HI, 0x04 }, + { OV9740_X_ADDR_END_LO, 0x63 }, + { OV9740_Y_ADDR_END_HI, 0x02 }, + { OV9740_Y_ADDR_END_LO, 0xd3 }, + { OV9740_X_OUTPUT_SIZE_HI, 0x02 }, + { OV9740_X_OUTPUT_SIZE_LO, 0x80 }, + { OV9740_Y_OUTPUT_SIZE_HI, 0x01 }, + { OV9740_Y_OUTPUT_SIZE_LO, 0xe0 }, + { OV9740_ISP_CTRL1E, 0x03 }, + { OV9740_ISP_CTRL1F, 0xc0 }, + { OV9740_ISP_CTRL20, 0x02 }, + { OV9740_ISP_CTRL21, 0xd0 }, + { OV9740_VFIFO_READ_START_HI, 0x01 }, + { OV9740_VFIFO_READ_START_LO, 0x40 }, + { OV9740_ISP_CTRL00, 0xff }, + { OV9740_ISP_CTRL01, 0xff }, + { OV9740_ISP_CTRL03, 0xff }, +}; + +static const struct ov9740_reg ov9740_regs_720p[] = { + { OV9740_X_ADDR_START_HI, 0x00 }, + { OV9740_X_ADDR_START_LO, 0x00 }, + { OV9740_Y_ADDR_START_HI, 0x00 }, + { OV9740_Y_ADDR_START_LO, 0x00 }, + { OV9740_X_ADDR_END_HI, 0x05 }, + { OV9740_X_ADDR_END_LO, 0x03 }, + { OV9740_Y_ADDR_END_HI, 0x02 }, + { OV9740_Y_ADDR_END_LO, 0xd3 }, + { OV9740_X_OUTPUT_SIZE_HI, 0x05 }, + { OV9740_X_OUTPUT_SIZE_LO, 0x00 }, + { OV9740_Y_OUTPUT_SIZE_HI, 0x02 }, + { OV9740_Y_OUTPUT_SIZE_LO, 0xd0 }, + { OV9740_ISP_CTRL1E, 0x05 }, + { OV9740_ISP_CTRL1F, 0x00 }, + { OV9740_ISP_CTRL20, 0x02 }, + { OV9740_ISP_CTRL21, 0xd0 }, + { OV9740_VFIFO_READ_START_HI, 0x02 }, + { OV9740_VFIFO_READ_START_LO, 0x30 }, + { OV9740_ISP_CTRL00, 0xff }, + { OV9740_ISP_CTRL01, 0xef }, + { OV9740_ISP_CTRL03, 0xff }, +}; + +static enum v4l2_mbus_pixelcode ov9740_codes[] = { + V4L2_MBUS_FMT_YUYV8_2X8, +}; + +static const struct v4l2_queryctrl ov9740_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + +/* read a register */ +static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val) +{ + int ret; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = (u8 *)®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = val, + }, + }; + + reg = swab16(reg); + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) { + dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg); + return ret; + } + + return 0; +} + +/* write a register */ +static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val) +{ + struct i2c_msg msg; + struct { + u16 reg; + u8 val; + } __packed buf; + int ret; + + reg = swab16(reg); + + buf.reg = reg; + buf.val = val; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = (u8 *)&buf; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg); + return ret; + } + + return 0; +} + + +/* Read a register, alter its bits, write it back */ +static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset) +{ + u8 val; + int ret; + + ret = ov9740_reg_read(client, reg, &val); + if (ret < 0) { + dev_err(&client->dev, + "[Read]-Modify-Write of register %02x failed!\n", reg); + return ret; + } + + val |= set; + val &= ~unset; + + ret = ov9740_reg_write(client, reg, val); + if (ret < 0) { + dev_err(&client->dev, + "Read-Modify-[Write] of register %02x failed!\n", reg); + return ret; + } + + return 0; +} + +static int ov9740_reg_write_array(struct i2c_client *client, + const struct ov9740_reg *regarray, + int regarraylen) +{ + int i; + int ret; + + for (i = 0; i < regarraylen; i++) { + ret = ov9740_reg_write(client, + regarray[i].reg, regarray[i].val); + if (ret < 0) + return ret; + } + + return 0; +} + +/* Start/Stop streaming from the device */ +static int ov9740_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov9740_priv *priv = to_ov9740(sd); + int ret; + + /* Program orientation register. */ + if (priv->flag_vflip) + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0); + else + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2); + if (ret < 0) + return ret; + + if (priv->flag_hflip) + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0); + else + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1); + if (ret < 0) + return ret; + + if (enable) { + dev_dbg(&client->dev, "Enabling Streaming\n"); + /* Start Streaming */ + ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01); + + } else { + dev_dbg(&client->dev, "Disabling Streaming\n"); + /* Software Reset */ + ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01); + if (!ret) + /* Setting Streaming to Standby */ + ret = ov9740_reg_write(client, OV9740_MODE_SELECT, + 0x00); + } + + return ret; +} + +/* Alter bus settings on camera side */ +static int ov9740_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +/* Request bus settings on camera side */ +static unsigned long ov9740_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +/* Get status of additional camera capabilities */ +static int ov9740_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ov9740_priv *priv = to_ov9740(sd); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + ctrl->value = priv->flag_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = priv->flag_hflip; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Set status of additional camera capabilities */ +static int ov9740_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ov9740_priv *priv = to_ov9740(sd); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + priv->flag_vflip = ctrl->value; + break; + case V4L2_CID_HFLIP: + priv->flag_hflip = ctrl->value; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Get chip identification */ +static int ov9740_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct ov9740_priv *priv = to_ov9740(sd); + + id->ident = priv->ident; + id->revision = priv->revision; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov9740_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val; + + if (reg->reg & ~0xffff) + return -EINVAL; + + reg->size = 2; + + ret = ov9740_reg_read(client, reg->reg, &val); + if (ret) + return ret; + + reg->val = (__u64)val; + + return ret; +} + +static int ov9740_set_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg & ~0xffff || reg->val & ~0xff) + return -EINVAL; + + return ov9740_reg_write(client, reg->reg, reg->val); +} +#endif + +/* select nearest higher resolution for capture */ +static void ov9740_res_roundup(u32 *width, u32 *height) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ov9740_resolutions); i++) + if ((ov9740_resolutions[i].width >= *width) && + (ov9740_resolutions[i].height >= *height)) { + *width = ov9740_resolutions[i].width; + *height = ov9740_resolutions[i].height; + return; + } + + *width = ov9740_resolutions[OV9740_720P].width; + *height = ov9740_resolutions[OV9740_720P].height; +} + +/* Setup registers according to resolution and color encoding */ +static int ov9740_set_res(struct i2c_client *client, u32 width) +{ + int ret; + + /* select register configuration for given resolution */ + if (width == ov9740_resolutions[OV9740_VGA].width) { + dev_dbg(&client->dev, "Setting image size to 640x480\n"); + ret = ov9740_reg_write_array(client, ov9740_regs_vga, + ARRAY_SIZE(ov9740_regs_vga)); + } else if (width == ov9740_resolutions[OV9740_720P].width) { + dev_dbg(&client->dev, "Setting image size to 1280x720\n"); + ret = ov9740_reg_write_array(client, ov9740_regs_720p, + ARRAY_SIZE(ov9740_regs_720p)); + } else { + dev_err(&client->dev, "Failed to select resolution!\n"); + return -EINVAL; + } + + return ret; +} + +/* set the format we will capture in */ +static int ov9740_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + enum v4l2_colorspace cspace; + enum v4l2_mbus_pixelcode code = mf->code; + int ret; + + ov9740_res_roundup(&mf->width, &mf->height); + + switch (code) { + case V4L2_MBUS_FMT_YUYV8_2X8: + cspace = V4L2_COLORSPACE_SRGB; + break; + default: + return -EINVAL; + } + + ret = ov9740_reg_write_array(client, ov9740_defaults, + ARRAY_SIZE(ov9740_defaults)); + if (ret < 0) + return ret; + + ret = ov9740_set_res(client, mf->width); + if (ret < 0) + return ret; + + mf->code = code; + mf->colorspace = cspace; + + return ret; +} + +static int ov9740_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + ov9740_res_roundup(&mf->width, &mf->height); + + mf->field = V4L2_FIELD_NONE; + mf->code = V4L2_MBUS_FMT_YUYV8_2X8; + mf->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int ov9740_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(ov9740_codes)) + return -EINVAL; + + *code = ov9740_codes[index]; + + return 0; +} + +static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = ov9740_resolutions[OV9740_720P].width; + a->bounds.height = ov9740_resolutions[OV9740_720P].height; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + a->c.left = 0; + a->c.top = 0; + a->c.width = ov9740_resolutions[OV9740_720P].width; + a->c.height = ov9740_resolutions[OV9740_720P].height; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int ov9740_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9740_priv *priv = to_ov9740(sd); + u8 modelhi, modello; + int ret; + + /* + * We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) { + dev_err(&client->dev, "Parent missing or invalid!\n"); + ret = -ENODEV; + goto err; + } + + /* + * check and show product ID and manufacturer ID + */ + ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi); + if (ret < 0) + goto err; + + ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello); + if (ret < 0) + goto err; + + priv->model = (modelhi << 8) | modello; + + ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision); + if (ret < 0) + goto err; + + ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid); + if (ret < 0) + goto err; + + ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver); + if (ret < 0) + goto err; + + if (priv->model != 0x9740) { + ret = -ENODEV; + goto err; + } + + priv->ident = V4L2_IDENT_OV9740; + + dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, " + "Manufacturer 0x%02x, SMIA Version 0x%02x\n", + priv->model, priv->revision, priv->manid, priv->smiaver); + +err: + return ret; +} + +static struct soc_camera_ops ov9740_ops = { + .set_bus_param = ov9740_set_bus_param, + .query_bus_param = ov9740_query_bus_param, + .controls = ov9740_controls, + .num_controls = ARRAY_SIZE(ov9740_controls), +}; + +static struct v4l2_subdev_core_ops ov9740_core_ops = { + .g_ctrl = ov9740_g_ctrl, + .s_ctrl = ov9740_s_ctrl, + .g_chip_ident = ov9740_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov9740_get_register, + .s_register = ov9740_set_register, +#endif + +}; + +static struct v4l2_subdev_video_ops ov9740_video_ops = { + .s_stream = ov9740_s_stream, + .s_mbus_fmt = ov9740_s_fmt, + .try_mbus_fmt = ov9740_try_fmt, + .enum_mbus_fmt = ov9740_enum_fmt, + .cropcap = ov9740_cropcap, + .g_crop = ov9740_g_crop, +}; + +static struct v4l2_subdev_ops ov9740_subdev_ops = { + .core = &ov9740_core_ops, + .video = &ov9740_video_ops, +}; + +/* + * i2c_driver function + */ +static int ov9740_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov9740_priv *priv; + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "Missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "Missing platform_data for driver\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(struct ov9740_priv), GFP_KERNEL); + if (!priv) { + dev_err(&client->dev, "Failed to allocate private data!\n"); + return -ENOMEM; + } + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops); + + icd->ops = &ov9740_ops; + + ret = ov9740_video_probe(icd, client); + if (ret < 0) { + icd->ops = NULL; + kfree(priv); + } + + return ret; +} + +static int ov9740_remove(struct i2c_client *client) +{ + struct ov9740_priv *priv = i2c_get_clientdata(client); + + kfree(priv); + + return 0; +} + +static const struct i2c_device_id ov9740_id[] = { + { "ov9740", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov9740_id); + +static struct i2c_driver ov9740_i2c_driver = { + .driver = { + .name = "ov9740", + }, + .probe = ov9740_probe, + .remove = ov9740_remove, + .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_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); +MODULE_AUTHOR("Andrew Chew "); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index ff4a52c..b3edb67 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -75,6 +75,7 @@ enum { V4L2_IDENT_OV9640 = 257, V4L2_IDENT_OV6650 = 258, V4L2_IDENT_OV2640 = 259, + V4L2_IDENT_OV9740 = 260, /* module saa7146: reserved range 300-309 */ V4L2_IDENT_SAA7146 = 300, -- cgit v0.10.2 From f72cfd859b0e436d9f680790cb665ad1390187ae Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 25 Jan 2011 17:38:52 -0300 Subject: [media] video/cx231xx: Fix sparse warning: Using plain integer as NULL pointer This patch fixes the warning "Using plain integer as NULL pointer", generated by sparse, by replacing the offending 0s with NULL. Signed-off-by: Peter Huewe Reviewed-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c index fc9526a..f8f0e59 100644 --- a/drivers/media/video/cx231xx/cx231xx-417.c +++ b/drivers/media/video/cx231xx/cx231xx-417.c @@ -942,13 +942,13 @@ static int cx231xx_load_firmware(struct cx231xx *dev) p_current_fw = vmalloc(1884180 * 4); p_fw = p_current_fw; - if (p_current_fw == 0) { + if (p_current_fw == NULL) { dprintk(2, "FAIL!!!\n"); return -1; } p_buffer = vmalloc(4096); - if (p_buffer == 0) { + if (p_buffer == NULL) { dprintk(2, "FAIL!!!\n"); return -1; } diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index 6540b8d..f49230d 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -360,19 +360,19 @@ struct cx231xx_board cx231xx_boards[] = { .type = CX231XX_VMUX_TELEVISION, .vmux = CX231XX_VIN_3_1, .amux = CX231XX_AMUX_VIDEO, - .gpio = 0, + .gpio = NULL, }, { .type = CX231XX_VMUX_COMPOSITE1, .vmux = CX231XX_VIN_2_1, .amux = CX231XX_AMUX_LINE_IN, - .gpio = 0, + .gpio = NULL, }, { .type = CX231XX_VMUX_SVIDEO, .vmux = CX231XX_VIN_1_1 | (CX231XX_VIN_1_2 << 8) | CX25840_SVIDEO_ON, .amux = CX231XX_AMUX_LINE_IN, - .gpio = 0, + .gpio = NULL, } }, }, [CX231XX_BOARD_HAUPPAUGE_USBLIVE2] = { @@ -391,14 +391,14 @@ struct cx231xx_board cx231xx_boards[] = { .type = CX231XX_VMUX_COMPOSITE1, .vmux = CX231XX_VIN_2_1, .amux = CX231XX_AMUX_LINE_IN, - .gpio = 0, + .gpio = NULL, }, { .type = CX231XX_VMUX_SVIDEO, .vmux = CX231XX_VIN_1_1 | (CX231XX_VIN_1_2 << 8) | CX25840_SVIDEO_ON, .amux = CX231XX_AMUX_LINE_IN, - .gpio = 0, + .gpio = NULL, } }, }, [CX231XX_BOARD_PV_PLAYTV_USB_HYBRID] = { @@ -425,19 +425,19 @@ struct cx231xx_board cx231xx_boards[] = { .type = CX231XX_VMUX_TELEVISION, .vmux = CX231XX_VIN_3_1, .amux = CX231XX_AMUX_VIDEO, - .gpio = 0, + .gpio = NULL, }, { .type = CX231XX_VMUX_COMPOSITE1, .vmux = CX231XX_VIN_2_1, .amux = CX231XX_AMUX_LINE_IN, - .gpio = 0, + .gpio = NULL, }, { .type = CX231XX_VMUX_SVIDEO, .vmux = CX231XX_VIN_1_1 | (CX231XX_VIN_1_2 << 8) | CX25840_SVIDEO_ON, .amux = CX231XX_AMUX_LINE_IN, - .gpio = 0, + .gpio = NULL, } }, }, [CX231XX_BOARD_PV_XCAPTURE_USB] = { -- cgit v0.10.2 From fa766c9be58bd872784b081074a7d15a3ce61c5f Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:00:00 -0300 Subject: [media] Altera FPGA firmware download module It uses STAPL files and programs Altera FPGA through JTAG. Interface to JTAG must be provided from main device module, for example through cx23885 GPIO. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index a2742d8..74a8b27 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -175,5 +175,7 @@ source "drivers/staging/cptm1217/Kconfig" source "drivers/staging/ste_rmi4/Kconfig" +source "drivers/staging/altera-stapl/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 4ddd3ce..9f50ec9 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -66,5 +66,6 @@ obj-$(CONFIG_BCM_WIMAX) += bcm/ obj-$(CONFIG_FT1000) += ft1000/ obj-$(CONFIG_SND_INTEL_SST) += intel_sst/ obj-$(CONFIG_SPEAKUP) += speakup/ +obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ diff --git a/drivers/staging/altera-stapl/Kconfig b/drivers/staging/altera-stapl/Kconfig new file mode 100644 index 0000000..7f01d8e --- /dev/null +++ b/drivers/staging/altera-stapl/Kconfig @@ -0,0 +1,8 @@ +comment "Altera FPGA firmware download module" + +config ALTERA_STAPL + tristate "Altera FPGA firmware download module" + depends on I2C + default n + help + An Altera FPGA module. Say Y when you want to support this tool. diff --git a/drivers/staging/altera-stapl/Makefile b/drivers/staging/altera-stapl/Makefile new file mode 100644 index 0000000..055f61ee --- /dev/null +++ b/drivers/staging/altera-stapl/Makefile @@ -0,0 +1,3 @@ +altera-stapl-objs = altera-lpt.o altera-jtag.o altera-comp.o altera.o + +obj-$(CONFIG_ALTERA_STAPL) += altera-stapl.o diff --git a/drivers/staging/altera-stapl/altera-comp.c b/drivers/staging/altera-stapl/altera-comp.c new file mode 100644 index 0000000..49b103b --- /dev/null +++ b/drivers/staging/altera-stapl/altera-comp.c @@ -0,0 +1,142 @@ +/* + * altera-comp.c + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include "altera-exprt.h" + +#define SHORT_BITS 16 +#define CHAR_BITS 8 +#define DATA_BLOB_LENGTH 3 +#define MATCH_DATA_LENGTH 8192 +#define ALTERA_REQUEST_SIZE 1024 +#define ALTERA_BUFFER_SIZE (MATCH_DATA_LENGTH + ALTERA_REQUEST_SIZE) + +static u32 altera_bits_req(u32 n) +{ + u32 result = SHORT_BITS; + + if (n == 0) + result = 1; + else { + /* Look for the highest non-zero bit position */ + while ((n & (1 << (SHORT_BITS - 1))) == 0) { + n <<= 1; + --result; + } + } + + return result; +} + +static u32 altera_read_packed(u8 *buffer, u32 bits, u32 *bits_avail, + u32 *in_index) +{ + u32 result = 0; + u32 shift = 0; + u32 databyte = 0; + + while (bits > 0) { + databyte = buffer[*in_index]; + result |= (((databyte >> (CHAR_BITS - *bits_avail)) + & (0xff >> (CHAR_BITS - *bits_avail))) << shift); + + if (bits <= *bits_avail) { + result &= (0xffff >> (SHORT_BITS - (bits + shift))); + *bits_avail -= bits; + bits = 0; + } else { + ++(*in_index); + shift += *bits_avail; + bits -= *bits_avail; + *bits_avail = CHAR_BITS; + } + } + + return result; +} + +u32 altera_shrink(u8 *in, u32 in_length, u8 *out, u32 out_length, s32 version) +{ + u32 i, j, data_length = 0L; + u32 offset, length; + u32 match_data_length = MATCH_DATA_LENGTH; + u32 bits_avail = CHAR_BITS; + u32 in_index = 0L; + + if (version > 0) + --match_data_length; + + for (i = 0; i < out_length; ++i) + out[i] = 0; + + /* Read number of bytes in data. */ + for (i = 0; i < sizeof(in_length); ++i) { + data_length = data_length | ( + altera_read_packed(in, + CHAR_BITS, + &bits_avail, + &in_index) << (i * CHAR_BITS)); + } + + if (data_length > out_length) { + data_length = 0L; + return data_length; + } + + i = 0; + while (i < data_length) { + /* A 0 bit indicates literal data. */ + if (altera_read_packed(in, 1, &bits_avail, + &in_index) == 0) { + for (j = 0; j < DATA_BLOB_LENGTH; ++j) { + if (i < data_length) { + out[i] = (u8)altera_read_packed(in, + CHAR_BITS, + &bits_avail, + &in_index); + i++; + } + } + } else { + /* A 1 bit indicates offset/length to follow. */ + offset = altera_read_packed(in, altera_bits_req((s16) + (i > match_data_length ? + match_data_length : i)), + &bits_avail, + &in_index); + length = altera_read_packed(in, CHAR_BITS, + &bits_avail, + &in_index); + for (j = 0; j < length; ++j) { + if (i < data_length) { + out[i] = out[i - offset]; + i++; + } + } + } + } + + return data_length; +} diff --git a/drivers/staging/altera-stapl/altera-exprt.h b/drivers/staging/altera-stapl/altera-exprt.h new file mode 100644 index 0000000..39c38d8 --- /dev/null +++ b/drivers/staging/altera-stapl/altera-exprt.h @@ -0,0 +1,33 @@ +/* + * altera-exprt.h + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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 ALTERA_EXPRT_H +#define ALTERA_EXPRT_H + + +u32 altera_shrink(u8 *in, u32 in_length, u8 *out, u32 out_length, s32 version); +int netup_jtag_io_lpt(void *device, int tms, int tdi, int read_tdo); + +#endif /* ALTERA_EXPRT_H */ diff --git a/drivers/staging/altera-stapl/altera-jtag.c b/drivers/staging/altera-stapl/altera-jtag.c new file mode 100644 index 0000000..6b633b1 --- /dev/null +++ b/drivers/staging/altera-stapl/altera-jtag.c @@ -0,0 +1,1020 @@ +/* + * altera-jtag.c + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include "altera-exprt.h" +#include "altera-jtag.h" + +#define alt_jtag_io(a, b, c)\ + astate->config->jtag_io(astate->config->dev, a, b, c); + +#define alt_malloc(a) kzalloc(a, GFP_KERNEL); + +/* + * This structure shows, for each JTAG state, which state is reached after + * a single TCK clock cycle with TMS high or TMS low, respectively. This + * describes all possible state transitions in the JTAG state machine. + */ +struct altera_jtag_machine { + enum altera_jtag_state tms_high; + enum altera_jtag_state tms_low; +}; + +static const struct altera_jtag_machine altera_transitions[] = { + /* RESET */ { RESET, IDLE }, + /* IDLE */ { DRSELECT, IDLE }, + /* DRSELECT */ { IRSELECT, DRCAPTURE }, + /* DRCAPTURE */ { DREXIT1, DRSHIFT }, + /* DRSHIFT */ { DREXIT1, DRSHIFT }, + /* DREXIT1 */ { DRUPDATE, DRPAUSE }, + /* DRPAUSE */ { DREXIT2, DRPAUSE }, + /* DREXIT2 */ { DRUPDATE, DRSHIFT }, + /* DRUPDATE */ { DRSELECT, IDLE }, + /* IRSELECT */ { RESET, IRCAPTURE }, + /* IRCAPTURE */ { IREXIT1, IRSHIFT }, + /* IRSHIFT */ { IREXIT1, IRSHIFT }, + /* IREXIT1 */ { IRUPDATE, IRPAUSE }, + /* IRPAUSE */ { IREXIT2, IRPAUSE }, + /* IREXIT2 */ { IRUPDATE, IRSHIFT }, + /* IRUPDATE */ { DRSELECT, IDLE } +}; + +/* + * This table contains the TMS value to be used to take the NEXT STEP on + * the path to the desired state. The array index is the current state, + * and the bit position is the desired endstate. To find out which state + * is used as the intermediate state, look up the TMS value in the + * altera_transitions[] table. + */ +static const u16 altera_jtag_path_map[16] = { + /* RST RTI SDRS CDR SDR E1DR PDR E2DR */ + 0x0001, 0xFFFD, 0xFE01, 0xFFE7, 0xFFEF, 0xFF0F, 0xFFBF, 0xFFFF, + /* UDR SIRS CIR SIR E1IR PIR E2IR UIR */ + 0xFEFD, 0x0001, 0xF3FF, 0xF7FF, 0x87FF, 0xDFFF, 0xFFFF, 0x7FFD +}; + +/* Flag bits for alt_jtag_io() function */ +#define TMS_HIGH 1 +#define TMS_LOW 0 +#define TDI_HIGH 1 +#define TDI_LOW 0 +#define READ_TDO 1 +#define IGNORE_TDO 0 + +int altera_jinit(struct altera_state *astate) +{ + struct altera_jtag *js = &astate->js; + + /* initial JTAG state is unknown */ + js->jtag_state = ILLEGAL_JTAG_STATE; + + /* initialize to default state */ + js->drstop_state = IDLE; + js->irstop_state = IDLE; + js->dr_pre = 0; + js->dr_post = 0; + js->ir_pre = 0; + js->ir_post = 0; + js->dr_length = 0; + js->ir_length = 0; + + js->dr_pre_data = NULL; + js->dr_post_data = NULL; + js->ir_pre_data = NULL; + js->ir_post_data = NULL; + js->dr_buffer = NULL; + js->ir_buffer = NULL; + + return 0; +} + +int altera_set_drstop(struct altera_jtag *js, enum altera_jtag_state state) +{ + js->drstop_state = state; + + return 0; +} + +int altera_set_irstop(struct altera_jtag *js, enum altera_jtag_state state) +{ + js->irstop_state = state; + + return 0; +} + +int altera_set_dr_pre(struct altera_jtag *js, + u32 count, u32 start_index, + u8 *preamble_data) +{ + int status = 0; + u32 i; + u32 j; + + if (count > js->dr_pre) { + kfree(js->dr_pre_data); + js->dr_pre_data = (u8 *)alt_malloc((count + 7) >> 3); + if (js->dr_pre_data == NULL) + status = -ENOMEM; + else + js->dr_pre = count; + } else + js->dr_pre = count; + + if (status == 0) { + for (i = 0; i < count; ++i) { + j = i + start_index; + + if (preamble_data == NULL) + js->dr_pre_data[i >> 3] |= (1 << (i & 7)); + else { + if (preamble_data[j >> 3] & (1 << (j & 7))) + js->dr_pre_data[i >> 3] |= + (1 << (i & 7)); + else + js->dr_pre_data[i >> 3] &= + ~(u32)(1 << (i & 7)); + + } + } + } + + return status; +} + +int altera_set_ir_pre(struct altera_jtag *js, u32 count, u32 start_index, + u8 *preamble_data) +{ + int status = 0; + u32 i; + u32 j; + + if (count > js->ir_pre) { + kfree(js->ir_pre_data); + js->ir_pre_data = (u8 *)alt_malloc((count + 7) >> 3); + if (js->ir_pre_data == NULL) + status = -ENOMEM; + else + js->ir_pre = count; + + } else + js->ir_pre = count; + + if (status == 0) { + for (i = 0; i < count; ++i) { + j = i + start_index; + if (preamble_data == NULL) + js->ir_pre_data[i >> 3] |= (1 << (i & 7)); + else { + if (preamble_data[j >> 3] & (1 << (j & 7))) + js->ir_pre_data[i >> 3] |= + (1 << (i & 7)); + else + js->ir_pre_data[i >> 3] &= + ~(u32)(1 << (i & 7)); + + } + } + } + + return status; +} + +int altera_set_dr_post(struct altera_jtag *js, u32 count, u32 start_index, + u8 *postamble_data) +{ + int status = 0; + u32 i; + u32 j; + + if (count > js->dr_post) { + kfree(js->dr_post_data); + js->dr_post_data = (u8 *)alt_malloc((count + 7) >> 3); + + if (js->dr_post_data == NULL) + status = -ENOMEM; + else + js->dr_post = count; + + } else + js->dr_post = count; + + if (status == 0) { + for (i = 0; i < count; ++i) { + j = i + start_index; + + if (postamble_data == NULL) + js->dr_post_data[i >> 3] |= (1 << (i & 7)); + else { + if (postamble_data[j >> 3] & (1 << (j & 7))) + js->dr_post_data[i >> 3] |= + (1 << (i & 7)); + else + js->dr_post_data[i >> 3] &= + ~(u32)(1 << (i & 7)); + + } + } + } + + return status; +} + +int altera_set_ir_post(struct altera_jtag *js, u32 count, u32 start_index, + u8 *postamble_data) +{ + int status = 0; + u32 i; + u32 j; + + if (count > js->ir_post) { + kfree(js->ir_post_data); + js->ir_post_data = (u8 *)alt_malloc((count + 7) >> 3); + if (js->ir_post_data == NULL) + status = -ENOMEM; + else + js->ir_post = count; + + } else + js->ir_post = count; + + if (status != 0) + return status; + + for (i = 0; i < count; ++i) { + j = i + start_index; + + if (postamble_data == NULL) + js->ir_post_data[i >> 3] |= (1 << (i & 7)); + else { + if (postamble_data[j >> 3] & (1 << (j & 7))) + js->ir_post_data[i >> 3] |= (1 << (i & 7)); + else + js->ir_post_data[i >> 3] &= + ~(u32)(1 << (i & 7)); + + } + } + + return status; +} + +static void altera_jreset_idle(struct altera_state *astate) +{ + struct altera_jtag *js = &astate->js; + int i; + /* Go to Test Logic Reset (no matter what the starting state may be) */ + for (i = 0; i < 5; ++i) + alt_jtag_io(TMS_HIGH, TDI_LOW, IGNORE_TDO); + + /* Now step to Run Test / Idle */ + alt_jtag_io(TMS_LOW, TDI_LOW, IGNORE_TDO); + js->jtag_state = IDLE; +} + +int altera_goto_jstate(struct altera_state *astate, + enum altera_jtag_state state) +{ + struct altera_jtag *js = &astate->js; + int tms; + int count = 0; + int status = 0; + + if (js->jtag_state == ILLEGAL_JTAG_STATE) + /* initialize JTAG chain to known state */ + altera_jreset_idle(astate); + + if (js->jtag_state == state) { + /* + * We are already in the desired state. + * If it is a stable state, loop here. + * Otherwise do nothing (no clock cycles). + */ + if ((state == IDLE) || (state == DRSHIFT) || + (state == DRPAUSE) || (state == IRSHIFT) || + (state == IRPAUSE)) { + alt_jtag_io(TMS_LOW, TDI_LOW, IGNORE_TDO); + } else if (state == RESET) + alt_jtag_io(TMS_HIGH, TDI_LOW, IGNORE_TDO); + + } else { + while ((js->jtag_state != state) && (count < 9)) { + /* Get TMS value to take a step toward desired state */ + tms = (altera_jtag_path_map[js->jtag_state] & + (1 << state)) + ? TMS_HIGH : TMS_LOW; + + /* Take a step */ + alt_jtag_io(tms, TDI_LOW, IGNORE_TDO); + + if (tms) + js->jtag_state = + altera_transitions[js->jtag_state].tms_high; + else + js->jtag_state = + altera_transitions[js->jtag_state].tms_low; + + ++count; + } + } + + if (js->jtag_state != state) + status = -EREMOTEIO; + + return status; +} + +int altera_wait_cycles(struct altera_state *astate, + s32 cycles, + enum altera_jtag_state wait_state) +{ + struct altera_jtag *js = &astate->js; + int tms; + s32 count; + int status = 0; + + if (js->jtag_state != wait_state) + status = altera_goto_jstate(astate, wait_state); + + if (status == 0) { + /* + * Set TMS high to loop in RESET state + * Set TMS low to loop in any other stable state + */ + tms = (wait_state == RESET) ? TMS_HIGH : TMS_LOW; + + for (count = 0L; count < cycles; count++) + alt_jtag_io(tms, TDI_LOW, IGNORE_TDO); + + } + + return status; +} + +int altera_wait_msecs(struct altera_state *astate, + s32 microseconds, enum altera_jtag_state wait_state) +/* + * Causes JTAG hardware to sit in the specified stable + * state for the specified duration of real time. If + * no JTAG operations have been performed yet, then only + * a delay is performed. This permits the WAIT USECS + * statement to be used in VECTOR programs without causing + * any JTAG operations. + * Returns 0 for success, else appropriate error code. + */ +{ + struct altera_jtag *js = &astate->js; + int status = 0; + + if ((js->jtag_state != ILLEGAL_JTAG_STATE) && + (js->jtag_state != wait_state)) + status = altera_goto_jstate(astate, wait_state); + + if (status == 0) + /* Wait for specified time interval */ + udelay(microseconds); + + return status; +} + +static void altera_concatenate_data(u8 *buffer, + u8 *preamble_data, + u32 preamble_count, + u8 *target_data, + u32 start_index, + u32 target_count, + u8 *postamble_data, + u32 postamble_count) +/* + * Copies preamble data, target data, and postamble data + * into one buffer for IR or DR scans. + */ +{ + u32 i, j, k; + + for (i = 0L; i < preamble_count; ++i) { + if (preamble_data[i >> 3L] & (1L << (i & 7L))) + buffer[i >> 3L] |= (1L << (i & 7L)); + else + buffer[i >> 3L] &= ~(u32)(1L << (i & 7L)); + + } + + j = start_index; + k = preamble_count + target_count; + for (; i < k; ++i, ++j) { + if (target_data[j >> 3L] & (1L << (j & 7L))) + buffer[i >> 3L] |= (1L << (i & 7L)); + else + buffer[i >> 3L] &= ~(u32)(1L << (i & 7L)); + + } + + j = 0L; + k = preamble_count + target_count + postamble_count; + for (; i < k; ++i, ++j) { + if (postamble_data[j >> 3L] & (1L << (j & 7L))) + buffer[i >> 3L] |= (1L << (i & 7L)); + else + buffer[i >> 3L] &= ~(u32)(1L << (i & 7L)); + + } +} + +static int alt_jtag_drscan(struct altera_state *astate, + int start_state, + int count, + u8 *tdi, + u8 *tdo) +{ + int i = 0; + int tdo_bit = 0; + int status = 1; + + /* First go to DRSHIFT state */ + switch (start_state) { + case 0: /* IDLE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(0, 0, 0); /* DRCAPTURE */ + alt_jtag_io(0, 0, 0); /* DRSHIFT */ + break; + + case 1: /* DRPAUSE */ + alt_jtag_io(1, 0, 0); /* DREXIT2 */ + alt_jtag_io(1, 0, 0); /* DRUPDATE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(0, 0, 0); /* DRCAPTURE */ + alt_jtag_io(0, 0, 0); /* DRSHIFT */ + break; + + case 2: /* IRPAUSE */ + alt_jtag_io(1, 0, 0); /* IREXIT2 */ + alt_jtag_io(1, 0, 0); /* IRUPDATE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(0, 0, 0); /* DRCAPTURE */ + alt_jtag_io(0, 0, 0); /* DRSHIFT */ + break; + + default: + status = 0; + } + + if (status) { + /* loop in the SHIFT-DR state */ + for (i = 0; i < count; i++) { + tdo_bit = alt_jtag_io( + (i == count - 1), + tdi[i >> 3] & (1 << (i & 7)), + (tdo != NULL)); + + if (tdo != NULL) { + if (tdo_bit) + tdo[i >> 3] |= (1 << (i & 7)); + else + tdo[i >> 3] &= ~(u32)(1 << (i & 7)); + + } + } + + alt_jtag_io(0, 0, 0); /* DRPAUSE */ + } + + return status; +} + +static int alt_jtag_irscan(struct altera_state *astate, + int start_state, + int count, + u8 *tdi, + u8 *tdo) +{ + int i = 0; + int tdo_bit = 0; + int status = 1; + + /* First go to IRSHIFT state */ + switch (start_state) { + case 0: /* IDLE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(1, 0, 0); /* IRSELECT */ + alt_jtag_io(0, 0, 0); /* IRCAPTURE */ + alt_jtag_io(0, 0, 0); /* IRSHIFT */ + break; + + case 1: /* DRPAUSE */ + alt_jtag_io(1, 0, 0); /* DREXIT2 */ + alt_jtag_io(1, 0, 0); /* DRUPDATE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(1, 0, 0); /* IRSELECT */ + alt_jtag_io(0, 0, 0); /* IRCAPTURE */ + alt_jtag_io(0, 0, 0); /* IRSHIFT */ + break; + + case 2: /* IRPAUSE */ + alt_jtag_io(1, 0, 0); /* IREXIT2 */ + alt_jtag_io(1, 0, 0); /* IRUPDATE */ + alt_jtag_io(1, 0, 0); /* DRSELECT */ + alt_jtag_io(1, 0, 0); /* IRSELECT */ + alt_jtag_io(0, 0, 0); /* IRCAPTURE */ + alt_jtag_io(0, 0, 0); /* IRSHIFT */ + break; + + default: + status = 0; + } + + if (status) { + /* loop in the SHIFT-IR state */ + for (i = 0; i < count; i++) { + tdo_bit = alt_jtag_io( + (i == count - 1), + tdi[i >> 3] & (1 << (i & 7)), + (tdo != NULL)); + if (tdo != NULL) { + if (tdo_bit) + tdo[i >> 3] |= (1 << (i & 7)); + else + tdo[i >> 3] &= ~(u32)(1 << (i & 7)); + + } + } + + alt_jtag_io(0, 0, 0); /* IRPAUSE */ + } + + return status; +} + +static void altera_extract_target_data(u8 *buffer, + u8 *target_data, + u32 start_index, + u32 preamble_count, + u32 target_count) +/* + * Copies target data from scan buffer, filtering out + * preamble and postamble data. + */ +{ + u32 i; + u32 j; + u32 k; + + j = preamble_count; + k = start_index + target_count; + for (i = start_index; i < k; ++i, ++j) { + if (buffer[j >> 3] & (1 << (j & 7))) + target_data[i >> 3] |= (1 << (i & 7)); + else + target_data[i >> 3] &= ~(u32)(1 << (i & 7)); + + } +} + +int altera_irscan(struct altera_state *astate, + u32 count, + u8 *tdi_data, + u32 start_index) +/* Shifts data into instruction register */ +{ + struct altera_jtag *js = &astate->js; + int start_code = 0; + u32 alloc_chars = 0; + u32 shift_count = js->ir_pre + count + js->ir_post; + int status = 0; + enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE; + + switch (js->jtag_state) { + case ILLEGAL_JTAG_STATE: + case RESET: + case IDLE: + start_code = 0; + start_state = IDLE; + break; + + case DRSELECT: + case DRCAPTURE: + case DRSHIFT: + case DREXIT1: + case DRPAUSE: + case DREXIT2: + case DRUPDATE: + start_code = 1; + start_state = DRPAUSE; + break; + + case IRSELECT: + case IRCAPTURE: + case IRSHIFT: + case IREXIT1: + case IRPAUSE: + case IREXIT2: + case IRUPDATE: + start_code = 2; + start_state = IRPAUSE; + break; + + default: + status = -EREMOTEIO; + break; + } + + if (status == 0) + if (js->jtag_state != start_state) + status = altera_goto_jstate(astate, start_state); + + if (status == 0) { + if (shift_count > js->ir_length) { + alloc_chars = (shift_count + 7) >> 3; + kfree(js->ir_buffer); + js->ir_buffer = (u8 *)alt_malloc(alloc_chars); + if (js->ir_buffer == NULL) + status = -ENOMEM; + else + js->ir_length = alloc_chars * 8; + + } + } + + if (status == 0) { + /* + * Copy preamble data, IR data, + * and postamble data into a buffer + */ + altera_concatenate_data(js->ir_buffer, + js->ir_pre_data, + js->ir_pre, + tdi_data, + start_index, + count, + js->ir_post_data, + js->ir_post); + /* Do the IRSCAN */ + alt_jtag_irscan(astate, + start_code, + shift_count, + js->ir_buffer, + NULL); + + /* alt_jtag_irscan() always ends in IRPAUSE state */ + js->jtag_state = IRPAUSE; + } + + if (status == 0) + if (js->irstop_state != IRPAUSE) + status = altera_goto_jstate(astate, js->irstop_state); + + + return status; +} + +int altera_swap_ir(struct altera_state *astate, + u32 count, + u8 *in_data, + u32 in_index, + u8 *out_data, + u32 out_index) +/* Shifts data into instruction register, capturing output data */ +{ + struct altera_jtag *js = &astate->js; + int start_code = 0; + u32 alloc_chars = 0; + u32 shift_count = js->ir_pre + count + js->ir_post; + int status = 0; + enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE; + + switch (js->jtag_state) { + case ILLEGAL_JTAG_STATE: + case RESET: + case IDLE: + start_code = 0; + start_state = IDLE; + break; + + case DRSELECT: + case DRCAPTURE: + case DRSHIFT: + case DREXIT1: + case DRPAUSE: + case DREXIT2: + case DRUPDATE: + start_code = 1; + start_state = DRPAUSE; + break; + + case IRSELECT: + case IRCAPTURE: + case IRSHIFT: + case IREXIT1: + case IRPAUSE: + case IREXIT2: + case IRUPDATE: + start_code = 2; + start_state = IRPAUSE; + break; + + default: + status = -EREMOTEIO; + break; + } + + if (status == 0) + if (js->jtag_state != start_state) + status = altera_goto_jstate(astate, start_state); + + if (status == 0) { + if (shift_count > js->ir_length) { + alloc_chars = (shift_count + 7) >> 3; + kfree(js->ir_buffer); + js->ir_buffer = (u8 *)alt_malloc(alloc_chars); + if (js->ir_buffer == NULL) + status = -ENOMEM; + else + js->ir_length = alloc_chars * 8; + + } + } + + if (status == 0) { + /* + * Copy preamble data, IR data, + * and postamble data into a buffer + */ + altera_concatenate_data(js->ir_buffer, + js->ir_pre_data, + js->ir_pre, + in_data, + in_index, + count, + js->ir_post_data, + js->ir_post); + + /* Do the IRSCAN */ + alt_jtag_irscan(astate, + start_code, + shift_count, + js->ir_buffer, + js->ir_buffer); + + /* alt_jtag_irscan() always ends in IRPAUSE state */ + js->jtag_state = IRPAUSE; + } + + if (status == 0) + if (js->irstop_state != IRPAUSE) + status = altera_goto_jstate(astate, js->irstop_state); + + + if (status == 0) + /* Now extract the returned data from the buffer */ + altera_extract_target_data(js->ir_buffer, + out_data, out_index, + js->ir_pre, count); + + return status; +} + +int altera_drscan(struct altera_state *astate, + u32 count, + u8 *tdi_data, + u32 start_index) +/* Shifts data into data register (ignoring output data) */ +{ + struct altera_jtag *js = &astate->js; + int start_code = 0; + u32 alloc_chars = 0; + u32 shift_count = js->dr_pre + count + js->dr_post; + int status = 0; + enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE; + + switch (js->jtag_state) { + case ILLEGAL_JTAG_STATE: + case RESET: + case IDLE: + start_code = 0; + start_state = IDLE; + break; + + case DRSELECT: + case DRCAPTURE: + case DRSHIFT: + case DREXIT1: + case DRPAUSE: + case DREXIT2: + case DRUPDATE: + start_code = 1; + start_state = DRPAUSE; + break; + + case IRSELECT: + case IRCAPTURE: + case IRSHIFT: + case IREXIT1: + case IRPAUSE: + case IREXIT2: + case IRUPDATE: + start_code = 2; + start_state = IRPAUSE; + break; + + default: + status = -EREMOTEIO; + break; + } + + if (status == 0) + if (js->jtag_state != start_state) + status = altera_goto_jstate(astate, start_state); + + if (status == 0) { + if (shift_count > js->dr_length) { + alloc_chars = (shift_count + 7) >> 3; + kfree(js->dr_buffer); + js->dr_buffer = (u8 *)alt_malloc(alloc_chars); + if (js->dr_buffer == NULL) + status = -ENOMEM; + else + js->dr_length = alloc_chars * 8; + + } + } + + if (status == 0) { + /* + * Copy preamble data, DR data, + * and postamble data into a buffer + */ + altera_concatenate_data(js->dr_buffer, + js->dr_pre_data, + js->dr_pre, + tdi_data, + start_index, + count, + js->dr_post_data, + js->dr_post); + /* Do the DRSCAN */ + alt_jtag_drscan(astate, start_code, shift_count, + js->dr_buffer, NULL); + /* alt_jtag_drscan() always ends in DRPAUSE state */ + js->jtag_state = DRPAUSE; + } + + if (status == 0) + if (js->drstop_state != DRPAUSE) + status = altera_goto_jstate(astate, js->drstop_state); + + return status; +} + +int altera_swap_dr(struct altera_state *astate, u32 count, + u8 *in_data, u32 in_index, + u8 *out_data, u32 out_index) +/* Shifts data into data register, capturing output data */ +{ + struct altera_jtag *js = &astate->js; + int start_code = 0; + u32 alloc_chars = 0; + u32 shift_count = js->dr_pre + count + js->dr_post; + int status = 0; + enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE; + + switch (js->jtag_state) { + case ILLEGAL_JTAG_STATE: + case RESET: + case IDLE: + start_code = 0; + start_state = IDLE; + break; + + case DRSELECT: + case DRCAPTURE: + case DRSHIFT: + case DREXIT1: + case DRPAUSE: + case DREXIT2: + case DRUPDATE: + start_code = 1; + start_state = DRPAUSE; + break; + + case IRSELECT: + case IRCAPTURE: + case IRSHIFT: + case IREXIT1: + case IRPAUSE: + case IREXIT2: + case IRUPDATE: + start_code = 2; + start_state = IRPAUSE; + break; + + default: + status = -EREMOTEIO; + break; + } + + if (status == 0) + if (js->jtag_state != start_state) + status = altera_goto_jstate(astate, start_state); + + if (status == 0) { + if (shift_count > js->dr_length) { + alloc_chars = (shift_count + 7) >> 3; + kfree(js->dr_buffer); + js->dr_buffer = (u8 *)alt_malloc(alloc_chars); + + if (js->dr_buffer == NULL) + status = -ENOMEM; + else + js->dr_length = alloc_chars * 8; + + } + } + + if (status == 0) { + /* + * Copy preamble data, DR data, + * and postamble data into a buffer + */ + altera_concatenate_data(js->dr_buffer, + js->dr_pre_data, + js->dr_pre, + in_data, + in_index, + count, + js->dr_post_data, + js->dr_post); + + /* Do the DRSCAN */ + alt_jtag_drscan(astate, + start_code, + shift_count, + js->dr_buffer, + js->dr_buffer); + + /* alt_jtag_drscan() always ends in DRPAUSE state */ + js->jtag_state = DRPAUSE; + } + + if (status == 0) + if (js->drstop_state != DRPAUSE) + status = altera_goto_jstate(astate, js->drstop_state); + + if (status == 0) + /* Now extract the returned data from the buffer */ + altera_extract_target_data(js->dr_buffer, + out_data, + out_index, + js->dr_pre, + count); + + return status; +} + +void altera_free_buffers(struct altera_state *astate) +{ + struct altera_jtag *js = &astate->js; + /* If the JTAG interface was used, reset it to TLR */ + if (js->jtag_state != ILLEGAL_JTAG_STATE) + altera_jreset_idle(astate); + + kfree(js->dr_pre_data); + js->dr_pre_data = NULL; + + kfree(js->dr_post_data); + js->dr_post_data = NULL; + + kfree(js->dr_buffer); + js->dr_buffer = NULL; + + kfree(js->ir_pre_data); + js->ir_pre_data = NULL; + + kfree(js->ir_post_data); + js->ir_post_data = NULL; + + kfree(js->ir_buffer); + js->ir_buffer = NULL; +} diff --git a/drivers/staging/altera-stapl/altera-jtag.h b/drivers/staging/altera-stapl/altera-jtag.h new file mode 100644 index 0000000..2f97e36 --- /dev/null +++ b/drivers/staging/altera-stapl/altera-jtag.h @@ -0,0 +1,113 @@ +/* + * altera-jtag.h + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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 ALTERA_JTAG_H +#define ALTERA_JTAG_H + +/* Function Prototypes */ +enum altera_jtag_state { + ILLEGAL_JTAG_STATE = -1, + RESET = 0, + IDLE = 1, + DRSELECT = 2, + DRCAPTURE = 3, + DRSHIFT = 4, + DREXIT1 = 5, + DRPAUSE = 6, + DREXIT2 = 7, + DRUPDATE = 8, + IRSELECT = 9, + IRCAPTURE = 10, + IRSHIFT = 11, + IREXIT1 = 12, + IRPAUSE = 13, + IREXIT2 = 14, + IRUPDATE = 15 + +}; + +struct altera_jtag { + /* Global variable to store the current JTAG state */ + enum altera_jtag_state jtag_state; + + /* Store current stop-state for DR and IR scan commands */ + enum altera_jtag_state drstop_state; + enum altera_jtag_state irstop_state; + + /* Store current padding values */ + u32 dr_pre; + u32 dr_post; + u32 ir_pre; + u32 ir_post; + u32 dr_length; + u32 ir_length; + u8 *dr_pre_data; + u8 *dr_post_data; + u8 *ir_pre_data; + u8 *ir_post_data; + u8 *dr_buffer; + u8 *ir_buffer; +}; + +#define ALTERA_STACK_SIZE 128 +#define ALTERA_MESSAGE_LENGTH 1024 + +struct altera_state { + struct altera_config *config; + struct altera_jtag js; + char msg_buff[ALTERA_MESSAGE_LENGTH + 1]; + long stack[ALTERA_STACK_SIZE]; +}; + +int altera_jinit(struct altera_state *astate); +int altera_set_drstop(struct altera_jtag *js, enum altera_jtag_state state); +int altera_set_irstop(struct altera_jtag *js, enum altera_jtag_state state); +int altera_set_dr_pre(struct altera_jtag *js, u32 count, u32 start_index, + u8 *preamble_data); +int altera_set_ir_pre(struct altera_jtag *js, u32 count, u32 start_index, + u8 *preamble_data); +int altera_set_dr_post(struct altera_jtag *js, u32 count, u32 start_index, + u8 *postamble_data); +int altera_set_ir_post(struct altera_jtag *js, u32 count, u32 start_index, + u8 *postamble_data); +int altera_goto_jstate(struct altera_state *astate, + enum altera_jtag_state state); +int altera_wait_cycles(struct altera_state *astate, s32 cycles, + enum altera_jtag_state wait_state); +int altera_wait_msecs(struct altera_state *astate, s32 microseconds, + enum altera_jtag_state wait_state); +int altera_irscan(struct altera_state *astate, u32 count, + u8 *tdi_data, u32 start_index); +int altera_swap_ir(struct altera_state *astate, + u32 count, u8 *in_data, + u32 in_index, u8 *out_data, + u32 out_index); +int altera_drscan(struct altera_state *astate, u32 count, + u8 *tdi_data, u32 start_index); +int altera_swap_dr(struct altera_state *astate, u32 count, + u8 *in_data, u32 in_index, + u8 *out_data, u32 out_index); +void altera_free_buffers(struct altera_state *astate); +#endif /* ALTERA_JTAG_H */ diff --git a/drivers/staging/altera-stapl/altera-lpt.c b/drivers/staging/altera-stapl/altera-lpt.c new file mode 100644 index 0000000..91456a0 --- /dev/null +++ b/drivers/staging/altera-stapl/altera-lpt.c @@ -0,0 +1,70 @@ +/* + * altera-lpt.c + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Abylay Ospan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include "altera-exprt.h" + +static int lpt_hardware_initialized; + +static void byteblaster_write(int port, int data) +{ + outb((u8)data, (u16)(port + 0x378)); +}; + +static int byteblaster_read(int port) +{ + int data = 0; + data = inb((u16)(port + 0x378)); + return data & 0xff; +}; + +int netup_jtag_io_lpt(void *device, int tms, int tdi, int read_tdo) +{ + int data = 0; + int tdo = 0; + int initial_lpt_ctrl = 0; + + if (!lpt_hardware_initialized) { + initial_lpt_ctrl = byteblaster_read(2); + byteblaster_write(2, (initial_lpt_ctrl | 0x02) & 0xdf); + lpt_hardware_initialized = 1; + } + + data = ((tdi ? 0x40 : 0) | (tms ? 0x02 : 0)); + + byteblaster_write(0, data); + + if (read_tdo) { + tdo = byteblaster_read(1); + tdo = ((tdo & 0x80) ? 0 : 1); + } + + byteblaster_write(0, data | 0x01); + + byteblaster_write(0, data); + + return tdo; +} diff --git a/drivers/staging/altera-stapl/altera.c b/drivers/staging/altera-stapl/altera.c new file mode 100644 index 0000000..05aad35 --- /dev/null +++ b/drivers/staging/altera-stapl/altera.c @@ -0,0 +1,2527 @@ +/* + * altera.c + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include "altera-exprt.h" +#include "altera-jtag.h" + +static int debug = 1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debugging information"); + +MODULE_DESCRIPTION("altera FPGA kernel module"); +MODULE_AUTHOR("Igor M. Liplianin "); +MODULE_LICENSE("GPL"); + +#define dprintk(args...) \ + if (debug) { \ + printk(KERN_DEBUG args); \ + } + +enum altera_fpga_opcode { + OP_NOP = 0, + OP_DUP, + OP_SWP, + OP_ADD, + OP_SUB, + OP_MULT, + OP_DIV, + OP_MOD, + OP_SHL, + OP_SHR, + OP_NOT, + OP_AND, + OP_OR, + OP_XOR, + OP_INV, + OP_GT, + OP_LT, + OP_RET, + OP_CMPS, + OP_PINT, + OP_PRNT, + OP_DSS, + OP_DSSC, + OP_ISS, + OP_ISSC, + OP_DPR = 0x1c, + OP_DPRL, + OP_DPO, + OP_DPOL, + OP_IPR, + OP_IPRL, + OP_IPO, + OP_IPOL, + OP_PCHR, + OP_EXIT, + OP_EQU, + OP_POPT, + OP_ABS = 0x2c, + OP_BCH0, + OP_PSH0 = 0x2f, + OP_PSHL = 0x40, + OP_PSHV, + OP_JMP, + OP_CALL, + OP_NEXT, + OP_PSTR, + OP_SINT = 0x47, + OP_ST, + OP_ISTP, + OP_DSTP, + OP_SWPN, + OP_DUPN, + OP_POPV, + OP_POPE, + OP_POPA, + OP_JMPZ, + OP_DS, + OP_IS, + OP_DPRA, + OP_DPOA, + OP_IPRA, + OP_IPOA, + OP_EXPT, + OP_PSHE, + OP_PSHA, + OP_DYNA, + OP_EXPV = 0x5c, + OP_COPY = 0x80, + OP_REVA, + OP_DSC, + OP_ISC, + OP_WAIT, + OP_VS, + OP_CMPA = 0xc0, + OP_VSC, +}; + +struct altera_procinfo { + char *name; + u8 attrs; + struct altera_procinfo *next; +}; + +/* This function checks if enough parameters are available on the stack. */ +static int altera_check_stack(int stack_ptr, int count, int *status) +{ + if (stack_ptr < count) { + *status = -EOVERFLOW; + return 0; + } + + return 1; +} + +static void altera_export_int(char *key, s32 value) +{ + dprintk("Export: key = \"%s\", value = %d\n", key, value); +} + +#define HEX_LINE_CHARS 72 +#define HEX_LINE_BITS (HEX_LINE_CHARS * 4) + +static void altera_export_bool_array(char *key, u8 *data, s32 count) +{ + char string[HEX_LINE_CHARS + 1]; + s32 i, offset; + u32 size, line, lines, linebits, value, j, k; + + if (count > HEX_LINE_BITS) { + dprintk("Export: key = \"%s\", %d bits, value = HEX\n", + key, count); + lines = (count + (HEX_LINE_BITS - 1)) / HEX_LINE_BITS; + + for (line = 0; line < lines; ++line) { + if (line < (lines - 1)) { + linebits = HEX_LINE_BITS; + size = HEX_LINE_CHARS; + offset = count - ((line + 1) * HEX_LINE_BITS); + } else { + linebits = + count - ((lines - 1) * HEX_LINE_BITS); + size = (linebits + 3) / 4; + offset = 0L; + } + + string[size] = '\0'; + j = size - 1; + value = 0; + + for (k = 0; k < linebits; ++k) { + i = k + offset; + if (data[i >> 3] & (1 << (i & 7))) + value |= (1 << (i & 3)); + if ((i & 3) == 3) { + sprintf(&string[j], "%1x", value); + value = 0; + --j; + } + } + if ((k & 3) > 0) + sprintf(&string[j], "%1x", value); + + dprintk("%s\n", string); + } + + } else { + size = (count + 3) / 4; + string[size] = '\0'; + j = size - 1; + value = 0; + + for (i = 0; i < count; ++i) { + if (data[i >> 3] & (1 << (i & 7))) + value |= (1 << (i & 3)); + if ((i & 3) == 3) { + sprintf(&string[j], "%1x", value); + value = 0; + --j; + } + } + if ((i & 3) > 0) + sprintf(&string[j], "%1x", value); + + dprintk("Export: key = \"%s\", %d bits, value = HEX %s\n", + key, count, string); + } +} + +static int altera_execute(struct altera_state *astate, + u8 *p, + s32 program_size, + s32 *error_address, + int *exit_code, + int *format_version) +{ + struct altera_config *aconf = astate->config; + char *msg_buff = astate->msg_buff; + long *stack = astate->stack; + int status = 0; + u32 first_word = 0L; + u32 action_table = 0L; + u32 proc_table = 0L; + u32 str_table = 0L; + u32 sym_table = 0L; + u32 data_sect = 0L; + u32 code_sect = 0L; + u32 debug_sect = 0L; + u32 action_count = 0L; + u32 proc_count = 0L; + u32 sym_count = 0L; + long *vars = NULL; + s32 *var_size = NULL; + char *attrs = NULL; + u8 *proc_attributes = NULL; + u32 pc; + u32 opcode_address; + u32 args[3]; + u32 opcode; + u32 name_id; + u8 charbuf[4]; + long long_tmp; + u32 variable_id; + u8 *charptr_tmp; + u8 *charptr_tmp2; + long *longptr_tmp; + int version = 0; + int delta = 0; + int stack_ptr = 0; + u32 arg_count; + int done = 0; + int bad_opcode = 0; + u32 count; + u32 index; + u32 index2; + s32 long_count; + s32 long_idx; + s32 long_idx2; + u32 i; + u32 j; + u32 uncomp_size; + u32 offset; + u32 value; + int current_proc = 0; + int reverse; + + char *name; + + dprintk("%s\n", __func__); + + /* Read header information */ + if (program_size > 52L) { + first_word = get_unaligned_be32(&p[0]); + version = (first_word & 1L); + *format_version = version + 1; + delta = version * 8; + + action_table = get_unaligned_be32(&p[4]); + proc_table = get_unaligned_be32(&p[8]); + str_table = get_unaligned_be32(&p[4 + delta]); + sym_table = get_unaligned_be32(&p[16 + delta]); + data_sect = get_unaligned_be32(&p[20 + delta]); + code_sect = get_unaligned_be32(&p[24 + delta]); + debug_sect = get_unaligned_be32(&p[28 + delta]); + action_count = get_unaligned_be32(&p[40 + delta]); + proc_count = get_unaligned_be32(&p[44 + delta]); + sym_count = get_unaligned_be32(&p[48 + (2 * delta)]); + } + + if ((first_word != 0x4A414D00L) && (first_word != 0x4A414D01L)) { + done = 1; + status = -EIO; + goto exit_done; + } + + if (sym_count <= 0) + goto exit_done; + + vars = kzalloc(sym_count * sizeof(long), GFP_KERNEL); + + if (vars == NULL) + status = -ENOMEM; + + if (status == 0) { + var_size = kzalloc(sym_count * sizeof(s32), GFP_KERNEL); + + if (var_size == NULL) + status = -ENOMEM; + } + + if (status == 0) { + attrs = kzalloc(sym_count, GFP_KERNEL); + + if (attrs == NULL) + status = -ENOMEM; + } + + if ((status == 0) && (version > 0)) { + proc_attributes = kzalloc(proc_count, GFP_KERNEL); + + if (proc_attributes == NULL) + status = -ENOMEM; + } + + if (status != 0) + goto exit_done; + + delta = version * 2; + + for (i = 0; i < sym_count; ++i) { + offset = (sym_table + ((11 + delta) * i)); + + value = get_unaligned_be32(&p[offset + 3 + delta]); + + attrs[i] = p[offset]; + + /* + * use bit 7 of attribute byte to indicate that + * this buffer was dynamically allocated + * and should be freed later + */ + attrs[i] &= 0x7f; + + var_size[i] = get_unaligned_be32(&p[offset + 7 + delta]); + + /* + * Attribute bits: + * bit 0: 0 = read-only, 1 = read-write + * bit 1: 0 = not compressed, 1 = compressed + * bit 2: 0 = not initialized, 1 = initialized + * bit 3: 0 = scalar, 1 = array + * bit 4: 0 = Boolean, 1 = integer + * bit 5: 0 = declared variable, + * 1 = compiler created temporary variable + */ + + if ((attrs[i] & 0x0c) == 0x04) + /* initialized scalar variable */ + vars[i] = value; + else if ((attrs[i] & 0x1e) == 0x0e) { + /* initialized compressed Boolean array */ + uncomp_size = get_unaligned_le32(&p[data_sect + value]); + + /* allocate a buffer for the uncompressed data */ + vars[i] = (long)kzalloc(uncomp_size, GFP_KERNEL); + if (vars[i] == 0L) + status = -ENOMEM; + else { + /* set flag so buffer will be freed later */ + attrs[i] |= 0x80; + + /* uncompress the data */ + if (altera_shrink(&p[data_sect + value], + var_size[i], + (u8 *)vars[i], + uncomp_size, + version) != uncomp_size) + /* decompression failed */ + status = -EIO; + else + var_size[i] = uncomp_size * 8L; + + } + } else if ((attrs[i] & 0x1e) == 0x0c) { + /* initialized Boolean array */ + vars[i] = value + data_sect + (long)p; + } else if ((attrs[i] & 0x1c) == 0x1c) { + /* initialized integer array */ + vars[i] = value + data_sect; + } else if ((attrs[i] & 0x0c) == 0x08) { + /* uninitialized array */ + + /* flag attrs so that memory is freed */ + attrs[i] |= 0x80; + + if (var_size[i] > 0) { + u32 size; + + if (attrs[i] & 0x10) + /* integer array */ + size = (var_size[i] * sizeof(s32)); + else + /* Boolean array */ + size = ((var_size[i] + 7L) / 8L); + + vars[i] = (long)kzalloc(size, GFP_KERNEL); + + if (vars[i] == 0) { + status = -ENOMEM; + } else { + /* zero out memory */ + for (j = 0; j < size; ++j) + ((u8 *)(vars[i]))[j] = 0; + + } + } else + vars[i] = 0; + + } else + vars[i] = 0; + + } + +exit_done: + if (status != 0) + done = 1; + + altera_jinit(astate); + + pc = code_sect; + msg_buff[0] = '\0'; + + /* + * For JBC version 2, we will execute the procedures corresponding to + * the selected ACTION + */ + if (version > 0) { + if (aconf->action == NULL) { + status = -EINVAL; + done = 1; + } else { + int action_found = 0; + for (i = 0; (i < action_count) && !action_found; ++i) { + name_id = get_unaligned_be32(&p[action_table + + (12 * i)]); + + name = &p[str_table + name_id]; + + if (strnicmp(aconf->action, name, strlen(name)) == 0) { + action_found = 1; + current_proc = + get_unaligned_be32(&p[action_table + + (12 * i) + 8]); + } + } + + if (!action_found) { + status = -EINVAL; + done = 1; + } + } + + if (status == 0) { + int first_time = 1; + i = current_proc; + while ((i != 0) || first_time) { + first_time = 0; + /* check procedure attribute byte */ + proc_attributes[i] = + (p[proc_table + + (13 * i) + 8] & + 0x03); + + /* + * BIT0 - OPTIONAL + * BIT1 - RECOMMENDED + * BIT6 - FORCED OFF + * BIT7 - FORCED ON + */ + + i = get_unaligned_be32(&p[proc_table + + (13 * i) + 4]); + } + + /* + * Set current_proc to the first procedure + * to be executed + */ + i = current_proc; + while ((i != 0) && + ((proc_attributes[i] == 1) || + ((proc_attributes[i] & 0xc0) == 0x40))) { + i = get_unaligned_be32(&p[proc_table + + (13 * i) + 4]); + } + + if ((i != 0) || ((i == 0) && (current_proc == 0) && + ((proc_attributes[0] != 1) && + ((proc_attributes[0] & 0xc0) != 0x40)))) { + current_proc = i; + pc = code_sect + + get_unaligned_be32(&p[proc_table + + (13 * i) + 9]); + if ((pc < code_sect) || (pc >= debug_sect)) + status = -ERANGE; + } else + /* there are no procedures to execute! */ + done = 1; + + } + } + + msg_buff[0] = '\0'; + + while (!done) { + opcode = (p[pc] & 0xff); + opcode_address = pc; + ++pc; + + if (debug > 1) + printk("opcode: %02x\n", opcode); + + arg_count = (opcode >> 6) & 3; + for (i = 0; i < arg_count; ++i) { + args[i] = get_unaligned_be32(&p[pc]); + pc += 4; + } + + switch (opcode) { + case OP_NOP: + break; + case OP_DUP: + if (altera_check_stack(stack_ptr, 1, &status)) { + stack[stack_ptr] = stack[stack_ptr - 1]; + ++stack_ptr; + } + break; + case OP_SWP: + if (altera_check_stack(stack_ptr, 2, &status)) { + long_tmp = stack[stack_ptr - 2]; + stack[stack_ptr - 2] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + break; + case OP_ADD: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] += stack[stack_ptr]; + } + break; + case OP_SUB: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] -= stack[stack_ptr]; + } + break; + case OP_MULT: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] *= stack[stack_ptr]; + } + break; + case OP_DIV: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] /= stack[stack_ptr]; + } + break; + case OP_MOD: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] %= stack[stack_ptr]; + } + break; + case OP_SHL: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] <<= stack[stack_ptr]; + } + break; + case OP_SHR: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] >>= stack[stack_ptr]; + } + break; + case OP_NOT: + if (altera_check_stack(stack_ptr, 1, &status)) + stack[stack_ptr - 1] ^= (-1L); + + break; + case OP_AND: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] &= stack[stack_ptr]; + } + break; + case OP_OR: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] |= stack[stack_ptr]; + } + break; + case OP_XOR: + if (altera_check_stack(stack_ptr, 2, &status)) { + --stack_ptr; + stack[stack_ptr - 1] ^= stack[stack_ptr]; + } + break; + case OP_INV: + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + stack[stack_ptr - 1] = stack[stack_ptr - 1] ? 0L : 1L; + break; + case OP_GT: + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + --stack_ptr; + stack[stack_ptr - 1] = + (stack[stack_ptr - 1] > stack[stack_ptr]) ? + 1L : 0L; + + break; + case OP_LT: + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + --stack_ptr; + stack[stack_ptr - 1] = + (stack[stack_ptr - 1] < stack[stack_ptr]) ? + 1L : 0L; + + break; + case OP_RET: + if ((version > 0) && (stack_ptr == 0)) { + /* + * We completed one of the main procedures + * of an ACTION. + * Find the next procedure + * to be executed and jump to it. + * If there are no more procedures, then EXIT. + */ + i = get_unaligned_be32(&p[proc_table + + (13 * current_proc) + 4]); + while ((i != 0) && + ((proc_attributes[i] == 1) || + ((proc_attributes[i] & 0xc0) == 0x40))) + i = get_unaligned_be32(&p[proc_table + + (13 * i) + 4]); + + if (i == 0) { + /* no procedures to execute! */ + done = 1; + *exit_code = 0; /* success */ + } else { + current_proc = i; + pc = code_sect + get_unaligned_be32( + &p[proc_table + + (13 * i) + 9]); + if ((pc < code_sect) || + (pc >= debug_sect)) + status = -ERANGE; + } + + } else + if (altera_check_stack(stack_ptr, 1, &status)) { + pc = stack[--stack_ptr] + code_sect; + if ((pc <= code_sect) || + (pc >= debug_sect)) + status = -ERANGE; + + } + + break; + case OP_CMPS: + /* + * Array short compare + * ...stack 0 is source 1 value + * ...stack 1 is source 2 value + * ...stack 2 is mask value + * ...stack 3 is count + */ + if (altera_check_stack(stack_ptr, 4, &status)) { + s32 a = stack[--stack_ptr]; + s32 b = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + count = stack[stack_ptr - 1]; + + if ((count < 1) || (count > 32)) + status = -ERANGE; + else { + long_tmp &= ((-1L) >> (32 - count)); + + stack[stack_ptr - 1] = + ((a & long_tmp) == (b & long_tmp)) + ? 1L : 0L; + } + } + break; + case OP_PINT: + /* + * PRINT add integer + * ...stack 0 is integer value + */ + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + sprintf(&msg_buff[strlen(msg_buff)], + "%ld", stack[--stack_ptr]); + break; + case OP_PRNT: + /* PRINT finish */ + if (debug) + printk(msg_buff, "\n"); + + msg_buff[0] = '\0'; + break; + case OP_DSS: + /* + * DRSCAN short + * ...stack 0 is scan data + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + count = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_drscan(astate, count, charbuf, 0); + break; + case OP_DSSC: + /* + * DRSCAN short with capture + * ...stack 0 is scan data + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + count = stack[stack_ptr - 1]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_swap_dr(astate, count, charbuf, + 0, charbuf, 0); + stack[stack_ptr - 1] = get_unaligned_le32(&charbuf[0]); + break; + case OP_ISS: + /* + * IRSCAN short + * ...stack 0 is scan data + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + count = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_irscan(astate, count, charbuf, 0); + break; + case OP_ISSC: + /* + * IRSCAN short with capture + * ...stack 0 is scan data + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + count = stack[stack_ptr - 1]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_swap_ir(astate, count, charbuf, + 0, charbuf, 0); + stack[stack_ptr - 1] = get_unaligned_le32(&charbuf[0]); + break; + case OP_DPR: + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + count = stack[--stack_ptr]; + status = altera_set_dr_pre(&astate->js, count, 0, NULL); + break; + case OP_DPRL: + /* + * DRPRE with literal data + * ...stack 0 is count + * ...stack 1 is literal data + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + count = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_set_dr_pre(&astate->js, count, 0, + charbuf); + break; + case OP_DPO: + /* + * DRPOST + * ...stack 0 is count + */ + if (altera_check_stack(stack_ptr, 1, &status)) { + count = stack[--stack_ptr]; + status = altera_set_dr_post(&astate->js, count, + 0, NULL); + } + break; + case OP_DPOL: + /* + * DRPOST with literal data + * ...stack 0 is count + * ...stack 1 is literal data + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + count = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_set_dr_post(&astate->js, count, 0, + charbuf); + break; + case OP_IPR: + if (altera_check_stack(stack_ptr, 1, &status)) { + count = stack[--stack_ptr]; + status = altera_set_ir_pre(&astate->js, count, + 0, NULL); + } + break; + case OP_IPRL: + /* + * IRPRE with literal data + * ...stack 0 is count + * ...stack 1 is literal data + */ + if (altera_check_stack(stack_ptr, 2, &status)) { + count = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_set_ir_pre(&astate->js, count, + 0, charbuf); + } + break; + case OP_IPO: + /* + * IRPOST + * ...stack 0 is count + */ + if (altera_check_stack(stack_ptr, 1, &status)) { + count = stack[--stack_ptr]; + status = altera_set_ir_post(&astate->js, count, + 0, NULL); + } + break; + case OP_IPOL: + /* + * IRPOST with literal data + * ...stack 0 is count + * ...stack 1 is literal data + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + count = stack[--stack_ptr]; + long_tmp = stack[--stack_ptr]; + put_unaligned_le32(long_tmp, &charbuf[0]); + status = altera_set_ir_post(&astate->js, count, 0, + charbuf); + break; + case OP_PCHR: + if (altera_check_stack(stack_ptr, 1, &status)) { + u8 ch; + count = strlen(msg_buff); + ch = (char) stack[--stack_ptr]; + if ((ch < 1) || (ch > 127)) { + /* + * character code out of range + * instead of flagging an error, + * force the value to 127 + */ + ch = 127; + } + msg_buff[count] = ch; + msg_buff[count + 1] = '\0'; + } + break; + case OP_EXIT: + if (altera_check_stack(stack_ptr, 1, &status)) + *exit_code = stack[--stack_ptr]; + + done = 1; + break; + case OP_EQU: + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + --stack_ptr; + stack[stack_ptr - 1] = + (stack[stack_ptr - 1] == stack[stack_ptr]) ? + 1L : 0L; + break; + case OP_POPT: + if (altera_check_stack(stack_ptr, 1, &status)) + --stack_ptr; + + break; + case OP_ABS: + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + if (stack[stack_ptr - 1] < 0) + stack[stack_ptr - 1] = 0 - stack[stack_ptr - 1]; + + break; + case OP_BCH0: + /* + * Batch operation 0 + * SWP + * SWPN 7 + * SWP + * SWPN 6 + * DUPN 8 + * SWPN 2 + * SWP + * DUPN 6 + * DUPN 6 + */ + + /* SWP */ + if (altera_check_stack(stack_ptr, 2, &status)) { + long_tmp = stack[stack_ptr - 2]; + stack[stack_ptr - 2] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* SWPN 7 */ + index = 7 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + long_tmp = stack[stack_ptr - index]; + stack[stack_ptr - index] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* SWP */ + if (altera_check_stack(stack_ptr, 2, &status)) { + long_tmp = stack[stack_ptr - 2]; + stack[stack_ptr - 2] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* SWPN 6 */ + index = 6 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + long_tmp = stack[stack_ptr - index]; + stack[stack_ptr - index] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* DUPN 8 */ + index = 8 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + stack[stack_ptr] = stack[stack_ptr - index]; + ++stack_ptr; + } + + /* SWPN 2 */ + index = 2 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + long_tmp = stack[stack_ptr - index]; + stack[stack_ptr - index] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* SWP */ + if (altera_check_stack(stack_ptr, 2, &status)) { + long_tmp = stack[stack_ptr - 2]; + stack[stack_ptr - 2] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + + /* DUPN 6 */ + index = 6 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + stack[stack_ptr] = stack[stack_ptr - index]; + ++stack_ptr; + } + + /* DUPN 6 */ + index = 6 + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + stack[stack_ptr] = stack[stack_ptr - index]; + ++stack_ptr; + } + break; + case OP_PSH0: + stack[stack_ptr++] = 0; + break; + case OP_PSHL: + stack[stack_ptr++] = (s32) args[0]; + break; + case OP_PSHV: + stack[stack_ptr++] = vars[args[0]]; + break; + case OP_JMP: + pc = args[0] + code_sect; + if ((pc < code_sect) || (pc >= debug_sect)) + status = -ERANGE; + break; + case OP_CALL: + stack[stack_ptr++] = pc; + pc = args[0] + code_sect; + if ((pc < code_sect) || (pc >= debug_sect)) + status = -ERANGE; + break; + case OP_NEXT: + /* + * Process FOR / NEXT loop + * ...argument 0 is variable ID + * ...stack 0 is step value + * ...stack 1 is end value + * ...stack 2 is top address + */ + if (altera_check_stack(stack_ptr, 3, &status)) { + s32 step = stack[stack_ptr - 1]; + s32 end = stack[stack_ptr - 2]; + s32 top = stack[stack_ptr - 3]; + s32 iterator = vars[args[0]]; + int break_out = 0; + + if (step < 0) { + if (iterator <= end) + break_out = 1; + } else if (iterator >= end) + break_out = 1; + + if (break_out) { + stack_ptr -= 3; + } else { + vars[args[0]] = iterator + step; + pc = top + code_sect; + if ((pc < code_sect) || + (pc >= debug_sect)) + status = -ERANGE; + } + } + break; + case OP_PSTR: + /* + * PRINT add string + * ...argument 0 is string ID + */ + count = strlen(msg_buff); + strlcpy(&msg_buff[count], + &p[str_table + args[0]], + ALTERA_MESSAGE_LENGTH - count); + break; + case OP_SINT: + /* + * STATE intermediate state + * ...argument 0 is state code + */ + status = altera_goto_jstate(astate, args[0]); + break; + case OP_ST: + /* + * STATE final state + * ...argument 0 is state code + */ + status = altera_goto_jstate(astate, args[0]); + break; + case OP_ISTP: + /* + * IRSTOP state + * ...argument 0 is state code + */ + status = altera_set_irstop(&astate->js, args[0]); + break; + case OP_DSTP: + /* + * DRSTOP state + * ...argument 0 is state code + */ + status = altera_set_drstop(&astate->js, args[0]); + break; + + case OP_SWPN: + /* + * Exchange top with Nth stack value + * ...argument 0 is 0-based stack entry + * to swap with top element + */ + index = (args[0]) + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + long_tmp = stack[stack_ptr - index]; + stack[stack_ptr - index] = stack[stack_ptr - 1]; + stack[stack_ptr - 1] = long_tmp; + } + break; + case OP_DUPN: + /* + * Duplicate Nth stack value + * ...argument 0 is 0-based stack entry to duplicate + */ + index = (args[0]) + 1; + if (altera_check_stack(stack_ptr, index, &status)) { + stack[stack_ptr] = stack[stack_ptr - index]; + ++stack_ptr; + } + break; + case OP_POPV: + /* + * Pop stack into scalar variable + * ...argument 0 is variable ID + * ...stack 0 is value + */ + if (altera_check_stack(stack_ptr, 1, &status)) + vars[args[0]] = stack[--stack_ptr]; + + break; + case OP_POPE: + /* + * Pop stack into integer array element + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is value + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + variable_id = args[0]; + + /* + * If variable is read-only, + * convert to writable array + */ + if ((version > 0) && + ((attrs[variable_id] & 0x9c) == 0x1c)) { + /* Allocate a writable buffer for this array */ + count = var_size[variable_id]; + long_tmp = vars[variable_id]; + longptr_tmp = kzalloc(count * sizeof(long), + GFP_KERNEL); + vars[variable_id] = (long)longptr_tmp; + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* copy previous contents into buffer */ + for (i = 0; i < count; ++i) { + longptr_tmp[i] = + get_unaligned_be32(&p[long_tmp]); + long_tmp += sizeof(long); + } + + /* + * set bit 7 - buffer was + * dynamically allocated + */ + attrs[variable_id] |= 0x80; + + /* clear bit 2 - variable is writable */ + attrs[variable_id] &= ~0x04; + attrs[variable_id] |= 0x01; + + } + + /* check that variable is a writable integer array */ + if ((attrs[variable_id] & 0x1c) != 0x18) + status = -ERANGE; + else { + longptr_tmp = (long *)vars[variable_id]; + + /* pop the array index */ + index = stack[--stack_ptr]; + + /* pop the value and store it into the array */ + longptr_tmp[index] = stack[--stack_ptr]; + } + + break; + case OP_POPA: + /* + * Pop stack into Boolean array + * ...argument 0 is variable ID + * ...stack 0 is count + * ...stack 1 is array index + * ...stack 2 is value + */ + if (!altera_check_stack(stack_ptr, 3, &status)) + break; + variable_id = args[0]; + + /* + * If variable is read-only, + * convert to writable array + */ + if ((version > 0) && + ((attrs[variable_id] & 0x9c) == 0x0c)) { + /* Allocate a writable buffer for this array */ + long_tmp = + (var_size[variable_id] + 7L) >> 3L; + charptr_tmp2 = (u8 *)vars[variable_id]; + charptr_tmp = + kzalloc(long_tmp, GFP_KERNEL); + vars[variable_id] = (long)charptr_tmp; + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* zero the buffer */ + for (long_idx = 0L; + long_idx < long_tmp; + ++long_idx) { + charptr_tmp[long_idx] = 0; + } + + /* copy previous contents into buffer */ + for (long_idx = 0L; + long_idx < var_size[variable_id]; + ++long_idx) { + long_idx2 = long_idx; + + if (charptr_tmp2[long_idx2 >> 3] & + (1 << (long_idx2 & 7))) { + charptr_tmp[long_idx >> 3] |= + (1 << (long_idx & 7)); + } + } + + /* + * set bit 7 - buffer was + * dynamically allocated + */ + attrs[variable_id] |= 0x80; + + /* clear bit 2 - variable is writable */ + attrs[variable_id] &= ~0x04; + attrs[variable_id] |= 0x01; + + } + + /* + * check that variable is + * a writable Boolean array + */ + if ((attrs[variable_id] & 0x1c) != 0x08) { + status = -ERANGE; + break; + } + + charptr_tmp = (u8 *)vars[variable_id]; + + /* pop the count (number of bits to copy) */ + long_count = stack[--stack_ptr]; + + /* pop the array index */ + long_idx = stack[--stack_ptr]; + + reverse = 0; + + if (version > 0) { + /* + * stack 0 = array right index + * stack 1 = array left index + */ + + if (long_idx > long_count) { + reverse = 1; + long_tmp = long_count; + long_count = 1 + long_idx - + long_count; + long_idx = long_tmp; + + /* reverse POPA is not supported */ + status = -ERANGE; + break; + } else + long_count = 1 + long_count - + long_idx; + + } + + /* pop the data */ + long_tmp = stack[--stack_ptr]; + + if (long_count < 1) { + status = -ERANGE; + break; + } + + for (i = 0; i < long_count; ++i) { + if (long_tmp & (1L << (s32) i)) + charptr_tmp[long_idx >> 3L] |= + (1L << (long_idx & 7L)); + else + charptr_tmp[long_idx >> 3L] &= + ~(1L << (long_idx & 7L)); + + ++long_idx; + } + + break; + case OP_JMPZ: + /* + * Pop stack and branch if zero + * ...argument 0 is address + * ...stack 0 is condition value + */ + if (altera_check_stack(stack_ptr, 1, &status)) { + if (stack[--stack_ptr] == 0) { + pc = args[0] + code_sect; + if ((pc < code_sect) || + (pc >= debug_sect)) + status = -ERANGE; + } + } + break; + case OP_DS: + case OP_IS: + /* + * DRSCAN + * IRSCAN + * ...argument 0 is scan data variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_idx = stack[--stack_ptr]; + long_count = stack[--stack_ptr]; + reverse = 0; + if (version > 0) { + /* + * stack 0 = array right index + * stack 1 = array left index + * stack 2 = count + */ + long_tmp = long_count; + long_count = stack[--stack_ptr]; + + if (long_idx > long_tmp) { + reverse = 1; + long_idx = long_tmp; + } + } + + charptr_tmp = (u8 *)vars[args[0]]; + + if (reverse) { + /* + * allocate a buffer + * and reverse the data order + */ + charptr_tmp2 = charptr_tmp; + charptr_tmp = kzalloc((long_count >> 3) + 1, + GFP_KERNEL); + if (charptr_tmp == NULL) { + status = -ENOMEM; + break; + } + + long_tmp = long_idx + long_count - 1; + long_idx2 = 0; + while (long_idx2 < long_count) { + if (charptr_tmp2[long_tmp >> 3] & + (1 << (long_tmp & 7))) + charptr_tmp[long_idx2 >> 3] |= + (1 << (long_idx2 & 7)); + else + charptr_tmp[long_idx2 >> 3] &= + ~(1 << (long_idx2 & 7)); + + --long_tmp; + ++long_idx2; + } + } + + if (opcode == 0x51) /* DS */ + status = altera_drscan(astate, long_count, + charptr_tmp, long_idx); + else /* IS */ + status = altera_irscan(astate, long_count, + charptr_tmp, long_idx); + + if (reverse) + kfree(charptr_tmp); + + break; + case OP_DPRA: + /* + * DRPRE with array data + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + index = stack[--stack_ptr]; + count = stack[--stack_ptr]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + charptr_tmp = (u8 *)vars[args[0]]; + status = altera_set_dr_pre(&astate->js, count, index, + charptr_tmp); + break; + case OP_DPOA: + /* + * DRPOST with array data + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + index = stack[--stack_ptr]; + count = stack[--stack_ptr]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + charptr_tmp = (u8 *)vars[args[0]]; + status = altera_set_dr_post(&astate->js, count, index, + charptr_tmp); + break; + case OP_IPRA: + /* + * IRPRE with array data + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + index = stack[--stack_ptr]; + count = stack[--stack_ptr]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + charptr_tmp = (u8 *)vars[args[0]]; + status = altera_set_ir_pre(&astate->js, count, index, + charptr_tmp); + + break; + case OP_IPOA: + /* + * IRPOST with array data + * ...argument 0 is variable ID + * ...stack 0 is array index + * ...stack 1 is count + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + index = stack[--stack_ptr]; + count = stack[--stack_ptr]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + charptr_tmp = (u8 *)vars[args[0]]; + status = altera_set_ir_post(&astate->js, count, index, + charptr_tmp); + + break; + case OP_EXPT: + /* + * EXPORT + * ...argument 0 is string ID + * ...stack 0 is integer expression + */ + if (altera_check_stack(stack_ptr, 1, &status)) { + name = &p[str_table + args[0]]; + long_tmp = stack[--stack_ptr]; + altera_export_int(name, long_tmp); + } + break; + case OP_PSHE: + /* + * Push integer array element + * ...argument 0 is variable ID + * ...stack 0 is array index + */ + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + variable_id = args[0]; + index = stack[stack_ptr - 1]; + + /* check variable type */ + if ((attrs[variable_id] & 0x1f) == 0x19) { + /* writable integer array */ + longptr_tmp = (long *)vars[variable_id]; + stack[stack_ptr - 1] = longptr_tmp[index]; + } else if ((attrs[variable_id] & 0x1f) == 0x1c) { + /* read-only integer array */ + long_tmp = vars[variable_id] + + (index * sizeof(long)); + stack[stack_ptr - 1] = + get_unaligned_be32(&p[long_tmp]); + } else + status = -ERANGE; + + break; + case OP_PSHA: + /* + * Push Boolean array + * ...argument 0 is variable ID + * ...stack 0 is count + * ...stack 1 is array index + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + variable_id = args[0]; + + /* check that variable is a Boolean array */ + if ((attrs[variable_id] & 0x18) != 0x08) { + status = -ERANGE; + break; + } + + charptr_tmp = (u8 *)vars[variable_id]; + + /* pop the count (number of bits to copy) */ + count = stack[--stack_ptr]; + + /* pop the array index */ + index = stack[stack_ptr - 1]; + + if (version > 0) + /* + * stack 0 = array right index + * stack 1 = array left index + */ + count = 1 + count - index; + + if ((count < 1) || (count > 32)) { + status = -ERANGE; + break; + } + + long_tmp = 0L; + + for (i = 0; i < count; ++i) + if (charptr_tmp[(i + index) >> 3] & + (1 << ((i + index) & 7))) + long_tmp |= (1L << i); + + stack[stack_ptr - 1] = long_tmp; + + break; + case OP_DYNA: + /* + * Dynamically change size of array + * ...argument 0 is variable ID + * ...stack 0 is new size + */ + if (!altera_check_stack(stack_ptr, 1, &status)) + break; + variable_id = args[0]; + long_tmp = stack[--stack_ptr]; + + if (long_tmp > var_size[variable_id]) { + var_size[variable_id] = long_tmp; + + if (attrs[variable_id] & 0x10) + /* allocate integer array */ + long_tmp *= sizeof(long); + else + /* allocate Boolean array */ + long_tmp = (long_tmp + 7) >> 3; + + /* + * If the buffer was previously allocated, + * free it + */ + if (attrs[variable_id] & 0x80) { + kfree((void *)vars[variable_id]); + vars[variable_id] = 0; + } + + /* + * Allocate a new buffer + * of the requested size + */ + vars[variable_id] = (long) + kzalloc(long_tmp, GFP_KERNEL); + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* + * Set the attribute bit to indicate that + * this buffer was dynamically allocated and + * should be freed later + */ + attrs[variable_id] |= 0x80; + + /* zero out memory */ + count = ((var_size[variable_id] + 7L) / + 8L); + charptr_tmp = (u8 *)(vars[variable_id]); + for (index = 0; index < count; ++index) + charptr_tmp[index] = 0; + + } + + break; + case OP_EXPV: + /* + * Export Boolean array + * ...argument 0 is string ID + * ...stack 0 is variable ID + * ...stack 1 is array right index + * ...stack 2 is array left index + */ + if (!altera_check_stack(stack_ptr, 3, &status)) + break; + if (version == 0) { + /* EXPV is not supported in JBC 1.0 */ + bad_opcode = 1; + break; + } + name = &p[str_table + args[0]]; + variable_id = stack[--stack_ptr]; + long_idx = stack[--stack_ptr];/* right indx */ + long_idx2 = stack[--stack_ptr];/* left indx */ + + if (long_idx > long_idx2) { + /* reverse indices not supported */ + status = -ERANGE; + break; + } + + long_count = 1 + long_idx2 - long_idx; + + charptr_tmp = (u8 *)vars[variable_id]; + charptr_tmp2 = NULL; + + if ((long_idx & 7L) != 0) { + s32 k = long_idx; + charptr_tmp2 = + kzalloc(((long_count + 7L) / 8L), + GFP_KERNEL); + if (charptr_tmp2 == NULL) { + status = -ENOMEM; + break; + } + + for (i = 0; i < long_count; ++i) { + if (charptr_tmp[k >> 3] & + (1 << (k & 7))) + charptr_tmp2[i >> 3] |= + (1 << (i & 7)); + else + charptr_tmp2[i >> 3] &= + ~(1 << (i & 7)); + + ++k; + } + charptr_tmp = charptr_tmp2; + + } else if (long_idx != 0) + charptr_tmp = &charptr_tmp[long_idx >> 3]; + + altera_export_bool_array(name, charptr_tmp, + long_count); + + /* free allocated buffer */ + if ((long_idx & 7L) != 0) + kfree(charptr_tmp2); + + break; + case OP_COPY: { + /* + * Array copy + * ...argument 0 is dest ID + * ...argument 1 is source ID + * ...stack 0 is count + * ...stack 1 is dest index + * ...stack 2 is source index + */ + s32 copy_count; + s32 copy_index; + s32 copy_index2; + s32 destleft; + s32 src_count; + s32 dest_count; + int src_reverse = 0; + int dest_reverse = 0; + + if (!altera_check_stack(stack_ptr, 3, &status)) + break; + + copy_count = stack[--stack_ptr]; + copy_index = stack[--stack_ptr]; + copy_index2 = stack[--stack_ptr]; + reverse = 0; + + if (version > 0) { + /* + * stack 0 = source right index + * stack 1 = source left index + * stack 2 = destination right index + * stack 3 = destination left index + */ + destleft = stack[--stack_ptr]; + + if (copy_count > copy_index) { + src_reverse = 1; + reverse = 1; + src_count = 1 + copy_count - copy_index; + /* copy_index = source start index */ + } else { + src_count = 1 + copy_index - copy_count; + /* source start index */ + copy_index = copy_count; + } + + if (copy_index2 > destleft) { + dest_reverse = 1; + reverse = !reverse; + dest_count = 1 + copy_index2 - destleft; + /* destination start index */ + copy_index2 = destleft; + } else + dest_count = 1 + destleft - copy_index2; + + copy_count = (src_count < dest_count) ? + src_count : dest_count; + + if ((src_reverse || dest_reverse) && + (src_count != dest_count)) + /* + * If either the source or destination + * is reversed, we can't tolerate + * a length mismatch, because we + * "left justify" arrays when copying. + * This won't work correctly + * with reversed arrays. + */ + status = -ERANGE; + + } + + count = copy_count; + index = copy_index; + index2 = copy_index2; + + /* + * If destination is a read-only array, + * allocate a buffer and convert it to a writable array + */ + variable_id = args[1]; + if ((version > 0) && + ((attrs[variable_id] & 0x9c) == 0x0c)) { + /* Allocate a writable buffer for this array */ + long_tmp = + (var_size[variable_id] + 7L) >> 3L; + charptr_tmp2 = (u8 *)vars[variable_id]; + charptr_tmp = + kzalloc(long_tmp, GFP_KERNEL); + vars[variable_id] = (long)charptr_tmp; + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* zero the buffer */ + for (long_idx = 0L; long_idx < long_tmp; + ++long_idx) + charptr_tmp[long_idx] = 0; + + /* copy previous contents into buffer */ + for (long_idx = 0L; + long_idx < var_size[variable_id]; + ++long_idx) { + long_idx2 = long_idx; + + if (charptr_tmp2[long_idx2 >> 3] & + (1 << (long_idx2 & 7))) + charptr_tmp[long_idx >> 3] |= + (1 << (long_idx & 7)); + + } + + /* + set bit 7 - buffer was dynamically allocated */ + attrs[variable_id] |= 0x80; + + /* clear bit 2 - variable is writable */ + attrs[variable_id] &= ~0x04; + attrs[variable_id] |= 0x01; + } + + charptr_tmp = (u8 *)vars[args[1]]; + charptr_tmp2 = (u8 *)vars[args[0]]; + + /* check if destination is a writable Boolean array */ + if ((attrs[args[1]] & 0x1c) != 0x08) { + status = -ERANGE; + break; + } + + if (count < 1) { + status = -ERANGE; + break; + } + + if (reverse) + index2 += (count - 1); + + for (i = 0; i < count; ++i) { + if (charptr_tmp2[index >> 3] & + (1 << (index & 7))) + charptr_tmp[index2 >> 3] |= + (1 << (index2 & 7)); + else + charptr_tmp[index2 >> 3] &= + ~(1 << (index2 & 7)); + + ++index; + if (reverse) + --index2; + else + ++index2; + } + + break; + } + case OP_DSC: + case OP_ISC: { + /* + * DRSCAN with capture + * IRSCAN with capture + * ...argument 0 is scan data variable ID + * ...argument 1 is capture variable ID + * ...stack 0 is capture index + * ...stack 1 is scan data index + * ...stack 2 is count + */ + s32 scan_right, scan_left; + s32 capture_count = 0; + s32 scan_count = 0; + s32 capture_index; + s32 scan_index; + + if (!altera_check_stack(stack_ptr, 3, &status)) + break; + + capture_index = stack[--stack_ptr]; + scan_index = stack[--stack_ptr]; + + if (version > 0) { + /* + * stack 0 = capture right index + * stack 1 = capture left index + * stack 2 = scan right index + * stack 3 = scan left index + * stack 4 = count + */ + scan_right = stack[--stack_ptr]; + scan_left = stack[--stack_ptr]; + capture_count = 1 + scan_index - capture_index; + scan_count = 1 + scan_left - scan_right; + scan_index = scan_right; + } + + long_count = stack[--stack_ptr]; + /* + * If capture array is read-only, allocate a buffer + * and convert it to a writable array + */ + variable_id = args[1]; + if ((version > 0) && + ((attrs[variable_id] & 0x9c) == 0x0c)) { + /* Allocate a writable buffer for this array */ + long_tmp = + (var_size[variable_id] + 7L) >> 3L; + charptr_tmp2 = (u8 *)vars[variable_id]; + charptr_tmp = + kzalloc(long_tmp, GFP_KERNEL); + vars[variable_id] = (long)charptr_tmp; + + if (vars[variable_id] == 0) { + status = -ENOMEM; + break; + } + + /* zero the buffer */ + for (long_idx = 0L; long_idx < long_tmp; + ++long_idx) + charptr_tmp[long_idx] = 0; + + /* copy previous contents into buffer */ + for (long_idx = 0L; + long_idx < var_size[variable_id]; + ++long_idx) { + long_idx2 = long_idx; + + if (charptr_tmp2[long_idx2 >> 3] & + (1 << (long_idx2 & 7))) + charptr_tmp[long_idx >> 3] |= + (1 << (long_idx & 7)); + + } + + /* + * set bit 7 - buffer was + * dynamically allocated + */ + attrs[variable_id] |= 0x80; + + /* clear bit 2 - variable is writable */ + attrs[variable_id] &= ~0x04; + attrs[variable_id] |= 0x01; + + } + + charptr_tmp = (u8 *)vars[args[0]]; + charptr_tmp2 = (u8 *)vars[args[1]]; + + if ((version > 0) && + ((long_count > capture_count) || + (long_count > scan_count))) { + status = -ERANGE; + break; + } + + /* + * check that capture array + * is a writable Boolean array + */ + if ((attrs[args[1]] & 0x1c) != 0x08) { + status = -ERANGE; + break; + } + + if (status == 0) { + if (opcode == 0x82) /* DSC */ + status = altera_swap_dr(astate, + long_count, + charptr_tmp, + scan_index, + charptr_tmp2, + capture_index); + else /* ISC */ + status = altera_swap_ir(astate, + long_count, + charptr_tmp, + scan_index, + charptr_tmp2, + capture_index); + + } + + break; + } + case OP_WAIT: + /* + * WAIT + * ...argument 0 is wait state + * ...argument 1 is end state + * ...stack 0 is cycles + * ...stack 1 is microseconds + */ + if (!altera_check_stack(stack_ptr, 2, &status)) + break; + long_tmp = stack[--stack_ptr]; + + if (long_tmp != 0L) + status = altera_wait_cycles(astate, long_tmp, + args[0]); + + long_tmp = stack[--stack_ptr]; + + if ((status == 0) && (long_tmp != 0L)) + status = altera_wait_msecs(astate, + long_tmp, + args[0]); + + if ((status == 0) && (args[1] != args[0])) + status = altera_goto_jstate(astate, + args[1]); + + if (version > 0) { + --stack_ptr; /* throw away MAX cycles */ + --stack_ptr; /* throw away MAX microseconds */ + } + break; + case OP_CMPA: { + /* + * Array compare + * ...argument 0 is source 1 ID + * ...argument 1 is source 2 ID + * ...argument 2 is mask ID + * ...stack 0 is source 1 index + * ...stack 1 is source 2 index + * ...stack 2 is mask index + * ...stack 3 is count + */ + s32 a, b; + u8 *source1 = (u8 *)vars[args[0]]; + u8 *source2 = (u8 *)vars[args[1]]; + u8 *mask = (u8 *)vars[args[2]]; + u32 index1; + u32 index2; + u32 mask_index; + + if (!altera_check_stack(stack_ptr, 4, &status)) + break; + + index1 = stack[--stack_ptr]; + index2 = stack[--stack_ptr]; + mask_index = stack[--stack_ptr]; + long_count = stack[--stack_ptr]; + + if (version > 0) { + /* + * stack 0 = source 1 right index + * stack 1 = source 1 left index + * stack 2 = source 2 right index + * stack 3 = source 2 left index + * stack 4 = mask right index + * stack 5 = mask left index + */ + s32 mask_right = stack[--stack_ptr]; + s32 mask_left = stack[--stack_ptr]; + /* source 1 count */ + a = 1 + index2 - index1; + /* source 2 count */ + b = 1 + long_count - mask_index; + a = (a < b) ? a : b; + /* mask count */ + b = 1 + mask_left - mask_right; + a = (a < b) ? a : b; + /* source 2 start index */ + index2 = mask_index; + /* mask start index */ + mask_index = mask_right; + long_count = a; + } + + long_tmp = 1L; + + if (long_count < 1) + status = -ERANGE; + else { + count = long_count; + + for (i = 0; i < count; ++i) { + if (mask[mask_index >> 3] & + (1 << (mask_index & 7))) { + a = source1[index1 >> 3] & + (1 << (index1 & 7)) + ? 1 : 0; + b = source2[index2 >> 3] & + (1 << (index2 & 7)) + ? 1 : 0; + + if (a != b) /* failure */ + long_tmp = 0L; + } + ++index1; + ++index2; + ++mask_index; + } + } + + stack[stack_ptr++] = long_tmp; + + break; + } + default: + /* Unrecognized opcode -- ERROR! */ + bad_opcode = 1; + break; + } + + if (bad_opcode) + status = -ENOSYS; + + if ((stack_ptr < 0) || (stack_ptr >= ALTERA_STACK_SIZE)) + status = -EOVERFLOW; + + if (status != 0) { + done = 1; + *error_address = (s32)(opcode_address - code_sect); + } + } + + altera_free_buffers(astate); + + /* Free all dynamically allocated arrays */ + if ((attrs != NULL) && (vars != NULL)) + for (i = 0; i < sym_count; ++i) + if (attrs[i] & 0x80) + kfree((void *)vars[i]); + + kfree(vars); + kfree(var_size); + kfree(attrs); + kfree(proc_attributes); + + return status; +} + +static int altera_get_note(u8 *p, s32 program_size, + s32 *offset, char *key, char *value, int length) +/* + * Gets key and value of NOTE fields in the JBC file. + * Can be called in two modes: if offset pointer is NULL, + * then the function searches for note fields which match + * the key string provided. If offset is not NULL, then + * the function finds the next note field of any key, + * starting at the offset specified by the offset pointer. + * Returns 0 for success, else appropriate error code + */ +{ + int status = -ENODATA; + u32 note_strings = 0L; + u32 note_table = 0L; + u32 note_count = 0L; + u32 first_word = 0L; + int version = 0; + int delta = 0; + char *key_ptr; + char *value_ptr; + int i; + + /* Read header information */ + if (program_size > 52L) { + first_word = get_unaligned_be32(&p[0]); + version = (first_word & 1L); + delta = version * 8; + + note_strings = get_unaligned_be32(&p[8 + delta]); + note_table = get_unaligned_be32(&p[12 + delta]); + note_count = get_unaligned_be32(&p[44 + (2 * delta)]); + } + + if ((first_word != 0x4A414D00L) && (first_word != 0x4A414D01L)) + return -EIO; + + if (note_count <= 0L) + return status; + + if (offset == NULL) { + /* + * We will search for the first note with a specific key, + * and return only the value + */ + for (i = 0; (i < note_count) && + (status != 0); ++i) { + key_ptr = &p[note_strings + + get_unaligned_be32( + &p[note_table + (8 * i)])]; + if ((strnicmp(key, key_ptr, strlen(key_ptr)) == 0) && + (key != NULL)) { + status = 0; + + value_ptr = &p[note_strings + + get_unaligned_be32( + &p[note_table + (8 * i) + 4])]; + + if (value != NULL) + strlcpy(value, value_ptr, length); + + } + } + } else { + /* + * We will search for the next note, regardless of the key, + * and return both the value and the key + */ + + i = *offset; + + if ((i >= 0) && (i < note_count)) { + status = 0; + + if (key != NULL) + strlcpy(key, &p[note_strings + + get_unaligned_be32( + &p[note_table + (8 * i)])], + length); + + if (value != NULL) + strlcpy(value, &p[note_strings + + get_unaligned_be32( + &p[note_table + (8 * i) + 4])], + length); + + *offset = i + 1; + } + } + + return status; +} + +static int altera_check_crc(u8 *p, s32 program_size) +{ + int status = 0; + u16 local_expected = 0, + local_actual = 0, + shift_reg = 0xffff; + int bit, feedback; + u8 databyte; + u32 i; + u32 crc_section = 0L; + u32 first_word = 0L; + int version = 0; + int delta = 0; + + if (program_size > 52L) { + first_word = get_unaligned_be32(&p[0]); + version = (first_word & 1L); + delta = version * 8; + + crc_section = get_unaligned_be32(&p[32 + delta]); + } + + if ((first_word != 0x4A414D00L) && (first_word != 0x4A414D01L)) + status = -EIO; + + if (crc_section >= program_size) + status = -EIO; + + if (status == 0) { + local_expected = (u16)get_unaligned_be16(&p[crc_section]); + + for (i = 0; i < crc_section; ++i) { + databyte = p[i]; + for (bit = 0; bit < 8; bit++) { + feedback = (databyte ^ shift_reg) & 0x01; + shift_reg >>= 1; + if (feedback) + shift_reg ^= 0x8408; + + databyte >>= 1; + } + } + + local_actual = (u16)~shift_reg; + + if (local_expected != local_actual) + status = -EILSEQ; + + } + + if (debug || status) { + switch (status) { + case 0: + printk(KERN_INFO "%s: CRC matched: %04x\n", __func__, + local_actual); + break; + case -EILSEQ: + printk(KERN_ERR "%s: CRC mismatch: expected %04x, " + "actual %04x\n", __func__, local_expected, + local_actual); + break; + case -ENODATA: + printk(KERN_ERR "%s: expected CRC not found, " + "actual CRC = %04x\n", __func__, + local_actual); + break; + case -EIO: + printk(KERN_ERR "%s: error: format isn't " + "recognized.\n", __func__); + break; + default: + printk(KERN_ERR "%s: CRC function returned error " + "code %d\n", __func__, status); + break; + } + } + + return status; +} + +static int altera_get_file_info(u8 *p, + s32 program_size, + int *format_version, + int *action_count, + int *procedure_count) +{ + int status = -EIO; + u32 first_word = 0; + int version = 0; + + if (program_size <= 52L) + return status; + + first_word = get_unaligned_be32(&p[0]); + + if ((first_word == 0x4A414D00L) || (first_word == 0x4A414D01L)) { + status = 0; + + version = (first_word & 1L); + *format_version = version + 1; + + if (version > 0) { + *action_count = get_unaligned_be32(&p[48]); + *procedure_count = get_unaligned_be32(&p[52]); + } + } + + return status; +} + +static int altera_get_act_info(u8 *p, + s32 program_size, + int index, + char **name, + char **description, + struct altera_procinfo **proc_list) +{ + int status = -EIO; + struct altera_procinfo *procptr = NULL; + struct altera_procinfo *tmpptr = NULL; + u32 first_word = 0L; + u32 action_table = 0L; + u32 proc_table = 0L; + u32 str_table = 0L; + u32 note_strings = 0L; + u32 action_count = 0L; + u32 proc_count = 0L; + u32 act_name_id = 0L; + u32 act_desc_id = 0L; + u32 act_proc_id = 0L; + u32 act_proc_name = 0L; + u8 act_proc_attribute = 0; + + if (program_size <= 52L) + return status; + /* Read header information */ + first_word = get_unaligned_be32(&p[0]); + + if (first_word != 0x4A414D01L) + return status; + + action_table = get_unaligned_be32(&p[4]); + proc_table = get_unaligned_be32(&p[8]); + str_table = get_unaligned_be32(&p[12]); + note_strings = get_unaligned_be32(&p[16]); + action_count = get_unaligned_be32(&p[48]); + proc_count = get_unaligned_be32(&p[52]); + + if (index >= action_count) + return status; + + act_name_id = get_unaligned_be32(&p[action_table + (12 * index)]); + act_desc_id = get_unaligned_be32(&p[action_table + (12 * index) + 4]); + act_proc_id = get_unaligned_be32(&p[action_table + (12 * index) + 8]); + + *name = &p[str_table + act_name_id]; + + if (act_desc_id < (note_strings - str_table)) + *description = &p[str_table + act_desc_id]; + + do { + act_proc_name = get_unaligned_be32( + &p[proc_table + (13 * act_proc_id)]); + act_proc_attribute = + (p[proc_table + (13 * act_proc_id) + 8] & 0x03); + + procptr = (struct altera_procinfo *) + kzalloc(sizeof(struct altera_procinfo), + GFP_KERNEL); + + if (procptr == NULL) + status = -ENOMEM; + else { + procptr->name = &p[str_table + act_proc_name]; + procptr->attrs = act_proc_attribute; + procptr->next = NULL; + + /* add record to end of linked list */ + if (*proc_list == NULL) + *proc_list = procptr; + else { + tmpptr = *proc_list; + while (tmpptr->next != NULL) + tmpptr = tmpptr->next; + tmpptr->next = procptr; + } + } + + act_proc_id = get_unaligned_be32( + &p[proc_table + (13 * act_proc_id) + 4]); + } while ((act_proc_id != 0) && (act_proc_id < proc_count)); + + return status; +} + +int altera_init(struct altera_config *config, const struct firmware *fw) +{ + struct altera_state *astate = NULL; + struct altera_procinfo *proc_list = NULL; + struct altera_procinfo *procptr = NULL; + char *key = NULL; + char *value = NULL; + char *action_name = NULL; + char *description = NULL; + int exec_result = 0; + int exit_code = 0; + int format_version = 0; + int action_count = 0; + int procedure_count = 0; + int index = 0; + s32 offset = 0L; + s32 error_address = 0L; + + key = kzalloc(33 * sizeof(char), GFP_KERNEL); + if (!key) + return -ENOMEM; + value = kzalloc(257 * sizeof(char), GFP_KERNEL); + if (!value) + return -ENOMEM; + astate = kzalloc(sizeof(struct altera_state), GFP_KERNEL); + if (!astate) + return -ENOMEM; + + astate->config = config; + if (!astate->config->jtag_io) { + dprintk(KERN_INFO "%s: using byteblaster!\n", __func__); + astate->config->jtag_io = netup_jtag_io_lpt; + } + + altera_check_crc((u8 *)fw->data, fw->size); + + if (debug) { + altera_get_file_info((u8 *)fw->data, fw->size, &format_version, + &action_count, &procedure_count); + printk(KERN_INFO "%s: File format is %s ByteCode format\n", + __func__, (format_version == 2) ? "Jam STAPL" : + "pre-standardized Jam 1.1"); + while (altera_get_note((u8 *)fw->data, fw->size, + &offset, key, value, 256) == 0) + printk(KERN_INFO "%s: NOTE \"%s\" = \"%s\"\n", + __func__, key, value); + } + + if (debug && (format_version == 2) && (action_count > 0)) { + printk(KERN_INFO "%s: Actions available:\n", __func__); + for (index = 0; index < action_count; ++index) { + altera_get_act_info((u8 *)fw->data, fw->size, + index, &action_name, + &description, + &proc_list); + + if (description == NULL) + printk(KERN_INFO "%s: %s\n", + __func__, + action_name); + else + printk(KERN_INFO "%s: %s \"%s\"\n", + __func__, + action_name, + description); + + procptr = proc_list; + while (procptr != NULL) { + if (procptr->attrs != 0) + printk(KERN_INFO "%s: %s (%s)\n", + __func__, + procptr->name, + (procptr->attrs == 1) ? + "optional" : "recommended"); + + proc_list = procptr->next; + kfree(procptr); + procptr = proc_list; + } + } + + printk(KERN_INFO "\n"); + } + + exec_result = altera_execute(astate, (u8 *)fw->data, fw->size, + &error_address, &exit_code, &format_version); + + if (exit_code) + exec_result = -EREMOTEIO; + + if ((format_version == 2) && (exec_result == -EINVAL)) { + if (astate->config->action == NULL) + printk(KERN_ERR "%s: error: no action specified for " + "Jam STAPL file.\nprogram terminated.\n", + __func__); + else + printk(KERN_ERR "%s: error: action \"%s\"" + " is not supported " + "for this Jam STAPL file.\n" + "Program terminated.\n", __func__, + astate->config->action); + + } else if (exec_result) + printk(KERN_ERR "%s: error %d\n", __func__, exec_result); + + kfree(key); + kfree(value); + kfree(astate); + + return 0; +} +EXPORT_SYMBOL(altera_init); diff --git a/include/staging/altera.h b/include/staging/altera.h new file mode 100644 index 0000000..94c0c61 --- /dev/null +++ b/include/staging/altera.h @@ -0,0 +1,49 @@ +/* + * altera.h + * + * altera FPGA driver + * + * Copyright (C) Altera Corporation 1998-2001 + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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 _ALTERA_H_ +#define _ALTERA_H_ + +struct altera_config { + void *dev; + u8 *action; + int (*jtag_io) (void *dev, int tms, int tdi, int tdo); +}; + +#if defined(CONFIG_ALTERA_STAPL) || \ + (defined(CONFIG_ALTERA_STAPL_MODULE) && defined(MODULE)) + +extern int altera_init(struct altera_config *config, const struct firmware *fw); +#else + +static inline int altera_init(struct altera_config *config, + const struct firmware *fw) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} +#endif /* CONFIG_ALTERA_STAPL */ + +#endif /* _ALTERA_H_ */ -- cgit v0.10.2 From 349bcf02e3615556e9c49ea73a04abab2a4d8ed7 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:01:00 -0300 Subject: [media] Altera FPGA based CI driver module An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index 6b4a516..5e5faad 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -33,3 +33,12 @@ config VIDEO_CX23885 To compile this driver as a module, choose M here: the module will be called cx23885 +config MEDIA_ALTERA_CI + tristate "Altera FPGA based CI module" + depends on VIDEO_CX23885 && DVB_CORE + select STAPL_ALTERA + ---help--- + An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card. + + To compile this driver as a module, choose M here: the + module will be called altera-ci diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index e2ee95f..23293c7 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -5,6 +5,7 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ cx23885-f300.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.o +obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/video/cx23885/altera-ci.c b/drivers/media/video/cx23885/altera-ci.c new file mode 100644 index 0000000..e5c0873 --- /dev/null +++ b/drivers/media/video/cx23885/altera-ci.c @@ -0,0 +1,830 @@ +/* + * altera-ci.c + * + * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card + * + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin + * + * 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. + */ + +/* + * currently cx23885 GPIO's used. + * GPIO-0 ~INT in + * GPIO-1 TMS out + * GPIO-2 ~reset chips out + * GPIO-3 to GPIO-10 data/addr for CA in/out + * GPIO-11 ~CS out + * GPIO-12 AD_RG out + * GPIO-13 ~WR out + * GPIO-14 ~RD out + * GPIO-15 ~RDY in + * GPIO-16 TCK out + * GPIO-17 TDO in + * GPIO-18 TDI out + */ +/* + * Bit definitions for MC417_RWD and MC417_OEN registers + * bits 31-16 + * +-----------+ + * | Reserved | + * +-----------+ + * bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | TDI | TDO | TCK | RDY# | #RD | #WR | AD_RG | #CS | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0| + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +#include +#include +#include +#include "altera-ci.h" +#include "dvb_ca_en50221.h" + +/* FPGA regs */ +#define NETUP_CI_INT_CTRL 0x00 +#define NETUP_CI_BUSCTRL2 0x01 +#define NETUP_CI_ADDR0 0x04 +#define NETUP_CI_ADDR1 0x05 +#define NETUP_CI_DATA 0x06 +#define NETUP_CI_BUSCTRL 0x07 +#define NETUP_CI_PID_ADDR0 0x08 +#define NETUP_CI_PID_ADDR1 0x09 +#define NETUP_CI_PID_DATA 0x0a +#define NETUP_CI_TSA_DIV 0x0c +#define NETUP_CI_TSB_DIV 0x0d +#define NETUP_CI_REVISION 0x0f + +/* const for ci op */ +#define NETUP_CI_FLG_CTL 1 +#define NETUP_CI_FLG_RD 1 +#define NETUP_CI_FLG_AD 1 + +static unsigned int ci_dbg; +module_param(ci_dbg, int, 0644); +MODULE_PARM_DESC(ci_dbg, "Enable CI debugging"); + +static unsigned int pid_dbg; +module_param(pid_dbg, int, 0644); +MODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging"); + +MODULE_DESCRIPTION("altera FPGA CI module"); +MODULE_AUTHOR("Igor M. Liplianin "); +MODULE_LICENSE("GPL"); + +#define ci_dbg_print(args...) \ + do { \ + if (ci_dbg) \ + printk(KERN_DEBUG args); \ + } while (0) + +#define pid_dbg_print(args...) \ + do { \ + if (pid_dbg) \ + printk(KERN_DEBUG args); \ + } while (0) + +struct altera_ci_state; +struct netup_hw_pid_filter; + +struct fpga_internal { + void *dev; + struct mutex fpga_mutex;/* two CI's on the same fpga */ + struct netup_hw_pid_filter *pid_filt[2]; + struct altera_ci_state *state[2]; + struct work_struct work; + int (*fpga_rw) (void *dev, int flag, int data, int rw); + int cis_used; + int filts_used; + int strt_wrk; +}; + +/* stores all private variables for communication with CI */ +struct altera_ci_state { + struct fpga_internal *internal; + struct dvb_ca_en50221 ca; + int status; + int nr; +}; + +/* stores all private variables for hardware pid filtering */ +struct netup_hw_pid_filter { + struct fpga_internal *internal; + struct dvb_demux *demux; + /* save old functions */ + int (*start_feed)(struct dvb_demux_feed *feed); + int (*stop_feed)(struct dvb_demux_feed *feed); + + int status; + int nr; +}; + +/* internal params node */ +struct fpga_inode { + /* pointer for internal params, one for each pair of CI's */ + struct fpga_internal *internal; + struct fpga_inode *next_inode; +}; + +/* first internal params */ +static struct fpga_inode *fpga_first_inode; + +/* find chip by dev */ +static struct fpga_inode *find_inode(void *dev) +{ + struct fpga_inode *temp_chip = fpga_first_inode; + + if (temp_chip == NULL) + return temp_chip; + + /* + Search for the last fpga CI chip or + find it by dev */ + while ((temp_chip != NULL) && + (temp_chip->internal->dev != dev)) + temp_chip = temp_chip->next_inode; + + return temp_chip; +} +/* check demux */ +static struct fpga_internal *check_filter(struct fpga_internal *temp_int, + void *demux_dev, int filt_nr) +{ + if (temp_int == NULL) + return NULL; + + if ((temp_int->pid_filt[filt_nr]) == NULL) + return NULL; + + if (temp_int->pid_filt[filt_nr]->demux == demux_dev) + return temp_int; + + return NULL; +} + +/* find chip by demux */ +static struct fpga_inode *find_dinode(void *demux_dev) +{ + struct fpga_inode *temp_chip = fpga_first_inode; + struct fpga_internal *temp_int; + + /* + * Search of the last fpga CI chip or + * find it by demux + */ + while (temp_chip != NULL) { + if (temp_chip->internal != NULL) { + temp_int = temp_chip->internal; + if (check_filter(temp_int, demux_dev, 0)) + break; + if (check_filter(temp_int, demux_dev, 1)) + break; + } + + temp_chip = temp_chip->next_inode; + } + + return temp_chip; +} + +/* deallocating chip */ +static void remove_inode(struct fpga_internal *internal) +{ + struct fpga_inode *prev_node = fpga_first_inode; + struct fpga_inode *del_node = find_inode(internal->dev); + + if (del_node != NULL) { + if (del_node == fpga_first_inode) { + fpga_first_inode = del_node->next_inode; + } else { + while (prev_node->next_inode != del_node) + prev_node = prev_node->next_inode; + + if (del_node->next_inode == NULL) + prev_node->next_inode = NULL; + else + prev_node->next_inode = + prev_node->next_inode->next_inode; + } + + kfree(del_node); + } +} + +/* allocating new chip */ +static struct fpga_inode *append_internal(struct fpga_internal *internal) +{ + struct fpga_inode *new_node = fpga_first_inode; + + if (new_node == NULL) { + new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL); + fpga_first_inode = new_node; + } else { + while (new_node->next_inode != NULL) + new_node = new_node->next_inode; + + new_node->next_inode = + kmalloc(sizeof(struct fpga_inode), GFP_KERNEL); + if (new_node->next_inode != NULL) + new_node = new_node->next_inode; + else + new_node = NULL; + } + + if (new_node != NULL) { + new_node->internal = internal; + new_node->next_inode = NULL; + } + + return new_node; +} + +static int netup_fpga_op_rw(struct fpga_internal *inter, int addr, + u8 val, u8 read) +{ + inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0); + return inter->fpga_rw(inter->dev, 0, val, read); +} + +/* flag - mem/io, read - read/write */ +int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, + u8 flag, u8 read, int addr, u8 val) +{ + + struct altera_ci_state *state = en50221->data; + struct fpga_internal *inter = state->internal; + + u8 store; + int mem = 0; + + if (0 != slot) + return -EINVAL; + + mutex_lock(&inter->fpga_mutex); + + netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0); + netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0); + store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); + + store &= 0x3f; + store |= ((state->nr << 7) | (flag << 6)); + + netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0); + mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read); + + mutex_unlock(&inter->fpga_mutex); + + ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__, + (read) ? "read" : "write", addr, + (flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem", + (read) ? mem : val); + + return mem; +} + +int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr) +{ + return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0); +} + +int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr, u8 data) +{ + return altera_ci_op_cam(en50221, slot, 0, 0, addr, data); +} + +int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr) +{ + return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, + NETUP_CI_FLG_RD, addr, 0); +} + +int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, + u8 addr, u8 data) +{ + return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data); +} + +int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) +{ + struct altera_ci_state *state = en50221->data; + struct fpga_internal *inter = state->internal; + /* reasonable timeout for CI reset is 10 seconds */ + unsigned long t_out = jiffies + msecs_to_jiffies(9999); + int ret; + + ci_dbg_print("%s\n", __func__); + + if (0 != slot) + return -EINVAL; + + mutex_lock(&inter->fpga_mutex); + + ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); + netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, + ret | (1 << (5 - state->nr)), 0); + + for (;;) { + mdelay(50); + ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, + 0, NETUP_CI_FLG_RD); + if ((ret & (1 << (5 - state->nr))) == 0) + break; + if (time_after(jiffies, t_out)) + break; + } + + mutex_unlock(&inter->fpga_mutex); + + ci_dbg_print("%s: %d msecs\n", __func__, + jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out)); + + return 0; +} + +int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) +{ + /* not implemented */ + return 0; +} + +int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot) +{ + struct altera_ci_state *state = en50221->data; + struct fpga_internal *inter = state->internal; + int ret; + + ci_dbg_print("%s\n", __func__); + + if (0 != slot) + return -EINVAL; + + mutex_lock(&inter->fpga_mutex); + + ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); + netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, + ret | (1 << (3 - state->nr)), 0); + + mutex_unlock(&inter->fpga_mutex); + + return 0; +} + +/* work handler */ +static void netup_read_ci_status(struct work_struct *work) +{ + struct fpga_internal *inter = + container_of(work, struct fpga_internal, work); + int ret; + + ci_dbg_print("%s\n", __func__); + + mutex_lock(&inter->fpga_mutex); + /* ack' irq */ + ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD); + ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); + + mutex_unlock(&inter->fpga_mutex); + + if (inter->state[1] != NULL) { + inter->state[1]->status = + ((ret & 1) == 0 ? + DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY : 0); + ci_dbg_print("%s: setting CI[1] status = 0x%x\n", + __func__, inter->state[1]->status); + }; + + if (inter->state[0] != NULL) { + inter->state[0]->status = + ((ret & 2) == 0 ? + DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY : 0); + ci_dbg_print("%s: setting CI[0] status = 0x%x\n", + __func__, inter->state[0]->status); + }; +} + +/* CI irq handler */ +int altera_ci_irq(void *dev) +{ + struct fpga_inode *temp_int = NULL; + struct fpga_internal *inter = NULL; + + ci_dbg_print("%s\n", __func__); + + if (dev != NULL) { + temp_int = find_inode(dev); + if (temp_int != NULL) { + inter = temp_int->internal; + schedule_work(&inter->work); + } + } + + return 1; +} +EXPORT_SYMBOL(altera_ci_irq); + +int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, + int open) +{ + struct altera_ci_state *state = en50221->data; + + if (0 != slot) + return -EINVAL; + + return state->status; +} + +void altera_hw_filt_release(void *main_dev, int filt_nr) +{ + struct fpga_inode *temp_int = find_inode(main_dev); + struct netup_hw_pid_filter *pid_filt = NULL; + + ci_dbg_print("%s\n", __func__); + + if (temp_int != NULL) { + pid_filt = temp_int->internal->pid_filt[filt_nr - 1]; + /* stored old feed controls */ + pid_filt->demux->start_feed = pid_filt->start_feed; + pid_filt->demux->stop_feed = pid_filt->stop_feed; + + if (((--(temp_int->internal->filts_used)) <= 0) && + ((temp_int->internal->cis_used) <= 0)) { + + ci_dbg_print("%s: Actually removing\n", __func__); + + remove_inode(temp_int->internal); + kfree(pid_filt->internal); + } + + kfree(pid_filt); + + } + +} +EXPORT_SYMBOL(altera_hw_filt_release); + +void altera_ci_release(void *dev, int ci_nr) +{ + struct fpga_inode *temp_int = find_inode(dev); + struct altera_ci_state *state = NULL; + + ci_dbg_print("%s\n", __func__); + + if (temp_int != NULL) { + state = temp_int->internal->state[ci_nr - 1]; + altera_hw_filt_release(dev, ci_nr); + + + if (((temp_int->internal->filts_used) <= 0) && + ((--(temp_int->internal->cis_used)) <= 0)) { + + ci_dbg_print("%s: Actually removing\n", __func__); + + remove_inode(temp_int->internal); + kfree(state->internal); + } + + if (state != NULL) { + if (state->ca.data != NULL) + dvb_ca_en50221_release(&state->ca); + + kfree(state); + } + } + +} +EXPORT_SYMBOL(altera_ci_release); + +static void altera_pid_control(struct netup_hw_pid_filter *pid_filt, + u16 pid, int onoff) +{ + struct fpga_internal *inter = pid_filt->internal; + u8 store = 0; + + if (pid == 0x2000) + return; + + mutex_lock(&inter->fpga_mutex); + + netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0); + netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1, + ((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0); + + store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD); + + if (onoff)/* 0 - on, 1 - off */ + store |= (1 << (pid & 7)); + else + store &= ~(1 << (pid & 7)); + + netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0); + + mutex_unlock(&inter->fpga_mutex); + + pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__, + pid_filt->nr, pid, pid, onoff ? "off" : "on"); +} + +static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt, + int filt_nr, int onoff) +{ + struct fpga_internal *inter = pid_filt->internal; + u8 store = 0; + int i; + + pid_dbg_print("%s: pid_filt->nr[%d] now %s\n", __func__, pid_filt->nr, + onoff ? "off" : "on"); + + if (onoff)/* 0 - on, 1 - off */ + store = 0xff;/* ignore pid */ + else + store = 0;/* enable pid */ + + mutex_lock(&inter->fpga_mutex); + + for (i = 0; i < 1024; i++) { + netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0); + + netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1, + ((i >> 8) & 0x03) | (pid_filt->nr << 2), 0); + + netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0); + } + + mutex_unlock(&inter->fpga_mutex); +} + +int altera_pid_feed_control(void *demux_dev, int filt_nr, + struct dvb_demux_feed *feed, int onoff) +{ + struct fpga_inode *temp_int = find_dinode(demux_dev); + struct fpga_internal *inter = temp_int->internal; + struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1]; + + altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1); + /* call old feed proc's */ + if (onoff) + pid_filt->start_feed(feed); + else + pid_filt->stop_feed(feed); + + if (feed->pid == 0x2000) + altera_toggle_fullts_streaming(pid_filt, filt_nr, + onoff ? 0 : 1); + + return 0; +} +EXPORT_SYMBOL(altera_pid_feed_control); + +int altera_ci_start_feed(struct dvb_demux_feed *feed, int num) +{ + altera_pid_feed_control(feed->demux, num, feed, 1); + + return 0; +} + +int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num) +{ + altera_pid_feed_control(feed->demux, num, feed, 0); + + return 0; +} + +int altera_ci_start_feed_1(struct dvb_demux_feed *feed) +{ + return altera_ci_start_feed(feed, 1); +} + +int altera_ci_stop_feed_1(struct dvb_demux_feed *feed) +{ + return altera_ci_stop_feed(feed, 1); +} + +int altera_ci_start_feed_2(struct dvb_demux_feed *feed) +{ + return altera_ci_start_feed(feed, 2); +} + +int altera_ci_stop_feed_2(struct dvb_demux_feed *feed) +{ + return altera_ci_stop_feed(feed, 2); +} + +int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr) +{ + struct netup_hw_pid_filter *pid_filt = NULL; + struct fpga_inode *temp_int = find_inode(config->dev); + struct fpga_internal *inter = NULL; + int ret = 0; + + pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL); + + ci_dbg_print("%s\n", __func__); + + if (!pid_filt) { + ret = -ENOMEM; + goto err; + } + + if (temp_int != NULL) { + inter = temp_int->internal; + (inter->filts_used)++; + ci_dbg_print("%s: Find Internal Structure!\n", __func__); + } else { + inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL); + if (!inter) { + ret = -ENOMEM; + goto err; + } + + temp_int = append_internal(inter); + inter->filts_used = 1; + inter->dev = config->dev; + inter->fpga_rw = config->fpga_rw; + mutex_init(&inter->fpga_mutex); + inter->strt_wrk = 1; + ci_dbg_print("%s: Create New Internal Structure!\n", __func__); + } + + ci_dbg_print("%s: setting hw pid filter = 0x%x for ci = %d\n", __func__, + (int)pid_filt, hw_filt_nr - 1); + inter->pid_filt[hw_filt_nr - 1] = pid_filt; + pid_filt->demux = config->demux; + pid_filt->internal = inter; + pid_filt->nr = hw_filt_nr - 1; + /* store old feed controls */ + pid_filt->start_feed = config->demux->start_feed; + pid_filt->stop_feed = config->demux->stop_feed; + /* replace with new feed controls */ + if (hw_filt_nr == 1) { + pid_filt->demux->start_feed = altera_ci_start_feed_1; + pid_filt->demux->stop_feed = altera_ci_stop_feed_1; + } else if (hw_filt_nr == 2) { + pid_filt->demux->start_feed = altera_ci_start_feed_2; + pid_filt->demux->stop_feed = altera_ci_stop_feed_2; + } + + altera_toggle_fullts_streaming(pid_filt, 0, 1); + + return 0; +err: + ci_dbg_print("%s: Can't init hardware filter: Error %d\n", + __func__, ret); + + kfree(pid_filt); + + return ret; +} +EXPORT_SYMBOL(altera_hw_filt_init); + +int altera_ci_init(struct altera_ci_config *config, int ci_nr) +{ + struct altera_ci_state *state; + struct fpga_inode *temp_int = find_inode(config->dev); + struct fpga_internal *inter = NULL; + int ret = 0; + u8 store = 0; + + state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL); + + ci_dbg_print("%s\n", __func__); + + if (!state) { + ret = -ENOMEM; + goto err; + } + + if (temp_int != NULL) { + inter = temp_int->internal; + (inter->cis_used)++; + ci_dbg_print("%s: Find Internal Structure!\n", __func__); + } else { + inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL); + if (!inter) { + ret = -ENOMEM; + goto err; + } + + temp_int = append_internal(inter); + inter->cis_used = 1; + inter->dev = config->dev; + inter->fpga_rw = config->fpga_rw; + mutex_init(&inter->fpga_mutex); + inter->strt_wrk = 1; + ci_dbg_print("%s: Create New Internal Structure!\n", __func__); + } + + ci_dbg_print("%s: setting state = 0x%x for ci = %d\n", __func__, + (int)state, ci_nr - 1); + inter->state[ci_nr - 1] = state; + state->internal = inter; + state->nr = ci_nr - 1; + + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = altera_ci_read_attribute_mem; + state->ca.write_attribute_mem = altera_ci_write_attribute_mem; + state->ca.read_cam_control = altera_ci_read_cam_ctl; + state->ca.write_cam_control = altera_ci_write_cam_ctl; + state->ca.slot_reset = altera_ci_slot_reset; + state->ca.slot_shutdown = altera_ci_slot_shutdown; + state->ca.slot_ts_enable = altera_ci_slot_ts_ctl; + state->ca.poll_slot_status = altera_poll_ci_slot_status; + state->ca.data = state; + + ret = dvb_ca_en50221_init(config->adapter, + &state->ca, + /* flags */ 0, + /* n_slots */ 1); + if (0 != ret) + goto err; + + altera_hw_filt_init(config, ci_nr); + + if (inter->strt_wrk) { + INIT_WORK(&inter->work, netup_read_ci_status); + inter->strt_wrk = 0; + } + + ci_dbg_print("%s: CI initialized!\n", __func__); + + mutex_lock(&inter->fpga_mutex); + + /* Enable div */ + netup_fpga_op_rw(inter, NETUP_CI_TSA_DIV, 0x0, 0); + netup_fpga_op_rw(inter, NETUP_CI_TSB_DIV, 0x0, 0); + + /* enable TS out */ + store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD); + store |= (3 << 4); + netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0); + + ret = netup_fpga_op_rw(inter, NETUP_CI_REVISION, 0, NETUP_CI_FLG_RD); + /* enable irq */ + netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0x44, 0); + + mutex_unlock(&inter->fpga_mutex); + + ci_dbg_print("%s: NetUP CI Revision = 0x%x\n", __func__, ret); + + schedule_work(&inter->work); + + return 0; +err: + ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret); + + kfree(state); + + return ret; +} +EXPORT_SYMBOL(altera_ci_init); + +int altera_ci_tuner_reset(void *dev, int ci_nr) +{ + struct fpga_inode *temp_int = find_inode(dev); + struct fpga_internal *inter = NULL; + u8 store; + + ci_dbg_print("%s\n", __func__); + + if (temp_int == NULL) + return -1; + + if (temp_int->internal == NULL) + return -1; + + inter = temp_int->internal; + + mutex_lock(&inter->fpga_mutex); + + store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD); + store &= ~(4 << (2 - ci_nr)); + netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0); + msleep(100); + store |= (4 << (2 - ci_nr)); + netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0); + + mutex_unlock(&inter->fpga_mutex); + + return 0; +} +EXPORT_SYMBOL(altera_ci_tuner_reset); diff --git a/drivers/media/video/cx23885/altera-ci.h b/drivers/media/video/cx23885/altera-ci.h new file mode 100644 index 0000000..ba0c6c0 --- /dev/null +++ b/drivers/media/video/cx23885/altera-ci.h @@ -0,0 +1,102 @@ +/* + * altera-ci.c + * + * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card + * + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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 __ALTERA_CI_H +#define __ALTERA_CI_H + +#define ALT_DATA 0x000000ff +#define ALT_TDI 0x00008000 +#define ALT_TDO 0x00004000 +#define ALT_TCK 0x00002000 +#define ALT_RDY 0x00001000 +#define ALT_RD 0x00000800 +#define ALT_WR 0x00000400 +#define ALT_AD_RG 0x00000200 +#define ALT_CS 0x00000100 + +struct altera_ci_config { + void *dev;/* main dev, for example cx23885_dev */ + void *adapter;/* for CI to connect to */ + struct dvb_demux *demux;/* for hardware PID filter to connect to */ + int (*fpga_rw) (void *dev, int ad_rg, int val, int rw); +}; + +#if defined(CONFIG_MEDIA_ALTERA_CI) || (defined(CONFIG_MEDIA_ALTERA_CI_MODULE) \ + && defined(MODULE)) + +extern int altera_ci_init(struct altera_ci_config *config, int ci_nr); +extern void altera_ci_release(void *dev, int ci_nr); +extern int altera_ci_irq(void *dev); +extern int altera_ci_tuner_reset(void *dev, int ci_nr); + +#else + +static inline int altera_ci_init(struct altera_ci_config *config, int ci_nr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +static inline void altera_ci_release(void *dev, int ci_nr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +static inline int altera_ci_irq(void *dev) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +static int altera_ci_tuner_reset(void *dev, int ci_nr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +#endif +#if 0 +static inline int altera_hw_filt_init(struct altera_ci_config *config, + int hw_filt_nr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +static inline void altera_hw_filt_release(void *dev, int filt_nr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +static inline int altera_pid_feed_control(void *dev, int filt_nr, + struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +#endif /* CONFIG_MEDIA_ALTERA_CI */ + +#endif /* __ALTERA_CI_H */ -- cgit v0.10.2 From 17cce932e8be9a3e32e1928524b73795b3f0e3aa Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:02:00 -0300 Subject: [media] Support for stv0367 multi-standard demodulator The STV0367 is a multi-standard demodulator which is capable of processing DVB-T as well as DVB-C signals. It is fully compliant with DVB-T and DVB-C recommendations for broadcast services. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index a20c153..83093d1 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -378,6 +378,13 @@ config DVB_EC100 help Say Y when you want to support this frontend. +config DVB_STV0367 + tristate "ST STV0367 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-T/C tuner module. Say Y when you want to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 31dd201..3b0c4bd 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -84,3 +84,4 @@ obj-$(CONFIG_DVB_DS3000) += ds3000.o obj-$(CONFIG_DVB_MB86A16) += mb86a16.o obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o obj-$(CONFIG_DVB_IX2505V) += ix2505v.o +obj-$(CONFIG_DVB_STV0367) += stv0367.o diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c new file mode 100644 index 0000000..eecdf23 --- /dev/null +++ b/drivers/media/dvb/frontends/stv0367.c @@ -0,0 +1,3441 @@ +/* + * stv0367.c + * + * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "stv0367.h" +#include "stv0367_regs.h" +#include "stv0367_priv.h" + +static int stvdebug; +module_param_named(debug, stvdebug, int, 0644); + +static int i2cdebug; +module_param_named(i2c_debug, i2cdebug, int, 0644); + +#define dprintk(args...) \ + do { \ + if (stvdebug) \ + printk(KERN_DEBUG args); \ + } while (0) + /* DVB-C */ + +struct stv0367cab_state { + enum stv0367_cab_signal_type state; + u32 mclk; + u32 adc_clk; + s32 search_range; + s32 derot_offset; + /* results */ + int locked; /* channel found */ + u32 freq_khz; /* found frequency (in kHz) */ + u32 symbol_rate; /* found symbol rate (in Bds) */ + enum stv0367cab_mod modulation; /* modulation */ + fe_spectral_inversion_t spect_inv; /* Spectrum Inversion */ +}; + +struct stv0367ter_state { + /* DVB-T */ + enum stv0367_ter_signal_type state; + enum stv0367_ter_if_iq_mode if_iq_mode; + enum stv0367_ter_mode mode;/* mode 2K or 8K */ + fe_guard_interval_t guard; + enum stv0367_ter_hierarchy hierarchy; + u32 frequency; + fe_spectral_inversion_t sense; /* current search spectrum */ + u8 force; /* force mode/guard */ + u8 bw; /* channel width 6, 7 or 8 in MHz */ + u8 pBW; /* channel width used during previous lock */ + u32 pBER; + u32 pPER; + u32 ucblocks; + s8 echo_pos; /* echo position */ + u8 first_lock; + u8 unlock_counter; + u32 agc_val; +}; + +struct stv0367_state { + struct dvb_frontend fe; + struct i2c_adapter *i2c; + /* config settings */ + const struct stv0367_config *config; + u8 chip_id; + /* DVB-C */ + struct stv0367cab_state *cab_state; + /* DVB-T */ + struct stv0367ter_state *ter_state; +}; + +struct st_register { + u16 addr; + u8 value; +}; + +/* values for STV4100 XTAL=30M int clk=53.125M*/ +static struct st_register def0367ter[STV0367TER_NBREGS] = { + {R367TER_ID, 0x60}, + {R367TER_I2CRPT, 0xa0}, + /* {R367TER_I2CRPT, 0x22},*/ + {R367TER_TOPCTRL, 0x00},/* for xc5000; was 0x02 */ + {R367TER_IOCFG0, 0x40}, + {R367TER_DAC0R, 0x00}, + {R367TER_IOCFG1, 0x00}, + {R367TER_DAC1R, 0x00}, + {R367TER_IOCFG2, 0x62}, + {R367TER_SDFR, 0x00}, + {R367TER_STATUS, 0xf8}, + {R367TER_AUX_CLK, 0x0a}, + {R367TER_FREESYS1, 0x00}, + {R367TER_FREESYS2, 0x00}, + {R367TER_FREESYS3, 0x00}, + {R367TER_GPIO_CFG, 0x55}, + {R367TER_GPIO_CMD, 0x00}, + {R367TER_AGC2MAX, 0xff}, + {R367TER_AGC2MIN, 0x00}, + {R367TER_AGC1MAX, 0xff}, + {R367TER_AGC1MIN, 0x00}, + {R367TER_AGCR, 0xbc}, + {R367TER_AGC2TH, 0x00}, + {R367TER_AGC12C, 0x00}, + {R367TER_AGCCTRL1, 0x85}, + {R367TER_AGCCTRL2, 0x1f}, + {R367TER_AGC1VAL1, 0x00}, + {R367TER_AGC1VAL2, 0x00}, + {R367TER_AGC2VAL1, 0x6f}, + {R367TER_AGC2VAL2, 0x05}, + {R367TER_AGC2PGA, 0x00}, + {R367TER_OVF_RATE1, 0x00}, + {R367TER_OVF_RATE2, 0x00}, + {R367TER_GAIN_SRC1, 0xaa},/* for xc5000; was 0x2b */ + {R367TER_GAIN_SRC2, 0xd6},/* for xc5000; was 0x04 */ + {R367TER_INC_DEROT1, 0x55}, + {R367TER_INC_DEROT2, 0x55}, + {R367TER_PPM_CPAMP_DIR, 0x2c}, + {R367TER_PPM_CPAMP_INV, 0x00}, + {R367TER_FREESTFE_1, 0x00}, + {R367TER_FREESTFE_2, 0x1c}, + {R367TER_DCOFFSET, 0x00}, + {R367TER_EN_PROCESS, 0x05}, + {R367TER_SDI_SMOOTHER, 0x80}, + {R367TER_FE_LOOP_OPEN, 0x1c}, + {R367TER_FREQOFF1, 0x00}, + {R367TER_FREQOFF2, 0x00}, + {R367TER_FREQOFF3, 0x00}, + {R367TER_TIMOFF1, 0x00}, + {R367TER_TIMOFF2, 0x00}, + {R367TER_EPQ, 0x02}, + {R367TER_EPQAUTO, 0x01}, + {R367TER_SYR_UPDATE, 0xf5}, + {R367TER_CHPFREE, 0x00}, + {R367TER_PPM_STATE_MAC, 0x23}, + {R367TER_INR_THRESHOLD, 0xff}, + {R367TER_EPQ_TPS_ID_CELL, 0xf9}, + {R367TER_EPQ_CFG, 0x00}, + {R367TER_EPQ_STATUS, 0x01}, + {R367TER_AUTORELOCK, 0x81}, + {R367TER_BER_THR_VMSB, 0x00}, + {R367TER_BER_THR_MSB, 0x00}, + {R367TER_BER_THR_LSB, 0x00}, + {R367TER_CCD, 0x83}, + {R367TER_SPECTR_CFG, 0x00}, + {R367TER_CHC_DUMMY, 0x18}, + {R367TER_INC_CTL, 0x88}, + {R367TER_INCTHRES_COR1, 0xb4}, + {R367TER_INCTHRES_COR2, 0x96}, + {R367TER_INCTHRES_DET1, 0x0e}, + {R367TER_INCTHRES_DET2, 0x11}, + {R367TER_IIR_CELLNB, 0x8d}, + {R367TER_IIRCX_COEFF1_MSB, 0x00}, + {R367TER_IIRCX_COEFF1_LSB, 0x00}, + {R367TER_IIRCX_COEFF2_MSB, 0x09}, + {R367TER_IIRCX_COEFF2_LSB, 0x18}, + {R367TER_IIRCX_COEFF3_MSB, 0x14}, + {R367TER_IIRCX_COEFF3_LSB, 0x9c}, + {R367TER_IIRCX_COEFF4_MSB, 0x00}, + {R367TER_IIRCX_COEFF4_LSB, 0x00}, + {R367TER_IIRCX_COEFF5_MSB, 0x36}, + {R367TER_IIRCX_COEFF5_LSB, 0x42}, + {R367TER_FEPATH_CFG, 0x00}, + {R367TER_PMC1_FUNC, 0x65}, + {R367TER_PMC1_FOR, 0x00}, + {R367TER_PMC2_FUNC, 0x00}, + {R367TER_STATUS_ERR_DA, 0xe0}, + {R367TER_DIG_AGC_R, 0xfe}, + {R367TER_COMAGC_TARMSB, 0x0b}, + {R367TER_COM_AGC_TAR_ENMODE, 0x41}, + {R367TER_COM_AGC_CFG, 0x3e}, + {R367TER_COM_AGC_GAIN1, 0x39}, + {R367TER_AUT_AGC_TARGETMSB, 0x0b}, + {R367TER_LOCK_DET_MSB, 0x01}, + {R367TER_AGCTAR_LOCK_LSBS, 0x40}, + {R367TER_AUT_GAIN_EN, 0xf4}, + {R367TER_AUT_CFG, 0xf0}, + {R367TER_LOCKN, 0x23}, + {R367TER_INT_X_3, 0x00}, + {R367TER_INT_X_2, 0x03}, + {R367TER_INT_X_1, 0x8d}, + {R367TER_INT_X_0, 0xa0}, + {R367TER_MIN_ERRX_MSB, 0x00}, + {R367TER_COR_CTL, 0x23}, + {R367TER_COR_STAT, 0xf6}, + {R367TER_COR_INTEN, 0x00}, + {R367TER_COR_INTSTAT, 0x3f}, + {R367TER_COR_MODEGUARD, 0x03}, + {R367TER_AGC_CTL, 0x08}, + {R367TER_AGC_MANUAL1, 0x00}, + {R367TER_AGC_MANUAL2, 0x00}, + {R367TER_AGC_TARG, 0x16}, + {R367TER_AGC_GAIN1, 0x53}, + {R367TER_AGC_GAIN2, 0x1d}, + {R367TER_RESERVED_1, 0x00}, + {R367TER_RESERVED_2, 0x00}, + {R367TER_RESERVED_3, 0x00}, + {R367TER_CAS_CTL, 0x44}, + {R367TER_CAS_FREQ, 0xb3}, + {R367TER_CAS_DAGCGAIN, 0x12}, + {R367TER_SYR_CTL, 0x04}, + {R367TER_SYR_STAT, 0x10}, + {R367TER_SYR_NCO1, 0x00}, + {R367TER_SYR_NCO2, 0x00}, + {R367TER_SYR_OFFSET1, 0x00}, + {R367TER_SYR_OFFSET2, 0x00}, + {R367TER_FFT_CTL, 0x00}, + {R367TER_SCR_CTL, 0x70}, + {R367TER_PPM_CTL1, 0xf8}, + {R367TER_TRL_CTL, 0x14},/* for xc5000; was 0xac */ + {R367TER_TRL_NOMRATE1, 0xae},/* for xc5000; was 0x1e */ + {R367TER_TRL_NOMRATE2, 0x56},/* for xc5000; was 0x58 */ + {R367TER_TRL_TIME1, 0x1d}, + {R367TER_TRL_TIME2, 0xfc}, + {R367TER_CRL_CTL, 0x24}, + {R367TER_CRL_FREQ1, 0xad}, + {R367TER_CRL_FREQ2, 0x9d}, + {R367TER_CRL_FREQ3, 0xff}, + {R367TER_CHC_CTL, 0x01}, + {R367TER_CHC_SNR, 0xf0}, + {R367TER_BDI_CTL, 0x00}, + {R367TER_DMP_CTL, 0x00}, + {R367TER_TPS_RCVD1, 0x30}, + {R367TER_TPS_RCVD2, 0x02}, + {R367TER_TPS_RCVD3, 0x01}, + {R367TER_TPS_RCVD4, 0x00}, + {R367TER_TPS_ID_CELL1, 0x00}, + {R367TER_TPS_ID_CELL2, 0x00}, + {R367TER_TPS_RCVD5_SET1, 0x02}, + {R367TER_TPS_SET2, 0x02}, + {R367TER_TPS_SET3, 0x01}, + {R367TER_TPS_CTL, 0x00}, + {R367TER_CTL_FFTOSNUM, 0x34}, + {R367TER_TESTSELECT, 0x09}, + {R367TER_MSC_REV, 0x0a}, + {R367TER_PIR_CTL, 0x00}, + {R367TER_SNR_CARRIER1, 0xa1}, + {R367TER_SNR_CARRIER2, 0x9a}, + {R367TER_PPM_CPAMP, 0x2c}, + {R367TER_TSM_AP0, 0x00}, + {R367TER_TSM_AP1, 0x00}, + {R367TER_TSM_AP2 , 0x00}, + {R367TER_TSM_AP3, 0x00}, + {R367TER_TSM_AP4, 0x00}, + {R367TER_TSM_AP5, 0x00}, + {R367TER_TSM_AP6, 0x00}, + {R367TER_TSM_AP7, 0x00}, + {R367TER_TSTRES, 0x00}, + {R367TER_ANACTRL, 0x0D},/* PLL stoped, restart at init!!! */ + {R367TER_TSTBUS, 0x00}, + {R367TER_TSTRATE, 0x00}, + {R367TER_CONSTMODE, 0x01}, + {R367TER_CONSTCARR1, 0x00}, + {R367TER_CONSTCARR2, 0x00}, + {R367TER_ICONSTEL, 0x0a}, + {R367TER_QCONSTEL, 0x15}, + {R367TER_TSTBISTRES0, 0x00}, + {R367TER_TSTBISTRES1, 0x00}, + {R367TER_TSTBISTRES2, 0x28}, + {R367TER_TSTBISTRES3, 0x00}, + {R367TER_RF_AGC1, 0xff}, + {R367TER_RF_AGC2, 0x83}, + {R367TER_ANADIGCTRL, 0x19}, + {R367TER_PLLMDIV, 0x01},/* for xc5000; was 0x0c */ + {R367TER_PLLNDIV, 0x06},/* for xc5000; was 0x55 */ + {R367TER_PLLSETUP, 0x18}, + {R367TER_DUAL_AD12, 0x04},/* for xc5000; was 0x00 */ + {R367TER_TSTBIST, 0x00}, + {R367TER_PAD_COMP_CTRL, 0x00}, + {R367TER_PAD_COMP_WR, 0x00}, + {R367TER_PAD_COMP_RD, 0xe0}, + {R367TER_SYR_TARGET_FFTADJT_MSB, 0x00}, + {R367TER_SYR_TARGET_FFTADJT_LSB, 0x00}, + {R367TER_SYR_TARGET_CHCADJT_MSB, 0x00}, + {R367TER_SYR_TARGET_CHCADJT_LSB, 0x00}, + {R367TER_SYR_FLAG, 0x00}, + {R367TER_CRL_TARGET1, 0x00}, + {R367TER_CRL_TARGET2, 0x00}, + {R367TER_CRL_TARGET3, 0x00}, + {R367TER_CRL_TARGET4, 0x00}, + {R367TER_CRL_FLAG, 0x00}, + {R367TER_TRL_TARGET1, 0x00}, + {R367TER_TRL_TARGET2, 0x00}, + {R367TER_TRL_CHC, 0x00}, + {R367TER_CHC_SNR_TARG, 0x00}, + {R367TER_TOP_TRACK, 0x00}, + {R367TER_TRACKER_FREE1, 0x00}, + {R367TER_ERROR_CRL1, 0x00}, + {R367TER_ERROR_CRL2, 0x00}, + {R367TER_ERROR_CRL3, 0x00}, + {R367TER_ERROR_CRL4, 0x00}, + {R367TER_DEC_NCO1, 0x2c}, + {R367TER_DEC_NCO2, 0x0f}, + {R367TER_DEC_NCO3, 0x20}, + {R367TER_SNR, 0xf1}, + {R367TER_SYR_FFTADJ1, 0x00}, + {R367TER_SYR_FFTADJ2, 0x00}, + {R367TER_SYR_CHCADJ1, 0x00}, + {R367TER_SYR_CHCADJ2, 0x00}, + {R367TER_SYR_OFF, 0x00}, + {R367TER_PPM_OFFSET1, 0x00}, + {R367TER_PPM_OFFSET2, 0x03}, + {R367TER_TRACKER_FREE2, 0x00}, + {R367TER_DEBG_LT10, 0x00}, + {R367TER_DEBG_LT11, 0x00}, + {R367TER_DEBG_LT12, 0x00}, + {R367TER_DEBG_LT13, 0x00}, + {R367TER_DEBG_LT14, 0x00}, + {R367TER_DEBG_LT15, 0x00}, + {R367TER_DEBG_LT16, 0x00}, + {R367TER_DEBG_LT17, 0x00}, + {R367TER_DEBG_LT18, 0x00}, + {R367TER_DEBG_LT19, 0x00}, + {R367TER_DEBG_LT1A, 0x00}, + {R367TER_DEBG_LT1B, 0x00}, + {R367TER_DEBG_LT1C, 0x00}, + {R367TER_DEBG_LT1D, 0x00}, + {R367TER_DEBG_LT1E, 0x00}, + {R367TER_DEBG_LT1F, 0x00}, + {R367TER_RCCFGH, 0x00}, + {R367TER_RCCFGM, 0x00}, + {R367TER_RCCFGL, 0x00}, + {R367TER_RCINSDELH, 0x00}, + {R367TER_RCINSDELM, 0x00}, + {R367TER_RCINSDELL, 0x00}, + {R367TER_RCSTATUS, 0x00}, + {R367TER_RCSPEED, 0x6f}, + {R367TER_RCDEBUGM, 0xe7}, + {R367TER_RCDEBUGL, 0x9b}, + {R367TER_RCOBSCFG, 0x00}, + {R367TER_RCOBSM, 0x00}, + {R367TER_RCOBSL, 0x00}, + {R367TER_RCFECSPY, 0x00}, + {R367TER_RCFSPYCFG, 0x00}, + {R367TER_RCFSPYDATA, 0x00}, + {R367TER_RCFSPYOUT, 0x00}, + {R367TER_RCFSTATUS, 0x00}, + {R367TER_RCFGOODPACK, 0x00}, + {R367TER_RCFPACKCNT, 0x00}, + {R367TER_RCFSPYMISC, 0x00}, + {R367TER_RCFBERCPT4, 0x00}, + {R367TER_RCFBERCPT3, 0x00}, + {R367TER_RCFBERCPT2, 0x00}, + {R367TER_RCFBERCPT1, 0x00}, + {R367TER_RCFBERCPT0, 0x00}, + {R367TER_RCFBERERR2, 0x00}, + {R367TER_RCFBERERR1, 0x00}, + {R367TER_RCFBERERR0, 0x00}, + {R367TER_RCFSTATESM, 0x00}, + {R367TER_RCFSTATESL, 0x00}, + {R367TER_RCFSPYBER, 0x00}, + {R367TER_RCFSPYDISTM, 0x00}, + {R367TER_RCFSPYDISTL, 0x00}, + {R367TER_RCFSPYOBS7, 0x00}, + {R367TER_RCFSPYOBS6, 0x00}, + {R367TER_RCFSPYOBS5, 0x00}, + {R367TER_RCFSPYOBS4, 0x00}, + {R367TER_RCFSPYOBS3, 0x00}, + {R367TER_RCFSPYOBS2, 0x00}, + {R367TER_RCFSPYOBS1, 0x00}, + {R367TER_RCFSPYOBS0, 0x00}, + {R367TER_TSGENERAL, 0x00}, + {R367TER_RC1SPEED, 0x6f}, + {R367TER_TSGSTATUS, 0x18}, + {R367TER_FECM, 0x01}, + {R367TER_VTH12, 0xff}, + {R367TER_VTH23, 0xa1}, + {R367TER_VTH34, 0x64}, + {R367TER_VTH56, 0x40}, + {R367TER_VTH67, 0x00}, + {R367TER_VTH78, 0x2c}, + {R367TER_VITCURPUN, 0x12}, + {R367TER_VERROR, 0x01}, + {R367TER_PRVIT, 0x3f}, + {R367TER_VAVSRVIT, 0x00}, + {R367TER_VSTATUSVIT, 0xbd}, + {R367TER_VTHINUSE, 0xa1}, + {R367TER_KDIV12, 0x20}, + {R367TER_KDIV23, 0x40}, + {R367TER_KDIV34, 0x20}, + {R367TER_KDIV56, 0x30}, + {R367TER_KDIV67, 0x00}, + {R367TER_KDIV78, 0x30}, + {R367TER_SIGPOWER, 0x54}, + {R367TER_DEMAPVIT, 0x40}, + {R367TER_VITSCALE, 0x00}, + {R367TER_FFEC1PRG, 0x00}, + {R367TER_FVITCURPUN, 0x12}, + {R367TER_FVERROR, 0x01}, + {R367TER_FVSTATUSVIT, 0xbd}, + {R367TER_DEBUG_LT1, 0x00}, + {R367TER_DEBUG_LT2, 0x00}, + {R367TER_DEBUG_LT3, 0x00}, + {R367TER_TSTSFMET, 0x00}, + {R367TER_SELOUT, 0x00}, + {R367TER_TSYNC, 0x00}, + {R367TER_TSTERR, 0x00}, + {R367TER_TSFSYNC, 0x00}, + {R367TER_TSTSFERR, 0x00}, + {R367TER_TSTTSSF1, 0x01}, + {R367TER_TSTTSSF2, 0x1f}, + {R367TER_TSTTSSF3, 0x00}, + {R367TER_TSTTS1, 0x00}, + {R367TER_TSTTS2, 0x1f}, + {R367TER_TSTTS3, 0x01}, + {R367TER_TSTTS4, 0x00}, + {R367TER_TSTTSRC, 0x00}, + {R367TER_TSTTSRS, 0x00}, + {R367TER_TSSTATEM, 0xb0}, + {R367TER_TSSTATEL, 0x40}, + {R367TER_TSCFGH, 0xC0}, + {R367TER_TSCFGM, 0xc0},/* for xc5000; was 0x00 */ + {R367TER_TSCFGL, 0x20}, + {R367TER_TSSYNC, 0x00}, + {R367TER_TSINSDELH, 0x00}, + {R367TER_TSINSDELM, 0x00}, + {R367TER_TSINSDELL, 0x00}, + {R367TER_TSDIVN, 0x03}, + {R367TER_TSDIVPM, 0x00}, + {R367TER_TSDIVPL, 0x00}, + {R367TER_TSDIVQM, 0x00}, + {R367TER_TSDIVQL, 0x00}, + {R367TER_TSDILSTKM, 0x00}, + {R367TER_TSDILSTKL, 0x00}, + {R367TER_TSSPEED, 0x40},/* for xc5000; was 0x6f */ + {R367TER_TSSTATUS, 0x81}, + {R367TER_TSSTATUS2, 0x6a}, + {R367TER_TSBITRATEM, 0x0f}, + {R367TER_TSBITRATEL, 0xc6}, + {R367TER_TSPACKLENM, 0x00}, + {R367TER_TSPACKLENL, 0xfc}, + {R367TER_TSBLOCLENM, 0x0a}, + {R367TER_TSBLOCLENL, 0x80}, + {R367TER_TSDLYH, 0x90}, + {R367TER_TSDLYM, 0x68}, + {R367TER_TSDLYL, 0x01}, + {R367TER_TSNPDAV, 0x00}, + {R367TER_TSBUFSTATH, 0x00}, + {R367TER_TSBUFSTATM, 0x00}, + {R367TER_TSBUFSTATL, 0x00}, + {R367TER_TSDEBUGM, 0xcf}, + {R367TER_TSDEBUGL, 0x1e}, + {R367TER_TSDLYSETH, 0x00}, + {R367TER_TSDLYSETM, 0x68}, + {R367TER_TSDLYSETL, 0x00}, + {R367TER_TSOBSCFG, 0x00}, + {R367TER_TSOBSM, 0x47}, + {R367TER_TSOBSL, 0x1f}, + {R367TER_ERRCTRL1, 0x95}, + {R367TER_ERRCNT1H, 0x80}, + {R367TER_ERRCNT1M, 0x00}, + {R367TER_ERRCNT1L, 0x00}, + {R367TER_ERRCTRL2, 0x95}, + {R367TER_ERRCNT2H, 0x00}, + {R367TER_ERRCNT2M, 0x00}, + {R367TER_ERRCNT2L, 0x00}, + {R367TER_FECSPY, 0x88}, + {R367TER_FSPYCFG, 0x2c}, + {R367TER_FSPYDATA, 0x3a}, + {R367TER_FSPYOUT, 0x06}, + {R367TER_FSTATUS, 0x61}, + {R367TER_FGOODPACK, 0xff}, + {R367TER_FPACKCNT, 0xff}, + {R367TER_FSPYMISC, 0x66}, + {R367TER_FBERCPT4, 0x00}, + {R367TER_FBERCPT3, 0x00}, + {R367TER_FBERCPT2, 0x36}, + {R367TER_FBERCPT1, 0x36}, + {R367TER_FBERCPT0, 0x14}, + {R367TER_FBERERR2, 0x00}, + {R367TER_FBERERR1, 0x03}, + {R367TER_FBERERR0, 0x28}, + {R367TER_FSTATESM, 0x00}, + {R367TER_FSTATESL, 0x02}, + {R367TER_FSPYBER, 0x00}, + {R367TER_FSPYDISTM, 0x01}, + {R367TER_FSPYDISTL, 0x9f}, + {R367TER_FSPYOBS7, 0xc9}, + {R367TER_FSPYOBS6, 0x99}, + {R367TER_FSPYOBS5, 0x08}, + {R367TER_FSPYOBS4, 0xec}, + {R367TER_FSPYOBS3, 0x01}, + {R367TER_FSPYOBS2, 0x0f}, + {R367TER_FSPYOBS1, 0xf5}, + {R367TER_FSPYOBS0, 0x08}, + {R367TER_SFDEMAP, 0x40}, + {R367TER_SFERROR, 0x00}, + {R367TER_SFAVSR, 0x30}, + {R367TER_SFECSTATUS, 0xcc}, + {R367TER_SFKDIV12, 0x20}, + {R367TER_SFKDIV23, 0x40}, + {R367TER_SFKDIV34, 0x20}, + {R367TER_SFKDIV56, 0x20}, + {R367TER_SFKDIV67, 0x00}, + {R367TER_SFKDIV78, 0x20}, + {R367TER_SFDILSTKM, 0x00}, + {R367TER_SFDILSTKL, 0x00}, + {R367TER_SFSTATUS, 0xb5}, + {R367TER_SFDLYH, 0x90}, + {R367TER_SFDLYM, 0x60}, + {R367TER_SFDLYL, 0x01}, + {R367TER_SFDLYSETH, 0xc0}, + {R367TER_SFDLYSETM, 0x60}, + {R367TER_SFDLYSETL, 0x00}, + {R367TER_SFOBSCFG, 0x00}, + {R367TER_SFOBSM, 0x47}, + {R367TER_SFOBSL, 0x05}, + {R367TER_SFECINFO, 0x40}, + {R367TER_SFERRCTRL, 0x74}, + {R367TER_SFERRCNTH, 0x80}, + {R367TER_SFERRCNTM , 0x00}, + {R367TER_SFERRCNTL, 0x00}, + {R367TER_SYMBRATEM, 0x2f}, + {R367TER_SYMBRATEL, 0x50}, + {R367TER_SYMBSTATUS, 0x7f}, + {R367TER_SYMBCFG, 0x00}, + {R367TER_SYMBFIFOM, 0xf4}, + {R367TER_SYMBFIFOL, 0x0d}, + {R367TER_SYMBOFFSM, 0xf0}, + {R367TER_SYMBOFFSL, 0x2d}, + {R367TER_DEBUG_LT4, 0x00}, + {R367TER_DEBUG_LT5, 0x00}, + {R367TER_DEBUG_LT6, 0x00}, + {R367TER_DEBUG_LT7, 0x00}, + {R367TER_DEBUG_LT8, 0x00}, + {R367TER_DEBUG_LT9, 0x00}, +}; + +#define RF_LOOKUP_TABLE_SIZE 31 +#define RF_LOOKUP_TABLE2_SIZE 16 +/* RF Level (for RF AGC->AGC1) Lookup Table, depends on the board and tuner.*/ +s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = { + {/*AGC1*/ + 48, 50, 51, 53, 54, 56, 57, 58, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 80, 83, 85, 88, + }, {/*RF(dbm)*/ + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 46, 47, + 49, 50, 52, 53, 54, 55, 56, + } +}; +/* RF Level (for IF AGC->AGC2) Lookup Table, depends on the board and tuner.*/ +s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_SIZE] = { + {/*AGC2*/ + 28, 29, 31, 32, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, + }, {/*RF(dbm)*/ + 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + } +}; + +static struct st_register def0367cab[STV0367CAB_NBREGS] = { + {R367CAB_ID, 0x60}, + {R367CAB_I2CRPT, 0xa0}, + /*{R367CAB_I2CRPT, 0x22},*/ + {R367CAB_TOPCTRL, 0x10}, + {R367CAB_IOCFG0, 0x80}, + {R367CAB_DAC0R, 0x00}, + {R367CAB_IOCFG1, 0x00}, + {R367CAB_DAC1R, 0x00}, + {R367CAB_IOCFG2, 0x00}, + {R367CAB_SDFR, 0x00}, + {R367CAB_AUX_CLK, 0x00}, + {R367CAB_FREESYS1, 0x00}, + {R367CAB_FREESYS2, 0x00}, + {R367CAB_FREESYS3, 0x00}, + {R367CAB_GPIO_CFG, 0x55}, + {R367CAB_GPIO_CMD, 0x01}, + {R367CAB_TSTRES, 0x00}, + {R367CAB_ANACTRL, 0x0d},/* was 0x00 need to check - I.M.L.*/ + {R367CAB_TSTBUS, 0x00}, + {R367CAB_RF_AGC1, 0xea}, + {R367CAB_RF_AGC2, 0x82}, + {R367CAB_ANADIGCTRL, 0x0b}, + {R367CAB_PLLMDIV, 0x01}, + {R367CAB_PLLNDIV, 0x08}, + {R367CAB_PLLSETUP, 0x18}, + {R367CAB_DUAL_AD12, 0x04}, + {R367CAB_TSTBIST, 0x00}, + {R367CAB_CTRL_1, 0x00}, + {R367CAB_CTRL_2, 0x03}, + {R367CAB_IT_STATUS1, 0x2b}, + {R367CAB_IT_STATUS2, 0x08}, + {R367CAB_IT_EN1, 0x00}, + {R367CAB_IT_EN2, 0x00}, + {R367CAB_CTRL_STATUS, 0x04}, + {R367CAB_TEST_CTL, 0x00}, + {R367CAB_AGC_CTL, 0x73}, + {R367CAB_AGC_IF_CFG, 0x50}, + {R367CAB_AGC_RF_CFG, 0x00}, + {R367CAB_AGC_PWM_CFG, 0x03}, + {R367CAB_AGC_PWR_REF_L, 0x5a}, + {R367CAB_AGC_PWR_REF_H, 0x00}, + {R367CAB_AGC_RF_TH_L, 0xff}, + {R367CAB_AGC_RF_TH_H, 0x07}, + {R367CAB_AGC_IF_LTH_L, 0x00}, + {R367CAB_AGC_IF_LTH_H, 0x08}, + {R367CAB_AGC_IF_HTH_L, 0xff}, + {R367CAB_AGC_IF_HTH_H, 0x07}, + {R367CAB_AGC_PWR_RD_L, 0xa0}, + {R367CAB_AGC_PWR_RD_M, 0xe9}, + {R367CAB_AGC_PWR_RD_H, 0x03}, + {R367CAB_AGC_PWM_IFCMD_L, 0xe4}, + {R367CAB_AGC_PWM_IFCMD_H, 0x00}, + {R367CAB_AGC_PWM_RFCMD_L, 0xff}, + {R367CAB_AGC_PWM_RFCMD_H, 0x07}, + {R367CAB_IQDEM_CFG, 0x01}, + {R367CAB_MIX_NCO_LL, 0x22}, + {R367CAB_MIX_NCO_HL, 0x96}, + {R367CAB_MIX_NCO_HH, 0x55}, + {R367CAB_SRC_NCO_LL, 0xff}, + {R367CAB_SRC_NCO_LH, 0x0c}, + {R367CAB_SRC_NCO_HL, 0xf5}, + {R367CAB_SRC_NCO_HH, 0x20}, + {R367CAB_IQDEM_GAIN_SRC_L, 0x06}, + {R367CAB_IQDEM_GAIN_SRC_H, 0x01}, + {R367CAB_IQDEM_DCRM_CFG_LL, 0xfe}, + {R367CAB_IQDEM_DCRM_CFG_LH, 0xff}, + {R367CAB_IQDEM_DCRM_CFG_HL, 0x0f}, + {R367CAB_IQDEM_DCRM_CFG_HH, 0x00}, + {R367CAB_IQDEM_ADJ_COEFF0, 0x34}, + {R367CAB_IQDEM_ADJ_COEFF1, 0xae}, + {R367CAB_IQDEM_ADJ_COEFF2, 0x46}, + {R367CAB_IQDEM_ADJ_COEFF3, 0x77}, + {R367CAB_IQDEM_ADJ_COEFF4, 0x96}, + {R367CAB_IQDEM_ADJ_COEFF5, 0x69}, + {R367CAB_IQDEM_ADJ_COEFF6, 0xc7}, + {R367CAB_IQDEM_ADJ_COEFF7, 0x01}, + {R367CAB_IQDEM_ADJ_EN, 0x04}, + {R367CAB_IQDEM_ADJ_AGC_REF, 0x94}, + {R367CAB_ALLPASSFILT1, 0xc9}, + {R367CAB_ALLPASSFILT2, 0x2d}, + {R367CAB_ALLPASSFILT3, 0xa3}, + {R367CAB_ALLPASSFILT4, 0xfb}, + {R367CAB_ALLPASSFILT5, 0xf6}, + {R367CAB_ALLPASSFILT6, 0x45}, + {R367CAB_ALLPASSFILT7, 0x6f}, + {R367CAB_ALLPASSFILT8, 0x7e}, + {R367CAB_ALLPASSFILT9, 0x05}, + {R367CAB_ALLPASSFILT10, 0x0a}, + {R367CAB_ALLPASSFILT11, 0x51}, + {R367CAB_TRL_AGC_CFG, 0x20}, + {R367CAB_TRL_LPF_CFG, 0x28}, + {R367CAB_TRL_LPF_ACQ_GAIN, 0x44}, + {R367CAB_TRL_LPF_TRK_GAIN, 0x22}, + {R367CAB_TRL_LPF_OUT_GAIN, 0x03}, + {R367CAB_TRL_LOCKDET_LTH, 0x04}, + {R367CAB_TRL_LOCKDET_HTH, 0x11}, + {R367CAB_TRL_LOCKDET_TRGVAL, 0x20}, + {R367CAB_IQ_QAM, 0x01}, + {R367CAB_FSM_STATE, 0xa0}, + {R367CAB_FSM_CTL, 0x08}, + {R367CAB_FSM_STS, 0x0c}, + {R367CAB_FSM_SNR0_HTH, 0x00}, + {R367CAB_FSM_SNR1_HTH, 0x00}, + {R367CAB_FSM_SNR2_HTH, 0x23},/* 0x00 */ + {R367CAB_FSM_SNR0_LTH, 0x00}, + {R367CAB_FSM_SNR1_LTH, 0x00}, + {R367CAB_FSM_EQA1_HTH, 0x00}, + {R367CAB_FSM_TEMPO, 0x32}, + {R367CAB_FSM_CONFIG, 0x03}, + {R367CAB_EQU_I_TESTTAP_L, 0x11}, + {R367CAB_EQU_I_TESTTAP_M, 0x00}, + {R367CAB_EQU_I_TESTTAP_H, 0x00}, + {R367CAB_EQU_TESTAP_CFG, 0x00}, + {R367CAB_EQU_Q_TESTTAP_L, 0xff}, + {R367CAB_EQU_Q_TESTTAP_M, 0x00}, + {R367CAB_EQU_Q_TESTTAP_H, 0x00}, + {R367CAB_EQU_TAP_CTRL, 0x00}, + {R367CAB_EQU_CTR_CRL_CONTROL_L, 0x11}, + {R367CAB_EQU_CTR_CRL_CONTROL_H, 0x05}, + {R367CAB_EQU_CTR_HIPOW_L, 0x00}, + {R367CAB_EQU_CTR_HIPOW_H, 0x00}, + {R367CAB_EQU_I_EQU_LO, 0xef}, + {R367CAB_EQU_I_EQU_HI, 0x00}, + {R367CAB_EQU_Q_EQU_LO, 0xee}, + {R367CAB_EQU_Q_EQU_HI, 0x00}, + {R367CAB_EQU_MAPPER, 0xc5}, + {R367CAB_EQU_SWEEP_RATE, 0x80}, + {R367CAB_EQU_SNR_LO, 0x64}, + {R367CAB_EQU_SNR_HI, 0x03}, + {R367CAB_EQU_GAMMA_LO, 0x00}, + {R367CAB_EQU_GAMMA_HI, 0x00}, + {R367CAB_EQU_ERR_GAIN, 0x36}, + {R367CAB_EQU_RADIUS, 0xaa}, + {R367CAB_EQU_FFE_MAINTAP, 0x00}, + {R367CAB_EQU_FFE_LEAKAGE, 0x63}, + {R367CAB_EQU_FFE_MAINTAP_POS, 0xdf}, + {R367CAB_EQU_GAIN_WIDE, 0x88}, + {R367CAB_EQU_GAIN_NARROW, 0x41}, + {R367CAB_EQU_CTR_LPF_GAIN, 0xd1}, + {R367CAB_EQU_CRL_LPF_GAIN, 0xa7}, + {R367CAB_EQU_GLOBAL_GAIN, 0x06}, + {R367CAB_EQU_CRL_LD_SEN, 0x85}, + {R367CAB_EQU_CRL_LD_VAL, 0xe2}, + {R367CAB_EQU_CRL_TFR, 0x20}, + {R367CAB_EQU_CRL_BISTH_LO, 0x00}, + {R367CAB_EQU_CRL_BISTH_HI, 0x00}, + {R367CAB_EQU_SWEEP_RANGE_LO, 0x00}, + {R367CAB_EQU_SWEEP_RANGE_HI, 0x00}, + {R367CAB_EQU_CRL_LIMITER, 0x40}, + {R367CAB_EQU_MODULUS_MAP, 0x90}, + {R367CAB_EQU_PNT_GAIN, 0xa7}, + {R367CAB_FEC_AC_CTR_0, 0x16}, + {R367CAB_FEC_AC_CTR_1, 0x0b}, + {R367CAB_FEC_AC_CTR_2, 0x88}, + {R367CAB_FEC_AC_CTR_3, 0x02}, + {R367CAB_FEC_STATUS, 0x12}, + {R367CAB_RS_COUNTER_0, 0x7d}, + {R367CAB_RS_COUNTER_1, 0xd0}, + {R367CAB_RS_COUNTER_2, 0x19}, + {R367CAB_RS_COUNTER_3, 0x0b}, + {R367CAB_RS_COUNTER_4, 0xa3}, + {R367CAB_RS_COUNTER_5, 0x00}, + {R367CAB_BERT_0, 0x01}, + {R367CAB_BERT_1, 0x25}, + {R367CAB_BERT_2, 0x41}, + {R367CAB_BERT_3, 0x39}, + {R367CAB_OUTFORMAT_0, 0xc2}, + {R367CAB_OUTFORMAT_1, 0x22}, + {R367CAB_SMOOTHER_2, 0x28}, + {R367CAB_TSMF_CTRL_0, 0x01}, + {R367CAB_TSMF_CTRL_1, 0xc6}, + {R367CAB_TSMF_CTRL_3, 0x43}, + {R367CAB_TS_ON_ID_0, 0x00}, + {R367CAB_TS_ON_ID_1, 0x00}, + {R367CAB_TS_ON_ID_2, 0x00}, + {R367CAB_TS_ON_ID_3, 0x00}, + {R367CAB_RE_STATUS_0, 0x00}, + {R367CAB_RE_STATUS_1, 0x00}, + {R367CAB_RE_STATUS_2, 0x00}, + {R367CAB_RE_STATUS_3, 0x00}, + {R367CAB_TS_STATUS_0, 0x00}, + {R367CAB_TS_STATUS_1, 0x00}, + {R367CAB_TS_STATUS_2, 0xa0}, + {R367CAB_TS_STATUS_3, 0x00}, + {R367CAB_T_O_ID_0, 0x00}, + {R367CAB_T_O_ID_1, 0x00}, + {R367CAB_T_O_ID_2, 0x00}, + {R367CAB_T_O_ID_3, 0x00}, +}; + +static +int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len) +{ + u8 buf[len + 2]; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = len + 2 + }; + int ret; + + buf[0] = MSB(reg); + buf[1] = LSB(reg); + memcpy(buf + 2, data, len); + + if (i2cdebug) + printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, buf[2]); + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) + printk(KERN_ERR "%s: i2c write error!\n", __func__); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int stv0367_writereg(struct stv0367_state *state, u16 reg, u8 data) +{ + return stv0367_writeregs(state, reg, &data, 1); +} + +static u8 stv0367_readreg(struct stv0367_state *state, u16 reg) +{ + u8 b0[] = { 0, 0 }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 2 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + int ret; + + b0[0] = MSB(reg); + b0[1] = LSB(reg); + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) + printk(KERN_ERR "%s: i2c read error\n", __func__); + + if (i2cdebug) + printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, b1[0]); + + return b1[0]; +} + +static void extract_mask_pos(u32 label, u8 *mask, u8 *pos) +{ + u8 position = 0, i = 0; + + (*mask) = label & 0xff; + + while ((position == 0) && (i < 8)) { + position = ((*mask) >> i) & 0x01; + i++; + } + + (*pos) = (i - 1); +} + +static void stv0367_writebits(struct stv0367_state *state, u32 label, u8 val) +{ + u8 reg, mask, pos; + + reg = stv0367_readreg(state, (label >> 16) & 0xffff); + extract_mask_pos(label, &mask, &pos); + + val = mask & (val << pos); + + reg = (reg & (~mask)) | val; + stv0367_writereg(state, (label >> 16) & 0xffff, reg); + +} + +static void stv0367_setbits(u8 *reg, u32 label, u8 val) +{ + u8 mask, pos; + + extract_mask_pos(label, &mask, &pos); + + val = mask & (val << pos); + + (*reg) = ((*reg) & (~mask)) | val; +} + +static u8 stv0367_readbits(struct stv0367_state *state, u32 label) +{ + u8 val = 0xff; + u8 mask, pos; + + extract_mask_pos(label, &mask, &pos); + + val = stv0367_readreg(state, label >> 16); + val = (val & mask) >> pos; + + return val; +} + +u8 stv0367_getbits(u8 reg, u32 label) +{ + u8 mask, pos; + + extract_mask_pos(label, &mask, &pos); + + return (reg & mask) >> pos; +} + +static int stv0367ter_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct stv0367_state *state = fe->demodulator_priv; + u8 tmp = stv0367_readreg(state, R367TER_I2CRPT); + + dprintk("%s:\n", __func__); + + if (enable) { + stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 0); + stv0367_setbits(&tmp, F367TER_I2CT_ON, 1); + } else { + stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 1); + stv0367_setbits(&tmp, F367TER_I2CT_ON, 0); + } + + stv0367_writereg(state, R367TER_I2CRPT, tmp); + + return 0; +} + +static u32 stv0367_get_tuner_freq(struct dvb_frontend *fe) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + u32 freq = 0; + u32 err = 0; + + dprintk("%s:\n", __func__); + + + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->get_frequency) { + err = tuner_ops->get_frequency(fe, &freq); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + dprintk("%s: frequency=%d\n", __func__, freq); + + } else + return -1; + + return freq; +} + +static u16 CellsCoeffs_8MHz_367cofdm[3][6][5] = { + { + {0x10EF, 0xE205, 0x10EF, 0xCE49, 0x6DA7}, /* CELL 1 COEFFS 27M*/ + {0x2151, 0xc557, 0x2151, 0xc705, 0x6f93}, /* CELL 2 COEFFS */ + {0x2503, 0xc000, 0x2503, 0xc375, 0x7194}, /* CELL 3 COEFFS */ + {0x20E9, 0xca94, 0x20e9, 0xc153, 0x7194}, /* CELL 4 COEFFS */ + {0x06EF, 0xF852, 0x06EF, 0xC057, 0x7207}, /* CELL 5 COEFFS */ + {0x0000, 0x0ECC, 0x0ECC, 0x0000, 0x3647} /* CELL 6 COEFFS */ + }, { + {0x10A0, 0xE2AF, 0x10A1, 0xCE76, 0x6D6D}, /* CELL 1 COEFFS 25M*/ + {0x20DC, 0xC676, 0x20D9, 0xC80A, 0x6F29}, + {0x2532, 0xC000, 0x251D, 0xC391, 0x706F}, + {0x1F7A, 0xCD2B, 0x2032, 0xC15E, 0x711F}, + {0x0698, 0xFA5E, 0x0568, 0xC059, 0x7193}, + {0x0000, 0x0918, 0x149C, 0x0000, 0x3642} /* CELL 6 COEFFS */ + }, { + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} + } +}; + +static u16 CellsCoeffs_7MHz_367cofdm[3][6][5] = { + { + {0x12CA, 0xDDAF, 0x12CA, 0xCCEB, 0x6FB1}, /* CELL 1 COEFFS 27M*/ + {0x2329, 0xC000, 0x2329, 0xC6B0, 0x725F}, /* CELL 2 COEFFS */ + {0x2394, 0xC000, 0x2394, 0xC2C7, 0x7410}, /* CELL 3 COEFFS */ + {0x251C, 0xC000, 0x251C, 0xC103, 0x74D9}, /* CELL 4 COEFFS */ + {0x0804, 0xF546, 0x0804, 0xC040, 0x7544}, /* CELL 5 COEFFS */ + {0x0000, 0x0CD9, 0x0CD9, 0x0000, 0x370A} /* CELL 6 COEFFS */ + }, { + {0x1285, 0xDE47, 0x1285, 0xCD17, 0x6F76}, /*25M*/ + {0x234C, 0xC000, 0x2348, 0xC6DA, 0x7206}, + {0x23B4, 0xC000, 0x23AC, 0xC2DB, 0x73B3}, + {0x253D, 0xC000, 0x25B6, 0xC10B, 0x747F}, + {0x0721, 0xF79C, 0x065F, 0xC041, 0x74EB}, + {0x0000, 0x08FA, 0x1162, 0x0000, 0x36FF} + }, { + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} + } +}; + +static u16 CellsCoeffs_6MHz_367cofdm[3][6][5] = { + { + {0x1699, 0xD5B8, 0x1699, 0xCBC3, 0x713B}, /* CELL 1 COEFFS 27M*/ + {0x2245, 0xC000, 0x2245, 0xC568, 0x74D5}, /* CELL 2 COEFFS */ + {0x227F, 0xC000, 0x227F, 0xC1FC, 0x76C6}, /* CELL 3 COEFFS */ + {0x235E, 0xC000, 0x235E, 0xC0A7, 0x778A}, /* CELL 4 COEFFS */ + {0x0ECB, 0xEA0B, 0x0ECB, 0xC027, 0x77DD}, /* CELL 5 COEFFS */ + {0x0000, 0x0B68, 0x0B68, 0x0000, 0xC89A}, /* CELL 6 COEFFS */ + }, { + {0x1655, 0xD64E, 0x1658, 0xCBEF, 0x70FE}, /*25M*/ + {0x225E, 0xC000, 0x2256, 0xC589, 0x7489}, + {0x2293, 0xC000, 0x2295, 0xC209, 0x767E}, + {0x2377, 0xC000, 0x23AA, 0xC0AB, 0x7746}, + {0x0DC7, 0xEBC8, 0x0D07, 0xC027, 0x7799}, + {0x0000, 0x0888, 0x0E9C, 0x0000, 0x3757} + + }, { + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} + } +}; + +static u32 stv0367ter_get_mclk(struct stv0367_state *state, u32 ExtClk_Hz) +{ + u32 mclk_Hz = 0; /* master clock frequency (Hz) */ + u32 m, n, p; + + dprintk("%s:\n", __func__); + + if (stv0367_readbits(state, F367TER_BYPASS_PLLXN) == 0) { + n = (u32)stv0367_readbits(state, F367TER_PLL_NDIV); + if (n == 0) + n = n + 1; + + m = (u32)stv0367_readbits(state, F367TER_PLL_MDIV); + if (m == 0) + m = m + 1; + + p = (u32)stv0367_readbits(state, F367TER_PLL_PDIV); + if (p > 5) + p = 5; + + mclk_Hz = ((ExtClk_Hz / 2) * n) / (m * (1 << p)); + + dprintk("N=%d M=%d P=%d mclk_Hz=%d ExtClk_Hz=%d\n", + n, m, p, mclk_Hz, ExtClk_Hz); + } else + mclk_Hz = ExtClk_Hz; + + dprintk("%s: mclk_Hz=%d\n", __func__, mclk_Hz); + + return mclk_Hz; +} + +static int stv0367ter_filt_coeff_init(struct stv0367_state *state, + u16 CellsCoeffs[2][6][5], u32 DemodXtal) +{ + int i, j, k, freq; + + dprintk("%s:\n", __func__); + + freq = stv0367ter_get_mclk(state, DemodXtal); + + if (freq == 53125000) + k = 1; /* equivalent to Xtal 25M on 362*/ + else if (freq == 54000000) + k = 0; /* equivalent to Xtal 27M on 362*/ + else if (freq == 52500000) + k = 2; /* equivalent to Xtal 30M on 362*/ + else + return 0; + + for (i = 1; i <= 6; i++) { + stv0367_writebits(state, F367TER_IIR_CELL_NB, i - 1); + + for (j = 1; j <= 5; j++) { + stv0367_writereg(state, + (R367TER_IIRCX_COEFF1_MSB + 2 * (j - 1)), + MSB(CellsCoeffs[k][i-1][j-1])); + stv0367_writereg(state, + (R367TER_IIRCX_COEFF1_LSB + 2 * (j - 1)), + LSB(CellsCoeffs[k][i-1][j-1])); + } + } + + return 1; + +} + +static void stv0367ter_agc_iir_lock_detect_set(struct stv0367_state *state) +{ + dprintk("%s:\n", __func__); + + stv0367_writebits(state, F367TER_LOCK_DETECT_LSB, 0x00); + + /* Lock detect 1 */ + stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x00); + stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04); + + /* Lock detect 2 */ + stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x01); + stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04); + + /* Lock detect 3 */ + stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x02); + stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00); + + /* Lock detect 4 */ + stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x03); + stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00); + +} + +static int stv0367_iir_filt_init(struct stv0367_state *state, u8 Bandwidth, + u32 DemodXtalValue) +{ + dprintk("%s:\n", __func__); + + stv0367_writebits(state, F367TER_NRST_IIR, 0); + + switch (Bandwidth) { + case 6: + if (!stv0367ter_filt_coeff_init(state, + CellsCoeffs_6MHz_367cofdm, + DemodXtalValue)) + return 0; + break; + case 7: + if (!stv0367ter_filt_coeff_init(state, + CellsCoeffs_7MHz_367cofdm, + DemodXtalValue)) + return 0; + break; + case 8: + if (!stv0367ter_filt_coeff_init(state, + CellsCoeffs_8MHz_367cofdm, + DemodXtalValue)) + return 0; + break; + default: + return 0; + } + + stv0367_writebits(state, F367TER_NRST_IIR, 1); + + return 1; +} + +static void stv0367ter_agc_iir_rst(struct stv0367_state *state) +{ + + u8 com_n; + + dprintk("%s:\n", __func__); + + com_n = stv0367_readbits(state, F367TER_COM_N); + + stv0367_writebits(state, F367TER_COM_N, 0x07); + + stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x00); + stv0367_writebits(state, F367TER_COM_AGC_ON, 0x00); + + stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x01); + stv0367_writebits(state, F367TER_COM_AGC_ON, 0x01); + + stv0367_writebits(state, F367TER_COM_N, com_n); + +} + +static int stv0367ter_duration(s32 mode, int tempo1, int tempo2, int tempo3) +{ + int local_tempo = 0; + switch (mode) { + case 0: + local_tempo = tempo1; + break; + case 1: + local_tempo = tempo2; + break ; + + case 2: + local_tempo = tempo3; + break; + + default: + break; + } + /* msleep(local_tempo); */ + return local_tempo; +} + +static enum +stv0367_ter_signal_type stv0367ter_check_syr(struct stv0367_state *state) +{ + int wd = 100; + unsigned short int SYR_var; + s32 SYRStatus; + + dprintk("%s:\n", __func__); + + SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK); + + while ((!SYR_var) && (wd > 0)) { + usleep_range(2000, 3000); + wd -= 2; + SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK); + } + + if (!SYR_var) + SYRStatus = FE_TER_NOSYMBOL; + else + SYRStatus = FE_TER_SYMBOLOK; + + dprintk("stv0367ter_check_syr SYRStatus %s\n", + SYR_var == 0 ? "No Symbol" : "OK"); + + return SYRStatus; +} + +static enum +stv0367_ter_signal_type stv0367ter_check_cpamp(struct stv0367_state *state, + s32 FFTmode) +{ + + s32 CPAMPvalue = 0, CPAMPStatus, CPAMPMin; + int wd = 0; + + dprintk("%s:\n", __func__); + + switch (FFTmode) { + case 0: /*2k mode*/ + CPAMPMin = 20; + wd = 10; + break; + case 1: /*8k mode*/ + CPAMPMin = 80; + wd = 55; + break; + case 2: /*4k mode*/ + CPAMPMin = 40; + wd = 30; + break; + default: + CPAMPMin = 0xffff; /*drives to NOCPAMP */ + break; + } + + dprintk("%s: CPAMPMin=%d wd=%d\n", __func__, CPAMPMin, wd); + + CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT); + while ((CPAMPvalue < CPAMPMin) && (wd > 0)) { + usleep_range(1000, 2000); + wd -= 1; + CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT); + /*dprintk("CPAMPvalue= %d at wd=%d\n",CPAMPvalue,wd); */ + } + dprintk("******last CPAMPvalue= %d at wd=%d\n", CPAMPvalue, wd); + if (CPAMPvalue < CPAMPMin) { + CPAMPStatus = FE_TER_NOCPAMP; + printk(KERN_ERR "CPAMP failed\n"); + } else { + printk(KERN_ERR "CPAMP OK !\n"); + CPAMPStatus = FE_TER_CPAMPOK; + } + + return CPAMPStatus; +} + +enum +stv0367_ter_signal_type stv0367ter_lock_algo(struct stv0367_state *state) +{ + enum stv0367_ter_signal_type ret_flag; + short int wd, tempo; + u8 try, u_var1 = 0, u_var2 = 0, u_var3 = 0, u_var4 = 0, mode, guard; + u8 tmp, tmp2; + + dprintk("%s:\n", __func__); + + if (state == NULL) + return FE_TER_SWNOK; + + try = 0; + do { + ret_flag = FE_TER_LOCKOK; + + stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); + + if (state->config->if_iq_mode != 0) + stv0367_writebits(state, F367TER_COM_N, 0x07); + + stv0367_writebits(state, F367TER_GUARD, 3);/* suggest 2k 1/4 */ + stv0367_writebits(state, F367TER_MODE, 0); + stv0367_writebits(state, F367TER_SYR_TR_DIS, 0); + usleep_range(5000, 10000); + + stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); + + + if (stv0367ter_check_syr(state) == FE_TER_NOSYMBOL) + return FE_TER_NOSYMBOL; + else { /* + if chip locked on wrong mode first try, + it must lock correctly second try */ + mode = stv0367_readbits(state, F367TER_SYR_MODE); + if (stv0367ter_check_cpamp(state, mode) == + FE_TER_NOCPAMP) { + if (try == 0) + ret_flag = FE_TER_NOCPAMP; + + } + } + + try++; + } while ((try < 10) && (ret_flag != FE_TER_LOCKOK)); + + tmp = stv0367_readreg(state, R367TER_SYR_STAT); + tmp2 = stv0367_readreg(state, R367TER_STATUS); + dprintk("state=0x%x\n", (int)state); + dprintk("LOCK OK! mode=%d SYR_STAT=0x%x R367TER_STATUS=0x%x\n", + mode, tmp, tmp2); + + tmp = stv0367_readreg(state, R367TER_PRVIT); + tmp2 = stv0367_readreg(state, R367TER_I2CRPT); + dprintk("PRVIT=0x%x I2CRPT=0x%x\n", tmp, tmp2); + + tmp = stv0367_readreg(state, R367TER_GAIN_SRC1); + dprintk("GAIN_SRC1=0x%x\n", tmp); + + if ((mode != 0) && (mode != 1) && (mode != 2)) + return FE_TER_SWNOK; + + /*guard=stv0367_readbits(state,F367TER_SYR_GUARD); */ + + /*supress EPQ auto for SYR_GARD 1/16 or 1/32 + and set channel predictor in automatic */ +#if 0 + switch (guard) { + + case 0: + case 1: + stv0367_writebits(state, F367TER_AUTO_LE_EN, 0); + stv0367_writereg(state, R367TER_CHC_CTL, 0x01); + break; + case 2: + case 3: + stv0367_writebits(state, F367TER_AUTO_LE_EN, 1); + stv0367_writereg(state, R367TER_CHC_CTL, 0x11); + break; + + default: + return FE_TER_SWNOK; + } +#endif + + /*reset fec an reedsolo FOR 367 only*/ + stv0367_writebits(state, F367TER_RST_SFEC, 1); + stv0367_writebits(state, F367TER_RST_REEDSOLO, 1); + usleep_range(1000, 2000); + stv0367_writebits(state, F367TER_RST_SFEC, 0); + stv0367_writebits(state, F367TER_RST_REEDSOLO, 0); + + u_var1 = stv0367_readbits(state, F367TER_LK); + u_var2 = stv0367_readbits(state, F367TER_PRF); + u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK); + /* u_var4=stv0367_readbits(state,F367TER_TSFIFO_LINEOK); */ + + wd = stv0367ter_duration(mode, 125, 500, 250); + tempo = stv0367ter_duration(mode, 4, 16, 8); + + /*while ( ((!u_var1)||(!u_var2)||(!u_var3)||(!u_var4)) && (wd>=0)) */ + while (((!u_var1) || (!u_var2) || (!u_var3)) && (wd >= 0)) { + usleep_range(1000 * tempo, 1000 * (tempo + 1)); + wd -= tempo; + u_var1 = stv0367_readbits(state, F367TER_LK); + u_var2 = stv0367_readbits(state, F367TER_PRF); + u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK); + /*u_var4=stv0367_readbits(state, F367TER_TSFIFO_LINEOK); */ + } + + if (!u_var1) + return FE_TER_NOLOCK; + + + if (!u_var2) + return FE_TER_NOPRFOUND; + + if (!u_var3) + return FE_TER_NOTPS; + + guard = stv0367_readbits(state, F367TER_SYR_GUARD); + stv0367_writereg(state, R367TER_CHC_CTL, 0x11); + switch (guard) { + case 0: + case 1: + stv0367_writebits(state, F367TER_AUTO_LE_EN, 0); + /*stv0367_writereg(state,R367TER_CHC_CTL, 0x1);*/ + stv0367_writebits(state, F367TER_SYR_FILTER, 0); + break; + case 2: + case 3: + stv0367_writebits(state, F367TER_AUTO_LE_EN, 1); + /*stv0367_writereg(state,R367TER_CHC_CTL, 0x11);*/ + stv0367_writebits(state, F367TER_SYR_FILTER, 1); + break; + + default: + return FE_TER_SWNOK; + } + + /* apply Sfec workaround if 8K 64QAM CR!=1/2*/ + if ((stv0367_readbits(state, F367TER_TPS_CONST) == 2) && + (mode == 1) && + (stv0367_readbits(state, F367TER_TPS_HPCODE) != 0)) { + stv0367_writereg(state, R367TER_SFDLYSETH, 0xc0); + stv0367_writereg(state, R367TER_SFDLYSETM, 0x60); + stv0367_writereg(state, R367TER_SFDLYSETL, 0x0); + } else + stv0367_writereg(state, R367TER_SFDLYSETH, 0x0); + + wd = stv0367ter_duration(mode, 125, 500, 250); + u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK); + + while ((!u_var4) && (wd >= 0)) { + usleep_range(1000 * tempo, 1000 * (tempo + 1)); + wd -= tempo; + u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK); + } + + if (!u_var4) + return FE_TER_NOLOCK; + + /* for 367 leave COM_N at 0x7 for IQ_mode*/ + /*if(ter_state->if_iq_mode!=FE_TER_NORMAL_IF_TUNER) { + tempo=0; + while ((stv0367_readbits(state,F367TER_COM_USEGAINTRK)!=1) && + (stv0367_readbits(state,F367TER_COM_AGCLOCK)!=1)&&(tempo<100)) { + ChipWaitOrAbort(state,1); + tempo+=1; + } + + stv0367_writebits(state,F367TER_COM_N,0x17); + } */ + + stv0367_writebits(state, F367TER_SYR_TR_DIS, 1); + + dprintk("FE_TER_LOCKOK !!!\n"); + + return FE_TER_LOCKOK; + +} + +static void stv0367ter_set_ts_mode(struct stv0367_state *state, + enum stv0367_ts_mode PathTS) +{ + + dprintk("%s:\n", __func__); + + if (state == NULL) + return; + + stv0367_writebits(state, F367TER_TS_DIS, 0); + switch (PathTS) { + default: + /*for removing warning :default we can assume in parallel mode*/ + case STV0367_PARALLEL_PUNCT_CLOCK: + stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 0); + stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 0); + break; + case STV0367_SERIAL_PUNCT_CLOCK: + stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 1); + stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 1); + break; + } +} + +static void stv0367ter_set_clk_pol(struct stv0367_state *state, + enum stv0367_clk_pol clock) +{ + + dprintk("%s:\n", __func__); + + if (state == NULL) + return; + + switch (clock) { + case STV0367_RISINGEDGE_CLOCK: + stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 1); + break; + case STV0367_FALLINGEDGE_CLOCK: + stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0); + break; + /*case FE_TER_CLOCK_POLARITY_DEFAULT:*/ + default: + stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0); + break; + } +} + +#if 0 +static void stv0367ter_core_sw(struct stv0367_state *state) +{ + + dprintk("%s:\n", __func__); + + stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); + stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); + msleep(350); +} +#endif +static int stv0367ter_standby(struct dvb_frontend *fe, u8 standby_on) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + if (standby_on) { + stv0367_writebits(state, F367TER_STDBY, 1); + stv0367_writebits(state, F367TER_STDBY_FEC, 1); + stv0367_writebits(state, F367TER_STDBY_CORE, 1); + } else { + stv0367_writebits(state, F367TER_STDBY, 0); + stv0367_writebits(state, F367TER_STDBY_FEC, 0); + stv0367_writebits(state, F367TER_STDBY_CORE, 0); + } + + return 0; +} + +static int stv0367ter_sleep(struct dvb_frontend *fe) +{ + return stv0367ter_standby(fe, 1); +} + +int stv0367ter_init(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + int i; + + dprintk("%s:\n", __func__); + + ter_state->pBER = 0; + + for (i = 0; i < STV0367TER_NBREGS; i++) + stv0367_writereg(state, def0367ter[i].addr, + def0367ter[i].value); + + switch (state->config->xtal) { + /*set internal freq to 53.125MHz */ + case 25000000: + stv0367_writereg(state, R367TER_PLLMDIV, 0xa); + stv0367_writereg(state, R367TER_PLLNDIV, 0x55); + stv0367_writereg(state, R367TER_PLLSETUP, 0x18); + break; + default: + case 27000000: + dprintk("FE_STV0367TER_SetCLKgen for 27Mhz\n"); + stv0367_writereg(state, R367TER_PLLMDIV, 0x1); + stv0367_writereg(state, R367TER_PLLNDIV, 0x8); + stv0367_writereg(state, R367TER_PLLSETUP, 0x18); + break; + case 30000000: + stv0367_writereg(state, R367TER_PLLMDIV, 0xc); + stv0367_writereg(state, R367TER_PLLNDIV, 0x55); + stv0367_writereg(state, R367TER_PLLSETUP, 0x18); + break; + } + + stv0367_writereg(state, R367TER_I2CRPT, 0xa0); + stv0367_writereg(state, R367TER_ANACTRL, 0x00); + + /*Set TS1 and TS2 to serial or parallel mode */ + stv0367ter_set_ts_mode(state, state->config->ts_mode); + stv0367ter_set_clk_pol(state, state->config->clk_pol); + + state->chip_id = stv0367_readreg(state, R367TER_ID); + ter_state->first_lock = 0; + ter_state->unlock_counter = 2; + + return 0; +} + +static int stv0367ter_algo(struct dvb_frontend *fe, + struct dvb_frontend_parameters *param) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + int offset = 0, tempo = 0; + u8 u_var; + u8 /*constell,*/ counter, tps_rcvd[2]; + s8 step; + s32 timing_offset = 0; + u32 trl_nomrate = 0, InternalFreq = 0, temp = 0; + + dprintk("%s:\n", __func__); + + ter_state->frequency = param->frequency; + ter_state->force = FE_TER_FORCENONE + + stv0367_readbits(state, F367TER_FORCE) * 2; + ter_state->if_iq_mode = state->config->if_iq_mode; + switch (state->config->if_iq_mode) { + case FE_TER_NORMAL_IF_TUNER: /* Normal IF mode */ + dprintk("ALGO: FE_TER_NORMAL_IF_TUNER selected\n"); + stv0367_writebits(state, F367TER_TUNER_BB, 0); + stv0367_writebits(state, F367TER_LONGPATH_IF, 0); + stv0367_writebits(state, F367TER_DEMUX_SWAP, 0); + break; + case FE_TER_LONGPATH_IF_TUNER: /* Long IF mode */ + dprintk("ALGO: FE_TER_LONGPATH_IF_TUNER selected\n"); + stv0367_writebits(state, F367TER_TUNER_BB, 0); + stv0367_writebits(state, F367TER_LONGPATH_IF, 1); + stv0367_writebits(state, F367TER_DEMUX_SWAP, 1); + break; + case FE_TER_IQ_TUNER: /* IQ mode */ + dprintk("ALGO: FE_TER_IQ_TUNER selected\n"); + stv0367_writebits(state, F367TER_TUNER_BB, 1); + stv0367_writebits(state, F367TER_PPM_INVSEL, 0); + break; + default: + printk(KERN_ERR "ALGO: wrong TUNER type selected\n"); + return -EINVAL; + } + + usleep_range(5000, 7000); + + switch (param->inversion) { + case INVERSION_AUTO: + default: + dprintk("%s: inversion AUTO\n", __func__); + if (ter_state->if_iq_mode == FE_TER_IQ_TUNER) + stv0367_writebits(state, F367TER_IQ_INVERT, + ter_state->sense); + else + stv0367_writebits(state, F367TER_INV_SPECTR, + ter_state->sense); + + break; + case INVERSION_ON: + case INVERSION_OFF: + if (ter_state->if_iq_mode == FE_TER_IQ_TUNER) + stv0367_writebits(state, F367TER_IQ_INVERT, + param->inversion); + else + stv0367_writebits(state, F367TER_INV_SPECTR, + param->inversion); + + break; + } + + if ((ter_state->if_iq_mode != FE_TER_NORMAL_IF_TUNER) && + (ter_state->pBW != ter_state->bw)) { + stv0367ter_agc_iir_lock_detect_set(state); + + /*set fine agc target to 180 for LPIF or IQ mode*/ + /* set Q_AGCTarget */ + stv0367_writebits(state, F367TER_SEL_IQNTAR, 1); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB); + /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */ + + /* set Q_AGCTarget */ + stv0367_writebits(state, F367TER_SEL_IQNTAR, 0); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB); + /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */ + + if (!stv0367_iir_filt_init(state, ter_state->bw, + state->config->xtal)) + return -EINVAL; + /*set IIR filter once for 6,7 or 8MHz BW*/ + ter_state->pBW = ter_state->bw; + + stv0367ter_agc_iir_rst(state); + } + + if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO) + stv0367_writebits(state, F367TER_BDI_LPSEL, 0x01); + else + stv0367_writebits(state, F367TER_BDI_LPSEL, 0x00); + + InternalFreq = stv0367ter_get_mclk(state, state->config->xtal) / 1000; + temp = (int) + ((((ter_state->bw * 64 * (1 << 15) * 100) + / (InternalFreq)) * 10) / 7); + + stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB, temp % 2); + temp = temp / 2; + stv0367_writebits(state, F367TER_TRL_NOMRATE_HI, temp / 256); + stv0367_writebits(state, F367TER_TRL_NOMRATE_LO, temp % 256); + + temp = stv0367_readbits(state, F367TER_TRL_NOMRATE_HI) * 512 + + stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2 + + stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB); + temp = (int)(((1 << 17) * ter_state->bw * 1000) / (7 * (InternalFreq))); + stv0367_writebits(state, F367TER_GAIN_SRC_HI, temp / 256); + stv0367_writebits(state, F367TER_GAIN_SRC_LO, temp % 256); + temp = stv0367_readbits(state, F367TER_GAIN_SRC_HI) * 256 + + stv0367_readbits(state, F367TER_GAIN_SRC_LO); + + temp = (int) + ((InternalFreq - state->config->if_khz) * (1 << 16) + / (InternalFreq)); + + dprintk("DEROT temp=0x%x\n", temp); + stv0367_writebits(state, F367TER_INC_DEROT_HI, temp / 256); + stv0367_writebits(state, F367TER_INC_DEROT_LO, temp % 256); + + ter_state->echo_pos = 0; + ter_state->ucblocks = 0; /* liplianin */ + ter_state->pBER = 0; /* liplianin */ + stv0367_writebits(state, F367TER_LONG_ECHO, ter_state->echo_pos); + + if (stv0367ter_lock_algo(state) != FE_TER_LOCKOK) + return 0; + + ter_state->state = FE_TER_LOCKOK; + /* update results */ + tps_rcvd[0] = stv0367_readreg(state, R367TER_TPS_RCVD2); + tps_rcvd[1] = stv0367_readreg(state, R367TER_TPS_RCVD3); + + ter_state->mode = stv0367_readbits(state, F367TER_SYR_MODE); + ter_state->guard = stv0367_readbits(state, F367TER_SYR_GUARD); + + ter_state->first_lock = 1; /* we know sense now :) */ + + ter_state->agc_val = + (stv0367_readbits(state, F367TER_AGC1_VAL_LO) << 16) + + (stv0367_readbits(state, F367TER_AGC1_VAL_HI) << 24) + + stv0367_readbits(state, F367TER_AGC2_VAL_LO) + + (stv0367_readbits(state, F367TER_AGC2_VAL_HI) << 8); + + /* Carrier offset calculation */ + stv0367_writebits(state, F367TER_FREEZE, 1); + offset = (stv0367_readbits(state, F367TER_CRL_FOFFSET_VHI) << 16) ; + offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_HI) << 8); + offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_LO)); + stv0367_writebits(state, F367TER_FREEZE, 0); + if (offset > 8388607) + offset -= 16777216; + + offset = offset * 2 / 16384; + + if (ter_state->mode == FE_TER_MODE_2K) + offset = (offset * 4464) / 1000;/*** 1 FFT BIN=4.464khz***/ + else if (ter_state->mode == FE_TER_MODE_4K) + offset = (offset * 223) / 100;/*** 1 FFT BIN=2.23khz***/ + else if (ter_state->mode == FE_TER_MODE_8K) + offset = (offset * 111) / 100;/*** 1 FFT BIN=1.1khz***/ + + if (stv0367_readbits(state, F367TER_PPM_INVSEL) == 1) { + if ((stv0367_readbits(state, F367TER_INV_SPECTR) == + (stv0367_readbits(state, + F367TER_STATUS_INV_SPECRUM) == 1))) + offset = offset * -1; + } + + if (ter_state->bw == 6) + offset = (offset * 6) / 8; + else if (ter_state->bw == 7) + offset = (offset * 7) / 8; + + ter_state->frequency += offset; + + tempo = 10; /* exit even if timing_offset stays null */ + while ((timing_offset == 0) && (tempo > 0)) { + usleep_range(10000, 20000); /*was 20ms */ + /* fine tuning of timing offset if required */ + timing_offset = stv0367_readbits(state, F367TER_TRL_TOFFSET_LO) + + 256 * stv0367_readbits(state, + F367TER_TRL_TOFFSET_HI); + if (timing_offset >= 32768) + timing_offset -= 65536; + trl_nomrate = (512 * stv0367_readbits(state, + F367TER_TRL_NOMRATE_HI) + + stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2 + + stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB)); + + timing_offset = ((signed)(1000000 / trl_nomrate) * + timing_offset) / 2048; + tempo--; + } + + if (timing_offset <= 0) { + timing_offset = (timing_offset - 11) / 22; + step = -1; + } else { + timing_offset = (timing_offset + 11) / 22; + step = 1; + } + + for (counter = 0; counter < abs(timing_offset); counter++) { + trl_nomrate += step; + stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB, + trl_nomrate % 2); + stv0367_writebits(state, F367TER_TRL_NOMRATE_LO, + trl_nomrate / 2); + usleep_range(1000, 2000); + } + + usleep_range(5000, 6000); + /* unlocks could happen in case of trl centring big step, + then a core off/on restarts demod */ + u_var = stv0367_readbits(state, F367TER_LK); + + if (!u_var) { + stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); + msleep(20); + stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); + } + + return 0; +} + +static int stv0367ter_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *param) +{ + struct dvb_ofdm_parameters *op = ¶m->u.ofdm; + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + + /*u8 trials[2]; */ + s8 num_trials, index; + u8 SenseTrials[] = { INVERSION_ON, INVERSION_OFF }; + + stv0367ter_init(fe); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe, param); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + switch (op->transmission_mode) { + default: + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_2K: + ter_state->mode = FE_TER_MODE_2K; + break; +/* case TRANSMISSION_MODE_4K: + pLook.mode = FE_TER_MODE_4K; + break;*/ + case TRANSMISSION_MODE_8K: + ter_state->mode = FE_TER_MODE_8K; + break; + } + + switch (op->guard_interval) { + default: + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_1_16: + case GUARD_INTERVAL_1_8: + case GUARD_INTERVAL_1_4: + ter_state->guard = op->guard_interval; + break; + case GUARD_INTERVAL_AUTO: + ter_state->guard = GUARD_INTERVAL_1_32; + break; + } + + switch (op->bandwidth) { + case BANDWIDTH_6_MHZ: + ter_state->bw = FE_TER_CHAN_BW_6M; + break; + case BANDWIDTH_7_MHZ: + ter_state->bw = FE_TER_CHAN_BW_7M; + break; + case BANDWIDTH_8_MHZ: + default: + ter_state->bw = FE_TER_CHAN_BW_8M; + } + + ter_state->hierarchy = FE_TER_HIER_NONE; + + switch (param->inversion) { + case INVERSION_OFF: + case INVERSION_ON: + num_trials = 1; + break; + default: + num_trials = 2; + if (ter_state->first_lock) + num_trials = 1; + break; + } + + ter_state->state = FE_TER_NOLOCK; + index = 0; + + while (((index) < num_trials) && (ter_state->state != FE_TER_LOCKOK)) { + if (!ter_state->first_lock) { + if (param->inversion == INVERSION_AUTO) + ter_state->sense = SenseTrials[index]; + + } + stv0367ter_algo(fe,/* &pLook, result,*/ param); + + if ((ter_state->state == FE_TER_LOCKOK) && + (param->inversion == INVERSION_AUTO) && + (index == 1)) { + /* invert spectrum sense */ + SenseTrials[index] = SenseTrials[0]; + SenseTrials[(index + 1) % 2] = (SenseTrials[1] + 1) % 2; + } + + index++; + } + + return 0; +} + +static int stv0367ter_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + u32 errs = 0; + + /*wait for counting completion*/ + if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0) { + errs = + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1) + * (1 << 16)) + + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI) + * (1 << 8)) + + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO)); + ter_state->ucblocks = errs; + } + + (*ucblocks) = ter_state->ucblocks; + + return 0; +} + +static int stv0367ter_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *param) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + struct dvb_ofdm_parameters *op = ¶m->u.ofdm; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + int error = 0; + enum stv0367_ter_mode mode; + int constell = 0,/* snr = 0,*/ Data = 0; + + param->frequency = stv0367_get_tuner_freq(fe); + if (param->frequency < 0) + param->frequency = c->frequency; + + constell = stv0367_readbits(state, F367TER_TPS_CONST); + if (constell == 0) + op->constellation = QPSK; + else if (constell == 1) + op->constellation = QAM_16; + else + op->constellation = QAM_64; + + param->inversion = stv0367_readbits(state, F367TER_INV_SPECTR); + + /* Get the Hierarchical mode */ + Data = stv0367_readbits(state, F367TER_TPS_HIERMODE); + + switch (Data) { + case 0: + op->hierarchy_information = HIERARCHY_NONE; + break; + case 1: + op->hierarchy_information = HIERARCHY_1; + break; + case 2: + op->hierarchy_information = HIERARCHY_2; + break; + case 3: + op->hierarchy_information = HIERARCHY_4; + break; + default: + op->hierarchy_information = HIERARCHY_AUTO; + break; /* error */ + } + + /* Get the FEC Rate */ + if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO) + Data = stv0367_readbits(state, F367TER_TPS_LPCODE); + else + Data = stv0367_readbits(state, F367TER_TPS_HPCODE); + + switch (Data) { + case 0: + op->code_rate_HP = FEC_1_2; + break; + case 1: + op->code_rate_HP = FEC_2_3; + break; + case 2: + op->code_rate_HP = FEC_3_4; + break; + case 3: + op->code_rate_HP = FEC_5_6; + break; + case 4: + op->code_rate_HP = FEC_7_8; + break; + default: + op->code_rate_HP = FEC_AUTO; + break; /* error */ + } + + mode = stv0367_readbits(state, F367TER_SYR_MODE); + + switch (mode) { + case FE_TER_MODE_2K: + op->transmission_mode = TRANSMISSION_MODE_2K; + break; +/* case FE_TER_MODE_4K: + op->transmission_mode = TRANSMISSION_MODE_4K; + break;*/ + case FE_TER_MODE_8K: + op->transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + op->transmission_mode = TRANSMISSION_MODE_AUTO; + } + + op->guard_interval = stv0367_readbits(state, F367TER_SYR_GUARD); + + return error; +} + +static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct stv0367_state *state = fe->demodulator_priv; + u32 snru32 = 0; + int cpt = 0; + u8 cut = stv0367_readbits(state, F367TER_IDENTIFICATIONREG); + + while (cpt < 10) { + usleep_range(2000, 3000); + if (cut == 0x50) /*cut 1.0 cut 1.1*/ + snru32 += stv0367_readbits(state, F367TER_CHCSNR) / 4; + else /*cu2.0*/ + snru32 += 125 * stv0367_readbits(state, F367TER_CHCSNR); + + cpt++; + } + + snru32 /= 10;/*average on 10 values*/ + + *snr = snru32 / 1000; + + return 0; +} + +#if 0 +static int stv0367ter_status(struct dvb_frontend *fe) +{ + + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + int locked = FALSE; + + locked = (stv0367_readbits(state, F367TER_LK)); + if (!locked) + ter_state->unlock_counter += 1; + else + ter_state->unlock_counter = 0; + + if (ter_state->unlock_counter > 2) { + if (!stv0367_readbits(state, F367TER_TPS_LOCK) || + (!stv0367_readbits(state, F367TER_LK))) { + stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); + usleep_range(2000, 3000); + stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); + msleep(350); + locked = (stv0367_readbits(state, F367TER_TPS_LOCK)) && + (stv0367_readbits(state, F367TER_LK)); + } + + } + + return locked; +} +#endif +static int stv0367ter_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + *status = 0; + + if (stv0367_readbits(state, F367TER_LK)) { + *status |= FE_HAS_LOCK; + dprintk("%s: stv0367 has locked\n", __func__); + } + + return 0; +} + +static int stv0367ter_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + u32 Errors = 0, tber = 0, temporary = 0; + int abc = 0, def = 0; + + + /*wait for counting completion*/ + if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0) + Errors = ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT) + * (1 << 16)) + + ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT_HI) + * (1 << 8)) + + ((u32)stv0367_readbits(state, + F367TER_SFEC_ERR_CNT_LO)); + /*measurement not completed, load previous value*/ + else { + tber = ter_state->pBER; + return 0; + } + + abc = stv0367_readbits(state, F367TER_SFEC_ERR_SOURCE); + def = stv0367_readbits(state, F367TER_SFEC_NUM_EVENT); + + if (Errors == 0) { + tber = 0; + } else if (abc == 0x7) { + if (Errors <= 4) { + temporary = (Errors * 1000000000) / (8 * (1 << 14)); + temporary = temporary; + } else if (Errors <= 42) { + temporary = (Errors * 100000000) / (8 * (1 << 14)); + temporary = temporary * 10; + } else if (Errors <= 429) { + temporary = (Errors * 10000000) / (8 * (1 << 14)); + temporary = temporary * 100; + } else if (Errors <= 4294) { + temporary = (Errors * 1000000) / (8 * (1 << 14)); + temporary = temporary * 1000; + } else if (Errors <= 42949) { + temporary = (Errors * 100000) / (8 * (1 << 14)); + temporary = temporary * 10000; + } else if (Errors <= 429496) { + temporary = (Errors * 10000) / (8 * (1 << 14)); + temporary = temporary * 100000; + } else { /*if (Errors<4294967) 2^22 max error*/ + temporary = (Errors * 1000) / (8 * (1 << 14)); + temporary = temporary * 100000; /* still to *10 */ + } + + /* Byte error*/ + if (def == 2) + /*tber=Errors/(8*(1 <<14));*/ + tber = temporary; + else if (def == 3) + /*tber=Errors/(8*(1 <<16));*/ + tber = temporary / 4; + else if (def == 4) + /*tber=Errors/(8*(1 <<18));*/ + tber = temporary / 16; + else if (def == 5) + /*tber=Errors/(8*(1 <<20));*/ + tber = temporary / 64; + else if (def == 6) + /*tber=Errors/(8*(1 <<22));*/ + tber = temporary / 256; + else + /* should not pass here*/ + tber = 0; + + if ((Errors < 4294967) && (Errors > 429496)) + tber *= 10; + + } + + /* save actual value */ + ter_state->pBER = tber; + + (*ber) = tber; + + return 0; +} +#if 0 +static u32 stv0367ter_get_per(struct stv0367_state *state) +{ + struct stv0367ter_state *ter_state = state->ter_state; + u32 Errors = 0, Per = 0, temporary = 0; + int abc = 0, def = 0, cpt = 0; + + while (((stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 1) && + (cpt < 400)) || ((Errors == 0) && (cpt < 400))) { + usleep_range(1000, 2000); + Errors = ((u32)stv0367_readbits(state, F367TER_ERR_CNT1) + * (1 << 16)) + + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI) + * (1 << 8)) + + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO)); + cpt++; + } + abc = stv0367_readbits(state, F367TER_ERR_SRC1); + def = stv0367_readbits(state, F367TER_NUM_EVT1); + + if (Errors == 0) + Per = 0; + else if (abc == 0x9) { + if (Errors <= 4) { + temporary = (Errors * 1000000000) / (8 * (1 << 8)); + temporary = temporary; + } else if (Errors <= 42) { + temporary = (Errors * 100000000) / (8 * (1 << 8)); + temporary = temporary * 10; + } else if (Errors <= 429) { + temporary = (Errors * 10000000) / (8 * (1 << 8)); + temporary = temporary * 100; + } else if (Errors <= 4294) { + temporary = (Errors * 1000000) / (8 * (1 << 8)); + temporary = temporary * 1000; + } else if (Errors <= 42949) { + temporary = (Errors * 100000) / (8 * (1 << 8)); + temporary = temporary * 10000; + } else { /*if(Errors<=429496) 2^16 errors max*/ + temporary = (Errors * 10000) / (8 * (1 << 8)); + temporary = temporary * 100000; + } + + /* pkt error*/ + if (def == 2) + /*Per=Errors/(1 << 8);*/ + Per = temporary; + else if (def == 3) + /*Per=Errors/(1 << 10);*/ + Per = temporary / 4; + else if (def == 4) + /*Per=Errors/(1 << 12);*/ + Per = temporary / 16; + else if (def == 5) + /*Per=Errors/(1 << 14);*/ + Per = temporary / 64; + else if (def == 6) + /*Per=Errors/(1 << 16);*/ + Per = temporary / 256; + else + Per = 0; + + } + /* save actual value */ + ter_state->pPER = Per; + + return Per; +} +#endif +static int stv0367_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 1000; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + + return 0; +} + +static void stv0367_release(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + + kfree(state->ter_state); + kfree(state->cab_state); + kfree(state); +} + +static struct dvb_frontend_ops stv0367ter_ops = { + .info = { + .name = "ST STV0367 DVB-T", + .type = FE_OFDM, + .frequency_min = 47000000, + .frequency_max = 862000000, + .frequency_stepsize = 15625, + .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_128 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER | + FE_CAN_INVERSION_AUTO | + FE_CAN_MUTE_TS + }, + .release = stv0367_release, + .init = stv0367ter_init, + .sleep = stv0367ter_sleep, + .i2c_gate_ctrl = stv0367ter_gate_ctrl, + .set_frontend = stv0367ter_set_frontend, + .get_frontend = stv0367ter_get_frontend, + .get_tune_settings = stv0367_get_tune_settings, + .read_status = stv0367ter_read_status, + .read_ber = stv0367ter_read_ber,/* too slow */ +/* .read_signal_strength = stv0367_read_signal_strength,*/ + .read_snr = stv0367ter_read_snr, + .read_ucblocks = stv0367ter_read_ucblocks, +}; + +struct dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + struct stv0367_state *state = NULL; + struct stv0367ter_state *ter_state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL); + if (state == NULL) + goto error; + ter_state = kzalloc(sizeof(struct stv0367ter_state), GFP_KERNEL); + if (ter_state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + state->config = config; + state->ter_state = ter_state; + state->fe.ops = stv0367ter_ops; + state->fe.demodulator_priv = state; + state->chip_id = stv0367_readreg(state, 0xf000); + + dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id); + + /* check if the demod is there */ + if ((state->chip_id != 0x50) && (state->chip_id != 0x60)) + goto error; + + return &state->fe; + +error: + kfree(ter_state); + kfree(state); + return NULL; +} +EXPORT_SYMBOL(stv0367ter_attach); + +static int stv0367cab_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + stv0367_writebits(state, F367CAB_I2CT_ON, (enable > 0) ? 1 : 0); + + return 0; +} + +static u32 stv0367cab_get_mclk(struct dvb_frontend *fe, u32 ExtClk_Hz) +{ + struct stv0367_state *state = fe->demodulator_priv; + u32 mclk_Hz = 0;/* master clock frequency (Hz) */ + u32 M, N, P; + + + if (stv0367_readbits(state, F367CAB_BYPASS_PLLXN) == 0) { + N = (u32)stv0367_readbits(state, F367CAB_PLL_NDIV); + if (N == 0) + N = N + 1; + + M = (u32)stv0367_readbits(state, F367CAB_PLL_MDIV); + if (M == 0) + M = M + 1; + + P = (u32)stv0367_readbits(state, F367CAB_PLL_PDIV); + + if (P > 5) + P = 5; + + mclk_Hz = ((ExtClk_Hz / 2) * N) / (M * (1 << P)); + dprintk("stv0367cab_get_mclk BYPASS_PLLXN mclk_Hz=%d\n", + mclk_Hz); + } else + mclk_Hz = ExtClk_Hz; + + dprintk("stv0367cab_get_mclk final mclk_Hz=%d\n", mclk_Hz); + + return mclk_Hz; +} + +static u32 stv0367cab_get_adc_freq(struct dvb_frontend *fe, u32 ExtClk_Hz) +{ + u32 ADCClk_Hz = ExtClk_Hz; + + ADCClk_Hz = stv0367cab_get_mclk(fe, ExtClk_Hz); + + return ADCClk_Hz; +} + +enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state, + u32 SymbolRate, + enum stv0367cab_mod QAMSize) +{ + /* Set QAM size */ + stv0367_writebits(state, F367CAB_QAM_MODE, QAMSize); + + /* Set Registers settings specific to the QAM size */ + switch (QAMSize) { + case FE_CAB_MOD_QAM4: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + break; + case FE_CAB_MOD_QAM16: + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x64); + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + stv0367_writereg(state, R367CAB_FSM_STATE, 0x90); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x8a); + break; + case FE_CAB_MOD_QAM32: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x6e); + stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xb7); + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x9d); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); + break; + case FE_CAB_MOD_QAM64: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x82); + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a); + if (SymbolRate > 45000000) { + stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa5); + } else if (SymbolRate > 25000000) { + stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6); + } else { + stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); + } + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x99); + break; + case FE_CAB_MOD_QAM128: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x76); + stv0367_writereg(state, R367CAB_FSM_STATE, 0x90); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xb1); + if (SymbolRate > 45000000) + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); + else if (SymbolRate > 25000000) + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6); + else + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0x97); + + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x8e); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); + break; + case FE_CAB_MOD_QAM256: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x94); + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a); + stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); + if (SymbolRate > 45000000) + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + else if (SymbolRate > 25000000) + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + else + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1); + + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x85); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); + break; + case FE_CAB_MOD_QAM512: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + break; + case FE_CAB_MOD_QAM1024: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + break; + default: + break; + } + + return QAMSize; +} + +static u32 stv0367cab_set_derot_freq(struct stv0367_state *state, + u32 adc_hz, s32 derot_hz) +{ + u32 sampled_if = 0; + u32 adc_khz; + + adc_khz = adc_hz / 1000; + + dprintk("%s: adc_hz=%d derot_hz=%d\n", __func__, adc_hz, derot_hz); + + if (adc_khz != 0) { + if (derot_hz < 1000000) + derot_hz = adc_hz / 4; /* ZIF operation */ + if (derot_hz > adc_hz) + derot_hz = derot_hz - adc_hz; + sampled_if = (u32)derot_hz / 1000; + sampled_if *= 32768; + sampled_if /= adc_khz; + sampled_if *= 256; + } + + if (sampled_if > 8388607) + sampled_if = 8388607; + + dprintk("%s: sampled_if=0x%x\n", __func__, sampled_if); + + stv0367_writereg(state, R367CAB_MIX_NCO_LL, sampled_if); + stv0367_writereg(state, R367CAB_MIX_NCO_HL, (sampled_if >> 8)); + stv0367_writebits(state, F367CAB_MIX_NCO_INC_HH, (sampled_if >> 16)); + + return derot_hz; +} + +static u32 stv0367cab_get_derot_freq(struct stv0367_state *state, u32 adc_hz) +{ + u32 sampled_if; + + sampled_if = stv0367_readbits(state, F367CAB_MIX_NCO_INC_LL) + + (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HL) << 8) + + (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HH) << 16); + + sampled_if /= 256; + sampled_if *= (adc_hz / 1000); + sampled_if += 1; + sampled_if /= 32768; + + return sampled_if; +} + +static u32 stv0367cab_set_srate(struct stv0367_state *state, u32 adc_hz, + u32 mclk_hz, u32 SymbolRate, + enum stv0367cab_mod QAMSize) +{ + u32 QamSizeCorr = 0; + u32 u32_tmp = 0, u32_tmp1 = 0; + u32 adp_khz; + + dprintk("%s:\n", __func__); + + /* Set Correction factor of SRC gain */ + switch (QAMSize) { + case FE_CAB_MOD_QAM4: + QamSizeCorr = 1110; + break; + case FE_CAB_MOD_QAM16: + QamSizeCorr = 1032; + break; + case FE_CAB_MOD_QAM32: + QamSizeCorr = 954; + break; + case FE_CAB_MOD_QAM64: + QamSizeCorr = 983; + break; + case FE_CAB_MOD_QAM128: + QamSizeCorr = 957; + break; + case FE_CAB_MOD_QAM256: + QamSizeCorr = 948; + break; + case FE_CAB_MOD_QAM512: + QamSizeCorr = 0; + break; + case FE_CAB_MOD_QAM1024: + QamSizeCorr = 944; + break; + default: + break; + } + + /* Transfer ratio calculation */ + if (adc_hz != 0) { + u32_tmp = 256 * SymbolRate; + u32_tmp = u32_tmp / adc_hz; + } + stv0367_writereg(state, R367CAB_EQU_CRL_TFR, (u8)u32_tmp); + + /* Symbol rate and SRC gain calculation */ + adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */ + if (adp_khz != 0) { + u32_tmp = SymbolRate; + u32_tmp1 = SymbolRate; + + if (u32_tmp < 2097152) { /* 2097152 = 2^21 */ + /* Symbol rate calculation */ + u32_tmp *= 2048; /* 2048 = 2^11 */ + u32_tmp = u32_tmp / adp_khz; + u32_tmp = u32_tmp * 16384; /* 16384 = 2^14 */ + u32_tmp /= 125 ; /* 125 = 1000/2^3 */ + u32_tmp = u32_tmp * 8; /* 8 = 2^3 */ + + /* SRC Gain Calculation */ + u32_tmp1 *= 2048; /* *2*2^10 */ + u32_tmp1 /= 439; /* *2/878 */ + u32_tmp1 *= 256; /* *2^8 */ + u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ + u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ + u32_tmp1 = u32_tmp1 / 10000000; + + } else if (u32_tmp < 4194304) { /* 4194304 = 2**22 */ + /* Symbol rate calculation */ + u32_tmp *= 1024 ; /* 1024 = 2**10 */ + u32_tmp = u32_tmp / adp_khz; + u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */ + u32_tmp /= 125 ; /* 125 = 1000/2**3 */ + u32_tmp = u32_tmp * 16; /* 16 = 2**4 */ + + /* SRC Gain Calculation */ + u32_tmp1 *= 1024; /* *2*2^9 */ + u32_tmp1 /= 439; /* *2/878 */ + u32_tmp1 *= 256; /* *2^8 */ + u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz)*/ + u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ + u32_tmp1 = u32_tmp1 / 5000000; + } else if (u32_tmp < 8388607) { /* 8388607 = 2**23 */ + /* Symbol rate calculation */ + u32_tmp *= 512 ; /* 512 = 2**9 */ + u32_tmp = u32_tmp / adp_khz; + u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */ + u32_tmp /= 125 ; /* 125 = 1000/2**3 */ + u32_tmp = u32_tmp * 32; /* 32 = 2**5 */ + + /* SRC Gain Calculation */ + u32_tmp1 *= 512; /* *2*2^8 */ + u32_tmp1 /= 439; /* *2/878 */ + u32_tmp1 *= 256; /* *2^8 */ + u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ + u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ + u32_tmp1 = u32_tmp1 / 2500000; + } else { + /* Symbol rate calculation */ + u32_tmp *= 256 ; /* 256 = 2**8 */ + u32_tmp = u32_tmp / adp_khz; + u32_tmp = u32_tmp * 16384; /* 16384 = 2**13 */ + u32_tmp /= 125 ; /* 125 = 1000/2**3 */ + u32_tmp = u32_tmp * 64; /* 64 = 2**6 */ + + /* SRC Gain Calculation */ + u32_tmp1 *= 256; /* 2*2^7 */ + u32_tmp1 /= 439; /* *2/878 */ + u32_tmp1 *= 256; /* *2^8 */ + u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ + u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ + u32_tmp1 = u32_tmp1 / 1250000; + } + } +#if 0 + /* Filters' coefficients are calculated and written + into registers only if the filters are enabled */ + if (stv0367_readbits(state, F367CAB_ADJ_EN)) { + stv0367cab_SetIirAdjacentcoefficient(state, mclk_hz, + SymbolRate); + /* AllPass filter must be enabled + when the adjacents filter is used */ + stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 1); + stv0367cab_SetAllPasscoefficient(state, mclk_hz, SymbolRate); + } else + /* AllPass filter must be disabled + when the adjacents filter is not used */ +#endif + stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0); + + stv0367_writereg(state, R367CAB_SRC_NCO_LL, u32_tmp); + stv0367_writereg(state, R367CAB_SRC_NCO_LH, (u32_tmp >> 8)); + stv0367_writereg(state, R367CAB_SRC_NCO_HL, (u32_tmp >> 16)); + stv0367_writereg(state, R367CAB_SRC_NCO_HH, (u32_tmp >> 24)); + + stv0367_writereg(state, R367CAB_IQDEM_GAIN_SRC_L, u32_tmp1 & 0x00ff); + stv0367_writebits(state, F367CAB_GAIN_SRC_HI, (u32_tmp1 >> 8) & 0x00ff); + + return SymbolRate ; +} + +static u32 stv0367cab_GetSymbolRate(struct stv0367_state *state, u32 mclk_hz) +{ + u32 regsym; + u32 adp_khz; + + regsym = stv0367_readreg(state, R367CAB_SRC_NCO_LL) + + (stv0367_readreg(state, R367CAB_SRC_NCO_LH) << 8) + + (stv0367_readreg(state, R367CAB_SRC_NCO_HL) << 16) + + (stv0367_readreg(state, R367CAB_SRC_NCO_HH) << 24); + + adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */ + + if (regsym < 134217728) { /* 134217728L = 2**27*/ + regsym = regsym * 32; /* 32 = 2**5 */ + regsym = regsym / 32768; /* 32768L = 2**15 */ + regsym = adp_khz * regsym; /* AdpClk in kHz */ + regsym = regsym / 128; /* 128 = 2**7 */ + regsym *= 125 ; /* 125 = 1000/2**3 */ + regsym /= 2048 ; /* 2048 = 2**11 */ + } else if (regsym < 268435456) { /* 268435456L = 2**28 */ + regsym = regsym * 16; /* 16 = 2**4 */ + regsym = regsym / 32768; /* 32768L = 2**15 */ + regsym = adp_khz * regsym; /* AdpClk in kHz */ + regsym = regsym / 128; /* 128 = 2**7 */ + regsym *= 125 ; /* 125 = 1000/2**3*/ + regsym /= 1024 ; /* 256 = 2**10*/ + } else if (regsym < 536870912) { /* 536870912L = 2**29*/ + regsym = regsym * 8; /* 8 = 2**3 */ + regsym = regsym / 32768; /* 32768L = 2**15 */ + regsym = adp_khz * regsym; /* AdpClk in kHz */ + regsym = regsym / 128; /* 128 = 2**7 */ + regsym *= 125 ; /* 125 = 1000/2**3 */ + regsym /= 512 ; /* 128 = 2**9 */ + } else { + regsym = regsym * 4; /* 4 = 2**2 */ + regsym = regsym / 32768; /* 32768L = 2**15 */ + regsym = adp_khz * regsym; /* AdpClk in kHz */ + regsym = regsym / 128; /* 128 = 2**7 */ + regsym *= 125 ; /* 125 = 1000/2**3 */ + regsym /= 256 ; /* 64 = 2**8 */ + } + + return regsym; +} + +static int stv0367cab_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + *status = 0; + + if (stv0367_readbits(state, F367CAB_QAMFEC_LOCK)) { + *status |= FE_HAS_LOCK; + dprintk("%s: stv0367 has locked\n", __func__); + } + + return 0; +} + +static int stv0367cab_standby(struct dvb_frontend *fe, u8 standby_on) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + if (standby_on) { + stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x03); + stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x01); + stv0367_writebits(state, F367CAB_STDBY, 1); + stv0367_writebits(state, F367CAB_STDBY_CORE, 1); + stv0367_writebits(state, F367CAB_EN_BUFFER_I, 0); + stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 0); + stv0367_writebits(state, F367CAB_POFFQ, 1); + stv0367_writebits(state, F367CAB_POFFI, 1); + } else { + stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x00); + stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x00); + stv0367_writebits(state, F367CAB_STDBY, 0); + stv0367_writebits(state, F367CAB_STDBY_CORE, 0); + stv0367_writebits(state, F367CAB_EN_BUFFER_I, 1); + stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 1); + stv0367_writebits(state, F367CAB_POFFQ, 0); + stv0367_writebits(state, F367CAB_POFFI, 0); + } + + return 0; +} + +static int stv0367cab_sleep(struct dvb_frontend *fe) +{ + return stv0367cab_standby(fe, 1); +} + +int stv0367cab_init(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367cab_state *cab_state = state->cab_state; + int i; + + dprintk("%s:\n", __func__); + + for (i = 0; i < STV0367CAB_NBREGS; i++) + stv0367_writereg(state, def0367cab[i].addr, + def0367cab[i].value); + + switch (state->config->ts_mode) { + case STV0367_DVBCI_CLOCK: + dprintk("Setting TSMode = STV0367_DVBCI_CLOCK\n"); + stv0367_writebits(state, F367CAB_OUTFORMAT, 0x03); + break; + case STV0367_SERIAL_PUNCT_CLOCK: + case STV0367_SERIAL_CONT_CLOCK: + stv0367_writebits(state, F367CAB_OUTFORMAT, 0x01); + break; + case STV0367_PARALLEL_PUNCT_CLOCK: + case STV0367_OUTPUTMODE_DEFAULT: + stv0367_writebits(state, F367CAB_OUTFORMAT, 0x00); + break; + } + + switch (state->config->clk_pol) { + case STV0367_RISINGEDGE_CLOCK: + stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x00); + break; + case STV0367_FALLINGEDGE_CLOCK: + case STV0367_CLOCKPOLARITY_DEFAULT: + stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x01); + break; + } + + stv0367_writebits(state, F367CAB_SYNC_STRIP, 0x00); + + stv0367_writebits(state, F367CAB_CT_NBST, 0x01); + + stv0367_writebits(state, F367CAB_TS_SWAP, 0x01); + + stv0367_writebits(state, F367CAB_FIFO_BYPASS, 0x00); + + stv0367_writereg(state, R367CAB_ANACTRL, 0x00);/*PLL enabled and used */ + + cab_state->mclk = stv0367cab_get_mclk(fe, state->config->xtal); + cab_state->adc_clk = stv0367cab_get_adc_freq(fe, state->config->xtal); + + return 0; +} +static +enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state, + struct dvb_frontend_parameters *param) +{ + struct dvb_qam_parameters *op = ¶m->u.qam; + struct stv0367cab_state *cab_state = state->cab_state; + enum stv0367_cab_signal_type signalType = FE_CAB_NOAGC; + u32 QAMFEC_Lock, QAM_Lock, u32_tmp, + LockTime, TRLTimeOut, AGCTimeOut, CRLSymbols, + CRLTimeOut, EQLTimeOut, DemodTimeOut, FECTimeOut; + u8 TrackAGCAccum; + s32 tmp; + + dprintk("%s:\n", __func__); + + /* Timeouts calculation */ + /* A max lock time of 25 ms is allowed for delayed AGC */ + AGCTimeOut = 25; + /* 100000 symbols needed by the TRL as a maximum value */ + TRLTimeOut = 100000000 / op->symbol_rate; + /* CRLSymbols is the needed number of symbols to achieve a lock + within [-4%, +4%] of the symbol rate. + CRL timeout is calculated + for a lock within [-search_range, +search_range]. + EQL timeout can be changed depending on + the micro-reflections we want to handle. + A characterization must be performed + with these echoes to get new timeout values. + */ + switch (op->modulation) { + case QAM_16: + CRLSymbols = 150000; + EQLTimeOut = 100; + break; + case QAM_32: + CRLSymbols = 250000; + EQLTimeOut = 100; + break; + case QAM_64: + CRLSymbols = 200000; + EQLTimeOut = 100; + break; + case QAM_128: + CRLSymbols = 250000; + EQLTimeOut = 100; + break; + case QAM_256: + CRLSymbols = 250000; + EQLTimeOut = 100; + break; + default: + CRLSymbols = 200000; + EQLTimeOut = 100; + break; + } +#if 0 + if (pIntParams->search_range < 0) { + CRLTimeOut = (25 * CRLSymbols * + (-pIntParams->search_range / 1000)) / + (pIntParams->symbol_rate / 1000); + } else +#endif + CRLTimeOut = (25 * CRLSymbols * (cab_state->search_range / 1000)) / + (op->symbol_rate / 1000); + + CRLTimeOut = (1000 * CRLTimeOut) / op->symbol_rate; + /* Timeouts below 50ms are coerced */ + if (CRLTimeOut < 50) + CRLTimeOut = 50; + /* A maximum of 100 TS packets is needed to get FEC lock even in case + the spectrum inversion needs to be changed. + This is equal to 20 ms in case of the lowest symbol rate of 0.87Msps + */ + FECTimeOut = 20; + DemodTimeOut = AGCTimeOut + TRLTimeOut + CRLTimeOut + EQLTimeOut; + + dprintk("%s: DemodTimeOut=%d\n", __func__, DemodTimeOut); + + /* Reset the TRL to ensure nothing starts until the + AGC is stable which ensures a better lock time + */ + stv0367_writereg(state, R367CAB_CTRL_1, 0x04); + /* Set AGC accumulation time to minimum and lock threshold to maximum + in order to speed up the AGC lock */ + TrackAGCAccum = stv0367_readbits(state, F367CAB_AGC_ACCUMRSTSEL); + stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, 0x0); + /* Modulus Mapper is disabled */ + stv0367_writebits(state, F367CAB_MODULUSMAP_EN, 0); + /* Disable the sweep function */ + stv0367_writebits(state, F367CAB_SWEEP_EN, 0); + /* The sweep function is never used, Sweep rate must be set to 0 */ + /* Set the derotator frequency in Hz */ + stv0367cab_set_derot_freq(state, cab_state->adc_clk, + (1000 * (s32)state->config->if_khz + cab_state->derot_offset)); + /* Disable the Allpass Filter when the symbol rate is out of range */ + if ((op->symbol_rate > 10800000) | (op->symbol_rate < 1800000)) { + stv0367_writebits(state, F367CAB_ADJ_EN, 0); + stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0); + } +#if 0 + /* Check if the tuner is locked */ + tuner_lock = stv0367cab_tuner_get_status(fe); + if (tuner_lock == 0) + return FE_367CAB_NOTUNER; +#endif + /* Relase the TRL to start demodulator acquisition */ + /* Wait for QAM lock */ + LockTime = 0; + stv0367_writereg(state, R367CAB_CTRL_1, 0x00); + do { + QAM_Lock = stv0367_readbits(state, F367CAB_FSM_STATUS); + if ((LockTime >= (DemodTimeOut - EQLTimeOut)) && + (QAM_Lock == 0x04)) + /* + * We don't wait longer, the frequency/phase offset + * must be too big + */ + LockTime = DemodTimeOut; + else if ((LockTime >= (AGCTimeOut + TRLTimeOut)) && + (QAM_Lock == 0x02)) + /* + * We don't wait longer, either there is no signal or + * it is not the right symbol rate or it is an analog + * carrier + */ + { + LockTime = DemodTimeOut; + u32_tmp = stv0367_readbits(state, + F367CAB_AGC_PWR_WORD_LO) + + (stv0367_readbits(state, + F367CAB_AGC_PWR_WORD_ME) << 8) + + (stv0367_readbits(state, + F367CAB_AGC_PWR_WORD_HI) << 16); + if (u32_tmp >= 131072) + u32_tmp = 262144 - u32_tmp; + u32_tmp = u32_tmp / (1 << (11 - stv0367_readbits(state, + F367CAB_AGC_IF_BWSEL))); + + if (u32_tmp < stv0367_readbits(state, + F367CAB_AGC_PWRREF_LO) + + 256 * stv0367_readbits(state, + F367CAB_AGC_PWRREF_HI) - 10) + QAM_Lock = 0x0f; + } else { + usleep_range(10000, 20000); + LockTime += 10; + } + dprintk("QAM_Lock=0x%x LockTime=%d\n", QAM_Lock, LockTime); + tmp = stv0367_readreg(state, R367CAB_IT_STATUS1); + + dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp); + + } while (((QAM_Lock != 0x0c) && (QAM_Lock != 0x0b)) && + (LockTime < DemodTimeOut)); + + dprintk("QAM_Lock=0x%x\n", QAM_Lock); + + tmp = stv0367_readreg(state, R367CAB_IT_STATUS1); + dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp); + tmp = stv0367_readreg(state, R367CAB_IT_STATUS2); + dprintk("R367CAB_IT_STATUS2=0x%x\n", tmp); + + tmp = stv0367cab_get_derot_freq(state, cab_state->adc_clk); + dprintk("stv0367cab_get_derot_freq=0x%x\n", tmp); + + if ((QAM_Lock == 0x0c) || (QAM_Lock == 0x0b)) { + /* Wait for FEC lock */ + LockTime = 0; + do { + usleep_range(5000, 7000); + LockTime += 5; + QAMFEC_Lock = stv0367_readbits(state, + F367CAB_QAMFEC_LOCK); + } while (!QAMFEC_Lock && (LockTime < FECTimeOut)); + } else + QAMFEC_Lock = 0; + + if (QAMFEC_Lock) { + signalType = FE_CAB_DATAOK; + cab_state->modulation = op->modulation; + cab_state->spect_inv = stv0367_readbits(state, + F367CAB_QUAD_INV); +#if 0 +/* not clear for me */ + if (state->config->if_khz != 0) { + if (state->config->if_khz > cab_state->adc_clk / 1000) { + cab_state->freq_khz = + FE_Cab_TunerGetFrequency(pIntParams->hTuner) + - stv0367cab_get_derot_freq(state, cab_state->adc_clk) + - cab_state->adc_clk / 1000 + state->config->if_khz; + } else { + cab_state->freq_khz = + FE_Cab_TunerGetFrequency(pIntParams->hTuner) + - stv0367cab_get_derot_freq(state, cab_state->adc_clk) + + state->config->if_khz; + } + } else { + cab_state->freq_khz = + FE_Cab_TunerGetFrequency(pIntParams->hTuner) + + stv0367cab_get_derot_freq(state, + cab_state->adc_clk) - + cab_state->adc_clk / 4000; + } +#endif + cab_state->symbol_rate = stv0367cab_GetSymbolRate(state, + cab_state->mclk); + cab_state->locked = 1; + + /* stv0367_setbits(state, F367CAB_AGC_ACCUMRSTSEL,7);*/ + } else { + switch (QAM_Lock) { + case 1: + signalType = FE_CAB_NOAGC; + break; + case 2: + signalType = FE_CAB_NOTIMING; + break; + case 3: + signalType = FE_CAB_TIMINGOK; + break; + case 4: + signalType = FE_CAB_NOCARRIER; + break; + case 5: + signalType = FE_CAB_CARRIEROK; + break; + case 7: + signalType = FE_CAB_NOBLIND; + break; + case 8: + signalType = FE_CAB_BLINDOK; + break; + case 10: + signalType = FE_CAB_NODEMOD; + break; + case 11: + signalType = FE_CAB_DEMODOK; + break; + case 12: + signalType = FE_CAB_DEMODOK; + break; + case 13: + signalType = FE_CAB_NODEMOD; + break; + case 14: + signalType = FE_CAB_NOBLIND; + break; + case 15: + signalType = FE_CAB_NOSIGNAL; + break; + default: + break; + } + + } + + /* Set the AGC control values to tracking values */ + stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, TrackAGCAccum); + return signalType; +} + +static int stv0367cab_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *param) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367cab_state *cab_state = state->cab_state; + struct dvb_qam_parameters *op = ¶m->u.qam; + enum stv0367cab_mod QAMSize = 0; + + dprintk("%s: freq = %d, srate = %d\n", __func__, + param->frequency, op->symbol_rate); + + cab_state->derot_offset = 0; + + switch (op->modulation) { + case QAM_16: + QAMSize = FE_CAB_MOD_QAM16; + break; + case QAM_32: + QAMSize = FE_CAB_MOD_QAM32; + break; + case QAM_64: + QAMSize = FE_CAB_MOD_QAM64; + break; + case QAM_128: + QAMSize = FE_CAB_MOD_QAM128; + break; + case QAM_256: + QAMSize = FE_CAB_MOD_QAM256; + break; + default: + break; + } + + stv0367cab_init(fe); + + /* Tuner Frequency Setting */ + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe, param); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + stv0367cab_SetQamSize( + state, + op->symbol_rate, + QAMSize); + + stv0367cab_set_srate(state, + cab_state->adc_clk, + cab_state->mclk, + op->symbol_rate, + QAMSize); + /* Search algorithm launch, [-1.1*RangeOffset, +1.1*RangeOffset] scan */ + cab_state->state = stv0367cab_algo(state, param); + return 0; +} + +static int stv0367cab_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *param) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367cab_state *cab_state = state->cab_state; + struct dvb_qam_parameters *op = ¶m->u.qam; + + enum stv0367cab_mod QAMSize; + + dprintk("%s:\n", __func__); + + op->symbol_rate = stv0367cab_GetSymbolRate(state, cab_state->mclk); + + QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE); + switch (QAMSize) { + case FE_CAB_MOD_QAM16: + op->modulation = QAM_16; + break; + case FE_CAB_MOD_QAM32: + op->modulation = QAM_32; + break; + case FE_CAB_MOD_QAM64: + op->modulation = QAM_64; + break; + case FE_CAB_MOD_QAM128: + op->modulation = QAM_128; + break; + case QAM_256: + op->modulation = QAM_256; + break; + default: + break; + } + + param->frequency = stv0367_get_tuner_freq(fe); + + dprintk("%s: tuner frequency = %d\n", __func__, param->frequency); + + if (state->config->if_khz == 0) { + param->frequency += + (stv0367cab_get_derot_freq(state, cab_state->adc_clk) - + cab_state->adc_clk / 4000); + return 0; + } + + if (state->config->if_khz > cab_state->adc_clk / 1000) + param->frequency += (state->config->if_khz + - stv0367cab_get_derot_freq(state, cab_state->adc_clk) + - cab_state->adc_clk / 1000); + else + param->frequency += (state->config->if_khz + - stv0367cab_get_derot_freq(state, cab_state->adc_clk)); + + return 0; +} + +#if 0 +void stv0367cab_GetErrorCount(state, enum stv0367cab_mod QAMSize, + u32 symbol_rate, FE_367qam_Monitor *Monitor_results) +{ + stv0367cab_OptimiseNByteAndGetBER(state, QAMSize, symbol_rate, Monitor_results); + stv0367cab_GetPacketsCount(state, Monitor_results); + + return; +} + +static int stv0367cab_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct stv0367_state *state = fe->demodulator_priv; + + return 0; +} +#endif +static s32 stv0367cab_get_rf_lvl(struct stv0367_state *state) +{ + s32 rfLevel = 0; + s32 RfAgcPwm = 0, IfAgcPwm = 0; + u8 i; + + stv0367_writebits(state, F367CAB_STDBY_ADCGP, 0x0); + + RfAgcPwm = + (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_LO) & 0x03) + + (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_HI) << 2); + RfAgcPwm = 100 * RfAgcPwm / 1023; + + IfAgcPwm = + stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_LO) + + (stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_HI) << 8); + if (IfAgcPwm >= 2048) + IfAgcPwm -= 2048; + else + IfAgcPwm += 2048; + + IfAgcPwm = 100 * IfAgcPwm / 4095; + + /* For DTT75467 on NIM */ + if (RfAgcPwm < 90 && IfAgcPwm < 28) { + for (i = 0; i < RF_LOOKUP_TABLE_SIZE; i++) { + if (RfAgcPwm <= stv0367cab_RF_LookUp1[0][i]) { + rfLevel = (-1) * stv0367cab_RF_LookUp1[1][i]; + break; + } + } + if (i == RF_LOOKUP_TABLE_SIZE) + rfLevel = -56; + } else { /*if IF AGC>10*/ + for (i = 0; i < RF_LOOKUP_TABLE2_SIZE; i++) { + if (IfAgcPwm <= stv0367cab_RF_LookUp2[0][i]) { + rfLevel = (-1) * stv0367cab_RF_LookUp2[1][i]; + break; + } + } + if (i == RF_LOOKUP_TABLE2_SIZE) + rfLevel = -72; + } + return rfLevel; +} + +static int stv0367cab_read_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct stv0367_state *state = fe->demodulator_priv; + + s32 signal = stv0367cab_get_rf_lvl(state); + + dprintk("%s: signal=%d dBm\n", __func__, signal); + + if (signal <= -72) + *strength = 65535; + else + *strength = (22 + signal) * (-1311); + + dprintk("%s: strength=%d\n", __func__, (*strength)); + + return 0; +} + +static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct stv0367_state *state = fe->demodulator_priv; + u32 noisepercentage; + enum stv0367cab_mod QAMSize; + u32 regval = 0, temp = 0; + int power, i; + + QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE); + switch (QAMSize) { + case FE_CAB_MOD_QAM4: + power = 21904; + break; + case FE_CAB_MOD_QAM16: + power = 20480; + break; + case FE_CAB_MOD_QAM32: + power = 23040; + break; + case FE_CAB_MOD_QAM64: + power = 21504; + break; + case FE_CAB_MOD_QAM128: + power = 23616; + break; + case FE_CAB_MOD_QAM256: + power = 21760; + break; + case FE_CAB_MOD_QAM512: + power = 1; + break; + case FE_CAB_MOD_QAM1024: + power = 21280; + break; + default: + power = 1; + break; + } + + for (i = 0; i < 10; i++) { + regval += (stv0367_readbits(state, F367CAB_SNR_LO) + + 256 * stv0367_readbits(state, F367CAB_SNR_HI)); + } + + regval /= 10; /*for average over 10 times in for loop above*/ + if (regval != 0) { + temp = power + * (1 << (3 + stv0367_readbits(state, F367CAB_SNR_PER))); + temp /= regval; + } + + /* table values, not needed to calculate logarithms */ + if (temp >= 5012) + noisepercentage = 100; + else if (temp >= 3981) + noisepercentage = 93; + else if (temp >= 3162) + noisepercentage = 86; + else if (temp >= 2512) + noisepercentage = 79; + else if (temp >= 1995) + noisepercentage = 72; + else if (temp >= 1585) + noisepercentage = 65; + else if (temp >= 1259) + noisepercentage = 58; + else if (temp >= 1000) + noisepercentage = 50; + else if (temp >= 794) + noisepercentage = 43; + else if (temp >= 501) + noisepercentage = 36; + else if (temp >= 316) + noisepercentage = 29; + else if (temp >= 200) + noisepercentage = 22; + else if (temp >= 158) + noisepercentage = 14; + else if (temp >= 126) + noisepercentage = 7; + else + noisepercentage = 0; + + dprintk("%s: noisepercentage=%d\n", __func__, noisepercentage); + + *snr = (noisepercentage * 65535) / 100; + + return 0; +} + +static struct dvb_frontend_ops stv0367cab_ops = { + .info = { + .name = "ST STV0367 DVB-C", + .type = FE_QAM, + .frequency_min = 47000000, + .frequency_max = 862000000, + .frequency_stepsize = 62500, + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000, + .caps = 0x400 |/* FE_CAN_QAM_4 */ + FE_CAN_QAM_16 | FE_CAN_QAM_32 | + FE_CAN_QAM_64 | FE_CAN_QAM_128 | + FE_CAN_QAM_256 | FE_CAN_FEC_AUTO + }, + .release = stv0367_release, + .init = stv0367cab_init, + .sleep = stv0367cab_sleep, + .i2c_gate_ctrl = stv0367cab_gate_ctrl, + .set_frontend = stv0367cab_set_frontend, + .get_frontend = stv0367cab_get_frontend, + .read_status = stv0367cab_read_status, +/* .read_ber = stv0367cab_read_ber, */ + .read_signal_strength = stv0367cab_read_strength, + .read_snr = stv0367cab_read_snr, +/* .read_ucblocks = stv0367cab_read_ucblcks,*/ + .get_tune_settings = stv0367_get_tune_settings, +}; + +struct dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + struct stv0367_state *state = NULL; + struct stv0367cab_state *cab_state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL); + if (state == NULL) + goto error; + cab_state = kzalloc(sizeof(struct stv0367cab_state), GFP_KERNEL); + if (cab_state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + state->config = config; + cab_state->search_range = 280000; + state->cab_state = cab_state; + state->fe.ops = stv0367cab_ops; + state->fe.demodulator_priv = state; + state->chip_id = stv0367_readreg(state, 0xf000); + + dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id); + + /* check if the demod is there */ + if ((state->chip_id != 0x50) && (state->chip_id != 0x60)) + goto error; + + return &state->fe; + +error: + kfree(cab_state); + kfree(state); + return NULL; +} +EXPORT_SYMBOL(stv0367cab_attach); + +MODULE_PARM_DESC(debug, "Set debug"); +MODULE_PARM_DESC(i2c_debug, "Set i2c debug"); + +MODULE_AUTHOR("Igor M. Liplianin"); +MODULE_DESCRIPTION("ST STV0367 DVB-C/T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/stv0367.h b/drivers/media/dvb/frontends/stv0367.h new file mode 100644 index 0000000..822a8e9 --- /dev/null +++ b/drivers/media/dvb/frontends/stv0367.h @@ -0,0 +1,66 @@ +/* + * stv0367.h + * + * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2010 NetUP Inc. + * Copyright (C) 2010 Igor M. Liplianin + * + * 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 STV0367_H +#define STV0367_H + +#include +#include "dvb_frontend.h" + +struct stv0367_config { + u8 demod_address; + u32 xtal; + u32 if_khz;/*4500*/ + int if_iq_mode; + int ts_mode; + int clk_pol; +}; + +#if defined(CONFIG_DVB_STV0367) || (defined(CONFIG_DVB_STV0367_MODULE) \ + && defined(MODULE)) +extern struct +dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c); +extern struct +dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c); +#else +static inline struct +dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline struct +dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb/frontends/stv0367_priv.h b/drivers/media/dvb/frontends/stv0367_priv.h new file mode 100644 index 0000000..995db06 --- /dev/null +++ b/drivers/media/dvb/frontends/stv0367_priv.h @@ -0,0 +1,212 @@ +/* + * stv0367_priv.h + * + * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin + * + * 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. + */ +/* Common driver error constants */ + +#ifndef STV0367_PRIV_H +#define STV0367_PRIV_H + +#ifndef TRUE + #define TRUE (1 == 1) +#endif +#ifndef FALSE + #define FALSE (!TRUE) +#endif + +#ifndef NULL +#define NULL 0 +#endif + +/* MACRO definitions */ +#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X)) +#define MAX(X, Y) ((X) >= (Y) ? (X) : (Y)) +#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) +#define INRANGE(X, Y, Z) \ + ((((X) <= (Y)) && ((Y) <= (Z))) || \ + (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0) + +#ifndef MAKEWORD +#define MAKEWORD(X, Y) (((X) << 8) + (Y)) +#endif + +#define LSB(X) (((X) & 0xff)) +#define MSB(Y) (((Y) >> 8) & 0xff) +#define MMSB(Y)(((Y) >> 16) & 0xff) + +enum stv0367_ter_signal_type { + FE_TER_NOAGC = 0, + FE_TER_AGCOK = 5, + FE_TER_NOTPS = 6, + FE_TER_TPSOK = 7, + FE_TER_NOSYMBOL = 8, + FE_TER_BAD_CPQ = 9, + FE_TER_PRFOUNDOK = 10, + FE_TER_NOPRFOUND = 11, + FE_TER_LOCKOK = 12, + FE_TER_NOLOCK = 13, + FE_TER_SYMBOLOK = 15, + FE_TER_CPAMPOK = 16, + FE_TER_NOCPAMP = 17, + FE_TER_SWNOK = 18 +}; + +enum stv0367_ts_mode { + STV0367_OUTPUTMODE_DEFAULT, + STV0367_SERIAL_PUNCT_CLOCK, + STV0367_SERIAL_CONT_CLOCK, + STV0367_PARALLEL_PUNCT_CLOCK, + STV0367_DVBCI_CLOCK +}; + +enum stv0367_clk_pol { + STV0367_CLOCKPOLARITY_DEFAULT, + STV0367_RISINGEDGE_CLOCK, + STV0367_FALLINGEDGE_CLOCK +}; + +enum stv0367_ter_bw { + FE_TER_CHAN_BW_6M = 6, + FE_TER_CHAN_BW_7M = 7, + FE_TER_CHAN_BW_8M = 8 +}; + +#if 0 +enum FE_TER_Rate_TPS { + FE_TER_TPS_1_2 = 0, + FE_TER_TPS_2_3 = 1, + FE_TER_TPS_3_4 = 2, + FE_TER_TPS_5_6 = 3, + FE_TER_TPS_7_8 = 4 +}; +#endif + +enum stv0367_ter_mode { + FE_TER_MODE_2K, + FE_TER_MODE_8K, + FE_TER_MODE_4K +}; +#if 0 +enum FE_TER_Hierarchy_Alpha { + FE_TER_HIER_ALPHA_NONE, /* Regular modulation */ + FE_TER_HIER_ALPHA_1, /* Hierarchical modulation a = 1*/ + FE_TER_HIER_ALPHA_2, /* Hierarchical modulation a = 2*/ + FE_TER_HIER_ALPHA_4 /* Hierarchical modulation a = 4*/ +}; +#endif +enum stv0367_ter_hierarchy { + FE_TER_HIER_NONE, /*Hierarchy None*/ + FE_TER_HIER_LOW_PRIO, /*Hierarchy : Low Priority*/ + FE_TER_HIER_HIGH_PRIO, /*Hierarchy : High Priority*/ + FE_TER_HIER_PRIO_ANY /*Hierarchy :Any*/ +}; + +#if 0 +enum fe_stv0367_ter_spec { + FE_TER_INVERSION_NONE = 0, + FE_TER_INVERSION = 1, + FE_TER_INVERSION_AUTO = 2, + FE_TER_INVERSION_UNK = 4 +}; +#endif + +enum stv0367_ter_if_iq_mode { + FE_TER_NORMAL_IF_TUNER = 0, + FE_TER_LONGPATH_IF_TUNER = 1, + FE_TER_IQ_TUNER = 2 + +}; + +#if 0 +enum FE_TER_FECRate { + FE_TER_FEC_NONE = 0x00, /* no FEC rate specified */ + FE_TER_FEC_ALL = 0xFF, /* Logical OR of all FECs */ + FE_TER_FEC_1_2 = 1, + FE_TER_FEC_2_3 = (1 << 1), + FE_TER_FEC_3_4 = (1 << 2), + FE_TER_FEC_4_5 = (1 << 3), + FE_TER_FEC_5_6 = (1 << 4), + FE_TER_FEC_6_7 = (1 << 5), + FE_TER_FEC_7_8 = (1 << 6), + FE_TER_FEC_8_9 = (1 << 7) +}; + +enum FE_TER_Rate { + FE_TER_FE_1_2 = 0, + FE_TER_FE_2_3 = 1, + FE_TER_FE_3_4 = 2, + FE_TER_FE_5_6 = 3, + FE_TER_FE_6_7 = 4, + FE_TER_FE_7_8 = 5 +}; +#endif + +enum stv0367_ter_force { + FE_TER_FORCENONE = 0, + FE_TER_FORCE_M_G = 1 +}; + +enum stv0367cab_mod { + FE_CAB_MOD_QAM4, + FE_CAB_MOD_QAM16, + FE_CAB_MOD_QAM32, + FE_CAB_MOD_QAM64, + FE_CAB_MOD_QAM128, + FE_CAB_MOD_QAM256, + FE_CAB_MOD_QAM512, + FE_CAB_MOD_QAM1024 +}; +#if 0 +enum { + FE_CAB_FEC_A = 1, /* J83 Annex A */ + FE_CAB_FEC_B = (1 << 1),/* J83 Annex B */ + FE_CAB_FEC_C = (1 << 2) /* J83 Annex C */ +} FE_CAB_FECType_t; +#endif +struct stv0367_cab_signal_info { + int locked; + u32 frequency; /* kHz */ + u32 symbol_rate; /* Mbds */ + enum stv0367cab_mod modulation; + fe_spectral_inversion_t spect_inv; + s32 Power_dBmx10; /* Power of the RF signal (dBm x 10) */ + u32 CN_dBx10; /* Carrier to noise ratio (dB x 10) */ + u32 BER; /* Bit error rate (x 10000000) */ +}; + +enum stv0367_cab_signal_type { + FE_CAB_NOTUNER, + FE_CAB_NOAGC, + FE_CAB_NOSIGNAL, + FE_CAB_NOTIMING, + FE_CAB_TIMINGOK, + FE_CAB_NOCARRIER, + FE_CAB_CARRIEROK, + FE_CAB_NOBLIND, + FE_CAB_BLINDOK, + FE_CAB_NODEMOD, + FE_CAB_DEMODOK, + FE_CAB_DATAOK +}; + +#endif diff --git a/drivers/media/dvb/frontends/stv0367_regs.h b/drivers/media/dvb/frontends/stv0367_regs.h new file mode 100644 index 0000000..a96fbdc --- /dev/null +++ b/drivers/media/dvb/frontends/stv0367_regs.h @@ -0,0 +1,3614 @@ +/* + * stv0367_regs.h + * + * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin + * + * 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 STV0367_REGS_H +#define STV0367_REGS_H + +/* ID */ +#define R367TER_ID 0xf000 +#define F367TER_IDENTIFICATIONREG 0xf00000ff + +/* I2CRPT */ +#define R367TER_I2CRPT 0xf001 +#define F367TER_I2CT_ON 0xf0010080 +#define F367TER_ENARPT_LEVEL 0xf0010070 +#define F367TER_SCLT_DELAY 0xf0010008 +#define F367TER_SCLT_NOD 0xf0010004 +#define F367TER_STOP_ENABLE 0xf0010002 +#define F367TER_SDAT_NOD 0xf0010001 + +/* TOPCTRL */ +#define R367TER_TOPCTRL 0xf002 +#define F367TER_STDBY 0xf0020080 +#define F367TER_STDBY_FEC 0xf0020040 +#define F367TER_STDBY_CORE 0xf0020020 +#define F367TER_QAM_COFDM 0xf0020010 +#define F367TER_TS_DIS 0xf0020008 +#define F367TER_DIR_CLK_216 0xf0020004 +#define F367TER_TUNER_BB 0xf0020002 +#define F367TER_DVBT_H 0xf0020001 + +/* IOCFG0 */ +#define R367TER_IOCFG0 0xf003 +#define F367TER_OP0_SD 0xf0030080 +#define F367TER_OP0_VAL 0xf0030040 +#define F367TER_OP0_OD 0xf0030020 +#define F367TER_OP0_INV 0xf0030010 +#define F367TER_OP0_DACVALUE_HI 0xf003000f + +/* DAc0R */ +#define R367TER_DAC0R 0xf004 +#define F367TER_OP0_DACVALUE_LO 0xf00400ff + +/* IOCFG1 */ +#define R367TER_IOCFG1 0xf005 +#define F367TER_IP0 0xf0050040 +#define F367TER_OP1_OD 0xf0050020 +#define F367TER_OP1_INV 0xf0050010 +#define F367TER_OP1_DACVALUE_HI 0xf005000f + +/* DAC1R */ +#define R367TER_DAC1R 0xf006 +#define F367TER_OP1_DACVALUE_LO 0xf00600ff + +/* IOCFG2 */ +#define R367TER_IOCFG2 0xf007 +#define F367TER_OP2_LOCK_CONF 0xf00700e0 +#define F367TER_OP2_OD 0xf0070010 +#define F367TER_OP2_VAL 0xf0070008 +#define F367TER_OP1_LOCK_CONF 0xf0070007 + +/* SDFR */ +#define R367TER_SDFR 0xf008 +#define F367TER_OP0_FREQ 0xf00800f0 +#define F367TER_OP1_FREQ 0xf008000f + +/* STATUS */ +#define R367TER_STATUS 0xf009 +#define F367TER_TPS_LOCK 0xf0090080 +#define F367TER_SYR_LOCK 0xf0090040 +#define F367TER_AGC_LOCK 0xf0090020 +#define F367TER_PRF 0xf0090010 +#define F367TER_LK 0xf0090008 +#define F367TER_PR 0xf0090007 + +/* AUX_CLK */ +#define R367TER_AUX_CLK 0xf00a +#define F367TER_AUXFEC_CTL 0xf00a00c0 +#define F367TER_DIS_CKX4 0xf00a0020 +#define F367TER_CKSEL 0xf00a0018 +#define F367TER_CKDIV_PROG 0xf00a0006 +#define F367TER_AUXCLK_ENA 0xf00a0001 + +/* FREESYS1 */ +#define R367TER_FREESYS1 0xf00b +#define F367TER_FREE_SYS1 0xf00b00ff + +/* FREESYS2 */ +#define R367TER_FREESYS2 0xf00c +#define F367TER_FREE_SYS2 0xf00c00ff + +/* FREESYS3 */ +#define R367TER_FREESYS3 0xf00d +#define F367TER_FREE_SYS3 0xf00d00ff + +/* GPIO_CFG */ +#define R367TER_GPIO_CFG 0xf00e +#define F367TER_GPIO7_NOD 0xf00e0080 +#define F367TER_GPIO7_CFG 0xf00e0040 +#define F367TER_GPIO6_NOD 0xf00e0020 +#define F367TER_GPIO6_CFG 0xf00e0010 +#define F367TER_GPIO5_NOD 0xf00e0008 +#define F367TER_GPIO5_CFG 0xf00e0004 +#define F367TER_GPIO4_NOD 0xf00e0002 +#define F367TER_GPIO4_CFG 0xf00e0001 + +/* GPIO_CMD */ +#define R367TER_GPIO_CMD 0xf00f +#define F367TER_GPIO7_VAL 0xf00f0008 +#define F367TER_GPIO6_VAL 0xf00f0004 +#define F367TER_GPIO5_VAL 0xf00f0002 +#define F367TER_GPIO4_VAL 0xf00f0001 + +/* AGC2MAX */ +#define R367TER_AGC2MAX 0xf010 +#define F367TER_AGC2_MAX 0xf01000ff + +/* AGC2MIN */ +#define R367TER_AGC2MIN 0xf011 +#define F367TER_AGC2_MIN 0xf01100ff + +/* AGC1MAX */ +#define R367TER_AGC1MAX 0xf012 +#define F367TER_AGC1_MAX 0xf01200ff + +/* AGC1MIN */ +#define R367TER_AGC1MIN 0xf013 +#define F367TER_AGC1_MIN 0xf01300ff + +/* AGCR */ +#define R367TER_AGCR 0xf014 +#define F367TER_RATIO_A 0xf01400e0 +#define F367TER_RATIO_B 0xf0140018 +#define F367TER_RATIO_C 0xf0140007 + +/* AGC2TH */ +#define R367TER_AGC2TH 0xf015 +#define F367TER_AGC2_THRES 0xf01500ff + +/* AGC12c */ +#define R367TER_AGC12C 0xf016 +#define F367TER_AGC1_IV 0xf0160080 +#define F367TER_AGC1_OD 0xf0160040 +#define F367TER_AGC1_LOAD 0xf0160020 +#define F367TER_AGC2_IV 0xf0160010 +#define F367TER_AGC2_OD 0xf0160008 +#define F367TER_AGC2_LOAD 0xf0160004 +#define F367TER_AGC12_MODE 0xf0160003 + +/* AGCCTRL1 */ +#define R367TER_AGCCTRL1 0xf017 +#define F367TER_DAGC_ON 0xf0170080 +#define F367TER_INVERT_AGC12 0xf0170040 +#define F367TER_AGC1_MODE 0xf0170008 +#define F367TER_AGC2_MODE 0xf0170007 + +/* AGCCTRL2 */ +#define R367TER_AGCCTRL2 0xf018 +#define F367TER_FRZ2_CTRL 0xf0180060 +#define F367TER_FRZ1_CTRL 0xf0180018 +#define F367TER_TIME_CST 0xf0180007 + +/* AGC1VAL1 */ +#define R367TER_AGC1VAL1 0xf019 +#define F367TER_AGC1_VAL_LO 0xf01900ff + +/* AGC1VAL2 */ +#define R367TER_AGC1VAL2 0xf01a +#define F367TER_AGC1_VAL_HI 0xf01a000f + +/* AGC2VAL1 */ +#define R367TER_AGC2VAL1 0xf01b +#define F367TER_AGC2_VAL_LO 0xf01b00ff + +/* AGC2VAL2 */ +#define R367TER_AGC2VAL2 0xf01c +#define F367TER_AGC2_VAL_HI 0xf01c000f + +/* AGC2PGA */ +#define R367TER_AGC2PGA 0xf01d +#define F367TER_AGC2_PGA 0xf01d00ff + +/* OVF_RATE1 */ +#define R367TER_OVF_RATE1 0xf01e +#define F367TER_OVF_RATE_HI 0xf01e000f + +/* OVF_RATE2 */ +#define R367TER_OVF_RATE2 0xf01f +#define F367TER_OVF_RATE_LO 0xf01f00ff + +/* GAIN_SRC1 */ +#define R367TER_GAIN_SRC1 0xf020 +#define F367TER_INV_SPECTR 0xf0200080 +#define F367TER_IQ_INVERT 0xf0200040 +#define F367TER_INR_BYPASS 0xf0200020 +#define F367TER_STATUS_INV_SPECRUM 0xf0200010 +#define F367TER_GAIN_SRC_HI 0xf020000f + +/* GAIN_SRC2 */ +#define R367TER_GAIN_SRC2 0xf021 +#define F367TER_GAIN_SRC_LO 0xf02100ff + +/* INC_DEROT1 */ +#define R367TER_INC_DEROT1 0xf022 +#define F367TER_INC_DEROT_HI 0xf02200ff + +/* INC_DEROT2 */ +#define R367TER_INC_DEROT2 0xf023 +#define F367TER_INC_DEROT_LO 0xf02300ff + +/* PPM_CPAMP_DIR */ +#define R367TER_PPM_CPAMP_DIR 0xf024 +#define F367TER_PPM_CPAMP_DIRECT 0xf02400ff + +/* PPM_CPAMP_INV */ +#define R367TER_PPM_CPAMP_INV 0xf025 +#define F367TER_PPM_CPAMP_INVER 0xf02500ff + +/* FREESTFE_1 */ +#define R367TER_FREESTFE_1 0xf026 +#define F367TER_SYMBOL_NUMBER_INC 0xf02600c0 +#define F367TER_SEL_LSB 0xf0260004 +#define F367TER_AVERAGE_ON 0xf0260002 +#define F367TER_DC_ADJ 0xf0260001 + +/* FREESTFE_2 */ +#define R367TER_FREESTFE_2 0xf027 +#define F367TER_SEL_SRCOUT 0xf02700c0 +#define F367TER_SEL_SYRTHR 0xf027001f + +/* DCOFFSET */ +#define R367TER_DCOFFSET 0xf028 +#define F367TER_SELECT_I_Q 0xf0280080 +#define F367TER_DC_OFFSET 0xf028007f + +/* EN_PROCESS */ +#define R367TER_EN_PROCESS 0xf029 +#define F367TER_FREE 0xf02900f0 +#define F367TER_ENAB_MANUAL 0xf0290001 + +/* SDI_SMOOTHER */ +#define R367TER_SDI_SMOOTHER 0xf02a +#define F367TER_DIS_SMOOTH 0xf02a0080 +#define F367TER_SDI_INC_SMOOTHER 0xf02a007f + +/* FE_LOOP_OPEN */ +#define R367TER_FE_LOOP_OPEN 0xf02b +#define F367TER_TRL_LOOP_OP 0xf02b0002 +#define F367TER_CRL_LOOP_OP 0xf02b0001 + +/* FREQOFF1 */ +#define R367TER_FREQOFF1 0xf02c +#define F367TER_FREQ_OFFSET_LOOP_OPEN_VHI 0xf02c00ff + +/* FREQOFF2 */ +#define R367TER_FREQOFF2 0xf02d +#define F367TER_FREQ_OFFSET_LOOP_OPEN_HI 0xf02d00ff + +/* FREQOFF3 */ +#define R367TER_FREQOFF3 0xf02e +#define F367TER_FREQ_OFFSET_LOOP_OPEN_LO 0xf02e00ff + +/* TIMOFF1 */ +#define R367TER_TIMOFF1 0xf02f +#define F367TER_TIM_OFFSET_LOOP_OPEN_HI 0xf02f00ff + +/* TIMOFF2 */ +#define R367TER_TIMOFF2 0xf030 +#define F367TER_TIM_OFFSET_LOOP_OPEN_LO 0xf03000ff + +/* EPQ */ +#define R367TER_EPQ 0xf031 +#define F367TER_EPQ1 0xf03100ff + +/* EPQAUTO */ +#define R367TER_EPQAUTO 0xf032 +#define F367TER_EPQ2 0xf03200ff + +/* SYR_UPDATE */ +#define R367TER_SYR_UPDATE 0xf033 +#define F367TER_SYR_PROTV 0xf0330080 +#define F367TER_SYR_PROTV_GAIN 0xf0330060 +#define F367TER_SYR_FILTER 0xf0330010 +#define F367TER_SYR_TRACK_THRES 0xf033000c + +/* CHPFREE */ +#define R367TER_CHPFREE 0xf034 +#define F367TER_CHP_FREE 0xf03400ff + +/* PPM_STATE_MAC */ +#define R367TER_PPM_STATE_MAC 0xf035 +#define F367TER_PPM_STATE_MACHINE_DECODER 0xf035003f + +/* INR_THRESHOLD */ +#define R367TER_INR_THRESHOLD 0xf036 +#define F367TER_INR_THRESH 0xf03600ff + +/* EPQ_TPS_ID_CELL */ +#define R367TER_EPQ_TPS_ID_CELL 0xf037 +#define F367TER_ENABLE_LGTH_TO_CF 0xf0370080 +#define F367TER_DIS_TPS_RSVD 0xf0370040 +#define F367TER_DIS_BCH 0xf0370020 +#define F367TER_DIS_ID_CEL 0xf0370010 +#define F367TER_TPS_ADJUST_SYM 0xf037000f + +/* EPQ_CFG */ +#define R367TER_EPQ_CFG 0xf038 +#define F367TER_EPQ_RANGE 0xf0380002 +#define F367TER_EPQ_SOFT 0xf0380001 + +/* EPQ_STATUS */ +#define R367TER_EPQ_STATUS 0xf039 +#define F367TER_SLOPE_INC 0xf03900fc +#define F367TER_TPS_FIELD 0xf0390003 + +/* AUTORELOCK */ +#define R367TER_AUTORELOCK 0xf03a +#define F367TER_BYPASS_BER_TEMPO 0xf03a0080 +#define F367TER_BER_TEMPO 0xf03a0070 +#define F367TER_BYPASS_COFDM_TEMPO 0xf03a0008 +#define F367TER_COFDM_TEMPO 0xf03a0007 + +/* BER_THR_VMSB */ +#define R367TER_BER_THR_VMSB 0xf03b +#define F367TER_BER_THRESHOLD_HI 0xf03b00ff + +/* BER_THR_MSB */ +#define R367TER_BER_THR_MSB 0xf03c +#define F367TER_BER_THRESHOLD_MID 0xf03c00ff + +/* BER_THR_LSB */ +#define R367TER_BER_THR_LSB 0xf03d +#define F367TER_BER_THRESHOLD_LO 0xf03d00ff + +/* CCD */ +#define R367TER_CCD 0xf03e +#define F367TER_CCD_DETECTED 0xf03e0080 +#define F367TER_CCD_RESET 0xf03e0040 +#define F367TER_CCD_THRESHOLD 0xf03e000f + +/* SPECTR_CFG */ +#define R367TER_SPECTR_CFG 0xf03f +#define F367TER_SPECT_CFG 0xf03f0003 + +/* CONSTMU_MSB */ +#define R367TER_CONSTMU_MSB 0xf040 +#define F367TER_CONSTMU_FREEZE 0xf0400080 +#define F367TER_CONSTNU_FORCE_EN 0xf0400040 +#define F367TER_CONST_MU_MSB 0xf040003f + +/* CONSTMU_LSB */ +#define R367TER_CONSTMU_LSB 0xf041 +#define F367TER_CONST_MU_LSB 0xf04100ff + +/* CONSTMU_MAX_MSB */ +#define R367TER_CONSTMU_MAX_MSB 0xf042 +#define F367TER_CONST_MU_MAX_MSB 0xf042003f + +/* CONSTMU_MAX_LSB */ +#define R367TER_CONSTMU_MAX_LSB 0xf043 +#define F367TER_CONST_MU_MAX_LSB 0xf04300ff + +/* ALPHANOISE */ +#define R367TER_ALPHANOISE 0xf044 +#define F367TER_USE_ALLFILTER 0xf0440080 +#define F367TER_INTER_ON 0xf0440040 +#define F367TER_ALPHA_NOISE 0xf044001f + +/* MAXGP_MSB */ +#define R367TER_MAXGP_MSB 0xf045 +#define F367TER_MUFILTER_LENGTH 0xf04500f0 +#define F367TER_MAX_GP_MSB 0xf045000f + +/* MAXGP_LSB */ +#define R367TER_MAXGP_LSB 0xf046 +#define F367TER_MAX_GP_LSB 0xf04600ff + +/* ALPHAMSB */ +#define R367TER_ALPHAMSB 0xf047 +#define F367TER_CHC_DATARATE 0xf04700c0 +#define F367TER_ALPHA_MSB 0xf047003f + +/* ALPHALSB */ +#define R367TER_ALPHALSB 0xf048 +#define F367TER_ALPHA_LSB 0xf04800ff + +/* PILOT_ACCU */ +#define R367TER_PILOT_ACCU 0xf049 +#define F367TER_USE_SCAT4ADDAPT 0xf0490080 +#define F367TER_PILOT_ACC 0xf049001f + +/* PILOTMU_ACCU */ +#define R367TER_PILOTMU_ACCU 0xf04a +#define F367TER_DISCARD_BAD_SP 0xf04a0080 +#define F367TER_DISCARD_BAD_CP 0xf04a0040 +#define F367TER_PILOT_MU_ACCU 0xf04a001f + +/* FILT_CHANNEL_EST */ +#define R367TER_FILT_CHANNEL_EST 0xf04b +#define F367TER_USE_FILT_PILOT 0xf04b0080 +#define F367TER_FILT_CHANNEL 0xf04b007f + +/* ALPHA_NOPISE_FREQ */ +#define R367TER_ALPHA_NOPISE_FREQ 0xf04c +#define F367TER_NOISE_FREQ_FILT 0xf04c0040 +#define F367TER_ALPHA_NOISE_FREQ 0xf04c003f + +/* RATIO_PILOT */ +#define R367TER_RATIO_PILOT 0xf04d +#define F367TER_RATIO_MEAN_SP 0xf04d00f0 +#define F367TER_RATIO_MEAN_CP 0xf04d000f + +/* CHC_CTL */ +#define R367TER_CHC_CTL 0xf04e +#define F367TER_TRACK_EN 0xf04e0080 +#define F367TER_NOISE_NORM_EN 0xf04e0040 +#define F367TER_FORCE_CHC_RESET 0xf04e0020 +#define F367TER_SHORT_TIME 0xf04e0010 +#define F367TER_FORCE_STATE_EN 0xf04e0008 +#define F367TER_FORCE_STATE 0xf04e0007 + +/* EPQ_ADJUST */ +#define R367TER_EPQ_ADJUST 0xf04f +#define F367TER_ADJUST_SCAT_IND 0xf04f00c0 +#define F367TER_ONE_SYMBOL 0xf04f0010 +#define F367TER_EPQ_DECAY 0xf04f000e +#define F367TER_HOLD_SLOPE 0xf04f0001 + +/* EPQ_THRES */ +#define R367TER_EPQ_THRES 0xf050 +#define F367TER_EPQ_THR 0xf05000ff + +/* OMEGA_CTL */ +#define R367TER_OMEGA_CTL 0xf051 +#define F367TER_OMEGA_RST 0xf0510080 +#define F367TER_FREEZE_OMEGA 0xf0510040 +#define F367TER_OMEGA_SEL 0xf051003f + +/* GP_CTL */ +#define R367TER_GP_CTL 0xf052 +#define F367TER_CHC_STATE 0xf05200e0 +#define F367TER_FREEZE_GP 0xf0520010 +#define F367TER_GP_SEL 0xf052000f + +/* MUMSB */ +#define R367TER_MUMSB 0xf053 +#define F367TER_MU_MSB 0xf053007f + +/* MULSB */ +#define R367TER_MULSB 0xf054 +#define F367TER_MU_LSB 0xf05400ff + +/* GPMSB */ +#define R367TER_GPMSB 0xf055 +#define F367TER_CSI_THRESHOLD 0xf05500e0 +#define F367TER_GP_MSB 0xf055000f + +/* GPLSB */ +#define R367TER_GPLSB 0xf056 +#define F367TER_GP_LSB 0xf05600ff + +/* OMEGAMSB */ +#define R367TER_OMEGAMSB 0xf057 +#define F367TER_OMEGA_MSB 0xf057007f + +/* OMEGALSB */ +#define R367TER_OMEGALSB 0xf058 +#define F367TER_OMEGA_LSB 0xf05800ff + +/* SCAT_NB */ +#define R367TER_SCAT_NB 0xf059 +#define F367TER_CHC_TEST 0xf05900f8 +#define F367TER_SCAT_NUMB 0xf0590003 + +/* CHC_DUMMY */ +#define R367TER_CHC_DUMMY 0xf05a +#define F367TER_CHC_DUM 0xf05a00ff + +/* INC_CTL */ +#define R367TER_INC_CTL 0xf05b +#define F367TER_INC_BYPASS 0xf05b0080 +#define F367TER_INC_NDEPTH 0xf05b000c +#define F367TER_INC_MADEPTH 0xf05b0003 + +/* INCTHRES_COR1 */ +#define R367TER_INCTHRES_COR1 0xf05c +#define F367TER_INC_THRES_COR1 0xf05c00ff + +/* INCTHRES_COR2 */ +#define R367TER_INCTHRES_COR2 0xf05d +#define F367TER_INC_THRES_COR2 0xf05d00ff + +/* INCTHRES_DET1 */ +#define R367TER_INCTHRES_DET1 0xf05e +#define F367TER_INC_THRES_DET1 0xf05e003f + +/* INCTHRES_DET2 */ +#define R367TER_INCTHRES_DET2 0xf05f +#define F367TER_INC_THRES_DET2 0xf05f003f + +/* IIR_CELLNB */ +#define R367TER_IIR_CELLNB 0xf060 +#define F367TER_NRST_IIR 0xf0600080 +#define F367TER_IIR_CELL_NB 0xf0600007 + +/* IIRCX_COEFF1_MSB */ +#define R367TER_IIRCX_COEFF1_MSB 0xf061 +#define F367TER_IIR_CX_COEFF1_MSB 0xf06100ff + +/* IIRCX_COEFF1_LSB */ +#define R367TER_IIRCX_COEFF1_LSB 0xf062 +#define F367TER_IIR_CX_COEFF1_LSB 0xf06200ff + +/* IIRCX_COEFF2_MSB */ +#define R367TER_IIRCX_COEFF2_MSB 0xf063 +#define F367TER_IIR_CX_COEFF2_MSB 0xf06300ff + +/* IIRCX_COEFF2_LSB */ +#define R367TER_IIRCX_COEFF2_LSB 0xf064 +#define F367TER_IIR_CX_COEFF2_LSB 0xf06400ff + +/* IIRCX_COEFF3_MSB */ +#define R367TER_IIRCX_COEFF3_MSB 0xf065 +#define F367TER_IIR_CX_COEFF3_MSB 0xf06500ff + +/* IIRCX_COEFF3_LSB */ +#define R367TER_IIRCX_COEFF3_LSB 0xf066 +#define F367TER_IIR_CX_COEFF3_LSB 0xf06600ff + +/* IIRCX_COEFF4_MSB */ +#define R367TER_IIRCX_COEFF4_MSB 0xf067 +#define F367TER_IIR_CX_COEFF4_MSB 0xf06700ff + +/* IIRCX_COEFF4_LSB */ +#define R367TER_IIRCX_COEFF4_LSB 0xf068 +#define F367TER_IIR_CX_COEFF4_LSB 0xf06800ff + +/* IIRCX_COEFF5_MSB */ +#define R367TER_IIRCX_COEFF5_MSB 0xf069 +#define F367TER_IIR_CX_COEFF5_MSB 0xf06900ff + +/* IIRCX_COEFF5_LSB */ +#define R367TER_IIRCX_COEFF5_LSB 0xf06a +#define F367TER_IIR_CX_COEFF5_LSB 0xf06a00ff + +/* FEPATH_CFG */ +#define R367TER_FEPATH_CFG 0xf06b +#define F367TER_DEMUX_SWAP 0xf06b0004 +#define F367TER_DIGAGC_SWAP 0xf06b0002 +#define F367TER_LONGPATH_IF 0xf06b0001 + +/* PMC1_FUNC */ +#define R367TER_PMC1_FUNC 0xf06c +#define F367TER_SOFT_RSTN 0xf06c0080 +#define F367TER_PMC1_AVERAGE_TIME 0xf06c0078 +#define F367TER_PMC1_WAIT_TIME 0xf06c0006 +#define F367TER_PMC1_2N_SEL 0xf06c0001 + +/* PMC1_FOR */ +#define R367TER_PMC1_FOR 0xf06d +#define F367TER_PMC1_FORCE 0xf06d0080 +#define F367TER_PMC1_FORCE_VALUE 0xf06d007c + +/* PMC2_FUNC */ +#define R367TER_PMC2_FUNC 0xf06e +#define F367TER_PMC2_SOFT_STN 0xf06e0080 +#define F367TER_PMC2_ACCU_TIME 0xf06e0070 +#define F367TER_PMC2_CMDP_MN 0xf06e0008 +#define F367TER_PMC2_SWAP 0xf06e0004 + +/* STATUS_ERR_DA */ +#define R367TER_STATUS_ERR_DA 0xf06f +#define F367TER_COM_USEGAINTRK 0xf06f0080 +#define F367TER_COM_AGCLOCK 0xf06f0040 +#define F367TER_AUT_AGCLOCK 0xf06f0020 +#define F367TER_MIN_ERR_X_LSB 0xf06f000f + +/* DIG_AGC_R */ +#define R367TER_DIG_AGC_R 0xf070 +#define F367TER_COM_SOFT_RSTN 0xf0700080 +#define F367TER_COM_AGC_ON 0xf0700040 +#define F367TER_COM_EARLY 0xf0700020 +#define F367TER_AUT_SOFT_RESETN 0xf0700010 +#define F367TER_AUT_AGC_ON 0xf0700008 +#define F367TER_AUT_EARLY 0xf0700004 +#define F367TER_AUT_ROT_EN 0xf0700002 +#define F367TER_LOCK_SOFT_RESETN 0xf0700001 + +/* COMAGC_TARMSB */ +#define R367TER_COMAGC_TARMSB 0xf071 +#define F367TER_COM_AGC_TARGET_MSB 0xf07100ff + +/* COM_AGC_TAR_ENMODE */ +#define R367TER_COM_AGC_TAR_ENMODE 0xf072 +#define F367TER_COM_AGC_TARGET_LSB 0xf07200f0 +#define F367TER_COM_ENMODE 0xf072000f + +/* COM_AGC_CFG */ +#define R367TER_COM_AGC_CFG 0xf073 +#define F367TER_COM_N 0xf07300f8 +#define F367TER_COM_STABMODE 0xf0730006 +#define F367TER_ERR_SEL 0xf0730001 + +/* COM_AGC_GAIN1 */ +#define R367TER_COM_AGC_GAIN1 0xf074 +#define F367TER_COM_GAIN1aCK 0xf07400f0 +#define F367TER_COM_GAIN1TRK 0xf074000f + +/* AUT_AGC_TARGETMSB */ +#define R367TER_AUT_AGC_TARGETMSB 0xf075 +#define F367TER_AUT_AGC_TARGET_MSB 0xf07500ff + +/* LOCK_DET_MSB */ +#define R367TER_LOCK_DET_MSB 0xf076 +#define F367TER_LOCK_DETECT_MSB 0xf07600ff + +/* AGCTAR_LOCK_LSBS */ +#define R367TER_AGCTAR_LOCK_LSBS 0xf077 +#define F367TER_AUT_AGC_TARGET_LSB 0xf07700f0 +#define F367TER_LOCK_DETECT_LSB 0xf077000f + +/* AUT_GAIN_EN */ +#define R367TER_AUT_GAIN_EN 0xf078 +#define F367TER_AUT_ENMODE 0xf07800f0 +#define F367TER_AUT_GAIN2 0xf078000f + +/* AUT_CFG */ +#define R367TER_AUT_CFG 0xf079 +#define F367TER_AUT_N 0xf07900f8 +#define F367TER_INT_CHOICE 0xf0790006 +#define F367TER_INT_LOAD 0xf0790001 + +/* LOCKN */ +#define R367TER_LOCKN 0xf07a +#define F367TER_LOCK_N 0xf07a00f8 +#define F367TER_SEL_IQNTAR 0xf07a0004 +#define F367TER_LOCK_DETECT_CHOICE 0xf07a0003 + +/* INT_X_3 */ +#define R367TER_INT_X_3 0xf07b +#define F367TER_INT_X3 0xf07b00ff + +/* INT_X_2 */ +#define R367TER_INT_X_2 0xf07c +#define F367TER_INT_X2 0xf07c00ff + +/* INT_X_1 */ +#define R367TER_INT_X_1 0xf07d +#define F367TER_INT_X1 0xf07d00ff + +/* INT_X_0 */ +#define R367TER_INT_X_0 0xf07e +#define F367TER_INT_X0 0xf07e00ff + +/* MIN_ERRX_MSB */ +#define R367TER_MIN_ERRX_MSB 0xf07f +#define F367TER_MIN_ERR_X_MSB 0xf07f00ff + +/* COR_CTL */ +#define R367TER_COR_CTL 0xf080 +#define F367TER_CORE_ACTIVE 0xf0800020 +#define F367TER_HOLD 0xf0800010 +#define F367TER_CORE_STATE_CTL 0xf080000f + +/* COR_STAT */ +#define R367TER_COR_STAT 0xf081 +#define F367TER_SCATT_LOCKED 0xf0810080 +#define F367TER_TPS_LOCKED 0xf0810040 +#define F367TER_SYR_LOCKED_COR 0xf0810020 +#define F367TER_AGC_LOCKED_STAT 0xf0810010 +#define F367TER_CORE_STATE_STAT 0xf081000f + +/* COR_INTEN */ +#define R367TER_COR_INTEN 0xf082 +#define F367TER_INTEN 0xf0820080 +#define F367TER_INTEN_SYR 0xf0820020 +#define F367TER_INTEN_FFT 0xf0820010 +#define F367TER_INTEN_AGC 0xf0820008 +#define F367TER_INTEN_TPS1 0xf0820004 +#define F367TER_INTEN_TPS2 0xf0820002 +#define F367TER_INTEN_TPS3 0xf0820001 + +/* COR_INTSTAT */ +#define R367TER_COR_INTSTAT 0xf083 +#define F367TER_INTSTAT_SYR 0xf0830020 +#define F367TER_INTSTAT_FFT 0xf0830010 +#define F367TER_INTSAT_AGC 0xf0830008 +#define F367TER_INTSTAT_TPS1 0xf0830004 +#define F367TER_INTSTAT_TPS2 0xf0830002 +#define F367TER_INTSTAT_TPS3 0xf0830001 + +/* COR_MODEGUARD */ +#define R367TER_COR_MODEGUARD 0xf084 +#define F367TER_FORCE 0xf0840010 +#define F367TER_MODE 0xf084000c +#define F367TER_GUARD 0xf0840003 + +/* AGC_CTL */ +#define R367TER_AGC_CTL 0xf085 +#define F367TER_AGC_TIMING_FACTOR 0xf08500e0 +#define F367TER_AGC_LAST 0xf0850010 +#define F367TER_AGC_GAIN 0xf085000c +#define F367TER_AGC_NEG 0xf0850002 +#define F367TER_AGC_SET 0xf0850001 + +/* AGC_MANUAL1 */ +#define R367TER_AGC_MANUAL1 0xf086 +#define F367TER_AGC_VAL_LO 0xf08600ff + +/* AGC_MANUAL2 */ +#define R367TER_AGC_MANUAL2 0xf087 +#define F367TER_AGC_VAL_HI 0xf087000f + +/* AGC_TARG */ +#define R367TER_AGC_TARG 0xf088 +#define F367TER_AGC_TARGET 0xf08800ff + +/* AGC_GAIN1 */ +#define R367TER_AGC_GAIN1 0xf089 +#define F367TER_AGC_GAIN_LO 0xf08900ff + +/* AGC_GAIN2 */ +#define R367TER_AGC_GAIN2 0xf08a +#define F367TER_AGC_LOCKED_GAIN2 0xf08a0010 +#define F367TER_AGC_GAIN_HI 0xf08a000f + +/* RESERVED_1 */ +#define R367TER_RESERVED_1 0xf08b +#define F367TER_RESERVED1 0xf08b00ff + +/* RESERVED_2 */ +#define R367TER_RESERVED_2 0xf08c +#define F367TER_RESERVED2 0xf08c00ff + +/* RESERVED_3 */ +#define R367TER_RESERVED_3 0xf08d +#define F367TER_RESERVED3 0xf08d00ff + +/* CAS_CTL */ +#define R367TER_CAS_CTL 0xf08e +#define F367TER_CCS_ENABLE 0xf08e0080 +#define F367TER_ACS_DISABLE 0xf08e0040 +#define F367TER_DAGC_DIS 0xf08e0020 +#define F367TER_DAGC_GAIN 0xf08e0018 +#define F367TER_CCSMU 0xf08e0007 + +/* CAS_FREQ */ +#define R367TER_CAS_FREQ 0xf08f +#define F367TER_CCS_FREQ 0xf08f00ff + +/* CAS_DAGCGAIN */ +#define R367TER_CAS_DAGCGAIN 0xf090 +#define F367TER_CAS_DAGC_GAIN 0xf09000ff + +/* SYR_CTL */ +#define R367TER_SYR_CTL 0xf091 +#define F367TER_SICTH_ENABLE 0xf0910080 +#define F367TER_LONG_ECHO 0xf0910078 +#define F367TER_AUTO_LE_EN 0xf0910004 +#define F367TER_SYR_BYPASS 0xf0910002 +#define F367TER_SYR_TR_DIS 0xf0910001 + +/* SYR_STAT */ +#define R367TER_SYR_STAT 0xf092 +#define F367TER_SYR_LOCKED_STAT 0xf0920010 +#define F367TER_SYR_MODE 0xf092000c +#define F367TER_SYR_GUARD 0xf0920003 + +/* SYR_NCO1 */ +#define R367TER_SYR_NCO1 0xf093 +#define F367TER_SYR_NCO_LO 0xf09300ff + +/* SYR_NCO2 */ +#define R367TER_SYR_NCO2 0xf094 +#define F367TER_SYR_NCO_HI 0xf094003f + +/* SYR_OFFSET1 */ +#define R367TER_SYR_OFFSET1 0xf095 +#define F367TER_SYR_OFFSET_LO 0xf09500ff + +/* SYR_OFFSET2 */ +#define R367TER_SYR_OFFSET2 0xf096 +#define F367TER_SYR_OFFSET_HI 0xf096003f + +/* FFT_CTL */ +#define R367TER_FFT_CTL 0xf097 +#define F367TER_SHIFT_FFT_TRIG 0xf0970018 +#define F367TER_FFT_TRIGGER 0xf0970004 +#define F367TER_FFT_MANUAL 0xf0970002 +#define F367TER_IFFT_MODE 0xf0970001 + +/* SCR_CTL */ +#define R367TER_SCR_CTL 0xf098 +#define F367TER_SYRADJDECAY 0xf0980070 +#define F367TER_SCR_CPEDIS 0xf0980002 +#define F367TER_SCR_DIS 0xf0980001 + +/* PPM_CTL1 */ +#define R367TER_PPM_CTL1 0xf099 +#define F367TER_PPM_MAXFREQ 0xf0990030 +#define F367TER_PPM_MAXTIM 0xf0990008 +#define F367TER_PPM_INVSEL 0xf0990004 +#define F367TER_PPM_SCATDIS 0xf0990002 +#define F367TER_PPM_BYP 0xf0990001 + +/* TRL_CTL */ +#define R367TER_TRL_CTL 0xf09a +#define F367TER_TRL_NOMRATE_LSB 0xf09a0080 +#define F367TER_TRL_GAIN_FACTOR 0xf09a0078 +#define F367TER_TRL_LOOPGAIN 0xf09a0007 + +/* TRL_NOMRATE1 */ +#define R367TER_TRL_NOMRATE1 0xf09b +#define F367TER_TRL_NOMRATE_LO 0xf09b00ff + +/* TRL_NOMRATE2 */ +#define R367TER_TRL_NOMRATE2 0xf09c +#define F367TER_TRL_NOMRATE_HI 0xf09c00ff + +/* TRL_TIME1 */ +#define R367TER_TRL_TIME1 0xf09d +#define F367TER_TRL_TOFFSET_LO 0xf09d00ff + +/* TRL_TIME2 */ +#define R367TER_TRL_TIME2 0xf09e +#define F367TER_TRL_TOFFSET_HI 0xf09e00ff + +/* CRL_CTL */ +#define R367TER_CRL_CTL 0xf09f +#define F367TER_CRL_DIS 0xf09f0080 +#define F367TER_CRL_GAIN_FACTOR 0xf09f0078 +#define F367TER_CRL_LOOPGAIN 0xf09f0007 + +/* CRL_FREQ1 */ +#define R367TER_CRL_FREQ1 0xf0a0 +#define F367TER_CRL_FOFFSET_LO 0xf0a000ff + +/* CRL_FREQ2 */ +#define R367TER_CRL_FREQ2 0xf0a1 +#define F367TER_CRL_FOFFSET_HI 0xf0a100ff + +/* CRL_FREQ3 */ +#define R367TER_CRL_FREQ3 0xf0a2 +#define F367TER_CRL_FOFFSET_VHI 0xf0a200ff + +/* TPS_SFRAME_CTL */ +#define R367TER_TPS_SFRAME_CTL 0xf0a3 +#define F367TER_TPS_SFRAME_SYNC 0xf0a30001 + +/* CHC_SNR */ +#define R367TER_CHC_SNR 0xf0a4 +#define F367TER_CHCSNR 0xf0a400ff + +/* BDI_CTL */ +#define R367TER_BDI_CTL 0xf0a5 +#define F367TER_BDI_LPSEL 0xf0a50002 +#define F367TER_BDI_SERIAL 0xf0a50001 + +/* DMP_CTL */ +#define R367TER_DMP_CTL 0xf0a6 +#define F367TER_DMP_SCALING_FACTOR 0xf0a6001e +#define F367TER_DMP_SDDIS 0xf0a60001 + +/* TPS_RCVD1 */ +#define R367TER_TPS_RCVD1 0xf0a7 +#define F367TER_TPS_CHANGE 0xf0a70040 +#define F367TER_BCH_OK 0xf0a70020 +#define F367TER_TPS_SYNC 0xf0a70010 +#define F367TER_TPS_FRAME 0xf0a70003 + +/* TPS_RCVD2 */ +#define R367TER_TPS_RCVD2 0xf0a8 +#define F367TER_TPS_HIERMODE 0xf0a80070 +#define F367TER_TPS_CONST 0xf0a80003 + +/* TPS_RCVD3 */ +#define R367TER_TPS_RCVD3 0xf0a9 +#define F367TER_TPS_LPCODE 0xf0a90070 +#define F367TER_TPS_HPCODE 0xf0a90007 + +/* TPS_RCVD4 */ +#define R367TER_TPS_RCVD4 0xf0aa +#define F367TER_TPS_GUARD 0xf0aa0030 +#define F367TER_TPS_MODE 0xf0aa0003 + +/* TPS_ID_CELL1 */ +#define R367TER_TPS_ID_CELL1 0xf0ab +#define F367TER_TPS_ID_CELL_LO 0xf0ab00ff + +/* TPS_ID_CELL2 */ +#define R367TER_TPS_ID_CELL2 0xf0ac +#define F367TER_TPS_ID_CELL_HI 0xf0ac00ff + +/* TPS_RCVD5_SET1 */ +#define R367TER_TPS_RCVD5_SET1 0xf0ad +#define F367TER_TPS_NA 0xf0ad00fC +#define F367TER_TPS_SETFRAME 0xf0ad0003 + +/* TPS_SET2 */ +#define R367TER_TPS_SET2 0xf0ae +#define F367TER_TPS_SETHIERMODE 0xf0ae0070 +#define F367TER_TPS_SETCONST 0xf0ae0003 + +/* TPS_SET3 */ +#define R367TER_TPS_SET3 0xf0af +#define F367TER_TPS_SETLPCODE 0xf0af0070 +#define F367TER_TPS_SETHPCODE 0xf0af0007 + +/* TPS_CTL */ +#define R367TER_TPS_CTL 0xf0b0 +#define F367TER_TPS_IMM 0xf0b00004 +#define F367TER_TPS_BCHDIS 0xf0b00002 +#define F367TER_TPS_UPDDIS 0xf0b00001 + +/* CTL_FFTOSNUM */ +#define R367TER_CTL_FFTOSNUM 0xf0b1 +#define F367TER_SYMBOL_NUMBER 0xf0b1007f + +/* TESTSELECT */ +#define R367TER_TESTSELECT 0xf0b2 +#define F367TER_TEST_SELECT 0xf0b2001f + +/* MSC_REV */ +#define R367TER_MSC_REV 0xf0b3 +#define F367TER_REV_NUMBER 0xf0b300ff + +/* PIR_CTL */ +#define R367TER_PIR_CTL 0xf0b4 +#define F367TER_FREEZE 0xf0b40001 + +/* SNR_CARRIER1 */ +#define R367TER_SNR_CARRIER1 0xf0b5 +#define F367TER_SNR_CARRIER_LO 0xf0b500ff + +/* SNR_CARRIER2 */ +#define R367TER_SNR_CARRIER2 0xf0b6 +#define F367TER_MEAN 0xf0b600c0 +#define F367TER_SNR_CARRIER_HI 0xf0b6001f + +/* PPM_CPAMP */ +#define R367TER_PPM_CPAMP 0xf0b7 +#define F367TER_PPM_CPC 0xf0b700ff + +/* TSM_AP0 */ +#define R367TER_TSM_AP0 0xf0b8 +#define F367TER_ADDRESS_BYTE_0 0xf0b800ff + +/* TSM_AP1 */ +#define R367TER_TSM_AP1 0xf0b9 +#define F367TER_ADDRESS_BYTE_1 0xf0b900ff + +/* TSM_AP2 */ +#define R367TER_TSM_AP2 0xf0bA +#define F367TER_DATA_BYTE_0 0xf0ba00ff + +/* TSM_AP3 */ +#define R367TER_TSM_AP3 0xf0bB +#define F367TER_DATA_BYTE_1 0xf0bb00ff + +/* TSM_AP4 */ +#define R367TER_TSM_AP4 0xf0bC +#define F367TER_DATA_BYTE_2 0xf0bc00ff + +/* TSM_AP5 */ +#define R367TER_TSM_AP5 0xf0bD +#define F367TER_DATA_BYTE_3 0xf0bd00ff + +/* TSM_AP6 */ +#define R367TER_TSM_AP6 0xf0bE +#define F367TER_TSM_AP_6 0xf0be00ff + +/* TSM_AP7 */ +#define R367TER_TSM_AP7 0xf0bF +#define F367TER_MEM_SELECT_BYTE 0xf0bf00ff + +/* TSTRES */ +#define R367TER_TSTRES 0xf0c0 +#define F367TER_FRES_DISPLAY 0xf0c00080 +#define F367TER_FRES_FIFO_AD 0xf0c00020 +#define F367TER_FRESRS 0xf0c00010 +#define F367TER_FRESACS 0xf0c00008 +#define F367TER_FRESFEC 0xf0c00004 +#define F367TER_FRES_PRIF 0xf0c00002 +#define F367TER_FRESCORE 0xf0c00001 + +/* ANACTRL */ +#define R367TER_ANACTRL 0xf0c1 +#define F367TER_BYPASS_XTAL 0xf0c10040 +#define F367TER_BYPASS_PLLXN 0xf0c1000c +#define F367TER_DIS_PAD_OSC 0xf0c10002 +#define F367TER_STDBY_PLLXN 0xf0c10001 + +/* TSTBUS */ +#define R367TER_TSTBUS 0xf0c2 +#define F367TER_TS_BYTE_CLK_INV 0xf0c20080 +#define F367TER_CFG_IP 0xf0c20070 +#define F367TER_CFG_TST 0xf0c2000f + +/* TSTRATE */ +#define R367TER_TSTRATE 0xf0c6 +#define F367TER_FORCEPHA 0xf0c60080 +#define F367TER_FNEWPHA 0xf0c60010 +#define F367TER_FROT90 0xf0c60008 +#define F367TER_FR 0xf0c60007 + +/* CONSTMODE */ +#define R367TER_CONSTMODE 0xf0cb +#define F367TER_TST_PRIF 0xf0cb00e0 +#define F367TER_CAR_TYPE 0xf0cb0018 +#define F367TER_CONST_MODE 0xf0cb0003 + +/* CONSTCARR1 */ +#define R367TER_CONSTCARR1 0xf0cc +#define F367TER_CONST_CARR_LO 0xf0cc00ff + +/* CONSTCARR2 */ +#define R367TER_CONSTCARR2 0xf0cd +#define F367TER_CONST_CARR_HI 0xf0cd001f + +/* ICONSTEL */ +#define R367TER_ICONSTEL 0xf0ce +#define F367TER_PICONSTEL 0xf0ce00ff + +/* QCONSTEL */ +#define R367TER_QCONSTEL 0xf0cf +#define F367TER_PQCONSTEL 0xf0cf00ff + +/* TSTBISTRES0 */ +#define R367TER_TSTBISTRES0 0xf0d0 +#define F367TER_BEND_PPM 0xf0d00080 +#define F367TER_BBAD_PPM 0xf0d00040 +#define F367TER_BEND_FFTW 0xf0d00020 +#define F367TER_BBAD_FFTW 0xf0d00010 +#define F367TER_BEND_FFT_BUF 0xf0d00008 +#define F367TER_BBAD_FFT_BUF 0xf0d00004 +#define F367TER_BEND_SYR 0xf0d00002 +#define F367TER_BBAD_SYR 0xf0d00001 + +/* TSTBISTRES1 */ +#define R367TER_TSTBISTRES1 0xf0d1 +#define F367TER_BEND_CHC_CP 0xf0d10080 +#define F367TER_BBAD_CHC_CP 0xf0d10040 +#define F367TER_BEND_CHCI 0xf0d10020 +#define F367TER_BBAD_CHCI 0xf0d10010 +#define F367TER_BEND_BDI 0xf0d10008 +#define F367TER_BBAD_BDI 0xf0d10004 +#define F367TER_BEND_SDI 0xf0d10002 +#define F367TER_BBAD_SDI 0xf0d10001 + +/* TSTBISTRES2 */ +#define R367TER_TSTBISTRES2 0xf0d2 +#define F367TER_BEND_CHC_INC 0xf0d20080 +#define F367TER_BBAD_CHC_INC 0xf0d20040 +#define F367TER_BEND_CHC_SPP 0xf0d20020 +#define F367TER_BBAD_CHC_SPP 0xf0d20010 +#define F367TER_BEND_CHC_CPP 0xf0d20008 +#define F367TER_BBAD_CHC_CPP 0xf0d20004 +#define F367TER_BEND_CHC_SP 0xf0d20002 +#define F367TER_BBAD_CHC_SP 0xf0d20001 + +/* TSTBISTRES3 */ +#define R367TER_TSTBISTRES3 0xf0d3 +#define F367TER_BEND_QAM 0xf0d30080 +#define F367TER_BBAD_QAM 0xf0d30040 +#define F367TER_BEND_SFEC_VIT 0xf0d30020 +#define F367TER_BBAD_SFEC_VIT 0xf0d30010 +#define F367TER_BEND_SFEC_DLINE 0xf0d30008 +#define F367TER_BBAD_SFEC_DLINE 0xf0d30004 +#define F367TER_BEND_SFEC_HW 0xf0d30002 +#define F367TER_BBAD_SFEC_HW 0xf0d30001 + +/* RF_AGC1 */ +#define R367TER_RF_AGC1 0xf0d4 +#define F367TER_RF_AGC1_LEVEL_HI 0xf0d400ff + +/* RF_AGC2 */ +#define R367TER_RF_AGC2 0xf0d5 +#define F367TER_REF_ADGP 0xf0d50080 +#define F367TER_STDBY_ADCGP 0xf0d50020 +#define F367TER_CHANNEL_SEL 0xf0d5001c +#define F367TER_RF_AGC1_LEVEL_LO 0xf0d50003 + +/* ANADIGCTRL */ +#define R367TER_ANADIGCTRL 0xf0d7 +#define F367TER_SEL_CLKDEM 0xf0d70020 +#define F367TER_EN_BUFFER_Q 0xf0d70010 +#define F367TER_EN_BUFFER_I 0xf0d70008 +#define F367TER_ADC_RIS_EGDE 0xf0d70004 +#define F367TER_SGN_ADC 0xf0d70002 +#define F367TER_SEL_AD12_SYNC 0xf0d70001 + +/* PLLMDIV */ +#define R367TER_PLLMDIV 0xf0d8 +#define F367TER_PLL_MDIV 0xf0d800ff + +/* PLLNDIV */ +#define R367TER_PLLNDIV 0xf0d9 +#define F367TER_PLL_NDIV 0xf0d900ff + +/* PLLSETUP */ +#define R367TER_PLLSETUP 0xf0dA +#define F367TER_PLL_PDIV 0xf0da0070 +#define F367TER_PLL_KDIV 0xf0da000f + +/* DUAL_AD12 */ +#define R367TER_DUAL_AD12 0xf0dB +#define F367TER_FS20M 0xf0db0020 +#define F367TER_FS50M 0xf0db0010 +#define F367TER_INMODe0 0xf0db0008 +#define F367TER_POFFQ 0xf0db0004 +#define F367TER_POFFI 0xf0db0002 +#define F367TER_INMODE1 0xf0db0001 + +/* TSTBIST */ +#define R367TER_TSTBIST 0xf0dC +#define F367TER_TST_BYP_CLK 0xf0dc0080 +#define F367TER_TST_GCLKENA_STD 0xf0dc0040 +#define F367TER_TST_GCLKENA 0xf0dc0020 +#define F367TER_TST_MEMBIST 0xf0dc001f + +/* PAD_COMP_CTRL */ +#define R367TER_PAD_COMP_CTRL 0xf0dD +#define F367TER_COMPTQ 0xf0dd0010 +#define F367TER_COMPEN 0xf0dd0008 +#define F367TER_FREEZE2 0xf0dd0004 +#define F367TER_SLEEP_INHBT 0xf0dd0002 +#define F367TER_CHIP_SLEEP 0xf0dd0001 + +/* PAD_COMP_WR */ +#define R367TER_PAD_COMP_WR 0xf0de +#define F367TER_WR_ASRC 0xf0de007f + +/* PAD_COMP_RD */ +#define R367TER_PAD_COMP_RD 0xf0df +#define F367TER_COMPOK 0xf0df0080 +#define F367TER_RD_ASRC 0xf0df007f + +/* SYR_TARGET_FFTADJT_MSB */ +#define R367TER_SYR_TARGET_FFTADJT_MSB 0xf100 +#define F367TER_SYR_START 0xf1000080 +#define F367TER_SYR_TARGET_FFTADJ_HI 0xf100000f + +/* SYR_TARGET_FFTADJT_LSB */ +#define R367TER_SYR_TARGET_FFTADJT_LSB 0xf101 +#define F367TER_SYR_TARGET_FFTADJ_LO 0xf10100ff + +/* SYR_TARGET_CHCADJT_MSB */ +#define R367TER_SYR_TARGET_CHCADJT_MSB 0xf102 +#define F367TER_SYR_TARGET_CHCADJ_HI 0xf102000f + +/* SYR_TARGET_CHCADJT_LSB */ +#define R367TER_SYR_TARGET_CHCADJT_LSB 0xf103 +#define F367TER_SYR_TARGET_CHCADJ_LO 0xf10300ff + +/* SYR_FLAG */ +#define R367TER_SYR_FLAG 0xf104 +#define F367TER_TRIG_FLG1 0xf1040080 +#define F367TER_TRIG_FLG0 0xf1040040 +#define F367TER_FFT_FLG1 0xf1040008 +#define F367TER_FFT_FLG0 0xf1040004 +#define F367TER_CHC_FLG1 0xf1040002 +#define F367TER_CHC_FLG0 0xf1040001 + +/* CRL_TARGET1 */ +#define R367TER_CRL_TARGET1 0xf105 +#define F367TER_CRL_START 0xf1050080 +#define F367TER_CRL_TARGET_VHI 0xf105000f + +/* CRL_TARGET2 */ +#define R367TER_CRL_TARGET2 0xf106 +#define F367TER_CRL_TARGET_HI 0xf10600ff + +/* CRL_TARGET3 */ +#define R367TER_CRL_TARGET3 0xf107 +#define F367TER_CRL_TARGET_LO 0xf10700ff + +/* CRL_TARGET4 */ +#define R367TER_CRL_TARGET4 0xf108 +#define F367TER_CRL_TARGET_VLO 0xf10800ff + +/* CRL_FLAG */ +#define R367TER_CRL_FLAG 0xf109 +#define F367TER_CRL_FLAG1 0xf1090002 +#define F367TER_CRL_FLAG0 0xf1090001 + +/* TRL_TARGET1 */ +#define R367TER_TRL_TARGET1 0xf10a +#define F367TER_TRL_TARGET_HI 0xf10a00ff + +/* TRL_TARGET2 */ +#define R367TER_TRL_TARGET2 0xf10b +#define F367TER_TRL_TARGET_LO 0xf10b00ff + +/* TRL_CHC */ +#define R367TER_TRL_CHC 0xf10c +#define F367TER_TRL_START 0xf10c0080 +#define F367TER_CHC_START 0xf10c0040 +#define F367TER_TRL_FLAG1 0xf10c0002 +#define F367TER_TRL_FLAG0 0xf10c0001 + +/* CHC_SNR_TARG */ +#define R367TER_CHC_SNR_TARG 0xf10d +#define F367TER_CHC_SNR_TARGET 0xf10d00ff + +/* TOP_TRACK */ +#define R367TER_TOP_TRACK 0xf10e +#define F367TER_TOP_START 0xf10e0080 +#define F367TER_FIRST_FLAG 0xf10e0070 +#define F367TER_TOP_FLAG1 0xf10e0008 +#define F367TER_TOP_FLAG0 0xf10e0004 +#define F367TER_CHC_FLAG1 0xf10e0002 +#define F367TER_CHC_FLAG0 0xf10e0001 + +/* TRACKER_FREE1 */ +#define R367TER_TRACKER_FREE1 0xf10f +#define F367TER_TRACKER_FREE_1 0xf10f00ff + +/* ERROR_CRL1 */ +#define R367TER_ERROR_CRL1 0xf110 +#define F367TER_ERROR_CRL_VHI 0xf11000ff + +/* ERROR_CRL2 */ +#define R367TER_ERROR_CRL2 0xf111 +#define F367TER_ERROR_CRL_HI 0xf11100ff + +/* ERROR_CRL3 */ +#define R367TER_ERROR_CRL3 0xf112 +#define F367TER_ERROR_CRL_LOI 0xf11200ff + +/* ERROR_CRL4 */ +#define R367TER_ERROR_CRL4 0xf113 +#define F367TER_ERROR_CRL_VLO 0xf11300ff + +/* DEC_NCO1 */ +#define R367TER_DEC_NCO1 0xf114 +#define F367TER_DEC_NCO_VHI 0xf11400ff + +/* DEC_NCO2 */ +#define R367TER_DEC_NCO2 0xf115 +#define F367TER_DEC_NCO_HI 0xf11500ff + +/* DEC_NCO3 */ +#define R367TER_DEC_NCO3 0xf116 +#define F367TER_DEC_NCO_LO 0xf11600ff + +/* SNR */ +#define R367TER_SNR 0xf117 +#define F367TER_SNRATIO 0xf11700ff + +/* SYR_FFTADJ1 */ +#define R367TER_SYR_FFTADJ1 0xf118 +#define F367TER_SYR_FFTADJ_HI 0xf11800ff + +/* SYR_FFTADJ2 */ +#define R367TER_SYR_FFTADJ2 0xf119 +#define F367TER_SYR_FFTADJ_LO 0xf11900ff + +/* SYR_CHCADJ1 */ +#define R367TER_SYR_CHCADJ1 0xf11a +#define F367TER_SYR_CHCADJ_HI 0xf11a00ff + +/* SYR_CHCADJ2 */ +#define R367TER_SYR_CHCADJ2 0xf11b +#define F367TER_SYR_CHCADJ_LO 0xf11b00ff + +/* SYR_OFF */ +#define R367TER_SYR_OFF 0xf11c +#define F367TER_SYR_OFFSET 0xf11c00ff + +/* PPM_OFFSET1 */ +#define R367TER_PPM_OFFSET1 0xf11d +#define F367TER_PPM_OFFSET_HI 0xf11d00ff + +/* PPM_OFFSET2 */ +#define R367TER_PPM_OFFSET2 0xf11e +#define F367TER_PPM_OFFSET_LO 0xf11e00ff + +/* TRACKER_FREE2 */ +#define R367TER_TRACKER_FREE2 0xf11f +#define F367TER_TRACKER_FREE_2 0xf11f00ff + +/* DEBG_LT10 */ +#define R367TER_DEBG_LT10 0xf120 +#define F367TER_DEBUG_LT10 0xf12000ff + +/* DEBG_LT11 */ +#define R367TER_DEBG_LT11 0xf121 +#define F367TER_DEBUG_LT11 0xf12100ff + +/* DEBG_LT12 */ +#define R367TER_DEBG_LT12 0xf122 +#define F367TER_DEBUG_LT12 0xf12200ff + +/* DEBG_LT13 */ +#define R367TER_DEBG_LT13 0xf123 +#define F367TER_DEBUG_LT13 0xf12300ff + +/* DEBG_LT14 */ +#define R367TER_DEBG_LT14 0xf124 +#define F367TER_DEBUG_LT14 0xf12400ff + +/* DEBG_LT15 */ +#define R367TER_DEBG_LT15 0xf125 +#define F367TER_DEBUG_LT15 0xf12500ff + +/* DEBG_LT16 */ +#define R367TER_DEBG_LT16 0xf126 +#define F367TER_DEBUG_LT16 0xf12600ff + +/* DEBG_LT17 */ +#define R367TER_DEBG_LT17 0xf127 +#define F367TER_DEBUG_LT17 0xf12700ff + +/* DEBG_LT18 */ +#define R367TER_DEBG_LT18 0xf128 +#define F367TER_DEBUG_LT18 0xf12800ff + +/* DEBG_LT19 */ +#define R367TER_DEBG_LT19 0xf129 +#define F367TER_DEBUG_LT19 0xf12900ff + +/* DEBG_LT1a */ +#define R367TER_DEBG_LT1A 0xf12a +#define F367TER_DEBUG_LT1A 0xf12a00ff + +/* DEBG_LT1b */ +#define R367TER_DEBG_LT1B 0xf12b +#define F367TER_DEBUG_LT1B 0xf12b00ff + +/* DEBG_LT1c */ +#define R367TER_DEBG_LT1C 0xf12c +#define F367TER_DEBUG_LT1C 0xf12c00ff + +/* DEBG_LT1D */ +#define R367TER_DEBG_LT1D 0xf12d +#define F367TER_DEBUG_LT1D 0xf12d00ff + +/* DEBG_LT1E */ +#define R367TER_DEBG_LT1E 0xf12e +#define F367TER_DEBUG_LT1E 0xf12e00ff + +/* DEBG_LT1F */ +#define R367TER_DEBG_LT1F 0xf12f +#define F367TER_DEBUG_LT1F 0xf12f00ff + +/* RCCFGH */ +#define R367TER_RCCFGH 0xf200 +#define F367TER_TSRCFIFO_DVBCI 0xf2000080 +#define F367TER_TSRCFIFO_SERIAL 0xf2000040 +#define F367TER_TSRCFIFO_DISABLE 0xf2000020 +#define F367TER_TSFIFO_2TORC 0xf2000010 +#define F367TER_TSRCFIFO_HSGNLOUT 0xf2000008 +#define F367TER_TSRCFIFO_ERRMODE 0xf2000006 +#define F367TER_RCCFGH_0 0xf2000001 + +/* RCCFGM */ +#define R367TER_RCCFGM 0xf201 +#define F367TER_TSRCFIFO_MANSPEED 0xf20100c0 +#define F367TER_TSRCFIFO_PERMDATA 0xf2010020 +#define F367TER_TSRCFIFO_NONEWSGNL 0xf2010010 +#define F367TER_RCBYTE_OVERSAMPLING 0xf201000e +#define F367TER_TSRCFIFO_INVDATA 0xf2010001 + +/* RCCFGL */ +#define R367TER_RCCFGL 0xf202 +#define F367TER_TSRCFIFO_BCLKDEL1cK 0xf20200c0 +#define F367TER_RCCFGL_5 0xf2020020 +#define F367TER_TSRCFIFO_DUTY50 0xf2020010 +#define F367TER_TSRCFIFO_NSGNL2dATA 0xf2020008 +#define F367TER_TSRCFIFO_DISSERMUX 0xf2020004 +#define F367TER_RCCFGL_1 0xf2020002 +#define F367TER_TSRCFIFO_STOPCKDIS 0xf2020001 + +/* RCINSDELH */ +#define R367TER_RCINSDELH 0xf203 +#define F367TER_TSRCDEL_SYNCBYTE 0xf2030080 +#define F367TER_TSRCDEL_XXHEADER 0xf2030040 +#define F367TER_TSRCDEL_BBHEADER 0xf2030020 +#define F367TER_TSRCDEL_DATAFIELD 0xf2030010 +#define F367TER_TSRCINSDEL_ISCR 0xf2030008 +#define F367TER_TSRCINSDEL_NPD 0xf2030004 +#define F367TER_TSRCINSDEL_RSPARITY 0xf2030002 +#define F367TER_TSRCINSDEL_CRC8 0xf2030001 + +/* RCINSDELM */ +#define R367TER_RCINSDELM 0xf204 +#define F367TER_TSRCINS_BBPADDING 0xf2040080 +#define F367TER_TSRCINS_BCHFEC 0xf2040040 +#define F367TER_TSRCINS_LDPCFEC 0xf2040020 +#define F367TER_TSRCINS_EMODCOD 0xf2040010 +#define F367TER_TSRCINS_TOKEN 0xf2040008 +#define F367TER_TSRCINS_XXXERR 0xf2040004 +#define F367TER_TSRCINS_MATYPE 0xf2040002 +#define F367TER_TSRCINS_UPL 0xf2040001 + +/* RCINSDELL */ +#define R367TER_RCINSDELL 0xf205 +#define F367TER_TSRCINS_DFL 0xf2050080 +#define F367TER_TSRCINS_SYNCD 0xf2050040 +#define F367TER_TSRCINS_BLOCLEN 0xf2050020 +#define F367TER_TSRCINS_SIGPCOUNT 0xf2050010 +#define F367TER_TSRCINS_FIFO 0xf2050008 +#define F367TER_TSRCINS_REALPACK 0xf2050004 +#define F367TER_TSRCINS_TSCONFIG 0xf2050002 +#define F367TER_TSRCINS_LATENCY 0xf2050001 + +/* RCSTATUS */ +#define R367TER_RCSTATUS 0xf206 +#define F367TER_TSRCFIFO_LINEOK 0xf2060080 +#define F367TER_TSRCFIFO_ERROR 0xf2060040 +#define F367TER_TSRCFIFO_DATA7 0xf2060020 +#define F367TER_RCSTATUS_4 0xf2060010 +#define F367TER_TSRCFIFO_DEMODSEL 0xf2060008 +#define F367TER_TSRC1FIFOSPEED_STORE 0xf2060004 +#define F367TER_RCSTATUS_1 0xf2060002 +#define F367TER_TSRCSERIAL_IMPOSSIBLE 0xf2060001 + +/* RCSPEED */ +#define R367TER_RCSPEED 0xf207 +#define F367TER_TSRCFIFO_OUTSPEED 0xf20700ff + +/* RCDEBUGM */ +#define R367TER_RCDEBUGM 0xf208 +#define F367TER_SD_UNSYNC 0xf2080080 +#define F367TER_ULFLOCK_DETECTM 0xf2080040 +#define F367TER_SUL_SELECTOS 0xf2080020 +#define F367TER_DILUL_NOSCRBLE 0xf2080010 +#define F367TER_NUL_SCRB 0xf2080008 +#define F367TER_UL_SCRB 0xf2080004 +#define F367TER_SCRAULBAD 0xf2080002 +#define F367TER_SCRAUL_UNSYNC 0xf2080001 + +/* RCDEBUGL */ +#define R367TER_RCDEBUGL 0xf209 +#define F367TER_RS_ERR 0xf2090080 +#define F367TER_LLFLOCK_DETECTM 0xf2090040 +#define F367TER_NOT_SUL_SELECTOS 0xf2090020 +#define F367TER_DILLL_NOSCRBLE 0xf2090010 +#define F367TER_NLL_SCRB 0xf2090008 +#define F367TER_LL_SCRB 0xf2090004 +#define F367TER_SCRALLBAD 0xf2090002 +#define F367TER_SCRALL_UNSYNC 0xf2090001 + +/* RCOBSCFG */ +#define R367TER_RCOBSCFG 0xf20a +#define F367TER_TSRCFIFO_OBSCFG 0xf20a00ff + +/* RCOBSM */ +#define R367TER_RCOBSM 0xf20b +#define F367TER_TSRCFIFO_OBSDATA_HI 0xf20b00ff + +/* RCOBSL */ +#define R367TER_RCOBSL 0xf20c +#define F367TER_TSRCFIFO_OBSDATA_LO 0xf20c00ff + +/* RCFECSPY */ +#define R367TER_RCFECSPY 0xf210 +#define F367TER_SPYRC_ENABLE 0xf2100080 +#define F367TER_RCNO_SYNCBYTE 0xf2100040 +#define F367TER_RCSERIAL_MODE 0xf2100020 +#define F367TER_RCUNUSUAL_PACKET 0xf2100010 +#define F367TER_BERRCMETER_DATAMODE 0xf210000c +#define F367TER_BERRCMETER_LMODE 0xf2100002 +#define F367TER_BERRCMETER_RESET 0xf2100001 + +/* RCFSPYCFG */ +#define R367TER_RCFSPYCFG 0xf211 +#define F367TER_FECSPYRC_INPUT 0xf21100c0 +#define F367TER_RCRST_ON_ERROR 0xf2110020 +#define F367TER_RCONE_SHOT 0xf2110010 +#define F367TER_RCI2C_MODE 0xf211000c +#define F367TER_SPYRC_HSTERESIS 0xf2110003 + +/* RCFSPYDATA */ +#define R367TER_RCFSPYDATA 0xf212 +#define F367TER_SPYRC_STUFFING 0xf2120080 +#define F367TER_RCNOERR_PKTJITTER 0xf2120040 +#define F367TER_SPYRC_CNULLPKT 0xf2120020 +#define F367TER_SPYRC_OUTDATA_MODE 0xf212001f + +/* RCFSPYOUT */ +#define R367TER_RCFSPYOUT 0xf213 +#define F367TER_FSPYRC_DIRECT 0xf2130080 +#define F367TER_RCFSPYOUT_6 0xf2130040 +#define F367TER_SPYRC_OUTDATA_BUS 0xf2130038 +#define F367TER_RCSTUFF_MODE 0xf2130007 + +/* RCFSTATUS */ +#define R367TER_RCFSTATUS 0xf214 +#define F367TER_SPYRC_ENDSIM 0xf2140080 +#define F367TER_RCVALID_SIM 0xf2140040 +#define F367TER_RCFOUND_SIGNAL 0xf2140020 +#define F367TER_RCDSS_SYNCBYTE 0xf2140010 +#define F367TER_RCRESULT_STATE 0xf214000f + +/* RCFGOODPACK */ +#define R367TER_RCFGOODPACK 0xf215 +#define F367TER_RCGOOD_PACKET 0xf21500ff + +/* RCFPACKCNT */ +#define R367TER_RCFPACKCNT 0xf216 +#define F367TER_RCPACKET_COUNTER 0xf21600ff + +/* RCFSPYMISC */ +#define R367TER_RCFSPYMISC 0xf217 +#define F367TER_RCLABEL_COUNTER 0xf21700ff + +/* RCFBERCPT4 */ +#define R367TER_RCFBERCPT4 0xf218 +#define F367TER_FBERRCMETER_CPT_MMMMSB 0xf21800ff + +/* RCFBERCPT3 */ +#define R367TER_RCFBERCPT3 0xf219 +#define F367TER_FBERRCMETER_CPT_MMMSB 0xf21900ff + +/* RCFBERCPT2 */ +#define R367TER_RCFBERCPT2 0xf21a +#define F367TER_FBERRCMETER_CPT_MMSB 0xf21a00ff + +/* RCFBERCPT1 */ +#define R367TER_RCFBERCPT1 0xf21b +#define F367TER_FBERRCMETER_CPT_MSB 0xf21b00ff + +/* RCFBERCPT0 */ +#define R367TER_RCFBERCPT0 0xf21c +#define F367TER_FBERRCMETER_CPT_LSB 0xf21c00ff + +/* RCFBERERR2 */ +#define R367TER_RCFBERERR2 0xf21d +#define F367TER_FBERRCMETER_ERR_HI 0xf21d00ff + +/* RCFBERERR1 */ +#define R367TER_RCFBERERR1 0xf21e +#define F367TER_FBERRCMETER_ERR 0xf21e00ff + +/* RCFBERERR0 */ +#define R367TER_RCFBERERR0 0xf21f +#define F367TER_FBERRCMETER_ERR_LO 0xf21f00ff + +/* RCFSTATESM */ +#define R367TER_RCFSTATESM 0xf220 +#define F367TER_RCRSTATE_F 0xf2200080 +#define F367TER_RCRSTATE_E 0xf2200040 +#define F367TER_RCRSTATE_D 0xf2200020 +#define F367TER_RCRSTATE_C 0xf2200010 +#define F367TER_RCRSTATE_B 0xf2200008 +#define F367TER_RCRSTATE_A 0xf2200004 +#define F367TER_RCRSTATE_9 0xf2200002 +#define F367TER_RCRSTATE_8 0xf2200001 + +/* RCFSTATESL */ +#define R367TER_RCFSTATESL 0xf221 +#define F367TER_RCRSTATE_7 0xf2210080 +#define F367TER_RCRSTATE_6 0xf2210040 +#define F367TER_RCRSTATE_5 0xf2210020 +#define F367TER_RCRSTATE_4 0xf2210010 +#define F367TER_RCRSTATE_3 0xf2210008 +#define F367TER_RCRSTATE_2 0xf2210004 +#define F367TER_RCRSTATE_1 0xf2210002 +#define F367TER_RCRSTATE_0 0xf2210001 + +/* RCFSPYBER */ +#define R367TER_RCFSPYBER 0xf222 +#define F367TER_RCFSPYBER_7 0xf2220080 +#define F367TER_SPYRCOBS_XORREAD 0xf2220040 +#define F367TER_FSPYRCBER_OBSMODE 0xf2220020 +#define F367TER_FSPYRCBER_SYNCBYT 0xf2220010 +#define F367TER_FSPYRCBER_UNSYNC 0xf2220008 +#define F367TER_FSPYRCBER_CTIME 0xf2220007 + +/* RCFSPYDISTM */ +#define R367TER_RCFSPYDISTM 0xf223 +#define F367TER_RCPKTTIME_DISTANCE_HI 0xf22300ff + +/* RCFSPYDISTL */ +#define R367TER_RCFSPYDISTL 0xf224 +#define F367TER_RCPKTTIME_DISTANCE_LO 0xf22400ff + +/* RCFSPYOBS7 */ +#define R367TER_RCFSPYOBS7 0xf228 +#define F367TER_RCSPYOBS_SPYFAIL 0xf2280080 +#define F367TER_RCSPYOBS_SPYFAIL1 0xf2280040 +#define F367TER_RCSPYOBS_ERROR 0xf2280020 +#define F367TER_RCSPYOBS_STROUT 0xf2280010 +#define F367TER_RCSPYOBS_RESULTSTATE1 0xf228000f + +/* RCFSPYOBS6 */ +#define R367TER_RCFSPYOBS6 0xf229 +#define F367TER_RCSPYOBS_RESULTSTATe0 0xf22900f0 +#define F367TER_RCSPYOBS_RESULTSTATEM1 0xf229000f + +/* RCFSPYOBS5 */ +#define R367TER_RCFSPYOBS5 0xf22a +#define F367TER_RCSPYOBS_BYTEOFPACKET1 0xf22a00ff + +/* RCFSPYOBS4 */ +#define R367TER_RCFSPYOBS4 0xf22b +#define F367TER_RCSPYOBS_BYTEVALUE1 0xf22b00ff + +/* RCFSPYOBS3 */ +#define R367TER_RCFSPYOBS3 0xf22c +#define F367TER_RCSPYOBS_DATA1 0xf22c00ff + +/* RCFSPYOBS2 */ +#define R367TER_RCFSPYOBS2 0xf22d +#define F367TER_RCSPYOBS_DATa0 0xf22d00ff + +/* RCFSPYOBS1 */ +#define R367TER_RCFSPYOBS1 0xf22e +#define F367TER_RCSPYOBS_DATAM1 0xf22e00ff + +/* RCFSPYOBS0 */ +#define R367TER_RCFSPYOBS0 0xf22f +#define F367TER_RCSPYOBS_DATAM2 0xf22f00ff + +/* TSGENERAL */ +#define R367TER_TSGENERAL 0xf230 +#define F367TER_TSGENERAL_7 0xf2300080 +#define F367TER_TSGENERAL_6 0xf2300040 +#define F367TER_TSFIFO_BCLK1aLL 0xf2300020 +#define F367TER_TSGENERAL_4 0xf2300010 +#define F367TER_MUXSTREAM_OUTMODE 0xf2300008 +#define F367TER_TSFIFO_PERMPARAL 0xf2300006 +#define F367TER_RST_REEDSOLO 0xf2300001 + +/* RC1SPEED */ +#define R367TER_RC1SPEED 0xf231 +#define F367TER_TSRCFIFO1_OUTSPEED 0xf23100ff + +/* TSGSTATUS */ +#define R367TER_TSGSTATUS 0xf232 +#define F367TER_TSGSTATUS_7 0xf2320080 +#define F367TER_TSGSTATUS_6 0xf2320040 +#define F367TER_RSMEM_FULL 0xf2320020 +#define F367TER_RS_MULTCALC 0xf2320010 +#define F367TER_RSIN_OVERTIME 0xf2320008 +#define F367TER_TSFIFO3_DEMODSEL 0xf2320004 +#define F367TER_TSFIFO2_DEMODSEL 0xf2320002 +#define F367TER_TSFIFO1_DEMODSEL 0xf2320001 + + +/* FECM */ +#define R367TER_FECM 0xf233 +#define F367TER_DSS_DVB 0xf2330080 +#define F367TER_DEMOD_BYPASS 0xf2330040 +#define F367TER_CMP_SLOWMODE 0xf2330020 +#define F367TER_DSS_SRCH 0xf2330010 +#define F367TER_FECM_3 0xf2330008 +#define F367TER_DIFF_MODEVIT 0xf2330004 +#define F367TER_SYNCVIT 0xf2330002 +#define F367TER_I2CSYM 0xf2330001 + +/* VTH12 */ +#define R367TER_VTH12 0xf234 +#define F367TER_VTH_12 0xf23400ff + +/* VTH23 */ +#define R367TER_VTH23 0xf235 +#define F367TER_VTH_23 0xf23500ff + +/* VTH34 */ +#define R367TER_VTH34 0xf236 +#define F367TER_VTH_34 0xf23600ff + +/* VTH56 */ +#define R367TER_VTH56 0xf237 +#define F367TER_VTH_56 0xf23700ff + +/* VTH67 */ +#define R367TER_VTH67 0xf238 +#define F367TER_VTH_67 0xf23800ff + +/* VTH78 */ +#define R367TER_VTH78 0xf239 +#define F367TER_VTH_78 0xf23900ff + +/* VITCURPUN */ +#define R367TER_VITCURPUN 0xf23a +#define F367TER_VIT_MAPPING 0xf23a00e0 +#define F367TER_VIT_CURPUN 0xf23a001f + +/* VERROR */ +#define R367TER_VERROR 0xf23b +#define F367TER_REGERR_VIT 0xf23b00ff + +/* PRVIT */ +#define R367TER_PRVIT 0xf23c +#define F367TER_PRVIT_7 0xf23c0080 +#define F367TER_DIS_VTHLOCK 0xf23c0040 +#define F367TER_E7_8VIT 0xf23c0020 +#define F367TER_E6_7VIT 0xf23c0010 +#define F367TER_E5_6VIT 0xf23c0008 +#define F367TER_E3_4VIT 0xf23c0004 +#define F367TER_E2_3VIT 0xf23c0002 +#define F367TER_E1_2VIT 0xf23c0001 + +/* VAVSRVIT */ +#define R367TER_VAVSRVIT 0xf23d +#define F367TER_AMVIT 0xf23d0080 +#define F367TER_FROZENVIT 0xf23d0040 +#define F367TER_SNVIT 0xf23d0030 +#define F367TER_TOVVIT 0xf23d000c +#define F367TER_HYPVIT 0xf23d0003 + +/* VSTATUSVIT */ +#define R367TER_VSTATUSVIT 0xf23e +#define F367TER_VITERBI_ON 0xf23e0080 +#define F367TER_END_LOOPVIT 0xf23e0040 +#define F367TER_VITERBI_DEPRF 0xf23e0020 +#define F367TER_PRFVIT 0xf23e0010 +#define F367TER_LOCKEDVIT 0xf23e0008 +#define F367TER_VITERBI_DELOCK 0xf23e0004 +#define F367TER_VIT_DEMODSEL 0xf23e0002 +#define F367TER_VITERBI_COMPOUT 0xf23e0001 + +/* VTHINUSE */ +#define R367TER_VTHINUSE 0xf23f +#define F367TER_VIT_INUSE 0xf23f00ff + +/* KDIV12 */ +#define R367TER_KDIV12 0xf240 +#define F367TER_KDIV12_MANUAL 0xf2400080 +#define F367TER_K_DIVIDER_12 0xf240007f + +/* KDIV23 */ +#define R367TER_KDIV23 0xf241 +#define F367TER_KDIV23_MANUAL 0xf2410080 +#define F367TER_K_DIVIDER_23 0xf241007f + +/* KDIV34 */ +#define R367TER_KDIV34 0xf242 +#define F367TER_KDIV34_MANUAL 0xf2420080 +#define F367TER_K_DIVIDER_34 0xf242007f + +/* KDIV56 */ +#define R367TER_KDIV56 0xf243 +#define F367TER_KDIV56_MANUAL 0xf2430080 +#define F367TER_K_DIVIDER_56 0xf243007f + +/* KDIV67 */ +#define R367TER_KDIV67 0xf244 +#define F367TER_KDIV67_MANUAL 0xf2440080 +#define F367TER_K_DIVIDER_67 0xf244007f + +/* KDIV78 */ +#define R367TER_KDIV78 0xf245 +#define F367TER_KDIV78_MANUAL 0xf2450080 +#define F367TER_K_DIVIDER_78 0xf245007f + +/* SIGPOWER */ +#define R367TER_SIGPOWER 0xf246 +#define F367TER_SIGPOWER_MANUAL 0xf2460080 +#define F367TER_SIG_POWER 0xf246007f + +/* DEMAPVIT */ +#define R367TER_DEMAPVIT 0xf247 +#define F367TER_DEMAPVIT_7 0xf2470080 +#define F367TER_K_DIVIDER_VIT 0xf247007f + +/* VITSCALE */ +#define R367TER_VITSCALE 0xf248 +#define F367TER_NVTH_NOSRANGE 0xf2480080 +#define F367TER_VERROR_MAXMODE 0xf2480040 +#define F367TER_KDIV_MODE 0xf2480030 +#define F367TER_NSLOWSN_LOCKED 0xf2480008 +#define F367TER_DELOCK_PRFLOSS 0xf2480004 +#define F367TER_DIS_RSFLOCK 0xf2480002 +#define F367TER_VITSCALE_0 0xf2480001 + +/* FFEC1PRG */ +#define R367TER_FFEC1PRG 0xf249 +#define F367TER_FDSS_DVB 0xf2490080 +#define F367TER_FDSS_SRCH 0xf2490040 +#define F367TER_FFECPROG_5 0xf2490020 +#define F367TER_FFECPROG_4 0xf2490010 +#define F367TER_FFECPROG_3 0xf2490008 +#define F367TER_FFECPROG_2 0xf2490004 +#define F367TER_FTS1_DISABLE 0xf2490002 +#define F367TER_FTS2_DISABLE 0xf2490001 + +/* FVITCURPUN */ +#define R367TER_FVITCURPUN 0xf24a +#define F367TER_FVIT_MAPPING 0xf24a00e0 +#define F367TER_FVIT_CURPUN 0xf24a001f + +/* FVERROR */ +#define R367TER_FVERROR 0xf24b +#define F367TER_FREGERR_VIT 0xf24b00ff + +/* FVSTATUSVIT */ +#define R367TER_FVSTATUSVIT 0xf24c +#define F367TER_FVITERBI_ON 0xf24c0080 +#define F367TER_F1END_LOOPVIT 0xf24c0040 +#define F367TER_FVITERBI_DEPRF 0xf24c0020 +#define F367TER_FPRFVIT 0xf24c0010 +#define F367TER_FLOCKEDVIT 0xf24c0008 +#define F367TER_FVITERBI_DELOCK 0xf24c0004 +#define F367TER_FVIT_DEMODSEL 0xf24c0002 +#define F367TER_FVITERBI_COMPOUT 0xf24c0001 + +/* DEBUG_LT1 */ +#define R367TER_DEBUG_LT1 0xf24d +#define F367TER_DBG_LT1 0xf24d00ff + +/* DEBUG_LT2 */ +#define R367TER_DEBUG_LT2 0xf24e +#define F367TER_DBG_LT2 0xf24e00ff + +/* DEBUG_LT3 */ +#define R367TER_DEBUG_LT3 0xf24f +#define F367TER_DBG_LT3 0xf24f00ff + +/* TSTSFMET */ +#define R367TER_TSTSFMET 0xf250 +#define F367TER_TSTSFEC_METRIQUES 0xf25000ff + +/* SELOUT */ +#define R367TER_SELOUT 0xf252 +#define F367TER_EN_SYNC 0xf2520080 +#define F367TER_EN_TBUSDEMAP 0xf2520040 +#define F367TER_SELOUT_5 0xf2520020 +#define F367TER_SELOUT_4 0xf2520010 +#define F367TER_TSTSYNCHRO_MODE 0xf2520002 + +/* TSYNC */ +#define R367TER_TSYNC 0xf253 +#define F367TER_CURPUN_INCMODE 0xf2530080 +#define F367TER_CERR_TSTMODE 0xf2530040 +#define F367TER_SHIFTSOF_MODE 0xf2530030 +#define F367TER_SLOWPHA_MODE 0xf2530008 +#define F367TER_PXX_BYPALL 0xf2530004 +#define F367TER_FROTA45_FIRST 0xf2530002 +#define F367TER_TST_BCHERROR 0xf2530001 + +/* TSTERR */ +#define R367TER_TSTERR 0xf254 +#define F367TER_TST_LONGPKT 0xf2540080 +#define F367TER_TST_ISSYION 0xf2540040 +#define F367TER_TST_NPDON 0xf2540020 +#define F367TER_TSTERR_4 0xf2540010 +#define F367TER_TRACEBACK_MODE 0xf2540008 +#define F367TER_TST_RSPARITY 0xf2540004 +#define F367TER_METRIQUE_MODE 0xf2540003 + +/* TSFSYNC */ +#define R367TER_TSFSYNC 0xf255 +#define F367TER_EN_SFECSYNC 0xf2550080 +#define F367TER_EN_SFECDEMAP 0xf2550040 +#define F367TER_SFCERR_TSTMODE 0xf2550020 +#define F367TER_SFECPXX_BYPALL 0xf2550010 +#define F367TER_SFECTSTSYNCHRO_MODE 0xf255000f + +/* TSTSFERR */ +#define R367TER_TSTSFERR 0xf256 +#define F367TER_TSTSTERR_7 0xf2560080 +#define F367TER_TSTSTERR_6 0xf2560040 +#define F367TER_TSTSTERR_5 0xf2560020 +#define F367TER_TSTSTERR_4 0xf2560010 +#define F367TER_SFECTRACEBACK_MODE 0xf2560008 +#define F367TER_SFEC_NCONVPROG 0xf2560004 +#define F367TER_SFECMETRIQUE_MODE 0xf2560003 + +/* TSTTSSF1 */ +#define R367TER_TSTTSSF1 0xf258 +#define F367TER_TSTERSSF 0xf2580080 +#define F367TER_TSTTSSFEN 0xf2580040 +#define F367TER_SFEC_OUTMODE 0xf2580030 +#define F367TER_XLSF_NOFTHRESHOLD 0xf2580008 +#define F367TER_TSTTSSF_STACKSEL 0xf2580007 + +/* TSTTSSF2 */ +#define R367TER_TSTTSSF2 0xf259 +#define F367TER_DILSF_DBBHEADER 0xf2590080 +#define F367TER_TSTTSSF_DISBUG 0xf2590040 +#define F367TER_TSTTSSF_NOBADSTART 0xf2590020 +#define F367TER_TSTTSSF_SELECT 0xf259001f + +/* TSTTSSF3 */ +#define R367TER_TSTTSSF3 0xf25a +#define F367TER_TSTTSSF3_7 0xf25a0080 +#define F367TER_TSTTSSF3_6 0xf25a0040 +#define F367TER_TSTTSSF3_5 0xf25a0020 +#define F367TER_TSTTSSF3_4 0xf25a0010 +#define F367TER_TSTTSSF3_3 0xf25a0008 +#define F367TER_TSTTSSF3_2 0xf25a0004 +#define F367TER_TSTTSSF3_1 0xf25a0002 +#define F367TER_DISSF_CLKENABLE 0xf25a0001 + +/* TSTTS1 */ +#define R367TER_TSTTS1 0xf25c +#define F367TER_TSTERS 0xf25c0080 +#define F367TER_TSFIFO_DSSSYNCB 0xf25c0040 +#define F367TER_TSTTS_FSPYBEFRS 0xf25c0020 +#define F367TER_NFORCE_SYNCBYTE 0xf25c0010 +#define F367TER_XL_NOFTHRESHOLD 0xf25c0008 +#define F367TER_TSTTS_FRFORCEPKT 0xf25c0004 +#define F367TER_DESCR_NOTAUTO 0xf25c0002 +#define F367TER_TSTTSEN 0xf25c0001 + +/* TSTTS2 */ +#define R367TER_TSTTS2 0xf25d +#define F367TER_DIL_DBBHEADER 0xf25d0080 +#define F367TER_TSTTS_NOBADXXX 0xf25d0040 +#define F367TER_TSFIFO_DELSPEEDUP 0xf25d0020 +#define F367TER_TSTTS_SELECT 0xf25d001f + +/* TSTTS3 */ +#define R367TER_TSTTS3 0xf25e +#define F367TER_TSTTS_NOPKTGAIN 0xf25e0080 +#define F367TER_TSTTS_NOPKTENE 0xf25e0040 +#define F367TER_TSTTS_ISOLATION 0xf25e0020 +#define F367TER_TSTTS_DISBUG 0xf25e0010 +#define F367TER_TSTTS_NOBADSTART 0xf25e0008 +#define F367TER_TSTTS_STACKSEL 0xf25e0007 + +/* TSTTS4 */ +#define R367TER_TSTTS4 0xf25f +#define F367TER_TSTTS4_7 0xf25f0080 +#define F367TER_TSTTS4_6 0xf25f0040 +#define F367TER_TSTTS4_5 0xf25f0020 +#define F367TER_TSTTS_DISDSTATE 0xf25f0010 +#define F367TER_TSTTS_FASTNOSYNC 0xf25f0008 +#define F367TER_EXT_FECSPYIN 0xf25f0004 +#define F367TER_TSTTS_NODPZERO 0xf25f0002 +#define F367TER_TSTTS_NODIV3 0xf25f0001 + +/* TSTTSRC */ +#define R367TER_TSTTSRC 0xf26c +#define F367TER_TSTTSRC_7 0xf26c0080 +#define F367TER_TSRCFIFO_DSSSYNCB 0xf26c0040 +#define F367TER_TSRCFIFO_DPUNACTIVE 0xf26c0020 +#define F367TER_TSRCFIFO_DELSPEEDUP 0xf26c0010 +#define F367TER_TSTTSRC_NODIV3 0xf26c0008 +#define F367TER_TSTTSRC_FRFORCEPKT 0xf26c0004 +#define F367TER_SAT25_SDDORIGINE 0xf26c0002 +#define F367TER_TSTTSRC_INACTIVE 0xf26c0001 + +/* TSTTSRS */ +#define R367TER_TSTTSRS 0xf26d +#define F367TER_TSTTSRS_7 0xf26d0080 +#define F367TER_TSTTSRS_6 0xf26d0040 +#define F367TER_TSTTSRS_5 0xf26d0020 +#define F367TER_TSTTSRS_4 0xf26d0010 +#define F367TER_TSTTSRS_3 0xf26d0008 +#define F367TER_TSTTSRS_2 0xf26d0004 +#define F367TER_TSTRS_DISRS2 0xf26d0002 +#define F367TER_TSTRS_DISRS1 0xf26d0001 + +/* TSSTATEM */ +#define R367TER_TSSTATEM 0xf270 +#define F367TER_TSDIL_ON 0xf2700080 +#define F367TER_TSSKIPRS_ON 0xf2700040 +#define F367TER_TSRS_ON 0xf2700020 +#define F367TER_TSDESCRAMB_ON 0xf2700010 +#define F367TER_TSFRAME_MODE 0xf2700008 +#define F367TER_TS_DISABLE 0xf2700004 +#define F367TER_TSACM_MODE 0xf2700002 +#define F367TER_TSOUT_NOSYNC 0xf2700001 + +/* TSSTATEL */ +#define R367TER_TSSTATEL 0xf271 +#define F367TER_TSNOSYNCBYTE 0xf2710080 +#define F367TER_TSPARITY_ON 0xf2710040 +#define F367TER_TSSYNCOUTRS_ON 0xf2710020 +#define F367TER_TSDVBS2_MODE 0xf2710010 +#define F367TER_TSISSYI_ON 0xf2710008 +#define F367TER_TSNPD_ON 0xf2710004 +#define F367TER_TSCRC8_ON 0xf2710002 +#define F367TER_TSDSS_PACKET 0xf2710001 + +/* TSCFGH */ +#define R367TER_TSCFGH 0xf272 +#define F367TER_TSFIFO_DVBCI 0xf2720080 +#define F367TER_TSFIFO_SERIAL 0xf2720040 +#define F367TER_TSFIFO_TEIUPDATE 0xf2720020 +#define F367TER_TSFIFO_DUTY50 0xf2720010 +#define F367TER_TSFIFO_HSGNLOUT 0xf2720008 +#define F367TER_TSFIFO_ERRMODE 0xf2720006 +#define F367TER_RST_HWARE 0xf2720001 + +/* TSCFGM */ +#define R367TER_TSCFGM 0xf273 +#define F367TER_TSFIFO_MANSPEED 0xf27300c0 +#define F367TER_TSFIFO_PERMDATA 0xf2730020 +#define F367TER_TSFIFO_NONEWSGNL 0xf2730010 +#define F367TER_TSFIFO_BITSPEED 0xf2730008 +#define F367TER_NPD_SPECDVBS2 0xf2730004 +#define F367TER_TSFIFO_STOPCKDIS 0xf2730002 +#define F367TER_TSFIFO_INVDATA 0xf2730001 + +/* TSCFGL */ +#define R367TER_TSCFGL 0xf274 +#define F367TER_TSFIFO_BCLKDEL1cK 0xf27400c0 +#define F367TER_BCHERROR_MODE 0xf2740030 +#define F367TER_TSFIFO_NSGNL2dATA 0xf2740008 +#define F367TER_TSFIFO_EMBINDVB 0xf2740004 +#define F367TER_TSFIFO_DPUNACT 0xf2740002 +#define F367TER_TSFIFO_NPDOFF 0xf2740001 + +/* TSSYNC */ +#define R367TER_TSSYNC 0xf275 +#define F367TER_TSFIFO_PERMUTE 0xf2750080 +#define F367TER_TSFIFO_FISCR3B 0xf2750060 +#define F367TER_TSFIFO_SYNCMODE 0xf2750018 +#define F367TER_TSFIFO_SYNCSEL 0xf2750007 + +/* TSINSDELH */ +#define R367TER_TSINSDELH 0xf276 +#define F367TER_TSDEL_SYNCBYTE 0xf2760080 +#define F367TER_TSDEL_XXHEADER 0xf2760040 +#define F367TER_TSDEL_BBHEADER 0xf2760020 +#define F367TER_TSDEL_DATAFIELD 0xf2760010 +#define F367TER_TSINSDEL_ISCR 0xf2760008 +#define F367TER_TSINSDEL_NPD 0xf2760004 +#define F367TER_TSINSDEL_RSPARITY 0xf2760002 +#define F367TER_TSINSDEL_CRC8 0xf2760001 + +/* TSINSDELM */ +#define R367TER_TSINSDELM 0xf277 +#define F367TER_TSINS_BBPADDING 0xf2770080 +#define F367TER_TSINS_BCHFEC 0xf2770040 +#define F367TER_TSINS_LDPCFEC 0xf2770020 +#define F367TER_TSINS_EMODCOD 0xf2770010 +#define F367TER_TSINS_TOKEN 0xf2770008 +#define F367TER_TSINS_XXXERR 0xf2770004 +#define F367TER_TSINS_MATYPE 0xf2770002 +#define F367TER_TSINS_UPL 0xf2770001 + +/* TSINSDELL */ +#define R367TER_TSINSDELL 0xf278 +#define F367TER_TSINS_DFL 0xf2780080 +#define F367TER_TSINS_SYNCD 0xf2780040 +#define F367TER_TSINS_BLOCLEN 0xf2780020 +#define F367TER_TSINS_SIGPCOUNT 0xf2780010 +#define F367TER_TSINS_FIFO 0xf2780008 +#define F367TER_TSINS_REALPACK 0xf2780004 +#define F367TER_TSINS_TSCONFIG 0xf2780002 +#define F367TER_TSINS_LATENCY 0xf2780001 + +/* TSDIVN */ +#define R367TER_TSDIVN 0xf279 +#define F367TER_TSFIFO_LOWSPEED 0xf2790080 +#define F367TER_BYTE_OVERSAMPLING 0xf2790070 +#define F367TER_TSMANUAL_PACKETNBR 0xf279000f + +/* TSDIVPM */ +#define R367TER_TSDIVPM 0xf27a +#define F367TER_TSMANUAL_P_HI 0xf27a00ff + +/* TSDIVPL */ +#define R367TER_TSDIVPL 0xf27b +#define F367TER_TSMANUAL_P_LO 0xf27b00ff + +/* TSDIVQM */ +#define R367TER_TSDIVQM 0xf27c +#define F367TER_TSMANUAL_Q_HI 0xf27c00ff + +/* TSDIVQL */ +#define R367TER_TSDIVQL 0xf27d +#define F367TER_TSMANUAL_Q_LO 0xf27d00ff + +/* TSDILSTKM */ +#define R367TER_TSDILSTKM 0xf27e +#define F367TER_TSFIFO_DILSTK_HI 0xf27e00ff + +/* TSDILSTKL */ +#define R367TER_TSDILSTKL 0xf27f +#define F367TER_TSFIFO_DILSTK_LO 0xf27f00ff + +/* TSSPEED */ +#define R367TER_TSSPEED 0xf280 +#define F367TER_TSFIFO_OUTSPEED 0xf28000ff + +/* TSSTATUS */ +#define R367TER_TSSTATUS 0xf281 +#define F367TER_TSFIFO_LINEOK 0xf2810080 +#define F367TER_TSFIFO_ERROR 0xf2810040 +#define F367TER_TSFIFO_DATA7 0xf2810020 +#define F367TER_TSFIFO_NOSYNC 0xf2810010 +#define F367TER_ISCR_INITIALIZED 0xf2810008 +#define F367TER_ISCR_UPDATED 0xf2810004 +#define F367TER_SOFFIFO_UNREGUL 0xf2810002 +#define F367TER_DIL_READY 0xf2810001 + +/* TSSTATUS2 */ +#define R367TER_TSSTATUS2 0xf282 +#define F367TER_TSFIFO_DEMODSEL 0xf2820080 +#define F367TER_TSFIFOSPEED_STORE 0xf2820040 +#define F367TER_DILXX_RESET 0xf2820020 +#define F367TER_TSSERIAL_IMPOSSIBLE 0xf2820010 +#define F367TER_TSFIFO_UNDERSPEED 0xf2820008 +#define F367TER_BITSPEED_EVENT 0xf2820004 +#define F367TER_UL_SCRAMBDETECT 0xf2820002 +#define F367TER_ULDTV67_FALSELOCK 0xf2820001 + +/* TSBITRATEM */ +#define R367TER_TSBITRATEM 0xf283 +#define F367TER_TSFIFO_BITRATE_HI 0xf28300ff + +/* TSBITRATEL */ +#define R367TER_TSBITRATEL 0xf284 +#define F367TER_TSFIFO_BITRATE_LO 0xf28400ff + +/* TSPACKLENM */ +#define R367TER_TSPACKLENM 0xf285 +#define F367TER_TSFIFO_PACKCPT 0xf28500e0 +#define F367TER_DIL_RPLEN_HI 0xf285001f + +/* TSPACKLENL */ +#define R367TER_TSPACKLENL 0xf286 +#define F367TER_DIL_RPLEN_LO 0xf28600ff + +/* TSBLOCLENM */ +#define R367TER_TSBLOCLENM 0xf287 +#define F367TER_TSFIFO_PFLEN_HI 0xf28700ff + +/* TSBLOCLENL */ +#define R367TER_TSBLOCLENL 0xf288 +#define F367TER_TSFIFO_PFLEN_LO 0xf28800ff + +/* TSDLYH */ +#define R367TER_TSDLYH 0xf289 +#define F367TER_SOFFIFO_TSTIMEVALID 0xf2890080 +#define F367TER_SOFFIFO_SPEEDUP 0xf2890040 +#define F367TER_SOFFIFO_STOP 0xf2890020 +#define F367TER_SOFFIFO_REGULATED 0xf2890010 +#define F367TER_SOFFIFO_REALSBOFF_HI 0xf289000f + +/* TSDLYM */ +#define R367TER_TSDLYM 0xf28a +#define F367TER_SOFFIFO_REALSBOFF_MED 0xf28a00ff + +/* TSDLYL */ +#define R367TER_TSDLYL 0xf28b +#define F367TER_SOFFIFO_REALSBOFF_LO 0xf28b00ff + +/* TSNPDAV */ +#define R367TER_TSNPDAV 0xf28c +#define F367TER_TSNPD_AVERAGE 0xf28c00ff + +/* TSBUFSTATH */ +#define R367TER_TSBUFSTATH 0xf28d +#define F367TER_TSISCR_3BYTES 0xf28d0080 +#define F367TER_TSISCR_NEWDATA 0xf28d0040 +#define F367TER_TSISCR_BUFSTAT_HI 0xf28d003f + +/* TSBUFSTATM */ +#define R367TER_TSBUFSTATM 0xf28e +#define F367TER_TSISCR_BUFSTAT_MED 0xf28e00ff + +/* TSBUFSTATL */ +#define R367TER_TSBUFSTATL 0xf28f +#define F367TER_TSISCR_BUFSTAT_LO 0xf28f00ff + +/* TSDEBUGM */ +#define R367TER_TSDEBUGM 0xf290 +#define F367TER_TSFIFO_ILLPACKET 0xf2900080 +#define F367TER_DIL_NOSYNC 0xf2900040 +#define F367TER_DIL_ISCR 0xf2900020 +#define F367TER_DILOUT_BSYNCB 0xf2900010 +#define F367TER_TSFIFO_EMPTYPKT 0xf2900008 +#define F367TER_TSFIFO_EMPTYRD 0xf2900004 +#define F367TER_SOFFIFO_STOPM 0xf2900002 +#define F367TER_SOFFIFO_SPEEDUPM 0xf2900001 + +/* TSDEBUGL */ +#define R367TER_TSDEBUGL 0xf291 +#define F367TER_TSFIFO_PACKLENFAIL 0xf2910080 +#define F367TER_TSFIFO_SYNCBFAIL 0xf2910040 +#define F367TER_TSFIFO_VITLIBRE 0xf2910020 +#define F367TER_TSFIFO_BOOSTSPEEDM 0xf2910010 +#define F367TER_TSFIFO_UNDERSPEEDM 0xf2910008 +#define F367TER_TSFIFO_ERROR_EVNT 0xf2910004 +#define F367TER_TSFIFO_FULL 0xf2910002 +#define F367TER_TSFIFO_OVERFLOWM 0xf2910001 + +/* TSDLYSETH */ +#define R367TER_TSDLYSETH 0xf292 +#define F367TER_SOFFIFO_OFFSET 0xf29200e0 +#define F367TER_SOFFIFO_SYMBOFFSET_HI 0xf292001f + +/* TSDLYSETM */ +#define R367TER_TSDLYSETM 0xf293 +#define F367TER_SOFFIFO_SYMBOFFSET_MED 0xf29300ff + +/* TSDLYSETL */ +#define R367TER_TSDLYSETL 0xf294 +#define F367TER_SOFFIFO_SYMBOFFSET_LO 0xf29400ff + +/* TSOBSCFG */ +#define R367TER_TSOBSCFG 0xf295 +#define F367TER_TSFIFO_OBSCFG 0xf29500ff + +/* TSOBSM */ +#define R367TER_TSOBSM 0xf296 +#define F367TER_TSFIFO_OBSDATA_HI 0xf29600ff + +/* TSOBSL */ +#define R367TER_TSOBSL 0xf297 +#define F367TER_TSFIFO_OBSDATA_LO 0xf29700ff + +/* ERRCTRL1 */ +#define R367TER_ERRCTRL1 0xf298 +#define F367TER_ERR_SRC1 0xf29800f0 +#define F367TER_ERRCTRL1_3 0xf2980008 +#define F367TER_NUM_EVT1 0xf2980007 + +/* ERRCNT1H */ +#define R367TER_ERRCNT1H 0xf299 +#define F367TER_ERRCNT1_OLDVALUE 0xf2990080 +#define F367TER_ERR_CNT1 0xf299007f + +/* ERRCNT1M */ +#define R367TER_ERRCNT1M 0xf29a +#define F367TER_ERR_CNT1_HI 0xf29a00ff + +/* ERRCNT1L */ +#define R367TER_ERRCNT1L 0xf29b +#define F367TER_ERR_CNT1_LO 0xf29b00ff + +/* ERRCTRL2 */ +#define R367TER_ERRCTRL2 0xf29c +#define F367TER_ERR_SRC2 0xf29c00f0 +#define F367TER_ERRCTRL2_3 0xf29c0008 +#define F367TER_NUM_EVT2 0xf29c0007 + +/* ERRCNT2H */ +#define R367TER_ERRCNT2H 0xf29d +#define F367TER_ERRCNT2_OLDVALUE 0xf29d0080 +#define F367TER_ERR_CNT2_HI 0xf29d007f + +/* ERRCNT2M */ +#define R367TER_ERRCNT2M 0xf29e +#define F367TER_ERR_CNT2_MED 0xf29e00ff + +/* ERRCNT2L */ +#define R367TER_ERRCNT2L 0xf29f +#define F367TER_ERR_CNT2_LO 0xf29f00ff + +/* FECSPY */ +#define R367TER_FECSPY 0xf2a0 +#define F367TER_SPY_ENABLE 0xf2a00080 +#define F367TER_NO_SYNCBYTE 0xf2a00040 +#define F367TER_SERIAL_MODE 0xf2a00020 +#define F367TER_UNUSUAL_PACKET 0xf2a00010 +#define F367TER_BERMETER_DATAMODE 0xf2a0000c +#define F367TER_BERMETER_LMODE 0xf2a00002 +#define F367TER_BERMETER_RESET 0xf2a00001 + +/* FSPYCFG */ +#define R367TER_FSPYCFG 0xf2a1 +#define F367TER_FECSPY_INPUT 0xf2a100c0 +#define F367TER_RST_ON_ERROR 0xf2a10020 +#define F367TER_ONE_SHOT 0xf2a10010 +#define F367TER_I2C_MOD 0xf2a1000c +#define F367TER_SPY_HYSTERESIS 0xf2a10003 + +/* FSPYDATA */ +#define R367TER_FSPYDATA 0xf2a2 +#define F367TER_SPY_STUFFING 0xf2a20080 +#define F367TER_NOERROR_PKTJITTER 0xf2a20040 +#define F367TER_SPY_CNULLPKT 0xf2a20020 +#define F367TER_SPY_OUTDATA_MODE 0xf2a2001f + +/* FSPYOUT */ +#define R367TER_FSPYOUT 0xf2a3 +#define F367TER_FSPY_DIRECT 0xf2a30080 +#define F367TER_FSPYOUT_6 0xf2a30040 +#define F367TER_SPY_OUTDATA_BUS 0xf2a30038 +#define F367TER_STUFF_MODE 0xf2a30007 + +/* FSTATUS */ +#define R367TER_FSTATUS 0xf2a4 +#define F367TER_SPY_ENDSIM 0xf2a40080 +#define F367TER_VALID_SIM 0xf2a40040 +#define F367TER_FOUND_SIGNAL 0xf2a40020 +#define F367TER_DSS_SYNCBYTE 0xf2a40010 +#define F367TER_RESULT_STATE 0xf2a4000f + +/* FGOODPACK */ +#define R367TER_FGOODPACK 0xf2a5 +#define F367TER_FGOOD_PACKET 0xf2a500ff + +/* FPACKCNT */ +#define R367TER_FPACKCNT 0xf2a6 +#define F367TER_FPACKET_COUNTER 0xf2a600ff + +/* FSPYMISC */ +#define R367TER_FSPYMISC 0xf2a7 +#define F367TER_FLABEL_COUNTER 0xf2a700ff + +/* FBERCPT4 */ +#define R367TER_FBERCPT4 0xf2a8 +#define F367TER_FBERMETER_CPT5 0xf2a800ff + +/* FBERCPT3 */ +#define R367TER_FBERCPT3 0xf2a9 +#define F367TER_FBERMETER_CPT4 0xf2a900ff + +/* FBERCPT2 */ +#define R367TER_FBERCPT2 0xf2aa +#define F367TER_FBERMETER_CPT3 0xf2aa00ff + +/* FBERCPT1 */ +#define R367TER_FBERCPT1 0xf2ab +#define F367TER_FBERMETER_CPT2 0xf2ab00ff + +/* FBERCPT0 */ +#define R367TER_FBERCPT0 0xf2ac +#define F367TER_FBERMETER_CPT1 0xf2ac00ff + +/* FBERERR2 */ +#define R367TER_FBERERR2 0xf2ad +#define F367TER_FBERMETER_ERR_HI 0xf2ad00ff + +/* FBERERR1 */ +#define R367TER_FBERERR1 0xf2ae +#define F367TER_FBERMETER_ERR_MED 0xf2ae00ff + +/* FBERERR0 */ +#define R367TER_FBERERR0 0xf2af +#define F367TER_FBERMETER_ERR_LO 0xf2af00ff + +/* FSTATESM */ +#define R367TER_FSTATESM 0xf2b0 +#define F367TER_RSTATE_F 0xf2b00080 +#define F367TER_RSTATE_E 0xf2b00040 +#define F367TER_RSTATE_D 0xf2b00020 +#define F367TER_RSTATE_C 0xf2b00010 +#define F367TER_RSTATE_B 0xf2b00008 +#define F367TER_RSTATE_A 0xf2b00004 +#define F367TER_RSTATE_9 0xf2b00002 +#define F367TER_RSTATE_8 0xf2b00001 + +/* FSTATESL */ +#define R367TER_FSTATESL 0xf2b1 +#define F367TER_RSTATE_7 0xf2b10080 +#define F367TER_RSTATE_6 0xf2b10040 +#define F367TER_RSTATE_5 0xf2b10020 +#define F367TER_RSTATE_4 0xf2b10010 +#define F367TER_RSTATE_3 0xf2b10008 +#define F367TER_RSTATE_2 0xf2b10004 +#define F367TER_RSTATE_1 0xf2b10002 +#define F367TER_RSTATE_0 0xf2b10001 + +/* FSPYBER */ +#define R367TER_FSPYBER 0xf2b2 +#define F367TER_FSPYBER_7 0xf2b20080 +#define F367TER_FSPYOBS_XORREAD 0xf2b20040 +#define F367TER_FSPYBER_OBSMODE 0xf2b20020 +#define F367TER_FSPYBER_SYNCBYTE 0xf2b20010 +#define F367TER_FSPYBER_UNSYNC 0xf2b20008 +#define F367TER_FSPYBER_CTIME 0xf2b20007 + +/* FSPYDISTM */ +#define R367TER_FSPYDISTM 0xf2b3 +#define F367TER_PKTTIME_DISTANCE_HI 0xf2b300ff + +/* FSPYDISTL */ +#define R367TER_FSPYDISTL 0xf2b4 +#define F367TER_PKTTIME_DISTANCE_LO 0xf2b400ff + +/* FSPYOBS7 */ +#define R367TER_FSPYOBS7 0xf2b8 +#define F367TER_FSPYOBS_SPYFAIL 0xf2b80080 +#define F367TER_FSPYOBS_SPYFAIL1 0xf2b80040 +#define F367TER_FSPYOBS_ERROR 0xf2b80020 +#define F367TER_FSPYOBS_STROUT 0xf2b80010 +#define F367TER_FSPYOBS_RESULTSTATE1 0xf2b8000f + +/* FSPYOBS6 */ +#define R367TER_FSPYOBS6 0xf2b9 +#define F367TER_FSPYOBS_RESULTSTATe0 0xf2b900f0 +#define F367TER_FSPYOBS_RESULTSTATEM1 0xf2b9000f + +/* FSPYOBS5 */ +#define R367TER_FSPYOBS5 0xf2ba +#define F367TER_FSPYOBS_BYTEOFPACKET1 0xf2ba00ff + +/* FSPYOBS4 */ +#define R367TER_FSPYOBS4 0xf2bb +#define F367TER_FSPYOBS_BYTEVALUE1 0xf2bb00ff + +/* FSPYOBS3 */ +#define R367TER_FSPYOBS3 0xf2bc +#define F367TER_FSPYOBS_DATA1 0xf2bc00ff + +/* FSPYOBS2 */ +#define R367TER_FSPYOBS2 0xf2bd +#define F367TER_FSPYOBS_DATa0 0xf2bd00ff + +/* FSPYOBS1 */ +#define R367TER_FSPYOBS1 0xf2be +#define F367TER_FSPYOBS_DATAM1 0xf2be00ff + +/* FSPYOBS0 */ +#define R367TER_FSPYOBS0 0xf2bf +#define F367TER_FSPYOBS_DATAM2 0xf2bf00ff + +/* SFDEMAP */ +#define R367TER_SFDEMAP 0xf2c0 +#define F367TER_SFDEMAP_7 0xf2c00080 +#define F367TER_SFEC_K_DIVIDER_VIT 0xf2c0007f + +/* SFERROR */ +#define R367TER_SFERROR 0xf2c1 +#define F367TER_SFEC_REGERR_VIT 0xf2c100ff + +/* SFAVSR */ +#define R367TER_SFAVSR 0xf2c2 +#define F367TER_SFEC_SUMERRORS 0xf2c20080 +#define F367TER_SERROR_MAXMODE 0xf2c20040 +#define F367TER_SN_SFEC 0xf2c20030 +#define F367TER_KDIV_MODE_SFEC 0xf2c2000c +#define F367TER_SFAVSR_1 0xf2c20002 +#define F367TER_SFAVSR_0 0xf2c20001 + +/* SFECSTATUS */ +#define R367TER_SFECSTATUS 0xf2c3 +#define F367TER_SFEC_ON 0xf2c30080 +#define F367TER_SFSTATUS_6 0xf2c30040 +#define F367TER_SFSTATUS_5 0xf2c30020 +#define F367TER_SFSTATUS_4 0xf2c30010 +#define F367TER_LOCKEDSFEC 0xf2c30008 +#define F367TER_SFEC_DELOCK 0xf2c30004 +#define F367TER_SFEC_DEMODSEL1 0xf2c30002 +#define F367TER_SFEC_OVFON 0xf2c30001 + +/* SFKDIV12 */ +#define R367TER_SFKDIV12 0xf2c4 +#define F367TER_SFECKDIV12_MAN 0xf2c40080 +#define F367TER_SFEC_K_DIVIDER_12 0xf2c4007f + +/* SFKDIV23 */ +#define R367TER_SFKDIV23 0xf2c5 +#define F367TER_SFECKDIV23_MAN 0xf2c50080 +#define F367TER_SFEC_K_DIVIDER_23 0xf2c5007f + +/* SFKDIV34 */ +#define R367TER_SFKDIV34 0xf2c6 +#define F367TER_SFECKDIV34_MAN 0xf2c60080 +#define F367TER_SFEC_K_DIVIDER_34 0xf2c6007f + +/* SFKDIV56 */ +#define R367TER_SFKDIV56 0xf2c7 +#define F367TER_SFECKDIV56_MAN 0xf2c70080 +#define F367TER_SFEC_K_DIVIDER_56 0xf2c7007f + +/* SFKDIV67 */ +#define R367TER_SFKDIV67 0xf2c8 +#define F367TER_SFECKDIV67_MAN 0xf2c80080 +#define F367TER_SFEC_K_DIVIDER_67 0xf2c8007f + +/* SFKDIV78 */ +#define R367TER_SFKDIV78 0xf2c9 +#define F367TER_SFECKDIV78_MAN 0xf2c90080 +#define F367TER_SFEC_K_DIVIDER_78 0xf2c9007f + +/* SFDILSTKM */ +#define R367TER_SFDILSTKM 0xf2ca +#define F367TER_SFEC_PACKCPT 0xf2ca00e0 +#define F367TER_SFEC_DILSTK_HI 0xf2ca001f + +/* SFDILSTKL */ +#define R367TER_SFDILSTKL 0xf2cb +#define F367TER_SFEC_DILSTK_LO 0xf2cb00ff + +/* SFSTATUS */ +#define R367TER_SFSTATUS 0xf2cc +#define F367TER_SFEC_LINEOK 0xf2cc0080 +#define F367TER_SFEC_ERROR 0xf2cc0040 +#define F367TER_SFEC_DATA7 0xf2cc0020 +#define F367TER_SFEC_OVERFLOW 0xf2cc0010 +#define F367TER_SFEC_DEMODSEL2 0xf2cc0008 +#define F367TER_SFEC_NOSYNC 0xf2cc0004 +#define F367TER_SFEC_UNREGULA 0xf2cc0002 +#define F367TER_SFEC_READY 0xf2cc0001 + +/* SFDLYH */ +#define R367TER_SFDLYH 0xf2cd +#define F367TER_SFEC_TSTIMEVALID 0xf2cd0080 +#define F367TER_SFEC_SPEEDUP 0xf2cd0040 +#define F367TER_SFEC_STOP 0xf2cd0020 +#define F367TER_SFEC_REGULATED 0xf2cd0010 +#define F367TER_SFEC_REALSYMBOFFSET 0xf2cd000f + +/* SFDLYM */ +#define R367TER_SFDLYM 0xf2ce +#define F367TER_SFEC_REALSYMBOFFSET_HI 0xf2ce00ff + +/* SFDLYL */ +#define R367TER_SFDLYL 0xf2cf +#define F367TER_SFEC_REALSYMBOFFSET_LO 0xf2cf00ff + +/* SFDLYSETH */ +#define R367TER_SFDLYSETH 0xf2d0 +#define F367TER_SFEC_OFFSET 0xf2d000e0 +#define F367TER_SFECDLYSETH_4 0xf2d00010 +#define F367TER_RST_SFEC 0xf2d00008 +#define F367TER_SFECDLYSETH_2 0xf2d00004 +#define F367TER_SFEC_DISABLE 0xf2d00002 +#define F367TER_SFEC_UNREGUL 0xf2d00001 + +/* SFDLYSETM */ +#define R367TER_SFDLYSETM 0xf2d1 +#define F367TER_SFECDLYSETM_7 0xf2d10080 +#define F367TER_SFEC_SYMBOFFSET_HI 0xf2d1007f + +/* SFDLYSETL */ +#define R367TER_SFDLYSETL 0xf2d2 +#define F367TER_SFEC_SYMBOFFSET_LO 0xf2d200ff + +/* SFOBSCFG */ +#define R367TER_SFOBSCFG 0xf2d3 +#define F367TER_SFEC_OBSCFG 0xf2d300ff + +/* SFOBSM */ +#define R367TER_SFOBSM 0xf2d4 +#define F367TER_SFEC_OBSDATA_HI 0xf2d400ff + +/* SFOBSL */ +#define R367TER_SFOBSL 0xf2d5 +#define F367TER_SFEC_OBSDATA_LO 0xf2d500ff + +/* SFECINFO */ +#define R367TER_SFECINFO 0xf2d6 +#define F367TER_SFECINFO_7 0xf2d60080 +#define F367TER_SFEC_SYNCDLSB 0xf2d60070 +#define F367TER_SFCE_S1cPHASE 0xf2d6000f + +/* SFERRCTRL */ +#define R367TER_SFERRCTRL 0xf2d8 +#define F367TER_SFEC_ERR_SOURCE 0xf2d800f0 +#define F367TER_SFERRCTRL_3 0xf2d80008 +#define F367TER_SFEC_NUM_EVENT 0xf2d80007 + +/* SFERRCNTH */ +#define R367TER_SFERRCNTH 0xf2d9 +#define F367TER_SFERRC_OLDVALUE 0xf2d90080 +#define F367TER_SFEC_ERR_CNT 0xf2d9007f + +/* SFERRCNTM */ +#define R367TER_SFERRCNTM 0xf2da +#define F367TER_SFEC_ERR_CNT_HI 0xf2da00ff + +/* SFERRCNTL */ +#define R367TER_SFERRCNTL 0xf2db +#define F367TER_SFEC_ERR_CNT_LO 0xf2db00ff + +/* SYMBRATEM */ +#define R367TER_SYMBRATEM 0xf2e0 +#define F367TER_DEFGEN_SYMBRATE_HI 0xf2e000ff + +/* SYMBRATEL */ +#define R367TER_SYMBRATEL 0xf2e1 +#define F367TER_DEFGEN_SYMBRATE_LO 0xf2e100ff + +/* SYMBSTATUS */ +#define R367TER_SYMBSTATUS 0xf2e2 +#define F367TER_SYMBDLINE2_OFF 0xf2e20080 +#define F367TER_SDDL_REINIT1 0xf2e20040 +#define F367TER_SDD_REINIT1 0xf2e20020 +#define F367TER_TOKENID_ERROR 0xf2e20010 +#define F367TER_SYMBRATE_OVERFLOW 0xf2e20008 +#define F367TER_SYMBRATE_UNDERFLOW 0xf2e20004 +#define F367TER_TOKENID_RSTEVENT 0xf2e20002 +#define F367TER_TOKENID_RESET1 0xf2e20001 + +/* SYMBCFG */ +#define R367TER_SYMBCFG 0xf2e3 +#define F367TER_SYMBCFG_7 0xf2e30080 +#define F367TER_SYMBCFG_6 0xf2e30040 +#define F367TER_SYMBCFG_5 0xf2e30020 +#define F367TER_SYMBCFG_4 0xf2e30010 +#define F367TER_SYMRATE_FSPEED 0xf2e3000c +#define F367TER_SYMRATE_SSPEED 0xf2e30003 + +/* SYMBFIFOM */ +#define R367TER_SYMBFIFOM 0xf2e4 +#define F367TER_SYMBFIFOM_7 0xf2e40080 +#define F367TER_SYMBFIFOM_6 0xf2e40040 +#define F367TER_DEFGEN_SYMFIFO_HI 0xf2e4003f + +/* SYMBFIFOL */ +#define R367TER_SYMBFIFOL 0xf2e5 +#define F367TER_DEFGEN_SYMFIFO_LO 0xf2e500ff + +/* SYMBOFFSM */ +#define R367TER_SYMBOFFSM 0xf2e6 +#define F367TER_TOKENID_RESET2 0xf2e60080 +#define F367TER_SDDL_REINIT2 0xf2e60040 +#define F367TER_SDD_REINIT2 0xf2e60020 +#define F367TER_SYMBOFFSM_4 0xf2e60010 +#define F367TER_SYMBOFFSM_3 0xf2e60008 +#define F367TER_DEFGEN_SYMBOFFSET_HI 0xf2e60007 + +/* SYMBOFFSL */ +#define R367TER_SYMBOFFSL 0xf2e7 +#define F367TER_DEFGEN_SYMBOFFSET_LO 0xf2e700ff + +/* DEBUG_LT4 */ +#define R367TER_DEBUG_LT4 0xf400 +#define F367TER_F_DEBUG_LT4 0xf40000ff + +/* DEBUG_LT5 */ +#define R367TER_DEBUG_LT5 0xf401 +#define F367TER_F_DEBUG_LT5 0xf40100ff + +/* DEBUG_LT6 */ +#define R367TER_DEBUG_LT6 0xf402 +#define F367TER_F_DEBUG_LT6 0xf40200ff + +/* DEBUG_LT7 */ +#define R367TER_DEBUG_LT7 0xf403 +#define F367TER_F_DEBUG_LT7 0xf40300ff + +/* DEBUG_LT8 */ +#define R367TER_DEBUG_LT8 0xf404 +#define F367TER_F_DEBUG_LT8 0xf40400ff + +/* DEBUG_LT9 */ +#define R367TER_DEBUG_LT9 0xf405 +#define F367TER_F_DEBUG_LT9 0xf40500ff + +#define STV0367TER_NBREGS 445 + +/* ID */ +#define R367CAB_ID 0xf000 +#define F367CAB_IDENTIFICATIONREGISTER 0xf00000ff + +/* I2CRPT */ +#define R367CAB_I2CRPT 0xf001 +#define F367CAB_I2CT_ON 0xf0010080 +#define F367CAB_ENARPT_LEVEL 0xf0010070 +#define F367CAB_SCLT_DELAY 0xf0010008 +#define F367CAB_SCLT_NOD 0xf0010004 +#define F367CAB_STOP_ENABLE 0xf0010002 +#define F367CAB_SDAT_NOD 0xf0010001 + +/* TOPCTRL */ +#define R367CAB_TOPCTRL 0xf002 +#define F367CAB_STDBY 0xf0020080 +#define F367CAB_STDBY_CORE 0xf0020020 +#define F367CAB_QAM_COFDM 0xf0020010 +#define F367CAB_TS_DIS 0xf0020008 +#define F367CAB_DIR_CLK_216 0xf0020004 + +/* IOCFG0 */ +#define R367CAB_IOCFG0 0xf003 +#define F367CAB_OP0_SD 0xf0030080 +#define F367CAB_OP0_VAL 0xf0030040 +#define F367CAB_OP0_OD 0xf0030020 +#define F367CAB_OP0_INV 0xf0030010 +#define F367CAB_OP0_DACVALUE_HI 0xf003000f + +/* DAc0R */ +#define R367CAB_DAC0R 0xf004 +#define F367CAB_OP0_DACVALUE_LO 0xf00400ff + +/* IOCFG1 */ +#define R367CAB_IOCFG1 0xf005 +#define F367CAB_IP0 0xf0050040 +#define F367CAB_OP1_OD 0xf0050020 +#define F367CAB_OP1_INV 0xf0050010 +#define F367CAB_OP1_DACVALUE_HI 0xf005000f + +/* DAC1R */ +#define R367CAB_DAC1R 0xf006 +#define F367CAB_OP1_DACVALUE_LO 0xf00600ff + +/* IOCFG2 */ +#define R367CAB_IOCFG2 0xf007 +#define F367CAB_OP2_LOCK_CONF 0xf00700e0 +#define F367CAB_OP2_OD 0xf0070010 +#define F367CAB_OP2_VAL 0xf0070008 +#define F367CAB_OP1_LOCK_CONF 0xf0070007 + +/* SDFR */ +#define R367CAB_SDFR 0xf008 +#define F367CAB_OP0_FREQ 0xf00800f0 +#define F367CAB_OP1_FREQ 0xf008000f + +/* AUX_CLK */ +#define R367CAB_AUX_CLK 0xf00a +#define F367CAB_AUXFEC_CTL 0xf00a00c0 +#define F367CAB_DIS_CKX4 0xf00a0020 +#define F367CAB_CKSEL 0xf00a0018 +#define F367CAB_CKDIV_PROG 0xf00a0006 +#define F367CAB_AUXCLK_ENA 0xf00a0001 + +/* FREESYS1 */ +#define R367CAB_FREESYS1 0xf00b +#define F367CAB_FREESYS_1 0xf00b00ff + +/* FREESYS2 */ +#define R367CAB_FREESYS2 0xf00c +#define F367CAB_FREESYS_2 0xf00c00ff + +/* FREESYS3 */ +#define R367CAB_FREESYS3 0xf00d +#define F367CAB_FREESYS_3 0xf00d00ff + +/* GPIO_CFG */ +#define R367CAB_GPIO_CFG 0xf00e +#define F367CAB_GPIO7_OD 0xf00e0080 +#define F367CAB_GPIO7_CFG 0xf00e0040 +#define F367CAB_GPIO6_OD 0xf00e0020 +#define F367CAB_GPIO6_CFG 0xf00e0010 +#define F367CAB_GPIO5_OD 0xf00e0008 +#define F367CAB_GPIO5_CFG 0xf00e0004 +#define F367CAB_GPIO4_OD 0xf00e0002 +#define F367CAB_GPIO4_CFG 0xf00e0001 + +/* GPIO_CMD */ +#define R367CAB_GPIO_CMD 0xf00f +#define F367CAB_GPIO7_VAL 0xf00f0008 +#define F367CAB_GPIO6_VAL 0xf00f0004 +#define F367CAB_GPIO5_VAL 0xf00f0002 +#define F367CAB_GPIO4_VAL 0xf00f0001 + +/* TSTRES */ +#define R367CAB_TSTRES 0xf0c0 +#define F367CAB_FRES_DISPLAY 0xf0c00080 +#define F367CAB_FRES_FIFO_AD 0xf0c00020 +#define F367CAB_FRESRS 0xf0c00010 +#define F367CAB_FRESACS 0xf0c00008 +#define F367CAB_FRESFEC 0xf0c00004 +#define F367CAB_FRES_PRIF 0xf0c00002 +#define F367CAB_FRESCORE 0xf0c00001 + +/* ANACTRL */ +#define R367CAB_ANACTRL 0xf0c1 +#define F367CAB_BYPASS_XTAL 0xf0c10040 +#define F367CAB_BYPASS_PLLXN 0xf0c1000c +#define F367CAB_DIS_PAD_OSC 0xf0c10002 +#define F367CAB_STDBY_PLLXN 0xf0c10001 + +/* TSTBUS */ +#define R367CAB_TSTBUS 0xf0c2 +#define F367CAB_TS_BYTE_CLK_INV 0xf0c20080 +#define F367CAB_CFG_IP 0xf0c20070 +#define F367CAB_CFG_TST 0xf0c2000f + +/* RF_AGC1 */ +#define R367CAB_RF_AGC1 0xf0d4 +#define F367CAB_RF_AGC1_LEVEL_HI 0xf0d400ff + +/* RF_AGC2 */ +#define R367CAB_RF_AGC2 0xf0d5 +#define F367CAB_REF_ADGP 0xf0d50080 +#define F367CAB_STDBY_ADCGP 0xf0d50020 +#define F367CAB_RF_AGC1_LEVEL_LO 0xf0d50003 + +/* ANADIGCTRL */ +#define R367CAB_ANADIGCTRL 0xf0d7 +#define F367CAB_SEL_CLKDEM 0xf0d70020 +#define F367CAB_EN_BUFFER_Q 0xf0d70010 +#define F367CAB_EN_BUFFER_I 0xf0d70008 +#define F367CAB_ADC_RIS_EGDE 0xf0d70004 +#define F367CAB_SGN_ADC 0xf0d70002 +#define F367CAB_SEL_AD12_SYNC 0xf0d70001 + +/* PLLMDIV */ +#define R367CAB_PLLMDIV 0xf0d8 +#define F367CAB_PLL_MDIV 0xf0d800ff + +/* PLLNDIV */ +#define R367CAB_PLLNDIV 0xf0d9 +#define F367CAB_PLL_NDIV 0xf0d900ff + +/* PLLSETUP */ +#define R367CAB_PLLSETUP 0xf0da +#define F367CAB_PLL_PDIV 0xf0da0070 +#define F367CAB_PLL_KDIV 0xf0da000f + +/* DUAL_AD12 */ +#define R367CAB_DUAL_AD12 0xf0db +#define F367CAB_FS20M 0xf0db0020 +#define F367CAB_FS50M 0xf0db0010 +#define F367CAB_INMODe0 0xf0db0008 +#define F367CAB_POFFQ 0xf0db0004 +#define F367CAB_POFFI 0xf0db0002 +#define F367CAB_INMODE1 0xf0db0001 + +/* TSTBIST */ +#define R367CAB_TSTBIST 0xf0dc +#define F367CAB_TST_BYP_CLK 0xf0dc0080 +#define F367CAB_TST_GCLKENA_STD 0xf0dc0040 +#define F367CAB_TST_GCLKENA 0xf0dc0020 +#define F367CAB_TST_MEMBIST 0xf0dc001f + +/* CTRL_1 */ +#define R367CAB_CTRL_1 0xf402 +#define F367CAB_SOFT_RST 0xf4020080 +#define F367CAB_EQU_RST 0xf4020008 +#define F367CAB_CRL_RST 0xf4020004 +#define F367CAB_TRL_RST 0xf4020002 +#define F367CAB_AGC_RST 0xf4020001 + +/* CTRL_2 */ +#define R367CAB_CTRL_2 0xf403 +#define F367CAB_DEINT_RST 0xf4030008 +#define F367CAB_RS_RST 0xf4030004 + +/* IT_STATUS1 */ +#define R367CAB_IT_STATUS1 0xf408 +#define F367CAB_SWEEP_OUT 0xf4080080 +#define F367CAB_FSM_CRL 0xf4080040 +#define F367CAB_CRL_LOCK 0xf4080020 +#define F367CAB_MFSM 0xf4080010 +#define F367CAB_TRL_LOCK 0xf4080008 +#define F367CAB_TRL_AGC_LIMIT 0xf4080004 +#define F367CAB_ADJ_AGC_LOCK 0xf4080002 +#define F367CAB_AGC_QAM_LOCK 0xf4080001 + +/* IT_STATUS2 */ +#define R367CAB_IT_STATUS2 0xf409 +#define F367CAB_TSMF_CNT 0xf4090080 +#define F367CAB_TSMF_EOF 0xf4090040 +#define F367CAB_TSMF_RDY 0xf4090020 +#define F367CAB_FEC_NOCORR 0xf4090010 +#define F367CAB_SYNCSTATE 0xf4090008 +#define F367CAB_DEINT_LOCK 0xf4090004 +#define F367CAB_FADDING_FRZ 0xf4090002 +#define F367CAB_TAPMON_ALARM 0xf4090001 + +/* IT_EN1 */ +#define R367CAB_IT_EN1 0xf40a +#define F367CAB_SWEEP_OUTE 0xf40a0080 +#define F367CAB_FSM_CRLE 0xf40a0040 +#define F367CAB_CRL_LOCKE 0xf40a0020 +#define F367CAB_MFSME 0xf40a0010 +#define F367CAB_TRL_LOCKE 0xf40a0008 +#define F367CAB_TRL_AGC_LIMITE 0xf40a0004 +#define F367CAB_ADJ_AGC_LOCKE 0xf40a0002 +#define F367CAB_AGC_LOCKE 0xf40a0001 + +/* IT_EN2 */ +#define R367CAB_IT_EN2 0xf40b +#define F367CAB_TSMF_CNTE 0xf40b0080 +#define F367CAB_TSMF_EOFE 0xf40b0040 +#define F367CAB_TSMF_RDYE 0xf40b0020 +#define F367CAB_FEC_NOCORRE 0xf40b0010 +#define F367CAB_SYNCSTATEE 0xf40b0008 +#define F367CAB_DEINT_LOCKE 0xf40b0004 +#define F367CAB_FADDING_FRZE 0xf40b0002 +#define F367CAB_TAPMON_ALARME 0xf40b0001 + +/* CTRL_STATUS */ +#define R367CAB_CTRL_STATUS 0xf40c +#define F367CAB_QAMFEC_LOCK 0xf40c0004 +#define F367CAB_TSMF_LOCK 0xf40c0002 +#define F367CAB_TSMF_ERROR 0xf40c0001 + +/* TEST_CTL */ +#define R367CAB_TEST_CTL 0xf40f +#define F367CAB_TST_BLK_SEL 0xf40f0060 +#define F367CAB_TST_BUS_SEL 0xf40f001f + +/* AGC_CTL */ +#define R367CAB_AGC_CTL 0xf410 +#define F367CAB_AGC_LCK_TH 0xf41000f0 +#define F367CAB_AGC_ACCUMRSTSEL 0xf4100007 + +/* AGC_IF_CFG */ +#define R367CAB_AGC_IF_CFG 0xf411 +#define F367CAB_AGC_IF_BWSEL 0xf41100f0 +#define F367CAB_AGC_IF_FREEZE 0xf4110002 + +/* AGC_RF_CFG */ +#define R367CAB_AGC_RF_CFG 0xf412 +#define F367CAB_AGC_RF_BWSEL 0xf4120070 +#define F367CAB_AGC_RF_FREEZE 0xf4120002 + +/* AGC_PWM_CFG */ +#define R367CAB_AGC_PWM_CFG 0xf413 +#define F367CAB_AGC_RF_PWM_TST 0xf4130080 +#define F367CAB_AGC_RF_PWM_INV 0xf4130040 +#define F367CAB_AGC_IF_PWM_TST 0xf4130008 +#define F367CAB_AGC_IF_PWM_INV 0xf4130004 +#define F367CAB_AGC_PWM_CLKDIV 0xf4130003 + +/* AGC_PWR_REF_L */ +#define R367CAB_AGC_PWR_REF_L 0xf414 +#define F367CAB_AGC_PWRREF_LO 0xf41400ff + +/* AGC_PWR_REF_H */ +#define R367CAB_AGC_PWR_REF_H 0xf415 +#define F367CAB_AGC_PWRREF_HI 0xf4150003 + +/* AGC_RF_TH_L */ +#define R367CAB_AGC_RF_TH_L 0xf416 +#define F367CAB_AGC_RF_TH_LO 0xf41600ff + +/* AGC_RF_TH_H */ +#define R367CAB_AGC_RF_TH_H 0xf417 +#define F367CAB_AGC_RF_TH_HI 0xf417000f + +/* AGC_IF_LTH_L */ +#define R367CAB_AGC_IF_LTH_L 0xf418 +#define F367CAB_AGC_IF_THLO_LO 0xf41800ff + +/* AGC_IF_LTH_H */ +#define R367CAB_AGC_IF_LTH_H 0xf419 +#define F367CAB_AGC_IF_THLO_HI 0xf419000f + +/* AGC_IF_HTH_L */ +#define R367CAB_AGC_IF_HTH_L 0xf41a +#define F367CAB_AGC_IF_THHI_LO 0xf41a00ff + +/* AGC_IF_HTH_H */ +#define R367CAB_AGC_IF_HTH_H 0xf41b +#define F367CAB_AGC_IF_THHI_HI 0xf41b000f + +/* AGC_PWR_RD_L */ +#define R367CAB_AGC_PWR_RD_L 0xf41c +#define F367CAB_AGC_PWR_WORD_LO 0xf41c00ff + +/* AGC_PWR_RD_M */ +#define R367CAB_AGC_PWR_RD_M 0xf41d +#define F367CAB_AGC_PWR_WORD_ME 0xf41d00ff + +/* AGC_PWR_RD_H */ +#define R367CAB_AGC_PWR_RD_H 0xf41e +#define F367CAB_AGC_PWR_WORD_HI 0xf41e0003 + +/* AGC_PWM_IFCMD_L */ +#define R367CAB_AGC_PWM_IFCMD_L 0xf420 +#define F367CAB_AGC_IF_PWMCMD_LO 0xf42000ff + +/* AGC_PWM_IFCMD_H */ +#define R367CAB_AGC_PWM_IFCMD_H 0xf421 +#define F367CAB_AGC_IF_PWMCMD_HI 0xf421000f + +/* AGC_PWM_RFCMD_L */ +#define R367CAB_AGC_PWM_RFCMD_L 0xf422 +#define F367CAB_AGC_RF_PWMCMD_LO 0xf42200ff + +/* AGC_PWM_RFCMD_H */ +#define R367CAB_AGC_PWM_RFCMD_H 0xf423 +#define F367CAB_AGC_RF_PWMCMD_HI 0xf423000f + +/* IQDEM_CFG */ +#define R367CAB_IQDEM_CFG 0xf424 +#define F367CAB_IQDEM_CLK_SEL 0xf4240004 +#define F367CAB_IQDEM_INVIQ 0xf4240002 +#define F367CAB_IQDEM_A2dTYPE 0xf4240001 + +/* MIX_NCO_LL */ +#define R367CAB_MIX_NCO_LL 0xf425 +#define F367CAB_MIX_NCO_INC_LL 0xf42500ff + +/* MIX_NCO_HL */ +#define R367CAB_MIX_NCO_HL 0xf426 +#define F367CAB_MIX_NCO_INC_HL 0xf42600ff + +/* MIX_NCO_HH */ +#define R367CAB_MIX_NCO_HH 0xf427 +#define F367CAB_MIX_NCO_INVCNST 0xf4270080 +#define F367CAB_MIX_NCO_INC_HH 0xf427007f + +/* SRC_NCO_LL */ +#define R367CAB_SRC_NCO_LL 0xf428 +#define F367CAB_SRC_NCO_INC_LL 0xf42800ff + +/* SRC_NCO_LH */ +#define R367CAB_SRC_NCO_LH 0xf429 +#define F367CAB_SRC_NCO_INC_LH 0xf42900ff + +/* SRC_NCO_HL */ +#define R367CAB_SRC_NCO_HL 0xf42a +#define F367CAB_SRC_NCO_INC_HL 0xf42a00ff + +/* SRC_NCO_HH */ +#define R367CAB_SRC_NCO_HH 0xf42b +#define F367CAB_SRC_NCO_INC_HH 0xf42b007f + +/* IQDEM_GAIN_SRC_L */ +#define R367CAB_IQDEM_GAIN_SRC_L 0xf42c +#define F367CAB_GAIN_SRC_LO 0xf42c00ff + +/* IQDEM_GAIN_SRC_H */ +#define R367CAB_IQDEM_GAIN_SRC_H 0xf42d +#define F367CAB_GAIN_SRC_HI 0xf42d0003 + +/* IQDEM_DCRM_CFG_LL */ +#define R367CAB_IQDEM_DCRM_CFG_LL 0xf430 +#define F367CAB_DCRM0_DCIN_L 0xf43000ff + +/* IQDEM_DCRM_CFG_LH */ +#define R367CAB_IQDEM_DCRM_CFG_LH 0xf431 +#define F367CAB_DCRM1_I_DCIN_L 0xf43100fc +#define F367CAB_DCRM0_DCIN_H 0xf4310003 + +/* IQDEM_DCRM_CFG_HL */ +#define R367CAB_IQDEM_DCRM_CFG_HL 0xf432 +#define F367CAB_DCRM1_Q_DCIN_L 0xf43200f0 +#define F367CAB_DCRM1_I_DCIN_H 0xf432000f + +/* IQDEM_DCRM_CFG_HH */ +#define R367CAB_IQDEM_DCRM_CFG_HH 0xf433 +#define F367CAB_DCRM1_FRZ 0xf4330080 +#define F367CAB_DCRM0_FRZ 0xf4330040 +#define F367CAB_DCRM1_Q_DCIN_H 0xf433003f + +/* IQDEM_ADJ_COEFf0 */ +#define R367CAB_IQDEM_ADJ_COEFF0 0xf434 +#define F367CAB_ADJIIR_COEFF10_L 0xf43400ff + +/* IQDEM_ADJ_COEFF1 */ +#define R367CAB_IQDEM_ADJ_COEFF1 0xf435 +#define F367CAB_ADJIIR_COEFF11_L 0xf43500fc +#define F367CAB_ADJIIR_COEFF10_H 0xf4350003 + +/* IQDEM_ADJ_COEFF2 */ +#define R367CAB_IQDEM_ADJ_COEFF2 0xf436 +#define F367CAB_ADJIIR_COEFF12_L 0xf43600f0 +#define F367CAB_ADJIIR_COEFF11_H 0xf436000f + +/* IQDEM_ADJ_COEFF3 */ +#define R367CAB_IQDEM_ADJ_COEFF3 0xf437 +#define F367CAB_ADJIIR_COEFF20_L 0xf43700c0 +#define F367CAB_ADJIIR_COEFF12_H 0xf437003f + +/* IQDEM_ADJ_COEFF4 */ +#define R367CAB_IQDEM_ADJ_COEFF4 0xf438 +#define F367CAB_ADJIIR_COEFF20_H 0xf43800ff + +/* IQDEM_ADJ_COEFF5 */ +#define R367CAB_IQDEM_ADJ_COEFF5 0xf439 +#define F367CAB_ADJIIR_COEFF21_L 0xf43900ff + +/* IQDEM_ADJ_COEFF6 */ +#define R367CAB_IQDEM_ADJ_COEFF6 0xf43a +#define F367CAB_ADJIIR_COEFF22_L 0xf43a00fc +#define F367CAB_ADJIIR_COEFF21_H 0xf43a0003 + +/* IQDEM_ADJ_COEFF7 */ +#define R367CAB_IQDEM_ADJ_COEFF7 0xf43b +#define F367CAB_ADJIIR_COEFF22_H 0xf43b000f + +/* IQDEM_ADJ_EN */ +#define R367CAB_IQDEM_ADJ_EN 0xf43c +#define F367CAB_ALLPASSFILT_EN 0xf43c0008 +#define F367CAB_ADJ_AGC_EN 0xf43c0004 +#define F367CAB_ADJ_COEFF_FRZ 0xf43c0002 +#define F367CAB_ADJ_EN 0xf43c0001 + +/* IQDEM_ADJ_AGC_REF */ +#define R367CAB_IQDEM_ADJ_AGC_REF 0xf43d +#define F367CAB_ADJ_AGC_REF 0xf43d00ff + +/* ALLPASSFILT1 */ +#define R367CAB_ALLPASSFILT1 0xf440 +#define F367CAB_ALLPASSFILT_COEFF1_LO 0xf44000ff + +/* ALLPASSFILT2 */ +#define R367CAB_ALLPASSFILT2 0xf441 +#define F367CAB_ALLPASSFILT_COEFF1_ME 0xf44100ff + +/* ALLPASSFILT3 */ +#define R367CAB_ALLPASSFILT3 0xf442 +#define F367CAB_ALLPASSFILT_COEFF2_LO 0xf44200c0 +#define F367CAB_ALLPASSFILT_COEFF1_HI 0xf442003f + +/* ALLPASSFILT4 */ +#define R367CAB_ALLPASSFILT4 0xf443 +#define F367CAB_ALLPASSFILT_COEFF2_MEL 0xf44300ff + +/* ALLPASSFILT5 */ +#define R367CAB_ALLPASSFILT5 0xf444 +#define F367CAB_ALLPASSFILT_COEFF2_MEH 0xf44400ff + +/* ALLPASSFILT6 */ +#define R367CAB_ALLPASSFILT6 0xf445 +#define F367CAB_ALLPASSFILT_COEFF3_LO 0xf44500f0 +#define F367CAB_ALLPASSFILT_COEFF2_HI 0xf445000f + +/* ALLPASSFILT7 */ +#define R367CAB_ALLPASSFILT7 0xf446 +#define F367CAB_ALLPASSFILT_COEFF3_MEL 0xf44600ff + +/* ALLPASSFILT8 */ +#define R367CAB_ALLPASSFILT8 0xf447 +#define F367CAB_ALLPASSFILT_COEFF3_MEH 0xf44700ff + +/* ALLPASSFILT9 */ +#define R367CAB_ALLPASSFILT9 0xf448 +#define F367CAB_ALLPASSFILT_COEFF4_LO 0xf44800fc +#define F367CAB_ALLPASSFILT_COEFF3_HI 0xf4480003 + +/* ALLPASSFILT10 */ +#define R367CAB_ALLPASSFILT10 0xf449 +#define F367CAB_ALLPASSFILT_COEFF4_ME 0xf44900ff + +/* ALLPASSFILT11 */ +#define R367CAB_ALLPASSFILT11 0xf44a +#define F367CAB_ALLPASSFILT_COEFF4_HI 0xf44a00ff + +/* TRL_AGC_CFG */ +#define R367CAB_TRL_AGC_CFG 0xf450 +#define F367CAB_TRL_AGC_FREEZE 0xf4500080 +#define F367CAB_TRL_AGC_REF 0xf450007f + +/* TRL_LPF_CFG */ +#define R367CAB_TRL_LPF_CFG 0xf454 +#define F367CAB_NYQPOINT_INV 0xf4540040 +#define F367CAB_TRL_SHIFT 0xf4540030 +#define F367CAB_NYQ_COEFF_SEL 0xf454000c +#define F367CAB_TRL_LPF_FREEZE 0xf4540002 +#define F367CAB_TRL_LPF_CRT 0xf4540001 + +/* TRL_LPF_ACQ_GAIN */ +#define R367CAB_TRL_LPF_ACQ_GAIN 0xf455 +#define F367CAB_TRL_GDIR_ACQ 0xf4550070 +#define F367CAB_TRL_GINT_ACQ 0xf4550007 + +/* TRL_LPF_TRK_GAIN */ +#define R367CAB_TRL_LPF_TRK_GAIN 0xf456 +#define F367CAB_TRL_GDIR_TRK 0xf4560070 +#define F367CAB_TRL_GINT_TRK 0xf4560007 + +/* TRL_LPF_OUT_GAIN */ +#define R367CAB_TRL_LPF_OUT_GAIN 0xf457 +#define F367CAB_TRL_GAIN_OUT 0xf4570007 + +/* TRL_LOCKDET_LTH */ +#define R367CAB_TRL_LOCKDET_LTH 0xf458 +#define F367CAB_TRL_LCK_THLO 0xf4580007 + +/* TRL_LOCKDET_HTH */ +#define R367CAB_TRL_LOCKDET_HTH 0xf459 +#define F367CAB_TRL_LCK_THHI 0xf45900ff + +/* TRL_LOCKDET_TRGVAL */ +#define R367CAB_TRL_LOCKDET_TRGVAL 0xf45a +#define F367CAB_TRL_LCK_TRG 0xf45a00ff + +/* IQ_QAM */ +#define R367CAB_IQ_QAM 0xf45c +#define F367CAB_IQ_INPUT 0xf45c0008 +#define F367CAB_DETECT_MODE 0xf45c0007 + +/* FSM_STATE */ +#define R367CAB_FSM_STATE 0xf460 +#define F367CAB_CRL_DFE 0xf4600080 +#define F367CAB_DFE_START 0xf4600040 +#define F367CAB_CTRLG_START 0xf4600030 +#define F367CAB_FSM_FORCESTATE 0xf460000f + +/* FSM_CTL */ +#define R367CAB_FSM_CTL 0xf461 +#define F367CAB_FEC2_EN 0xf4610040 +#define F367CAB_SIT_EN 0xf4610020 +#define F367CAB_TRL_AHEAD 0xf4610010 +#define F367CAB_TRL2_EN 0xf4610008 +#define F367CAB_FSM_EQA1_EN 0xf4610004 +#define F367CAB_FSM_BKP_DIS 0xf4610002 +#define F367CAB_FSM_FORCE_EN 0xf4610001 + +/* FSM_STS */ +#define R367CAB_FSM_STS 0xf462 +#define F367CAB_FSM_STATUS 0xf462000f + +/* FSM_SNR0_HTH */ +#define R367CAB_FSM_SNR0_HTH 0xf463 +#define F367CAB_SNR0_HTH 0xf46300ff + +/* FSM_SNR1_HTH */ +#define R367CAB_FSM_SNR1_HTH 0xf464 +#define F367CAB_SNR1_HTH 0xf46400ff + +/* FSM_SNR2_HTH */ +#define R367CAB_FSM_SNR2_HTH 0xf465 +#define F367CAB_SNR2_HTH 0xf46500ff + +/* FSM_SNR0_LTH */ +#define R367CAB_FSM_SNR0_LTH 0xf466 +#define F367CAB_SNR0_LTH 0xf46600ff + +/* FSM_SNR1_LTH */ +#define R367CAB_FSM_SNR1_LTH 0xf467 +#define F367CAB_SNR1_LTH 0xf46700ff + +/* FSM_EQA1_HTH */ +#define R367CAB_FSM_EQA1_HTH 0xf468 +#define F367CAB_SNR3_HTH_LO 0xf46800f0 +#define F367CAB_EQA1_HTH 0xf468000f + +/* FSM_TEMPO */ +#define R367CAB_FSM_TEMPO 0xf469 +#define F367CAB_SIT 0xf46900c0 +#define F367CAB_WST 0xf4690038 +#define F367CAB_ELT 0xf4690006 +#define F367CAB_SNR3_HTH_HI 0xf4690001 + +/* FSM_CONFIG */ +#define R367CAB_FSM_CONFIG 0xf46a +#define F367CAB_FEC2_DFEOFF 0xf46a0004 +#define F367CAB_PRIT_STATE 0xf46a0002 +#define F367CAB_MODMAP_STATE 0xf46a0001 + +/* EQU_I_TESTTAP_L */ +#define R367CAB_EQU_I_TESTTAP_L 0xf474 +#define F367CAB_I_TEST_TAP_L 0xf47400ff + +/* EQU_I_TESTTAP_M */ +#define R367CAB_EQU_I_TESTTAP_M 0xf475 +#define F367CAB_I_TEST_TAP_M 0xf47500ff + +/* EQU_I_TESTTAP_H */ +#define R367CAB_EQU_I_TESTTAP_H 0xf476 +#define F367CAB_I_TEST_TAP_H 0xf476001f + +/* EQU_TESTAP_CFG */ +#define R367CAB_EQU_TESTAP_CFG 0xf477 +#define F367CAB_TEST_FFE_DFE_SEL 0xf4770040 +#define F367CAB_TEST_TAP_SELECT 0xf477003f + +/* EQU_Q_TESTTAP_L */ +#define R367CAB_EQU_Q_TESTTAP_L 0xf478 +#define F367CAB_Q_TEST_TAP_L 0xf47800ff + +/* EQU_Q_TESTTAP_M */ +#define R367CAB_EQU_Q_TESTTAP_M 0xf479 +#define F367CAB_Q_TEST_TAP_M 0xf47900ff + +/* EQU_Q_TESTTAP_H */ +#define R367CAB_EQU_Q_TESTTAP_H 0xf47a +#define F367CAB_Q_TEST_TAP_H 0xf47a001f + +/* EQU_TAP_CTRL */ +#define R367CAB_EQU_TAP_CTRL 0xf47b +#define F367CAB_MTAP_FRZ 0xf47b0010 +#define F367CAB_PRE_FREEZE 0xf47b0008 +#define F367CAB_DFE_TAPMON_EN 0xf47b0004 +#define F367CAB_FFE_TAPMON_EN 0xf47b0002 +#define F367CAB_MTAP_ONLY 0xf47b0001 + +/* EQU_CTR_CRL_CONTROL_L */ +#define R367CAB_EQU_CTR_CRL_CONTROL_L 0xf47c +#define F367CAB_EQU_CTR_CRL_CONTROL_LO 0xf47c00ff + +/* EQU_CTR_CRL_CONTROL_H */ +#define R367CAB_EQU_CTR_CRL_CONTROL_H 0xf47d +#define F367CAB_EQU_CTR_CRL_CONTROL_HI 0xf47d00ff + +/* EQU_CTR_HIPOW_L */ +#define R367CAB_EQU_CTR_HIPOW_L 0xf47e +#define F367CAB_CTR_HIPOW_L 0xf47e00ff + +/* EQU_CTR_HIPOW_H */ +#define R367CAB_EQU_CTR_HIPOW_H 0xf47f +#define F367CAB_CTR_HIPOW_H 0xf47f00ff + +/* EQU_I_EQU_LO */ +#define R367CAB_EQU_I_EQU_LO 0xf480 +#define F367CAB_EQU_I_EQU_L 0xf48000ff + +/* EQU_I_EQU_HI */ +#define R367CAB_EQU_I_EQU_HI 0xf481 +#define F367CAB_EQU_I_EQU_H 0xf4810003 + +/* EQU_Q_EQU_LO */ +#define R367CAB_EQU_Q_EQU_LO 0xf482 +#define F367CAB_EQU_Q_EQU_L 0xf48200ff + +/* EQU_Q_EQU_HI */ +#define R367CAB_EQU_Q_EQU_HI 0xf483 +#define F367CAB_EQU_Q_EQU_H 0xf4830003 + +/* EQU_MAPPER */ +#define R367CAB_EQU_MAPPER 0xf484 +#define F367CAB_QUAD_AUTO 0xf4840080 +#define F367CAB_QUAD_INV 0xf4840040 +#define F367CAB_QAM_MODE 0xf4840007 + +/* EQU_SWEEP_RATE */ +#define R367CAB_EQU_SWEEP_RATE 0xf485 +#define F367CAB_SNR_PER 0xf48500c0 +#define F367CAB_SWEEP_RATE 0xf485003f + +/* EQU_SNR_LO */ +#define R367CAB_EQU_SNR_LO 0xf486 +#define F367CAB_SNR_LO 0xf48600ff + +/* EQU_SNR_HI */ +#define R367CAB_EQU_SNR_HI 0xf487 +#define F367CAB_SNR_HI 0xf48700ff + +/* EQU_GAMMA_LO */ +#define R367CAB_EQU_GAMMA_LO 0xf488 +#define F367CAB_GAMMA_LO 0xf48800ff + +/* EQU_GAMMA_HI */ +#define R367CAB_EQU_GAMMA_HI 0xf489 +#define F367CAB_GAMMA_ME 0xf48900ff + +/* EQU_ERR_GAIN */ +#define R367CAB_EQU_ERR_GAIN 0xf48a +#define F367CAB_EQA1MU 0xf48a0070 +#define F367CAB_CRL2MU 0xf48a000e +#define F367CAB_GAMMA_HI 0xf48a0001 + +/* EQU_RADIUS */ +#define R367CAB_EQU_RADIUS 0xf48b +#define F367CAB_RADIUS 0xf48b00ff + +/* EQU_FFE_MAINTAP */ +#define R367CAB_EQU_FFE_MAINTAP 0xf48c +#define F367CAB_FFE_MAINTAP_INIT 0xf48c00ff + +/* EQU_FFE_LEAKAGE */ +#define R367CAB_EQU_FFE_LEAKAGE 0xf48e +#define F367CAB_LEAK_PER 0xf48e00f0 +#define F367CAB_EQU_OUTSEL 0xf48e0002 +#define F367CAB_PNT2dFE 0xf48e0001 + +/* EQU_FFE_MAINTAP_POS */ +#define R367CAB_EQU_FFE_MAINTAP_POS 0xf48f +#define F367CAB_FFE_LEAK_EN 0xf48f0080 +#define F367CAB_DFE_LEAK_EN 0xf48f0040 +#define F367CAB_FFE_MAINTAP_POS 0xf48f003f + +/* EQU_GAIN_WIDE */ +#define R367CAB_EQU_GAIN_WIDE 0xf490 +#define F367CAB_DFE_GAIN_WIDE 0xf49000f0 +#define F367CAB_FFE_GAIN_WIDE 0xf490000f + +/* EQU_GAIN_NARROW */ +#define R367CAB_EQU_GAIN_NARROW 0xf491 +#define F367CAB_DFE_GAIN_NARROW 0xf49100f0 +#define F367CAB_FFE_GAIN_NARROW 0xf491000f + +/* EQU_CTR_LPF_GAIN */ +#define R367CAB_EQU_CTR_LPF_GAIN 0xf492 +#define F367CAB_CTR_GTO 0xf4920080 +#define F367CAB_CTR_GDIR 0xf4920070 +#define F367CAB_SWEEP_EN 0xf4920008 +#define F367CAB_CTR_GINT 0xf4920007 + +/* EQU_CRL_LPF_GAIN */ +#define R367CAB_EQU_CRL_LPF_GAIN 0xf493 +#define F367CAB_CRL_GTO 0xf4930080 +#define F367CAB_CRL_GDIR 0xf4930070 +#define F367CAB_SWEEP_DIR 0xf4930008 +#define F367CAB_CRL_GINT 0xf4930007 + +/* EQU_GLOBAL_GAIN */ +#define R367CAB_EQU_GLOBAL_GAIN 0xf494 +#define F367CAB_CRL_GAIN 0xf49400f8 +#define F367CAB_CTR_INC_GAIN 0xf4940004 +#define F367CAB_CTR_FRAC 0xf4940003 + +/* EQU_CRL_LD_SEN */ +#define R367CAB_EQU_CRL_LD_SEN 0xf495 +#define F367CAB_CTR_BADPOINT_EN 0xf4950080 +#define F367CAB_CTR_GAIN 0xf4950070 +#define F367CAB_LIMANEN 0xf4950008 +#define F367CAB_CRL_LD_SEN 0xf4950007 + +/* EQU_CRL_LD_VAL */ +#define R367CAB_EQU_CRL_LD_VAL 0xf496 +#define F367CAB_CRL_BISTH_LIMIT 0xf4960080 +#define F367CAB_CARE_EN 0xf4960040 +#define F367CAB_CRL_LD_PER 0xf4960030 +#define F367CAB_CRL_LD_WST 0xf496000c +#define F367CAB_CRL_LD_TFS 0xf4960003 + +/* EQU_CRL_TFR */ +#define R367CAB_EQU_CRL_TFR 0xf497 +#define F367CAB_CRL_LD_TFR 0xf49700ff + +/* EQU_CRL_BISTH_LO */ +#define R367CAB_EQU_CRL_BISTH_LO 0xf498 +#define F367CAB_CRL_BISTH_LO 0xf49800ff + +/* EQU_CRL_BISTH_HI */ +#define R367CAB_EQU_CRL_BISTH_HI 0xf499 +#define F367CAB_CRL_BISTH_HI 0xf49900ff + +/* EQU_SWEEP_RANGE_LO */ +#define R367CAB_EQU_SWEEP_RANGE_LO 0xf49a +#define F367CAB_SWEEP_RANGE_LO 0xf49a00ff + +/* EQU_SWEEP_RANGE_HI */ +#define R367CAB_EQU_SWEEP_RANGE_HI 0xf49b +#define F367CAB_SWEEP_RANGE_HI 0xf49b00ff + +/* EQU_CRL_LIMITER */ +#define R367CAB_EQU_CRL_LIMITER 0xf49c +#define F367CAB_BISECTOR_EN 0xf49c0080 +#define F367CAB_PHEST128_EN 0xf49c0040 +#define F367CAB_CRL_LIM 0xf49c003f + +/* EQU_MODULUS_MAP */ +#define R367CAB_EQU_MODULUS_MAP 0xf49d +#define F367CAB_PNT_DEPTH 0xf49d00e0 +#define F367CAB_MODULUS_CMP 0xf49d001f + +/* EQU_PNT_GAIN */ +#define R367CAB_EQU_PNT_GAIN 0xf49e +#define F367CAB_PNT_EN 0xf49e0080 +#define F367CAB_MODULUSMAP_EN 0xf49e0040 +#define F367CAB_PNT_GAIN 0xf49e003f + +/* FEC_AC_CTR_0 */ +#define R367CAB_FEC_AC_CTR_0 0xf4a8 +#define F367CAB_BE_BYPASS 0xf4a80020 +#define F367CAB_REFRESH47 0xf4a80010 +#define F367CAB_CT_NBST 0xf4a80008 +#define F367CAB_TEI_ENA 0xf4a80004 +#define F367CAB_DS_ENA 0xf4a80002 +#define F367CAB_TSMF_EN 0xf4a80001 + +/* FEC_AC_CTR_1 */ +#define R367CAB_FEC_AC_CTR_1 0xf4a9 +#define F367CAB_DEINT_DEPTH 0xf4a900ff + +/* FEC_AC_CTR_2 */ +#define R367CAB_FEC_AC_CTR_2 0xf4aa +#define F367CAB_DEINT_M 0xf4aa00f8 +#define F367CAB_DIS_UNLOCK 0xf4aa0004 +#define F367CAB_DESCR_MODE 0xf4aa0003 + +/* FEC_AC_CTR_3 */ +#define R367CAB_FEC_AC_CTR_3 0xf4ab +#define F367CAB_DI_UNLOCK 0xf4ab0080 +#define F367CAB_DI_FREEZE 0xf4ab0040 +#define F367CAB_MISMATCH 0xf4ab0030 +#define F367CAB_ACQ_MODE 0xf4ab000c +#define F367CAB_TRK_MODE 0xf4ab0003 + +/* FEC_STATUS */ +#define R367CAB_FEC_STATUS 0xf4ac +#define F367CAB_DEINT_SMCNTR 0xf4ac00e0 +#define F367CAB_DEINT_SYNCSTATE 0xf4ac0018 +#define F367CAB_DEINT_SYNLOST 0xf4ac0004 +#define F367CAB_DESCR_SYNCSTATE 0xf4ac0002 + +/* RS_COUNTER_0 */ +#define R367CAB_RS_COUNTER_0 0xf4ae +#define F367CAB_BK_CT_L 0xf4ae00ff + +/* RS_COUNTER_1 */ +#define R367CAB_RS_COUNTER_1 0xf4af +#define F367CAB_BK_CT_H 0xf4af00ff + +/* RS_COUNTER_2 */ +#define R367CAB_RS_COUNTER_2 0xf4b0 +#define F367CAB_CORR_CT_L 0xf4b000ff + +/* RS_COUNTER_3 */ +#define R367CAB_RS_COUNTER_3 0xf4b1 +#define F367CAB_CORR_CT_H 0xf4b100ff + +/* RS_COUNTER_4 */ +#define R367CAB_RS_COUNTER_4 0xf4b2 +#define F367CAB_UNCORR_CT_L 0xf4b200ff + +/* RS_COUNTER_5 */ +#define R367CAB_RS_COUNTER_5 0xf4b3 +#define F367CAB_UNCORR_CT_H 0xf4b300ff + +/* BERT_0 */ +#define R367CAB_BERT_0 0xf4b4 +#define F367CAB_RS_NOCORR 0xf4b40004 +#define F367CAB_CT_HOLD 0xf4b40002 +#define F367CAB_CT_CLEAR 0xf4b40001 + +/* BERT_1 */ +#define R367CAB_BERT_1 0xf4b5 +#define F367CAB_BERT_ON 0xf4b50020 +#define F367CAB_BERT_ERR_SRC 0xf4b50010 +#define F367CAB_BERT_ERR_MODE 0xf4b50008 +#define F367CAB_BERT_NBYTE 0xf4b50007 + +/* BERT_2 */ +#define R367CAB_BERT_2 0xf4b6 +#define F367CAB_BERT_ERRCOUNT_L 0xf4b600ff + +/* BERT_3 */ +#define R367CAB_BERT_3 0xf4b7 +#define F367CAB_BERT_ERRCOUNT_H 0xf4b700ff + +/* OUTFORMAT_0 */ +#define R367CAB_OUTFORMAT_0 0xf4b8 +#define F367CAB_CLK_POLARITY 0xf4b80080 +#define F367CAB_FEC_TYPE 0xf4b80040 +#define F367CAB_SYNC_STRIP 0xf4b80008 +#define F367CAB_TS_SWAP 0xf4b80004 +#define F367CAB_OUTFORMAT 0xf4b80003 + +/* OUTFORMAT_1 */ +#define R367CAB_OUTFORMAT_1 0xf4b9 +#define F367CAB_CI_DIVRANGE 0xf4b900ff + +/* SMOOTHER_2 */ +#define R367CAB_SMOOTHER_2 0xf4be +#define F367CAB_FIFO_BYPASS 0xf4be0020 + +/* TSMF_CTRL_0 */ +#define R367CAB_TSMF_CTRL_0 0xf4c0 +#define F367CAB_TS_NUMBER 0xf4c0001e +#define F367CAB_SEL_MODE 0xf4c00001 + +/* TSMF_CTRL_1 */ +#define R367CAB_TSMF_CTRL_1 0xf4c1 +#define F367CAB_CHECK_ERROR_BIT 0xf4c10080 +#define F367CAB_CHCK_F_SYNC 0xf4c10040 +#define F367CAB_H_MODE 0xf4c10008 +#define F367CAB_D_V_MODE 0xf4c10004 +#define F367CAB_MODE 0xf4c10003 + +/* TSMF_CTRL_3 */ +#define R367CAB_TSMF_CTRL_3 0xf4c3 +#define F367CAB_SYNC_IN_COUNT 0xf4c300f0 +#define F367CAB_SYNC_OUT_COUNT 0xf4c3000f + +/* TS_ON_ID_0 */ +#define R367CAB_TS_ON_ID_0 0xf4c4 +#define F367CAB_TS_ID_L 0xf4c400ff + +/* TS_ON_ID_1 */ +#define R367CAB_TS_ON_ID_1 0xf4c5 +#define F367CAB_TS_ID_H 0xf4c500ff + +/* TS_ON_ID_2 */ +#define R367CAB_TS_ON_ID_2 0xf4c6 +#define F367CAB_ON_ID_L 0xf4c600ff + +/* TS_ON_ID_3 */ +#define R367CAB_TS_ON_ID_3 0xf4c7 +#define F367CAB_ON_ID_H 0xf4c700ff + +/* RE_STATUS_0 */ +#define R367CAB_RE_STATUS_0 0xf4c8 +#define F367CAB_RECEIVE_STATUS_L 0xf4c800ff + +/* RE_STATUS_1 */ +#define R367CAB_RE_STATUS_1 0xf4c9 +#define F367CAB_RECEIVE_STATUS_LH 0xf4c900ff + +/* RE_STATUS_2 */ +#define R367CAB_RE_STATUS_2 0xf4ca +#define F367CAB_RECEIVE_STATUS_HL 0xf4ca00ff + +/* RE_STATUS_3 */ +#define R367CAB_RE_STATUS_3 0xf4cb +#define F367CAB_RECEIVE_STATUS_HH 0xf4cb003f + +/* TS_STATUS_0 */ +#define R367CAB_TS_STATUS_0 0xf4cc +#define F367CAB_TS_STATUS_L 0xf4cc00ff + +/* TS_STATUS_1 */ +#define R367CAB_TS_STATUS_1 0xf4cd +#define F367CAB_TS_STATUS_H 0xf4cd007f + +/* TS_STATUS_2 */ +#define R367CAB_TS_STATUS_2 0xf4ce +#define F367CAB_ERROR 0xf4ce0080 +#define F367CAB_EMERGENCY 0xf4ce0040 +#define F367CAB_CRE_TS 0xf4ce0030 +#define F367CAB_VER 0xf4ce000e +#define F367CAB_M_LOCK 0xf4ce0001 + +/* TS_STATUS_3 */ +#define R367CAB_TS_STATUS_3 0xf4cf +#define F367CAB_UPDATE_READY 0xf4cf0080 +#define F367CAB_END_FRAME_HEADER 0xf4cf0040 +#define F367CAB_CONTCNT 0xf4cf0020 +#define F367CAB_TS_IDENTIFIER_SEL 0xf4cf000f + +/* T_O_ID_0 */ +#define R367CAB_T_O_ID_0 0xf4d0 +#define F367CAB_ON_ID_I_L 0xf4d000ff + +/* T_O_ID_1 */ +#define R367CAB_T_O_ID_1 0xf4d1 +#define F367CAB_ON_ID_I_H 0xf4d100ff + +/* T_O_ID_2 */ +#define R367CAB_T_O_ID_2 0xf4d2 +#define F367CAB_TS_ID_I_L 0xf4d200ff + +/* T_O_ID_3 */ +#define R367CAB_T_O_ID_3 0xf4d3 +#define F367CAB_TS_ID_I_H 0xf4d300ff + +#define STV0367CAB_NBREGS 187 + +#endif -- cgit v0.10.2 From e80edce1abfabf637edc68e8dd13ef0af6e46cdf Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:03:00 -0300 Subject: [media] xc5000: add support for DVB-C tuning Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index 76ac5cd..e3218c9 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -683,6 +683,24 @@ static int xc5000_set_params(struct dvb_frontend *fe, return -EINVAL; } priv->rf_mode = XC_RF_MODE_AIR; + } else if (fe->ops.info.type == FE_QAM) { + dprintk(1, "%s() QAM\n", __func__); + switch (params->u.qam.modulation) { + case QAM_16: + case QAM_32: + case QAM_64: + case QAM_128: + case QAM_256: + case QAM_AUTO: + dprintk(1, "%s() QAM modulation\n", __func__); + priv->bandwidth = BANDWIDTH_8_MHZ; + priv->video_standard = DTV7_8; + priv->freq_hz = params->frequency - 2750000; + priv->rf_mode = XC_RF_MODE_CABLE; + break; + default: + return -EINVAL; + } } else { printk(KERN_ERR "xc5000 modulation type not supported!\n"); return -EINVAL; -- cgit v0.10.2 From 78db854757aa4110f9c6873d1529b851037a3405 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:04:00 -0300 Subject: [media] Initial commit to support NetUP Dual DVB-T/C CI RF card The card based on cx23885 PCI-e brige. Altera FPGA for CI, multistandard demods stv0367 from STM for QAM & OFDM, Xcieve xc5000 tuners and additional cx25840 for second analog input. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0367.h b/drivers/media/dvb/frontends/stv0367.h index 822a8e9..93cc4a5 100644 --- a/drivers/media/dvb/frontends/stv0367.h +++ b/drivers/media/dvb/frontends/stv0367.h @@ -4,8 +4,8 @@ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. * * Copyright (C) ST Microelectronics. - * Copyright (C) 2010 NetUP Inc. - * Copyright (C) 2010 Igor M. Liplianin + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin * * 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 diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index 5e5faad..3b6e7f2 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -1,6 +1,7 @@ config VIDEO_CX23885 tristate "Conexant cx23885 (2388x successor) support" - depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT + depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND + select SND_PCM select I2C_ALGOBIT select VIDEO_BTCX select VIDEO_TUNER diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index b298b73..a3fe26c 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -24,10 +24,14 @@ #include #include #include +#include +#include #include "cx23885.h" #include "tuner-xc2028.h" #include "netup-init.h" +#include "altera-ci.h" +#include "xc5000.h" #include "cx23888-ir.h" static unsigned int enable_885_ir; @@ -187,7 +191,7 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, }, [CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = { - .cimax = 1, + .ci_type = 1, .name = "NetUP Dual DVB-S2 CI", .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, @@ -329,6 +333,19 @@ struct cx23885_board cx23885_boards[] = { CX25840_SVIDEO_CHROMA4, } }, }, + [CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF] = { + .ci_type = 2, + .name = "NetUP Dual DVB-T/C-CI RF", + .porta = CX23885_ANALOG_VIDEO, + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x64, + .input = { { + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_COMPOSITE1, + } }, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -520,6 +537,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x5654, .subdevice = 0x2390, .card = CX23885_BOARD_GOTVIEW_X5_3D_HYBRID, + }, { + .subvendor = 0x1b55, + .subdevice = 0xe2e4, + .card = CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -740,6 +761,9 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) /* Tuner Reset Command */ bitmask = 0x02; break; + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: + altera_ci_tuner_reset(dev, port->nr); + break; } if (bitmask) { @@ -998,6 +1022,33 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID: cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */ break; + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: + /* GPIO-0 ~INT in + GPIO-1 TMS out + GPIO-2 ~reset chips out + GPIO-3 to GPIO-10 data/addr for CA in/out + GPIO-11 ~CS out + GPIO-12 ADDR out + GPIO-13 ~WR out + GPIO-14 ~RD out + GPIO-15 ~RDY in + GPIO-16 TCK out + GPIO-17 TDO in + GPIO-18 TDI out + */ + cx_set(GP0_IO, 0x00060000); /* GPIO-1,2 as out */ + /* GPIO-0 as INT, reset & TMS low */ + cx_clear(GP0_IO, 0x00010006); + mdelay(100);/* reset delay */ + cx_set(GP0_IO, 0x00000004); /* reset high */ + cx_write(MC417_CTL, 0x00000037);/* enable GPIO-3..18 pins */ + /* GPIO-17 is TDO in, GPIO-15 is ~RDY in, rest is out */ + cx_write(MC417_OEN, 0x00005000); + /* ~RD, ~WR high; ADDR low; ~CS high */ + cx_write(MC417_RWD, 0x00000d00); + /* enable irq */ + cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ + break; } } @@ -1113,6 +1164,31 @@ void cx23885_ir_fini(struct cx23885_dev *dev) } } +int netup_jtag_io(void *device, int tms, int tdi, int read_tdo) +{ + int data; + int tdo = 0; + struct cx23885_dev *dev = (struct cx23885_dev *)device; + /*TMS*/ + data = ((cx_read(GP0_IO)) & (~0x00000002)); + data |= (tms ? 0x00020002 : 0x00020000); + cx_write(GP0_IO, data); + + /*TDI*/ + data = ((cx_read(MC417_RWD)) & (~0x0000a000)); + data |= (tdi ? 0x00008000 : 0); + cx_write(MC417_RWD, data); + if (read_tdo) + tdo = (data & 0x00004000) ? 1 : 0; /*TDO*/ + + cx_write(MC417_RWD, data | 0x00002000); + udelay(1); + /*TCK*/ + cx_write(MC417_RWD, data); + + return tdo; +} + void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) { switch (dev->board) { @@ -1212,6 +1288,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; @@ -1271,6 +1348,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_MYGICA_X8506: @@ -1293,6 +1371,29 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: netup_initialize(dev); break; + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: { + int ret; + const struct firmware *fw; + const char *filename = "dvb-netup-altera-01.fw"; + char *action = "configure"; + struct altera_config netup_config = { + .dev = dev, + .action = action, + .jtag_io = netup_jtag_io, + }; + + netup_initialize(dev); + + ret = request_firmware(&fw, filename, &dev->pci->dev); + if (ret != 0) + printk(KERN_ERR "did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details " + "on firmware-problems.", filename); + else + altera_init(&netup_config, fw); + + break; + } } } diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 3598824..9ad4c9c 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -29,9 +29,11 @@ #include #include #include +#include #include "cx23885.h" #include "cimax2.h" +#include "altera-ci.h" #include "cx23888-ir.h" #include "cx23885-ir.h" #include "cx23885-av.h" @@ -902,8 +904,6 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dev->pci_bus = dev->pci->bus->number; dev->pci_slot = PCI_SLOT(dev->pci->devfn); cx23885_irq_add(dev, 0x001f00); - if (cx23885_boards[dev->board].cimax > 0) - cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */ /* External Master 1 Bus */ dev->i2c_bus[0].nr = 0; @@ -1822,14 +1822,13 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) PCI_MSK_IR); } - if (cx23885_boards[dev->board].cimax > 0 && - ((pci_status & PCI_MSK_GPIO0) || - (pci_status & PCI_MSK_GPIO1))) { + if (cx23885_boards[dev->board].ci_type == 1 && + (pci_status & (PCI_MSK_GPIO1 | PCI_MSK_GPIO0))) + handled += netup_ci_slot_status(dev, pci_status); - if (cx23885_boards[dev->board].cimax > 0) - handled += netup_ci_slot_status(dev, pci_status); - - } + if (cx23885_boards[dev->board].ci_type == 2 && + (pci_status & PCI_MSK_GPIO0)) + handled += altera_ci_irq(dev); if (ts1_status) { if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) @@ -2064,7 +2063,10 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, switch (dev->board) { case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: - cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */ + cx23885_irq_add_enable(dev, PCI_MSK_GPIO1 | PCI_MSK_GPIO0); + break; + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: + cx23885_irq_add_enable(dev, PCI_MSK_GPIO0); break; } diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 5958cb8..cf36b9b 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -58,6 +58,8 @@ #include "atbm8830.h" #include "ds3000.h" #include "cx23885-f300.h" +#include "altera-ci.h" +#include "stv0367.h" static unsigned int debug; @@ -108,6 +110,22 @@ static void dvb_buf_release(struct videobuf_queue *q, cx23885_free_buffer(q, (struct cx23885_buffer *)vb); } +static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open) +{ + struct videobuf_dvb_frontends *f; + struct videobuf_dvb_frontend *fe; + + f = &port->frontends; + + if (f->gate <= 1) /* undefined or fe0 */ + fe = videobuf_dvb_get_frontend(f, 1); + else + fe = videobuf_dvb_get_frontend(f, f->gate); + + if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) + fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open); +} + static struct videobuf_queue_ops dvb_qops = { .buf_setup = dvb_buf_setup, .buf_prepare = dvb_buf_prepare, @@ -570,12 +588,84 @@ static struct max2165_config mygic_x8558pro_max2165_cfg2 = { .i2c_address = 0x60, .osc_clk = 20 }; +static struct stv0367_config netup_stv0367_config[] = { + { + .demod_address = 0x1c, + .xtal = 27000000, + .if_khz = 4500, + .if_iq_mode = 0, + .ts_mode = 1, + .clk_pol = 0, + }, { + .demod_address = 0x1d, + .xtal = 27000000, + .if_khz = 4500, + .if_iq_mode = 0, + .ts_mode = 1, + .clk_pol = 0, + }, +}; + +static struct xc5000_config netup_xc5000_config[] = { + { + .i2c_address = 0x61, + .if_khz = 4500, + }, { + .i2c_address = 0x64, + .if_khz = 4500, + }, +}; + +int netup_altera_fpga_rw(void *device, int flag, int data, int read) +{ + struct cx23885_dev *dev = (struct cx23885_dev *)device; + unsigned long timeout = jiffies + msecs_to_jiffies(1); + int mem = 0; + + cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS); + if (read) + cx_set(MC417_OEN, ALT_DATA); + else { + cx_clear(MC417_OEN, ALT_DATA);/* D0-D7 out */ + mem = cx_read(MC417_RWD); + mem &= ~ALT_DATA; + mem |= (data & ALT_DATA); + cx_write(MC417_RWD, mem); + } + + if (flag) + cx_set(MC417_RWD, ALT_AD_RG);/* ADDR */ + else + cx_clear(MC417_RWD, ALT_AD_RG);/* VAL */ + + cx_clear(MC417_RWD, ALT_CS);/* ~CS */ + if (read) + cx_clear(MC417_RWD, ALT_RD); + else + cx_clear(MC417_RWD, ALT_WR); + + for (;;) { + mem = cx_read(MC417_RWD); + if ((mem & ALT_RDY) == 0) + break; + if (time_after(jiffies, timeout)) + break; + udelay(1); + } + + cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS); + if (read) + return mem & ALT_DATA; + + return 0; +}; static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL; - struct videobuf_dvb_frontend *fe0; + struct videobuf_dvb_frontend *fe0, *fe1 = NULL; + int mfe_shared = 0; /* bus not shared by default */ int ret; /* Get the first frontend */ @@ -586,6 +676,12 @@ static int dvb_register(struct cx23885_tsport *port) /* init struct videobuf_dvb */ fe0->dvb.name = dev->name; + /* multi-frontend gate control is undefined or defaults to fe0 */ + port->frontends.gate = 0; + + /* Sets the gate control callback to be used by i2c command calls */ + port->gate_ctrl = cx23885_dvb_gate_ctrl; + /* init frontend */ switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1250: @@ -966,20 +1062,61 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; - + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: + i2c_bus = &dev->i2c_bus[0]; + mfe_shared = 1;/* MFE */ + port->frontends.gate = 0;/* not clear for me yet */ + /* ports B, C */ + /* MFE frontend 1 DVB-T */ + fe0->dvb.frontend = dvb_attach(stv0367ter_attach, + &netup_stv0367_config[port->nr - 1], + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) + if (NULL == dvb_attach(xc5000_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &netup_xc5000_config[port->nr - 1])) + goto frontend_detach; + /* MFE frontend 2 */ + fe1 = videobuf_dvb_get_frontend(&port->frontends, 2); + if (fe1 == NULL) + goto frontend_detach; + /* DVB-C init */ + fe1->dvb.frontend = dvb_attach(stv0367cab_attach, + &netup_stv0367_config[port->nr - 1], + &i2c_bus->i2c_adap); + if (fe1->dvb.frontend != NULL) { + fe1->dvb.frontend->id = 1; + if (NULL == dvb_attach(xc5000_attach, + fe1->dvb.frontend, + &i2c_bus->i2c_adap, + &netup_xc5000_config[port->nr - 1])) + goto frontend_detach; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", dev->name); break; } - if (NULL == fe0->dvb.frontend) { + + if ((NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend)) { printk(KERN_ERR "%s: frontend initialization failed\n", - dev->name); - return -1; + dev->name); + goto frontend_detach; } + /* define general-purpose callback pointer */ fe0->dvb.frontend->callback = cx23885_tuner_callback; + if (fe1) + fe1->dvb.frontend->callback = cx23885_tuner_callback; +#if 0 + /* Ensure all frontends negotiate bus access */ + fe0->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl; + if (fe1) + fe1->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl; +#endif /* Put the analog decoder in standby to keep it quiet */ call_all(dev, core, s_power, 0); @@ -989,10 +1126,10 @@ static int dvb_register(struct cx23885_tsport *port) /* register everything */ ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, - &dev->pci->dev, adapter_nr, 0, + &dev->pci->dev, adapter_nr, mfe_shared, cx23885_dvb_fe_ioctl_override); if (ret) - return ret; + goto frontend_detach; /* init CI & MAC */ switch (dev->board) { @@ -1008,6 +1145,17 @@ static int dvb_register(struct cx23885_tsport *port) netup_ci_init(port); break; } + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: { + struct altera_ci_config netup_ci_cfg = { + .dev = dev,/* magic number to identify*/ + .adapter = &port->frontends.adapter,/* for CI */ + .demux = &fe0->dvb.demux,/* for hw pid filter */ + .fpga_rw = netup_altera_fpga_rw, + }; + + altera_ci_init(&netup_ci_cfg, port->nr); + break; + } case CX23885_BOARD_TEVII_S470: { u8 eeprom[256]; /* 24C02 i2c eeprom */ @@ -1024,6 +1172,11 @@ static int dvb_register(struct cx23885_tsport *port) } return ret; + +frontend_detach: + port->gate_ctrl = NULL; + videobuf_dvb_dealloc_frontends(&port->frontends); + return -EINVAL; } int cx23885_dvb_register(struct cx23885_tsport *port) @@ -1100,8 +1253,13 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port) case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: netup_ci_exit(port); break; + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: + altera_ci_release(port->dev, port->nr); + break; } + port->gate_ctrl = NULL; + return 0; } diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 62e41ab..8b77fea 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -85,6 +85,7 @@ #define CX23885_BOARD_MYGICA_X8558PRO 27 #define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28 #define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID 29 +#define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -220,7 +221,7 @@ struct cx23885_board { */ u32 clk_freq; struct cx23885_input input[MAX_CX23885_INPUT]; - int cimax; /* for NetUP */ + int ci_type; /* for NetUP */ }; struct cx23885_subid { @@ -303,6 +304,7 @@ struct cx23885_tsport { /* Allow a single tsport to have multiple frontends */ u32 num_frontends; + void (*gate_ctrl)(struct cx23885_tsport *port, int open); void *port_priv; }; -- cgit v0.10.2 From 557f48d5720b45dae07788bc9976762c363b8ad0 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:05:00 -0300 Subject: [media] cx23885: implement tuner_bus parameter for cx23885_board structure There is two external I2C buses in cx23885 chip. Currently, analog tuners supported for second I2C bus only In NetUP Dual DVB-T/C CI RF card tuners connected to first bus So, in order to support analog tuners sitting on first bus we need modifications. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index a3fe26c..fb2045a 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -94,6 +94,7 @@ struct cx23885_board cx23885_boards[] = { .portc = CX23885_MPEG_DVB, .tuner_type = TUNER_PHILIPS_TDA8290, .tuner_addr = 0x42, /* 0x84 >> 1 */ + .tuner_bus = 1, .input = {{ .type = CX23885_VMUX_TELEVISION, .vmux = CX25840_VIN7_CH3 | @@ -216,6 +217,7 @@ struct cx23885_board cx23885_boards[] = { .name = "Mygica X8506 DMB-TH", .tuner_type = TUNER_XC5000, .tuner_addr = 0x61, + .tuner_bus = 1, .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, .input = { @@ -245,6 +247,7 @@ struct cx23885_board cx23885_boards[] = { .name = "Magic-Pro ProHDTV Extreme 2", .tuner_type = TUNER_XC5000, .tuner_addr = 0x61, + .tuner_bus = 1, .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, .input = { @@ -293,6 +296,7 @@ struct cx23885_board cx23885_boards[] = { .porta = CX23885_ANALOG_VIDEO, .tuner_type = TUNER_XC2028, .tuner_addr = 0x61, + .tuner_bus = 1, .input = {{ .type = CX23885_VMUX_TELEVISION, .vmux = CX25840_VIN2_CH1 | @@ -317,6 +321,7 @@ struct cx23885_board cx23885_boards[] = { .name = "GoTView X5 3D Hybrid", .tuner_type = TUNER_XC5000, .tuner_addr = 0x64, + .tuner_bus = 1, .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, .input = {{ diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 9ad4c9c..d621d76 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -970,11 +970,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) /* Assume some sensible defaults */ dev->tuner_type = cx23885_boards[dev->board].tuner_type; dev->tuner_addr = cx23885_boards[dev->board].tuner_addr; + dev->tuner_bus = cx23885_boards[dev->board].tuner_bus; dev->radio_type = cx23885_boards[dev->board].radio_type; dev->radio_addr = cx23885_boards[dev->board].radio_addr; - dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x\n", - __func__, dev->tuner_type, dev->tuner_addr); + dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x tuner_bus = %d\n", + __func__, dev->tuner_type, dev->tuner_addr, dev->tuner_bus); dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n", __func__, dev->radio_type, dev->radio_addr); diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 644fcb8..ee57f6b 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -1468,16 +1468,17 @@ int cx23885_video_register(struct cx23885_dev *dev) cx23885_irq_add_enable(dev, 0x01); - if (TUNER_ABSENT != dev->tuner_type) { + if ((TUNER_ABSENT != dev->tuner_type) && + ((dev->tuner_bus == 0) || (dev->tuner_bus == 1))) { struct v4l2_subdev *sd = NULL; if (dev->tuner_addr) sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_bus[1].i2c_adap, + &dev->i2c_bus[dev->tuner_bus].i2c_adap, "tuner", dev->tuner_addr, NULL); else sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_bus[1].i2c_adap, + &dev->i2c_bus[dev->tuner_bus].i2c_adap, "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV)); if (sd) { struct tuner_setup tun_setup; diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 8b77fea..d8c76b0 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -209,6 +209,7 @@ struct cx23885_board { unsigned int radio_type; unsigned char tuner_addr; unsigned char radio_addr; + unsigned int tuner_bus; /* Vendors can and do run the PCIe bridge at different * clock rates, driven physically by crystals on the PCBs. @@ -364,6 +365,7 @@ struct cx23885_dev { v4l2_std_id tvnorm; unsigned int tuner_type; unsigned char tuner_addr; + unsigned int tuner_bus; unsigned int radio_type; unsigned char radio_addr; unsigned int has_radio; -- cgit v0.10.2 From 10d0dcd73c619d86b8ba1c9cd753ce749f578bf4 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:06:00 -0300 Subject: [media] cx23885: implement num_fds_portb, num_fds_portc parameters for cx23885_board structure This is needed for multifrontend support. NetUP Dual DVB-T/C CI RF card has frontends connected to port B & C Each frontend has two switchable cores - DVB-T & DVB-C Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index fb2045a..ea88722 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -344,6 +344,8 @@ struct cx23885_board cx23885_boards[] = { .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, + .num_fds_portb = 2, + .num_fds_portc = 2, .tuner_type = TUNER_XC5000, .tuner_addr = 0x64, .input = { { diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index d621d76..d778b1a 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -1005,6 +1005,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) { + if (cx23885_boards[dev->board].num_fds_portb) + dev->ts1.num_frontends = + cx23885_boards[dev->board].num_fds_portb; if (cx23885_dvb_register(&dev->ts1) < 0) { printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n", __func__); @@ -1019,6 +1022,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) { + if (cx23885_boards[dev->board].num_fds_portc) + dev->ts2.num_frontends = + cx23885_boards[dev->board].num_fds_portc; if (cx23885_dvb_register(&dev->ts2) < 0) { printk(KERN_ERR "%s() Failed to register dvb on VID_C\n", diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index d8c76b0..8db2797 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -205,6 +205,7 @@ typedef enum { struct cx23885_board { char *name; port_t porta, portb, portc; + int num_fds_portb, num_fds_portc; unsigned int tuner_type; unsigned int radio_type; unsigned char tuner_addr; -- cgit v0.10.2 From 702dd7903bda0b83f6f6cb66fdadc4e5bbb60646 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:07:00 -0300 Subject: [media] cx23885: disable MSI for NetUP cards, otherwise CI is not working Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index d778b1a..9933810 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -1041,6 +1041,10 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_dev_checkrevision(dev); + /* disable MSI for NetUP cards, otherwise CI is not working */ + if (cx23885_boards[dev->board].ci_type > 0) + cx_clear(RDR_RDRCTL1, 1 << 8); + return 0; } diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h index a28772d..c87ac68 100644 --- a/drivers/media/video/cx23885/cx23885-reg.h +++ b/drivers/media/video/cx23885/cx23885-reg.h @@ -292,6 +292,7 @@ Channel manager Data Structure entry = 20 DWORD #define RDR_CFG0 0x00050000 #define RDR_CFG1 0x00050004 #define RDR_CFG2 0x00050008 +#define RDR_RDRCTL1 0x0005030c #define RDR_TLCTL0 0x00050318 /* APB DMAC Current Buffer Pointer */ -- cgit v0.10.2 From 2f2710bd4ee11a06e4c87956be4733566e288a2e Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 25 Jan 2011 17:08:00 -0300 Subject: [media] cx23885, altera-ci: enable all PID's less than 0x20 in hardware PID filter It takes too long time to scan due to low symbol rate PID's like PAT, PMT, CAT, NIT. For that matter we enabled permanently all PID's less 0x20 in hardware PID filter for NetUP Dual DVB-T/C CI RF card to combine rates. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/altera-ci.c b/drivers/media/video/cx23885/altera-ci.c index e5c0873..ad6cc68 100644 --- a/drivers/media/video/cx23885/altera-ci.c +++ b/drivers/media/video/cx23885/altera-ci.c @@ -521,7 +521,8 @@ static void altera_pid_control(struct netup_hw_pid_filter *pid_filt, struct fpga_internal *inter = pid_filt->internal; u8 store = 0; - if (pid == 0x2000) + /* pid 0-0x1f always enabled, don't touch them */ + if ((pid == 0x2000) || (pid < 0x20)) return; mutex_lock(&inter->fpga_mutex); @@ -567,8 +568,9 @@ static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt, netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1, ((i >> 8) & 0x03) | (pid_filt->nr << 2), 0); - - netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0); + /* pid 0-0x1f always enabled */ + netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, + (i > 3 ? store : 0), 0); } mutex_unlock(&inter->fpga_mutex); -- cgit v0.10.2 From 7ee9e64a3d7379fd443090880158374181f754cf Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 25 Jan 2011 18:25:39 -0300 Subject: [media] cx18: fix kernel oops when setting MPEG control before capturing The cxhdl->priv field was not set initially, only after capturing started. Signed-off-by: Hans Verkuil Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index b988ec6..321c1b7 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -734,6 +734,7 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI; cx->cxhdl.ops = &cx18_cxhdl_ops; cx->cxhdl.func = cx18_api_func; + cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; ret = cx2341x_handler_init(&cx->cxhdl, 50); if (ret) return ret; -- cgit v0.10.2 From 61ca1500c5ee04f2be34a4f58bb9baed0214b7a9 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Sun, 30 Jan 2011 16:33:01 -0300 Subject: [media] video/saa7164: Fix sparse warning: Using plain integer as NULL pointer This patch fixes the warning "Using plain integer as NULL pointer", generated by sparse, by replacing if (var == 0) with if (!var) after an allocation and all other offending 0s with NULL. KernelVersion: linus' tree-1f0324c Signed-off-by: Peter Huewe Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c index bd86d97..8a98ab6 100644 --- a/drivers/media/video/saa7164/saa7164-api.c +++ b/drivers/media/video/saa7164/saa7164-api.c @@ -743,7 +743,7 @@ int saa7164_api_configure_dif(struct saa7164_port *port, u32 std) int saa7164_api_initialize_dif(struct saa7164_port *port) { struct saa7164_dev *dev = port->dev; - struct saa7164_port *p = 0; + struct saa7164_port *p = NULL; int ret = -EINVAL; u32 std = 0; @@ -926,9 +926,9 @@ int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev, int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len) { - struct saa7164_port *tsport = 0; - struct saa7164_port *encport = 0; - struct saa7164_port *vbiport = 0; + struct saa7164_port *tsport = NULL; + struct saa7164_port *encport = NULL; + struct saa7164_port *vbiport = NULL; u32 idx, next_offset; int i; struct tmComResDescrHeader *hdr, *t; @@ -1340,7 +1340,7 @@ int saa7164_api_enum_subdevs(struct saa7164_dev *dev) /* Allocate enough storage for all of the descs */ buf = kzalloc(buflen, GFP_KERNEL); - if (buf == NULL) + if (!buf) return SAA_ERR_NO_RESOURCES; /* Retrieve them */ diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c index ddd2521..66696fa 100644 --- a/drivers/media/video/saa7164/saa7164-buffer.c +++ b/drivers/media/video/saa7164/saa7164-buffer.c @@ -93,7 +93,7 @@ struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port, u32 len) { struct tmHWStreamParameters *params = &port->hw_streamingparams; - struct saa7164_buffer *buf = 0; + struct saa7164_buffer *buf = NULL; struct saa7164_dev *dev = port->dev; int i; @@ -103,7 +103,7 @@ struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port, } buf = kzalloc(sizeof(struct saa7164_buffer), GFP_KERNEL); - if (buf == NULL) { + if (!buf) { log_warn("%s() SAA_ERR_NO_RESOURCES\n", __func__); goto ret; } @@ -157,7 +157,7 @@ fail2: fail1: kfree(buf); - buf = 0; + buf = NULL; ret: return buf; } @@ -289,14 +289,14 @@ struct saa7164_user_buffer *saa7164_buffer_alloc_user(struct saa7164_dev *dev, struct saa7164_user_buffer *buf; buf = kzalloc(sizeof(struct saa7164_user_buffer), GFP_KERNEL); - if (buf == 0) - return 0; + if (!buf) + return NULL; buf->data = kzalloc(len, GFP_KERNEL); - if (buf->data == 0) { + if (!buf->data) { kfree(buf); - return 0; + return NULL; } buf->actual_size = len; @@ -315,7 +315,7 @@ void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf) return; kfree(buf->data); - buf->data = 0; + buf->data = NULL; kfree(buf); } diff --git a/drivers/media/video/saa7164/saa7164-bus.c b/drivers/media/video/saa7164/saa7164-bus.c index b2b0d97..466e1b0 100644 --- a/drivers/media/video/saa7164/saa7164-bus.c +++ b/drivers/media/video/saa7164/saa7164-bus.c @@ -158,7 +158,7 @@ int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg, return SAA_ERR_BAD_PARAMETER; } - if ((msg->size > 0) && (buf == 0)) { + if ((msg->size > 0) && (buf == NULL)) { printk(KERN_ERR "%s() Missing message buffer\n", __func__); return SAA_ERR_BAD_PARAMETER; } @@ -315,7 +315,7 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, saa7164_bus_verify(dev); - if (msg == 0) + if (msg == NULL) return ret; if (msg->size > dev->bus.m_wMaxReqSize) { @@ -324,7 +324,7 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, return ret; } - if ((peekonly == 0) && (msg->size > 0) && (buf == 0)) { + if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) { printk(KERN_ERR "%s() Missing msg buf, size should be %d bytes\n", __func__, msg->size); @@ -392,7 +392,7 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__); saa7164_bus_dumpmsg(dev, msg, buf); - saa7164_bus_dumpmsg(dev, &msg_tmp, 0); + saa7164_bus_dumpmsg(dev, &msg_tmp, NULL); ret = SAA_ERR_INVALID_COMMAND; goto out; } diff --git a/drivers/media/video/saa7164/saa7164-cmd.c b/drivers/media/video/saa7164/saa7164-cmd.c index a97ae17..6a4c217 100644 --- a/drivers/media/video/saa7164/saa7164-cmd.c +++ b/drivers/media/video/saa7164/saa7164-cmd.c @@ -84,7 +84,7 @@ int saa7164_irq_dequeue(struct saa7164_dev *dev) { int ret = SAA_OK, i = 0; u32 timeout; - wait_queue_head_t *q = 0; + wait_queue_head_t *q = NULL; u8 tmp[512]; dprintk(DBGLVL_CMD, "%s()\n", __func__); @@ -137,7 +137,7 @@ int saa7164_cmd_dequeue(struct saa7164_dev *dev) int loop = 1; int ret; u32 timeout; - wait_queue_head_t *q = 0; + wait_queue_head_t *q = NULL; u8 tmp[512]; dprintk(DBGLVL_CMD, "%s()\n", __func__); @@ -261,7 +261,7 @@ out: */ int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno) { - wait_queue_head_t *q = 0; + wait_queue_head_t *q = NULL; int ret = SAA_BUS_TIMEOUT; unsigned long stamp; int r; @@ -357,7 +357,7 @@ int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command, "sel = 0x%x)\n", __func__, saa7164_unitid_name(dev, id), id, command, controlselector); - if ((size == 0) || (buf == 0)) { + if ((size == 0) || (buf == NULL)) { printk(KERN_ERR "%s() Invalid param\n", __func__); return SAA_ERR_BAD_PARAMETER; } @@ -538,7 +538,7 @@ int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command, /* Invalid */ dprintk(DBGLVL_CMD, "%s() Invalid\n", __func__); - ret = saa7164_bus_get(dev, presponse_t, 0, 0); + ret = saa7164_bus_get(dev, presponse_t, NULL, 0); if (ret != SAA_OK) { printk(KERN_ERR "get failed\n"); return ret; diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c index 58af67f..b813aec 100644 --- a/drivers/media/video/saa7164/saa7164-core.c +++ b/drivers/media/video/saa7164/saa7164-core.c @@ -277,8 +277,8 @@ static void saa7164_histogram_print(struct saa7164_port *port, static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr) { struct saa7164_dev *dev = port->dev; - struct saa7164_buffer *buf = 0; - struct saa7164_user_buffer *ubuf = 0; + struct saa7164_buffer *buf = NULL; + struct saa7164_user_buffer *ubuf = NULL; struct list_head *c, *n; int i = 0; u8 __iomem *p; @@ -649,7 +649,7 @@ static irqreturn_t saa7164_irq(int irq, void *dev_id) u32 intid, intstat[INT_SIZE/4]; int i, handled = 0, bit; - if (dev == 0) { + if (dev == NULL) { printk(KERN_ERR "%s() No device specified\n", __func__); handled = 0; goto out; @@ -945,7 +945,7 @@ static int get_resources(struct saa7164_dev *dev) static int saa7164_port_init(struct saa7164_dev *dev, int portnr) { - struct saa7164_port *port = 0; + struct saa7164_port *port = NULL; if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS)) BUG(); diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c index b305a01..f65eab6 100644 --- a/drivers/media/video/saa7164/saa7164-dvb.c +++ b/drivers/media/video/saa7164/saa7164-dvb.c @@ -309,8 +309,8 @@ static int dvb_register(struct saa7164_port *port) port->hw_streamingparams.pitch = 188; port->hw_streamingparams.linethreshold = 0; - port->hw_streamingparams.pagetablelistvirt = 0; - port->hw_streamingparams.pagetablelistphys = 0; + port->hw_streamingparams.pagetablelistvirt = NULL; + port->hw_streamingparams.pagetablelistphys = NULL; port->hw_streamingparams.numpagetables = 2 + ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE); diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c index 1838408..f9d5946 100644 --- a/drivers/media/video/saa7164/saa7164-encoder.c +++ b/drivers/media/video/saa7164/saa7164-encoder.c @@ -152,8 +152,8 @@ static int saa7164_encoder_buffers_alloc(struct saa7164_port *port) /* Init and establish defaults */ params->bitspersample = 8; params->linethreshold = 0; - params->pagetablelistvirt = 0; - params->pagetablelistphys = 0; + params->pagetablelistvirt = NULL; + params->pagetablelistphys = NULL; params->numpagetableentries = port->hwcfg.buffercount; /* Allocate the PCI resources, buffers (hard) */ @@ -1108,7 +1108,7 @@ static int fops_release(struct file *file) struct saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port) { - struct saa7164_user_buffer *ubuf = 0; + struct saa7164_user_buffer *ubuf = NULL; struct saa7164_dev *dev = port->dev; u32 crc; @@ -1443,7 +1443,7 @@ int saa7164_encoder_register(struct saa7164_port *port) port->v4l_device = saa7164_encoder_alloc(port, dev->pci, &saa7164_mpeg_template, "mpeg"); - if (port->v4l_device == NULL) { + if (!port->v4l_device) { printk(KERN_INFO "%s: can't allocate mpeg device\n", dev->name); result = -ENOMEM; diff --git a/drivers/media/video/saa7164/saa7164-fw.c b/drivers/media/video/saa7164/saa7164-fw.c index ebed6f7..b369300 100644 --- a/drivers/media/video/saa7164/saa7164-fw.c +++ b/drivers/media/video/saa7164/saa7164-fw.c @@ -88,7 +88,7 @@ int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize, "%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n", __func__, src, srcsize, dlflags, dst, dstsize); - if ((src == 0) || (dst == 0)) { + if ((src == NULL) || (dst == NULL)) { ret = -EIO; goto out; } diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c index 8abbe6d..9e5b01c 100644 --- a/drivers/media/video/saa7164/saa7164-vbi.c +++ b/drivers/media/video/saa7164/saa7164-vbi.c @@ -123,8 +123,8 @@ static int saa7164_vbi_buffers_alloc(struct saa7164_port *port) ((params->numberoflines * params->pitch) / PAGE_SIZE); params->bitspersample = 8; params->linethreshold = 0; - params->pagetablelistvirt = 0; - params->pagetablelistphys = 0; + params->pagetablelistvirt = NULL; + params->pagetablelistphys = NULL; params->numpagetableentries = port->hwcfg.buffercount; /* Allocate the PCI resources, buffers (hard) */ @@ -1054,7 +1054,7 @@ static int fops_release(struct file *file) struct saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port) { - struct saa7164_user_buffer *ubuf = 0; + struct saa7164_user_buffer *ubuf = NULL; struct saa7164_dev *dev = port->dev; u32 crc; @@ -1334,7 +1334,7 @@ int saa7164_vbi_register(struct saa7164_port *port) port->v4l_device = saa7164_vbi_alloc(port, dev->pci, &saa7164_vbi_template, "vbi"); - if (port->v4l_device == NULL) { + if (!port->v4l_device) { printk(KERN_INFO "%s: can't allocate vbi device\n", dev->name); result = -ENOMEM; -- cgit v0.10.2 From d1303567f7738c07c0e5fd22420a9a8e2f55a0c4 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 31 Jan 2011 09:16:09 -0300 Subject: [media] omap24xxcam: Fix compilation Add linux/sched.h because of missing declaration of TASK_NORMAL. This patch fixes the following error: drivers/media/video/omap24xxcam.c: In function 'omap24xxcam_vbq_complete': drivers/media/video/omap24xxcam.c:415: error: 'TASK_NORMAL' undeclared (first use in this function) drivers/media/video/omap24xxcam.c:415: error: (Each undeclared identifier is reported only once drivers/media/video/omap24xxcam.c:415: error: for each function it appears in.) Signed-off-by: Thomas Weber Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index 0175527..f6626e8 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From 724dcbfa9c24af5afdaa2b7e0449e20066d6555a Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Tue, 1 Feb 2011 05:25:19 -0300 Subject: [media] xc5000: add set_config and other Add one more radio input, usefull for tm6010 Add control output amplitude. Add set_config function for configure tuner when TV card hasn't dvb part. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index e3218c9..1e28f7d 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -65,7 +65,7 @@ struct xc5000_priv { }; /* Misc Defines */ -#define MAX_TV_STANDARD 23 +#define MAX_TV_STANDARD 24 #define XC_MAX_I2C_WRITE_LENGTH 64 /* Signal Types */ @@ -92,6 +92,8 @@ struct xc5000_priv { #define XREG_IF_OUT 0x05 #define XREG_SEEK_MODE 0x07 #define XREG_POWER_DOWN 0x0A /* Obsolete */ +/* Set the output amplitude - SIF for analog, DTVP/DTVN for digital */ +#define XREG_OUTPUT_AMP 0x0B #define XREG_SIGNALSOURCE 0x0D /* 0=Air, 1=Cable */ #define XREG_SMOOTHEDCVBS 0x0E #define XREG_XTALFREQ 0x0F @@ -173,6 +175,7 @@ struct XC_TV_STANDARD { #define DTV7 20 #define FM_Radio_INPUT2 21 #define FM_Radio_INPUT1 22 +#define FM_Radio_INPUT1_MONO 23 static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020}, @@ -197,7 +200,8 @@ static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { {"DTV7/8", 0x00C0, 0x801B}, {"DTV7", 0x00C0, 0x8007}, {"FM Radio-INPUT2", 0x9802, 0x9002}, - {"FM Radio-INPUT1", 0x0208, 0x9002} + {"FM Radio-INPUT1", 0x0208, 0x9002}, + {"FM Radio-INPUT1_MONO", 0x0278, 0x9002} }; static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe); @@ -732,6 +736,8 @@ static int xc5000_set_params(struct dvb_frontend *fe, return -EIO; } + xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a); + xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL); if (debug) @@ -836,6 +842,8 @@ tune_channel: return -EREMOTEIO; } + xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09); + xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG); if (debug) @@ -863,6 +871,8 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe, radio_input = FM_Radio_INPUT1; else if (priv->radio_input == XC5000_RADIO_FM2) radio_input = FM_Radio_INPUT2; + else if (priv->radio_input == XC5000_RADIO_FM1_MONO) + radio_input = FM_Radio_INPUT1_MONO; else { dprintk(1, "%s() unknown radio input %d\n", __func__, priv->radio_input); @@ -889,6 +899,12 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe, return -EREMOTEIO; } + if ((priv->radio_input == XC5000_RADIO_FM1) || + (priv->radio_input == XC5000_RADIO_FM2)) + xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09); + else if (priv->radio_input == XC5000_RADIO_FM1_MONO) + xc_write_reg(priv, XREG_OUTPUT_AMP, 0x06); + xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG); return 0; @@ -1039,6 +1055,23 @@ static int xc5000_release(struct dvb_frontend *fe) return 0; } +static int xc5000_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct xc5000_priv *priv = fe->tuner_priv; + struct xc5000_config *p = priv_cfg; + + dprintk(1, "%s()\n", __func__); + + if (p->if_khz) + priv->if_khz = p->if_khz; + + if (p->radio_input) + priv->radio_input = p->radio_input; + + return 0; +} + + static const struct dvb_tuner_ops xc5000_tuner_ops = { .info = { .name = "Xceive XC5000", @@ -1051,6 +1084,7 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = { .init = xc5000_init, .sleep = xc5000_sleep, + .set_config = xc5000_set_config, .set_params = xc5000_set_params, .set_analog_params = xc5000_set_analog_params, .get_frequency = xc5000_get_frequency, diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index 3756e73..e295745 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -40,6 +40,7 @@ struct xc5000_config { #define XC5000_RADIO_NOT_CONFIGURED 0 #define XC5000_RADIO_FM1 1 #define XC5000_RADIO_FM2 2 +#define XC5000_RADIO_FM1_MONO 3 /* For each bridge framework, when it attaches either analog or digital, * it has to store a reference back to its _core equivalent structure, -- cgit v0.10.2 From 6951803c2402d1af0e76df051cc9b117f504550f Mon Sep 17 00:00:00 2001 From: Lawrence Rust Date: Sun, 6 Feb 2011 17:46:12 -0300 Subject: [media] Add proper audio support for Nova-S Plus with wm8775 ADC This patch adds audio DMA capture and ALSA mixer elements for the line input jack of the Hauppauge Nova-S-plus DVB-S PCI card. The Nova-S-plus has a WM8775 ADC that is currently not detected. This patch enables this chip and exports volume, balance mute and ALC elements for ALSA mixer controls. [mchehab@redhat.com: Changed the patch to only talk with wm8775 if board info says so. Also, added platform_data support, to avoid changing the behaviour for other boards, and fixed CodingStyle] [awalls@md.metrocast.net: Changed patch to make the WM8775_GID defintion bridge driver private and let the bridge driver set the value of v4l2_subdev.grp_id.] -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Andy Walls diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 54b7fcd..a2d688e 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "cx88.h" #include "cx88-reg.h" @@ -577,6 +578,35 @@ static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol, return 0; } +static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); + struct cx88_core *core = chip->core; + struct v4l2_control client_ctl; + int left = value->value.integer.value[0]; + int right = value->value.integer.value[1]; + int v, b; + + memset(&client_ctl, 0, sizeof(client_ctl)); + + /* Pass volume & balance onto any WM8775 */ + if (left >= right) { + v = left << 10; + b = left ? (0x8000 * right) / left : 0x8000; + } else { + v = right << 10; + b = right ? 0xffff - (0x8000 * left) / right : 0x8000; + } + client_ctl.value = v; + client_ctl.id = V4L2_CID_AUDIO_VOLUME; + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); + + client_ctl.value = b; + client_ctl.id = V4L2_CID_AUDIO_BALANCE; + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); +} + /* OK - TODO: test it */ static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) @@ -587,25 +617,28 @@ static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, int changed = 0; u32 old; + if (core->board.audio_chip == V4L2_IDENT_WM8775) + snd_cx88_wm8775_volume_put(kcontrol, value); + left = value->value.integer.value[0] & 0x3f; right = value->value.integer.value[1] & 0x3f; b = right - left; if (b < 0) { - v = 0x3f - left; - b = (-b) | 0x40; + v = 0x3f - left; + b = (-b) | 0x40; } else { - v = 0x3f - right; + v = 0x3f - right; } /* Do we really know this will always be called with IRQs on? */ spin_lock_irq(&chip->reg_lock); old = cx_read(AUD_VOL_CTL); if (v != (old & 0x3f)) { - cx_write(AUD_VOL_CTL, (old & ~0x3f) | v); - changed = 1; + cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v); + changed = 1; } - if (cx_read(AUD_BAL_CTL) != b) { - cx_write(AUD_BAL_CTL, b); - changed = 1; + if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) { + cx_write(AUD_BAL_CTL, b); + changed = 1; } spin_unlock_irq(&chip->reg_lock); @@ -618,7 +651,7 @@ static const struct snd_kcontrol_new snd_cx88_volume = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, - .name = "Playback Volume", + .name = "Analog-TV Volume", .info = snd_cx88_volume_info, .get = snd_cx88_volume_get, .put = snd_cx88_volume_put, @@ -649,7 +682,17 @@ static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol, vol = cx_read(AUD_VOL_CTL); if (value->value.integer.value[0] != !(vol & bit)) { vol ^= bit; - cx_write(AUD_VOL_CTL, vol); + cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol); + /* Pass mute onto any WM8775 */ + if ((core->board.audio_chip == V4L2_IDENT_WM8775) && + ((1<<6) == bit)) { + struct v4l2_control client_ctl; + + memset(&client_ctl, 0, sizeof(client_ctl)); + client_ctl.value = 0 != (vol & bit); + client_ctl.id = V4L2_CID_AUDIO_MUTE; + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); + } ret = 1; } spin_unlock_irq(&chip->reg_lock); @@ -658,7 +701,7 @@ static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new snd_cx88_dac_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Playback Switch", + .name = "Audio-Out Switch", .info = snd_ctl_boolean_mono_info, .get = snd_cx88_switch_get, .put = snd_cx88_switch_put, @@ -667,13 +710,51 @@ static const struct snd_kcontrol_new snd_cx88_dac_switch = { static const struct snd_kcontrol_new snd_cx88_source_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Switch", + .name = "Analog-TV Switch", .info = snd_ctl_boolean_mono_info, .get = snd_cx88_switch_get, .put = snd_cx88_switch_put, .private_value = (1<<6), }; +static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); + struct cx88_core *core = chip->core; + struct v4l2_control client_ctl; + + memset(&client_ctl, 0, sizeof(client_ctl)); + client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; + call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl); + value->value.integer.value[0] = client_ctl.value ? 1 : 0; + + return 0; +} + +static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); + struct cx88_core *core = chip->core; + struct v4l2_control client_ctl; + + memset(&client_ctl, 0, sizeof(client_ctl)); + client_ctl.value = 0 != value->value.integer.value[0]; + client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); + + return 0; +} + +static struct snd_kcontrol_new snd_cx88_alc_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line-In ALC Switch", + .info = snd_ctl_boolean_mono_info, + .get = snd_cx88_alc_get, + .put = snd_cx88_alc_put, +}; + /**************************************************************************** Basic Flow for Sound Devices ****************************************************************************/ @@ -724,7 +805,8 @@ static void snd_cx88_dev_free(struct snd_card * card) static int devno; static int __devinit snd_cx88_create(struct snd_card *card, struct pci_dev *pci, - snd_cx88_card_t **rchip) + snd_cx88_card_t **rchip, + struct cx88_core **core_ptr) { snd_cx88_card_t *chip; struct cx88_core *core; @@ -750,7 +832,7 @@ static int __devinit snd_cx88_create(struct snd_card *card, if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) { dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); err = -EIO; - cx88_core_put(core,pci); + cx88_core_put(core, pci); return err; } @@ -786,6 +868,7 @@ static int __devinit snd_cx88_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev); *rchip = chip; + *core_ptr = core; return 0; } @@ -795,6 +878,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, { struct snd_card *card; snd_cx88_card_t *chip; + struct cx88_core *core; int err; if (devno >= SNDRV_CARDS) @@ -812,7 +896,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, card->private_free = snd_cx88_dev_free; - err = snd_cx88_create(card, pci, &chip); + err = snd_cx88_create(card, pci, &chip, &core); if (err < 0) goto error; @@ -830,6 +914,10 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, if (err < 0) goto error; + /* If there's a wm8775 then add a Line-In ALC switch */ + if (core->board.audio_chip == V4L2_IDENT_WM8775) + snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip)); + strcpy (card->driver, "CX88x"); sprintf(card->shortname, "Conexant CX%x", pci->device); sprintf(card->longname, "%s at %#llx", diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 8128b936..f735262 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -970,7 +970,8 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .audio_chip = V4L2_IDENT_WM8775, + .audio_chip = V4L2_IDENT_WM8775, + .i2sinputcntl = 2, .input = {{ .type = CX88_VMUX_DVB, .vmux = 0, diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index 08220de..770ec05 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -786,8 +786,12 @@ void cx88_set_tvaudio(struct cx88_core *core) break; case WW_I2SADC: set_audio_start(core, 0x01); - /* Slave/Philips/Autobaud */ - cx_write(AUD_I2SINPUTCNTL, 0); + /* + * Slave/Philips/Autobaud + * NB on Nova-S bit1 NPhilipsSony appears to be inverted: + * 0= Sony, 1=Philips + */ + cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl); /* Switch to "I2S ADC mode" */ cx_write(AUD_I2SCNTL, 0x1); set_audio_finish(core, EN_I2SIN_ENABLE); diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index f814886..287a41e 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -40,6 +40,7 @@ #include "cx88.h" #include #include +#include MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); @@ -989,6 +990,32 @@ int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl) ctl->value = c->v.minimum; if (ctl->value > c->v.maximum) ctl->value = c->v.maximum; + + /* Pass changes onto any WM8775 */ + if (core->board.audio_chip == V4L2_IDENT_WM8775) { + struct v4l2_control client_ctl; + memset(&client_ctl, 0, sizeof(client_ctl)); + client_ctl.id = ctl->id; + + switch (ctl->id) { + case V4L2_CID_AUDIO_MUTE: + client_ctl.value = ctl->value; + break; + case V4L2_CID_AUDIO_VOLUME: + client_ctl.value = (ctl->value) ? + (0x90 + ctl->value) << 8 : 0; + break; + case V4L2_CID_AUDIO_BALANCE: + client_ctl.value = ctl->value << 9; + break; + default: + client_ctl.id = 0; + break; + } + if (client_ctl.id) + call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); + } + mask=c->mask; switch (ctl->id) { case V4L2_CID_AUDIO_BALANCE: @@ -1526,7 +1553,9 @@ static int radio_queryctrl (struct file *file, void *priv, if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) return -EINVAL; - if (c->id == V4L2_CID_AUDIO_MUTE) { + if (c->id == V4L2_CID_AUDIO_MUTE || + c->id == V4L2_CID_AUDIO_VOLUME || + c->id == V4L2_CID_AUDIO_BALANCE) { for (i = 0; i < CX8800_CTLS; i++) { if (cx8800_ctls[i].v.id == c->id) break; @@ -1856,9 +1885,24 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* load and configure helper modules */ - if (core->board.audio_chip == V4L2_IDENT_WM8775) - v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, - "wm8775", 0x36 >> 1, NULL); + if (core->board.audio_chip == V4L2_IDENT_WM8775) { + struct i2c_board_info wm8775_info = { + .type = "wm8775", + .addr = 0x36 >> 1, + .platform_data = &core->wm8775_data, + }; + struct v4l2_subdev *sd; + + if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1) + core->wm8775_data.is_nova_s = true; + else + core->wm8775_data.is_nova_s = false; + + sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap, + &wm8775_info, NULL); + if (sd != NULL) + sd->grp_id = WM8775_GID; + } if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) { /* This probes for a tda9874 as is used on some diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index c9981e7..aaf7d12b 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -33,6 +33,7 @@ #include #include #include +#include #include "btcx-risc.h" #include "cx88-reg.h" @@ -273,6 +274,9 @@ struct cx88_board { enum cx88_board_type mpeg; unsigned int audio_chip; int num_frontends; + + /* Used for I2S devices */ + int i2sinputcntl; }; struct cx88_subid { @@ -379,6 +383,7 @@ struct cx88_core { /* I2C remote data */ struct IR_i2c_init_data init_data; + struct wm8775_platform_data wm8775_data; struct mutex lock; /* various v4l controls */ @@ -398,17 +403,21 @@ static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev) return container_of(v4l2_dev, struct cx88_core, v4l2_dev); } -#define call_all(core, o, f, args...) \ +#define WM8775_GID (1 << 0) + +#define call_hw(core, grpid, o, f, args...) \ do { \ if (!core->i2c_rc) { \ if (core->gate_ctrl) \ core->gate_ctrl(core, 1); \ - v4l2_device_call_all(&core->v4l2_dev, 0, o, f, ##args); \ + v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \ if (core->gate_ctrl) \ core->gate_ctrl(core, 0); \ } \ } while (0) +#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args) + struct cx8800_dev; struct cx8802_dev; diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index fe8ef64..9cedb1e 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -35,6 +35,7 @@ #include #include #include +#include MODULE_DESCRIPTION("wm8775 driver"); MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); @@ -50,10 +51,16 @@ enum { TOT_REGS }; +#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */ +#define ALC_EN 0x100 /* R17: ALC enable */ + struct wm8775_state { struct v4l2_subdev sd; struct v4l2_ctrl_handler hdl; struct v4l2_ctrl *mute; + struct v4l2_ctrl *vol; + struct v4l2_ctrl *bal; + struct v4l2_ctrl *loud; u8 input; /* Last selected input (0-0xf) */ }; @@ -85,6 +92,30 @@ static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) return -1; } +static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly) +{ + struct wm8775_state *state = to_state(sd); + u8 vol_l, vol_r; + int muted = 0 != state->mute->val; + u16 volume = (u16)state->vol->val; + u16 balance = (u16)state->bal->val; + + /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */ + vol_l = (min(65536 - balance, 32768) * volume) >> 23; + vol_r = (min(balance, (u16)32768) * volume) >> 23; + + /* Mute */ + if (muted || quietly) + wm8775_write(sd, R21, 0x0c0 | state->input); + + wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */ + wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */ + + /* Un-mute */ + if (!muted) + wm8775_write(sd, R21, state->input); +} + static int wm8775_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -102,25 +133,26 @@ static int wm8775_s_routing(struct v4l2_subdev *sd, state->input = input; if (!v4l2_ctrl_g_ctrl(state->mute)) return 0; - wm8775_write(sd, R21, 0x0c0); - wm8775_write(sd, R14, 0x1d4); - wm8775_write(sd, R15, 0x1d4); - wm8775_write(sd, R21, 0x100 + state->input); + if (!v4l2_ctrl_g_ctrl(state->vol)) + return 0; + if (!v4l2_ctrl_g_ctrl(state->bal)) + return 0; + wm8775_set_audio(sd, 1); return 0; } static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); - struct wm8775_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - wm8775_write(sd, R21, 0x0c0); - wm8775_write(sd, R14, 0x1d4); - wm8775_write(sd, R15, 0x1d4); - if (!ctrl->val) - wm8775_write(sd, R21, 0x100 + state->input); + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BALANCE: + wm8775_set_audio(sd, 0); + return 0; + case V4L2_CID_AUDIO_LOUDNESS: + wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD); return 0; } return -EINVAL; @@ -144,16 +176,7 @@ static int wm8775_log_status(struct v4l2_subdev *sd) static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) { - struct wm8775_state *state = to_state(sd); - - /* If I remove this, then it can happen that I have no - sound the first time I tune from static to a valid channel. - It's difficult to reproduce and is almost certainly related - to the zero cross detect circuit. */ - wm8775_write(sd, R21, 0x0c0); - wm8775_write(sd, R14, 0x1d4); - wm8775_write(sd, R15, 0x1d4); - wm8775_write(sd, R21, 0x100 + state->input); + wm8775_set_audio(sd, 0); return 0; } @@ -203,6 +226,13 @@ static int wm8775_probe(struct i2c_client *client, { struct wm8775_state *state; struct v4l2_subdev *sd; + int err; + bool is_nova_s = false; + + if (client->dev.platform_data) { + struct wm8775_platform_data *data = client->dev.platform_data; + is_nova_s = data->is_nova_s; + } /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -218,13 +248,18 @@ static int wm8775_probe(struct i2c_client *client, v4l2_i2c_subdev_init(sd, client, &wm8775_ops); state->input = 2; - v4l2_ctrl_handler_init(&state->hdl, 1); + v4l2_ctrl_handler_init(&state->hdl, 4); state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/ + state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768); + state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1); sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - + err = state->hdl.error; + if (err) { v4l2_ctrl_handler_free(&state->hdl); kfree(state); return err; @@ -236,29 +271,44 @@ static int wm8775_probe(struct i2c_client *client, wm8775_write(sd, R23, 0x000); /* Disable zero cross detect timeout */ wm8775_write(sd, R7, 0x000); - /* Left justified, 24-bit mode */ + /* HPF enable, left justified, 24-bit (Philips) mode */ wm8775_write(sd, R11, 0x021); /* Master mode, clock ratio 256fs */ wm8775_write(sd, R12, 0x102); /* Powered up */ wm8775_write(sd, R13, 0x000); - /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(sd, R14, 0x1d4); - /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(sd, R15, 0x1d4); - /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ - wm8775_write(sd, R16, 0x1bf); - /* Enable gain control, use zero cross detection, - ALC hold time 42.6 ms */ - wm8775_write(sd, R17, 0x185); + + if (!is_nova_s) { + /* ADC gain +2.5dB, enable zero cross */ + wm8775_write(sd, R14, 0x1d4); + /* ADC gain +2.5dB, enable zero cross */ + wm8775_write(sd, R15, 0x1d4); + /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ + wm8775_write(sd, R16, 0x1bf); + /* Enable gain control, use zero cross detection, + ALC hold time 42.6 ms */ + wm8775_write(sd, R17, 0x185); + } else { + /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */ + wm8775_write(sd, R16, 0x1bb); + /* Set ALC mode and hold time */ + wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD); + } /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ wm8775_write(sd, R18, 0x0a2); /* Enable noise gate, threshold -72dBfs */ wm8775_write(sd, R19, 0x005); - /* Transient window 4ms, lower PGA gain limit -1dB */ - wm8775_write(sd, R20, 0x07a); - /* LRBOTH = 1, use input 2. */ - wm8775_write(sd, R21, 0x102); + if (!is_nova_s) { + /* Transient window 4ms, lower PGA gain limit -1dB */ + wm8775_write(sd, R20, 0x07a); + /* LRBOTH = 1, use input 2. */ + wm8775_write(sd, R21, 0x102); + } else { + /* Transient window 4ms, ALC min gain -5dB */ + wm8775_write(sd, R20, 0x0fb); + + wm8775_set_audio(sd, 1); /* set volume/mute/mux */ + } return 0; } diff --git a/include/media/wm8775.h b/include/media/wm8775.h index 60739c5..d0e801a 100644 --- a/include/media/wm8775.h +++ b/include/media/wm8775.h @@ -32,4 +32,13 @@ #define WM8775_AIN3 4 #define WM8775_AIN4 8 + +struct wm8775_platform_data { + /* + * FIXME: Instead, we should parametrize the params + * that need different settings between ivtv, pvrusb2, and Nova-S + */ + bool is_nova_s; +}; + #endif -- cgit v0.10.2 From a0ea298d57153cf71f9f7628830dbba1a9a8a15d Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 1 Feb 2011 19:40:03 -0300 Subject: [media] ds3000: fill in demod init function Make some initializations in init, not in tune function Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index fc61d92..655f182 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -1065,10 +1065,6 @@ static int ds3000_tune(struct dvb_frontend *fe, /* Reset status register */ status = 0; /* Tune */ - /* TS2020 init */ - ds3000_tuner_writereg(state, 0x42, 0x73); - ds3000_tuner_writereg(state, 0x05, 0x01); - ds3000_tuner_writereg(state, 0x62, 0xf5); /* unknown */ ds3000_tuner_writereg(state, 0x07, 0x02); ds3000_tuner_writereg(state, 0x10, 0x00); @@ -1306,7 +1302,19 @@ static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) */ static int ds3000_initfe(struct dvb_frontend *fe) { + struct ds3000_state *state = fe->demodulator_priv; + int ret; + dprintk("%s()\n", __func__); + /* hard reset */ + ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08)); + msleep(1); + + /* TS2020 init */ + ds3000_tuner_writereg(state, 0x42, 0x73); + ds3000_tuner_writereg(state, 0x05, 0x01); + ds3000_tuner_writereg(state, 0x62, 0xf5); + return 0; } -- cgit v0.10.2 From 2a66bf370c2aca617fad8dd8fe6c276d6bb1b44c Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 1 Feb 2011 19:40:17 -0300 Subject: [media] ds3000: decrease mpeg clock output TeVii s480 works fine with that on DVB-S2 channels Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index 655f182..41af75a 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -1203,7 +1203,7 @@ static int ds3000_tune(struct dvb_frontend *fe, ds3000_writereg(state, ds3000_dvbs2_init_tab[i], ds3000_dvbs2_init_tab[i + 1]); - ds3000_writereg(state, 0xfe, 0x54); + ds3000_writereg(state, 0xfe, 0x98); break; default: return 1; -- cgit v0.10.2 From caa687c805332a384afd292e2b20e25a6282ed66 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 1 Feb 2011 19:40:25 -0300 Subject: [media] ds3000: loading firmware in bigger chunks Decrease firmware loading time. Before it is ~4000 i2c calls, now it is ~256 i2c calls to load ds3000 firmware. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index 41af75a..b2f0f4e 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -305,7 +305,7 @@ static int ds3000_writeFW(struct ds3000_state *state, int reg, struct i2c_msg msg; u8 *buf; - buf = kmalloc(3, GFP_KERNEL); + buf = kmalloc(33, GFP_KERNEL); if (buf == NULL) { printk(KERN_ERR "Unable to kmalloc\n"); ret = -ENOMEM; @@ -317,10 +317,10 @@ static int ds3000_writeFW(struct ds3000_state *state, int reg, msg.addr = state->config->demod_address; msg.flags = 0; msg.buf = buf; - msg.len = 3; + msg.len = 33; - for (i = 0; i < len; i += 2) { - memcpy(buf + 1, data + i, 2); + for (i = 0; i < len; i += 32) { + memcpy(buf + 1, data + i, 32); dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len); -- cgit v0.10.2 From b9bf2eafaad9c1ef02fb3db38c74568be601a43a Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 1 Feb 2011 19:40:36 -0300 Subject: [media] ds3000: don't load firmware during demod init Speed up tuning, as firmware is not necessary to load every attempt to tune Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index b2f0f4e..9933573 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -1029,14 +1029,6 @@ static int ds3000_tune(struct dvb_frontend *fe, dprintk("%s() ", __func__); - /* Load the firmware if required */ - ret = ds3000_firmware_ondemand(fe); - if (ret != 0) { - printk(KERN_ERR "%s: Unable initialise the firmware\n", - __func__); - return ret; - } - state->dnxt.delivery = c->modulation; state->dnxt.frequency = c->frequency; state->dnxt.rolloff = 2; /* fixme */ @@ -1314,6 +1306,12 @@ static int ds3000_initfe(struct dvb_frontend *fe) ds3000_tuner_writereg(state, 0x42, 0x73); ds3000_tuner_writereg(state, 0x05, 0x01); ds3000_tuner_writereg(state, 0x62, 0xf5); + /* Load the firmware if required */ + ret = ds3000_firmware_ondemand(fe); + if (ret != 0) { + printk(KERN_ERR "%s: Unable initialize firmware\n", __func__); + return ret; + } return 0; } -- cgit v0.10.2 From 1f65dd365831ff6836eeaf66c9d7bf1ae46ac4d2 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Wed, 2 Feb 2011 12:15:52 -0300 Subject: [media] tm6000: add new TV cards of Beholder Add two new TV cards of Beholder. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index 22a2222..bd99ca7 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -50,6 +50,8 @@ #define TM6010_BOARD_BEHOLD_VOYAGER 11 #define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12 #define TM6010_BOARD_TWINHAN_TU501 13 +#define TM6010_BOARD_BEHOLD_WANDER_LITE 14 +#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 #define TM6000_MAXBOARDS 16 static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; @@ -303,6 +305,40 @@ struct tm6000_board tm6000_boards[] = { .dvb_led = TM6010_GPIO_5, .ir = TM6010_GPIO_0, }, + }, + [TM6010_BOARD_BEHOLD_WANDER_LITE] = { + .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = { + .name = "Beholder Voyager Lite TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, } }; @@ -325,6 +361,8 @@ struct usb_device_id tm6000_id_table[] = { { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE }, + { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE }, { }, }; @@ -346,6 +384,8 @@ void tm6000_flash_led(struct tm6000_core *dev, u8 state) break; case TM6010_BOARD_BEHOLD_WANDER: case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); break; @@ -362,6 +402,8 @@ void tm6000_flash_led(struct tm6000_core *dev, u8 state) break; case TM6010_BOARD_BEHOLD_WANDER: case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); break; @@ -520,6 +562,7 @@ int tm6000_cards_setup(struct tm6000_core *dev) msleep(15); break; case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: /* Power led on (blue) */ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); msleep(15); @@ -530,6 +573,7 @@ int tm6000_cards_setup(struct tm6000_core *dev) msleep(15); break; case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: /* Power led on (blue) */ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); msleep(15); @@ -955,6 +999,8 @@ static void tm6000_usb_disconnect(struct usb_interface *interface) break; case TM6010_BOARD_BEHOLD_WANDER: case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: /* Power led off */ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); -- cgit v0.10.2 From 6ca000479fdb80b9059b69c67c82f8dc0d5f5d47 Mon Sep 17 00:00:00 2001 From: Holger Nelson Date: Thu, 3 Feb 2011 01:59:29 -0300 Subject: [media] tm6000: Add support for Terratec Grabster AV 150/250 MX This patch adds support for Terratec Grabster AV 150/250 MX. For now it is only possible to use composite input as switching inputs does not work. [mchehab@redhat.com: Fix merge conflicts and simplify board entries] Signed-off-by: Holger Nelson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index bd99ca7..290037e 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -52,6 +52,7 @@ #define TM6010_BOARD_TWINHAN_TU501 13 #define TM6010_BOARD_BEHOLD_WANDER_LITE 14 #define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 +#define TM5600_BOARD_TERRATEC_GRABSTER 16 #define TM6000_MAXBOARDS 16 static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; @@ -283,6 +284,11 @@ struct tm6000_board tm6000_boards[] = { }, .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, }, + [TM5600_BOARD_TERRATEC_GRABSTER] = { + .name = "Terratec Grabster AV 150/250 MX", + .type = TM5600, + .tuner_type = TUNER_ABSENT, + }, [TM6010_BOARD_TWINHAN_TU501] = { .name = "Twinhan TU501(704D1)", .tuner_type = TUNER_XC2028, /* has a XC3028 */ @@ -339,7 +345,7 @@ struct tm6000_board tm6000_boards[] = { .tuner_reset = TM6010_GPIO_0, .power_led = TM6010_GPIO_6, }, - } + }, }; /* table of devices that work with this driver */ @@ -357,6 +363,7 @@ struct usb_device_id tm6000_id_table[] = { { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER }, { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER }, { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, -- cgit v0.10.2 From 8aff8ba95155dff265de5ea6f5d5d92caa7bc728 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Thu, 17 Feb 2011 22:11:05 -0300 Subject: [media] tm6000: add radio support to the driver Changes: Add function tm6000_set_reg_mask for change some bits in regs. Very usefull, simplify some code with this function. Add control mute Add control volume Add control audio input MUX Add support radio Radio works well. TV works too Known bugs: The programm gnomeradio can't set freq for radio, it use old v4l API. Audio over USB works via arecord. The programm mplayer can set freq but no any audio mplayer -v -rawaudio rate=48000 radio://105.2/capture driver=v4l2:alsa:adevice=hw.1,0:amode=1:audiorate=48000:forceaudio:immediatemode=0 When start watch TV very shortly after radio the kernel crashed hardly. Didn't stop all USB URBs, need some time for stop. [mchehab@redhat.com: fix merge conflicts] Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/tm6000/tm6000-alsa.c b/drivers/staging/tm6000/tm6000-alsa.c index 184cc50..acb0317 100644 --- a/drivers/staging/tm6000/tm6000-alsa.c +++ b/drivers/staging/tm6000/tm6000-alsa.c @@ -76,14 +76,11 @@ MODULE_PARM_DESC(debug, "enable debug messages"); static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) { struct tm6000_core *core = chip->core; - int val; dprintk(1, "Starting audio DMA\n"); /* Enables audio */ - val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0); - val |= 0x20; - tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x40, 0x40); tm6000_set_audio_bitrate(core, 48000); @@ -98,13 +95,11 @@ static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) { struct tm6000_core *core = chip->core; - int val; + dprintk(1, "Stopping audio DMA\n"); - /* Enables audio */ - val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0); - val &= ~0x20; - tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + /* Disables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x00, 0x40); tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0); diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index 290037e..a356ba7 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -66,6 +66,8 @@ struct tm6000_board { char *name; struct tm6000_capabilities caps; + enum tm6000_inaudio aradio; + enum tm6000_inaudio avideo; enum tm6000_devtype type; /* variant of the chipset */ int tuner_type; /* type of the tuner */ @@ -230,6 +232,8 @@ struct tm6000_board tm6000_boards[] = { .tuner_addr = 0xc2 >> 1, .demod_addr = 0x1e >> 1, .type = TM6010, + .avideo = TM6000_AIP_SIF1, + .aradio = TM6000_AIP_LINE1, .caps = { .has_tuner = 1, .has_dvb = 1, @@ -248,6 +252,8 @@ struct tm6000_board tm6000_boards[] = { .tuner_type = TUNER_XC5000, .tuner_addr = 0xc2 >> 1, .type = TM6010, + .avideo = TM6000_AIP_SIF1, + .aradio = TM6000_AIP_LINE1, .caps = { .has_tuner = 1, .has_dvb = 0, @@ -693,13 +699,12 @@ static void tm6000_config_tuner(struct tm6000_core *dev) struct xc5000_config ctl = { .i2c_address = dev->tuner_addr, .if_khz = 4570, - .radio_input = XC5000_RADIO_FM1, + .radio_input = XC5000_RADIO_FM1_MONO, }; xc5000_cfg.tuner = TUNER_XC5000; xc5000_cfg.priv = &ctl; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc5000_cfg); } @@ -732,6 +737,8 @@ static int tm6000_init_dev(struct tm6000_core *dev) dev->caps = tm6000_boards[dev->model].caps; + dev->avideo = tm6000_boards[dev->model].avideo; + dev->aradio = tm6000_boards[dev->model].aradio; /* initialize hardware */ rc = tm6000_init(dev); if (rc < 0) diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/staging/tm6000/tm6000-core.c index 5162cd4..778e534 100644 --- a/drivers/staging/tm6000/tm6000-core.c +++ b/drivers/staging/tm6000/tm6000-core.c @@ -116,6 +116,29 @@ int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) } EXPORT_SYMBOL_GPL(tm6000_get_reg); +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask) +{ + int rc; + u8 buf[1]; + u8 new_index; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); + + if (rc < 0) + return rc; + + new_index = (buf[0] & ~mask) | (index & mask); + + if (new_index == index) + return 0; + + return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, new_index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg_mask); + int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) { int rc; @@ -245,17 +268,12 @@ int tm6000_init_analog_mode(struct tm6000_core *dev) struct v4l2_frequency f; if (dev->dev_type == TM6010) { - int val; - /* Enable video */ - val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0); - val |= 0x60; - tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); - val = tm6000_get_reg(dev, - TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0); - val &= ~0x40; - tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val); + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, + 0x60, 0x60); + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x00, 0x40); tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc); } else { @@ -471,6 +489,14 @@ struct reg_init tm6010_init_tab[] = { { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, + { TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00}, + { TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80}, + { TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a}, + { TM6010_REQ08_R0D_A_AMD_THRES, 0x40}, + { TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64}, + { TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20}, + { TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe}, + { TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01}, { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, { TM6010_REQ07_R3F_RESET, 0x01 }, @@ -591,51 +617,213 @@ int tm6000_init(struct tm6000_core *dev) int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) { - int val; + int val = 0; + u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + u8 areg_0a = 0x91; /* SIF 48KHz */ + + switch (bitrate) { + case 48000: + areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + areg_0a = 0x91; /* SIF 48KHz */ + dev->audio_bitrate = bitrate; + break; + case 32000: + areg_f0 = 0x00; /* ADC MCLK = 375 Fs */ + areg_0a = 0x90; /* SIF 32KHz */ + dev->audio_bitrate = bitrate; + break; + default: + return -EINVAL; + } + /* enable I2S, if we use sif or external I2S device */ if (dev->dev_type == TM6010) { - val = tm6000_get_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0); - if (val < 0) - return val; - val = (val & 0xf0) | 0x1; /* 48 kHz, not muted */ - val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, val); + val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a); if (val < 0) return val; - } - /* different reg's to set audio bitrate */ - if (dev->dev_type == TM6010) { - val = tm6000_get_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x0); + val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0xf0); if (val < 0) return val; } else { - val = tm6000_get_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x0); + val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_f0, 0xf0); if (val < 0) return val; } + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); - val &= 0x0f; /* Preserve the audio input control bits */ - switch (bitrate) { - case 44100: - val |= 0xd0; - dev->audio_bitrate = bitrate; +int tm6000_set_audio_input(struct tm6000_core *dev, enum tm6000_inaudio ainp) +{ + if (dev->dev_type == TM6010) { + /* Audio crossbar setting, default SIF1 */ + u8 areg_f0 = 0x03; + + switch (ainp) { + case TM6000_AIP_SIF1: + case TM6000_AIP_SIF2: + areg_f0 = 0x03; + break; + case TM6000_AIP_LINE1: + areg_f0 = 0x00; + break; + case TM6000_AIP_LINE2: + areg_f0 = 0x08; + break; + default: + return 0; + break; + } + /* Set audio input crossbar */ + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0x0f); + } else { + /* Audio setting, default LINE1 */ + u8 areg_eb = 0x00; + + switch (ainp) { + case TM6000_AIP_LINE1: + areg_eb = 0x00; + break; + case TM6000_AIP_LINE2: + areg_eb = 0x04; + break; + default: + return 0; + break; + } + /* Set audio input */ + tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_eb, 0x0f); + } + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_input); + +void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x08; + + tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08); +} + +void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x20; + + if (dev->dev_type == TM6010) { + tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, + mute_reg, 0x20); + } else { + tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, + mute_reg, 0x20); + } +} + +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute) +{ + enum tm6000_inaudio ainp; + + if (dev->radio) + ainp = dev->aradio; + else + ainp = dev->avideo; + + switch (ainp) { + case TM6000_AIP_SIF1: + case TM6000_AIP_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_mute_sif(dev, mute); + else { + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" + " SIF audio inputs. Please check the %s" + " configuration.\n", dev->name); + return -EINVAL; + } break; - case 48000: - val |= 0x60; - dev->audio_bitrate = bitrate; + case TM6000_AIP_LINE1: + case TM6000_AIP_LINE2: + tm6010_set_mute_adc(dev, mute); + break; + default: + return -EINVAL; break; } - if (dev->dev_type == TM6010) - val = tm6000_set_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - val); - else - val = tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, val); + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_tvaudio_set_mute); + +void tm6010_set_volume_sif(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; - return val; + vol_reg = vol & 0x0F; + + if (vol < 0) + vol_reg |= 0x40; + + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg); } -EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); + +void tm6010_set_volume_adc(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = (vol + 0x10) & 0x1f; + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg); + } else { + tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg); + tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg); + } +} + +void tm6000_set_volume(struct tm6000_core *dev, int vol) +{ + enum tm6000_inaudio ainp; + + if (dev->radio) { + ainp = dev->aradio; + vol += 8; /* Offset to 0 dB */ + } else + ainp = dev->avideo; + + switch (ainp) { + case TM6000_AIP_SIF1: + case TM6000_AIP_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_volume_sif(dev, vol); + else + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" + " SIF audio inputs. Please check the %s" + " configuration.\n", dev->name); + break; + case TM6000_AIP_LINE1: + case TM6000_AIP_LINE2: + tm6010_set_volume_adc(dev, vol); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(tm6000_set_volume); static LIST_HEAD(tm6000_devlist); static DEFINE_MUTEX(tm6000_devlist_mutex); diff --git a/drivers/staging/tm6000/tm6000-stds.c b/drivers/staging/tm6000/tm6000-stds.c index cc7b866..a4c07e5 100644 --- a/drivers/staging/tm6000/tm6000-stds.c +++ b/drivers/staging/tm6000/tm6000-stds.c @@ -952,6 +952,22 @@ static int tm6000_set_audio_std(struct tm6000_core *dev, uint8_t mono_flag = 0; /* No mono */ uint8_t nicam_flag = 0; /* No NICAM */ + if (dev->radio) { + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + return 0; + } + switch (std) { #if 0 case DK_MONO: @@ -984,20 +1000,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev, case EIAJ: areg_05 = 0x02; break; - case FM_RADIO: - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); - tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); - tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); - tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91); - tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe); - tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01); - tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); - return 0; - break; case I_NICAM: areg_05 = 0x08; nicam_flag = 1; @@ -1010,6 +1012,9 @@ static int tm6000_set_audio_std(struct tm6000_core *dev, areg_05 = 0x0a; nicam_flag = 1; break; + default: + /* do nothink */ + break; } #if 0 diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/staging/tm6000/tm6000-video.c index eb9b9f1..b550340 100644 --- a/drivers/staging/tm6000/tm6000-video.c +++ b/drivers/staging/tm6000/tm6000-video.c @@ -53,11 +53,17 @@ /* Declare static vars that will be used as parameters */ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ +static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ /* Debug level */ int tm6000_debug; EXPORT_SYMBOL_GPL(tm6000_debug); +static const struct v4l2_queryctrl no_ctrl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + /* supported controls */ static struct v4l2_queryctrl tm6000_qctrl[] = { { @@ -96,9 +102,26 @@ static struct v4l2_queryctrl tm6000_qctrl[] = { .step = 0x1, .default_value = 0, .flags = 0, + }, + /* --- audio --- */ + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = -15, + .maximum = 15, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, } }; +static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl); static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)]; static struct tm6000_fmt format[] = { @@ -117,6 +140,16 @@ static struct tm6000_fmt format[] = { } }; +static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) +{ + unsigned int i; + + for (i = 0; i < CTRLS; i++) + if (tm6000_qctrl[i].id == id) + return tm6000_qctrl+i; + return NULL; +} + /* ------------------------------------------------------------------ * DMA and thread functions * ------------------------------------------------------------------ @@ -199,13 +232,17 @@ static int copy_streams(u8 *data, unsigned long len, char *voutp = NULL; unsigned int linewidth; - /* get video buffer */ - get_next_buf(dma_q, &vbuf); - if (!vbuf) - return rc; - voutp = videobuf_to_vmalloc(&vbuf->vb); - if (!voutp) - return 0; + if (!dev->radio) { + /* get video buffer */ + get_next_buf(dma_q, &vbuf); + + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + + if (!voutp) + return 0; + } for (ptr = data; ptr < endp;) { if (!dev->isoc_ctl.cmd) { @@ -257,29 +294,31 @@ static int copy_streams(u8 *data, unsigned long len, */ switch (cmd) { case TM6000_URB_MSG_VIDEO: - if ((dev->isoc_ctl.vfield != field) && - (field == 1)) { + if (!dev->radio) { + if ((dev->isoc_ctl.vfield != field) && + (field == 1)) { /* Announces that a new buffer * were filled */ - buffer_filled(dev, dma_q, vbuf); - dprintk(dev, V4L2_DEBUG_ISOC, + buffer_filled(dev, dma_q, vbuf); + dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); - get_next_buf(dma_q, &vbuf); - if (!vbuf) - return rc; - voutp = videobuf_to_vmalloc(&vbuf->vb); - if (!voutp) - return rc; - memset(voutp, 0, vbuf->vb.size); - } - linewidth = vbuf->vb.width << 1; - pos = ((line << 1) - field - 1) * linewidth + - block * TM6000_URB_MSG_LEN; - /* Don't allow to write out of the buffer */ - if (pos + size > vbuf->vb.size) - cmd = TM6000_URB_MSG_ERR; - dev->isoc_ctl.vfield = field; + get_next_buf(dma_q, &vbuf); + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + if (!voutp) + return rc; + memset(voutp, 0, vbuf->vb.size); + } + linewidth = vbuf->vb.width << 1; + pos = ((line << 1) - field - 1) * + linewidth + block * TM6000_URB_MSG_LEN; + /* Don't allow to write out of the buffer */ + if (pos + size > vbuf->vb.size) + cmd = TM6000_URB_MSG_ERR; + dev->isoc_ctl.vfield = field; + } break; case TM6000_URB_MSG_VBI: break; @@ -537,7 +576,7 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) /* * Allocate URBs and start IRQ */ -static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize) +static int tm6000_prepare_isoc(struct tm6000_core *dev) { struct tm6000_dmaqueue *dma_q = &dev->vidq; int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; @@ -566,11 +605,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize) dev->isoc_ctl.max_pkt_size = size; - max_packets = (framesize + size - 1) / size; - - if (max_packets > TM6000_MAX_ISO_PACKETS) - max_packets = TM6000_MAX_ISO_PACKETS; - + max_packets = TM6000_MAX_ISO_PACKETS; sb_size = max_packets * size; dev->isoc_ctl.num_bufs = num_bufs; @@ -746,7 +781,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { - rc = tm6000_prepare_isoc(dev, buf->vb.size); + rc = tm6000_prepare_isoc(dev); if (rc < 0) goto fail; @@ -1143,6 +1178,12 @@ static int vidioc_g_ctrl(struct file *file, void *priv, case V4L2_CID_HUE: val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0); return 0; + case V4L2_CID_AUDIO_MUTE: + val = dev->ctl_mute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + val = dev->ctl_volume; + return 0; default: return -EINVAL; } @@ -1174,6 +1215,14 @@ static int vidioc_s_ctrl(struct file *file, void *priv, case V4L2_CID_HUE: tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); return 0; + case V4L2_CID_AUDIO_MUTE: + dev->ctl_mute = val; + tm6000_tvaudio_set_mute(dev, val); + return 0; + case V4L2_CID_AUDIO_VOLUME: + dev->ctl_volume = val; + tm6000_set_volume(dev, val); + return 0; } return -EINVAL; } @@ -1221,7 +1270,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, if (unlikely(UNSET == dev->tuner_type)) return -EINVAL; - f->type = V4L2_TUNER_ANALOG_TV; + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); @@ -1235,13 +1284,14 @@ static int vidioc_s_frequency(struct file *file, void *priv, struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; - if (unlikely(f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(UNSET == dev->tuner_type)) return -EINVAL; if (unlikely(f->tuner != 0)) return -EINVAL; + if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) + return -EINVAL; dev->freq = f->frequency; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); @@ -1249,6 +1299,122 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + strcpy(cap->driver, "tm6000"); + strlcpy(cap->card, dev->name, sizeof(dev->name)); + sprintf(cap->bus_info, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + cap->version = dev->dev_type; + cap->capabilities = V4L2_CAP_TUNER; + + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + memset(t, 0, sizeof(*t)); + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + if ((dev->aradio == TM6000_AIP_LINE1) || + (dev->aradio == TM6000_AIP_LINE2)) { + t->rxsubchans = V4L2_TUNER_SUB_MONO; + } + else { + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + } + + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + memset(a, 0, sizeof(*a)); + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *filp, void *priv, unsigned int i) +{ + return 0; +} + +static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + const struct v4l2_queryctrl *ctrl; + + if (c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + ctrl = ctrl_by_id(c->id); + *c = *ctrl; + } else + *c = no_ctrl; + + return 0; +} + /* ------------------------------------------------------------------ File operations for the device ------------------------------------------------------------------*/ @@ -1260,6 +1426,7 @@ static int tm6000_open(struct file *file) struct tm6000_fh *fh; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; int i, rc; + int radio = 0; printk(KERN_INFO "tm6000: open called (dev=%s)\n", video_device_node_name(vdev)); @@ -1267,6 +1434,17 @@ static int tm6000_open(struct file *file) dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", video_device_node_name(vdev)); + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + case VFL_TYPE_RADIO: + radio = 1; + break; + } /* If more than one user, mutex should be added */ dev->users++; @@ -1284,8 +1462,9 @@ static int tm6000_open(struct file *file) file->private_data = fh; fh->dev = dev; - - fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->radio = radio; + dev->radio = radio; + fh->type = type; dev->fourcc = format[0].fourcc; fh->fmt = format_by_fourcc(dev->fourcc); @@ -1322,6 +1501,19 @@ static int tm6000_open(struct file *file) V4L2_FIELD_INTERLACED, sizeof(struct tm6000_buffer), fh, &dev->lock); + if (fh->radio) { + dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); + tm6000_set_audio_input(dev, dev->aradio); + tm6000_set_volume(dev, dev->ctl_volume); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); + tm6000_prepare_isoc(dev); + tm6000_start_thread(dev); + } + else { + tm6000_set_audio_input(dev, dev->avideo); + tm6000_set_volume(dev, dev->ctl_volume); + } + return 0; } @@ -1445,6 +1637,36 @@ static struct video_device tm6000_template = { .current_norm = V4L2_STD_NTSC_M, }; +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_s_std = radio_s_std, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_input = radio_g_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +}; + +struct video_device tm6000_radio_template = { + .name = "tm6000", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, +}; + /* ----------------------------------------------------------------- * Initialization and module stuff * ------------------------------------------------------------------ @@ -1499,6 +1721,25 @@ int tm6000_v4l2_register(struct tm6000_core *dev) printk(KERN_INFO "%s: registered device %s\n", dev->name, video_device_node_name(dev->vfd)); + dev->radio_dev = vdev_init(dev, &tm6000_radio_template, + "radio"); + if (!dev->radio_dev) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + return ret; /* FIXME release resource */ + } + + ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr); + if (ret < 0) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + return ret; /* FIXME release resource */ + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->radio_dev)); + printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); return ret; } @@ -1507,6 +1748,14 @@ int tm6000_v4l2_unregister(struct tm6000_core *dev) { video_unregister_device(dev->vfd); + if (dev->radio_dev) { + if (video_is_registered(dev->radio_dev)) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + return 0; } diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/staging/tm6000/tm6000.h index bf11eee..ccd120f 100644 --- a/drivers/staging/tm6000/tm6000.h +++ b/drivers/staging/tm6000/tm6000.h @@ -53,6 +53,14 @@ enum tm6000_devtype { TM6010, }; +enum tm6000_inaudio { + TM6000_AIP_UNK = 0, + TM6000_AIP_SIF1, + TM6000_AIP_SIF2, + TM6000_AIP_LINE1, + TM6000_AIP_LINE2, +}; + /* ------------------------------------------------------------------ * Basic structures * ------------------------------------------------------------------ @@ -174,6 +182,8 @@ struct tm6000_core { char *ir_codes; + __u8 radio; + /* Demodulator configuration */ int demod_addr; /* demodulator address */ @@ -194,6 +204,7 @@ struct tm6000_core { bool is_res_read; struct video_device *vfd; + struct video_device *radio_dev; struct tm6000_dmaqueue vidq; struct v4l2_device v4l2_dev; @@ -203,6 +214,9 @@ struct tm6000_core { enum tm6000_mode mode; + int ctl_mute; /* audio */ + int ctl_volume; + /* DVB-T support */ struct tm6000_dvb *dvb; @@ -210,7 +224,8 @@ struct tm6000_core { struct snd_tm6000_card *adev; struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ atomic_t stream_started; /* stream should be running if true */ - + enum tm6000_inaudio avideo; + enum tm6000_inaudio aradio; struct tm6000_IR *ir; @@ -248,6 +263,7 @@ struct tm6000_ops { struct tm6000_fh { struct tm6000_core *dev; + unsigned int radio; /* video capture */ struct tm6000_fmt *fmt; @@ -276,12 +292,17 @@ int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask); int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep); int tm6000_init(struct tm6000_core *dev); int tm6000_init_analog_mode(struct tm6000_core *dev); int tm6000_init_digital_mode(struct tm6000_core *dev); int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate); +int tm6000_set_audio_input(struct tm6000_core *dev, enum tm6000_inaudio ainp); +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute); +void tm6000_set_volume(struct tm6000_core *dev, int vol); int tm6000_v4l2_register(struct tm6000_core *dev); int tm6000_v4l2_unregister(struct tm6000_core *dev); -- cgit v0.10.2 From 14ddc3188d50855ae2a419a6aced995e2834e5d4 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 09:23:33 -0300 Subject: [media] video: sn9c102: world-wirtable sysfs files Don't allow everybody to change video settings. Signed-off-by: Vasiliy Kulikov Acked-by: Mauro Carvalho Chehab Acked-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 84984f6..ce56a1c 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -1430,9 +1430,9 @@ static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, sn9c102_show_i2c_val, sn9c102_store_i2c_val); -static DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); -static DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue); -static DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red); +static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green); +static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue); +static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red); static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); -- cgit v0.10.2 From 92374e886c7518387e6816dedfe60fc7bdfa8fdd Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 6 Feb 2011 11:41:44 -0300 Subject: [media] firedtv: drop obsolete backend abstraction Since the drivers/ieee1394/ backend was removed from firedtv, its I/O no longer needs to be abstracted as exchangeable backend methods. Also, ieee1394 variants of module and device probe and removal are no longer there. Move module probe and removal into firedtv-fw.c where device probe and removal are implemented. Signed-off-by: Stefan Richter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index f0f1842..fc5ccd8 100644 --- a/drivers/media/dvb/firewire/firedtv-avc.c +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -241,8 +241,8 @@ static int avc_write(struct firedtv *fdtv) if (unlikely(avc_debug)) debug_fcp(fdtv->avc_data, fdtv->avc_data_length); - err = fdtv->backend->write(fdtv, FCP_COMMAND_REGISTER, - fdtv->avc_data, fdtv->avc_data_length); + err = fdtv_write(fdtv, FCP_COMMAND_REGISTER, + fdtv->avc_data, fdtv->avc_data_length); if (err) { dev_err(fdtv->device, "FCP command write failed\n"); @@ -1322,7 +1322,7 @@ static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data) mutex_lock(&fdtv->avc_mutex); - ret = fdtv->backend->read(fdtv, addr, data); + ret = fdtv_read(fdtv, addr, data); if (ret < 0) dev_err(fdtv->device, "CMP: read I/O error\n"); @@ -1340,7 +1340,7 @@ static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) /* data[] is stack-allocated and should not be DMA-mapped. */ memcpy(fdtv->avc_data, data, 8); - ret = fdtv->backend->lock(fdtv, addr, fdtv->avc_data); + ret = fdtv_lock(fdtv, addr, fdtv->avc_data); if (ret < 0) dev_err(fdtv->device, "CMP: lock I/O error\n"); else @@ -1405,10 +1405,7 @@ repeat: /* FIXME: this is for the worst case - optimize */ set_opcr_overhead_id(opcr, 0); - /* - * FIXME: allocate isochronous channel and bandwidth at IRM - * fdtv->backend->alloc_resources(fdtv, channels_mask, bw); - */ + /* FIXME: allocate isochronous channel and bandwidth at IRM */ } set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) + 1); @@ -1424,8 +1421,6 @@ repeat: /* * FIXME: if old_opcr.P2P_Connections > 0, * deallocate isochronous channel and bandwidth at IRM - * if (...) - * fdtv->backend->dealloc_resources(fdtv, channel, bw); */ if (++attempts < 6) /* arbitrary limit */ diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c index 86349db..fd8bbbf 100644 --- a/drivers/media/dvb/firewire/firedtv-dvb.c +++ b/drivers/media/dvb/firewire/firedtv-dvb.c @@ -14,14 +14,9 @@ #include #include #include -#include #include #include -#include -#include #include -#include -#include #include #include @@ -166,11 +161,11 @@ int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -int fdtv_dvb_register(struct firedtv *fdtv) +int fdtv_dvb_register(struct firedtv *fdtv, const char *name) { int err; - err = dvb_register_adapter(&fdtv->adapter, fdtv_model_names[fdtv->type], + err = dvb_register_adapter(&fdtv->adapter, name, THIS_MODULE, fdtv->device, adapter_nr); if (err < 0) goto fail_log; @@ -210,7 +205,7 @@ int fdtv_dvb_register(struct firedtv *fdtv) dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx); - fdtv_frontend_init(fdtv); + fdtv_frontend_init(fdtv, name); err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe); if (err) goto fail_net_release; @@ -248,122 +243,3 @@ void fdtv_dvb_unregister(struct firedtv *fdtv) dvb_dmx_release(&fdtv->demux); dvb_unregister_adapter(&fdtv->adapter); } - -const char *fdtv_model_names[] = { - [FIREDTV_UNKNOWN] = "unknown type", - [FIREDTV_DVB_S] = "FireDTV S/CI", - [FIREDTV_DVB_C] = "FireDTV C/CI", - [FIREDTV_DVB_T] = "FireDTV T/CI", - [FIREDTV_DVB_S2] = "FireDTV S2 ", -}; - -struct firedtv *fdtv_alloc(struct device *dev, - const struct firedtv_backend *backend, - const char *name, size_t name_len) -{ - struct firedtv *fdtv; - int i; - - fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); - if (!fdtv) - return NULL; - - dev_set_drvdata(dev, fdtv); - fdtv->device = dev; - fdtv->isochannel = -1; - fdtv->voltage = 0xff; - fdtv->tone = 0xff; - fdtv->backend = backend; - - mutex_init(&fdtv->avc_mutex); - init_waitqueue_head(&fdtv->avc_wait); - mutex_init(&fdtv->demux_mutex); - INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); - - for (i = ARRAY_SIZE(fdtv_model_names); --i; ) - if (strlen(fdtv_model_names[i]) <= name_len && - strncmp(name, fdtv_model_names[i], name_len) == 0) - break; - fdtv->type = i; - - return fdtv; -} - -#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ - IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION) - -#define DIGITAL_EVERYWHERE_OUI 0x001287 -#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d -#define AVC_SW_VERSION_ENTRY 0x010001 - -const struct ieee1394_device_id fdtv_id_table[] = { - { - /* FloppyDTV S/CI and FloppyDTV S2 */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000024, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - }, { - /* FloppyDTV T/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000025, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - }, { - /* FloppyDTV C/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000026, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - }, { - /* FireDTV S/CI and FloppyDTV S2 */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000034, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - }, { - /* FireDTV T/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000035, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - }, { - /* FireDTV C/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000036, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - }, {} -}; -MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); - -static int __init fdtv_init(void) -{ - int ret; - - ret = fdtv_fw_init(); - if (ret < 0) - return ret; - - return ret; -} - -static void __exit fdtv_exit(void) -{ - fdtv_fw_exit(); -} - -module_init(fdtv_init); -module_exit(fdtv_exit); - -MODULE_AUTHOR("Andreas Monitzer "); -MODULE_AUTHOR("Ben Backx "); -MODULE_DESCRIPTION("FireDTV DVB Driver"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("FireDTV DVB"); diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/dvb/firewire/firedtv-fe.c index d10920e..8748a61 100644 --- a/drivers/media/dvb/firewire/firedtv-fe.c +++ b/drivers/media/dvb/firewire/firedtv-fe.c @@ -36,14 +36,14 @@ static int fdtv_dvb_init(struct dvb_frontend *fe) return err; } - return fdtv->backend->start_iso(fdtv); + return fdtv_start_iso(fdtv); } static int fdtv_sleep(struct dvb_frontend *fe) { struct firedtv *fdtv = fe->sec_priv; - fdtv->backend->stop_iso(fdtv); + fdtv_stop_iso(fdtv); cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel); fdtv->isochannel = -1; return 0; @@ -165,7 +165,7 @@ static int fdtv_set_property(struct dvb_frontend *fe, struct dtv_property *tvp) return 0; } -void fdtv_frontend_init(struct firedtv *fdtv) +void fdtv_frontend_init(struct firedtv *fdtv, const char *name) { struct dvb_frontend_ops *ops = &fdtv->fe.ops; struct dvb_frontend_info *fi = &ops->info; @@ -266,7 +266,7 @@ void fdtv_frontend_init(struct firedtv *fdtv) dev_err(fdtv->device, "no frontend for model type %d\n", fdtv->type); } - strcpy(fi->name, fdtv_model_names[fdtv->type]); + strcpy(fi->name, name); fdtv->fe.dvb = &fdtv->adapter; fdtv->fe.sec_priv = fdtv; diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c index 7424b04..8022b74 100644 --- a/drivers/media/dvb/firewire/firedtv-fw.c +++ b/drivers/media/dvb/firewire/firedtv-fw.c @@ -9,11 +9,18 @@ #include #include #include +#include +#include +#include #include #include +#include #include +#include +#include #include +#include #include @@ -41,17 +48,17 @@ static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len, return rcode != RCODE_COMPLETE ? -EIO : 0; } -static int node_lock(struct firedtv *fdtv, u64 addr, void *data) +int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data) { return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP); } -static int node_read(struct firedtv *fdtv, u64 addr, void *data) +int fdtv_read(struct firedtv *fdtv, u64 addr, void *data) { return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST); } -static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) +int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) { return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST); } @@ -67,7 +74,7 @@ static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) #define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE) #define IRQ_INTERVAL 16 -struct firedtv_receive_context { +struct fdtv_ir_context { struct fw_iso_context *context; struct fw_iso_buffer buffer; int interrupt_packet; @@ -75,7 +82,7 @@ struct firedtv_receive_context { char *pages[N_PAGES]; }; -static int queue_iso(struct firedtv_receive_context *ctx, int index) +static int queue_iso(struct fdtv_ir_context *ctx, int index) { struct fw_iso_packet p; @@ -92,7 +99,7 @@ static void handle_iso(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *data) { struct firedtv *fdtv = data; - struct firedtv_receive_context *ctx = fdtv->backend_data; + struct fdtv_ir_context *ctx = fdtv->ir_context; __be32 *h, *h_end; int length, err, i = ctx->current_packet; char *p, *p_end; @@ -121,9 +128,9 @@ static void handle_iso(struct fw_iso_context *context, u32 cycle, ctx->current_packet = i; } -static int start_iso(struct firedtv *fdtv) +int fdtv_start_iso(struct firedtv *fdtv) { - struct firedtv_receive_context *ctx; + struct fdtv_ir_context *ctx; struct fw_device *device = device_of(fdtv); int i, err; @@ -161,7 +168,7 @@ static int start_iso(struct firedtv *fdtv) if (err) goto fail; - fdtv->backend_data = ctx; + fdtv->ir_context = ctx; return 0; fail: @@ -174,9 +181,9 @@ fail_free: return err; } -static void stop_iso(struct firedtv *fdtv) +void fdtv_stop_iso(struct firedtv *fdtv) { - struct firedtv_receive_context *ctx = fdtv->backend_data; + struct fdtv_ir_context *ctx = fdtv->ir_context; fw_iso_context_stop(ctx->context); fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card); @@ -184,14 +191,6 @@ static void stop_iso(struct firedtv *fdtv) kfree(ctx); } -static const struct firedtv_backend backend = { - .lock = node_lock, - .read = node_read, - .write = node_write, - .start_iso = start_iso, - .stop_iso = stop_iso, -}; - static void handle_fcp(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, int generation, unsigned long long offset, void *payload, size_t length, @@ -238,6 +237,14 @@ static const struct fw_address_region fcp_region = { .end = CSR_REGISTER_BASE + CSR_FCP_END, }; +static const char * const model_names[] = { + [FIREDTV_UNKNOWN] = "unknown type", + [FIREDTV_DVB_S] = "FireDTV S/CI", + [FIREDTV_DVB_C] = "FireDTV C/CI", + [FIREDTV_DVB_T] = "FireDTV T/CI", + [FIREDTV_DVB_S2] = "FireDTV S2 ", +}; + /* Adjust the template string if models with longer names appear. */ #define MAX_MODEL_NAME_LEN sizeof("FireDTV ????") @@ -245,15 +252,31 @@ static int node_probe(struct device *dev) { struct firedtv *fdtv; char name[MAX_MODEL_NAME_LEN]; - int name_len, err; - - name_len = fw_csr_string(fw_unit(dev)->directory, CSR_MODEL, - name, sizeof(name)); + int name_len, i, err; - fdtv = fdtv_alloc(dev, &backend, name, name_len >= 0 ? name_len : 0); + fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); if (!fdtv) return -ENOMEM; + dev_set_drvdata(dev, fdtv); + fdtv->device = dev; + fdtv->isochannel = -1; + fdtv->voltage = 0xff; + fdtv->tone = 0xff; + + mutex_init(&fdtv->avc_mutex); + init_waitqueue_head(&fdtv->avc_wait); + mutex_init(&fdtv->demux_mutex); + INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); + + name_len = fw_csr_string(fw_unit(dev)->directory, CSR_MODEL, + name, sizeof(name)); + for (i = ARRAY_SIZE(model_names); --i; ) + if (strlen(model_names[i]) <= name_len && + strncmp(name, model_names[i], name_len) == 0) + break; + fdtv->type = i; + err = fdtv_register_rc(fdtv, dev); if (err) goto fail_free; @@ -266,7 +289,7 @@ static int node_probe(struct device *dev) if (err) goto fail; - err = fdtv_dvb_register(fdtv); + err = fdtv_dvb_register(fdtv, model_names[fdtv->type]); if (err) goto fail; @@ -309,6 +332,60 @@ static void node_update(struct fw_unit *unit) fdtv->isochannel); } +#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ + IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION) + +#define DIGITAL_EVERYWHERE_OUI 0x001287 +#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d +#define AVC_SW_VERSION_ENTRY 0x010001 + +static const struct ieee1394_device_id fdtv_id_table[] = { + { + /* FloppyDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000024, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FloppyDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000025, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FloppyDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000026, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000034, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000035, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000036, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, {} +}; +MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); + static struct fw_driver fdtv_driver = { .driver = { .owner = THIS_MODULE, @@ -321,7 +398,7 @@ static struct fw_driver fdtv_driver = { .id_table = fdtv_id_table, }; -int __init fdtv_fw_init(void) +static int __init fdtv_init(void) { int ret; @@ -329,11 +406,24 @@ int __init fdtv_fw_init(void) if (ret < 0) return ret; - return driver_register(&fdtv_driver.driver); + ret = driver_register(&fdtv_driver.driver); + if (ret < 0) + fw_core_remove_address_handler(&fcp_handler); + + return ret; } -void fdtv_fw_exit(void) +static void __exit fdtv_exit(void) { driver_unregister(&fdtv_driver.driver); fw_core_remove_address_handler(&fcp_handler); } + +module_init(fdtv_init); +module_exit(fdtv_exit); + +MODULE_AUTHOR("Andreas Monitzer "); +MODULE_AUTHOR("Ben Backx "); +MODULE_DESCRIPTION("FireDTV DVB Driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("FireDTV DVB"); diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h index 4a87049..bd00b04 100644 --- a/drivers/media/dvb/firewire/firedtv.h +++ b/drivers/media/dvb/firewire/firedtv.h @@ -70,15 +70,7 @@ enum model_type { struct device; struct input_dev; -struct firedtv; - -struct firedtv_backend { - int (*lock)(struct firedtv *fdtv, u64 addr, void *data); - int (*read)(struct firedtv *fdtv, u64 addr, void *data); - int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len); - int (*start_iso)(struct firedtv *fdtv); - void (*stop_iso)(struct firedtv *fdtv); -}; +struct fdtv_ir_context; struct firedtv { struct device *device; @@ -104,12 +96,11 @@ struct firedtv { enum model_type type; char subunit; char isochannel; + struct fdtv_ir_context *ir_context; + fe_sec_voltage_t voltage; fe_sec_tone_mode_t tone; - const struct firedtv_backend *backend; - void *backend_data; - struct mutex demux_mutex; unsigned long channel_active; u16 channel_pid[16]; @@ -149,20 +140,18 @@ void fdtv_ca_release(struct firedtv *fdtv); /* firedtv-dvb.c */ int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed); int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed); -int fdtv_dvb_register(struct firedtv *fdtv); +int fdtv_dvb_register(struct firedtv *fdtv, const char *name); void fdtv_dvb_unregister(struct firedtv *fdtv); -struct firedtv *fdtv_alloc(struct device *dev, - const struct firedtv_backend *backend, - const char *name, size_t name_len); -extern const char *fdtv_model_names[]; -extern const struct ieee1394_device_id fdtv_id_table[]; /* firedtv-fe.c */ -void fdtv_frontend_init(struct firedtv *fdtv); +void fdtv_frontend_init(struct firedtv *fdtv, const char *name); /* firedtv-fw.c */ -int fdtv_fw_init(void); -void fdtv_fw_exit(void); +int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data); +int fdtv_read(struct firedtv *fdtv, u64 addr, void *data); +int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len); +int fdtv_start_iso(struct firedtv *fdtv); +void fdtv_stop_iso(struct firedtv *fdtv); /* firedtv-rc.c */ #ifdef CONFIG_DVB_FIREDTV_INPUT -- cgit v0.10.2 From 2b74334d12914eb10b8dac90300c3799a2115569 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 6 Feb 2011 12:29:51 -0300 Subject: [media] dvb_pll: DVB-S incorrect tune settings for dw2102/dm1105/cx88/opera1 This tuner PLL has missing initialisation settings resulting in haphazard tuning. The PLL LPF was set to just 22000 symbol rate. Basically, the module is a Sharp BS2F7HZ0194 (STV0299+IX2410) I have had problems implementing the PLL in a new driver and did not want to break the IX2410 out of the PLL. This applies to DW2102, DM1105, CX88 and OPERA1 drivers. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index 4d4d0bb..62a65ef 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -64,6 +64,7 @@ struct dvb_pll_desc { void (*set)(struct dvb_frontend *fe, u8 *buf, const struct dvb_frontend_parameters *params); u8 *initdata; + u8 *initdata2; u8 *sleepdata; int count; struct { @@ -321,26 +322,73 @@ static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { static void opera1_bw(struct dvb_frontend *fe, u8 *buf, const struct dvb_frontend_parameters *params) { - if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) - buf[2] |= 0x08; + struct dvb_pll_priv *priv = fe->tuner_priv; + u32 b_w = (params->u.qpsk.symbol_rate * 27) / 32000; + struct i2c_msg msg = { + .addr = priv->pll_i2c_address, + .flags = 0, + .buf = buf, + .len = 4 + }; + int result; + u8 lpf; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + result = i2c_transfer(priv->i2c, &msg, 1); + if (result != 1) + printk(KERN_ERR "%s: i2c_transfer failed:%d", + __func__, result); + + if (b_w <= 10000) + lpf = 0xc; + else if (b_w <= 12000) + lpf = 0x2; + else if (b_w <= 14000) + lpf = 0xa; + else if (b_w <= 16000) + lpf = 0x6; + else if (b_w <= 18000) + lpf = 0xe; + else if (b_w <= 20000) + lpf = 0x1; + else if (b_w <= 22000) + lpf = 0x9; + else if (b_w <= 24000) + lpf = 0x5; + else if (b_w <= 26000) + lpf = 0xd; + else if (b_w <= 28000) + lpf = 0x3; + else + lpf = 0xb; + buf[2] ^= 0x1c; /* Flip bits 3-5 */ + /* Set lpf */ + buf[2] |= ((lpf >> 2) & 0x3) << 3; + buf[3] |= (lpf & 0x3) << 2; + + return; } static struct dvb_pll_desc dvb_pll_opera1 = { .name = "Opera Tuner", .min = 900000, .max = 2250000, + .initdata = (u8[]){ 4, 0x08, 0xe5, 0xe1, 0x00 }, + .initdata2 = (u8[]){ 4, 0x08, 0xe5, 0xe5, 0x00 }, .iffreq= 0, .set = opera1_bw, .count = 8, .entries = { - { 1064000, 500, 0xe5, 0xc6 }, - { 1169000, 500, 0xe5, 0xe6 }, - { 1299000, 500, 0xe5, 0x24 }, - { 1444000, 500, 0xe5, 0x44 }, - { 1606000, 500, 0xe5, 0x64 }, - { 1777000, 500, 0xe5, 0x84 }, - { 1941000, 500, 0xe5, 0xa4 }, - { 2250000, 500, 0xe5, 0xc4 }, + { 1064000, 500, 0xf9, 0xc2 }, + { 1169000, 500, 0xf9, 0xe2 }, + { 1299000, 500, 0xf9, 0x20 }, + { 1444000, 500, 0xf9, 0x40 }, + { 1606000, 500, 0xf9, 0x60 }, + { 1777000, 500, 0xf9, 0x80 }, + { 1941000, 500, 0xf9, 0xa0 }, + { 2250000, 500, 0xf9, 0xc0 }, } }; @@ -648,8 +696,17 @@ static int dvb_pll_init(struct dvb_frontend *fe) int result; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + result = i2c_transfer(priv->i2c, &msg, 1); + if (result != 1) return result; + if (priv->pll_desc->initdata2) { + msg.buf = priv->pll_desc->initdata2 + 1; + msg.len = priv->pll_desc->initdata2[0]; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + result = i2c_transfer(priv->i2c, &msg, 1); + if (result != 1) + return result; } return 0; } -- cgit v0.10.2 From 9bf98321b4894b1e6787b4b93cd8fb81906a0f79 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 13 Feb 2011 17:31:55 -0300 Subject: [media] pvrusb2: Handle change of mode before handling change of video standard pvrusb2: Recognize and handle mode change before dealing with changes related to the video standard. Even though the video standard should only matter when in analog mode, doing this way is technically cleaner in case there's other stuff that might depend on both. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 66ad516..55074a8 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3165,6 +3165,19 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) struct pvr2_ctrl *cptr; int disruptive_change; + if (hdw->input_dirty && hdw->state_pathway_ok && + (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? + PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != + hdw->pathway_state)) { + /* Change of mode being asked for... */ + hdw->state_pathway_ok = 0; + trace_stbit("state_pathway_ok", hdw->state_pathway_ok); + } + if (!hdw->state_pathway_ok) { + /* Can't commit anything until pathway is ok. */ + return 0; + } + /* Handle some required side effects when the video standard is changed.... */ if (hdw->std_dirty) { @@ -3199,18 +3212,6 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } } - if (hdw->input_dirty && hdw->state_pathway_ok && - (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? - PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != - hdw->pathway_state)) { - /* Change of mode being asked for... */ - hdw->state_pathway_ok = 0; - trace_stbit("state_pathway_ok",hdw->state_pathway_ok); - } - if (!hdw->state_pathway_ok) { - /* Can't commit anything until pathway is ok. */ - return 0; - } /* The broadcast decoder can only scale down, so if * res_*_dirty && crop window < output format ==> enlarge crop. * -- cgit v0.10.2 From 4cfe3319a11613690c3cd755b890149d0532c833 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 13 Feb 2011 17:32:47 -0300 Subject: [media] pvrusb2: Minor cosmetic code tweak Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 55074a8..f83e5cf 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -5160,8 +5160,7 @@ void pvr2_hdw_status_poll(struct pvr2_hdw *hdw) using v4l2-subdev - therefore we can't support that AT ALL right now. (Of course, no sub-drivers seem to implement it either. But now it's a a chicken and egg problem...) */ - v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, - &hdw->tuner_signal_info); + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp); pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll" " type=%u strength=%u audio=0x%x cap=0x%x" " low=%u hi=%u", -- cgit v0.10.2 From 35fa5d4c48fbb02d637c3659f00331aa33f0f029 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 13 Feb 2011 17:34:33 -0300 Subject: [media] pvrusb2: Fix a few missing default control values, for cropping Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index f83e5cf..5e6437d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1114,6 +1114,7 @@ static const struct pvr2_ctl_info control_defs[] = { .internal_id = PVR2_CID_CROPW, .default_value = 720, DEFREF(cropw), + DEFINT(0, 864), .get_max_value = ctrl_cropw_max_get, .get_def_value = ctrl_get_cropcapdw, }, { @@ -1122,6 +1123,7 @@ static const struct pvr2_ctl_info control_defs[] = { .internal_id = PVR2_CID_CROPH, .default_value = 480, DEFREF(croph), + DEFINT(0, 576), .get_max_value = ctrl_croph_max_get, .get_def_value = ctrl_get_cropcapdh, }, { -- cgit v0.10.2 From 6188a0f151204d6f895822e8c0059b8951879a42 Mon Sep 17 00:00:00 2001 From: Servaas Vandenberghe Date: Sun, 13 Feb 2011 17:51:09 -0300 Subject: [media] pvrusb2: width and height maximum values pvrusb2: The origin of the capability bounds rectangle may differ from (left=0,top=0) so the calculation should use absolute coordinates, here below, or use relative coordinates like cropl_val-cap->bounds.left and cropt_val-cap->bounds.top. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 5e6437d..4885937 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -499,31 +499,35 @@ static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top) return 0; } -static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *val) +static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *width) { struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); + int stat, bleftend, cleft; + + stat = pvr2_hdw_check_cropcap(cptr->hdw); if (stat != 0) { return stat; } - *val = 0; - if (cap->bounds.width > cptr->hdw->cropl_val) { - *val = cap->bounds.width - cptr->hdw->cropl_val; - } + bleftend = cap->bounds.left+cap->bounds.width; + cleft = cptr->hdw->cropl_val; + + *width = cleft < bleftend ? bleftend-cleft : 0; return 0; } -static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *val) +static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height) { struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; - int stat = pvr2_hdw_check_cropcap(cptr->hdw); + int stat, btopend, ctop; + + stat = pvr2_hdw_check_cropcap(cptr->hdw); if (stat != 0) { return stat; } - *val = 0; - if (cap->bounds.height > cptr->hdw->cropt_val) { - *val = cap->bounds.height - cptr->hdw->cropt_val; - } + btopend = cap->bounds.top+cap->bounds.height; + ctop = cptr->hdw->cropt_val; + + *height = ctop < btopend ? btopend-ctop : 0; return 0; } -- cgit v0.10.2 From 058caa878476a09159e2fcc65463b9174c4fde8d Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 13 Feb 2011 17:53:07 -0300 Subject: [media] pvrusb2: Minor VBI tweak to help potential CC support pvrusb2: Declare closed-caption setup for line 21 - this is needed for sliced VBI capture support. (However none of that works right now anyway.) Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 4885937..d33dd61 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2033,6 +2033,8 @@ static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw) hdw->decoder_client_id); memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + fmt.fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt.fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, vbi, s_sliced_fmt, &fmt.fmt.sliced); } -- cgit v0.10.2 From 19a0a296f2df31c24f87fd3e7ee7f2640e49e5d4 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 13 Feb 2011 17:57:19 -0300 Subject: [media] pvrusb2: Use sysfs_attr_init() where appropriate Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 281806b..6ef1335 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -324,36 +324,45 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) } sfp->item_last = cip; + sysfs_attr_init(&cip->attr_name.attr); cip->attr_name.attr.name = "name"; cip->attr_name.attr.mode = S_IRUGO; cip->attr_name.show = show_name; + sysfs_attr_init(&cip->attr_type.attr); cip->attr_type.attr.name = "type"; cip->attr_type.attr.mode = S_IRUGO; cip->attr_type.show = show_type; + sysfs_attr_init(&cip->attr_min.attr); cip->attr_min.attr.name = "min_val"; cip->attr_min.attr.mode = S_IRUGO; cip->attr_min.show = show_min; + sysfs_attr_init(&cip->attr_max.attr); cip->attr_max.attr.name = "max_val"; cip->attr_max.attr.mode = S_IRUGO; cip->attr_max.show = show_max; + sysfs_attr_init(&cip->attr_def.attr); cip->attr_def.attr.name = "def_val"; cip->attr_def.attr.mode = S_IRUGO; cip->attr_def.show = show_def; + sysfs_attr_init(&cip->attr_val.attr); cip->attr_val.attr.name = "cur_val"; cip->attr_val.attr.mode = S_IRUGO; + sysfs_attr_init(&cip->attr_custom.attr); cip->attr_custom.attr.name = "custom_val"; cip->attr_custom.attr.mode = S_IRUGO; + sysfs_attr_init(&cip->attr_enum.attr); cip->attr_enum.attr.name = "enum_val"; cip->attr_enum.attr.mode = S_IRUGO; cip->attr_enum.show = show_enum; + sysfs_attr_init(&cip->attr_bits.attr); cip->attr_bits.attr.name = "bit_val"; cip->attr_bits.attr.mode = S_IRUGO; cip->attr_bits.show = show_bits; -- cgit v0.10.2 From ba0fd56a4fbb92999554a2e417924796c2ad2f8f Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 6 Feb 2011 17:49:02 -0300 Subject: [media] TTUSB DVB: ttusb_boot_dsp() needs to release_firmware() or it leaks memory In drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c:ttusb_boot_dsp() we need to call release_firmware(fw) before returning or we'll leak - no matter if we succeed or fail. Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 40625b2..cbe2f0d 100644 --- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -334,6 +334,7 @@ static int ttusb_boot_dsp(struct ttusb *ttusb) err = ttusb_cmd(ttusb, b, 4, 0); done: + release_firmware(fw); if (err) { dprintk("%s: usb_bulk_msg() failed, return value %i!\n", __func__, err); -- cgit v0.10.2 From feb16e9833ad06ac00f23c1abf1e7d649192e3f5 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:22 -0300 Subject: [media] dw2102: Extend keymap parameter for not used remote Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 2c307ba..21be0cc 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -87,7 +87,8 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer 4=rc(or-able))." /* keymaps */ static int ir_keymap; module_param_named(keymap, ir_keymap, int, 0644); -MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ..."); +MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ..." + " 256=none"); /* demod probe */ static int demod_probe = 1; @@ -1089,7 +1090,8 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) if ((ir_keymap > 0) && (ir_keymap <= ARRAY_SIZE(keys_tables))) { keymap = keys_tables[ir_keymap - 1].rc_keys ; keymap_size = keys_tables[ir_keymap - 1].rc_keys_size; - } + } else if (ir_keymap > ARRAY_SIZE(keys_tables)) + return 0; /* none */ *state = REMOTE_NO_KEY_PRESSED; if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) { -- cgit v0.10.2 From 195288da53e03c35f97df8fa08f48f5b625bfb4e Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:22 -0300 Subject: [media] dw2102: use separate firmwares for Prof 1100, TeVii S630, S660 Before this patch we can use only one card type from list due to sharing one firmware name. Now it's fixed. Also driver tries to attach only appropriate demod for this cards. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 21be0cc..f9affdd 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -886,7 +886,7 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d) return -EIO; } -static int s6x0_frontend_attach(struct dvb_usb_adapter *d) +static int zl100313_frontend_attach(struct dvb_usb_adapter *d) { d->fe = dvb_attach(mt312_attach, &zl313_config, &d->dev->i2c_adap); @@ -899,6 +899,11 @@ static int s6x0_frontend_attach(struct dvb_usb_adapter *d) } } + return -EIO; +} + +static int stv0288_frontend_attach(struct dvb_usb_adapter *d) +{ d->fe = dvb_attach(stv0288_attach, &earda_config, &d->dev->i2c_adap); if (d->fe != NULL) { @@ -910,6 +915,11 @@ static int s6x0_frontend_attach(struct dvb_usb_adapter *d) } } + return -EIO; +} + +static int ds3000_frontend_attach(struct dvb_usb_adapter *d) +{ d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, &d->dev->i2c_adap); if (d->fe != NULL) { @@ -1418,7 +1428,7 @@ static struct dvb_usb_device_properties s6x0_properties = { .read_mac_address = s6x0_read_mac_address, .adapter = { { - .frontend_attach = s6x0_frontend_attach, + .frontend_attach = zl100313_frontend_attach, .streaming_ctrl = NULL, .tuner_attach = NULL, .stream = { @@ -1433,23 +1443,29 @@ static struct dvb_usb_device_properties s6x0_properties = { }, } }, - .num_device_descs = 3, + .num_device_descs = 1, .devices = { {"TeVii S630 USB", {&dw2102_table[6], NULL}, {NULL}, }, - {"Prof 1100 USB ", - {&dw2102_table[7], NULL}, - {NULL}, - }, - {"TeVii S660 USB", - {&dw2102_table[8], NULL}, - {NULL}, - }, } }; +struct dvb_usb_device_properties *p1100; +static struct dvb_usb_device_description d1100 = { + "Prof 1100 USB ", + {&dw2102_table[7], NULL}, + {NULL}, +}; + +struct dvb_usb_device_properties *s660; +static struct dvb_usb_device_description d660 = { + "TeVii S660 USB", + {&dw2102_table[8], NULL}, + {NULL}, +}; + struct dvb_usb_device_properties *p7500; static struct dvb_usb_device_description d7500 = { "Prof 7500 USB DVB-S2", @@ -1460,14 +1476,38 @@ static struct dvb_usb_device_description d7500 = { static int dw2102_probe(struct usb_interface *intf, const struct usb_device_id *id) { + p1100 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL); + if (!p1100) + return -ENOMEM; + /* copy default structure */ + memcpy(p1100, &s6x0_properties, + sizeof(struct dvb_usb_device_properties)); + /* fill only different fields */ + p1100->firmware = "dvb-usb-p1100.fw"; + p1100->devices[0] = d1100; + p1100->rc.legacy.rc_map_table = rc_map_tbs_table; + p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); + p1100->adapter->frontend_attach = stv0288_frontend_attach; + + s660 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL); + if (!s660) { + kfree(p1100); + return -ENOMEM; + } + memcpy(s660, &s6x0_properties, + sizeof(struct dvb_usb_device_properties)); + s660->firmware = "dvb-usb-s660.fw"; + s660->devices[0] = d660; + s660->adapter->frontend_attach = ds3000_frontend_attach; p7500 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL); - if (!p7500) + if (!p7500) { + kfree(p1100); + kfree(s660); return -ENOMEM; - /* copy default structure */ + } memcpy(p7500, &s6x0_properties, sizeof(struct dvb_usb_device_properties)); - /* fill only different fields */ p7500->firmware = "dvb-usb-p7500.fw"; p7500->devices[0] = d7500; p7500->rc.legacy.rc_map_table = rc_map_tbs_table; @@ -1482,6 +1522,10 @@ static int dw2102_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &s6x0_properties, THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, p1100, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, s660, + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, p7500, THIS_MODULE, NULL, adapter_nr)) return 0; -- cgit v0.10.2 From d2ffc447cabb3a497d7f413403abd8793b348329 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:22 -0300 Subject: [media] dw2102: add support for Geniatech SU3000 USB DVB-S2 card The card uses ds3000 demod from Montage. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index f9affdd..461c96a 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1,7 +1,8 @@ /* DVB USB framework compliant Linux driver for the * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, * TeVii S600, S630, S650, -* Prof 1100, 7500 Cards +* Prof 1100, 7500, + * Geniatech SU3000 Cards * Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by) * * This program is free software; you can redistribute it and/or modify it @@ -67,6 +68,7 @@ #define REG_21_SYMBOLRATE_BYTE2 0x21 /* on my own*/ #define DW2102_VOLTAGE_CTRL (0x1800) +#define SU3000_STREAM_CTRL (0x1900) #define DW2102_RC_QUERY (0x1a00) #define err_str "did not find the firmware file. (%s) " \ @@ -78,6 +80,10 @@ struct rc_map_dvb_usb_table_table { int rc_keys_size; }; +struct su3000_state { + u8 initialized; +}; + /* debug */ static int dvb_usb_dw2102_debug; module_param_named(debug, dvb_usb_dw2102_debug, int, 0644); @@ -570,6 +576,70 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], return num; } +static int su3000_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + u8 obuf[0x40], ibuf[0x40]; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 1: + switch (msg[0].addr) { + case SU3000_STREAM_CTRL: + obuf[0] = msg[0].buf[0] + 0x36; + obuf[1] = 3; + obuf[2] = 0; + if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 0, 0) < 0) + err("i2c transfer failed."); + break; + case DW2102_RC_QUERY: + obuf[0] = 0x10; + if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 2, 0) < 0) + err("i2c transfer failed."); + msg[0].buf[1] = ibuf[0]; + msg[0].buf[0] = ibuf[1]; + break; + default: + /* always i2c write*/ + obuf[0] = 0x08; + obuf[1] = msg[0].addr; + obuf[2] = msg[0].len; + + memcpy(&obuf[3], msg[0].buf, msg[0].len); + + if (dvb_usb_generic_rw(d, obuf, msg[0].len + 3, + ibuf, 1, 0) < 0) + err("i2c transfer failed."); + + } + break; + case 2: + /* always i2c read */ + obuf[0] = 0x09; + obuf[1] = msg[0].len; + obuf[2] = msg[1].len; + obuf[3] = msg[0].addr; + memcpy(&obuf[4], msg[0].buf, msg[0].len); + + if (dvb_usb_generic_rw(d, obuf, msg[0].len + 4, + ibuf, msg[1].len + 1, 0) < 0) + err("i2c transfer failed."); + + memcpy(msg[1].buf, &ibuf[1], msg[1].len); + break; + default: + warn("more than 2 i2c messages at a time is not handled yet."); + break; + } + mutex_unlock(&d->i2c_mutex); + return num; +} + static u32 dw210x_i2c_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C; @@ -605,6 +675,11 @@ static struct i2c_algorithm s6x0_i2c_algo = { .functionality = dw210x_i2c_func, }; +static struct i2c_algorithm su3000_i2c_algo = { + .master_xfer = su3000_i2c_transfer, + .functionality = dw210x_i2c_func, +}; + static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) { int i; @@ -669,6 +744,82 @@ static int s6x0_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) return 0; }; +static int su3000_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + static u8 command_start[] = {0x00}; + static u8 command_stop[] = {0x01}; + struct i2c_msg msg = { + .addr = SU3000_STREAM_CTRL, + .flags = 0, + .buf = onoff ? command_start : command_stop, + .len = 1 + }; + + i2c_transfer(&adap->dev->i2c_adap, &msg, 1); + + return 0; +} + +static int su3000_power_ctrl(struct dvb_usb_device *d, int i) +{ + struct su3000_state *state = (struct su3000_state *)d->priv; + u8 obuf[] = {0xde, 0}; + + info("%s: %d, initialized %d\n", __func__, i, state->initialized); + + if (i && !state->initialized) { + state->initialized = 1; + /* reset board */ + dvb_usb_generic_rw(d, obuf, 2, NULL, 0, 0); + } + + return 0; +} + +static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + int i; + u8 obuf[] = { 0x1f, 0xf0 }; + u8 ibuf[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = 0x51, + .flags = 0, + .buf = obuf, + .len = 2, + }, { + .addr = 0x51, + .flags = I2C_M_RD, + .buf = ibuf, + .len = 1, + + } + }; + + for (i = 0; i < 6; i++) { + obuf[1] = 0xf0 + i; + if (i2c_transfer(&d->i2c_adap, msg, 2) != 2) + break; + else + mac[i] = ibuf[0]; + + debug_dump(mac, 6, printk); + } + + return 0; +} + +static int su3000_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + info("%s\n", __func__); + + *cold = 0; + return 0; +} + static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { static u8 command_13v[] = {0x00, 0x01}; @@ -774,6 +925,11 @@ static struct stv0900_config prof_7500_stv0900_config = { .tun1_type = 3, }; +static struct ds3000_config su3000_ds3000_config = { + .demod_address = 0x68, + .ci_mode = 1, +}; + static int dw2104_frontend_attach(struct dvb_usb_adapter *d) { struct dvb_tuner_ops *tuner_ops = NULL; @@ -944,6 +1100,43 @@ static int prof_7500_frontend_attach(struct dvb_usb_adapter *d) return 0; } +static int su3000_frontend_attach(struct dvb_usb_adapter *d) +{ + u8 obuf[3] = { 0xe, 0x80, 0 }; + u8 ibuf[] = { 0 }; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0xe; + obuf[1] = 0x83; + obuf[2] = 0; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0xe; + obuf[1] = 0x83; + obuf[2] = 1; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0x51; + + if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0) + err("command 0x51 transfer failed."); + + d->fe = dvb_attach(ds3000_attach, &su3000_ds3000_config, + &d->dev->i2c_adap); + if (d->fe == NULL) + return -EIO; + + info("Attached DS3000!\n"); + + return 0; +} + static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach(dvb_pll_attach, adap->fe, 0x60, @@ -1078,10 +1271,49 @@ static struct rc_map_table rc_map_tbs_table[] = { { 0xf89b, KEY_MODE } }; +static struct rc_map_table rc_map_su3000_table[] = { + { 0x25, KEY_POWER }, /* right-bottom Red */ + { 0x0a, KEY_MUTE }, /* -/-- */ + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x00, KEY_0 }, + { 0x20, KEY_UP }, /* CH+ */ + { 0x21, KEY_DOWN }, /* CH+ */ + { 0x12, KEY_VOLUMEUP }, /* Brightness Up */ + { 0x13, KEY_VOLUMEDOWN },/* Brightness Down */ + { 0x1f, KEY_RECORD }, + { 0x17, KEY_PLAY }, + { 0x16, KEY_PAUSE }, + { 0x0b, KEY_STOP }, + { 0x27, KEY_FASTFORWARD },/* >> */ + { 0x26, KEY_REWIND }, /* << */ + { 0x0d, KEY_OK }, /* Mute */ + { 0x11, KEY_LEFT }, /* VOL- */ + { 0x10, KEY_RIGHT }, /* VOL+ */ + { 0x29, KEY_BACK }, /* button under 9 */ + { 0x2c, KEY_MENU }, /* TTX */ + { 0x2b, KEY_EPG }, /* EPG */ + { 0x1e, KEY_RED }, /* OSD */ + { 0x0e, KEY_GREEN }, /* Window */ + { 0x2d, KEY_YELLOW }, /* button under << */ + { 0x0f, KEY_BLUE }, /* bottom yellow button */ + { 0x14, KEY_AUDIO }, /* Snapshot */ + { 0x38, KEY_TV }, /* TV/Radio */ + { 0x0c, KEY_ESC } /* upper Red buttton */ +}; + static struct rc_map_dvb_usb_table_table keys_tables[] = { { rc_map_dw210x_table, ARRAY_SIZE(rc_map_dw210x_table) }, { rc_map_tevii_table, ARRAY_SIZE(rc_map_tevii_table) }, { rc_map_tbs_table, ARRAY_SIZE(rc_map_tbs_table) }, + { rc_map_su3000_table, ARRAY_SIZE(rc_map_su3000_table) }, }; static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) @@ -1137,6 +1369,7 @@ static struct usb_device_id dw2102_table[] = { {USB_DEVICE(0x3011, USB_PID_PROF_1100)}, {USB_DEVICE(0x9022, USB_PID_TEVII_S660)}, {USB_DEVICE(0x3034, 0x7500)}, + {USB_DEVICE(0x1f4d, 0x3000)}, { } }; @@ -1473,6 +1706,51 @@ static struct dvb_usb_device_description d7500 = { {NULL}, }; +static struct dvb_usb_device_properties su3000_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .size_of_priv = sizeof(struct su3000_state), + .power_ctrl = su3000_power_ctrl, + .num_adapters = 1, + .identify_state = su3000_identify_state, + .i2c_algo = &su3000_i2c_algo, + + .rc.legacy = { + .rc_map_table = rc_map_su3000_table, + .rc_map_size = ARRAY_SIZE(rc_map_su3000_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, + + .read_mac_address = su3000_read_mac_address, + + .generic_bulk_ctrl_endpoint = 0x01, + + .adapter = { + { + .streaming_ctrl = su3000_streaming_ctrl, + .frontend_attach = su3000_frontend_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + } + } + }, + .num_device_descs = 1, + .devices = { + { "SU3000HD DVB-S USB2.0", + { &dw2102_table[10], NULL }, + { NULL }, + }, + } +}; + static int dw2102_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1527,7 +1805,9 @@ static int dw2102_probe(struct usb_interface *intf, 0 == dvb_usb_device_init(intf, s660, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, p7500, - THIS_MODULE, NULL, adapter_nr)) + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &su3000_properties, + THIS_MODULE, NULL, adapter_nr)) return 0; return -ENODEV; @@ -1561,6 +1841,7 @@ MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," " DVB-C 3101 USB2.0," " TeVii S600, S630, S650, S660 USB2.0," - " Prof 1100, 7500 USB2.0 devices"); + " Prof 1100, 7500 USB2.0," + " Geniatech SU3000 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index 9933573..3373890 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -509,6 +509,33 @@ static int ds3000_load_firmware(struct dvb_frontend *fe, return 0; } +static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct ds3000_state *state = fe->demodulator_priv; + u8 data; + + dprintk("%s(%d)\n", __func__, voltage); + + data = ds3000_readreg(state, 0xa2); + data |= 0x03; /* bit0 V/H, bit1 off/on */ + + switch (voltage) { + case SEC_VOLTAGE_18: + data &= ~0x03; + break; + case SEC_VOLTAGE_13: + data &= ~0x03; + data |= 0x01; + break; + case SEC_VOLTAGE_OFF: + break; + } + + ds3000_writereg(state, 0xa2, data); + + return 0; +} + static void ds3000_dump_registers(struct dvb_frontend *fe) { struct ds3000_state *state = fe->demodulator_priv; @@ -1255,6 +1282,18 @@ static int ds3000_tune(struct dvb_frontend *fe, ds3000_writereg(state, 0xfd, 0x42); ds3000_writereg(state, 0x08, 0x07);*/ + if (state->config->ci_mode) { + switch (c->delivery_system) { + case SYS_DVBS: + default: + ds3000_writereg(state, 0xfd, 0x80); + break; + case SYS_DVBS2: + ds3000_writereg(state, 0xfd, 0x01); + break; + } + } + /* ds3000 out of software reset */ ds3000_writereg(state, 0x00, 0x00); /* start ds3000 build-in uC */ @@ -1351,6 +1390,7 @@ static struct dvb_frontend_ops ds3000_ops = { .read_signal_strength = ds3000_read_signal_strength, .read_snr = ds3000_read_snr, .read_ucblocks = ds3000_read_ucblocks, + .set_voltage = ds3000_set_voltage, .set_tone = ds3000_set_tone, .diseqc_send_master_cmd = ds3000_send_diseqc_msg, .diseqc_send_burst = ds3000_diseqc_send_burst, diff --git a/drivers/media/dvb/frontends/ds3000.h b/drivers/media/dvb/frontends/ds3000.h index 67f6703..00e4e95 100644 --- a/drivers/media/dvb/frontends/ds3000.h +++ b/drivers/media/dvb/frontends/ds3000.h @@ -27,6 +27,7 @@ struct ds3000_config { /* the demodulator's i2c address */ u8 demod_address; + u8 ci_mode; }; #if defined(CONFIG_DVB_DS3000) || \ -- cgit v0.10.2 From 2adc591fb7764bf70f65a5923ff50c1c1b671c20 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:22 -0300 Subject: [media] dw2102: Add Terratec Cinergy S2 USB HD MarkusF wrote: I accidently got a Terratec Cinergy S2 USB HD (instead of a Terratec Cinergy S2 PCI HD). I found in the windows driver, that the driver is for the usb ids: 0CCD:00A8 1F4D:3000 The dvb-s2 usb adapter has the id 0CCD-00A8. So I patches your driver and added the usb id, where the 1F4D:3000 is and it worked (after I downloaded the firmware dvb-fe-ds3000.fw). w_scan found the channels and vdr is working fine. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 461c96a..57af4c1 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1370,6 +1370,7 @@ static struct usb_device_id dw2102_table[] = { {USB_DEVICE(0x9022, USB_PID_TEVII_S660)}, {USB_DEVICE(0x3034, 0x7500)}, {USB_DEVICE(0x1f4d, 0x3000)}, + {USB_DEVICE(USB_VID_TERRATEC, 0x00a8)}, { } }; @@ -1742,12 +1743,16 @@ static struct dvb_usb_device_properties su3000_properties = { } } }, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "SU3000HD DVB-S USB2.0", { &dw2102_table[10], NULL }, { NULL }, }, + { "Terratec Cinergy S2 USB HD", + { &dw2102_table[11], NULL }, + { NULL }, + }, } }; -- cgit v0.10.2 From fa8bae102fbbabe9ecc4381ace3b74a199684594 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:22 -0300 Subject: [media] dw2102: Prof 7500: Lock LED implemented Code changed for dw2102, stv0900 in order to achieve that. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 57af4c1..4ae4fb0 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -70,6 +70,7 @@ #define DW2102_VOLTAGE_CTRL (0x1800) #define SU3000_STREAM_CTRL (0x1900) #define DW2102_RC_QUERY (0x1a00) +#define DW2102_LED_CTRL (0x1b00) #define err_str "did not find the firmware file. (%s) " \ "Please see linux/Documentation/dvb/ for more details " \ @@ -509,6 +510,15 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf, 2, DW210X_WRITE_MSG); break; } + case (DW2102_LED_CTRL): { + u8 obuf[2]; + + obuf[0] = 5; + obuf[1] = msg[j].buf[0]; + ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, + obuf, 2, DW210X_WRITE_MSG); + break; + } /*case 0x55: cx24116 case 0x6a: stv0903 case 0x68: ds3000, stv0903 @@ -844,6 +854,24 @@ static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } +static void dw210x_led_ctrl(struct dvb_frontend *fe, int offon) +{ + static u8 led_off[] = { 0 }; + static u8 led_on[] = { 1 }; + struct i2c_msg msg = { + .addr = DW2102_LED_CTRL, + .flags = 0, + .buf = led_off, + .len = 1 + }; + struct dvb_usb_adapter *udev_adap = + (struct dvb_usb_adapter *)(fe->dvb->priv); + + if (offon) + msg.buf = led_on; + i2c_transfer(&udev_adap->dev->i2c_adap, &msg, 1); +} + static struct stv0299_config sharp_z0194a_config = { .demod_address = 0x68, .inittab = sharp_z0194a_inittab, @@ -923,6 +951,7 @@ static struct stv0900_config prof_7500_stv0900_config = { .tun1_adc = 0,/* 2 Vpp */ .path1_mode = 3, .tun1_type = 3, + .set_lock_led = dw210x_led_ctrl, }; static struct ds3000_config su3000_ds3000_config = { diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h index e3e35d1..91c7ee8 100644 --- a/drivers/media/dvb/frontends/stv0900.h +++ b/drivers/media/dvb/frontends/stv0900.h @@ -53,6 +53,8 @@ struct stv0900_config { u8 tun2_type; /* Set device param to start dma */ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); + /* Hook for Lock LED */ + void (*set_lock_led)(struct dvb_frontend *fe, int offon); }; #if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \ diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 4f5e7d3..1ca1c83 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -1660,8 +1660,13 @@ static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status) | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - } else + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 1); + } else { + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); dprintk("DEMOD LOCK FAIL\n"); + } return 0; } @@ -1831,6 +1836,9 @@ static void stv0900_release(struct dvb_frontend *fe) dprintk("%s\n", __func__); + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + if ((--(state->internal->dmds_used)) <= 0) { dprintk("%s: Actually removing\n", __func__); @@ -1842,6 +1850,18 @@ static void stv0900_release(struct dvb_frontend *fe) kfree(state); } +static int stv0900_sleep(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + + return 0; +} + static int stv0900_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { @@ -1876,6 +1896,7 @@ static struct dvb_frontend_ops stv0900_ops = { .release = stv0900_release, .init = stv0900_init, .get_frontend = stv0900_get_frontend, + .sleep = stv0900_sleep, .get_frontend_algo = stv0900_frontend_algo, .i2c_gate_ctrl = stv0900_i2c_gate_ctrl, .diseqc_send_master_cmd = stv0900_send_master_cmd, -- cgit v0.10.2 From 4e59df8f25950520cd6a8b16a39d48b5fa5aeeda Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:23 -0300 Subject: [media] dw2102: Prof 7500 remote fix Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 4ae4fb0..ea9a01c 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1118,12 +1118,17 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d) static int prof_7500_frontend_attach(struct dvb_usb_adapter *d) { + u8 obuf[] = {7, 1}; + d->fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config, &d->dev->i2c_adap, 0); if (d->fe == NULL) return -EIO; + d->fe->ops.set_voltage = dw210x_set_voltage; + dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); + info("Attached STV0900+STB6100A!\n"); return 0; -- cgit v0.10.2 From d3210b5f9618fc3d231caf39ffd32fda490e3ea5 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:23 -0300 Subject: [media] dw2102: Prof 1100 initialization fix RC keys structure for Prof 1100 already defined, so remove extra initialization. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index ea9a01c..4458199 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1464,11 +1464,6 @@ static int dw2102_load_firmware(struct usb_device *dev, } /* init registers */ switch (dev->descriptor.idProduct) { - case USB_PID_PROF_1100: - s6x0_properties.rc.legacy.rc_map_table = rc_map_tbs_table; - s6x0_properties.rc.legacy.rc_map_size = - ARRAY_SIZE(rc_map_tbs_table); - break; case USB_PID_TEVII_S650: dw2104_properties.rc.legacy.rc_map_table = rc_map_tevii_table; dw2104_properties.rc.legacy.rc_map_size = -- cgit v0.10.2 From 69519c3eff2a4a164351792d70666efa900b697e Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:23 -0300 Subject: [media] dw2102: unnecessary NULL's removed Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 4458199..d1ddf7e 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1546,8 +1546,6 @@ static struct dvb_usb_device_properties dw2102_properties = { .adapter = { { .frontend_attach = dw2102_frontend_attach, - .streaming_ctrl = NULL, - .tuner_attach = NULL, .stream = { .type = USB_BULK, .count = 8, @@ -1599,8 +1597,6 @@ static struct dvb_usb_device_properties dw2104_properties = { .adapter = { { .frontend_attach = dw2104_frontend_attach, - .streaming_ctrl = NULL, - /*.tuner_attach = dw2104_tuner_attach,*/ .stream = { .type = USB_BULK, .count = 8, @@ -1648,7 +1644,6 @@ static struct dvb_usb_device_properties dw3101_properties = { .adapter = { { .frontend_attach = dw3101_frontend_attach, - .streaming_ctrl = NULL, .tuner_attach = dw3101_tuner_attach, .stream = { .type = USB_BULK, @@ -1692,8 +1687,6 @@ static struct dvb_usb_device_properties s6x0_properties = { .adapter = { { .frontend_attach = zl100313_frontend_attach, - .streaming_ctrl = NULL, - .tuner_attach = NULL, .stream = { .type = USB_BULK, .count = 8, -- cgit v0.10.2 From 52c506e09cd624cbf7d21d9c16283f3e8b32f4bc Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:23 -0300 Subject: [media] dw2102: corrections for TeVii s660 LNB power control There are two TeVii s660 modifications with different LNB power control implementations. It is safe to call both methods for each card. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index d1ddf7e..0ab377e 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -85,6 +85,10 @@ struct su3000_state { u8 initialized; }; +struct s6x0_state { + int (*old_set_voltage)(struct dvb_frontend *f, fe_sec_voltage_t v); +}; + /* debug */ static int dvb_usb_dw2102_debug; module_param_named(debug, dvb_usb_dw2102_debug, int, 0644); @@ -854,6 +858,19 @@ static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } +static int s660_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct dvb_usb_adapter *d = + (struct dvb_usb_adapter *)(fe->dvb->priv); + struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; + + dw210x_set_voltage(fe, voltage); + if (st->old_set_voltage) + st->old_set_voltage(fe, voltage); + + return 0; +} + static void dw210x_led_ctrl(struct dvb_frontend *fe, int offon) { static u8 led_off[] = { 0 }; @@ -1105,15 +1122,19 @@ static int stv0288_frontend_attach(struct dvb_usb_adapter *d) static int ds3000_frontend_attach(struct dvb_usb_adapter *d) { + struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; + d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, &d->dev->i2c_adap); - if (d->fe != NULL) { - d->fe->ops.set_voltage = dw210x_set_voltage; - info("Attached ds3000+ds2020!\n"); - return 0; - } - return -EIO; + if (d->fe == NULL) + return -EIO; + + st->old_set_voltage = d->fe->ops.set_voltage; + d->fe->ops.set_voltage = s660_set_voltage; + info("Attached ds3000+ds2020!\n"); + + return 0; } static int prof_7500_frontend_attach(struct dvb_usb_adapter *d) @@ -1669,6 +1690,7 @@ static struct dvb_usb_device_properties dw3101_properties = { static struct dvb_usb_device_properties s6x0_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, + .size_of_priv = sizeof(struct s6x0_state), .firmware = "dvb-usb-s630.fw", .no_reconnect = 1, -- cgit v0.10.2 From 5a530cbecaa5a21d185e0549ea9a866483661f6f Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:23 -0300 Subject: [media] dw2102: fix TeVii s660 remote control Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 0ab377e..8931491 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -495,10 +495,10 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], for (j = 0; j < num; j++) { switch (msg[j].addr) { case (DW2102_RC_QUERY): { - u8 ibuf[4]; + u8 ibuf[5]; ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, - ibuf, 4, DW210X_READ_MSG); - memcpy(msg[j].buf, ibuf + 1, 2); + ibuf, 5, DW210X_READ_MSG); + memcpy(msg[j].buf, ibuf + 3, 2); break; } case (DW2102_VOLTAGE_CTRL): { @@ -1123,6 +1123,7 @@ static int stv0288_frontend_attach(struct dvb_usb_adapter *d) static int ds3000_frontend_attach(struct dvb_usb_adapter *d) { struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; + u8 obuf[] = {7, 1}; d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, &d->dev->i2c_adap); @@ -1132,6 +1133,9 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d) st->old_set_voltage = d->fe->ops.set_voltage; d->fe->ops.set_voltage = s660_set_voltage; + + dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); + info("Attached ds3000+ds2020!\n"); return 0; -- cgit v0.10.2 From e8f5055ac85fb68eeaa335f0dae14b31cc037f6d Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:23 -0300 Subject: [media] dw2102: add support for the TeVii S480 PCIe The card consists of a PCI-e to USB Host Controller and two TeVii S660 equivalent cards. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 8931491..c5a0921 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1,6 +1,6 @@ /* DVB USB framework compliant Linux driver for the * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, -* TeVii S600, S630, S650, +* TeVii S600, S630, S650, S480 * Prof 1100, 7500, * Geniatech SU3000 Cards * Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by) @@ -56,6 +56,14 @@ #define USB_PID_TEVII_S660 0xd660 #endif +#ifndef USB_PID_TEVII_S480_1 +#define USB_PID_TEVII_S480_1 0xd481 +#endif + +#ifndef USB_PID_TEVII_S480_2 +#define USB_PID_TEVII_S480_2 0xd482 +#endif + #ifndef USB_PID_PROF_1100 #define USB_PID_PROF_1100 0xb012 #endif @@ -1430,6 +1438,8 @@ static struct usb_device_id dw2102_table[] = { {USB_DEVICE(0x3034, 0x7500)}, {USB_DEVICE(0x1f4d, 0x3000)}, {USB_DEVICE(USB_VID_TERRATEC, 0x00a8)}, + {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)}, + {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)}, { } }; @@ -1748,6 +1758,18 @@ static struct dvb_usb_device_description d660 = { {NULL}, }; +static struct dvb_usb_device_description d480_1 = { + "TeVii S480.1 USB", + {&dw2102_table[12], NULL}, + {NULL}, +}; + +static struct dvb_usb_device_description d480_2 = { + "TeVii S480.2 USB", + {&dw2102_table[13], NULL}, + {NULL}, +}; + struct dvb_usb_device_properties *p7500; static struct dvb_usb_device_description d7500 = { "Prof 7500 USB DVB-S2", @@ -1828,7 +1850,10 @@ static int dw2102_probe(struct usb_interface *intf, memcpy(s660, &s6x0_properties, sizeof(struct dvb_usb_device_properties)); s660->firmware = "dvb-usb-s660.fw"; + s660->num_device_descs = 3; s660->devices[0] = d660; + s660->devices[1] = d480_1; + s660->devices[2] = d480_2; s660->adapter->frontend_attach = ds3000_frontend_attach; p7500 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL); @@ -1893,7 +1918,7 @@ module_exit(dw2102_module_exit); MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," " DVB-C 3101 USB2.0," - " TeVii S600, S630, S650, S660 USB2.0," + " TeVii S600, S630, S650, S660, S480," " Prof 1100, 7500 USB2.0," " Geniatech SU3000 devices"); MODULE_VERSION("0.1"); -- cgit v0.10.2 From 2f30fb49c347e8696a8784c57810b41d185707ec Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:23 -0300 Subject: [media] dw2102: Copyright, cards list updated Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index c5a0921..1a1b84c 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1,16 +1,16 @@ /* DVB USB framework compliant Linux driver for the -* DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, -* TeVii S600, S630, S650, S480 -* Prof 1100, 7500, + * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, + * TeVii S600, S630, S650, S660, S480, + * Prof 1100, 7500, * Geniatech SU3000 Cards -* Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by) -* -* 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, version 2. -* -* see Documentation/dvb/README.dvb-usb for more information -*/ + * Copyright (C) 2008-2011 Igor M. Liplianin (liplianin@me.by) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ #include "dw2102.h" #include "si21xx.h" #include "stv0299.h" -- cgit v0.10.2 From 18a73f36a2109563beee00f8be3f19f6c1fe2ff6 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:23 -0300 Subject: [media] ds3000: clean up in tune procedure Variable 'retune' does not make sense. Loop is not needed for only one try. Remove unnecessary dprintk's. Remove a lot of debug messages and delays. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index 3373890..882be67 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -536,25 +536,6 @@ static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } -static void ds3000_dump_registers(struct dvb_frontend *fe) -{ - struct ds3000_state *state = fe->demodulator_priv; - int x, y, reg = 0, val; - - for (y = 0; y < 16; y++) { - dprintk("%s: %02x: ", __func__, y); - for (x = 0; x < 16; x++) { - reg = (y << 4) + x; - val = ds3000_readreg(state, reg); - if (x != 15) - dprintk("%02x ", val); - else - dprintk("%02x\n", val); - } - } - dprintk("%s: -- DS3000 DUMP DONE --\n", __func__); -} - static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) { struct ds3000_state *state = fe->demodulator_priv; @@ -589,16 +570,6 @@ static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) return 0; } -#define FE_IS_TUNED (FE_HAS_SIGNAL + FE_HAS_LOCK) -static int ds3000_is_tuned(struct dvb_frontend *fe) -{ - fe_status_t tunerstat; - - ds3000_read_status(fe, &tunerstat); - - return ((tunerstat & FE_IS_TUNED) == FE_IS_TUNED); -} - /* read DS3000 BER value */ static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) { @@ -1049,7 +1020,7 @@ static int ds3000_tune(struct dvb_frontend *fe, struct ds3000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret = 0, retune, i; + int i; u8 status, mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf; u16 value, ndiv; u32 f3db; @@ -1072,252 +1043,235 @@ static int ds3000_tune(struct dvb_frontend *fe, /* discard the 'current' tuning parameters and prepare to tune */ ds3000_clone_params(fe); - retune = 1; /* try 1 times */ - dprintk("%s: retune = %d\n", __func__, retune); - dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); - dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); - dprintk("%s: FEC = %d \n", __func__, - state->dcur.fec); - dprintk("%s: Inversion = %d\n", __func__, state->dcur.inversion); - - do { - /* Reset status register */ - status = 0; - /* Tune */ - /* unknown */ - ds3000_tuner_writereg(state, 0x07, 0x02); - ds3000_tuner_writereg(state, 0x10, 0x00); - ds3000_tuner_writereg(state, 0x60, 0x79); - ds3000_tuner_writereg(state, 0x08, 0x01); - ds3000_tuner_writereg(state, 0x00, 0x01); - /* calculate and set freq divider */ - if (state->dcur.frequency < 1146000) { - ds3000_tuner_writereg(state, 0x10, 0x11); - ndiv = ((state->dcur.frequency * (6 + 8) * 4) + - (DS3000_XTAL_FREQ / 2)) / - DS3000_XTAL_FREQ - 1024; - } else { - ds3000_tuner_writereg(state, 0x10, 0x01); - ndiv = ((state->dcur.frequency * (6 + 8) * 2) + - (DS3000_XTAL_FREQ / 2)) / - DS3000_XTAL_FREQ - 1024; - } + /* Reset status register */ + status = 0; + /* Tune */ + /* unknown */ + ds3000_tuner_writereg(state, 0x07, 0x02); + ds3000_tuner_writereg(state, 0x10, 0x00); + ds3000_tuner_writereg(state, 0x60, 0x79); + ds3000_tuner_writereg(state, 0x08, 0x01); + ds3000_tuner_writereg(state, 0x00, 0x01); + /* calculate and set freq divider */ + if (state->dcur.frequency < 1146000) { + ds3000_tuner_writereg(state, 0x10, 0x11); + ndiv = ((state->dcur.frequency * (6 + 8) * 4) + + (DS3000_XTAL_FREQ / 2)) / + DS3000_XTAL_FREQ - 1024; + } else { + ds3000_tuner_writereg(state, 0x10, 0x01); + ndiv = ((state->dcur.frequency * (6 + 8) * 2) + + (DS3000_XTAL_FREQ / 2)) / + DS3000_XTAL_FREQ - 1024; + } - ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8); - ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff); - - /* set pll */ - ds3000_tuner_writereg(state, 0x03, 0x06); - ds3000_tuner_writereg(state, 0x51, 0x0f); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x10); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - /* unknown */ - ds3000_tuner_writereg(state, 0x51, 0x17); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x08); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - value = ds3000_tuner_readreg(state, 0x3d); - value &= 0x0f; - if ((value > 4) && (value < 15)) { - value -= 3; - if (value < 4) - value = 4; - value = ((value << 3) | 0x01) & 0x79; - } + ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8); + ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff); + + /* set pll */ + ds3000_tuner_writereg(state, 0x03, 0x06); + ds3000_tuner_writereg(state, 0x51, 0x0f); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x10); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + /* unknown */ + ds3000_tuner_writereg(state, 0x51, 0x17); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x08); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + value = ds3000_tuner_readreg(state, 0x3d); + value &= 0x0f; + if ((value > 4) && (value < 15)) { + value -= 3; + if (value < 4) + value = 4; + value = ((value << 3) | 0x01) & 0x79; + } - ds3000_tuner_writereg(state, 0x60, value); - ds3000_tuner_writereg(state, 0x51, 0x17); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x08); - ds3000_tuner_writereg(state, 0x50, 0x00); - - /* set low-pass filter period */ - ds3000_tuner_writereg(state, 0x04, 0x2e); - ds3000_tuner_writereg(state, 0x51, 0x1b); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x04); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - f3db = ((state->dcur.symbol_rate / 1000) << 2) / 5 + 2000; - if ((state->dcur.symbol_rate / 1000) < 5000) - f3db += 3000; - if (f3db < 7000) - f3db = 7000; - if (f3db > 40000) - f3db = 40000; - - /* set low-pass filter baseband */ - value = ds3000_tuner_readreg(state, 0x26); - mlpf = 0x2e * 207 / ((value << 1) + 151); - mlpf_max = mlpf * 135 / 100; - mlpf_min = mlpf * 78 / 100; - if (mlpf_max > 63) - mlpf_max = 63; - - /* rounded to the closest integer */ - nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2)) - / (2766 * DS3000_XTAL_FREQ); - if (nlpf > 23) - nlpf = 23; - if (nlpf < 1) - nlpf = 1; - - /* rounded to the closest integer */ + ds3000_tuner_writereg(state, 0x60, value); + ds3000_tuner_writereg(state, 0x51, 0x17); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x08); + ds3000_tuner_writereg(state, 0x50, 0x00); + + /* set low-pass filter period */ + ds3000_tuner_writereg(state, 0x04, 0x2e); + ds3000_tuner_writereg(state, 0x51, 0x1b); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x04); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + f3db = ((state->dcur.symbol_rate / 1000) << 2) / 5 + 2000; + if ((state->dcur.symbol_rate / 1000) < 5000) + f3db += 3000; + if (f3db < 7000) + f3db = 7000; + if (f3db > 40000) + f3db = 40000; + + /* set low-pass filter baseband */ + value = ds3000_tuner_readreg(state, 0x26); + mlpf = 0x2e * 207 / ((value << 1) + 151); + mlpf_max = mlpf * 135 / 100; + mlpf_min = mlpf * 78 / 100; + if (mlpf_max > 63) + mlpf_max = 63; + + /* rounded to the closest integer */ + nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2)) + / (2766 * DS3000_XTAL_FREQ); + if (nlpf > 23) + nlpf = 23; + if (nlpf < 1) + nlpf = 1; + + /* rounded to the closest integer */ + mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + + (1000 * f3db / 2)) / (1000 * f3db); + + if (mlpf_new < mlpf_min) { + nlpf++; mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + (1000 * f3db / 2)) / (1000 * f3db); + } - if (mlpf_new < mlpf_min) { - nlpf++; - mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); - } + if (mlpf_new > mlpf_max) + mlpf_new = mlpf_max; + + ds3000_tuner_writereg(state, 0x04, mlpf_new); + ds3000_tuner_writereg(state, 0x06, nlpf); + ds3000_tuner_writereg(state, 0x51, 0x1b); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x04); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + /* unknown */ + ds3000_tuner_writereg(state, 0x51, 0x1e); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x01); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(60); + + /* ds3000 global reset */ + ds3000_writereg(state, 0x07, 0x80); + ds3000_writereg(state, 0x07, 0x00); + /* ds3000 build-in uC reset */ + ds3000_writereg(state, 0xb2, 0x01); + /* ds3000 software reset */ + ds3000_writereg(state, 0x00, 0x01); - if (mlpf_new > mlpf_max) - mlpf_new = mlpf_max; - - ds3000_tuner_writereg(state, 0x04, mlpf_new); - ds3000_tuner_writereg(state, 0x06, nlpf); - ds3000_tuner_writereg(state, 0x51, 0x1b); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x04); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - /* unknown */ - ds3000_tuner_writereg(state, 0x51, 0x1e); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x01); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(60); - - /* ds3000 global reset */ - ds3000_writereg(state, 0x07, 0x80); - ds3000_writereg(state, 0x07, 0x00); - /* ds3000 build-in uC reset */ - ds3000_writereg(state, 0xb2, 0x01); - /* ds3000 software reset */ - ds3000_writereg(state, 0x00, 0x01); + switch (c->delivery_system) { + case SYS_DVBS: + /* initialise the demod in DVB-S mode */ + for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2) + ds3000_writereg(state, + ds3000_dvbs_init_tab[i], + ds3000_dvbs_init_tab[i + 1]); + value = ds3000_readreg(state, 0xfe); + value &= 0xc0; + value |= 0x1b; + ds3000_writereg(state, 0xfe, value); + break; + case SYS_DVBS2: + /* initialise the demod in DVB-S2 mode */ + for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2) + ds3000_writereg(state, + ds3000_dvbs2_init_tab[i], + ds3000_dvbs2_init_tab[i + 1]); + ds3000_writereg(state, 0xfe, 0x98); + break; + default: + return 1; + } + + /* enable 27MHz clock output */ + ds3000_writereg(state, 0x29, 0x80); + /* enable ac coupling */ + ds3000_writereg(state, 0x25, 0x8a); + + /* enhance symbol rate performance */ + if ((state->dcur.symbol_rate / 1000) <= 5000) { + value = 29777 / (state->dcur.symbol_rate / 1000) + 1; + if (value % 2 != 0) + value++; + ds3000_writereg(state, 0xc3, 0x0d); + ds3000_writereg(state, 0xc8, value); + ds3000_writereg(state, 0xc4, 0x10); + ds3000_writereg(state, 0xc7, 0x0e); + } else if ((state->dcur.symbol_rate / 1000) <= 10000) { + value = 92166 / (state->dcur.symbol_rate / 1000) + 1; + if (value % 2 != 0) + value++; + ds3000_writereg(state, 0xc3, 0x07); + ds3000_writereg(state, 0xc8, value); + ds3000_writereg(state, 0xc4, 0x09); + ds3000_writereg(state, 0xc7, 0x12); + } else if ((state->dcur.symbol_rate / 1000) <= 20000) { + value = 64516 / (state->dcur.symbol_rate / 1000) + 1; + ds3000_writereg(state, 0xc3, value); + ds3000_writereg(state, 0xc8, 0x0e); + ds3000_writereg(state, 0xc4, 0x07); + ds3000_writereg(state, 0xc7, 0x18); + } else { + value = 129032 / (state->dcur.symbol_rate / 1000) + 1; + ds3000_writereg(state, 0xc3, value); + ds3000_writereg(state, 0xc8, 0x0a); + ds3000_writereg(state, 0xc4, 0x05); + ds3000_writereg(state, 0xc7, 0x24); + } + + /* normalized symbol rate rounded to the closest integer */ + value = (((state->dcur.symbol_rate / 1000) << 16) + + (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE; + ds3000_writereg(state, 0x61, value & 0x00ff); + ds3000_writereg(state, 0x62, (value & 0xff00) >> 8); + + /* co-channel interference cancellation disabled */ + ds3000_writereg(state, 0x56, 0x00); + + /* equalizer disabled */ + ds3000_writereg(state, 0x76, 0x00); + /*ds3000_writereg(state, 0x08, 0x03); + ds3000_writereg(state, 0xfd, 0x22); + ds3000_writereg(state, 0x08, 0x07); + ds3000_writereg(state, 0xfd, 0x42); + ds3000_writereg(state, 0x08, 0x07);*/ + + if (state->config->ci_mode) { switch (c->delivery_system) { case SYS_DVBS: - /* initialise the demod in DVB-S mode */ - for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2) - ds3000_writereg(state, - ds3000_dvbs_init_tab[i], - ds3000_dvbs_init_tab[i + 1]); - value = ds3000_readreg(state, 0xfe); - value &= 0xc0; - value |= 0x1b; - ds3000_writereg(state, 0xfe, value); - break; + default: + ds3000_writereg(state, 0xfd, 0x80); + break; case SYS_DVBS2: - /* initialise the demod in DVB-S2 mode */ - for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2) - ds3000_writereg(state, - ds3000_dvbs2_init_tab[i], - ds3000_dvbs2_init_tab[i + 1]); - ds3000_writereg(state, 0xfe, 0x98); + ds3000_writereg(state, 0xfd, 0x01); break; - default: - return 1; - } - - /* enable 27MHz clock output */ - ds3000_writereg(state, 0x29, 0x80); - /* enable ac coupling */ - ds3000_writereg(state, 0x25, 0x8a); - - /* enhance symbol rate performance */ - if ((state->dcur.symbol_rate / 1000) <= 5000) { - value = 29777 / (state->dcur.symbol_rate / 1000) + 1; - if (value % 2 != 0) - value++; - ds3000_writereg(state, 0xc3, 0x0d); - ds3000_writereg(state, 0xc8, value); - ds3000_writereg(state, 0xc4, 0x10); - ds3000_writereg(state, 0xc7, 0x0e); - } else if ((state->dcur.symbol_rate / 1000) <= 10000) { - value = 92166 / (state->dcur.symbol_rate / 1000) + 1; - if (value % 2 != 0) - value++; - ds3000_writereg(state, 0xc3, 0x07); - ds3000_writereg(state, 0xc8, value); - ds3000_writereg(state, 0xc4, 0x09); - ds3000_writereg(state, 0xc7, 0x12); - } else if ((state->dcur.symbol_rate / 1000) <= 20000) { - value = 64516 / (state->dcur.symbol_rate / 1000) + 1; - ds3000_writereg(state, 0xc3, value); - ds3000_writereg(state, 0xc8, 0x0e); - ds3000_writereg(state, 0xc4, 0x07); - ds3000_writereg(state, 0xc7, 0x18); - } else { - value = 129032 / (state->dcur.symbol_rate / 1000) + 1; - ds3000_writereg(state, 0xc3, value); - ds3000_writereg(state, 0xc8, 0x0a); - ds3000_writereg(state, 0xc4, 0x05); - ds3000_writereg(state, 0xc7, 0x24); } + } - /* normalized symbol rate rounded to the closest integer */ - value = (((state->dcur.symbol_rate / 1000) << 16) + - (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE; - ds3000_writereg(state, 0x61, value & 0x00ff); - ds3000_writereg(state, 0x62, (value & 0xff00) >> 8); - - /* co-channel interference cancellation disabled */ - ds3000_writereg(state, 0x56, 0x00); - - /* equalizer disabled */ - ds3000_writereg(state, 0x76, 0x00); - - /*ds3000_writereg(state, 0x08, 0x03); - ds3000_writereg(state, 0xfd, 0x22); - ds3000_writereg(state, 0x08, 0x07); - ds3000_writereg(state, 0xfd, 0x42); - ds3000_writereg(state, 0x08, 0x07);*/ - - if (state->config->ci_mode) { - switch (c->delivery_system) { - case SYS_DVBS: - default: - ds3000_writereg(state, 0xfd, 0x80); - break; - case SYS_DVBS2: - ds3000_writereg(state, 0xfd, 0x01); - break; - } - } + /* ds3000 out of software reset */ + ds3000_writereg(state, 0x00, 0x00); + /* start ds3000 build-in uC */ + ds3000_writereg(state, 0xb2, 0x00); - /* ds3000 out of software reset */ - ds3000_writereg(state, 0x00, 0x00); - /* start ds3000 build-in uC */ - ds3000_writereg(state, 0xb2, 0x00); - - /* TODO: calculate and set carrier offset */ - - /* wait before retrying */ - for (i = 0; i < 30 ; i++) { - if (ds3000_is_tuned(fe)) { - dprintk("%s: Tuned\n", __func__); - ds3000_dump_registers(fe); - goto tuned; - } - msleep(1); - } + /* TODO: calculate and set carrier offset */ - dprintk("%s: Not tuned\n", __func__); - ds3000_dump_registers(fe); + for (i = 0; i < 30 ; i++) { + ds3000_read_status(fe, &status); + if (status && FE_HAS_LOCK) + break; - } while (--retune); + msleep(10); + } -tuned: - return ret; + return 0; } static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) -- cgit v0.10.2 From cb8f74da107cf593eb50ac4968faaa5f49fbad4a Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:24 -0300 Subject: [media] ds3000: remove unnecessary dnxt, dcur structures All necessary parameters already stored in frontend cache. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index 882be67..13f9193 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -229,31 +229,11 @@ static u8 ds3000_dvbs2_init_tab[] = { 0xb8, 0x00, }; -/* DS3000 doesn't need some parameters as input and auto-detects them */ -/* save input from the application of those parameters */ -struct ds3000_tuning { - u32 frequency; - u32 symbol_rate; - fe_spectral_inversion_t inversion; - enum fe_code_rate fec; - - /* input values */ - u8 inversion_val; - fe_modulation_t delivery; - u8 rolloff; -}; - struct ds3000_state { struct i2c_adapter *i2c; const struct ds3000_config *config; - struct dvb_frontend frontend; - - struct ds3000_tuning dcur; - struct ds3000_tuning dnxt; - u8 skip_fw_load; - /* previous uncorrected block counter for DVB-S2 */ u16 prevUCBS2; }; @@ -401,45 +381,6 @@ static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg) return b1[0]; } -static int ds3000_set_inversion(struct ds3000_state *state, - fe_spectral_inversion_t inversion) -{ - dprintk("%s(%d)\n", __func__, inversion); - - switch (inversion) { - case INVERSION_OFF: - case INVERSION_ON: - case INVERSION_AUTO: - break; - default: - return -EINVAL; - } - - state->dnxt.inversion = inversion; - - return 0; -} - -static int ds3000_set_symbolrate(struct ds3000_state *state, u32 rate) -{ - int ret = 0; - - dprintk("%s()\n", __func__); - - dprintk("%s() symbol_rate = %d\n", __func__, state->dnxt.symbol_rate); - - /* check if symbol rate is within limits */ - if ((state->dnxt.symbol_rate > - state->frontend.ops.info.symbol_rate_max) || - (state->dnxt.symbol_rate < - state->frontend.ops.info.symbol_rate_min)) - ret = -EOPNOTSUPP; - - state->dnxt.symbol_rate = rate; - - return ret; -} - static int ds3000_load_firmware(struct dvb_frontend *fe, const struct firmware *fw); @@ -790,13 +731,6 @@ static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) return 0; } -/* Overwrite the current tuning params, we are about to tune */ -static void ds3000_clone_params(struct dvb_frontend *fe) -{ - struct ds3000_state *state = fe->demodulator_priv; - memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur)); -} - static int ds3000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) { struct ds3000_state *state = fe->demodulator_priv; @@ -1027,22 +961,6 @@ static int ds3000_tune(struct dvb_frontend *fe, dprintk("%s() ", __func__); - state->dnxt.delivery = c->modulation; - state->dnxt.frequency = c->frequency; - state->dnxt.rolloff = 2; /* fixme */ - state->dnxt.fec = c->fec_inner; - - ret = ds3000_set_inversion(state, p->inversion); - if (ret != 0) - return ret; - - ret = ds3000_set_symbolrate(state, c->symbol_rate); - if (ret != 0) - return ret; - - /* discard the 'current' tuning parameters and prepare to tune */ - ds3000_clone_params(fe); - /* Reset status register */ status = 0; /* Tune */ @@ -1053,14 +971,14 @@ static int ds3000_tune(struct dvb_frontend *fe, ds3000_tuner_writereg(state, 0x08, 0x01); ds3000_tuner_writereg(state, 0x00, 0x01); /* calculate and set freq divider */ - if (state->dcur.frequency < 1146000) { + if (p->frequency < 1146000) { ds3000_tuner_writereg(state, 0x10, 0x11); - ndiv = ((state->dcur.frequency * (6 + 8) * 4) + + ndiv = ((p->frequency * (6 + 8) * 4) + (DS3000_XTAL_FREQ / 2)) / DS3000_XTAL_FREQ - 1024; } else { ds3000_tuner_writereg(state, 0x10, 0x01); - ndiv = ((state->dcur.frequency * (6 + 8) * 2) + + ndiv = ((p->frequency * (6 + 8) * 2) + (DS3000_XTAL_FREQ / 2)) / DS3000_XTAL_FREQ - 1024; } @@ -1106,8 +1024,8 @@ static int ds3000_tune(struct dvb_frontend *fe, ds3000_tuner_writereg(state, 0x50, 0x00); msleep(5); - f3db = ((state->dcur.symbol_rate / 1000) << 2) / 5 + 2000; - if ((state->dcur.symbol_rate / 1000) < 5000) + f3db = ((c->symbol_rate / 1000) << 2) / 5 + 2000; + if ((c->symbol_rate / 1000) < 5000) f3db += 3000; if (f3db < 7000) f3db = 7000; @@ -1196,30 +1114,30 @@ static int ds3000_tune(struct dvb_frontend *fe, ds3000_writereg(state, 0x25, 0x8a); /* enhance symbol rate performance */ - if ((state->dcur.symbol_rate / 1000) <= 5000) { - value = 29777 / (state->dcur.symbol_rate / 1000) + 1; + if ((c->symbol_rate / 1000) <= 5000) { + value = 29777 / (c->symbol_rate / 1000) + 1; if (value % 2 != 0) value++; ds3000_writereg(state, 0xc3, 0x0d); ds3000_writereg(state, 0xc8, value); ds3000_writereg(state, 0xc4, 0x10); ds3000_writereg(state, 0xc7, 0x0e); - } else if ((state->dcur.symbol_rate / 1000) <= 10000) { - value = 92166 / (state->dcur.symbol_rate / 1000) + 1; + } else if ((c->symbol_rate / 1000) <= 10000) { + value = 92166 / (c->symbol_rate / 1000) + 1; if (value % 2 != 0) value++; ds3000_writereg(state, 0xc3, 0x07); ds3000_writereg(state, 0xc8, value); ds3000_writereg(state, 0xc4, 0x09); ds3000_writereg(state, 0xc7, 0x12); - } else if ((state->dcur.symbol_rate / 1000) <= 20000) { - value = 64516 / (state->dcur.symbol_rate / 1000) + 1; + } else if ((c->symbol_rate / 1000) <= 20000) { + value = 64516 / (c->symbol_rate / 1000) + 1; ds3000_writereg(state, 0xc3, value); ds3000_writereg(state, 0xc8, 0x0e); ds3000_writereg(state, 0xc4, 0x07); ds3000_writereg(state, 0xc7, 0x18); } else { - value = 129032 / (state->dcur.symbol_rate / 1000) + 1; + value = 129032 / (c->symbol_rate / 1000) + 1; ds3000_writereg(state, 0xc3, value); ds3000_writereg(state, 0xc8, 0x0a); ds3000_writereg(state, 0xc4, 0x05); @@ -1227,7 +1145,7 @@ static int ds3000_tune(struct dvb_frontend *fe, } /* normalized symbol rate rounded to the closest integer */ - value = (((state->dcur.symbol_rate / 1000) << 16) + + value = (((c->symbol_rate / 1000) << 16) + (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE; ds3000_writereg(state, 0x61, value & 0x00ff); ds3000_writereg(state, 0x62, (value & 0xff00) >> 8); -- cgit v0.10.2 From a5bf834b4a29b0c7f813ce1c456563c470d4181f Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:24 -0300 Subject: [media] ds3000: add carrier offset calculation Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index 13f9193..a0bf70f 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -948,6 +948,25 @@ static int ds3000_get_property(struct dvb_frontend *fe, return 0; } +static int ds3000_set_carrier_offset(struct dvb_frontend *fe, + s32 carrier_offset_khz) +{ + struct ds3000_state *state = fe->demodulator_priv; + s32 tmp; + + tmp = carrier_offset_khz; + tmp *= 65536; + tmp = (2 * tmp + DS3000_SAMPLE_RATE) / (2 * DS3000_SAMPLE_RATE); + + if (tmp < 0) + tmp += 65536; + + ds3000_writereg(state, 0x5f, tmp >> 8); + ds3000_writereg(state, 0x5e, tmp & 0xff); + + return 0; +} + static int ds3000_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { @@ -955,7 +974,8 @@ static int ds3000_tune(struct dvb_frontend *fe, struct dtv_frontend_properties *c = &fe->dtv_property_cache; int i; - u8 status, mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf; + u8 status, mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; + s32 offset_khz; u16 value, ndiv; u32 f3db; @@ -970,9 +990,12 @@ static int ds3000_tune(struct dvb_frontend *fe, ds3000_tuner_writereg(state, 0x60, 0x79); ds3000_tuner_writereg(state, 0x08, 0x01); ds3000_tuner_writereg(state, 0x00, 0x01); + div4 = 0; + /* calculate and set freq divider */ if (p->frequency < 1146000) { ds3000_tuner_writereg(state, 0x10, 0x11); + div4 = 1; ndiv = ((p->frequency * (6 + 8) * 4) + (DS3000_XTAL_FREQ / 2)) / DS3000_XTAL_FREQ - 1024; @@ -1076,6 +1099,9 @@ static int ds3000_tune(struct dvb_frontend *fe, ds3000_tuner_writereg(state, 0x50, 0x00); msleep(60); + offset_khz = (ndiv - ndiv % 2 + 1024) * DS3000_XTAL_FREQ + / (6 + 8) / (div4 + 1) / 2 - p->frequency; + /* ds3000 global reset */ ds3000_writereg(state, 0x07, 0x80); ds3000_writereg(state, 0x07, 0x00); @@ -1179,7 +1205,7 @@ static int ds3000_tune(struct dvb_frontend *fe, /* start ds3000 build-in uC */ ds3000_writereg(state, 0xb2, 0x00); - /* TODO: calculate and set carrier offset */ + ds3000_set_carrier_offset(fe, offset_khz); for (i = 0; i < 30 ; i++) { ds3000_read_status(fe, &status); -- cgit v0.10.2 From dcc8a122230b1c65ee8ba83536fd53fcaf27884d Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:24 -0300 Subject: [media] ds3000: hardware tune algorithm Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index a0bf70f..2ae5699 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -967,22 +967,21 @@ static int ds3000_set_carrier_offset(struct dvb_frontend *fe, return 0; } -static int ds3000_tune(struct dvb_frontend *fe, +static int ds3000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { struct ds3000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int i; - u8 status, mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; + fe_status_t status; + u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; s32 offset_khz; u16 value, ndiv; u32 f3db; dprintk("%s() ", __func__); - /* Reset status register */ - status = 0; /* Tune */ /* unknown */ ds3000_tuner_writereg(state, 0x07, 0x02); @@ -1218,10 +1217,16 @@ static int ds3000_tune(struct dvb_frontend *fe, return 0; } +static int ds3000_tune(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + return ds3000_set_frontend(fe, p); +} + static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) { dprintk("%s()\n", __func__); - return DVBFE_ALGO_SW; + return DVBFE_ALGO_HW; } /* @@ -1296,7 +1301,8 @@ static struct dvb_frontend_ops ds3000_ops = { .set_property = ds3000_set_property, .get_property = ds3000_get_property, - .set_frontend = ds3000_tune, + .set_frontend = ds3000_set_frontend, + .tune = ds3000_tune, }; module_param(debug, int, 0644); -- cgit v0.10.2 From 0cb73639cfbdc9a074cbb7737d173f7a7381036d Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 25 Feb 2011 18:41:24 -0300 Subject: [media] cx88: add support for TeVii S464 PCI card The card uses ds3000 demod from Montage. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index 2ae5699..7f3d118 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -982,6 +982,8 @@ static int ds3000_set_frontend(struct dvb_frontend *fe, dprintk("%s() ", __func__); + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); /* Tune */ /* unknown */ ds3000_tuner_writereg(state, 0x07, 0x02); diff --git a/drivers/media/dvb/frontends/ds3000.h b/drivers/media/dvb/frontends/ds3000.h index 00e4e95..1b73688 100644 --- a/drivers/media/dvb/frontends/ds3000.h +++ b/drivers/media/dvb/frontends/ds3000.h @@ -28,6 +28,8 @@ struct ds3000_config { /* the demodulator's i2c address */ u8 demod_address; u8 ci_mode; + /* Set device param to start dma */ + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); }; #if defined(CONFIG_DVB_DS3000) || \ diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index f735262..27222c9 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1953,6 +1953,18 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_TEVII_S464] = { + .name = "TeVii S464 DVB-S/S2", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_OMICOM_SS4_PCI] = { .name = "Omicom SS4 DVB-S/S2 PCI", .tuner_type = UNSET, @@ -2529,6 +2541,10 @@ static const struct cx88_subid cx88_subids[] = { .subdevice = 0x9022, .card = CX88_BOARD_TEVII_S460, }, { + .subvendor = 0xd464, + .subdevice = 0x9022, + .card = CX88_BOARD_TEVII_S464, + }, { .subvendor = 0xA044, .subdevice = 0x2011, .card = CX88_BOARD_OMICOM_SS4_PCI, @@ -3286,6 +3302,7 @@ static void cx88_card_setup(struct cx88_core *core) } case CX88_BOARD_TEVII_S420: case CX88_BOARD_TEVII_S460: + case CX88_BOARD_TEVII_S464: case CX88_BOARD_OMICOM_SS4_PCI: case CX88_BOARD_TBS_8910: case CX88_BOARD_TBS_8920: diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 90717ee..7b8c9d3 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -57,6 +57,7 @@ #include "stb6100.h" #include "stb6100_proc.h" #include "mb86a16.h" +#include "ds3000.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe "); @@ -648,6 +649,20 @@ static const struct cx24116_config tevii_s460_config = { .reset_device = cx24116_reset_device, }; +static int ds3000_set_ts_param(struct dvb_frontend *fe, + int is_punctured) +{ + struct cx8802_dev *dev = fe->dvb->priv; + dev->ts_gen_cntrl = 4; + + return 0; +} + +static struct ds3000_config tevii_ds3000_config = { + .demod_address = 0x68, + .set_ts_params = ds3000_set_ts_param, +}; + static const struct stv0900_config prof_7301_stv0900_config = { .demod_address = 0x6a, /* demod_mode = 0,*/ @@ -1381,6 +1396,14 @@ static int dvb_register(struct cx8802_dev *dev) if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; break; + case CX88_BOARD_TEVII_S464: + fe0->dvb.frontend = dvb_attach(ds3000_attach, + &tevii_ds3000_config, + &core->i2c_adap); + if (fe0->dvb.frontend != NULL) + fe0->dvb.frontend->ops.set_voltage = + tevii_dvbs_set_voltage; + break; case CX88_BOARD_OMICOM_SS4_PCI: case CX88_BOARD_TBS_8920: case CX88_BOARD_PROF_7300: diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 06f7d1d..fbfbba5 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -373,6 +373,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir_codes = RC_MAP_TBS_NEC; ir->sampling = 0xff00; /* address */ break; + case CX88_BOARD_TEVII_S464: case CX88_BOARD_TEVII_S460: case CX88_BOARD_TEVII_S420: ir_codes = RC_MAP_TEVII_NEC; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index aaf7d12b..9b3742a 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -241,6 +241,7 @@ extern const struct sram_channel const cx88_sram_channels[]; #define CX88_BOARD_PROF_7301 83 #define CX88_BOARD_SAMSUNG_SMT_7020 84 #define CX88_BOARD_TWINHAN_VP1027_DVBS 85 +#define CX88_BOARD_TEVII_S464 86 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, -- cgit v0.10.2 From e45c2be9704381971d78d711e58dec35b1c4cf6f Mon Sep 17 00:00:00 2001 From: Paul Cassella Date: Tue, 8 Feb 2011 21:07:00 -0300 Subject: [media] Documentation: README.ivtv: Remove note that ivtvfb is not yet in the kernel ivtvfb is now in the kernel, so stop saying it's not. Signed-off-by: Paul Cassella Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/README.ivtv b/Documentation/video4linux/README.ivtv index 42b0668..2579b5b 100644 --- a/Documentation/video4linux/README.ivtv +++ b/Documentation/video4linux/README.ivtv @@ -36,8 +36,7 @@ Additional features for the PVR-350 (CX23415 based): * Provides comprehensive OSD (On Screen Display: ie. graphics overlaying the video signal) * Provides a framebuffer (allowing X applications to appear on the video - device) (this framebuffer is not yet part of the kernel. In the meantime it - is available from www.ivtvdriver.org). + device) * Supports raw YUV output. IMPORTANT: In case of problems first read this page: -- cgit v0.10.2 From 793e71b667856ed6163ad0dc7de1f453e9385590 Mon Sep 17 00:00:00 2001 From: Paul Cassella Date: Tue, 8 Feb 2011 22:22:52 -0300 Subject: [media] ivtv: udma: handle get_user_pages() returning fewer pages than we asked for get_user_pages() may return fewer page than we ask for. As we give up and return an error in this case, we need to put_page() each of the pages we did get. [Andy Walls modified the patch, only removing the braces in the 'for' statement, to eliminate a checkpatch warning.] Signed-off-by: Paul Cassella Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c index 1daf1dd..69cc816 100644 --- a/drivers/media/video/ivtv/ivtv-udma.c +++ b/drivers/media/video/ivtv/ivtv-udma.c @@ -132,7 +132,12 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, if (user_dma.page_count != err) { IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", err, user_dma.page_count); - return -EINVAL; + if (err >= 0) { + for (i = 0; i < err; i++) + put_page(dma->map[i]); + return -EINVAL; + } + return err; } dma->page_count = user_dma.page_count; -- cgit v0.10.2 From 0d44b1235c60bf3134f5730ebb4a115f857a2da4 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Tue, 8 Feb 2011 22:39:28 -0300 Subject: [media] ivtv: Fix sparse warning regarding a user pointer in ivtv_write_vbi_from_user() Fix the first, botched attempt at preventing direct use of a user pointer in ivtv_write_vbi(). Reported-by: Dr. David Alan Gilbert Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c index 2dfa957..b6eb51c 100644 --- a/drivers/media/video/ivtv/ivtv-vbi.c +++ b/drivers/media/video/ivtv/ivtv-vbi.c @@ -174,7 +174,7 @@ ivtv_write_vbi_from_user(struct ivtv *itv, ret = -EFAULT; break; } - ivtv_write_vbi_line(itv, sliced + i, &cc, &found_cc); + ivtv_write_vbi_line(itv, &d, &cc, &found_cc); } if (found_cc) -- cgit v0.10.2 From 7fd4b41f053681cccf188cc1731ae43fe38fa969 Mon Sep 17 00:00:00 2001 From: Paul Cassella Date: Sat, 12 Feb 2011 10:39:51 -0300 Subject: [media] ivtv: yuv: handle get_user_pages() -errno returns get_user_pages() may return -errno, such as -EFAULT. So don't blindly use its return value as an offset into dma->map[] for the next get_user_pages() call. Since we'll give up and return an error if either fails, don't even make the second call if the first failed to give us exactly what we were looking for. The old code would also call put_page() on as many elements of dma->map[] as we'd asked for, regardless of how many were valid. [Andy Walls modified this patch to return -EFAULT instead of -EINVAL as Paul's observation "I'm not sure -EINVAL is the best return code vs -EFAULT or -ENOMEM, [...]" was correct. The return value bubbles up as a return code for write(), for which the V4L2 API spec indicates EINVAL is incorrect and EFAULT is correct.] Signed-off-by: Paul Cassella Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c index c087537..dcbab6a 100644 --- a/drivers/media/video/ivtv/ivtv-yuv.c +++ b/drivers/media/video/ivtv/ivtv-yuv.c @@ -77,23 +77,51 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, /* Get user pages for DMA Xfer */ down_read(¤t->mm->mmap_sem); y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL); - uv_pages = get_user_pages(current, current->mm, uv_dma.uaddr, uv_dma.page_count, 0, 1, &dma->map[y_pages], NULL); + uv_pages = 0; /* silence gcc. value is set and consumed only if: */ + if (y_pages == y_dma.page_count) { + uv_pages = get_user_pages(current, current->mm, + uv_dma.uaddr, uv_dma.page_count, 0, 1, + &dma->map[y_pages], NULL); + } up_read(¤t->mm->mmap_sem); - dma->page_count = y_dma.page_count + uv_dma.page_count; - - if (y_pages + uv_pages != dma->page_count) { - IVTV_DEBUG_WARN - ("failed to map user pages, returned %d instead of %d\n", - y_pages + uv_pages, dma->page_count); - - for (i = 0; i < dma->page_count; i++) { - put_page(dma->map[i]); + if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) { + int rc = -EFAULT; + + if (y_pages == y_dma.page_count) { + IVTV_DEBUG_WARN + ("failed to map uv user pages, returned %d " + "expecting %d\n", uv_pages, uv_dma.page_count); + + if (uv_pages >= 0) { + for (i = 0; i < uv_pages; i++) + put_page(dma->map[y_pages + i]); + rc = -EFAULT; + } else { + rc = uv_pages; + } + } else { + IVTV_DEBUG_WARN + ("failed to map y user pages, returned %d " + "expecting %d\n", y_pages, y_dma.page_count); } - dma->page_count = 0; - return -EINVAL; + if (y_pages >= 0) { + for (i = 0; i < y_pages; i++) + put_page(dma->map[i]); + /* + * Inherit the -EFAULT from rc's + * initialization, but allow it to be + * overriden by uv_pages above if it was an + * actual errno. + */ + } else { + rc = y_pages; + } + return rc; } + dma->page_count = y_pages + uv_pages; + /* Fill & map SG List */ if (ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)) < 0) { IVTV_DEBUG_WARN("could not allocate bounce buffers for highmem userspace buffers\n"); -- cgit v0.10.2 From 8d19824ba2ef37ff989bee5941c87b45c4c6f468 Mon Sep 17 00:00:00 2001 From: Manjunatha Halli Date: Tue, 11 Jan 2011 06:33:18 -0300 Subject: [media] drivers:media:radio: wl128x: FM Driver common header file These are common headers used in FM submodules (FM V4L2, FM common, FM Rx,and FM TX). Signed-off-by: Manjunatha Halli Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h new file mode 100644 index 0000000..5db6fd1 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv.h @@ -0,0 +1,244 @@ +/* + * FM Driver for Connectivity chip of Texas Instruments. + * + * Common header for all FM driver sub-modules. + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _FM_DRV_H +#define _FM_DRV_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FM_DRV_VERSION "0.10" +/* Should match with FM_DRV_VERSION */ +#define FM_DRV_RADIO_VERSION KERNEL_VERSION(0, 0, 1) +#define FM_DRV_NAME "ti_fmdrv" +#define FM_DRV_CARD_SHORT_NAME "TI FM Radio" +#define FM_DRV_CARD_LONG_NAME "Texas Instruments FM Radio" + +/* Flag info */ +#define FM_INTTASK_RUNNING 0 +#define FM_INTTASK_SCHEDULE_PENDING 1 +#define FM_FW_DW_INPROGRESS 2 +#define FM_CORE_READY 3 +#define FM_CORE_TRANSPORT_READY 4 +#define FM_AF_SWITCH_INPROGRESS 5 +#define FM_CORE_TX_XMITING 6 + +#define FM_TUNE_COMPLETE 0x1 +#define FM_BAND_LIMIT 0x2 + +#define FM_DRV_TX_TIMEOUT (5*HZ) /* 5 seconds */ +#define FM_DRV_RX_SEEK_TIMEOUT (20*HZ) /* 20 seconds */ + +#define NO_OF_ENTRIES_IN_ARRAY(array) (sizeof(array) / sizeof(array[0])) + +#define fmerr(format, ...) \ + printk(KERN_ERR "fmdrv: " format, ## __VA_ARGS__) +#define fmwarn(format, ...) \ + printk(KERN_WARNING "fmdrv: " format, ##__VA_ARGS__) +#ifdef DEBUG +#define fmdbg(format, ...) \ + printk(KERN_DEBUG "fmdrv: " format, ## __VA_ARGS__) +#else /* DEBUG */ +#define fmdbg(format, ...) +#endif +enum { + FM_MODE_OFF, + FM_MODE_TX, + FM_MODE_RX, + FM_MODE_ENTRY_MAX +}; + +#define FM_RX_RDS_INFO_FIELD_MAX 8 /* 4 Group * 2 Bytes */ + +/* RX RDS data format */ +struct fm_rdsdata_format { + union { + struct { + u8 buff[FM_RX_RDS_INFO_FIELD_MAX]; + } groupdatabuff; + struct { + u16 pidata; + u8 blk_b[2]; + u8 blk_c[2]; + u8 blk_d[2]; + } groupgeneral; + struct { + u16 pidata; + u8 blk_b[2]; + u8 af[2]; + u8 ps[2]; + } group0A; + struct { + u16 pi[2]; + u8 blk_b[2]; + u8 ps[2]; + } group0B; + } data; +}; + +/* FM region (Europe/US, Japan) info */ +struct region_info { + u32 chanl_space; + u32 bot_freq; + u32 top_freq; + u8 fm_band; +}; +struct fmdev; +typedef void (*int_handler_prototype) (struct fmdev *); + +/* FM Interrupt processing related info */ +struct fm_irq { + u8 stage; + u16 flag; /* FM interrupt flag */ + u16 mask; /* FM interrupt mask */ + /* Interrupt process timeout handler */ + struct timer_list timer; + u8 retry; + int_handler_prototype *handlers; +}; + +/* RDS info */ +struct fm_rds { + u8 flag; /* RX RDS on/off status */ + u8 last_blk_idx; /* Last received RDS block */ + + /* RDS buffer */ + wait_queue_head_t read_queue; + u32 buf_size; /* Size is always multiple of 3 */ + u32 wr_idx; + u32 rd_idx; + u8 *buff; +}; + +#define FM_RDS_MAX_AF_LIST 25 + +/* + * Current RX channel Alternate Frequency cache. + * This info is used to switch to other freq (AF) + * when current channel signal strengh is below RSSI threshold. + */ +struct tuned_station_info { + u16 picode; + u32 af_cache[FM_RDS_MAX_AF_LIST]; + u8 afcache_size; + u8 af_list_max; +}; + +/* FM RX mode info */ +struct fm_rx { + struct region_info region; /* Current selected band */ + u32 freq; /* Current RX frquency */ + u8 mute_mode; /* Current mute mode */ + u8 deemphasis_mode; /* Current deemphasis mode */ + /* RF dependent soft mute mode */ + u8 rf_depend_mute; + u16 volume; /* Current volume level */ + u16 rssi_threshold; /* Current RSSI threshold level */ + /* Holds the index of the current AF jump */ + u8 afjump_idx; + /* Will hold the frequency before the jump */ + u32 freq_before_jump; + u8 rds_mode; /* RDS operation mode (RDS/RDBS) */ + u8 af_mode; /* Alternate frequency on/off */ + struct tuned_station_info stat_info; + struct fm_rds rds; +}; + +#define FMTX_RDS_TXT_STR_SIZE 25 +/* + * FM TX RDS data + * + * @ text_type: is the text following PS or RT + * @ text: radio text string which could either be PS or RT + * @ af_freq: alternate frequency for Tx + * TODO: to be declared in application + */ +struct tx_rds { + u8 text_type; + u8 text[FMTX_RDS_TXT_STR_SIZE]; + u8 flag; + u32 af_freq; +}; +/* + * FM TX global data + * + * @ pwr_lvl: Power Level of the Transmission from mixer control + * @ xmit_state: Transmission state = Updated locally upon Start/Stop + * @ audio_io: i2S/Analog + * @ tx_frq: Transmission frequency + */ +struct fmtx_data { + u8 pwr_lvl; + u8 xmit_state; + u8 audio_io; + u8 region; + u16 aud_mode; + u32 preemph; + u32 tx_frq; + struct tx_rds rds; +}; + +/* FM driver operation structure */ +struct fmdev { + struct video_device *radio_dev; /* V4L2 video device pointer */ + struct snd_card *card; /* Card which holds FM mixer controls */ + u16 asci_id; + spinlock_t rds_buff_lock; /* To protect access to RDS buffer */ + spinlock_t resp_skb_lock; /* To protect access to received SKB */ + + long flag; /* FM driver state machine info */ + u8 streg_cbdata; /* status of ST registration */ + + struct sk_buff_head rx_q; /* RX queue */ + struct tasklet_struct rx_task; /* RX Tasklet */ + + struct sk_buff_head tx_q; /* TX queue */ + struct tasklet_struct tx_task; /* TX Tasklet */ + unsigned long last_tx_jiffies; /* Timestamp of last pkt sent */ + atomic_t tx_cnt; /* Number of packets can send at a time */ + + struct sk_buff *resp_skb; /* Response from the chip */ + /* Main task completion handler */ + struct completion maintask_comp; + /* Opcode of last command sent to the chip */ + u8 pre_op; + /* Handler used for wakeup when response packet is received */ + struct completion *resp_comp; + struct fm_irq irq_info; + u8 curr_fmmode; /* Current FM chip mode (TX, RX, OFF) */ + struct fm_rx rx; /* FM receiver info */ + struct fmtx_data tx_data; + + /* V4L2 ctrl framwork handler*/ + struct v4l2_ctrl_handler ctrl_handler; + + /* For core assisted locking */ + struct mutex mutex; +}; +#endif -- cgit v0.10.2 From 7e38849631c2f570fe66b52b5b54697fe338f194 Mon Sep 17 00:00:00 2001 From: Manjunatha Halli Date: Tue, 11 Jan 2011 06:34:26 -0300 Subject: [media] drivers:media:radio: wl128x: FM Driver V4L2 sources This module interfaces V4L2 subsystem and FM common module. It registers itself with V4L2 as Radio module. Signed-off-by: Manjunatha Halli Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c new file mode 100644 index 0000000..d50e5ac --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -0,0 +1,580 @@ +/* + * FM Driver for Connectivity chip of Texas Instruments. + * This file provides interfaces to V4L2 subsystem. + * + * This module registers with V4L2 subsystem as Radio + * data system interface (/dev/radio). During the registration, + * it will expose two set of function pointers. + * + * 1) File operation related API (open, close, read, write, poll...etc). + * 2) Set of V4L2 IOCTL complaint API. + * + * Copyright (C) 2011 Texas Instruments + * Author: Raja Mani + * Author: Manjunatha Halli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "fmdrv.h" +#include "fmdrv_v4l2.h" +#include "fmdrv_common.h" +#include "fmdrv_rx.h" +#include "fmdrv_tx.h" + +static struct video_device *gradio_dev; +static u8 radio_disconnected; + +/* -- V4L2 RADIO (/dev/radioX) device file operation interfaces --- */ + +/* Read RX RDS data */ +static ssize_t fm_v4l2_fops_read(struct file *file, char __user * buf, + size_t count, loff_t *ppos) +{ + u8 rds_mode; + int ret; + struct fmdev *fmdev; + + fmdev = video_drvdata(file); + + if (!radio_disconnected) { + fmerr("FM device is already disconnected\n"); + return -EIO; + } + + /* Turn on RDS mode , if it is disabled */ + ret = fm_rx_get_rds_mode(fmdev, &rds_mode); + if (ret < 0) { + fmerr("Unable to read current rds mode\n"); + return ret; + } + + if (rds_mode == FM_RDS_DISABLE) { + ret = fmc_set_rds_mode(fmdev, FM_RDS_ENABLE); + if (ret < 0) { + fmerr("Failed to enable rds mode\n"); + return ret; + } + } + + /* Copy RDS data from internal buffer to user buffer */ + return fmc_transfer_rds_from_internal_buff(fmdev, file, buf, count); +} + +/* Write TX RDS data */ +static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf, + size_t count, loff_t *ppos) +{ + struct tx_rds rds; + int ret; + struct fmdev *fmdev; + + ret = copy_from_user(&rds, buf, sizeof(rds)); + fmdbg("(%d)type: %d, text %s, af %d\n", + ret, rds.text_type, rds.text, rds.af_freq); + + fmdev = video_drvdata(file); + fm_tx_set_radio_text(fmdev, rds.text, rds.text_type); + fm_tx_set_af(fmdev, rds.af_freq); + + return 0; +} + +static u32 fm_v4l2_fops_poll(struct file *file, struct poll_table_struct *pts) +{ + int ret; + struct fmdev *fmdev; + + fmdev = video_drvdata(file); + ret = fmc_is_rds_data_available(fmdev, file, pts); + if (ret < 0) + return POLLIN | POLLRDNORM; + + return 0; +} + +/* + * Handle open request for "/dev/radioX" device. + * Start with FM RX mode as default. + */ +static int fm_v4l2_fops_open(struct file *file) +{ + int ret; + struct fmdev *fmdev = NULL; + + /* Don't allow multiple open */ + if (radio_disconnected) { + fmerr("FM device is already opened\n"); + return -EBUSY; + } + + fmdev = video_drvdata(file); + + ret = fmc_prepare(fmdev); + if (ret < 0) { + fmerr("Unable to prepare FM CORE\n"); + return ret; + } + + fmdbg("Load FM RX firmware..\n"); + + ret = fmc_set_mode(fmdev, FM_MODE_RX); + if (ret < 0) { + fmerr("Unable to load FM RX firmware\n"); + return ret; + } + radio_disconnected = 1; + + return ret; +} + +static int fm_v4l2_fops_release(struct file *file) +{ + int ret; + struct fmdev *fmdev; + + fmdev = video_drvdata(file); + if (!radio_disconnected) { + fmdbg("FM device is already closed\n"); + return 0; + } + + ret = fmc_set_mode(fmdev, FM_MODE_OFF); + if (ret < 0) { + fmerr("Unable to turn off the chip\n"); + return ret; + } + + ret = fmc_release(fmdev); + if (ret < 0) { + fmerr("FM CORE release failed\n"); + return ret; + } + radio_disconnected = 0; + + return ret; +} + +/* V4L2 RADIO (/dev/radioX) device IOCTL interfaces */ +static int fm_v4l2_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + strlcpy(capability->driver, FM_DRV_NAME, sizeof(capability->driver)); + strlcpy(capability->card, FM_DRV_CARD_SHORT_NAME, + sizeof(capability->card)); + sprintf(capability->bus_info, "UART"); + capability->version = FM_DRV_RADIO_VERSION; + capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | + V4L2_CAP_RADIO | V4L2_CAP_MODULATOR | + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | + V4L2_CAP_RDS_CAPTURE; + + return 0; +} + +static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct fmdev *fmdev = container_of(ctrl->handler, + struct fmdev, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + ctrl->val = fm_tx_get_tune_cap_val(fmdev); + break; + default: + fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id); + break; + } + + return 0; +} + +static int fm_v4l2_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct fmdev *fmdev = container_of(ctrl->handler, + struct fmdev, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: /* set volume */ + return fm_rx_set_volume(fmdev, (u16)ctrl->val); + + case V4L2_CID_AUDIO_MUTE: /* set mute */ + return fmc_set_mute_mode(fmdev, (u8)ctrl->val); + + case V4L2_CID_TUNE_POWER_LEVEL: + /* set TX power level - ext control */ + return fm_tx_set_pwr_lvl(fmdev, (u8)ctrl->val); + + case V4L2_CID_TUNE_PREEMPHASIS: + return fm_tx_set_preemph_filter(fmdev, (u8) ctrl->val); + + default: + return -EINVAL; + } +} + +static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + memset(audio, 0, sizeof(*audio)); + strcpy(audio->name, "Radio"); + audio->capability = V4L2_AUDCAP_STEREO; + + return 0; +} + +static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + if (audio->index != 0) + return -EINVAL; + + return 0; +} + +/* Get tuner attributes. If current mode is NOT RX, return error */ +static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + struct fmdev *fmdev = video_drvdata(file); + u32 bottom_freq; + u32 top_freq; + u16 stereo_mono_mode; + u16 rssilvl; + int ret; + + if (tuner->index != 0) + return -EINVAL; + + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + ret = fm_rx_get_band_freq_range(fmdev, &bottom_freq, &top_freq); + if (ret != 0) + return ret; + + ret = fm_rx_get_stereo_mono(fmdev, &stereo_mono_mode); + if (ret != 0) + return ret; + + ret = fm_rx_get_rssi_level(fmdev, &rssilvl); + if (ret != 0) + return ret; + + strcpy(tuner->name, "FM"); + tuner->type = V4L2_TUNER_RADIO; + /* Store rangelow and rangehigh freq in unit of 62.5 Hz */ + tuner->rangelow = bottom_freq * 16; + tuner->rangehigh = top_freq * 16; + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO | + ((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0); + tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | + V4L2_TUNER_CAP_LOW; + tuner->audmode = (stereo_mono_mode ? + V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO); + + /* + * Actual rssi value lies in between -128 to +127. + * Convert this range from 0 to 255 by adding +128 + */ + rssilvl += 128; + + /* + * Return signal strength value should be within 0 to 65535. + * Find out correct signal radio by multiplying (65535/255) = 257 + */ + tuner->signal = rssilvl * 257; + tuner->afc = 0; + + return ret; +} + +/* + * Set tuner attributes. If current mode is NOT RX, set to RX. + * Currently, we set only audio mode (mono/stereo) and RDS state (on/off). + * Should we set other tuner attributes, too? + */ +static int fm_v4l2_vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + struct fmdev *fmdev = video_drvdata(file); + u16 aud_mode; + u8 rds_mode; + int ret; + + if (tuner->index != 0) + return -EINVAL; + + aud_mode = (tuner->audmode == V4L2_TUNER_MODE_STEREO) ? + FM_STEREO_MODE : FM_MONO_MODE; + rds_mode = (tuner->rxsubchans & V4L2_TUNER_SUB_RDS) ? + FM_RDS_ENABLE : FM_RDS_DISABLE; + + if (fmdev->curr_fmmode != FM_MODE_RX) { + ret = fmc_set_mode(fmdev, FM_MODE_RX); + if (ret < 0) { + fmerr("Failed to set RX mode\n"); + return ret; + } + } + + ret = fmc_set_stereo_mono(fmdev, aud_mode); + if (ret < 0) { + fmerr("Failed to set RX stereo/mono mode\n"); + return ret; + } + + ret = fmc_set_rds_mode(fmdev, rds_mode); + if (ret < 0) + fmerr("Failed to set RX RDS mode\n"); + + return ret; +} + +/* Get tuner or modulator radio frequency */ +static int fm_v4l2_vidioc_g_freq(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct fmdev *fmdev = video_drvdata(file); + int ret; + + ret = fmc_get_freq(fmdev, &freq->frequency); + if (ret < 0) { + fmerr("Failed to get frequency\n"); + return ret; + } + + /* Frequency unit of 62.5 Hz*/ + freq->frequency = (u32) freq->frequency * 16; + + return 0; +} + +/* Set tuner or modulator radio frequency */ +static int fm_v4l2_vidioc_s_freq(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct fmdev *fmdev = video_drvdata(file); + + /* + * As V4L2_TUNER_CAP_LOW is set 1 user sends the frequency + * in units of 62.5 Hz. + */ + freq->frequency = (u32)(freq->frequency / 16); + + return fmc_set_freq(fmdev, freq->frequency); +} + +/* Set hardware frequency seek. If current mode is NOT RX, set it RX. */ +static int fm_v4l2_vidioc_s_hw_freq_seek(struct file *file, void *priv, + struct v4l2_hw_freq_seek *seek) +{ + struct fmdev *fmdev = video_drvdata(file); + int ret; + + if (fmdev->curr_fmmode != FM_MODE_RX) { + ret = fmc_set_mode(fmdev, FM_MODE_RX); + if (ret != 0) { + fmerr("Failed to set RX mode\n"); + return ret; + } + } + + ret = fm_rx_seek(fmdev, seek->seek_upward, seek->wrap_around, + seek->spacing); + if (ret < 0) + fmerr("RX seek failed - %d\n", ret); + + return ret; +} +/* Get modulator attributes. If mode is not TX, return no attributes. */ +static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv, + struct v4l2_modulator *mod) +{ + struct fmdev *fmdev = video_drvdata(file);; + + if (mod->index != 0) + return -EINVAL; + + if (fmdev->curr_fmmode != FM_MODE_TX) + return -EPERM; + + mod->txsubchans = ((fmdev->tx_data.aud_mode == FM_STEREO_MODE) ? + V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO) | + ((fmdev->tx_data.rds.flag == FM_RDS_ENABLE) ? + V4L2_TUNER_SUB_RDS : 0); + + mod->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | + V4L2_TUNER_CAP_LOW; + + return 0; +} + +/* Set modulator attributes. If mode is not TX, set to TX. */ +static int fm_v4l2_vidioc_s_modulator(struct file *file, void *priv, + struct v4l2_modulator *mod) +{ + struct fmdev *fmdev = video_drvdata(file); + u8 rds_mode; + u16 aud_mode; + int ret; + + if (mod->index != 0) + return -EINVAL; + + if (fmdev->curr_fmmode != FM_MODE_TX) { + ret = fmc_set_mode(fmdev, FM_MODE_TX); + if (ret != 0) { + fmerr("Failed to set TX mode\n"); + return ret; + } + } + + aud_mode = (mod->txsubchans & V4L2_TUNER_SUB_STEREO) ? + FM_STEREO_MODE : FM_MONO_MODE; + rds_mode = (mod->txsubchans & V4L2_TUNER_SUB_RDS) ? + FM_RDS_ENABLE : FM_RDS_DISABLE; + ret = fm_tx_set_stereo_mono(fmdev, aud_mode); + if (ret < 0) { + fmerr("Failed to set mono/stereo mode for TX\n"); + return ret; + } + ret = fm_tx_set_rds_mode(fmdev, rds_mode); + if (ret < 0) + fmerr("Failed to set rds mode for TX\n"); + + return ret; +} + +static const struct v4l2_file_operations fm_drv_fops = { + .owner = THIS_MODULE, + .read = fm_v4l2_fops_read, + .write = fm_v4l2_fops_write, + .poll = fm_v4l2_fops_poll, + .unlocked_ioctl = video_ioctl2, + .open = fm_v4l2_fops_open, + .release = fm_v4l2_fops_release, +}; + +static const struct v4l2_ctrl_ops fm_ctrl_ops = { + .s_ctrl = fm_v4l2_s_ctrl, + .g_volatile_ctrl = fm_g_volatile_ctrl, +}; +static const struct v4l2_ioctl_ops fm_drv_ioctl_ops = { + .vidioc_querycap = fm_v4l2_vidioc_querycap, + .vidioc_g_audio = fm_v4l2_vidioc_g_audio, + .vidioc_s_audio = fm_v4l2_vidioc_s_audio, + .vidioc_g_tuner = fm_v4l2_vidioc_g_tuner, + .vidioc_s_tuner = fm_v4l2_vidioc_s_tuner, + .vidioc_g_frequency = fm_v4l2_vidioc_g_freq, + .vidioc_s_frequency = fm_v4l2_vidioc_s_freq, + .vidioc_s_hw_freq_seek = fm_v4l2_vidioc_s_hw_freq_seek, + .vidioc_g_modulator = fm_v4l2_vidioc_g_modulator, + .vidioc_s_modulator = fm_v4l2_vidioc_s_modulator +}; + +/* V4L2 RADIO device parent structure */ +static struct video_device fm_viddev_template = { + .fops = &fm_drv_fops, + .ioctl_ops = &fm_drv_ioctl_ops, + .name = FM_DRV_NAME, + .release = video_device_release, +}; + +int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) +{ + struct v4l2_ctrl *ctrl; + int ret; + + /* Init mutex for core locking */ + mutex_init(&fmdev->mutex); + + /* Allocate new video device */ + gradio_dev = video_device_alloc(); + if (NULL == gradio_dev) { + fmerr("Can't allocate video device\n"); + return -ENOMEM; + } + + /* Setup FM driver's V4L2 properties */ + memcpy(gradio_dev, &fm_viddev_template, sizeof(fm_viddev_template)); + + video_set_drvdata(gradio_dev, fmdev); + + gradio_dev->lock = &fmdev->mutex; + + /* Register with V4L2 subsystem as RADIO device */ + if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) { + video_device_release(gradio_dev); + fmerr("Could not register video device\n"); + return -ENOMEM; + } + + fmdev->radio_dev = gradio_dev; + + /* Register to v4l2 ctrl handler framework */ + fmdev->radio_dev->ctrl_handler = &fmdev->ctrl_handler; + + ret = v4l2_ctrl_handler_init(&fmdev->ctrl_handler, 5); + if (ret < 0) { + fmerr("(fmdev): Can't init ctrl handler\n"); + v4l2_ctrl_handler_free(&fmdev->ctrl_handler); + return -EBUSY; + } + + /* + * Following controls are handled by V4L2 control framework. + * Added in ascending ID order. + */ + v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, FM_RX_VOLUME_MIN, + FM_RX_VOLUME_MAX, 1, FM_RX_VOLUME_MAX); + + v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + + v4l2_ctrl_new_std_menu(&fmdev->ctrl_handler, &fm_ctrl_ops, + V4L2_CID_TUNE_PREEMPHASIS, V4L2_PREEMPHASIS_75_uS, + 0, V4L2_PREEMPHASIS_75_uS); + + v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, + V4L2_CID_TUNE_POWER_LEVEL, FM_PWR_LVL_LOW, + FM_PWR_LVL_HIGH, 1, FM_PWR_LVL_HIGH); + + ctrl = v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, + V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, + 255, 1, 255); + + if (ctrl) + ctrl->is_volatile = 1; + + return 0; +} + +void *fm_v4l2_deinit_video_device(void) +{ + struct fmdev *fmdev; + + + fmdev = video_get_drvdata(gradio_dev); + + /* Unregister to v4l2 ctrl handler framework*/ + v4l2_ctrl_handler_free(&fmdev->ctrl_handler); + + /* Unregister RADIO device from V4L2 subsystem */ + video_unregister_device(gradio_dev); + + return fmdev; +} diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.h b/drivers/media/radio/wl128x/fmdrv_v4l2.h new file mode 100644 index 0000000..0ba79d7 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.h @@ -0,0 +1,33 @@ +/* + * FM Driver for Connectivity chip of Texas Instruments. + * + * FM V4L2 module header. + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _FMDRV_V4L2_H +#define _FMDRV_V4L2_H + +#include +#include +#include + +int fm_v4l2_init_video_device(struct fmdev *, int); +void *fm_v4l2_deinit_video_device(void); + +#endif -- cgit v0.10.2 From e8454ff7b9a4d56f02c095bff12d3c92ef4c7fa6 Mon Sep 17 00:00:00 2001 From: Manjunatha Halli Date: Tue, 11 Jan 2011 06:35:22 -0300 Subject: [media] drivers:media:radio: wl128x: FM Driver Common sources These are the sources for the common interfaces required by the FM V4L2 driver for TI WL127x and WL128x chips. These implement the FM channel-8 protocol communication with the chip. This makes use of the Shared Transport as its transport. Signed-off-by: Manjunatha Halli Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c new file mode 100644 index 0000000..12f4c65 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -0,0 +1,1677 @@ +/* + * FM Driver for Connectivity chip of Texas Instruments. + * + * This sub-module of FM driver is common for FM RX and TX + * functionality. This module is responsible for: + * 1) Forming group of Channel-8 commands to perform particular + * functionality (eg., frequency set require more than + * one Channel-8 command to be sent to the chip). + * 2) Sending each Channel-8 command to the chip and reading + * response back over Shared Transport. + * 3) Managing TX and RX Queues and Tasklets. + * 4) Handling FM Interrupt packet and taking appropriate action. + * 5) Loading FM firmware to the chip (common, FM TX, and FM RX + * firmware files based on mode selection) + * + * Copyright (C) 2011 Texas Instruments + * Author: Raja Mani + * Author: Manjunatha Halli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "fmdrv.h" +#include "fmdrv_v4l2.h" +#include "fmdrv_common.h" +#include +#include "fmdrv_rx.h" +#include "fmdrv_tx.h" + +/* Region info */ +static struct region_info region_configs[] = { + /* Europe/US */ + { + .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL, + .bot_freq = 87500, /* 87.5 MHz */ + .top_freq = 108000, /* 108 MHz */ + .fm_band = 0, + }, + /* Japan */ + { + .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL, + .bot_freq = 76000, /* 76 MHz */ + .top_freq = 90000, /* 90 MHz */ + .fm_band = 1, + }, +}; + +/* Band selection */ +static u8 default_radio_region; /* Europe/US */ +module_param(default_radio_region, byte, 0); +MODULE_PARM_DESC(default_radio_region, "Region: 0=Europe/US, 1=Japan"); + +/* RDS buffer blocks */ +static u32 default_rds_buf = 300; +module_param(default_rds_buf, uint, 0444); +MODULE_PARM_DESC(rds_buf, "RDS buffer entries"); + +/* Radio Nr */ +static u32 radio_nr = -1; +module_param(radio_nr, int, 0444); +MODULE_PARM_DESC(radio_nr, "Radio Nr"); + +/* FM irq handlers forward declaration */ +static void fm_irq_send_flag_getcmd(struct fmdev *); +static void fm_irq_handle_flag_getcmd_resp(struct fmdev *); +static void fm_irq_handle_hw_malfunction(struct fmdev *); +static void fm_irq_handle_rds_start(struct fmdev *); +static void fm_irq_send_rdsdata_getcmd(struct fmdev *); +static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *); +static void fm_irq_handle_rds_finish(struct fmdev *); +static void fm_irq_handle_tune_op_ended(struct fmdev *); +static void fm_irq_handle_power_enb(struct fmdev *); +static void fm_irq_handle_low_rssi_start(struct fmdev *); +static void fm_irq_afjump_set_pi(struct fmdev *); +static void fm_irq_handle_set_pi_resp(struct fmdev *); +static void fm_irq_afjump_set_pimask(struct fmdev *); +static void fm_irq_handle_set_pimask_resp(struct fmdev *); +static void fm_irq_afjump_setfreq(struct fmdev *); +static void fm_irq_handle_setfreq_resp(struct fmdev *); +static void fm_irq_afjump_enableint(struct fmdev *); +static void fm_irq_afjump_enableint_resp(struct fmdev *); +static void fm_irq_start_afjump(struct fmdev *); +static void fm_irq_handle_start_afjump_resp(struct fmdev *); +static void fm_irq_afjump_rd_freq(struct fmdev *); +static void fm_irq_afjump_rd_freq_resp(struct fmdev *); +static void fm_irq_handle_low_rssi_finish(struct fmdev *); +static void fm_irq_send_intmsk_cmd(struct fmdev *); +static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *); + +/* + * When FM common module receives interrupt packet, following handlers + * will be executed one after another to service the interrupt(s) + */ +enum fmc_irq_handler_index { + FM_SEND_FLAG_GETCMD_IDX, + FM_HANDLE_FLAG_GETCMD_RESP_IDX, + + /* HW malfunction irq handler */ + FM_HW_MAL_FUNC_IDX, + + /* RDS threshold reached irq handler */ + FM_RDS_START_IDX, + FM_RDS_SEND_RDS_GETCMD_IDX, + FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX, + FM_RDS_FINISH_IDX, + + /* Tune operation ended irq handler */ + FM_HW_TUNE_OP_ENDED_IDX, + + /* TX power enable irq handler */ + FM_HW_POWER_ENB_IDX, + + /* Low RSSI irq handler */ + FM_LOW_RSSI_START_IDX, + FM_AF_JUMP_SETPI_IDX, + FM_AF_JUMP_HANDLE_SETPI_RESP_IDX, + FM_AF_JUMP_SETPI_MASK_IDX, + FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX, + FM_AF_JUMP_SET_AF_FREQ_IDX, + FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX, + FM_AF_JUMP_ENABLE_INT_IDX, + FM_AF_JUMP_ENABLE_INT_RESP_IDX, + FM_AF_JUMP_START_AFJUMP_IDX, + FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX, + FM_AF_JUMP_RD_FREQ_IDX, + FM_AF_JUMP_RD_FREQ_RESP_IDX, + FM_LOW_RSSI_FINISH_IDX, + + /* Interrupt process post action */ + FM_SEND_INTMSK_CMD_IDX, + FM_HANDLE_INTMSK_CMD_RESP_IDX, +}; + +/* FM interrupt handler table */ +static int_handler_prototype int_handler_table[] = { + fm_irq_send_flag_getcmd, + fm_irq_handle_flag_getcmd_resp, + fm_irq_handle_hw_malfunction, + fm_irq_handle_rds_start, /* RDS threshold reached irq handler */ + fm_irq_send_rdsdata_getcmd, + fm_irq_handle_rdsdata_getcmd_resp, + fm_irq_handle_rds_finish, + fm_irq_handle_tune_op_ended, + fm_irq_handle_power_enb, /* TX power enable irq handler */ + fm_irq_handle_low_rssi_start, + fm_irq_afjump_set_pi, + fm_irq_handle_set_pi_resp, + fm_irq_afjump_set_pimask, + fm_irq_handle_set_pimask_resp, + fm_irq_afjump_setfreq, + fm_irq_handle_setfreq_resp, + fm_irq_afjump_enableint, + fm_irq_afjump_enableint_resp, + fm_irq_start_afjump, + fm_irq_handle_start_afjump_resp, + fm_irq_afjump_rd_freq, + fm_irq_afjump_rd_freq_resp, + fm_irq_handle_low_rssi_finish, + fm_irq_send_intmsk_cmd, /* Interrupt process post action */ + fm_irq_handle_intmsk_cmd_resp +}; + +long (*g_st_write) (struct sk_buff *skb); +static struct completion wait_for_fmdrv_reg_comp; + +static inline void fm_irq_call(struct fmdev *fmdev) +{ + fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev); +} + +/* Continue next function in interrupt handler table */ +static inline void fm_irq_call_stage(struct fmdev *fmdev, u8 stage) +{ + fmdev->irq_info.stage = stage; + fm_irq_call(fmdev); +} + +static inline void fm_irq_timeout_stage(struct fmdev *fmdev, u8 stage) +{ + fmdev->irq_info.stage = stage; + mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT); +} + +#ifdef FM_DUMP_TXRX_PKT + /* To dump outgoing FM Channel-8 packets */ +inline void dump_tx_skb_data(struct sk_buff *skb) +{ + int len, len_org; + u8 index; + struct fm_cmd_msg_hdr *cmd_hdr; + + cmd_hdr = (struct fm_cmd_msg_hdr *)skb->data; + printk(KERN_INFO "<<%shdr:%02x len:%02x opcode:%02x type:%s dlen:%02x", + fm_cb(skb)->completion ? " " : "*", cmd_hdr->hdr, + cmd_hdr->len, cmd_hdr->op, + cmd_hdr->rd_wr ? "RD" : "WR", cmd_hdr->dlen); + + len_org = skb->len - FM_CMD_MSG_HDR_SIZE; + if (len_org > 0) { + printk("\n data(%d): ", cmd_hdr->dlen); + len = min(len_org, 14); + for (index = 0; index < len; index++) + printk("%x ", + skb->data[FM_CMD_MSG_HDR_SIZE + index]); + printk("%s", (len_org > 14) ? ".." : ""); + } + printk("\n"); +} + + /* To dump incoming FM Channel-8 packets */ +inline void dump_rx_skb_data(struct sk_buff *skb) +{ + int len, len_org; + u8 index; + struct fm_event_msg_hdr *evt_hdr; + + evt_hdr = (struct fm_event_msg_hdr *)skb->data; + printk(KERN_INFO ">> hdr:%02x len:%02x sts:%02x numhci:%02x " + "opcode:%02x type:%s dlen:%02x", evt_hdr->hdr, evt_hdr->len, + evt_hdr->status, evt_hdr->num_fm_hci_cmds, evt_hdr->op, + (evt_hdr->rd_wr) ? "RD" : "WR", evt_hdr->dlen); + + len_org = skb->len - FM_EVT_MSG_HDR_SIZE; + if (len_org > 0) { + printk("\n data(%d): ", evt_hdr->dlen); + len = min(len_org, 14); + for (index = 0; index < len; index++) + printk("%x ", + skb->data[FM_EVT_MSG_HDR_SIZE + index]); + printk("%s", (len_org > 14) ? ".." : ""); + } + printk("\n"); +} +#endif + +void fmc_update_region_info(struct fmdev *fmdev, u8 region_to_set) +{ + fmdev->rx.region = region_configs[region_to_set]; +} + +/* + * FM common sub-module will schedule this tasklet whenever it receives + * FM packet from ST driver. + */ +static void recv_tasklet(unsigned long arg) +{ + struct fmdev *fmdev; + struct fm_irq *irq_info; + struct fm_event_msg_hdr *evt_hdr; + struct sk_buff *skb; + u8 num_fm_hci_cmds; + unsigned long flags; + + fmdev = (struct fmdev *)arg; + irq_info = &fmdev->irq_info; + /* Process all packets in the RX queue */ + while ((skb = skb_dequeue(&fmdev->rx_q))) { + if (skb->len < sizeof(struct fm_event_msg_hdr)) { + fmerr("skb(%p) has only %d bytes" + "atleast need %d bytes to decode\n", skb, + skb->len, sizeof(struct fm_event_msg_hdr)); + kfree_skb(skb); + continue; + } + + evt_hdr = (void *)skb->data; + num_fm_hci_cmds = evt_hdr->num_fm_hci_cmds; + + /* FM interrupt packet? */ + if (evt_hdr->op == FM_INTERRUPT) { + /* FM interrupt handler started already? */ + if (!test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) { + set_bit(FM_INTTASK_RUNNING, &fmdev->flag); + if (irq_info->stage != 0) { + fmerr("Inval stage resetting to zero\n"); + irq_info->stage = 0; + } + + /* + * Execute first function in interrupt handler + * table. + */ + irq_info->handlers[irq_info->stage](fmdev); + } else { + set_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag); + } + kfree_skb(skb); + } + /* Anyone waiting for this with completion handler? */ + else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp != NULL) { + + spin_lock_irqsave(&fmdev->resp_skb_lock, flags); + fmdev->resp_skb = skb; + spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); + complete(fmdev->resp_comp); + + fmdev->resp_comp = NULL; + atomic_set(&fmdev->tx_cnt, 1); + } + /* Is this for interrupt handler? */ + else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp == NULL) { + if (fmdev->resp_skb != NULL) + fmerr("Response SKB ptr not NULL\n"); + + spin_lock_irqsave(&fmdev->resp_skb_lock, flags); + fmdev->resp_skb = skb; + spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); + + /* Execute interrupt handler where state index points */ + irq_info->handlers[irq_info->stage](fmdev); + + kfree_skb(skb); + atomic_set(&fmdev->tx_cnt, 1); + } else { + fmerr("Nobody claimed SKB(%p),purging\n", skb); + } + + /* + * Check flow control field. If Num_FM_HCI_Commands field is + * not zero, schedule FM TX tasklet. + */ + if (num_fm_hci_cmds && atomic_read(&fmdev->tx_cnt)) + if (!skb_queue_empty(&fmdev->tx_q)) + tasklet_schedule(&fmdev->tx_task); + } +} + +/* FM send tasklet: is scheduled when FM packet has to be sent to chip */ +static void send_tasklet(unsigned long arg) +{ + struct fmdev *fmdev; + struct sk_buff *skb; + int len; + + fmdev = (struct fmdev *)arg; + + if (!atomic_read(&fmdev->tx_cnt)) + return; + + /* Check, is there any timeout happenned to last transmitted packet */ + if ((jiffies - fmdev->last_tx_jiffies) > FM_DRV_TX_TIMEOUT) { + fmerr("TX timeout occurred\n"); + atomic_set(&fmdev->tx_cnt, 1); + } + + /* Send queued FM TX packets */ + skb = skb_dequeue(&fmdev->tx_q); + if (!skb) + return; + + atomic_dec(&fmdev->tx_cnt); + fmdev->pre_op = fm_cb(skb)->fm_op; + + if (fmdev->resp_comp != NULL) + fmerr("Response completion handler is not NULL\n"); + + fmdev->resp_comp = fm_cb(skb)->completion; + + /* Write FM packet to ST driver */ + len = g_st_write(skb); + if (len < 0) { + kfree_skb(skb); + fmdev->resp_comp = NULL; + fmerr("TX tasklet failed to send skb(%p)\n", skb); + atomic_set(&fmdev->tx_cnt, 1); + } else { + fmdev->last_tx_jiffies = jiffies; + } +} + +/* + * Queues FM Channel-8 packet to FM TX queue and schedules FM TX tasklet for + * transmission + */ +static u32 fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, + int payload_len, struct completion *wait_completion) +{ + struct sk_buff *skb; + struct fm_cmd_msg_hdr *hdr; + int size; + + if (fm_op >= FM_INTERRUPT) { + fmerr("Invalid fm opcode - %d\n", fm_op); + return -EINVAL; + } + if (test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) && payload == NULL) { + fmerr("Payload data is NULL during fw download\n"); + return -EINVAL; + } + if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag)) + size = + FM_CMD_MSG_HDR_SIZE + ((payload == NULL) ? 0 : payload_len); + else + size = payload_len; + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) { + fmerr("No memory to create new SKB\n"); + return -ENOMEM; + } + /* + * Don't fill FM header info for the commands which come from + * FM firmware file. + */ + if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) || + test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) { + /* Fill command header info */ + hdr = (struct fm_cmd_msg_hdr *)skb_put(skb, FM_CMD_MSG_HDR_SIZE); + hdr->hdr = FM_PKT_LOGICAL_CHAN_NUMBER; /* 0x08 */ + + /* 3 (fm_opcode,rd_wr,dlen) + payload len) */ + hdr->len = ((payload == NULL) ? 0 : payload_len) + 3; + + /* FM opcode */ + hdr->op = fm_op; + + /* read/write type */ + hdr->rd_wr = type; + hdr->dlen = payload_len; + fm_cb(skb)->fm_op = fm_op; + + /* + * If firmware download has finished and the command is + * not a read command then payload is != NULL - a write + * command with u16 payload - convert to be16 + */ + if (payload != NULL) + *(u16 *)payload = cpu_to_be16(*(u16 *)payload); + + } else if (payload != NULL) { + fm_cb(skb)->fm_op = *((u8 *)payload + 2); + } + if (payload != NULL) + memcpy(skb_put(skb, payload_len), payload, payload_len); + + fm_cb(skb)->completion = wait_completion; + skb_queue_tail(&fmdev->tx_q, skb); + tasklet_schedule(&fmdev->tx_task); + + return 0; +} + +/* Sends FM Channel-8 command to the chip and waits for the response */ +u32 fmc_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, + unsigned int payload_len, void *response, int *response_len) +{ + struct sk_buff *skb; + struct fm_event_msg_hdr *evt_hdr; + unsigned long flags; + u32 ret; + + init_completion(&fmdev->maintask_comp); + ret = fm_send_cmd(fmdev, fm_op, type, payload, payload_len, + &fmdev->maintask_comp); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&fmdev->maintask_comp, FM_DRV_TX_TIMEOUT); + if (!ret) { + fmerr("Timeout(%d sec),didn't get reg" + "completion signal from RX tasklet\n", + jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); + return -ETIMEDOUT; + } + if (!fmdev->resp_skb) { + fmerr("Reponse SKB is missing\n"); + return -EFAULT; + } + spin_lock_irqsave(&fmdev->resp_skb_lock, flags); + skb = fmdev->resp_skb; + fmdev->resp_skb = NULL; + spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); + + evt_hdr = (void *)skb->data; + if (evt_hdr->status != 0) { + fmerr("Received event pkt status(%d) is not zero\n", + evt_hdr->status); + kfree_skb(skb); + return -EIO; + } + /* Send response data to caller */ + if (response != NULL && response_len != NULL && evt_hdr->dlen) { + /* Skip header info and copy only response data */ + skb_pull(skb, sizeof(struct fm_event_msg_hdr)); + memcpy(response, skb->data, evt_hdr->dlen); + *response_len = evt_hdr->dlen; + } else if (response_len != NULL && evt_hdr->dlen == 0) { + *response_len = 0; + } + kfree_skb(skb); + + return 0; +} + +/* --- Helper functions used in FM interrupt handlers ---*/ +static inline u32 check_cmdresp_status(struct fmdev *fmdev, + struct sk_buff **skb) +{ + struct fm_event_msg_hdr *fm_evt_hdr; + unsigned long flags; + + del_timer(&fmdev->irq_info.timer); + + spin_lock_irqsave(&fmdev->resp_skb_lock, flags); + *skb = fmdev->resp_skb; + fmdev->resp_skb = NULL; + spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); + + fm_evt_hdr = (void *)(*skb)->data; + if (fm_evt_hdr->status != 0) { + fmerr("irq: opcode %x response status is not zero " + "Initiating irq recovery process\n", + fm_evt_hdr->op); + + mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT); + return -1; + } + + return 0; +} + +static inline void fm_irq_common_cmd_resp_helper(struct fmdev *fmdev, u8 stage) +{ + struct sk_buff *skb; + + if (!check_cmdresp_status(fmdev, &skb)) + fm_irq_call_stage(fmdev, stage); +} + +/* + * Interrupt process timeout handler. + * One of the irq handler did not get proper response from the chip. So take + * recovery action here. FM interrupts are disabled in the beginning of + * interrupt process. Therefore reset stage index to re-enable default + * interrupts. So that next interrupt will be processed as usual. + */ +static void int_timeout_handler(unsigned long data) +{ + struct fmdev *fmdev; + struct fm_irq *fmirq; + + fmdbg("irq: timeout,trying to re-enable fm interrupts\n"); + fmdev = (struct fmdev *)data; + fmirq = &fmdev->irq_info; + fmirq->retry++; + + if (fmirq->retry > FM_IRQ_TIMEOUT_RETRY_MAX) { + /* Stop recovery action (interrupt reenable process) and + * reset stage index & retry count values */ + fmirq->stage = 0; + fmirq->retry = 0; + fmerr("Recovery action failed during" + "irq processing, max retry reached\n"); + return; + } + fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX); +} + +/* --------- FM interrupt handlers ------------*/ +static void fm_irq_send_flag_getcmd(struct fmdev *fmdev) +{ + u16 flag; + + /* Send FLAG_GET command , to know the source of interrupt */ + if (!fm_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, sizeof(flag), NULL)) + fm_irq_timeout_stage(fmdev, FM_HANDLE_FLAG_GETCMD_RESP_IDX); +} + +static void fm_irq_handle_flag_getcmd_resp(struct fmdev *fmdev) +{ + struct sk_buff *skb; + struct fm_event_msg_hdr *fm_evt_hdr; + + if (check_cmdresp_status(fmdev, &skb)) + return; + + fm_evt_hdr = (void *)skb->data; + + /* Skip header info and copy only response data */ + skb_pull(skb, sizeof(struct fm_event_msg_hdr)); + memcpy(&fmdev->irq_info.flag, skb->data, fm_evt_hdr->dlen); + + fmdev->irq_info.flag = be16_to_cpu(fmdev->irq_info.flag); + fmdbg("irq: flag register(0x%x)\n", fmdev->irq_info.flag); + + /* Continue next function in interrupt handler table */ + fm_irq_call_stage(fmdev, FM_HW_MAL_FUNC_IDX); +} + +static void fm_irq_handle_hw_malfunction(struct fmdev *fmdev) +{ + if (fmdev->irq_info.flag & FM_MAL_EVENT & fmdev->irq_info.mask) + fmerr("irq: HW MAL int received - do nothing\n"); + + /* Continue next function in interrupt handler table */ + fm_irq_call_stage(fmdev, FM_RDS_START_IDX); +} + +static void fm_irq_handle_rds_start(struct fmdev *fmdev) +{ + if (fmdev->irq_info.flag & FM_RDS_EVENT & fmdev->irq_info.mask) { + fmdbg("irq: rds threshold reached\n"); + fmdev->irq_info.stage = FM_RDS_SEND_RDS_GETCMD_IDX; + } else { + /* Continue next function in interrupt handler table */ + fmdev->irq_info.stage = FM_HW_TUNE_OP_ENDED_IDX; + } + + fm_irq_call(fmdev); +} + +static void fm_irq_send_rdsdata_getcmd(struct fmdev *fmdev) +{ + /* Send the command to read RDS data from the chip */ + if (!fm_send_cmd(fmdev, RDS_DATA_GET, REG_RD, NULL, + (FM_RX_RDS_FIFO_THRESHOLD * 3), NULL)) + fm_irq_timeout_stage(fmdev, FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX); +} + +/* Keeps track of current RX channel AF (Alternate Frequency) */ +static void fm_rx_update_af_cache(struct fmdev *fmdev, u8 af) +{ + struct tuned_station_info *stat_info = &fmdev->rx.stat_info; + u8 reg_idx = fmdev->rx.region.fm_band; + u8 index; + u32 freq; + + /* First AF indicates the number of AF follows. Reset the list */ + if ((af >= FM_RDS_1_AF_FOLLOWS) && (af <= FM_RDS_25_AF_FOLLOWS)) { + fmdev->rx.stat_info.af_list_max = (af - FM_RDS_1_AF_FOLLOWS + 1); + fmdev->rx.stat_info.afcache_size = 0; + fmdbg("No of expected AF : %d\n", fmdev->rx.stat_info.af_list_max); + return; + } + + if (af < FM_RDS_MIN_AF) + return; + if (reg_idx == FM_BAND_EUROPE_US && af > FM_RDS_MAX_AF) + return; + if (reg_idx == FM_BAND_JAPAN && af > FM_RDS_MAX_AF_JAPAN) + return; + + freq = fmdev->rx.region.bot_freq + (af * 100); + if (freq == fmdev->rx.freq) { + fmdbg("Current freq(%d) is matching with received AF(%d)\n", + fmdev->rx.freq, freq); + return; + } + /* Do check in AF cache */ + for (index = 0; index < stat_info->afcache_size; index++) { + if (stat_info->af_cache[index] == freq) + break; + } + /* Reached the limit of the list - ignore the next AF */ + if (index == stat_info->af_list_max) { + fmdbg("AF cache is full\n"); + return; + } + /* + * If we reached the end of the list then this AF is not + * in the list - add it. + */ + if (index == stat_info->afcache_size) { + fmdbg("Storing AF %d to cache index %d\n", freq, index); + stat_info->af_cache[index] = freq; + stat_info->afcache_size++; + } +} + +/* + * Converts RDS buffer data from big endian format + * to little endian format. + */ +static void fm_rdsparse_swapbytes(struct fmdev *fmdev, + struct fm_rdsdata_format *rds_format) +{ + u8 byte1; + u8 index = 0; + u8 *rds_buff; + + /* + * Since in Orca the 2 RDS Data bytes are in little endian and + * in Dolphin they are in big endian, the parsing of the RDS data + * is chip dependent + */ + if (fmdev->asci_id != 0x6350) { + rds_buff = &rds_format->data.groupdatabuff.buff[0]; + while (index + 1 < FM_RX_RDS_INFO_FIELD_MAX) { + byte1 = rds_buff[index]; + rds_buff[index] = rds_buff[index + 1]; + rds_buff[index + 1] = byte1; + index += 2; + } + } +} + +static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev) +{ + struct sk_buff *skb; + struct fm_rdsdata_format rds_fmt; + struct fm_rds *rds = &fmdev->rx.rds; + unsigned long group_idx, flags; + u8 *rds_data, meta_data, tmpbuf[3]; + u8 type, blk_idx; + u16 cur_picode; + u32 rds_len; + + if (check_cmdresp_status(fmdev, &skb)) + return; + + /* Skip header info */ + skb_pull(skb, sizeof(struct fm_event_msg_hdr)); + rds_data = skb->data; + rds_len = skb->len; + + /* Parse the RDS data */ + while (rds_len >= FM_RDS_BLK_SIZE) { + meta_data = rds_data[2]; + /* Get the type: 0=A, 1=B, 2=C, 3=C', 4=D, 5=E */ + type = (meta_data & 0x07); + + /* Transform the blk type into index sequence (0, 1, 2, 3, 4) */ + blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1)); + fmdbg("Block index:%d(%s)\n", blk_idx, + (meta_data & FM_RDS_STATUS_ERR_MASK) ? "Bad" : "Ok"); + + if ((meta_data & FM_RDS_STATUS_ERR_MASK) != 0) + break; + + if (blk_idx < FM_RDS_BLK_IDX_A || blk_idx > FM_RDS_BLK_IDX_D) { + fmdbg("Block sequence mismatch\n"); + rds->last_blk_idx = -1; + break; + } + + /* Skip checkword (control) byte and copy only data byte */ + memcpy(&rds_fmt.data.groupdatabuff. + buff[blk_idx * (FM_RDS_BLK_SIZE - 1)], + rds_data, (FM_RDS_BLK_SIZE - 1)); + + rds->last_blk_idx = blk_idx; + + /* If completed a whole group then handle it */ + if (blk_idx == FM_RDS_BLK_IDX_D) { + fmdbg("Good block received\n"); + fm_rdsparse_swapbytes(fmdev, &rds_fmt); + + /* + * Extract PI code and store in local cache. + * We need this during AF switch processing. + */ + cur_picode = be16_to_cpu(rds_fmt.data.groupgeneral.pidata); + if (fmdev->rx.stat_info.picode != cur_picode) + fmdev->rx.stat_info.picode = cur_picode; + + fmdbg("picode:%d\n", cur_picode); + + group_idx = (rds_fmt.data.groupgeneral.blk_b[0] >> 3); + fmdbg("(fmdrv):Group:%ld%s\n", group_idx/2, + (group_idx % 2) ? "B" : "A"); + + group_idx = 1 << (rds_fmt.data.groupgeneral.blk_b[0] >> 3); + if (group_idx == FM_RDS_GROUP_TYPE_MASK_0A) { + fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[0]); + fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[1]); + } + } + rds_len -= FM_RDS_BLK_SIZE; + rds_data += FM_RDS_BLK_SIZE; + } + + /* Copy raw rds data to internal rds buffer */ + rds_data = skb->data; + rds_len = skb->len; + + spin_lock_irqsave(&fmdev->rds_buff_lock, flags); + while (rds_len > 0) { + /* + * Fill RDS buffer as per V4L2 specification. + * Store control byte + */ + type = (rds_data[2] & 0x07); + blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1)); + tmpbuf[2] = blk_idx; /* Offset name */ + tmpbuf[2] |= blk_idx << 3; /* Received offset */ + + /* Store data byte */ + tmpbuf[0] = rds_data[0]; + tmpbuf[1] = rds_data[1]; + + memcpy(&rds->buff[rds->wr_idx], &tmpbuf, FM_RDS_BLK_SIZE); + rds->wr_idx = (rds->wr_idx + FM_RDS_BLK_SIZE) % rds->buf_size; + + /* Check for overflow & start over */ + if (rds->wr_idx == rds->rd_idx) { + fmdbg("RDS buffer overflow\n"); + rds->wr_idx = 0; + rds->rd_idx = 0; + break; + } + rds_len -= FM_RDS_BLK_SIZE; + rds_data += FM_RDS_BLK_SIZE; + } + spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags); + + /* Wakeup read queue */ + if (rds->wr_idx != rds->rd_idx) + wake_up_interruptible(&rds->read_queue); + + fm_irq_call_stage(fmdev, FM_RDS_FINISH_IDX); +} + +static void fm_irq_handle_rds_finish(struct fmdev *fmdev) +{ + fm_irq_call_stage(fmdev, FM_HW_TUNE_OP_ENDED_IDX); +} + +static void fm_irq_handle_tune_op_ended(struct fmdev *fmdev) +{ + if (fmdev->irq_info.flag & (FM_FR_EVENT | FM_BL_EVENT) & fmdev-> + irq_info.mask) { + fmdbg("irq: tune ended/bandlimit reached\n"); + if (test_and_clear_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag)) { + fmdev->irq_info.stage = FM_AF_JUMP_RD_FREQ_IDX; + } else { + complete(&fmdev->maintask_comp); + fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX; + } + } else + fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX; + + fm_irq_call(fmdev); +} + +static void fm_irq_handle_power_enb(struct fmdev *fmdev) +{ + if (fmdev->irq_info.flag & FM_POW_ENB_EVENT) { + fmdbg("irq: Power Enabled/Disabled\n"); + complete(&fmdev->maintask_comp); + } + + fm_irq_call_stage(fmdev, FM_LOW_RSSI_START_IDX); +} + +static void fm_irq_handle_low_rssi_start(struct fmdev *fmdev) +{ + if ((fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) && + (fmdev->irq_info.flag & FM_LEV_EVENT & fmdev->irq_info.mask) && + (fmdev->rx.freq != FM_UNDEFINED_FREQ) && + (fmdev->rx.stat_info.afcache_size != 0)) { + fmdbg("irq: rssi level has fallen below threshold level\n"); + + /* Disable further low RSSI interrupts */ + fmdev->irq_info.mask &= ~FM_LEV_EVENT; + + fmdev->rx.afjump_idx = 0; + fmdev->rx.freq_before_jump = fmdev->rx.freq; + fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX; + } else { + /* Continue next function in interrupt handler table */ + fmdev->irq_info.stage = FM_SEND_INTMSK_CMD_IDX; + } + + fm_irq_call(fmdev); +} + +static void fm_irq_afjump_set_pi(struct fmdev *fmdev) +{ + u16 payload; + + /* Set PI code - must be updated if the AF list is not empty */ + payload = fmdev->rx.stat_info.picode; + if (!fm_send_cmd(fmdev, RDS_PI_SET, REG_WR, &payload, sizeof(payload), NULL)) + fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_RESP_IDX); +} + +static void fm_irq_handle_set_pi_resp(struct fmdev *fmdev) +{ + fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SETPI_MASK_IDX); +} + +/* + * Set PI mask. + * 0xFFFF = Enable PI code matching + * 0x0000 = Disable PI code matching + */ +static void fm_irq_afjump_set_pimask(struct fmdev *fmdev) +{ + u16 payload; + + payload = 0x0000; + if (!fm_send_cmd(fmdev, RDS_PI_MASK_SET, REG_WR, &payload, sizeof(payload), NULL)) + fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX); +} + +static void fm_irq_handle_set_pimask_resp(struct fmdev *fmdev) +{ + fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SET_AF_FREQ_IDX); +} + +static void fm_irq_afjump_setfreq(struct fmdev *fmdev) +{ + u16 frq_index; + u16 payload; + + fmdbg("Swtich to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]); + frq_index = (fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx] - + fmdev->rx.region.bot_freq) / FM_FREQ_MUL; + + payload = frq_index; + if (!fm_send_cmd(fmdev, AF_FREQ_SET, REG_WR, &payload, sizeof(payload), NULL)) + fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX); +} + +static void fm_irq_handle_setfreq_resp(struct fmdev *fmdev) +{ + fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_ENABLE_INT_IDX); +} + +static void fm_irq_afjump_enableint(struct fmdev *fmdev) +{ + u16 payload; + + /* Enable FR (tuning operation ended) interrupt */ + payload = FM_FR_EVENT; + if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, sizeof(payload), NULL)) + fm_irq_timeout_stage(fmdev, FM_AF_JUMP_ENABLE_INT_RESP_IDX); +} + +static void fm_irq_afjump_enableint_resp(struct fmdev *fmdev) +{ + fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_START_AFJUMP_IDX); +} + +static void fm_irq_start_afjump(struct fmdev *fmdev) +{ + u16 payload; + + payload = FM_TUNER_AF_JUMP_MODE; + if (!fm_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload, + sizeof(payload), NULL)) + fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX); +} + +static void fm_irq_handle_start_afjump_resp(struct fmdev *fmdev) +{ + struct sk_buff *skb; + + if (check_cmdresp_status(fmdev, &skb)) + return; + + fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX; + set_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag); + clear_bit(FM_INTTASK_RUNNING, &fmdev->flag); +} + +static void fm_irq_afjump_rd_freq(struct fmdev *fmdev) +{ + u16 payload; + + if (!fm_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, sizeof(payload), NULL)) + fm_irq_timeout_stage(fmdev, FM_AF_JUMP_RD_FREQ_RESP_IDX); +} + +static void fm_irq_afjump_rd_freq_resp(struct fmdev *fmdev) +{ + struct sk_buff *skb; + u16 read_freq; + u32 curr_freq, jumped_freq; + + if (check_cmdresp_status(fmdev, &skb)) + return; + + /* Skip header info and copy only response data */ + skb_pull(skb, sizeof(struct fm_event_msg_hdr)); + memcpy(&read_freq, skb->data, sizeof(read_freq)); + read_freq = be16_to_cpu(read_freq); + curr_freq = fmdev->rx.region.bot_freq + ((u32)read_freq * FM_FREQ_MUL); + + jumped_freq = fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]; + + /* If the frequency was changed the jump succeeded */ + if ((curr_freq != fmdev->rx.freq_before_jump) && (curr_freq == jumped_freq)) { + fmdbg("Successfully switched to alternate freq %d\n", curr_freq); + fmdev->rx.freq = curr_freq; + fm_rx_reset_rds_cache(fmdev); + + /* AF feature is on, enable low level RSSI interrupt */ + if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) + fmdev->irq_info.mask |= FM_LEV_EVENT; + + fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX; + } else { /* jump to the next freq in the AF list */ + fmdev->rx.afjump_idx++; + + /* If we reached the end of the list - stop searching */ + if (fmdev->rx.afjump_idx >= fmdev->rx.stat_info.afcache_size) { + fmdbg("AF switch processing failed\n"); + fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX; + } else { /* AF List is not over - try next one */ + + fmdbg("Trying next freq in AF cache\n"); + fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX; + } + } + fm_irq_call(fmdev); +} + +static void fm_irq_handle_low_rssi_finish(struct fmdev *fmdev) +{ + fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX); +} + +static void fm_irq_send_intmsk_cmd(struct fmdev *fmdev) +{ + u16 payload; + + /* Re-enable FM interrupts */ + payload = fmdev->irq_info.mask; + + if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, + sizeof(payload), NULL)) + fm_irq_timeout_stage(fmdev, FM_HANDLE_INTMSK_CMD_RESP_IDX); +} + +static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *fmdev) +{ + struct sk_buff *skb; + + if (check_cmdresp_status(fmdev, &skb)) + return; + /* + * This is last function in interrupt table to be executed. + * So, reset stage index to 0. + */ + fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX; + + /* Start processing any pending interrupt */ + if (test_and_clear_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag)) + fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev); + else + clear_bit(FM_INTTASK_RUNNING, &fmdev->flag); +} + +/* Returns availability of RDS data in internel buffer */ +u32 fmc_is_rds_data_available(struct fmdev *fmdev, struct file *file, + struct poll_table_struct *pts) +{ + poll_wait(file, &fmdev->rx.rds.read_queue, pts); + if (fmdev->rx.rds.rd_idx != fmdev->rx.rds.wr_idx) + return 0; + + return -EAGAIN; +} + +/* Copies RDS data from internal buffer to user buffer */ +u32 fmc_transfer_rds_from_internal_buff(struct fmdev *fmdev, struct file *file, + u8 __user *buf, size_t count) +{ + u32 block_count; + unsigned long flags; + int ret; + + if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) { + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + ret = wait_event_interruptible(fmdev->rx.rds.read_queue, + (fmdev->rx.rds.wr_idx != fmdev->rx.rds.rd_idx)); + if (ret) + return -EINTR; + } + + /* Calculate block count from byte count */ + count /= 3; + block_count = 0; + ret = 0; + + spin_lock_irqsave(&fmdev->rds_buff_lock, flags); + + while (block_count < count) { + if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) + break; + + if (copy_to_user(buf, &fmdev->rx.rds.buff[fmdev->rx.rds.rd_idx], + FM_RDS_BLK_SIZE)) + break; + + fmdev->rx.rds.rd_idx += FM_RDS_BLK_SIZE; + if (fmdev->rx.rds.rd_idx >= fmdev->rx.rds.buf_size) + fmdev->rx.rds.rd_idx = 0; + + block_count++; + buf += FM_RDS_BLK_SIZE; + ret += FM_RDS_BLK_SIZE; + } + spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags); + return ret; +} + +u32 fmc_set_freq(struct fmdev *fmdev, u32 freq_to_set) +{ + switch (fmdev->curr_fmmode) { + case FM_MODE_RX: + return fm_rx_set_freq(fmdev, freq_to_set); + + case FM_MODE_TX: + return fm_tx_set_freq(fmdev, freq_to_set); + + default: + return -EINVAL; + } +} + +u32 fmc_get_freq(struct fmdev *fmdev, u32 *cur_tuned_frq) +{ + if (fmdev->rx.freq == FM_UNDEFINED_FREQ) { + fmerr("RX frequency is not set\n"); + return -EPERM; + } + if (cur_tuned_frq == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + switch (fmdev->curr_fmmode) { + case FM_MODE_RX: + *cur_tuned_frq = fmdev->rx.freq; + return 0; + + case FM_MODE_TX: + *cur_tuned_frq = 0; /* TODO : Change this later */ + return 0; + + default: + return -EINVAL; + } + +} + +u32 fmc_set_region(struct fmdev *fmdev, u8 region_to_set) +{ + switch (fmdev->curr_fmmode) { + case FM_MODE_RX: + return fm_rx_set_region(fmdev, region_to_set); + + case FM_MODE_TX: + return fm_tx_set_region(fmdev, region_to_set); + + default: + return -EINVAL; + } +} + +u32 fmc_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) +{ + switch (fmdev->curr_fmmode) { + case FM_MODE_RX: + return fm_rx_set_mute_mode(fmdev, mute_mode_toset); + + case FM_MODE_TX: + return fm_tx_set_mute_mode(fmdev, mute_mode_toset); + + default: + return -EINVAL; + } +} + +u32 fmc_set_stereo_mono(struct fmdev *fmdev, u16 mode) +{ + switch (fmdev->curr_fmmode) { + case FM_MODE_RX: + return fm_rx_set_stereo_mono(fmdev, mode); + + case FM_MODE_TX: + return fm_tx_set_stereo_mono(fmdev, mode); + + default: + return -EINVAL; + } +} + +u32 fmc_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) +{ + switch (fmdev->curr_fmmode) { + case FM_MODE_RX: + return fm_rx_set_rds_mode(fmdev, rds_en_dis); + + case FM_MODE_TX: + return fm_tx_set_rds_mode(fmdev, rds_en_dis); + + default: + return -EINVAL; + } +} + +/* Sends power off command to the chip */ +static u32 fm_power_down(struct fmdev *fmdev) +{ + u16 payload; + u32 ret; + + if (!test_bit(FM_CORE_READY, &fmdev->flag)) { + fmerr("FM core is not ready\n"); + return -EPERM; + } + if (fmdev->curr_fmmode == FM_MODE_OFF) { + fmdbg("FM chip is already in OFF state\n"); + return 0; + } + + payload = 0x0; + ret = fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + return fmc_release(fmdev); +} + +/* Reads init command from FM firmware file and loads to the chip */ +static u32 fm_download_firmware(struct fmdev *fmdev, const u8 *fw_name) +{ + const struct firmware *fw_entry; + struct bts_header *fw_header; + struct bts_action *action; + struct bts_action_delay *delay; + u8 *fw_data; + int ret, fw_len, cmd_cnt; + + cmd_cnt = 0; + set_bit(FM_FW_DW_INPROGRESS, &fmdev->flag); + + ret = request_firmware(&fw_entry, fw_name, + &fmdev->radio_dev->dev); + if (ret < 0) { + fmerr("Unable to read firmware(%s) content\n", fw_name); + return ret; + } + fmdbg("Firmware(%s) length : %d bytes\n", fw_name, fw_entry->size); + + fw_data = (void *)fw_entry->data; + fw_len = fw_entry->size; + + fw_header = (struct bts_header *)fw_data; + if (fw_header->magic != FM_FW_FILE_HEADER_MAGIC) { + fmerr("%s not a legal TI firmware file\n", fw_name); + ret = -EINVAL; + goto rel_fw; + } + fmdbg("FW(%s) magic number : 0x%x\n", fw_name, fw_header->magic); + + /* Skip file header info , we already verified it */ + fw_data += sizeof(struct bts_header); + fw_len -= sizeof(struct bts_header); + + while (fw_data && fw_len > 0) { + action = (struct bts_action *)fw_data; + + switch (action->type) { + case ACTION_SEND_COMMAND: /* Send */ + if (fmc_send_cmd(fmdev, 0, 0, action->data, + action->size, NULL, NULL)) + goto rel_fw; + + cmd_cnt++; + break; + + case ACTION_DELAY: /* Delay */ + delay = (struct bts_action_delay *)action->data; + mdelay(delay->msec); + break; + } + + fw_data += (sizeof(struct bts_action) + (action->size)); + fw_len -= (sizeof(struct bts_action) + (action->size)); + } + fmdbg("Firmware commands(%d) loaded to chip\n", cmd_cnt); +rel_fw: + release_firmware(fw_entry); + clear_bit(FM_FW_DW_INPROGRESS, &fmdev->flag); + + return ret; +} + +/* Loads default RX configuration to the chip */ +static u32 load_default_rx_configuration(struct fmdev *fmdev) +{ + int ret; + + ret = fm_rx_set_volume(fmdev, FM_DEFAULT_RX_VOLUME); + if (ret < 0) + return ret; + + return fm_rx_set_rssi_threshold(fmdev, FM_DEFAULT_RSSI_THRESHOLD); +} + +/* Does FM power on sequence */ +static u32 fm_power_up(struct fmdev *fmdev, u8 mode) +{ + u16 payload, asic_id, asic_ver; + int resp_len, ret; + u8 fw_name[50]; + + if (mode >= FM_MODE_ENTRY_MAX) { + fmerr("Invalid firmware download option\n"); + return -EINVAL; + } + + /* + * Initialize FM common module. FM GPIO toggling is + * taken care in Shared Transport driver. + */ + ret = fmc_prepare(fmdev); + if (ret < 0) { + fmerr("Unable to prepare FM Common\n"); + return ret; + } + + payload = FM_ENABLE; + if (fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload, + sizeof(payload), NULL, NULL)) + goto rel; + + /* Allow the chip to settle down in Channel-8 mode */ + msleep(20); + + if (fmc_send_cmd(fmdev, ASIC_ID_GET, REG_RD, NULL, + sizeof(asic_id), &asic_id, &resp_len)) + goto rel; + + if (fmc_send_cmd(fmdev, ASIC_VER_GET, REG_RD, NULL, + sizeof(asic_ver), &asic_ver, &resp_len)) + goto rel; + + fmdbg("ASIC ID: 0x%x , ASIC Version: %d\n", + be16_to_cpu(asic_id), be16_to_cpu(asic_ver)); + + sprintf(fw_name, "%s_%x.%d.bts", FM_FMC_FW_FILE_START, + be16_to_cpu(asic_id), be16_to_cpu(asic_ver)); + + ret = fm_download_firmware(fmdev, fw_name); + if (ret < 0) { + fmdbg("Failed to download firmware file %s\n", fw_name); + goto rel; + } + sprintf(fw_name, "%s_%x.%d.bts", (mode == FM_MODE_RX) ? + FM_RX_FW_FILE_START : FM_TX_FW_FILE_START, + be16_to_cpu(asic_id), be16_to_cpu(asic_ver)); + + ret = fm_download_firmware(fmdev, fw_name); + if (ret < 0) { + fmdbg("Failed to download firmware file %s\n", fw_name); + goto rel; + } else + return ret; +rel: + return fmc_release(fmdev); +} + +/* Set FM Modes(TX, RX, OFF) */ +u32 fmc_set_mode(struct fmdev *fmdev, u8 fm_mode) +{ + int ret = 0; + + if (fm_mode >= FM_MODE_ENTRY_MAX) { + fmerr("Invalid FM mode\n"); + return -EINVAL; + } + if (fmdev->curr_fmmode == fm_mode) { + fmdbg("Already fm is in mode(%d)\n", fm_mode); + return ret; + } + + switch (fm_mode) { + case FM_MODE_OFF: /* OFF Mode */ + ret = fm_power_down(fmdev); + if (ret < 0) { + fmerr("Failed to set OFF mode\n"); + return ret; + } + break; + + case FM_MODE_TX: /* TX Mode */ + case FM_MODE_RX: /* RX Mode */ + /* Power down before switching to TX or RX mode */ + if (fmdev->curr_fmmode != FM_MODE_OFF) { + ret = fm_power_down(fmdev); + if (ret < 0) { + fmerr("Failed to set OFF mode\n"); + return ret; + } + msleep(30); + } + ret = fm_power_up(fmdev, fm_mode); + if (ret < 0) { + fmerr("Failed to load firmware\n"); + return ret; + } + } + fmdev->curr_fmmode = fm_mode; + + /* Set default configuration */ + if (fmdev->curr_fmmode == FM_MODE_RX) { + fmdbg("Loading default rx configuration..\n"); + ret = load_default_rx_configuration(fmdev); + if (ret < 0) + fmerr("Failed to load default values\n"); + } + + return ret; +} + +/* Returns current FM mode (TX, RX, OFF) */ +u32 fmc_get_mode(struct fmdev *fmdev, u8 *fmmode) +{ + if (!test_bit(FM_CORE_READY, &fmdev->flag)) { + fmerr("FM core is not ready\n"); + return -EPERM; + } + if (fmmode == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + *fmmode = fmdev->curr_fmmode; + return 0; +} + +/* Called by ST layer when FM packet is available */ +static long fm_st_receive(void *arg, struct sk_buff *skb) +{ + struct fmdev *fmdev; + + fmdev = (struct fmdev *)arg; + + if (skb == NULL) { + fmerr("Invalid SKB received from ST\n"); + return -EFAULT; + } + + if (skb->cb[0] != FM_PKT_LOGICAL_CHAN_NUMBER) { + fmerr("Received SKB (%p) is not FM Channel 8 pkt\n", skb); + return -EINVAL; + } + + memcpy(skb_push(skb, 1), &skb->cb[0], 1); + skb_queue_tail(&fmdev->rx_q, skb); + tasklet_schedule(&fmdev->rx_task); + + return 0; +} + +/* + * Called by ST layer to indicate protocol registration completion + * status. + */ +static void fm_st_reg_comp_cb(void *arg, char data) +{ + struct fmdev *fmdev; + + fmdev = (struct fmdev *)arg; + fmdev->streg_cbdata = data; + complete(&wait_for_fmdrv_reg_comp); +} + +/* + * This function will be called from FM V4L2 open function. + * Register with ST driver and initialize driver data. + */ +u32 fmc_prepare(struct fmdev *fmdev) +{ + static struct st_proto_s fm_st_proto; + u32 ret; + + if (test_bit(FM_CORE_READY, &fmdev->flag)) { + fmdbg("FM Core is already up\n"); + return 0; + } + + memset(&fm_st_proto, 0, sizeof(fm_st_proto)); + fm_st_proto.type = ST_FM; + fm_st_proto.recv = fm_st_receive; + fm_st_proto.match_packet = NULL; + fm_st_proto.reg_complete_cb = fm_st_reg_comp_cb; + fm_st_proto.write = NULL; /* TI ST driver will fill write pointer */ + fm_st_proto.priv_data = fmdev; + + ret = st_register(&fm_st_proto); + if (ret == -EINPROGRESS) { + init_completion(&wait_for_fmdrv_reg_comp); + fmdev->streg_cbdata = -EINPROGRESS; + fmdbg("%s waiting for ST reg completion signal\n", __func__); + + ret = wait_for_completion_timeout(&wait_for_fmdrv_reg_comp, + FM_ST_REG_TIMEOUT); + + if (!ret) { + fmerr("Timeout(%d sec), didn't get reg " + "completion signal from ST\n", + jiffies_to_msecs(FM_ST_REG_TIMEOUT) / 1000); + return -ETIMEDOUT; + } + if (fmdev->streg_cbdata != 0) { + fmerr("ST reg comp CB called with error " + "status %d\n", fmdev->streg_cbdata); + return -EAGAIN; + } + + ret = 0; + } else if (ret == -1) { + fmerr("st_register failed %d\n", ret); + return -EAGAIN; + } + + if (fm_st_proto.write != NULL) { + g_st_write = fm_st_proto.write; + } else { + fmerr("Failed to get ST write func pointer\n"); + ret = st_unregister(ST_FM); + if (ret < 0) + fmerr("st_unregister failed %d\n", ret); + return -EAGAIN; + } + + spin_lock_init(&fmdev->rds_buff_lock); + spin_lock_init(&fmdev->resp_skb_lock); + + /* Initialize TX queue and TX tasklet */ + skb_queue_head_init(&fmdev->tx_q); + tasklet_init(&fmdev->tx_task, send_tasklet, (unsigned long)fmdev); + + /* Initialize RX Queue and RX tasklet */ + skb_queue_head_init(&fmdev->rx_q); + tasklet_init(&fmdev->rx_task, recv_tasklet, (unsigned long)fmdev); + + fmdev->irq_info.stage = 0; + atomic_set(&fmdev->tx_cnt, 1); + fmdev->resp_comp = NULL; + + init_timer(&fmdev->irq_info.timer); + fmdev->irq_info.timer.function = &int_timeout_handler; + fmdev->irq_info.timer.data = (unsigned long)fmdev; + /*TODO: add FM_STIC_EVENT later */ + fmdev->irq_info.mask = FM_MAL_EVENT; + + /* Region info */ + memcpy(&fmdev->rx.region, ®ion_configs[default_radio_region], + sizeof(struct region_info)); + + fmdev->rx.mute_mode = FM_MUTE_OFF; + fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF; + fmdev->rx.rds.flag = FM_RDS_DISABLE; + fmdev->rx.freq = FM_UNDEFINED_FREQ; + fmdev->rx.rds_mode = FM_RDS_SYSTEM_RDS; + fmdev->rx.af_mode = FM_RX_RDS_AF_SWITCH_MODE_OFF; + fmdev->irq_info.retry = 0; + + fm_rx_reset_rds_cache(fmdev); + init_waitqueue_head(&fmdev->rx.rds.read_queue); + + fm_rx_reset_station_info(fmdev); + set_bit(FM_CORE_READY, &fmdev->flag); + + return ret; +} + +/* + * This function will be called from FM V4L2 release function. + * Unregister from ST driver. + */ +u32 fmc_release(struct fmdev *fmdev) +{ + u32 ret; + + if (!test_bit(FM_CORE_READY, &fmdev->flag)) { + fmdbg("FM Core is already down\n"); + return 0; + } + /* Sevice pending read */ + wake_up_interruptible(&fmdev->rx.rds.read_queue); + + tasklet_kill(&fmdev->tx_task); + tasklet_kill(&fmdev->rx_task); + + skb_queue_purge(&fmdev->tx_q); + skb_queue_purge(&fmdev->rx_q); + + fmdev->resp_comp = NULL; + fmdev->rx.freq = 0; + + ret = st_unregister(ST_FM); + if (ret < 0) + fmerr("Failed to de-register FM from ST %d\n", ret); + else + fmdbg("Successfully unregistered from ST\n"); + + clear_bit(FM_CORE_READY, &fmdev->flag); + return ret; +} + +/* + * Module init function. Ask FM V4L module to register video device. + * Allocate memory for FM driver context and RX RDS buffer. + */ +static int __init fm_drv_init(void) +{ + struct fmdev *fmdev = NULL; + u32 ret = -ENOMEM; + + fmdbg("FM driver version %s\n", FM_DRV_VERSION); + + fmdev = kzalloc(sizeof(struct fmdev), GFP_KERNEL); + if (NULL == fmdev) { + fmerr("Can't allocate operation structure memory\n"); + return ret; + } + fmdev->rx.rds.buf_size = default_rds_buf * FM_RDS_BLK_SIZE; + fmdev->rx.rds.buff = kzalloc(fmdev->rx.rds.buf_size, GFP_KERNEL); + if (NULL == fmdev->rx.rds.buff) { + fmerr("Can't allocate rds ring buffer\n"); + goto rel_dev; + } + + ret = fm_v4l2_init_video_device(fmdev, radio_nr); + if (ret < 0) + goto rel_rdsbuf; + + fmdev->irq_info.handlers = int_handler_table; + fmdev->curr_fmmode = FM_MODE_OFF; + fmdev->tx_data.pwr_lvl = FM_PWR_LVL_DEF; + fmdev->tx_data.preemph = FM_TX_PREEMPH_50US; + return ret; + +rel_rdsbuf: + kfree(fmdev->rx.rds.buff); +rel_dev: + kfree(fmdev); + + return ret; +} + +/* Module exit function. Ask FM V4L module to unregister video device */ +static void __exit fm_drv_exit(void) +{ + struct fmdev *fmdev = NULL; + + fmdev = fm_v4l2_deinit_video_device(); + if (fmdev != NULL) { + kfree(fmdev->rx.rds.buff); + kfree(fmdev); + } +} + +module_init(fm_drv_init); +module_exit(fm_drv_exit); + +/* ------------- Module Info ------------- */ +MODULE_AUTHOR("Manjunatha Halli "); +MODULE_DESCRIPTION("FM Driver for TI's Connectivity chip. " FM_DRV_VERSION); +MODULE_VERSION(FM_DRV_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/radio/wl128x/fmdrv_common.h b/drivers/media/radio/wl128x/fmdrv_common.h new file mode 100644 index 0000000..427c416 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_common.h @@ -0,0 +1,402 @@ +/* + * FM Driver for Connectivity chip of Texas Instruments. + * FM Common module header file + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _FMDRV_COMMON_H +#define _FMDRV_COMMON_H + +#define FM_ST_REG_TIMEOUT msecs_to_jiffies(6000) /* 6 sec */ +#define FM_PKT_LOGICAL_CHAN_NUMBER 0x08 /* Logical channel 8 */ + +#define REG_RD 0x1 +#define REG_WR 0x0 + +struct fm_reg_table { + u8 opcode; + u8 type; + u8 *name; +}; + +#define STEREO_GET 0 +#define RSSI_LVL_GET 1 +#define IF_COUNT_GET 2 +#define FLAG_GET 3 +#define RDS_SYNC_GET 4 +#define RDS_DATA_GET 5 +#define FREQ_SET 10 +#define AF_FREQ_SET 11 +#define MOST_MODE_SET 12 +#define MOST_BLEND_SET 13 +#define DEMPH_MODE_SET 14 +#define SEARCH_LVL_SET 15 +#define BAND_SET 16 +#define MUTE_STATUS_SET 17 +#define RDS_PAUSE_LVL_SET 18 +#define RDS_PAUSE_DUR_SET 19 +#define RDS_MEM_SET 20 +#define RDS_BLK_B_SET 21 +#define RDS_MSK_B_SET 22 +#define RDS_PI_MASK_SET 23 +#define RDS_PI_SET 24 +#define RDS_SYSTEM_SET 25 +#define INT_MASK_SET 26 +#define SEARCH_DIR_SET 27 +#define VOLUME_SET 28 +#define AUDIO_ENABLE_SET 29 +#define PCM_MODE_SET 30 +#define I2S_MODE_CONFIG_SET 31 +#define POWER_SET 32 +#define INTX_CONFIG_SET 33 +#define PULL_EN_SET 34 +#define HILO_SET 35 +#define SWITCH2FREF 36 +#define FREQ_DRIFT_REPORT 37 + +#define PCE_GET 40 +#define FIRM_VER_GET 41 +#define ASIC_VER_GET 42 +#define ASIC_ID_GET 43 +#define MAN_ID_GET 44 +#define TUNER_MODE_SET 45 +#define STOP_SEARCH 46 +#define RDS_CNTRL_SET 47 + +#define WRITE_HARDWARE_REG 100 +#define CODE_DOWNLOAD 101 +#define RESET 102 + +#define FM_POWER_MODE 254 +#define FM_INTERRUPT 255 + +/* Transmitter API */ + +#define CHANL_SET 55 +#define CHANL_BW_SET 56 +#define REF_SET 57 +#define POWER_ENB_SET 90 +#define POWER_ATT_SET 58 +#define POWER_LEV_SET 59 +#define AUDIO_DEV_SET 60 +#define PILOT_DEV_SET 61 +#define RDS_DEV_SET 62 +#define TX_BAND_SET 65 +#define PUPD_SET 91 +#define AUDIO_IO_SET 63 +#define PREMPH_SET 64 +#define MONO_SET 66 +#define MUTE 92 +#define MPX_LMT_ENABLE 67 +#define PI_SET 93 +#define ECC_SET 69 +#define PTY 70 +#define AF 71 +#define DISPLAY_MODE 74 +#define RDS_REP_SET 77 +#define RDS_CONFIG_DATA_SET 98 +#define RDS_DATA_SET 99 +#define RDS_DATA_ENB 94 +#define TA_SET 78 +#define TP_SET 79 +#define DI_SET 80 +#define MS_SET 81 +#define PS_SCROLL_SPEED 82 +#define TX_AUDIO_LEVEL_TEST 96 +#define TX_AUDIO_LEVEL_TEST_THRESHOLD 73 +#define TX_AUDIO_INPUT_LEVEL_RANGE_SET 54 +#define RX_ANTENNA_SELECT 87 +#define I2C_DEV_ADDR_SET 86 +#define REF_ERR_CALIB_PARAM_SET 88 +#define REF_ERR_CALIB_PERIODICITY_SET 89 +#define SOC_INT_TRIGGER 52 +#define SOC_AUDIO_PATH_SET 83 +#define SOC_PCMI_OVERRIDE 84 +#define SOC_I2S_OVERRIDE 85 +#define RSSI_BLOCK_SCAN_FREQ_SET 95 +#define RSSI_BLOCK_SCAN_START 97 +#define RSSI_BLOCK_SCAN_DATA_GET 5 +#define READ_FMANT_TUNE_VALUE 104 + +/* SKB helpers */ +struct fm_skb_cb { + __u8 fm_op; + struct completion *completion; +}; + +#define fm_cb(skb) ((struct fm_skb_cb *)(skb->cb)) + +/* FM Channel-8 command message format */ +struct fm_cmd_msg_hdr { + __u8 hdr; /* Logical Channel-8 */ + __u8 len; /* Number of bytes follows */ + __u8 op; /* FM Opcode */ + __u8 rd_wr; /* Read/Write command */ + __u8 dlen; /* Length of payload */ +} __attribute__ ((packed)); + +#define FM_CMD_MSG_HDR_SIZE 5 /* sizeof(struct fm_cmd_msg_hdr) */ + +/* FM Channel-8 event messgage format */ +struct fm_event_msg_hdr { + __u8 header; /* Logical Channel-8 */ + __u8 len; /* Number of bytes follows */ + __u8 status; /* Event status */ + __u8 num_fm_hci_cmds; /* Number of pkts the host allowed to send */ + __u8 op; /* FM Opcode */ + __u8 rd_wr; /* Read/Write command */ + __u8 dlen; /* Length of payload */ +} __attribute__ ((packed)); + +#define FM_EVT_MSG_HDR_SIZE 7 /* sizeof(struct fm_event_msg_hdr) */ + +/* TI's magic number in firmware file */ +#define FM_FW_FILE_HEADER_MAGIC 0x42535442 + +#define FM_ENABLE 1 +#define FM_DISABLE 0 + +/* FLAG_GET register bits */ +#define FM_FR_EVENT (1 << 0) +#define FM_BL_EVENT (1 << 1) +#define FM_RDS_EVENT (1 << 2) +#define FM_BBLK_EVENT (1 << 3) +#define FM_LSYNC_EVENT (1 << 4) +#define FM_LEV_EVENT (1 << 5) +#define FM_IFFR_EVENT (1 << 6) +#define FM_PI_EVENT (1 << 7) +#define FM_PD_EVENT (1 << 8) +#define FM_STIC_EVENT (1 << 9) +#define FM_MAL_EVENT (1 << 10) +#define FM_POW_ENB_EVENT (1 << 11) + +/* + * Firmware files of FM. ASIC ID and ASIC version will be appened to this, + * later. + */ +#define FM_FMC_FW_FILE_START ("fmc_ch8") +#define FM_RX_FW_FILE_START ("fm_rx_ch8") +#define FM_TX_FW_FILE_START ("fm_tx_ch8") + +#define FM_UNDEFINED_FREQ 0xFFFFFFFF + +/* Band types */ +#define FM_BAND_EUROPE_US 0 +#define FM_BAND_JAPAN 1 + +/* Seek directions */ +#define FM_SEARCH_DIRECTION_DOWN 0 +#define FM_SEARCH_DIRECTION_UP 1 + +/* Tunner modes */ +#define FM_TUNER_STOP_SEARCH_MODE 0 +#define FM_TUNER_PRESET_MODE 1 +#define FM_TUNER_AUTONOMOUS_SEARCH_MODE 2 +#define FM_TUNER_AF_JUMP_MODE 3 + +/* Min and Max volume */ +#define FM_RX_VOLUME_MIN 0 +#define FM_RX_VOLUME_MAX 70 + +/* Volume gain step */ +#define FM_RX_VOLUME_GAIN_STEP 0x370 + +/* Mute modes */ +#define FM_MUTE_ON 0 +#define FM_MUTE_OFF 1 +#define FM_MUTE_ATTENUATE 2 + +#define FM_RX_UNMUTE_MODE 0x00 +#define FM_RX_RF_DEP_MODE 0x01 +#define FM_RX_AC_MUTE_MODE 0x02 +#define FM_RX_HARD_MUTE_LEFT_MODE 0x04 +#define FM_RX_HARD_MUTE_RIGHT_MODE 0x08 +#define FM_RX_SOFT_MUTE_FORCE_MODE 0x10 + +/* RF dependent mute mode */ +#define FM_RX_RF_DEPENDENT_MUTE_ON 1 +#define FM_RX_RF_DEPENDENT_MUTE_OFF 0 + +/* RSSI threshold min and max */ +#define FM_RX_RSSI_THRESHOLD_MIN -128 +#define FM_RX_RSSI_THRESHOLD_MAX 127 + +/* Stereo/Mono mode */ +#define FM_STEREO_MODE 0 +#define FM_MONO_MODE 1 +#define FM_STEREO_SOFT_BLEND 1 + +/* FM RX De-emphasis filter modes */ +#define FM_RX_EMPHASIS_FILTER_50_USEC 0 +#define FM_RX_EMPHASIS_FILTER_75_USEC 1 + +/* FM RDS modes */ +#define FM_RDS_DISABLE 0 +#define FM_RDS_ENABLE 1 + +#define FM_NO_PI_CODE 0 + +/* FM and RX RDS block enable/disable */ +#define FM_RX_PWR_SET_FM_ON_RDS_OFF 0x1 +#define FM_RX_PWR_SET_FM_AND_RDS_BLK_ON 0x3 +#define FM_RX_PWR_SET_FM_AND_RDS_BLK_OFF 0x0 + +/* RX RDS */ +#define FM_RX_RDS_FLUSH_FIFO 0x1 +#define FM_RX_RDS_FIFO_THRESHOLD 64 /* tuples */ +#define FM_RDS_BLK_SIZE 3 /* 3 bytes */ + +/* RDS block types */ +#define FM_RDS_BLOCK_A 0 +#define FM_RDS_BLOCK_B 1 +#define FM_RDS_BLOCK_C 2 +#define FM_RDS_BLOCK_Ctag 3 +#define FM_RDS_BLOCK_D 4 +#define FM_RDS_BLOCK_E 5 + +#define FM_RDS_BLK_IDX_A 0 +#define FM_RDS_BLK_IDX_B 1 +#define FM_RDS_BLK_IDX_C 2 +#define FM_RDS_BLK_IDX_D 3 +#define FM_RDS_BLK_IDX_UNKNOWN 0xF0 + +#define FM_RDS_STATUS_ERR_MASK 0x18 + +/* + * Represents an RDS group type & version. + * There are 15 groups, each group has 2 versions: A and B. + */ +#define FM_RDS_GROUP_TYPE_MASK_0A ((unsigned long)1<<0) +#define FM_RDS_GROUP_TYPE_MASK_0B ((unsigned long)1<<1) +#define FM_RDS_GROUP_TYPE_MASK_1A ((unsigned long)1<<2) +#define FM_RDS_GROUP_TYPE_MASK_1B ((unsigned long)1<<3) +#define FM_RDS_GROUP_TYPE_MASK_2A ((unsigned long)1<<4) +#define FM_RDS_GROUP_TYPE_MASK_2B ((unsigned long)1<<5) +#define FM_RDS_GROUP_TYPE_MASK_3A ((unsigned long)1<<6) +#define FM_RDS_GROUP_TYPE_MASK_3B ((unsigned long)1<<7) +#define FM_RDS_GROUP_TYPE_MASK_4A ((unsigned long)1<<8) +#define FM_RDS_GROUP_TYPE_MASK_4B ((unsigned long)1<<9) +#define FM_RDS_GROUP_TYPE_MASK_5A ((unsigned long)1<<10) +#define FM_RDS_GROUP_TYPE_MASK_5B ((unsigned long)1<<11) +#define FM_RDS_GROUP_TYPE_MASK_6A ((unsigned long)1<<12) +#define FM_RDS_GROUP_TYPE_MASK_6B ((unsigned long)1<<13) +#define FM_RDS_GROUP_TYPE_MASK_7A ((unsigned long)1<<14) +#define FM_RDS_GROUP_TYPE_MASK_7B ((unsigned long)1<<15) +#define FM_RDS_GROUP_TYPE_MASK_8A ((unsigned long)1<<16) +#define FM_RDS_GROUP_TYPE_MASK_8B ((unsigned long)1<<17) +#define FM_RDS_GROUP_TYPE_MASK_9A ((unsigned long)1<<18) +#define FM_RDS_GROUP_TYPE_MASK_9B ((unsigned long)1<<19) +#define FM_RDS_GROUP_TYPE_MASK_10A ((unsigned long)1<<20) +#define FM_RDS_GROUP_TYPE_MASK_10B ((unsigned long)1<<21) +#define FM_RDS_GROUP_TYPE_MASK_11A ((unsigned long)1<<22) +#define FM_RDS_GROUP_TYPE_MASK_11B ((unsigned long)1<<23) +#define FM_RDS_GROUP_TYPE_MASK_12A ((unsigned long)1<<24) +#define FM_RDS_GROUP_TYPE_MASK_12B ((unsigned long)1<<25) +#define FM_RDS_GROUP_TYPE_MASK_13A ((unsigned long)1<<26) +#define FM_RDS_GROUP_TYPE_MASK_13B ((unsigned long)1<<27) +#define FM_RDS_GROUP_TYPE_MASK_14A ((unsigned long)1<<28) +#define FM_RDS_GROUP_TYPE_MASK_14B ((unsigned long)1<<29) +#define FM_RDS_GROUP_TYPE_MASK_15A ((unsigned long)1<<30) +#define FM_RDS_GROUP_TYPE_MASK_15B ((unsigned long)1<<31) + +/* RX Alternate Frequency info */ +#define FM_RDS_MIN_AF 1 +#define FM_RDS_MAX_AF 204 +#define FM_RDS_MAX_AF_JAPAN 140 +#define FM_RDS_1_AF_FOLLOWS 225 +#define FM_RDS_25_AF_FOLLOWS 249 + +/* RDS system type (RDS/RBDS) */ +#define FM_RDS_SYSTEM_RDS 0 +#define FM_RDS_SYSTEM_RBDS 1 + +/* AF on/off */ +#define FM_RX_RDS_AF_SWITCH_MODE_ON 1 +#define FM_RX_RDS_AF_SWITCH_MODE_OFF 0 + +/* Retry count when interrupt process goes wrong */ +#define FM_IRQ_TIMEOUT_RETRY_MAX 5 /* 5 times */ + +/* Audio IO set values */ +#define FM_RX_AUDIO_ENABLE_I2S 0x01 +#define FM_RX_AUDIO_ENABLE_ANALOG 0x02 +#define FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG 0x03 +#define FM_RX_AUDIO_ENABLE_DISABLE 0x00 + +/* HI/LO set values */ +#define FM_RX_IFFREQ_TO_HI_SIDE 0x0 +#define FM_RX_IFFREQ_TO_LO_SIDE 0x1 +#define FM_RX_IFFREQ_HILO_AUTOMATIC 0x2 + +/* + * Default RX mode configuration. Chip will be configured + * with this default values after loading RX firmware. + */ +#define FM_DEFAULT_RX_VOLUME 10 +#define FM_DEFAULT_RSSI_THRESHOLD 3 + +/* Range for TX power level in units for dB/uV */ +#define FM_PWR_LVL_LOW 91 +#define FM_PWR_LVL_HIGH 122 + +/* Chip specific default TX power level value */ +#define FM_PWR_LVL_DEF 4 + +/* FM TX Pre-emphasis filter values */ +#define FM_TX_PREEMPH_OFF 1 +#define FM_TX_PREEMPH_50US 0 +#define FM_TX_PREEMPH_75US 2 + +/* FM TX antenna impedence values */ +#define FM_TX_ANT_IMP_50 0 +#define FM_TX_ANT_IMP_200 1 +#define FM_TX_ANT_IMP_500 2 + +/* Functions exported by FM common sub-module */ +u32 fmc_prepare(struct fmdev *); +u32 fmc_release(struct fmdev *); + +void fmc_update_region_info(struct fmdev *, u8); +u32 fmc_send_cmd(struct fmdev *, u8, u16, + void *, unsigned int, void *, int *); +u32 fmc_is_rds_data_available(struct fmdev *, struct file *, + struct poll_table_struct *); +u32 fmc_transfer_rds_from_internal_buff(struct fmdev *, struct file *, + u8 __user *, size_t); + +u32 fmc_set_freq(struct fmdev *, u32); +u32 fmc_set_mode(struct fmdev *, u8); +u32 fmc_set_region(struct fmdev *, u8); +u32 fmc_set_mute_mode(struct fmdev *, u8); +u32 fmc_set_stereo_mono(struct fmdev *, u16); +u32 fmc_set_rds_mode(struct fmdev *, u8); + +u32 fmc_get_freq(struct fmdev *, u32 *); +u32 fmc_get_region(struct fmdev *, u8 *); +u32 fmc_get_mode(struct fmdev *, u8 *); + +/* + * channel spacing + */ +#define FM_CHANNEL_SPACING_50KHZ 1 +#define FM_CHANNEL_SPACING_100KHZ 2 +#define FM_CHANNEL_SPACING_200KHZ 4 +#define FM_FREQ_MUL 50 + +#endif + -- cgit v0.10.2 From 59da522a4e1eaa10e3cbb7b86ec94dc42eff0ede Mon Sep 17 00:00:00 2001 From: Manjunatha Halli Date: Tue, 11 Jan 2011 06:37:17 -0300 Subject: [media] drivers:media:radio: wl128x: FM driver RX sources This has implementation for FM RX functionality. It communicates with FM V4l2 module and FM common module Signed-off-by: Manjunatha Halli Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c new file mode 100644 index 0000000..ec529b5 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_rx.c @@ -0,0 +1,847 @@ +/* + * FM Driver for Connectivity chip of Texas Instruments. + * This sub-module of FM driver implements FM RX functionality. + * + * Copyright (C) 2011 Texas Instruments + * Author: Raja Mani + * Author: Manjunatha Halli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "fmdrv.h" +#include "fmdrv_common.h" +#include "fmdrv_rx.h" + +void fm_rx_reset_rds_cache(struct fmdev *fmdev) +{ + fmdev->rx.rds.flag = FM_RDS_DISABLE; + fmdev->rx.rds.last_blk_idx = 0; + fmdev->rx.rds.wr_idx = 0; + fmdev->rx.rds.rd_idx = 0; + + if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) + fmdev->irq_info.mask |= FM_LEV_EVENT; +} + +void fm_rx_reset_station_info(struct fmdev *fmdev) +{ + fmdev->rx.stat_info.picode = FM_NO_PI_CODE; + fmdev->rx.stat_info.afcache_size = 0; + fmdev->rx.stat_info.af_list_max = 0; +} + +u32 fm_rx_set_freq(struct fmdev *fmdev, u32 freq) +{ + unsigned long timeleft; + u16 payload, curr_frq, intr_flag; + u32 curr_frq_in_khz; + u32 ret, resp_len; + + if (freq < fmdev->rx.region.bot_freq || freq > fmdev->rx.region.top_freq) { + fmerr("Invalid frequency %d\n", freq); + return -EINVAL; + } + + /* Set audio enable */ + payload = FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG; + + ret = fmc_send_cmd(fmdev, AUDIO_ENABLE_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Set hilo to automatic selection */ + payload = FM_RX_IFFREQ_HILO_AUTOMATIC; + ret = fmc_send_cmd(fmdev, HILO_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Calculate frequency index and set*/ + payload = (freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; + + ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Read flags - just to clear any pending interrupts if we had */ + ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL); + if (ret < 0) + return ret; + + /* Enable FR, BL interrupts */ + intr_flag = fmdev->irq_info.mask; + fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT); + payload = fmdev->irq_info.mask; + ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Start tune */ + payload = FM_TUNER_PRESET_MODE; + ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + goto exit; + + /* Wait for tune ended interrupt */ + init_completion(&fmdev->maintask_comp); + timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, + FM_DRV_TX_TIMEOUT); + if (!timeleft) { + fmerr("Timeout(%d sec),didn't get tune ended int\n", + jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); + ret = -ETIMEDOUT; + goto exit; + } + + /* Read freq back to confirm */ + ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, &curr_frq, &resp_len); + if (ret < 0) + goto exit; + + curr_frq = be16_to_cpu(curr_frq); + curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL)); + + if (curr_frq_in_khz != freq) { + pr_info("Frequency is set to (%d) but " + "requested freq is (%d)\n", curr_frq_in_khz, freq); + } + + /* Update local cache */ + fmdev->rx.freq = curr_frq_in_khz; +exit: + /* Re-enable default FM interrupts */ + fmdev->irq_info.mask = intr_flag; + payload = fmdev->irq_info.mask; + ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Reset RDS cache and current station pointers */ + fm_rx_reset_rds_cache(fmdev); + fm_rx_reset_station_info(fmdev); + + return ret; +} + +static u32 fm_rx_set_channel_spacing(struct fmdev *fmdev, u32 spacing) +{ + u16 payload; + u32 ret; + + if (spacing > 0 && spacing <= 50000) + spacing = FM_CHANNEL_SPACING_50KHZ; + else if (spacing > 50000 && spacing <= 100000) + spacing = FM_CHANNEL_SPACING_100KHZ; + else + spacing = FM_CHANNEL_SPACING_200KHZ; + + /* set channel spacing */ + payload = spacing; + ret = fmc_send_cmd(fmdev, CHANL_BW_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + fmdev->rx.region.chanl_space = spacing * FM_FREQ_MUL; + + return ret; +} + +u32 fm_rx_seek(struct fmdev *fmdev, u32 seek_upward, + u32 wrap_around, u32 spacing) +{ + u32 resp_len; + u16 curr_frq, next_frq, last_frq; + u16 payload, int_reason, intr_flag; + u16 offset, space_idx; + unsigned long timeleft; + u32 ret; + + /* Set channel spacing */ + ret = fm_rx_set_channel_spacing(fmdev, spacing); + if (ret < 0) { + fmerr("Failed to set channel spacing\n"); + return ret; + } + + /* Read the current frequency from chip */ + ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, + sizeof(curr_frq), &curr_frq, &resp_len); + if (ret < 0) + return ret; + + curr_frq = be16_to_cpu(curr_frq); + last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; + + /* Check the offset in order to be aligned to the channel spacing*/ + space_idx = fmdev->rx.region.chanl_space / FM_FREQ_MUL; + offset = curr_frq % space_idx; + + next_frq = seek_upward ? curr_frq + space_idx /* Seek Up */ : + curr_frq - space_idx /* Seek Down */ ; + + /* + * Add or subtract offset in order to stay aligned to the channel + * spacing. + */ + if ((short)next_frq < 0) + next_frq = last_frq - offset; + else if (next_frq > last_frq) + next_frq = 0 + offset; + +again: + /* Set calculated next frequency to perform seek */ + payload = next_frq; + ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Set search direction (0:Seek Down, 1:Seek Up) */ + payload = (seek_upward ? FM_SEARCH_DIRECTION_UP : FM_SEARCH_DIRECTION_DOWN); + ret = fmc_send_cmd(fmdev, SEARCH_DIR_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Read flags - just to clear any pending interrupts if we had */ + ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL); + if (ret < 0) + return ret; + + /* Enable FR, BL interrupts */ + intr_flag = fmdev->irq_info.mask; + fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT); + payload = fmdev->irq_info.mask; + ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Start seek */ + payload = FM_TUNER_AUTONOMOUS_SEARCH_MODE; + ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Wait for tune ended/band limit reached interrupt */ + init_completion(&fmdev->maintask_comp); + timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, + FM_DRV_RX_SEEK_TIMEOUT); + if (!timeleft) { + fmerr("Timeout(%d sec),didn't get tune ended int\n", + jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000); + return -ETIMEDOUT; + } + + int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT); + + /* Re-enable default FM interrupts */ + fmdev->irq_info.mask = intr_flag; + payload = fmdev->irq_info.mask; + ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + if (int_reason & FM_BL_EVENT) { + if (wrap_around == 0) { + fmdev->rx.freq = seek_upward ? + fmdev->rx.region.top_freq : + fmdev->rx.region.bot_freq; + } else { + fmdev->rx.freq = seek_upward ? + fmdev->rx.region.bot_freq : + fmdev->rx.region.top_freq; + /* Calculate frequency index to write */ + next_frq = (fmdev->rx.freq - + fmdev->rx.region.bot_freq) / FM_FREQ_MUL; + goto again; + } + } else { + /* Read freq to know where operation tune operation stopped */ + ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, + &curr_frq, &resp_len); + if (ret < 0) + return ret; + + curr_frq = be16_to_cpu(curr_frq); + fmdev->rx.freq = (fmdev->rx.region.bot_freq + + ((u32)curr_frq * FM_FREQ_MUL)); + + } + /* Reset RDS cache and current station pointers */ + fm_rx_reset_rds_cache(fmdev); + fm_rx_reset_station_info(fmdev); + + return ret; +} + +u32 fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set) +{ + u16 payload; + u32 ret; + + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (vol_to_set < FM_RX_VOLUME_MIN || vol_to_set > FM_RX_VOLUME_MAX) { + fmerr("Volume is not within(%d-%d) range\n", + FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX); + return -EINVAL; + } + vol_to_set *= FM_RX_VOLUME_GAIN_STEP; + + payload = vol_to_set; + ret = fmc_send_cmd(fmdev, VOLUME_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + fmdev->rx.volume = vol_to_set; + return ret; +} + +/* Get volume */ +u32 fm_rx_get_volume(struct fmdev *fmdev, u16 *curr_vol) +{ + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (curr_vol == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + *curr_vol = fmdev->rx.volume / FM_RX_VOLUME_GAIN_STEP; + + return 0; +} + +/* To get current band's bottom and top frequency */ +u32 fm_rx_get_band_freq_range(struct fmdev *fmdev, u32 *bot_freq, u32 *top_freq) +{ + if (bot_freq != NULL) + *bot_freq = fmdev->rx.region.bot_freq; + + if (top_freq != NULL) + *top_freq = fmdev->rx.region.top_freq; + + return 0; +} + +/* Returns current band index (0-Europe/US; 1-Japan) */ +void fm_rx_get_region(struct fmdev *fmdev, u8 *region) +{ + *region = fmdev->rx.region.fm_band; +} + +/* Sets band (0-Europe/US; 1-Japan) */ +u32 fm_rx_set_region(struct fmdev *fmdev, u8 region_to_set) +{ + u16 payload; + u32 new_frq = 0; + u32 ret; + + if (region_to_set != FM_BAND_EUROPE_US && + region_to_set != FM_BAND_JAPAN) { + fmerr("Invalid band\n"); + return -EINVAL; + } + + if (fmdev->rx.region.fm_band == region_to_set) { + fmerr("Requested band is already configured\n"); + return 0; + } + + /* Send cmd to set the band */ + payload = (u16)region_to_set; + ret = fmc_send_cmd(fmdev, BAND_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + fmc_update_region_info(fmdev, region_to_set); + + /* Check whether current RX frequency is within band boundary */ + if (fmdev->rx.freq < fmdev->rx.region.bot_freq) + new_frq = fmdev->rx.region.bot_freq; + else if (fmdev->rx.freq > fmdev->rx.region.top_freq) + new_frq = fmdev->rx.region.top_freq; + + if (new_frq) { + fmdbg("Current freq is not within band limit boundary," + "switching to %d KHz\n", new_frq); + /* Current RX frequency is not in range. So, update it */ + ret = fm_rx_set_freq(fmdev, new_frq); + } + + return ret; +} + +/* Reads current mute mode (Mute Off/On/Attenuate)*/ +u32 fm_rx_get_mute_mode(struct fmdev *fmdev, u8 *curr_mute_mode) +{ + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (curr_mute_mode == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + *curr_mute_mode = fmdev->rx.mute_mode; + + return 0; +} + +static u32 fm_config_rx_mute_reg(struct fmdev *fmdev) +{ + u16 payload, muteval; + u32 ret; + + muteval = 0; + switch (fmdev->rx.mute_mode) { + case FM_MUTE_ON: + muteval = FM_RX_AC_MUTE_MODE; + break; + + case FM_MUTE_OFF: + muteval = FM_RX_UNMUTE_MODE; + break; + + case FM_MUTE_ATTENUATE: + muteval = FM_RX_SOFT_MUTE_FORCE_MODE; + break; + } + if (fmdev->rx.rf_depend_mute == FM_RX_RF_DEPENDENT_MUTE_ON) + muteval |= FM_RX_RF_DEP_MODE; + else + muteval &= ~FM_RX_RF_DEP_MODE; + + payload = muteval; + ret = fmc_send_cmd(fmdev, MUTE_STATUS_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + return 0; +} + +/* Configures mute mode (Mute Off/On/Attenuate) */ +u32 fm_rx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) +{ + u8 org_state; + u32 ret; + + if (fmdev->rx.mute_mode == mute_mode_toset) + return 0; + + org_state = fmdev->rx.mute_mode; + fmdev->rx.mute_mode = mute_mode_toset; + + ret = fm_config_rx_mute_reg(fmdev); + if (ret < 0) { + fmdev->rx.mute_mode = org_state; + return ret; + } + + return 0; +} + +/* Gets RF dependent soft mute mode enable/disable status */ +u32 fm_rx_get_rfdepend_softmute(struct fmdev *fmdev, u8 *curr_mute_mode) +{ + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (curr_mute_mode == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + *curr_mute_mode = fmdev->rx.rf_depend_mute; + + return 0; +} + +/* Sets RF dependent soft mute mode */ +u32 fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute) +{ + u8 org_state; + u32 ret; + + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_ON && + rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_OFF) { + fmerr("Invalid RF dependent soft mute\n"); + return -EINVAL; + } + if (fmdev->rx.rf_depend_mute == rfdepend_mute) + return 0; + + org_state = fmdev->rx.rf_depend_mute; + fmdev->rx.rf_depend_mute = rfdepend_mute; + + ret = fm_config_rx_mute_reg(fmdev); + if (ret < 0) { + fmdev->rx.rf_depend_mute = org_state; + return ret; + } + + return 0; +} + +/* Returns the signal strength level of current channel */ +u32 fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl) +{ + u16 curr_rssi_lel; + u32 resp_len; + u32 ret; + + if (rssilvl == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + /* Read current RSSI level */ + ret = fmc_send_cmd(fmdev, RSSI_LVL_GET, REG_RD, NULL, 2, + &curr_rssi_lel, &resp_len); + if (ret < 0) + return ret; + + *rssilvl = be16_to_cpu(curr_rssi_lel); + + return 0; +} + +/* + * Sets the signal strength level that once reached + * will stop the auto search process + */ +u32 fm_rx_set_rssi_threshold(struct fmdev *fmdev, short rssi_lvl_toset) +{ + u16 payload; + u32 ret; + + if (rssi_lvl_toset < FM_RX_RSSI_THRESHOLD_MIN || + rssi_lvl_toset > FM_RX_RSSI_THRESHOLD_MAX) { + fmerr("Invalid RSSI threshold level\n"); + return -EINVAL; + } + payload = (u16)rssi_lvl_toset; + ret = fmc_send_cmd(fmdev, SEARCH_LVL_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + fmdev->rx.rssi_threshold = rssi_lvl_toset; + + return 0; +} + +/* Returns current RX RSSI threshold value */ +u32 fm_rx_get_rssi_threshold(struct fmdev *fmdev, short *curr_rssi_lvl) +{ + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (curr_rssi_lvl == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + *curr_rssi_lvl = fmdev->rx.rssi_threshold; + + return 0; +} + +/* Sets RX stereo/mono modes */ +u32 fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode) +{ + u16 payload; + u32 ret; + + if (mode != FM_STEREO_MODE && mode != FM_MONO_MODE) { + fmerr("Invalid mode\n"); + return -EINVAL; + } + + /* Set stereo/mono mode */ + payload = (u16)mode; + ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Set stereo blending mode */ + payload = FM_STEREO_SOFT_BLEND; + ret = fmc_send_cmd(fmdev, MOST_BLEND_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + return 0; +} + +/* Gets current RX stereo/mono mode */ +u32 fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode) +{ + u16 curr_mode; + u32 ret, resp_len; + + if (mode == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_RD, NULL, 2, + &curr_mode, &resp_len); + if (ret < 0) + return ret; + + *mode = be16_to_cpu(curr_mode); + + return 0; +} + +/* Choose RX de-emphasis filter mode (50us/75us) */ +u32 fm_rx_set_deemphasis_mode(struct fmdev *fmdev, u16 mode) +{ + u16 payload; + u32 ret; + + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (mode != FM_RX_EMPHASIS_FILTER_50_USEC && + mode != FM_RX_EMPHASIS_FILTER_75_USEC) { + fmerr("Invalid rx de-emphasis mode (%d)\n", mode); + return -EINVAL; + } + + payload = mode; + ret = fmc_send_cmd(fmdev, DEMPH_MODE_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + fmdev->rx.deemphasis_mode = mode; + + return 0; +} + +/* Gets current RX de-emphasis filter mode */ +u32 fm_rx_get_deemph_mode(struct fmdev *fmdev, u16 *curr_deemphasis_mode) +{ + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (curr_deemphasis_mode == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + *curr_deemphasis_mode = fmdev->rx.deemphasis_mode; + + return 0; +} + +/* Enable/Disable RX RDS */ +u32 fm_rx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) +{ + u16 payload; + u32 ret; + + if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) { + fmerr("Invalid rds option\n"); + return -EINVAL; + } + + if (rds_en_dis == FM_RDS_ENABLE + && fmdev->rx.rds.flag == FM_RDS_DISABLE) { + /* Turn on RX RDS and RDS circuit */ + payload = FM_RX_PWR_SET_FM_AND_RDS_BLK_ON; + ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Clear and reset RDS FIFO */ + payload = FM_RX_RDS_FLUSH_FIFO; + ret = fmc_send_cmd(fmdev, RDS_CNTRL_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Read flags - just to clear any pending interrupts. */ + ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, + NULL, NULL); + if (ret < 0) + return ret; + + /* Set RDS FIFO threshold value */ + payload = FM_RX_RDS_FIFO_THRESHOLD; + ret = fmc_send_cmd(fmdev, RDS_MEM_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Enable RDS interrupt */ + fmdev->irq_info.mask |= FM_RDS_EVENT; + payload = fmdev->irq_info.mask; + ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) { + fmdev->irq_info.mask &= ~FM_RDS_EVENT; + return ret; + } + + /* Update our local flag */ + fmdev->rx.rds.flag = FM_RDS_ENABLE; + } else if (rds_en_dis == FM_RDS_DISABLE + && fmdev->rx.rds.flag == FM_RDS_ENABLE) { + /* Turn off RX RDS */ + payload = FM_RX_PWR_SET_FM_ON_RDS_OFF; + ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Reset RDS pointers */ + fmdev->rx.rds.last_blk_idx = 0; + fmdev->rx.rds.wr_idx = 0; + fmdev->rx.rds.rd_idx = 0; + fm_rx_reset_station_info(fmdev); + + /* Update RDS local cache */ + fmdev->irq_info.mask &= ~(FM_RDS_EVENT); + fmdev->rx.rds.flag = FM_RDS_DISABLE; + } + + return 0; +} + +/* Returns current RX RDS enable/disable status */ +u32 fm_rx_get_rds_mode(struct fmdev *fmdev, u8 *curr_rds_en_dis) +{ + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (curr_rds_en_dis == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + *curr_rds_en_dis = fmdev->rx.rds.flag; + + return 0; +} + +/* Sets RDS operation mode (RDS/RDBS) */ +u32 fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode) +{ + u16 payload; + u32 ret; + + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (rds_mode != FM_RDS_SYSTEM_RDS && rds_mode != FM_RDS_SYSTEM_RBDS) { + fmerr("Invalid rds mode\n"); + return -EINVAL; + } + /* Set RDS operation mode */ + payload = (u16)rds_mode; + ret = fmc_send_cmd(fmdev, RDS_SYSTEM_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + fmdev->rx.rds_mode = rds_mode; + + return 0; +} + +/* Returns current RDS operation mode */ +u32 fm_rx_get_rds_system(struct fmdev *fmdev, u8 *rds_mode) +{ + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (rds_mode == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + *rds_mode = fmdev->rx.rds_mode; + + return 0; +} + +/* Configures Alternate Frequency switch mode */ +u32 fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode) +{ + u16 payload; + u32 ret; + + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (af_mode != FM_RX_RDS_AF_SWITCH_MODE_ON && + af_mode != FM_RX_RDS_AF_SWITCH_MODE_OFF) { + fmerr("Invalid af mode\n"); + return -EINVAL; + } + /* Enable/disable low RSSI interrupt based on af_mode */ + if (af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) + fmdev->irq_info.mask |= FM_LEV_EVENT; + else + fmdev->irq_info.mask &= ~FM_LEV_EVENT; + + payload = fmdev->irq_info.mask; + ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + fmdev->rx.af_mode = af_mode; + + return 0; +} + +/* Returns Alternate Frequency switch status */ +u32 fm_rx_get_af_switch(struct fmdev *fmdev, u8 *af_mode) +{ + if (fmdev->curr_fmmode != FM_MODE_RX) + return -EPERM; + + if (af_mode == NULL) { + fmerr("Invalid memory\n"); + return -ENOMEM; + } + + *af_mode = fmdev->rx.af_mode; + + return 0; +} diff --git a/drivers/media/radio/wl128x/fmdrv_rx.h b/drivers/media/radio/wl128x/fmdrv_rx.h new file mode 100644 index 0000000..329e62f --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_rx.h @@ -0,0 +1,59 @@ +/* + * FM Driver for Connectivity chip of Texas Instruments. + * FM RX module header. + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _FMDRV_RX_H +#define _FMDRV_RX_H + +u32 fm_rx_set_freq(struct fmdev *, u32); +u32 fm_rx_set_mute_mode(struct fmdev *, u8); +u32 fm_rx_set_stereo_mono(struct fmdev *, u16); +u32 fm_rx_set_rds_mode(struct fmdev *, u8); +u32 fm_rx_set_rds_system(struct fmdev *, u8); +u32 fm_rx_set_volume(struct fmdev *, u16); +u32 fm_rx_set_rssi_threshold(struct fmdev *, short); +u32 fm_rx_set_region(struct fmdev *, u8); +u32 fm_rx_set_rfdepend_softmute(struct fmdev *, u8); +u32 fm_rx_set_deemphasis_mode(struct fmdev *, u16); +u32 fm_rx_set_af_switch(struct fmdev *, u8); + +void fm_rx_reset_rds_cache(struct fmdev *); +void fm_rx_reset_station_info(struct fmdev *); + +u32 fm_rx_seek(struct fmdev *, u32, u32, u32); + +u32 fm_rx_get_rds_mode(struct fmdev *, u8 *); +u32 fm_rx_get_rds_system(struct fmdev *, u8 *); +u32 fm_rx_get_mute_mode(struct fmdev *, u8 *); +u32 fm_rx_get_volume(struct fmdev *, u16 *); +u32 fm_rx_get_band_freq_range(struct fmdev *, + u32 *, u32 *); +u32 fm_rx_get_stereo_mono(struct fmdev *, u16 *); +u32 fm_rx_get_rssi_level(struct fmdev *, u16 *); +u32 fm_rx_get_rssi_threshold(struct fmdev *, short *); +u32 fm_rx_get_rfdepend_softmute(struct fmdev *, u8 *); +u32 fm_rx_get_deemph_mode(struct fmdev *, u16 *); +u32 fm_rx_get_af_switch(struct fmdev *, u8 *); +void fm_rx_get_region(struct fmdev *, u8 *); + +u32 fm_rx_set_chanl_spacing(struct fmdev *, u8); +u32 fm_rx_get_chanl_spacing(struct fmdev *, u8 *); +#endif + -- cgit v0.10.2 From 8b7a186c9b9322a4519af710736210bf8d5aa7a7 Mon Sep 17 00:00:00 2001 From: Manjunatha Halli Date: Tue, 11 Jan 2011 06:37:53 -0300 Subject: [media] drivers:media:radio: wl128x: FM driver TX sources This has implementation for FM TX functionality. It communicates with FM V4l2 module and FM common module. Signed-off-by: Manjunatha Halli Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/wl128x/fmdrv_tx.c b/drivers/media/radio/wl128x/fmdrv_tx.c new file mode 100644 index 0000000..be54068 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_tx.c @@ -0,0 +1,425 @@ +/* + * FM Driver for Connectivity chip of Texas Instruments. + * This sub-module of FM driver implements FM TX functionality. + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "fmdrv.h" +#include "fmdrv_common.h" +#include "fmdrv_tx.h" + +u32 fm_tx_set_stereo_mono(struct fmdev *fmdev, u16 mode) +{ + u16 payload; + u32 ret; + + if (fmdev->tx_data.aud_mode == mode) + return 0; + + fmdbg("stereo mode: %d\n", mode); + + /* Set Stereo/Mono mode */ + payload = (1 - mode); + ret = fmc_send_cmd(fmdev, MONO_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + fmdev->tx_data.aud_mode = mode; + + return ret; +} + +static u32 set_rds_text(struct fmdev *fmdev, u8 *rds_text) +{ + u16 payload; + u32 ret; + + ret = fmc_send_cmd(fmdev, RDS_DATA_SET, REG_WR, rds_text, + strlen(rds_text), NULL, NULL); + if (ret < 0) + return ret; + + /* Scroll mode */ + payload = (u16)0x1; + ret = fmc_send_cmd(fmdev, DISPLAY_MODE, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + return 0; +} + +static u32 set_rds_data_mode(struct fmdev *fmdev, u8 mode) +{ + u16 payload; + u32 ret; + + /* Setting unique PI TODO: how unique? */ + payload = (u16)0xcafe; + ret = fmc_send_cmd(fmdev, PI_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Set decoder id */ + payload = (u16)0xa; + ret = fmc_send_cmd(fmdev, DI_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* TODO: RDS_MODE_GET? */ + return 0; +} + +static u32 set_rds_len(struct fmdev *fmdev, u8 type, u16 len) +{ + u16 payload; + u32 ret; + + len |= type << 8; + payload = len; + ret = fmc_send_cmd(fmdev, RDS_CONFIG_DATA_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* TODO: LENGTH_GET? */ + return 0; +} + +u32 fm_tx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) +{ + u16 payload; + u32 ret; + u8 rds_text[] = "Zoom2\n"; + + fmdbg("rds_en_dis:%d(E:%d, D:%d)\n", rds_en_dis, + FM_RDS_ENABLE, FM_RDS_DISABLE); + + if (rds_en_dis == FM_RDS_ENABLE) { + /* Set RDS length */ + set_rds_len(fmdev, 0, strlen(rds_text)); + + /* Set RDS text */ + set_rds_text(fmdev, rds_text); + + /* Set RDS mode */ + set_rds_data_mode(fmdev, 0x0); + } + + /* Send command to enable RDS */ + if (rds_en_dis == FM_RDS_ENABLE) + payload = 0x01; + else + payload = 0x00; + + ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + if (rds_en_dis == FM_RDS_ENABLE) { + /* Set RDS length */ + set_rds_len(fmdev, 0, strlen(rds_text)); + + /* Set RDS text */ + set_rds_text(fmdev, rds_text); + } + fmdev->tx_data.rds.flag = rds_en_dis; + + return 0; +} + +u32 fm_tx_set_radio_text(struct fmdev *fmdev, u8 *rds_text, u8 rds_type) +{ + u16 payload; + u32 ret; + + if (fmdev->curr_fmmode != FM_MODE_TX) + return -EPERM; + + fm_tx_set_rds_mode(fmdev, 0); + + /* Set RDS length */ + set_rds_len(fmdev, rds_type, strlen(rds_text)); + + /* Set RDS text */ + set_rds_text(fmdev, rds_text); + + /* Set RDS mode */ + set_rds_data_mode(fmdev, 0x0); + + payload = 1; + ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + return 0; +} + +u32 fm_tx_set_af(struct fmdev *fmdev, u32 af) +{ + u16 payload; + u32 ret; + + if (fmdev->curr_fmmode != FM_MODE_TX) + return -EPERM; + + fmdbg("AF: %d\n", af); + + af = (af - 87500) / 100; + payload = (u16)af; + ret = fmc_send_cmd(fmdev, TA_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + return 0; +} + +u32 fm_tx_set_region(struct fmdev *fmdev, u8 region) +{ + u16 payload; + u32 ret; + + if (region != FM_BAND_EUROPE_US && region != FM_BAND_JAPAN) { + fmerr("Invalid band\n"); + return -EINVAL; + } + + /* Send command to set the band */ + payload = (u16)region; + ret = fmc_send_cmd(fmdev, TX_BAND_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + return 0; +} + +u32 fm_tx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) +{ + u16 payload; + u32 ret; + + fmdbg("tx: mute mode %d\n", mute_mode_toset); + + payload = mute_mode_toset; + ret = fmc_send_cmd(fmdev, MUTE, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + return 0; +} + +/* Set TX Audio I/O */ +static u32 set_audio_io(struct fmdev *fmdev) +{ + struct fmtx_data *tx = &fmdev->tx_data; + u16 payload; + u32 ret; + + /* Set Audio I/O Enable */ + payload = tx->audio_io; + ret = fmc_send_cmd(fmdev, AUDIO_IO_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* TODO: is audio set? */ + return 0; +} + +/* Start TX Transmission */ +static u32 enable_xmit(struct fmdev *fmdev, u8 new_xmit_state) +{ + struct fmtx_data *tx = &fmdev->tx_data; + unsigned long timeleft; + u16 payload; + u32 ret; + + /* Enable POWER_ENB interrupts */ + payload = FM_POW_ENB_EVENT; + ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Set Power Enable */ + payload = new_xmit_state; + ret = fmc_send_cmd(fmdev, POWER_ENB_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* Wait for Power Enabled */ + init_completion(&fmdev->maintask_comp); + timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, + FM_DRV_TX_TIMEOUT); + if (!timeleft) { + fmerr("Timeout(%d sec),didn't get tune ended interrupt\n", + jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); + return -ETIMEDOUT; + } + + set_bit(FM_CORE_TX_XMITING, &fmdev->flag); + tx->xmit_state = new_xmit_state; + + return 0; +} + +/* Set TX power level */ +u32 fm_tx_set_pwr_lvl(struct fmdev *fmdev, u8 new_pwr_lvl) +{ + u16 payload; + struct fmtx_data *tx = &fmdev->tx_data; + u32 ret; + + if (fmdev->curr_fmmode != FM_MODE_TX) + return -EPERM; + fmdbg("tx: pwr_level_to_set %ld\n", (long int)new_pwr_lvl); + + /* If the core isn't ready update global variable */ + if (!test_bit(FM_CORE_READY, &fmdev->flag)) { + tx->pwr_lvl = new_pwr_lvl; + return 0; + } + + /* Set power level: Application will specify power level value in + * units of dB/uV, whereas range and step are specific to FM chip. + * For TI's WL chips, convert application specified power level value + * to chip specific value by subtracting 122 from it. Refer to TI FM + * data sheet for details. + * */ + + payload = (FM_PWR_LVL_HIGH - new_pwr_lvl); + ret = fmc_send_cmd(fmdev, POWER_LEV_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + /* TODO: is the power level set? */ + tx->pwr_lvl = new_pwr_lvl; + + return 0; +} + +/* + * Sets FM TX pre-emphasis filter value (OFF, 50us, or 75us) + * Convert V4L2 specified filter values to chip specific filter values. + */ +u32 fm_tx_set_preemph_filter(struct fmdev *fmdev, u32 preemphasis) +{ + struct fmtx_data *tx = &fmdev->tx_data; + u16 payload; + u32 ret; + + if (fmdev->curr_fmmode != FM_MODE_TX) + return -EPERM; + + switch (preemphasis) { + case V4L2_PREEMPHASIS_DISABLED: + payload = FM_TX_PREEMPH_OFF; + break; + case V4L2_PREEMPHASIS_50_uS: + payload = FM_TX_PREEMPH_50US; + break; + case V4L2_PREEMPHASIS_75_uS: + payload = FM_TX_PREEMPH_75US; + break; + } + + ret = fmc_send_cmd(fmdev, PREMPH_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + tx->preemph = payload; + + return ret; +} + +/* Get the TX tuning capacitor value.*/ +u32 fm_tx_get_tune_cap_val(struct fmdev *fmdev) +{ + u16 curr_val; + u32 ret, resp_len; + + if (fmdev->curr_fmmode != FM_MODE_TX) + return -EPERM; + + ret = fmc_send_cmd(fmdev, READ_FMANT_TUNE_VALUE, REG_RD, + NULL, sizeof(curr_val), &curr_val, &resp_len); + if (ret < 0) + return ret; + + curr_val = be16_to_cpu(curr_val); + + return curr_val; +} + +/* Set TX Frequency */ +u32 fm_tx_set_freq(struct fmdev *fmdev, u32 freq_to_set) +{ + struct fmtx_data *tx = &fmdev->tx_data; + u16 payload, chanl_index; + u32 ret; + + if (test_bit(FM_CORE_TX_XMITING, &fmdev->flag)) { + enable_xmit(fmdev, 0); + clear_bit(FM_CORE_TX_XMITING, &fmdev->flag); + } + + /* Enable FR, BL interrupts */ + payload = (FM_FR_EVENT | FM_BL_EVENT); + ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + tx->tx_frq = (unsigned long)freq_to_set; + fmdbg("tx: freq_to_set %ld\n", (long int)tx->tx_frq); + + chanl_index = freq_to_set / 10; + + /* Set current tuner channel */ + payload = chanl_index; + ret = fmc_send_cmd(fmdev, CHANL_SET, REG_WR, &payload, + sizeof(payload), NULL, NULL); + if (ret < 0) + return ret; + + fm_tx_set_pwr_lvl(fmdev, tx->pwr_lvl); + fm_tx_set_preemph_filter(fmdev, tx->preemph); + + tx->audio_io = 0x01; /* I2S */ + set_audio_io(fmdev); + + enable_xmit(fmdev, 0x01); /* Enable transmission */ + + tx->aud_mode = FM_STEREO_MODE; + tx->rds.flag = FM_RDS_DISABLE; + + return 0; +} + diff --git a/drivers/media/radio/wl128x/fmdrv_tx.h b/drivers/media/radio/wl128x/fmdrv_tx.h new file mode 100644 index 0000000..e393a2b --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_tx.h @@ -0,0 +1,37 @@ +/* + * FM Driver for Connectivity chip of Texas Instruments. + * FM TX module header. + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _FMDRV_TX_H +#define _FMDRV_TX_H + +u32 fm_tx_set_freq(struct fmdev *, u32); +u32 fm_tx_set_pwr_lvl(struct fmdev *, u8); +u32 fm_tx_set_region(struct fmdev *, u8); +u32 fm_tx_set_mute_mode(struct fmdev *, u8); +u32 fm_tx_set_stereo_mono(struct fmdev *, u16); +u32 fm_tx_set_rds_mode(struct fmdev *, u8); +u32 fm_tx_set_radio_text(struct fmdev *, u8 *, u8); +u32 fm_tx_set_af(struct fmdev *, u32); +u32 fm_tx_set_preemph_filter(struct fmdev *, u32); +u32 fm_tx_get_tune_cap_val(struct fmdev *); + +#endif + -- cgit v0.10.2 From a68793d2dac1f753b82e55fb5c6b7dccff1ca4e4 Mon Sep 17 00:00:00 2001 From: Manjunatha Halli Date: Tue, 11 Jan 2011 06:39:22 -0300 Subject: [media] drivers:media:radio: wl128x: Kconfig & Makefile for wl128x driver Signed-off-by: Manjunatha Halli Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig new file mode 100644 index 0000000..749f67b --- /dev/null +++ b/drivers/media/radio/wl128x/Kconfig @@ -0,0 +1,17 @@ +# +# TI's wl128x FM driver based on TI's ST driver. +# +menu "Texas Instruments WL128x FM driver (ST based)" +config RADIO_WL128X + tristate "Texas Instruments WL128x FM Radio" + depends on VIDEO_V4L2 && RFKILL + select TI_ST + help + Choose Y here if you have this FM radio chip. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux 2 API. Information on + this API and pointers to "v4l2" programs may be found at + . + +endmenu diff --git a/drivers/media/radio/wl128x/Makefile b/drivers/media/radio/wl128x/Makefile new file mode 100644 index 0000000..32a0ead --- /dev/null +++ b/drivers/media/radio/wl128x/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for TI's shared transport driver based wl128x +# FM radio. +# +obj-$(CONFIG_RADIO_WL128X) += fm_drv.o +fm_drv-objs := fmdrv_common.o fmdrv_rx.o fmdrv_tx.o fmdrv_v4l2.o -- cgit v0.10.2 From 57f05bfa2e90fe52fa36755f4cb91e6f495f8353 Mon Sep 17 00:00:00 2001 From: Manjunatha Halli Date: Mon, 24 Jan 2011 02:26:59 -0300 Subject: [media] drivers:media:radio: Update Kconfig and Makefile for wl128x FM driver Signed-off-by: Manjunatha Halli Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index ecdffa6..f75cdad 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -454,4 +454,7 @@ config RADIO_WL1273 To compile this driver as a module, choose M here: the module will be called radio-wl1273. +# TI's ST based wl128x FM radio +source "drivers/media/radio/wl128x/Kconfig" + endif # RADIO_ADAPTERS diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 717656d..2faa333 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -26,5 +26,6 @@ obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o obj-$(CONFIG_RADIO_TEF6862) += tef6862.o obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o +obj-$(CONFIG_RADIO_WL128X) += wl128x/ EXTRA_CFLAGS += -Isound -- cgit v0.10.2 From 07988007f8dd2827ba1eb39e6e3c5bbb29cfc30b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Feb 2011 07:10:08 -0300 Subject: [media] stv090x: handle allocation failures kmalloc() can fail so check whether state->internal is NULL. append_internal() can return NULL on allocation failures so check that. Also if we hit the error condition later in the function then there is a memory leak and we need to call remove_dev() to fix it. Also Oliver Endriss pointed out an additional leak that I missed in the first version of this patch. Signed-off-by: Dan Carpenter Acked-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index d3362d0..41d0f0a 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -4783,7 +4783,13 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, } else { state->internal = kmalloc(sizeof(struct stv090x_internal), GFP_KERNEL); + if (!state->internal) + goto error; temp_int = append_internal(state->internal); + if (!temp_int) { + kfree(state->internal); + goto error; + } state->internal->num_used = 1; state->internal->mclk = 0; state->internal->dev_ver = 0; @@ -4796,7 +4802,7 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, if (stv090x_setup(&state->frontend) < 0) { dprintk(FE_ERROR, 1, "Error setting up device"); - goto error; + goto err_remove; } } @@ -4811,6 +4817,9 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, return &state->frontend; +err_remove: + remove_dev(state->internal); + kfree(state->internal); error: kfree(state); return NULL; -- cgit v0.10.2 From 498e677cfd78b098342640f382293782bfa0bd63 Mon Sep 17 00:00:00 2001 From: Lukas Max Fisch Date: Tue, 8 Feb 2011 16:51:45 -0300 Subject: [media] Technisat AirStar TeleStick 2 Based on staging 2.6.39. This patch is based on Veit Berwig's work. Signed-off-by: Lukas Fisch Thanks-to: Veit Berwig Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index c25b428..97af266 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -2801,6 +2801,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM9090MD) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM7090) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090PVR) }, + { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -3410,7 +3411,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 2, + .num_device_descs = 3, .devices = { { "DiBcom STK7770P reference design", { &dib0700_usb_id_table[59], NULL }, @@ -3422,6 +3423,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { &dib0700_usb_id_table[60], NULL}, { NULL }, }, + { "TechniSat AirStar TeleStick 2", + { &dib0700_usb_id_table[74], NULL }, + { NULL }, + }, }, .rc.core = { diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index b71540d..3a8b744 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -317,5 +317,6 @@ #define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac #define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001 #define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002 +#define USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2 0x0004 #define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500 #endif -- cgit v0.10.2 From 0522921c0f89f274a99d5084ca0a6fd370749cc8 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Fri, 11 Feb 2011 18:15:02 -0300 Subject: [media] via-camera: Add suspend/resume support Add suspend/resume support to the via-camera driver, so that the video continues streaming over a suspend-resume cycle. Originally implemented by Jon Corbet. [mchehab@redhat.com: fix a small CodingStyle issue] Signed-off-by: Daniel Drake Acked-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c index 2f973cd..3f0146f 100644 --- a/drivers/media/video/via-camera.c +++ b/drivers/media/video/via-camera.c @@ -1246,6 +1246,62 @@ static const struct v4l2_ioctl_ops viacam_ioctl_ops = { /* * Power management. */ +#ifdef CONFIG_PM + +static int viacam_suspend(void *priv) +{ + struct via_camera *cam = priv; + enum viacam_opstate state = cam->opstate; + + if (cam->opstate != S_IDLE) { + viacam_stop_engine(cam); + cam->opstate = state; /* So resume restarts */ + } + + return 0; +} + +static int viacam_resume(void *priv) +{ + struct via_camera *cam = priv; + int ret = 0; + + /* + * Get back to a reasonable operating state. + */ + via_write_reg_mask(VIASR, 0x78, 0, 0x80); + via_write_reg_mask(VIASR, 0x1e, 0xc0, 0xc0); + viacam_int_disable(cam); + set_bit(CF_CONFIG_NEEDED, &cam->flags); + /* + * Make sure the sensor's power state is correct + */ + if (cam->users > 0) + via_sensor_power_up(cam); + else + via_sensor_power_down(cam); + /* + * If it was operating, try to restart it. + */ + if (cam->opstate != S_IDLE) { + mutex_lock(&cam->lock); + ret = viacam_configure_sensor(cam); + if (!ret) + ret = viacam_config_controller(cam); + mutex_unlock(&cam->lock); + if (!ret) + viacam_start_engine(cam); + } + + return ret; +} + +static struct viafb_pm_hooks viacam_pm_hooks = { + .suspend = viacam_suspend, + .resume = viacam_resume +}; + +#endif /* CONFIG_PM */ /* * Setup stuff. @@ -1369,6 +1425,14 @@ static __devinit int viacam_probe(struct platform_device *pdev) goto out_irq; video_set_drvdata(&cam->vdev, cam); +#ifdef CONFIG_PM + /* + * Hook into PM events + */ + viacam_pm_hooks.private = cam; + viafb_pm_register(&viacam_pm_hooks); +#endif + /* Power the sensor down until somebody opens the device */ via_sensor_power_down(cam); return 0; -- cgit v0.10.2 From 1b149bbe9156d2eb2afd5a072bd61ad0d4bfaca7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 27 Feb 2011 14:51:54 -0300 Subject: [media] media/radio/wl1273: fix build errors RADIO_WL1273 needs to make sure that the mfd core is built to avoid build errors: ERROR: "mfd_add_devices" [drivers/mfd/wl1273-core.ko] undefined! ERROR: "mfd_remove_devices" [drivers/mfd/wl1273-core.ko] undefined! Signed-off-by: Randy Dunlap Cc: Matti Aaltonen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index f75cdad..299994c 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -441,6 +441,7 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" depends on I2C && VIDEO_V4L2 + select MFD_CORE select MFD_WL1273_CORE select FW_LOADER ---help--- -- cgit v0.10.2 From d4c9adb742abd1e8025b6f2e3bb24e3efab86da9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 1 Mar 2011 08:17:47 -0300 Subject: matrox: Remove legacy VIDIOC_*_OLD ioctls Those ioctls were produced by the wrong arguments for _IO macros, and were replaced by fixed versions on an early 2.6 kernel. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c index a082deb..8c9dbac 100644 --- a/drivers/video/matrox/matroxfb_base.c +++ b/drivers/video/matrox/matroxfb_base.c @@ -101,8 +101,6 @@ #include -#define __OLD_VIDIOC_ - #include "matroxfb_base.h" #include "matroxfb_misc.h" #include "matroxfb_accel.h" @@ -1152,7 +1150,6 @@ static int matroxfb_ioctl(struct fb_info *info, return -EFAULT; return err; } - case VIDIOC_S_CTRL_OLD: case VIDIOC_S_CTRL: { struct v4l2_control ctrl; -- cgit v0.10.2 From f6a373fb0569c98d6a44108f5e879f06abd46df3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 1 Mar 2011 09:48:27 -0300 Subject: [media] videodev2.h.xml: Update to reflect videodev2.h changes A few changes happened at videodev2.h: - Addition of multiplane API; - removal of VIDIOC_*_OLD ioctls; - a few more video standards. Update the file to reflect the latest changes. Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/videodev2.h.xml b/Documentation/DocBook/v4l/videodev2.h.xml index 325b23b..2b796a2 100644 --- a/Documentation/DocBook/v4l/videodev2.h.xml +++ b/Documentation/DocBook/v4l/videodev2.h.xml @@ -71,6 +71,7 @@ * Moved from videodev.h */ #define VIDEO_MAX_FRAME 32 +#define VIDEO_MAX_PLANES 8 #ifndef __KERNEL__ @@ -158,9 +159,23 @@ enum v4l2_buf_type { /* Experimental */ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, #endif + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10, V4L2_BUF_TYPE_PRIVATE = 0x80, }; +#define V4L2_TYPE_IS_MULTIPLANAR(type) \ + ((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE \ + || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + +#define V4L2_TYPE_IS_OUTPUT(type) \ + ((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT \ + || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE \ + || (type) == V4L2_BUF_TYPE_VIDEO_OVERLAY \ + || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY \ + || (type) == V4L2_BUF_TYPE_VBI_OUTPUT \ + || (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) + enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, V4L2_TUNER_ANALOG_TV = 2, @@ -246,6 +261,11 @@ struct v4l2_capability { #define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */ #define V4L2_CAP_RDS_OUTPUT 0x00000800 /* Is an RDS encoder */ +/* Is a video capture device that supports multiplanar formats */ +#define V4L2_CAP_VIDEO_CAPTURE_MPLANE 0x00001000 +/* Is a video output device that supports multiplanar formats */ +#define V4L2_CAP_VIDEO_OUTPUT_MPLANE 0x00002000 + #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ #define V4L2_CAP_RADIO 0x00040000 /* is a radio device */ @@ -320,6 +340,13 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_NV16 v4l2_fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */ #define V4L2_PIX_FMT_NV61 v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */ +/* two non contiguous planes - one Y, one Cr + Cb interleaved */ +#define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */ +#define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 macroblocks */ + +/* three non contiguous planes - Y, Cb, Cr */ +#define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12 YUV420 planar */ + /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */ #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */ @@ -518,6 +545,62 @@ struct v4l2_requestbuffers { __u32 reserved[2]; }; +/** + * struct v4l2_plane - plane info for multi-planar buffers + * @bytesused: number of bytes occupied by data in the plane (payload) + * @length: size of this plane (NOT the payload) in bytes + * @mem_offset: when memory in the associated struct v4l2_buffer is + * V4L2_MEMORY_MMAP, equals the offset from the start of + * the device memory for this plane (or is a "cookie" that + * should be passed to mmap() called on the video node) + * @userptr: when memory is V4L2_MEMORY_USERPTR, a userspace pointer + * pointing to this plane + * @data_offset: offset in the plane to the start of data; usually 0, + * unless there is a header in front of the data + * + * Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer + * with two planes can have one plane for Y, and another for interleaved CbCr + * components. Each plane can reside in a separate memory buffer, or even in + * a completely separate memory node (e.g. in embedded devices). + */ +struct v4l2_plane { + __u32 bytesused; + __u32 length; + union { + __u32 mem_offset; + unsigned long userptr; + } m; + __u32 data_offset; + __u32 reserved[11]; +}; + +/** + * struct v4l2_buffer - video buffer info + * @index: id number of the buffer + * @type: buffer type (type == *_MPLANE for multiplanar buffers) + * @bytesused: number of bytes occupied by data in the buffer (payload); + * unused (set to 0) for multiplanar buffers + * @flags: buffer informational flags + * @field: field order of the image in the buffer + * @timestamp: frame timestamp + * @timecode: frame timecode + * @sequence: sequence count of this frame + * @memory: the method, in which the actual video data is passed + * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP; + * offset from the start of the device memory for this plane, + * (or a "cookie" that should be passed to mmap() as offset) + * @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR; + * a userspace pointer pointing to this buffer + * @planes: for multiplanar buffers; userspace pointer to the array of plane + * info structs for this buffer + * @length: size in bytes of the buffer (NOT its payload) for single-plane + * buffers (when type != *_MPLANE); number of elements in the + * planes array for multi-plane buffers + * @input: input number from which the video data has has been captured + * + * Contains data exchanged by application and driver using one of the Streaming + * I/O methods. + */ struct v4l2_buffer { __u32 index; enum v4l2_buf_type type; @@ -533,6 +616,7 @@ struct v4l2_buffer { union { __u32 offset; unsigned long userptr; + struct v4l2_plane *planes; } m; __u32 length; __u32 input; @@ -1623,12 +1707,56 @@ struct v4l2_mpeg_vbi_fmt_ivtv { * A G G R E G A T E S T R U C T U R E S */ -/* Stream data format +/** + * struct v4l2_plane_pix_format - additional, per-plane format definition + * @sizeimage: maximum size in bytes required for data, for which + * this plane will be used + * @bytesperline: distance in bytes between the leftmost pixels in two + * adjacent lines + */ +struct v4l2_plane_pix_format { + __u32 sizeimage; + __u16 bytesperline; + __u16 reserved[7]; +} __attribute__ ((packed)); + +/** + * struct v4l2_pix_format_mplane - multiplanar format definition + * @width: image width in pixels + * @height: image height in pixels + * @pixelformat: little endian four character code (fourcc) + * @field: field order (for interlaced video) + * @colorspace: supplemental to pixelformat + * @plane_fmt: per-plane information + * @num_planes: number of planes for this format + */ +struct v4l2_pix_format_mplane { + __u32 width; + __u32 height; + __u32 pixelformat; + enum v4l2_field field; + enum v4l2_colorspace colorspace; + + struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES]; + __u8 num_planes; + __u8 reserved[11]; +} __attribute__ ((packed)); + +/** + * struct v4l2_format - stream data format + * @type: type of the data stream + * @pix: definition of an image format + * @pix_mp: definition of a multiplanar image format + * @win: definition of an overlaid image + * @vbi: raw VBI capture or output parameters + * @sliced: sliced VBI capture or output parameters + * @raw_data: placeholder for future extensions and custom formats */ struct v4l2_format { enum v4l2_buf_type type; union { struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ + struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */ struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ @@ -1636,7 +1764,6 @@ struct v4l2_format { } fmt; }; - /* Stream type-dependent parameters */ struct v4l2_streamparm { @@ -1809,16 +1936,6 @@ struct v4l2_dbg_chip_ident { /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ -#ifdef __OLD_VIDIOC_ -/* for compatibility, will go away some day */ -#define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int) -#define VIDIOC_S_PARM_OLD _IOW('V', 22, struct v4l2_streamparm) -#define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control) -#define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct v4l2_audio) -#define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct v4l2_audioout) -#define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct v4l2_cropcap) -#endif - #define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */ #endif /* __LINUX_VIDEODEV2_H */ -- cgit v0.10.2 From a602d520acd331ba8bbc02aacccb5e9a0f561f25 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 1 Mar 2011 10:12:36 -0300 Subject: [media] DocBook: Document the removal of the old VIDIOC_*_OLD ioctls Those ioctls passed away. Properly documented it. Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/compat.xml b/Documentation/DocBook/v4l/compat.xml index 223c24c..4d74bf2 100644 --- a/Documentation/DocBook/v4l/compat.xml +++ b/Documentation/DocBook/v4l/compat.xml @@ -1711,8 +1711,8 @@ ioctl would enumerate the available audio inputs. An ioctl to determine the current audio input, if more than one combines with the current video input, did not exist. So VIDIOC_G_AUDIO was renamed to -VIDIOC_G_AUDIO_OLD, this ioctl will be removed in -the future. The &VIDIOC-ENUMAUDIO; ioctl was added to enumerate +VIDIOC_G_AUDIO_OLD, this ioctl was removed on +Kernel 2.6.39. The &VIDIOC-ENUMAUDIO; ioctl was added to enumerate audio inputs, while &VIDIOC-G-AUDIO; now reports the current audio input. The same changes were made to &VIDIOC-G-AUDOUT; and @@ -1726,7 +1726,7 @@ must be updated to successfully compile again. The &VIDIOC-OVERLAY; ioctl was incorrectly defined with write-read parameter. It was changed to write-only, while the write-read version was renamed to VIDIOC_OVERLAY_OLD. The old -ioctl will be removed in the future. Until further the "videodev" +ioctl was removed on Kernel 2.6.39. Until further the "videodev" kernel module will automatically translate to the new version, so drivers must be recompiled, but not applications. @@ -1744,7 +1744,7 @@ surface can be seen. defined with write-only parameter, inconsistent with other ioctls modifying their argument. They were changed to write-read, while a _OLD suffix was added to the write-only versions. -The old ioctls will be removed in the future. Drivers and +The old ioctls were removed on Kernel 2.6.39. Drivers and applications assuming a constant parameter need an update. @@ -1815,8 +1815,8 @@ yet to be addressed, for details see The &VIDIOC-CROPCAP; ioctl was incorrectly defined with read-only parameter. It is now defined as write-read ioctl, while the read-only version was renamed to -VIDIOC_CROPCAP_OLD. The old ioctl will be removed -in the future. +VIDIOC_CROPCAP_OLD. The old ioctl was removed +on Kernel 2.6.39.
@@ -2364,6 +2364,14 @@ that used it. It was originally scheduled for removal in 2.6.35. +
+ V4L2 in Linux 2.6.39 + + + The old VIDIOC_*_OLD symbols and V4L1 support were removed. + + +
Relation of V4L2 to other Linux multimedia APIs -- cgit v0.10.2 From 3822f18d8f1c2cf13ade623b2bba6d4a7a1a3f53 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 1 Mar 2011 16:03:11 -0300 Subject: [media] DocBook/v4l2.xml: Update version of the spec Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml index c3d6991..7859d7d 100644 --- a/Documentation/DocBook/v4l/v4l2.xml +++ b/Documentation/DocBook/v4l/v4l2.xml @@ -128,6 +128,14 @@ structs, ioctls) must be noted in more detail in the history chapter applications. --> + 2.6.39 + 2011-03-01 + mcc + Removed VIDIOC_*_OLD from videodev2.h header and update it to reflect latest changes. + + + + 2.6.38 2011-01-16 po -- cgit v0.10.2 From 94fd5b7401e330498331ea3667d796e74c63d08a Mon Sep 17 00:00:00 2001 From: Matti Aaltonen Date: Tue, 1 Mar 2011 10:10:35 -0300 Subject: [media] MFD: WL1273 FM Radio: MFD driver for the FM radio This is the core of the WL1273 FM radio driver, it connects the two child modules. The two child drivers are drivers/media/radio/radio-wl1273.c and sound/soc/codecs/wl1273.c. The radio-wl1273 driver implements the V4L2 interface and communicates with the device. The ALSA codec offers digital audio, without it only analog audio is available. Signed-off-by: Matti J. Aaltonen Acked-by: Samuel Ortiz Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fd01836..9db079b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -615,7 +615,7 @@ config MFD_VX855 and/or vx855_gpio drivers for this to do anything useful. config MFD_WL1273_CORE - tristate + tristate "Support for TI WL1273 FM radio." depends on I2C select MFD_CORE default n diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c index d2ecc24..4025a4b 100644 --- a/drivers/mfd/wl1273-core.c +++ b/drivers/mfd/wl1273-core.c @@ -1,7 +1,7 @@ /* * MFD driver for wl1273 FM radio and audio codec submodules. * - * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2011 Nokia Corporation * Author: Matti Aaltonen * * This program is free software; you can redistribute it and/or modify @@ -31,6 +31,145 @@ static struct i2c_device_id wl1273_driver_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, wl1273_driver_id_table); +static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value) +{ + struct i2c_client *client = core->client; + u8 b[2]; + int r; + + r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b); + if (r != 2) { + dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg); + return -EREMOTEIO; + } + + *value = (u16)b[0] << 8 | b[1]; + + return 0; +} + +static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param) +{ + struct i2c_client *client = core->client; + u8 buf[] = { (param >> 8) & 0xff, param & 0xff }; + int r; + + r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf); + if (r) { + dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd); + return r; + } + + return 0; +} + +static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len) +{ + struct i2c_client *client = core->client; + struct i2c_msg msg; + int r; + + msg.addr = client->addr; + msg.flags = 0; + msg.buf = data; + msg.len = len; + + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + dev_err(&client->dev, "%s: write error.\n", __func__); + return -EREMOTEIO; + } + + return 0; +} + +/** + * wl1273_fm_set_audio() - Set audio mode. + * @core: A pointer to the device struct. + * @new_mode: The new audio mode. + * + * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG. + */ +static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode) +{ + int r = 0; + + if (core->mode == WL1273_MODE_OFF || + core->mode == WL1273_MODE_SUSPENDED) + return -EPERM; + + if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) { + r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET, + WL1273_PCM_DEF_MODE); + if (r) + goto out; + + r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, + core->i2s_mode); + if (r) + goto out; + + r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, + WL1273_AUDIO_ENABLE_I2S); + if (r) + goto out; + + } else if (core->mode == WL1273_MODE_RX && + new_mode == WL1273_AUDIO_ANALOG) { + r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, + WL1273_AUDIO_ENABLE_ANALOG); + if (r) + goto out; + + } else if (core->mode == WL1273_MODE_TX && + new_mode == WL1273_AUDIO_DIGITAL) { + r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, + core->i2s_mode); + if (r) + goto out; + + r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, + WL1273_AUDIO_IO_SET_I2S); + if (r) + goto out; + + } else if (core->mode == WL1273_MODE_TX && + new_mode == WL1273_AUDIO_ANALOG) { + r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, + WL1273_AUDIO_IO_SET_ANALOG); + if (r) + goto out; + } + + core->audio_mode = new_mode; +out: + return r; +} + +/** + * wl1273_fm_set_volume() - Set volume. + * @core: A pointer to the device struct. + * @volume: The new volume value. + */ +static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume) +{ + u16 val; + int r; + + if (volume > WL1273_MAX_VOLUME) + return -EINVAL; + + if (core->volume == volume) + return 0; + + r = wl1273_fm_write_cmd(core, WL1273_VOLUME_SET, volume); + if (r) + return r; + + core->volume = volume; + return 0; +} + static int wl1273_core_remove(struct i2c_client *client) { struct wl1273_core *core = i2c_get_clientdata(client); @@ -38,7 +177,6 @@ static int wl1273_core_remove(struct i2c_client *client) dev_dbg(&client->dev, "%s\n", __func__); mfd_remove_devices(&client->dev); - i2c_set_clientdata(client, NULL); kfree(core); return 0; @@ -83,6 +221,12 @@ static int __devinit wl1273_core_probe(struct i2c_client *client, cell->data_size = sizeof(core); children++; + core->read = wl1273_fm_read_reg; + core->write = wl1273_fm_write_cmd; + core->write_data = wl1273_fm_write_data; + core->set_audio = wl1273_fm_set_audio; + core->set_volume = wl1273_fm_set_volume; + if (pdata->children & WL1273_CODEC_CHILD) { cell = &core->cells[children]; @@ -104,7 +248,6 @@ static int __devinit wl1273_core_probe(struct i2c_client *client, return 0; err: - i2c_set_clientdata(client, NULL); pdata->free_resources(); kfree(core); diff --git a/include/linux/mfd/wl1273-core.h b/include/linux/mfd/wl1273-core.h index 9787293..db2f3f4 100644 --- a/include/linux/mfd/wl1273-core.h +++ b/include/linux/mfd/wl1273-core.h @@ -280,7 +280,9 @@ struct wl1273_core { struct i2c_client *client; + int (*read)(struct wl1273_core *core, u8, u16 *); int (*write)(struct wl1273_core *core, u8, u16); + int (*write_data)(struct wl1273_core *core, u8 *, u16); int (*set_audio)(struct wl1273_core *core, unsigned int); int (*set_volume)(struct wl1273_core *core, unsigned int); }; -- cgit v0.10.2 From 96a9cc681ecfaf2ed9b6c62090888550f604d783 Mon Sep 17 00:00:00 2001 From: Matti Aaltonen Date: Tue, 1 Mar 2011 10:10:36 -0300 Subject: [media] V4L2: WL1273 FM Radio: TI WL1273 FM radio driver This module implements V4L2 controls for the Texas Instruments WL1273 FM Radio and handles the communication with the chip. Signed-off-by: Matti J. Aaltonen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 7ecc8e6..9e177dc 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1,7 +1,7 @@ /* * Driver for the Texas Instruments WL1273 FM radio. * - * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2011 Nokia Corporation * Author: Matti J. Aaltonen * * This program is free software; you can redistribute it and/or @@ -104,58 +104,6 @@ static unsigned int rds_buf = 100; module_param(rds_buf, uint, 0); MODULE_PARM_DESC(rds_buf, "Number of RDS buffer entries. Default = 100"); -static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value) -{ - struct i2c_client *client = core->client; - u8 b[2]; - int r; - - r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b); - if (r != 2) { - dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg); - return -EREMOTEIO; - } - - *value = (u16)b[0] << 8 | b[1]; - - return 0; -} - -static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param) -{ - struct i2c_client *client = core->client; - u8 buf[] = { (param >> 8) & 0xff, param & 0xff }; - int r; - - r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf); - if (r) { - dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd); - return r; - } - - return 0; -} - -static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len) -{ - struct i2c_client *client = core->client; - struct i2c_msg msg; - int r; - - msg.addr = client->addr; - msg.flags = 0; - msg.buf = data; - msg.len = len; - - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - dev_err(&client->dev, "%s: write error.\n", __func__); - return -EREMOTEIO; - } - - return 0; -} - static int wl1273_fm_write_fw(struct wl1273_core *core, __u8 *fw, int len) { @@ -188,94 +136,6 @@ static int wl1273_fm_write_fw(struct wl1273_core *core, return r; } -/** - * wl1273_fm_set_audio() - Set audio mode. - * @core: A pointer to the device struct. - * @new_mode: The new audio mode. - * - * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG. - */ -static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode) -{ - int r = 0; - - if (core->mode == WL1273_MODE_OFF || - core->mode == WL1273_MODE_SUSPENDED) - return -EPERM; - - if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) { - r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET, - WL1273_PCM_DEF_MODE); - if (r) - goto out; - - r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, - core->i2s_mode); - if (r) - goto out; - - r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, - WL1273_AUDIO_ENABLE_I2S); - if (r) - goto out; - - } else if (core->mode == WL1273_MODE_RX && - new_mode == WL1273_AUDIO_ANALOG) { - r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, - WL1273_AUDIO_ENABLE_ANALOG); - if (r) - goto out; - - } else if (core->mode == WL1273_MODE_TX && - new_mode == WL1273_AUDIO_DIGITAL) { - r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, - core->i2s_mode); - if (r) - goto out; - - r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, - WL1273_AUDIO_IO_SET_I2S); - if (r) - goto out; - - } else if (core->mode == WL1273_MODE_TX && - new_mode == WL1273_AUDIO_ANALOG) { - r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, - WL1273_AUDIO_IO_SET_ANALOG); - if (r) - goto out; - } - - core->audio_mode = new_mode; -out: - return r; -} - -/** - * wl1273_fm_set_volume() - Set volume. - * @core: A pointer to the device struct. - * @volume: The new volume value. - */ -static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume) -{ - u16 val; - int r; - - if (volume > WL1273_MAX_VOLUME) - return -EINVAL; - - if (core->volume == volume) - return 0; - - val = volume; - r = wl1273_fm_read_reg(core, WL1273_VOLUME_SET, &val); - if (r) - return r; - - core->volume = volume; - return 0; -} - #define WL1273_FIFO_HAS_DATA(status) (1 << 5 & status) #define WL1273_RDS_CORRECTABLE_ERROR (1 << 3) #define WL1273_RDS_UNCORRECTABLE_ERROR (1 << 4) @@ -306,7 +166,7 @@ static int wl1273_fm_rds(struct wl1273_device *radio) if (core->mode != WL1273_MODE_RX) return 0; - r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val); + r = core->read(core, WL1273_RDS_SYNC_GET, &val); if (r) return r; @@ -374,7 +234,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) u16 flags; int r; - r = wl1273_fm_read_reg(core, WL1273_FLAG_GET, &flags); + r = core->read(core, WL1273_FLAG_GET, &flags); if (r) goto out; @@ -398,7 +258,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) if (flags & WL1273_LEV_EVENT) { u16 level; - r = wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &level); + r = core->read(core, WL1273_RSSI_LVL_GET, &level); if (r) goto out; @@ -439,8 +299,8 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) dev_dbg(radio->dev, "IRQ: FR:\n"); if (core->mode == WL1273_MODE_RX) { - r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET, - TUNER_MODE_STOP_SEARCH); + r = core->write(core, WL1273_TUNER_MODE_SET, + TUNER_MODE_STOP_SEARCH); if (r) { dev_err(radio->dev, "%s: TUNER_MODE_SET fails: %d\n", @@ -448,7 +308,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) goto out; } - r = wl1273_fm_read_reg(core, WL1273_FREQ_SET, &freq); + r = core->read(core, WL1273_FREQ_SET, &freq); if (r) goto out; @@ -467,7 +327,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) dev_dbg(radio->dev, "%dkHz\n", radio->rx_frequency); } else { - r = wl1273_fm_read_reg(core, WL1273_CHANL_SET, &freq); + r = core->read(core, WL1273_CHANL_SET, &freq); if (r) goto out; @@ -477,8 +337,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) } out: - wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, - radio->irq_flags); + core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); complete(&radio->busy); return IRQ_HANDLED; @@ -512,7 +371,7 @@ static int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq) dev_dbg(radio->dev, "%s: freq: %d kHz\n", __func__, freq); /* Set the current tx channel */ - r = wl1273_fm_write_cmd(core, WL1273_CHANL_SET, freq / 10); + r = core->write(core, WL1273_CHANL_SET, freq / 10); if (r) return r; @@ -526,7 +385,7 @@ static int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq) dev_dbg(radio->dev, "WL1273_CHANL_SET: %d\n", r); /* Enable the output power */ - r = wl1273_fm_write_cmd(core, WL1273_POWER_ENB_SET, 1); + r = core->write(core, WL1273_POWER_ENB_SET, 1); if (r) return r; @@ -566,20 +425,20 @@ static int wl1273_fm_set_rx_freq(struct wl1273_device *radio, unsigned int freq) dev_dbg(radio->dev, "%s: %dkHz\n", __func__, freq); - wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, radio->irq_flags); + core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); if (radio->band == WL1273_BAND_JAPAN) f = (freq - WL1273_BAND_JAPAN_LOW) / 50; else f = (freq - WL1273_BAND_OTHER_LOW) / 50; - r = wl1273_fm_write_cmd(core, WL1273_FREQ_SET, f); + r = core->write(core, WL1273_FREQ_SET, f); if (r) { dev_err(radio->dev, "FREQ_SET fails\n"); goto err; } - r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET, TUNER_MODE_PRESET); + r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_PRESET); if (r) { dev_err(radio->dev, "TUNER_MODE_SET fails\n"); goto err; @@ -609,7 +468,7 @@ static int wl1273_fm_get_freq(struct wl1273_device *radio) int r; if (core->mode == WL1273_MODE_RX) { - r = wl1273_fm_read_reg(core, WL1273_FREQ_SET, &f); + r = core->read(core, WL1273_FREQ_SET, &f); if (r) return r; @@ -619,7 +478,7 @@ static int wl1273_fm_get_freq(struct wl1273_device *radio) else freq = WL1273_BAND_OTHER_LOW + 50 * f; } else { - r = wl1273_fm_read_reg(core, WL1273_CHANL_SET, &f); + r = core->read(core, WL1273_CHANL_SET, &f); if (r) return r; @@ -670,7 +529,7 @@ static int wl1273_fm_upload_firmware_patch(struct wl1273_device *radio) } /* ignore possible error here */ - wl1273_fm_write_cmd(core, WL1273_RESET, 0); + core->write(core, WL1273_RESET, 0); dev_dbg(dev, "%s - download OK, r: %d\n", __func__, r); out: @@ -683,14 +542,14 @@ static int wl1273_fm_stop(struct wl1273_device *radio) struct wl1273_core *core = radio->core; if (core->mode == WL1273_MODE_RX) { - int r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, + int r = core->write(core, WL1273_POWER_SET, WL1273_POWER_SET_OFF); if (r) dev_err(radio->dev, "%s: POWER_SET fails: %d\n", __func__, r); } else if (core->mode == WL1273_MODE_TX) { - int r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, - WL1273_PUPD_SET_OFF); + int r = core->write(core, WL1273_PUPD_SET, + WL1273_PUPD_SET_OFF); if (r) dev_err(radio->dev, "%s: PUPD_SET fails: %d\n", __func__, r); @@ -725,11 +584,11 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode) val |= WL1273_POWER_SET_RDS; /* If this fails try again */ - r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, val); + r = core->write(core, WL1273_POWER_SET, val); if (r) { msleep(100); - r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, val); + r = core->write(core, WL1273_POWER_SET, val); if (r) { dev_err(dev, "%s: POWER_SET fails\n", __func__); goto fail; @@ -742,11 +601,10 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode) } else if (new_mode == WL1273_MODE_TX) { /* If this fails try again once */ - r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, - WL1273_PUPD_SET_ON); + r = core->write(core, WL1273_PUPD_SET, WL1273_PUPD_SET_ON); if (r) { msleep(100); - r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, + r = core->write(core, WL1273_PUPD_SET, WL1273_PUPD_SET_ON); if (r) { dev_err(dev, "%s: PUPD_SET fails\n", __func__); @@ -755,9 +613,9 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode) } if (radio->rds_on) - r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 1); + r = core->write(core, WL1273_RDS_DATA_ENB, 1); else - r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 0); + r = core->write(core, WL1273_RDS_DATA_ENB, 0); } else { dev_warn(dev, "%s: Illegal mode.\n", __func__); } @@ -777,14 +635,14 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode) if (radio->rds_on) val |= WL1273_POWER_SET_RDS; - r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, val); + r = core->write(core, WL1273_POWER_SET, val); if (r) { dev_err(dev, "%s: POWER_SET fails\n", __func__); goto fail; } } else if (new_mode == WL1273_MODE_TX) { - r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, - WL1273_PUPD_SET_ON); + r = core->write(core, WL1273_PUPD_SET, + WL1273_PUPD_SET_ON); if (r) { dev_err(dev, "%s: PUPD_SET fails\n", __func__); goto fail; @@ -808,10 +666,10 @@ static int wl1273_fm_suspend(struct wl1273_device *radio) /* Cannot go from OFF to SUSPENDED */ if (core->mode == WL1273_MODE_RX) - r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, + r = core->write(core, WL1273_POWER_SET, WL1273_POWER_SET_RETENTION); else if (core->mode == WL1273_MODE_TX) - r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET, + r = core->write(core, WL1273_PUPD_SET, WL1273_PUPD_SET_RETENTION); else r = -EINVAL; @@ -852,8 +710,7 @@ static int wl1273_fm_set_mode(struct wl1273_device *radio, int mode) } core->mode = mode; - r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, - radio->irq_flags); + r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); if (r) { dev_err(dev, "INT_MASK_SET fails.\n"); goto out; @@ -951,22 +808,21 @@ static int wl1273_fm_set_seek(struct wl1273_device *radio, INIT_COMPLETION(radio->busy); dev_dbg(radio->dev, "%s: BUSY\n", __func__); - r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, radio->irq_flags); + r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); if (r) goto out; dev_dbg(radio->dev, "%s\n", __func__); - r = wl1273_fm_write_cmd(core, WL1273_SEARCH_LVL_SET, level); + r = core->write(core, WL1273_SEARCH_LVL_SET, level); if (r) goto out; - r = wl1273_fm_write_cmd(core, WL1273_SEARCH_DIR_SET, dir); + r = core->write(core, WL1273_SEARCH_DIR_SET, dir); if (r) goto out; - r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET, - TUNER_MODE_AUTO_SEEK); + r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK); if (r) goto out; @@ -994,8 +850,7 @@ static int wl1273_fm_set_seek(struct wl1273_device *radio, INIT_COMPLETION(radio->busy); dev_dbg(radio->dev, "%s: BUSY\n", __func__); - r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET, - TUNER_MODE_AUTO_SEEK); + r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK); if (r) goto out; @@ -1020,7 +875,7 @@ static unsigned int wl1273_fm_get_tx_ctune(struct wl1273_device *radio) core->mode == WL1273_MODE_SUSPENDED) return -EPERM; - r = wl1273_fm_read_reg(core, WL1273_READ_FMANT_TUNE_VALUE, &val); + r = core->read(core, WL1273_READ_FMANT_TUNE_VALUE, &val); if (r) { dev_err(dev, "%s: read error: %d\n", __func__, r); goto out; @@ -1066,7 +921,7 @@ static int wl1273_fm_set_preemphasis(struct wl1273_device *radio, goto out; } - r = wl1273_fm_write_cmd(core, WL1273_PREMPH_SET, em); + r = core->write(core, WL1273_PREMPH_SET, em); if (r) goto out; @@ -1086,7 +941,7 @@ static int wl1273_fm_rds_on(struct wl1273_device *radio) if (radio->rds_on) return 0; - r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, + r = core->write(core, WL1273_POWER_SET, WL1273_POWER_SET_FM | WL1273_POWER_SET_RDS); if (r) goto out; @@ -1108,7 +963,7 @@ static int wl1273_fm_rds_off(struct wl1273_device *radio) radio->irq_flags &= ~WL1273_RDS_EVENT; - r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, radio->irq_flags); + r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); if (r) goto out; @@ -1120,7 +975,7 @@ static int wl1273_fm_rds_off(struct wl1273_device *radio) dev_dbg(radio->dev, "%s\n", __func__); - r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, WL1273_POWER_SET_FM); + r = core->write(core, WL1273_POWER_SET, WL1273_POWER_SET_FM); if (r) goto out; @@ -1143,14 +998,14 @@ static int wl1273_fm_set_rds(struct wl1273_device *radio, unsigned int new_mode) return -EPERM; if (new_mode == WL1273_RDS_RESET) { - r = wl1273_fm_write_cmd(core, WL1273_RDS_CNTRL_SET, 1); + r = core->write(core, WL1273_RDS_CNTRL_SET, 1); return r; } if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_OFF) { - r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 0); + r = core->write(core, WL1273_RDS_DATA_ENB, 0); } else if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_ON) { - r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 1); + r = core->write(core, WL1273_RDS_DATA_ENB, 1); } else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_OFF) { r = wl1273_fm_rds_off(radio); } else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_ON) { @@ -1171,12 +1026,13 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); + struct wl1273_core *core = radio->core; u16 val; int r; dev_dbg(radio->dev, "%s\n", __func__); - if (radio->core->mode != WL1273_MODE_TX) + if (core->mode != WL1273_MODE_TX) return count; if (radio->rds_users == 0) { @@ -1184,7 +1040,7 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf, return 0; } - if (mutex_lock_interruptible(&radio->core->lock)) + if (mutex_lock_interruptible(&core->lock)) return -EINTR; /* * Multiple processes can open the device, but only @@ -1202,7 +1058,7 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf, else val = count; - wl1273_fm_write_cmd(radio->core, WL1273_RDS_CONFIG_DATA_SET, val); + core->write(core, WL1273_RDS_CONFIG_DATA_SET, val); if (copy_from_user(radio->write_buf + 1, buf, val)) { r = -EFAULT; @@ -1213,11 +1069,11 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf, dev_dbg(radio->dev, "From user: \"%s\"\n", radio->write_buf); radio->write_buf[0] = WL1273_RDS_DATA_SET; - wl1273_fm_write_data(radio->core, radio->write_buf, val + 1); + core->write_data(core, radio->write_buf, val + 1); r = val; out: - mutex_unlock(&radio->core->lock); + mutex_unlock(&core->lock); return r; } @@ -1263,8 +1119,8 @@ static int wl1273_fm_fops_open(struct file *file) radio->irq_flags |= WL1273_RDS_EVENT; - r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, - radio->irq_flags); + r = core->write(core, WL1273_INT_MASK_SET, + radio->irq_flags); if (r) { mutex_unlock(&core->lock); goto out; @@ -1295,9 +1151,9 @@ static int wl1273_fm_fops_release(struct file *file) radio->irq_flags &= ~WL1273_RDS_EVENT; if (core->mode == WL1273_MODE_RX) { - r = wl1273_fm_write_cmd(core, - WL1273_INT_MASK_SET, - radio->irq_flags); + r = core->write(core, + WL1273_INT_MASK_SET, + radio->irq_flags); if (r) { mutex_unlock(&core->lock); goto out; @@ -1324,7 +1180,7 @@ static ssize_t wl1273_fm_fops_read(struct file *file, char __user *buf, dev_dbg(radio->dev, "%s\n", __func__); - if (radio->core->mode != WL1273_MODE_RX) + if (core->mode != WL1273_MODE_RX) return 0; if (radio->rds_users == 0) { @@ -1345,7 +1201,7 @@ static ssize_t wl1273_fm_fops_read(struct file *file, char __user *buf, } radio->owner = file; - r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val); + r = core->read(core, WL1273_RDS_SYNC_GET, &val); if (r) { dev_err(radio->dev, "%s: Get RDS_SYNC fails.\n", __func__); goto out; @@ -1466,23 +1322,24 @@ static int wl1273_fm_vidioc_s_input(struct file *file, void *priv, */ static int wl1273_fm_set_tx_power(struct wl1273_device *radio, u16 power) { + struct wl1273_core *core = radio->core; int r; - if (radio->core->mode == WL1273_MODE_OFF || - radio->core->mode == WL1273_MODE_SUSPENDED) + if (core->mode == WL1273_MODE_OFF || + core->mode == WL1273_MODE_SUSPENDED) return -EPERM; - mutex_lock(&radio->core->lock); + mutex_lock(&core->lock); /* Convert the dBuV value to chip presentation */ - r = wl1273_fm_write_cmd(radio->core, WL1273_POWER_LEV_SET, 122 - power); + r = core->write(core, WL1273_POWER_LEV_SET, 122 - power); if (r) goto out; radio->tx_power = power; out: - mutex_unlock(&radio->core->lock); + mutex_unlock(&core->lock); return r; } @@ -1493,23 +1350,24 @@ out: static int wl1273_fm_tx_set_spacing(struct wl1273_device *radio, unsigned int spacing) { + struct wl1273_core *core = radio->core; int r; if (spacing == 0) { - r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET, - WL1273_SPACING_100kHz); + r = core->write(core, WL1273_SCAN_SPACING_SET, + WL1273_SPACING_100kHz); radio->spacing = 100; } else if (spacing - 50000 < 25000) { - r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET, - WL1273_SPACING_50kHz); + r = core->write(core, WL1273_SCAN_SPACING_SET, + WL1273_SPACING_50kHz); radio->spacing = 50; } else if (spacing - 100000 < 50000) { - r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET, - WL1273_SPACING_100kHz); + r = core->write(core, WL1273_SCAN_SPACING_SET, + WL1273_SPACING_100kHz); radio->spacing = 100; } else { - r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET, - WL1273_SPACING_200kHz); + r = core->write(core, WL1273_SCAN_SPACING_SET, + WL1273_SPACING_200kHz); radio->spacing = 200; } @@ -1567,17 +1425,17 @@ static int wl1273_fm_vidioc_s_ctrl(struct v4l2_ctrl *ctrl) return -EINTR; if (core->mode == WL1273_MODE_RX && ctrl->val) - r = wl1273_fm_write_cmd(core, - WL1273_MUTE_STATUS_SET, - WL1273_MUTE_HARD_LEFT | - WL1273_MUTE_HARD_RIGHT); + r = core->write(core, + WL1273_MUTE_STATUS_SET, + WL1273_MUTE_HARD_LEFT | + WL1273_MUTE_HARD_RIGHT); else if (core->mode == WL1273_MODE_RX) - r = wl1273_fm_write_cmd(core, - WL1273_MUTE_STATUS_SET, 0x0); + r = core->write(core, + WL1273_MUTE_STATUS_SET, 0x0); else if (core->mode == WL1273_MODE_TX && ctrl->val) - r = wl1273_fm_write_cmd(core, WL1273_MUTE, 1); + r = core->write(core, WL1273_MUTE, 1); else if (core->mode == WL1273_MODE_TX) - r = wl1273_fm_write_cmd(core, WL1273_MUTE, 0); + r = core->write(core, WL1273_MUTE, 0); mutex_unlock(&core->lock); break; @@ -1672,7 +1530,7 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv, if (mutex_lock_interruptible(&core->lock)) return -EINTR; - r = wl1273_fm_read_reg(core, WL1273_STEREO_GET, &val); + r = core->read(core, WL1273_STEREO_GET, &val); if (r) goto out; @@ -1681,7 +1539,7 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv, else tuner->rxsubchans = V4L2_TUNER_SUB_MONO; - r = wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &val); + r = core->read(core, WL1273_RSSI_LVL_GET, &val); if (r) goto out; @@ -1690,7 +1548,7 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv, tuner->afc = 0; - r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val); + r = core->read(core, WL1273_RDS_SYNC_GET, &val); if (r) goto out; @@ -1736,8 +1594,7 @@ static int wl1273_fm_vidioc_s_tuner(struct file *file, void *priv, dev_warn(radio->dev, "%s: RDS fails: %d\n", __func__, r); if (tuner->audmode == V4L2_TUNER_MODE_MONO) { - r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET, - WL1273_RX_MONO); + r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO); if (r < 0) { dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n", __func__, r); @@ -1745,8 +1602,7 @@ static int wl1273_fm_vidioc_s_tuner(struct file *file, void *priv, } radio->stereo = false; } else if (tuner->audmode == V4L2_TUNER_MODE_STEREO) { - r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET, - WL1273_RX_STEREO); + r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO); if (r < 0) { dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n", __func__, r); @@ -1885,10 +1741,10 @@ static int wl1273_fm_vidioc_s_modulator(struct file *file, void *priv, r = wl1273_fm_set_rds(radio, WL1273_RDS_OFF); if (modulator->txsubchans & V4L2_TUNER_SUB_MONO) - r = wl1273_fm_write_cmd(core, WL1273_MONO_SET, WL1273_TX_MONO); + r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO); else - r = wl1273_fm_write_cmd(core, WL1273_MONO_SET, - WL1273_RX_STEREO); + r = core->write(core, WL1273_MONO_SET, + WL1273_RX_STEREO); if (r < 0) dev_warn(radio->dev, WL1273_FM_DRIVER_NAME "MONO_SET fails: %d\n", r); @@ -1923,7 +1779,7 @@ static int wl1273_fm_vidioc_g_modulator(struct file *file, void *priv, if (mutex_lock_interruptible(&core->lock)) return -EINTR; - r = wl1273_fm_read_reg(core, WL1273_MONO_SET, &val); + r = core->read(core, WL1273_MONO_SET, &val); if (r) goto out; @@ -1960,38 +1816,38 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) return 0; } - r = wl1273_fm_read_reg(core, WL1273_ASIC_ID_GET, &val); + r = core->read(core, WL1273_ASIC_ID_GET, &val); if (r) dev_err(dev, "%s: Get ASIC_ID fails.\n", __func__); else dev_info(dev, "ASIC_ID: 0x%04x\n", val); - r = wl1273_fm_read_reg(core, WL1273_ASIC_VER_GET, &val); + r = core->read(core, WL1273_ASIC_VER_GET, &val); if (r) dev_err(dev, "%s: Get ASIC_VER fails.\n", __func__); else dev_info(dev, "ASIC Version: 0x%04x\n", val); - r = wl1273_fm_read_reg(core, WL1273_FIRM_VER_GET, &val); + r = core->read(core, WL1273_FIRM_VER_GET, &val); if (r) dev_err(dev, "%s: Get FIRM_VER fails.\n", __func__); else dev_info(dev, "FW version: %d(0x%04x)\n", val, val); - r = wl1273_fm_read_reg(core, WL1273_BAND_SET, &val); + r = core->read(core, WL1273_BAND_SET, &val); if (r) dev_err(dev, "%s: Get BAND fails.\n", __func__); else dev_info(dev, "BAND: %d\n", val); if (core->mode == WL1273_MODE_TX) { - r = wl1273_fm_read_reg(core, WL1273_PUPD_SET, &val); + r = core->read(core, WL1273_PUPD_SET, &val); if (r) dev_err(dev, "%s: Get PUPD fails.\n", __func__); else dev_info(dev, "PUPD: 0x%04x\n", val); - r = wl1273_fm_read_reg(core, WL1273_CHANL_SET, &val); + r = core->read(core, WL1273_CHANL_SET, &val); if (r) dev_err(dev, "%s: Get CHANL fails.\n", __func__); else @@ -1999,13 +1855,13 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) } else if (core->mode == WL1273_MODE_RX) { int bf = radio->rangelow; - r = wl1273_fm_read_reg(core, WL1273_FREQ_SET, &val); + r = core->read(core, WL1273_FREQ_SET, &val); if (r) dev_err(dev, "%s: Get FREQ fails.\n", __func__); else dev_info(dev, "RX Frequency: %dkHz\n", bf + val*50); - r = wl1273_fm_read_reg(core, WL1273_MOST_MODE_SET, &val); + r = core->read(core, WL1273_MOST_MODE_SET, &val); if (r) dev_err(dev, "%s: Get MOST_MODE fails.\n", __func__); @@ -2016,7 +1872,7 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) else dev_info(dev, "MOST_MODE: Unexpected value: %d\n", val); - r = wl1273_fm_read_reg(core, WL1273_MOST_BLEND_SET, &val); + r = core->read(core, WL1273_MOST_BLEND_SET, &val); if (r) dev_err(dev, "%s: Get MOST_BLEND fails.\n", __func__); else if (val == 0) @@ -2027,7 +1883,7 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) else dev_info(dev, "MOST_BLEND: Unexpected val: %d\n", val); - r = wl1273_fm_read_reg(core, WL1273_STEREO_GET, &val); + r = core->read(core, WL1273_STEREO_GET, &val); if (r) dev_err(dev, "%s: Get STEREO fails.\n", __func__); else if (val == 0) @@ -2037,25 +1893,25 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) else dev_info(dev, "STEREO: Unexpected value: %d\n", val); - r = wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &val); + r = core->read(core, WL1273_RSSI_LVL_GET, &val); if (r) dev_err(dev, "%s: Get RSSI_LVL fails.\n", __func__); else dev_info(dev, "RX signal strength: %d\n", (s16) val); - r = wl1273_fm_read_reg(core, WL1273_POWER_SET, &val); + r = core->read(core, WL1273_POWER_SET, &val); if (r) dev_err(dev, "%s: Get POWER fails.\n", __func__); else dev_info(dev, "POWER: 0x%04x\n", val); - r = wl1273_fm_read_reg(core, WL1273_INT_MASK_SET, &val); + r = core->read(core, WL1273_INT_MASK_SET, &val); if (r) dev_err(dev, "%s: Get INT_MASK fails.\n", __func__); else dev_info(dev, "INT_MASK: 0x%04x\n", val); - r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val); + r = core->read(core, WL1273_RDS_SYNC_GET, &val); if (r) dev_err(dev, "%s: Get RDS_SYNC fails.\n", __func__); @@ -2067,14 +1923,14 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv) else dev_info(dev, "RDS_SYNC: Unexpected value: %d\n", val); - r = wl1273_fm_read_reg(core, WL1273_I2S_MODE_CONFIG_SET, &val); + r = core->read(core, WL1273_I2S_MODE_CONFIG_SET, &val); if (r) dev_err(dev, "%s: Get I2S_MODE_CONFIG fails.\n", __func__); else dev_info(dev, "I2S_MODE_CONFIG: 0x%04x\n", val); - r = wl1273_fm_read_reg(core, WL1273_VOLUME_SET, &val); + r = core->read(core, WL1273_VOLUME_SET, &val); if (r) dev_err(dev, "%s: Get VOLUME fails.\n", __func__); else @@ -2184,10 +2040,6 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) radio->stereo = true; radio->bus_type = "I2C"; - radio->core->write = wl1273_fm_write_cmd; - radio->core->set_audio = wl1273_fm_set_audio; - radio->core->set_volume = wl1273_fm_set_volume; - if (radio->core->pdata->request_resources) { r = radio->core->pdata->request_resources(radio->core->client); if (r) { -- cgit v0.10.2 From 40285f832b09feb621d8da9db7983200a4b29311 Mon Sep 17 00:00:00 2001 From: Matti Aaltonen Date: Tue, 1 Mar 2011 10:10:37 -0300 Subject: [media] ASoC: WL1273 FM radio: Access I2C IO functions through pointers These changes are needed to keep up with the changes in the MFD core and V4L2 parts of the wl1273 FM radio driver. Use function pointers instead of exported functions for I2C IO. Also move all preprocessor constants from the wl1273.h to include/linux/mfd/wl1273-core.h. Also update the year in the copyright statement. Signed-off-by: Matti J. Aaltonen Acked-by: Mark Brown Signed-off-by: Mauro Carvalho Chehab diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c48b23c..9726d6e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -44,7 +44,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TWL6040 if TWL4030_CORE select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C - select SND_SOC_WL1273 if RADIO_WL1273 + select SND_SOC_WL1273 if MFD_WL1273_CORE select SND_SOC_WM2000 if I2C select SND_SOC_WM8350 if MFD_WM8350 select SND_SOC_WM8400 if MFD_WM8400 diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 861b28f..5836201 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -3,7 +3,7 @@ * * Author: Matti Aaltonen, * - * Copyright: (C) 2010 Nokia Corporation + * Copyright: (C) 2010, 2011 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -179,7 +179,12 @@ static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, return 0; } -static const char *wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; +/* + * TODO: Implement the audio routing in the driver. Now this control + * only indicates the setting that has been done elsewhere (in the user + * space). + */ +static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -239,7 +244,7 @@ static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, return 1; } -static const char *wl1273_audio_strings[] = { "Digital", "Analog" }; +static const char * const wl1273_audio_strings[] = { "Digital", "Analog" }; static const struct soc_enum wl1273_audio_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_strings), -- cgit v0.10.2 From ed54c0e33c6ac72c681f00a41b47840f6570262c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 Jan 2011 11:27:58 -0300 Subject: [media] dib8000: fix small memory leak on error kfree(state) if fe allocation fails. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index 3e20aa8..c1c3e26 100644 --- a/drivers/media/dvb/frontends/dib8000.c +++ b/drivers/media/dvb/frontends/dib8000.c @@ -2514,7 +2514,7 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s return NULL; fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL); if (fe == NULL) - return NULL; + goto error; memcpy(&state->cfg, cfg, sizeof(struct dib8000_config)); state->i2c.adap = i2c_adap; -- cgit v0.10.2 From b00aff6a36aeafc68cf9044751041ba432ab4495 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 Jan 2011 11:28:27 -0300 Subject: [media] dib9000: fix return type in dib9000_mbx_send_attr() dib9000_mbx_send_attr() returns an int. It doesn't work to save negative error codes in an unsigned char, so I've made "ret" an int type. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c index 43fb6e4..9151876 100644 --- a/drivers/media/dvb/frontends/dib9000.c +++ b/drivers/media/dvb/frontends/dib9000.c @@ -486,10 +486,11 @@ static int dib9000_mbx_host_init(struct dib9000_state *state, u8 risc_id) #define MAX_MAILBOX_TRY 100 static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, u8 len, u16 attr) { - u8 ret = 0, *d, b[2]; + u8 *d, b[2]; u16 tmp; u16 size; u32 i; + int ret = 0; if (!state->platform.risc.fw_is_running) return -EINVAL; -- cgit v0.10.2 From 050b648e63b6a3afd22181d6c2113c315113e4db Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 27 Feb 2011 18:08:46 -0300 Subject: [media] cx23885, altera-ci: remove operator return in void procedure Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/altera-ci.h b/drivers/media/video/cx23885/altera-ci.h index ba0c6c0..bf0aa2c 100644 --- a/drivers/media/video/cx23885/altera-ci.h +++ b/drivers/media/video/cx23885/altera-ci.h @@ -60,7 +60,6 @@ static inline int altera_ci_init(struct altera_ci_config *config, int ci_nr) static inline void altera_ci_release(void *dev, int ci_nr) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return 0; } static inline int altera_ci_irq(void *dev) @@ -87,7 +86,6 @@ static inline int altera_hw_filt_init(struct altera_ci_config *config, static inline void altera_hw_filt_release(void *dev, int filt_nr) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return 0; } static inline int altera_pid_feed_control(void *dev, int filt_nr, -- cgit v0.10.2 From 38cdbce7634bdd914b6c6e673c2f6ebedca30d43 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Mon, 28 Feb 2011 16:52:08 -0300 Subject: [media] stv0900: speed up DVB-S searching Simptoms: After zapping DVB-S2 channel searching for DVB-S tooks long time. Now it takes less than a second. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 1ca1c83..0d9b456 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -1604,6 +1604,9 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe, p_search.standard = STV0900_AUTO_SEARCH; p_search.iq_inversion = STV0900_IQ_AUTO; p_search.search_algo = STV0900_BLIND_SEARCH; + /* Speeds up DVB-S searching */ + if (c->delivery_system == SYS_DVBS) + p_search.standard = STV0900_SEARCH_DVBS1; intp->srch_standard[demod] = p_search.standard; intp->symbol_rate[demod] = p_search.symbol_rate; -- cgit v0.10.2 From 3b30e0a871389eda9034084faec67d43478fb4c0 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Mon, 14 Feb 2011 16:03:13 -0300 Subject: [media] stv0900: Update status when LOCK is missed Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 0d9b456..0ca316d 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -1666,6 +1666,7 @@ static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status) if (state->config->set_lock_led) state->config->set_lock_led(fe, 1); } else { + *status = 0; if (state->config->set_lock_led) state->config->set_lock_led(fe, 0); dprintk("DEMOD LOCK FAIL\n"); -- cgit v0.10.2 From 738e8ff90fae01dccc9e7f92e0cfa4819567dbb8 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 27 Feb 2011 17:29:55 -0300 Subject: [media] ds3000: wrong hardware tune function implemented It is taken from another tree, where it is modified. Patch to fix that Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index 7f3d118..90bf573 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -1220,9 +1220,20 @@ static int ds3000_set_frontend(struct dvb_frontend *fe, } static int ds3000_tune(struct dvb_frontend *fe, - struct dvb_frontend_parameters *p) + struct dvb_frontend_parameters *p, + unsigned int mode_flags, + unsigned int *delay, + fe_status_t *status) { - return ds3000_set_frontend(fe, p); + if (p) { + int ret = ds3000_set_frontend(fe, p); + if (ret) + return ret; + } + + *delay = HZ / 5; + + return ds3000_read_status(fe, status); } static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) -- cgit v0.10.2 From 5265ea54bf442cef83bcbb8b5da03d1fdb37bcb8 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 27 Feb 2011 16:17:25 -0300 Subject: [media] dw2102: X3M TV SPC1400HD added The card consist of PCI to USB bridge and USB DVB-S2 receiver. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 1a1b84c..3ec0e8e 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1440,6 +1440,7 @@ static struct usb_device_id dw2102_table[] = { {USB_DEVICE(USB_VID_TERRATEC, 0x00a8)}, {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)}, {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)}, + {USB_DEVICE(0x1f4d, 0x3100)}, { } }; @@ -1813,7 +1814,7 @@ static struct dvb_usb_device_properties su3000_properties = { } } }, - .num_device_descs = 2, + .num_device_descs = 3, .devices = { { "SU3000HD DVB-S USB2.0", { &dw2102_table[10], NULL }, @@ -1823,6 +1824,10 @@ static struct dvb_usb_device_properties su3000_properties = { { &dw2102_table[11], NULL }, { NULL }, }, + { "X3M TV SPC1400HD PCI", + { &dw2102_table[14], NULL }, + { NULL }, + }, } }; -- cgit v0.10.2 From e86e4dfa5a436ccbef9ea4f7c28907aef40b875f Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 27 Feb 2011 16:18:34 -0300 Subject: [media] dw2102: remove unnecessary delays for i2c transfer for some cards Prof 7500 and TeVii s630, s660, s480 works faster now Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 3ec0e8e..1b75da0 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -590,8 +590,6 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], break; } } - - msleep(3); } mutex_unlock(&d->i2c_mutex); -- cgit v0.10.2 From a84adf4007d80249cbe04da4ee80689a8cfdffda Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 27 Feb 2011 16:18:38 -0300 Subject: [media] dw2102: i2c transfer corrected for some cards Prof 7500 and TeVii s630, s660, s480 works better now Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 1b75da0..ac70e7b 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -564,14 +564,15 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i += 16; len -= 16; } while (len > 0); - } else if ((udev->descriptor.idProduct == 0x7500) - && (j < (num - 1))) { + } else if (j < (num - 1)) { /* write register addr before read */ u8 obuf[msg[j].len + 2]; obuf[0] = msg[j + 1].len; obuf[1] = (msg[j].addr << 1); memcpy(obuf + 2, msg[j].buf, msg[j].len); - ret = dw210x_op_rw(d->udev, 0x92, 0, 0, + ret = dw210x_op_rw(d->udev, + udev->descriptor.idProduct == + 0x7500 ? 0x92 : 0x90, 0, 0, obuf, msg[j].len + 2, DW210X_WRITE_MSG); break; @@ -581,8 +582,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = msg[j].len + 1; obuf[1] = (msg[j].addr << 1); memcpy(obuf + 2, msg[j].buf, msg[j].len); - ret = dw210x_op_rw(d->udev, - (num > 1 ? 0x90 : 0x80), 0, 0, + ret = dw210x_op_rw(d->udev, 0x80, 0, 0, obuf, msg[j].len + 2, DW210X_WRITE_MSG); break; -- cgit v0.10.2 From b16af29a561c92ed76b5b882dd68bb082b07a09b Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 27 Feb 2011 16:22:57 -0300 Subject: [media] dw2102: i2c transfer corrected for yet another cards Related to Acorp DS120 and some revisions of DVBWorld DW2102. If you read three or more registers at one time, you get wrong value for third etc. Actually, it didn't affect the driver as it reads registers only by one at a time. But it needs to correct. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index ac70e7b..2889d32 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -156,8 +156,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], /* read stv0299 register */ value = msg[0].buf[0];/* register */ for (i = 0; i < msg[1].len; i++) { - value = value + i; - ret = dw210x_op_rw(d->udev, 0xb5, value, 0, + ret = dw210x_op_rw(d->udev, 0xb5, value + i, 0, buf6, 2, DW210X_READ_MSG); msg[1].buf[i] = buf6[0]; } -- cgit v0.10.2 From fd5466d1dfeba87155c7af70b7d53faa60960c19 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 27 Feb 2011 16:22:52 -0300 Subject: [media] dw2102: prof 1100 corrected Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 2889d32..98cf302 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1111,18 +1111,25 @@ static int zl100313_frontend_attach(struct dvb_usb_adapter *d) static int stv0288_frontend_attach(struct dvb_usb_adapter *d) { + u8 obuf[] = {7, 1}; + d->fe = dvb_attach(stv0288_attach, &earda_config, &d->dev->i2c_adap); - if (d->fe != NULL) { - if (dvb_attach(stb6000_attach, d->fe, 0x61, - &d->dev->i2c_adap)) { - d->fe->ops.set_voltage = dw210x_set_voltage; - info("Attached stv0288+stb6000!\n"); - return 0; - } - } - return -EIO; + if (d->fe == NULL) + return -EIO; + + if (NULL == dvb_attach(stb6000_attach, d->fe, 0x61, &d->dev->i2c_adap)) + return -EIO; + + d->fe->ops.set_voltage = dw210x_set_voltage; + + dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); + + info("Attached stv0288+stb6000!\n"); + + return 0; + } static int ds3000_frontend_attach(struct dvb_usb_adapter *d) -- cgit v0.10.2 From dc46b8c8e3d7e6c7c7ddb78fc2137a5462e98679 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Tue, 1 Mar 2011 01:55:11 -0300 Subject: [media] tm6000: add audio conf for new cards Add configuration of an audio for our new TV cards. With my best regards, Dmitry. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index a356ba7..88144a1 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -324,6 +324,8 @@ struct tm6000_board tm6000_boards[] = { .tuner_addr = 0xc2 >> 1, .demod_addr = 0x1e >> 1, .type = TM6010, + .avideo = TM6000_AIP_SIF1, + .aradio = TM6000_AIP_LINE1, .caps = { .has_tuner = 1, .has_dvb = 1, @@ -341,6 +343,8 @@ struct tm6000_board tm6000_boards[] = { .tuner_type = TUNER_XC5000, .tuner_addr = 0xc2 >> 1, .type = TM6010, + .avideo = TM6000_AIP_SIF1, + .aradio = TM6000_AIP_LINE1, .caps = { .has_tuner = 1, .has_dvb = 0, -- cgit v0.10.2 From 0cd301f19a91b3e1fb4130dcd9106777003ed4ed Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 2 Mar 2011 17:15:15 -0300 Subject: [media] staging/cx25721: serialize access to devlist Out of the three files accessing the device list, one uses a mutex, one uses the BKL and one does not have any locking. That is of course pointless, so let's make all of them use the same mutex, and get rid of one more BKL user. Signed-off-by: Arnd Bergmann Signed-off-by: Palash Bandyopadhyay Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/cx25821/Kconfig b/drivers/staging/cx25821/Kconfig index b265695..5f6b542 100644 --- a/drivers/staging/cx25821/Kconfig +++ b/drivers/staging/cx25821/Kconfig @@ -1,7 +1,6 @@ config VIDEO_CX25821 tristate "Conexant cx25821 support" depends on DVB_CORE && VIDEO_DEV && PCI && I2C - depends on BKL # please fix select I2C_ALGOBIT select VIDEO_BTCX select VIDEO_TVEEPROM diff --git a/drivers/staging/cx25821/cx25821-alsa.c b/drivers/staging/cx25821/cx25821-alsa.c index 160f669..ebdba7c 100644 --- a/drivers/staging/cx25821/cx25821-alsa.c +++ b/drivers/staging/cx25821/cx25821-alsa.c @@ -770,10 +770,12 @@ static int cx25821_alsa_init(void) struct cx25821_dev *dev = NULL; struct list_head *list; + mutex_lock(&cx25821_devlist_mutex); list_for_each(list, &cx25821_devlist) { dev = list_entry(list, struct cx25821_dev, devlist); cx25821_audio_initdev(dev); } + mutex_unlock(&cx25821_devlist_mutex); if (dev == NULL) pr_info("ERROR ALSA: no cx25821 cards found\n"); diff --git a/drivers/staging/cx25821/cx25821-core.c b/drivers/staging/cx25821/cx25821-core.c index a216b62..523ac5e 100644 --- a/drivers/staging/cx25821/cx25821-core.c +++ b/drivers/staging/cx25821/cx25821-core.c @@ -33,9 +33,6 @@ MODULE_DESCRIPTION("Driver for Athena cards"); MODULE_AUTHOR("Shu Lin - Hiep Huynh"); MODULE_LICENSE("GPL"); -struct list_head cx25821_devlist; -EXPORT_SYMBOL(cx25821_devlist); - static unsigned int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); @@ -46,8 +43,10 @@ MODULE_PARM_DESC(card, "card type"); static unsigned int cx25821_devcount; -static DEFINE_MUTEX(devlist); +DEFINE_MUTEX(cx25821_devlist_mutex); +EXPORT_SYMBOL(cx25821_devlist_mutex); LIST_HEAD(cx25821_devlist); +EXPORT_SYMBOL(cx25821_devlist); struct sram_channel cx25821_sram_channels[] = { [SRAM_CH00] = { @@ -911,9 +910,9 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->nr = ++cx25821_devcount; sprintf(dev->name, "cx25821[%d]", dev->nr); - mutex_lock(&devlist); + mutex_lock(&cx25821_devlist_mutex); list_add_tail(&dev->devlist, &cx25821_devlist); - mutex_unlock(&devlist); + mutex_unlock(&cx25821_devlist_mutex); strcpy(cx25821_boards[UNKNOWN_BOARD].name, "unknown"); strcpy(cx25821_boards[CX25821_BOARD].name, "cx25821"); @@ -1465,9 +1464,9 @@ static void __devexit cx25821_finidev(struct pci_dev *pci_dev) if (pci_dev->irq) free_irq(pci_dev->irq, dev); - mutex_lock(&devlist); + mutex_lock(&cx25821_devlist_mutex); list_del(&dev->devlist); - mutex_unlock(&devlist); + mutex_unlock(&cx25821_devlist_mutex); cx25821_dev_unregister(dev); v4l2_device_unregister(v4l2_dev); @@ -1501,7 +1500,6 @@ static struct pci_driver cx25821_pci_driver = { static int __init cx25821_init(void) { - INIT_LIST_HEAD(&cx25821_devlist); pr_info("driver version %d.%d.%d loaded\n", (CX25821_VERSION_CODE >> 16) & 0xff, (CX25821_VERSION_CODE >> 8) & 0xff, diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/staging/cx25821/cx25821-video.c index 0d8d756..ab05392 100644 --- a/drivers/staging/cx25821/cx25821-video.c +++ b/drivers/staging/cx25821/cx25821-video.c @@ -27,7 +27,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "cx25821-video.h" -#include MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); MODULE_AUTHOR("Hiep Huynh "); @@ -815,7 +814,7 @@ static int video_open(struct file *file) if (NULL == fh) return -ENOMEM; - lock_kernel(); + mutex_lock(&cx25821_devlist_mutex); list_for_each(list, &cx25821_devlist) { @@ -832,8 +831,8 @@ static int video_open(struct file *file) } if (NULL == dev) { - unlock_kernel(); - return -ENODEV; + mutex_unlock(&cx25821_devlist_mutex); + return -ENODEV; } file->private_data = fh; @@ -862,7 +861,7 @@ static int video_open(struct file *file) sizeof(struct cx25821_buffer), fh, NULL); dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); + mutex_unlock(&cx25821_devlist_mutex); return 0; } diff --git a/drivers/staging/cx25821/cx25821.h b/drivers/staging/cx25821/cx25821.h index 5511523..6230243 100644 --- a/drivers/staging/cx25821/cx25821.h +++ b/drivers/staging/cx25821/cx25821.h @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -445,6 +444,8 @@ static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev) v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) extern struct list_head cx25821_devlist; +extern struct mutex cx25821_devlist_mutex; + extern struct cx25821_board cx25821_boards[]; extern struct cx25821_subid cx25821_subids[]; -- cgit v0.10.2 From f41e4e99404ad7a6afce2817843336c681b63e46 Mon Sep 17 00:00:00 2001 From: "Justin P. Mattock" Date: Fri, 25 Feb 2011 03:11:17 -0300 Subject: [media] drivers:media:cx23418.h remove one to many l's in the word The patch below removes an extra "l" in the word. Signed-off-by: Justin P. Mattock Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 7e40035..935f557 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h @@ -477,7 +477,7 @@ /* The are no buffers ready. Try again soon! */ #define CXERR_NODATA_AGAIN 0x00001E -/* The stream is stopping. Function not alllowed now! */ +/* The stream is stopping. Function not allowed now! */ #define CXERR_STOPPING_STATUS 0x00001F /* Trying to access hardware when the power is turned OFF */ -- cgit v0.10.2 From b251e618470fee811535123c2775dfcf2b8223ab Mon Sep 17 00:00:00 2001 From: "Justin P. Mattock" Date: Fri, 25 Feb 2011 03:11:36 -0300 Subject: [media] drivers:media:cx231xx.h remove one to many l's in the word The patch below removes an extra "l" in the word. Signed-off-by: Justin P. Mattock Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index 2d5ab0c..bd4a9cf 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -469,7 +469,7 @@ struct cx231xx_fh { #define I2C_STOP 0x0 /* 1-- do not transmit STOP at end of transaction */ #define I2C_NOSTOP 0x1 -/* 1--alllow slave to insert clock wait states */ +/* 1--allow slave to insert clock wait states */ #define I2C_SYNC 0x1 struct cx231xx_i2c { -- cgit v0.10.2 From 9f00edaef8a8741a2d5333676fe9aa23a2a3d2be Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 27 Feb 2011 14:38:19 -0300 Subject: [media] v4l: videobuf2: Typo fix vb2_get_plane_payload() gets the bytesused field for a plane, it doesn't set it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 0d71fc5..597efe6 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -351,7 +351,7 @@ static inline void vb2_set_plane_payload(struct vb2_buffer *vb, } /** - * vb2_get_plane_payload() - set bytesused for the plane plane_no + * vb2_get_plane_payload() - get bytesused for the plane plane_no * @vb: buffer for which plane payload should be set * @plane_no: plane number for which payload should be set * @size: payload in bytes -- cgit v0.10.2 From fc0a80798576f80ca10b3f6c9c7097f12fd1d64e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 12 Jul 2010 11:09:41 -0300 Subject: [media] v4l: Share code between video_usercopy and video_ioctl2 The two functions are mostly identical. They handle the copy_from_user and copy_to_user operations related with V4L2 ioctls and call the real ioctl handler. Create a __video_usercopy function that implements the core of video_usercopy and video_ioctl2, and call that function from both. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 7a72074..db6ec38 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -294,101 +294,6 @@ void v4l_printk_ioctl(unsigned int cmd) } EXPORT_SYMBOL(v4l_printk_ioctl); -/* - * helper function -- handles userspace copying for ioctl arguments - * Obsolete usercopy function - Should be removed soon - */ -long -video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, - v4l2_kioctl func) -{ - char sbuf[128]; - void *mbuf = NULL; - void *parg = NULL; - long err = -EINVAL; - int is_ext_ctrl; - size_t ctrls_size = 0; - void __user *user_ptr = NULL; - - is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || - cmd == VIDIOC_TRY_EXT_CTRLS); - - /* Copy arguments into temp kernel buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: - parg = NULL; - break; - case _IOC_READ: - case _IOC_WRITE: - case (_IOC_WRITE | _IOC_READ): - if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { - parg = sbuf; - } else { - /* too big to allocate from stack */ - mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); - if (NULL == mbuf) - return -ENOMEM; - parg = mbuf; - } - - err = -EFAULT; - if (_IOC_DIR(cmd) & _IOC_WRITE) - if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) - goto out; - break; - } - if (is_ext_ctrl) { - struct v4l2_ext_controls *p = parg; - - /* In case of an error, tell the caller that it wasn't - a specific control that caused it. */ - p->error_idx = p->count; - user_ptr = (void __user *)p->controls; - if (p->count) { - ctrls_size = sizeof(struct v4l2_ext_control) * p->count; - /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ - mbuf = kmalloc(ctrls_size, GFP_KERNEL); - err = -ENOMEM; - if (NULL == mbuf) - goto out_ext_ctrl; - err = -EFAULT; - if (copy_from_user(mbuf, user_ptr, ctrls_size)) - goto out_ext_ctrl; - p->controls = mbuf; - } - } - - /* call driver */ - err = func(file, cmd, parg); - if (err == -ENOIOCTLCMD) - err = -EINVAL; - if (is_ext_ctrl) { - struct v4l2_ext_controls *p = parg; - - p->controls = (void *)user_ptr; - if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) - err = -EFAULT; - goto out_ext_ctrl; - } - if (err < 0) - goto out; - -out_ext_ctrl: - /* Copy results into user buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_READ: - case (_IOC_WRITE | _IOC_READ): - if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) - err = -EFAULT; - break; - } - -out: - kfree(mbuf); - return err; -} -EXPORT_SYMBOL(video_usercopy); - static void dbgbuf(unsigned int cmd, struct video_device *vfd, struct v4l2_buffer *p) { @@ -2332,8 +2237,9 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, return ret; } -long video_ioctl2(struct file *file, - unsigned int cmd, unsigned long arg) +long +video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + v4l2_kioctl func) { char sbuf[128]; void *mbuf = NULL; @@ -2395,7 +2301,7 @@ long video_ioctl2(struct file *file, } /* Handles IOCTL */ - err = __video_do_ioctl(file, cmd, parg); + err = func(file, cmd, parg); if (err == -ENOIOCTLCMD) err = -EINVAL; @@ -2422,4 +2328,11 @@ out: kfree(mbuf); return err; } +EXPORT_SYMBOL(video_usercopy); + +long video_ioctl2(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, __video_do_ioctl); +} EXPORT_SYMBOL(video_ioctl2); -- cgit v0.10.2 From 0070d91e5b5ae594116202ab7d62d8264830b1cd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 31 May 2010 05:33:06 -0300 Subject: [media] v4l: subdev: Don't require core operations There's no reason to require subdevices to implement the core operations. Remove the check for non-NULL core operations when initializing the subdev. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index d4d74f9..da16d2f 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -481,8 +481,7 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) { INIT_LIST_HEAD(&sd->list); - /* ops->core MUST be set */ - BUG_ON(!ops || !ops->core); + BUG_ON(!ops); sd->ops = ops; sd->v4l2_dev = NULL; sd->flags = 0; -- cgit v0.10.2 From 2096a5dcf9704f5a86ecba37169eb813aaf0431c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:38:49 -0300 Subject: [media] v4l: subdev: Add device node support Create a device node named subdevX for every registered subdev. As the device node is registered before the subdev core::s_config function is called, return -EGAIN on open until initialization completes. Signed-off-by: Laurent Pinchart Signed-off-by: Vimarsh Zutshi Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index f22f35c..8b35871 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -319,6 +319,22 @@ controlled through GPIO pins. This distinction is only relevant when setting up the device, but once the subdev is registered it is completely transparent. +V4L2 sub-device userspace API +----------------------------- + +Beside exposing a kernel API through the v4l2_subdev_ops structure, V4L2 +sub-devices can also be controlled directly by userspace applications. + +Device nodes named v4l-subdevX can be created in /dev to access sub-devices +directly. If a sub-device supports direct userspace configuration it must set +the V4L2_SUBDEV_FL_HAS_DEVNODE flag before being registered. + +After registering sub-devices, the v4l2_device driver can create device nodes +for all registered sub-devices marked with V4L2_SUBDEV_FL_HAS_DEVNODE by calling +v4l2_device_register_subdev_nodes(). Those device nodes will be automatically +removed when sub-devices are unregistered. + + I2C sub-device drivers ---------------------- diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index ac54652..7ea6516 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -11,7 +11,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ - v4l2-event.o v4l2-ctrls.o + v4l2-event.o v4l2-ctrls.o v4l2-subdev.o # V4L2 core modules diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 341764a..abe04ef 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -408,13 +408,14 @@ static int get_index(struct video_device *vdev) } /** - * video_register_device - register video4linux devices + * __video_register_device - register video4linux devices * @vdev: video device structure we want to register * @type: type of device to register * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ... * -1 == first free) * @warn_if_nr_in_use: warn if the desired device node number * was already in use and another number was chosen instead. + * @owner: module that owns the video device node * * The registration code assigns minor numbers and device node numbers * based on the requested type and registers the new device node with @@ -435,9 +436,11 @@ static int get_index(struct video_device *vdev) * %VFL_TYPE_VBI - Vertical blank data (undecoded) * * %VFL_TYPE_RADIO - A radio card + * + * %VFL_TYPE_SUBDEV - A subdevice */ -static int __video_register_device(struct video_device *vdev, int type, int nr, - int warn_if_nr_in_use) +int __video_register_device(struct video_device *vdev, int type, int nr, + int warn_if_nr_in_use, struct module *owner) { int i = 0; int ret; @@ -469,6 +472,9 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, case VFL_TYPE_RADIO: name_base = "radio"; break; + case VFL_TYPE_SUBDEV: + name_base = "v4l-subdev"; + break; default: printk(KERN_ERR "%s called with unknown type: %d\n", __func__, type); @@ -552,7 +558,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, goto cleanup; } vdev->cdev->ops = &v4l2_fops; - vdev->cdev->owner = vdev->fops->owner; + vdev->cdev->owner = owner; ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); if (ret < 0) { printk(KERN_ERR "%s: cdev_add failed\n", __func__); @@ -597,18 +603,7 @@ cleanup: vdev->minor = -1; return ret; } - -int video_register_device(struct video_device *vdev, int type, int nr) -{ - return __video_register_device(vdev, type, nr, 1); -} -EXPORT_SYMBOL(video_register_device); - -int video_register_device_no_warn(struct video_device *vdev, int type, int nr) -{ - return __video_register_device(vdev, type, nr, 0); -} -EXPORT_SYMBOL(video_register_device_no_warn); +EXPORT_SYMBOL(__video_register_device); /** * video_unregister_device - unregister a video4linux device diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index ce64fe1..8c0ad8b 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -124,16 +124,20 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, /* Check for valid input */ if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) return -EINVAL; + /* Warn if we apparently re-register a subdev */ WARN_ON(sd->v4l2_dev != NULL); + if (!try_module_get(sd->owner)) return -ENODEV; + sd->v4l2_dev = v4l2_dev; if (sd->internal_ops && sd->internal_ops->registered) { err = sd->internal_ops->registered(sd); if (err) return err; } + /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); if (err) { @@ -141,24 +145,57 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, sd->internal_ops->unregistered(sd); return err; } + spin_lock(&v4l2_dev->lock); list_add_tail(&sd->list, &v4l2_dev->subdevs); spin_unlock(&v4l2_dev->lock); + return 0; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); +int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) +{ + struct video_device *vdev; + struct v4l2_subdev *sd; + int err; + + /* Register a device node for every subdev marked with the + * V4L2_SUBDEV_FL_HAS_DEVNODE flag. + */ + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) + continue; + + vdev = &sd->devnode; + strlcpy(vdev->name, sd->name, sizeof(vdev->name)); + vdev->v4l2_dev = v4l2_dev; + vdev->fops = &v4l2_subdev_fops; + vdev->release = video_device_release_empty; + err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, + sd->owner); + if (err < 0) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); + void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) { /* return if it isn't registered */ if (sd == NULL || sd->v4l2_dev == NULL) return; + spin_lock(&sd->v4l2_dev->lock); list_del(&sd->list); spin_unlock(&sd->v4l2_dev->lock); if (sd->internal_ops && sd->internal_ops->unregistered) sd->internal_ops->unregistered(sd); sd->v4l2_dev = NULL; + + video_unregister_device(&sd->devnode); module_put(sd->owner); } EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c new file mode 100644 index 0000000..037aa28 --- /dev/null +++ b/drivers/media/video/v4l2-subdev.c @@ -0,0 +1,60 @@ +/* + * V4L2 subdevice support. + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Laurent Pinchart + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include + +static int subdev_open(struct file *file) +{ + return 0; +} + +static int subdev_close(struct file *file) +{ + return 0; +} + +static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) +{ + switch (cmd) { + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static long subdev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, subdev_do_ioctl); +} + +const struct v4l2_file_operations v4l2_subdev_fops = { + .owner = THIS_MODULE, + .open = subdev_open, + .unlocked_ioctl = subdev_ioctl, + .release = subdev_close, +}; diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 15802a0..4fe6831 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -21,7 +21,8 @@ #define VFL_TYPE_GRABBER 0 #define VFL_TYPE_VBI 1 #define VFL_TYPE_RADIO 2 -#define VFL_TYPE_MAX 3 +#define VFL_TYPE_SUBDEV 3 +#define VFL_TYPE_MAX 4 struct v4l2_ioctl_callbacks; struct video_device; @@ -102,15 +103,26 @@ struct video_device /* dev to video-device */ #define to_video_device(cd) container_of(cd, struct video_device, dev) +int __must_check __video_register_device(struct video_device *vdev, int type, + int nr, int warn_if_nr_in_use, struct module *owner); + /* Register video devices. Note that if video_register_device fails, the release() callback of the video_device structure is *not* called, so the caller is responsible for freeing any data. Usually that means that you call video_device_release() on failure. */ -int __must_check video_register_device(struct video_device *vdev, int type, int nr); +static inline int __must_check video_register_device(struct video_device *vdev, + int type, int nr) +{ + return __video_register_device(vdev, type, nr, 1, vdev->fops->owner); +} /* Same as video_register_device, but no warning is issued if the desired device node number was already in use. */ -int __must_check video_register_device_no_warn(struct video_device *vdev, int type, int nr); +static inline int __must_check video_register_device_no_warn( + struct video_device *vdev, int type, int nr) +{ + return __video_register_device(vdev, type, nr, 0, vdev->fops->owner); +} /* Unregister video devices. Will do nothing if vdev == NULL or video_is_registered() returns false. */ diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index b16f307..78b11e5 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -96,6 +96,12 @@ int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, wasn't registered. In that case it will do nothing. */ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); +/* Register device nodes for all subdev of the v4l2 device that are marked with + * the V4L2_SUBDEV_FL_HAS_DEVNODE flag. + */ +int __must_check +v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev); + /* Iterate over all subdevs. */ #define v4l2_device_for_each_subdev(sd, v4l2_dev) \ list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index da16d2f..474ef00 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -22,6 +22,7 @@ #define _V4L2_SUBDEV_H #include +#include #include /* generic v4l2_device notify callback notification values */ @@ -431,9 +432,11 @@ struct v4l2_subdev_internal_ops { #define V4L2_SUBDEV_NAME_SIZE 32 /* Set this flag if this subdev is a i2c device. */ -#define V4L2_SUBDEV_FL_IS_I2C (1U << 0) +#define V4L2_SUBDEV_FL_IS_I2C (1U << 0) /* Set this flag if this subdev is a spi device. */ -#define V4L2_SUBDEV_FL_IS_SPI (1U << 1) +#define V4L2_SUBDEV_FL_IS_SPI (1U << 1) +/* Set this flag if this subdev needs a device node. */ +#define V4L2_SUBDEV_FL_HAS_DEVNODE (1U << 2) /* Each instance of a subdev driver should create this struct, either stand-alone or embedded in a larger struct. @@ -455,8 +458,15 @@ struct v4l2_subdev { /* pointer to private data */ void *dev_priv; void *host_priv; + /* subdev device node */ + struct video_device devnode; }; +#define vdev_to_v4l2_subdev(vdev) \ + container_of(vdev, struct v4l2_subdev, devnode) + +extern const struct v4l2_file_operations v4l2_subdev_fops; + static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) { sd->dev_priv = p; -- cgit v0.10.2 From 3dd5ee0801ee10e5632b40fd8d0495417b32910a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:38:52 -0300 Subject: [media] v4l: subdev: Uninline the v4l2_subdev_init function The function isn't small or performance sensitive enough to be inlined. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 037aa28..460b336 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -1,22 +1,23 @@ /* - * V4L2 subdevice support. + * V4L2 sub-device * - * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2010 Nokia Corporation * - * Contact: Laurent Pinchart + * Contact: Laurent Pinchart + * Sakari Ailus * - * 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. + * 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. + * 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 + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include @@ -58,3 +59,17 @@ const struct v4l2_file_operations v4l2_subdev_fops = { .unlocked_ioctl = subdev_ioctl, .release = subdev_close, }; + +void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) +{ + INIT_LIST_HEAD(&sd->list); + BUG_ON(!ops); + sd->ops = ops; + sd->v4l2_dev = NULL; + sd->flags = 0; + sd->name[0] = '\0'; + sd->grp_id = 0; + sd->dev_priv = NULL; + sd->host_priv = NULL; +} +EXPORT_SYMBOL(v4l2_subdev_init); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 474ef00..3276065 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -487,19 +487,8 @@ static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd) return sd->host_priv; } -static inline void v4l2_subdev_init(struct v4l2_subdev *sd, - const struct v4l2_subdev_ops *ops) -{ - INIT_LIST_HEAD(&sd->list); - BUG_ON(!ops); - sd->ops = ops; - sd->v4l2_dev = NULL; - sd->flags = 0; - sd->name[0] = '\0'; - sd->grp_id = 0; - sd->dev_priv = NULL; - sd->host_priv = NULL; -} +void v4l2_subdev_init(struct v4l2_subdev *sd, + const struct v4l2_subdev_ops *ops); /* Call an ops of a v4l2_subdev, doing the right checks against NULL pointers. -- cgit v0.10.2 From ea8aa4349e11c62242a8908fc172de27d7a151d7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:39:54 -0300 Subject: [media] v4l: subdev: Control ioctls support Pass the control-related ioctls to the subdev driver through the control framework. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 8b35871..1feecfc 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -334,6 +334,22 @@ for all registered sub-devices marked with V4L2_SUBDEV_FL_HAS_DEVNODE by calling v4l2_device_register_subdev_nodes(). Those device nodes will be automatically removed when sub-devices are unregistered. +The device node handles a subset of the V4L2 API. + +VIDIOC_QUERYCTRL +VIDIOC_QUERYMENU +VIDIOC_G_CTRL +VIDIOC_S_CTRL +VIDIOC_G_EXT_CTRLS +VIDIOC_S_EXT_CTRLS +VIDIOC_TRY_EXT_CTRLS + + The controls ioctls are identical to the ones defined in V4L2. They + behave identically, with the only exception that they deal only with + controls implemented in the sub-device. Depending on the driver, those + controls can be also be accessed through one (or several) V4L2 device + nodes. + I2C sub-device drivers ---------------------- diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 460b336..da10430 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -39,7 +40,31 @@ static int subdev_close(struct file *file) static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + switch (cmd) { + case VIDIOC_QUERYCTRL: + return v4l2_subdev_queryctrl(sd, arg); + + case VIDIOC_QUERYMENU: + return v4l2_subdev_querymenu(sd, arg); + + case VIDIOC_G_CTRL: + return v4l2_subdev_g_ctrl(sd, arg); + + case VIDIOC_S_CTRL: + return v4l2_subdev_s_ctrl(sd, arg); + + case VIDIOC_G_EXT_CTRLS: + return v4l2_subdev_g_ext_ctrls(sd, arg); + + case VIDIOC_S_EXT_CTRLS: + return v4l2_subdev_s_ext_ctrls(sd, arg); + + case VIDIOC_TRY_EXT_CTRLS: + return v4l2_subdev_try_ext_ctrls(sd, arg); + default: return -ENOIOCTLCMD; } -- cgit v0.10.2 From 02adb1cc765b8c29dc83c6602bda19003cce62f1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 3 Mar 2010 12:49:38 -0300 Subject: [media] v4l: subdev: Events support Provide v4l2_subdevs with v4l2_event support. Subdev drivers only need very little to support events. Signed-off-by: Sakari Ailus Signed-off-by: David Cohen Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 1feecfc..eb84795 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -350,6 +350,24 @@ VIDIOC_TRY_EXT_CTRLS controls can be also be accessed through one (or several) V4L2 device nodes. +VIDIOC_DQEVENT +VIDIOC_SUBSCRIBE_EVENT +VIDIOC_UNSUBSCRIBE_EVENT + + The events ioctls are identical to the ones defined in V4L2. They + behave identically, with the only exception that they deal only with + events generated by the sub-device. Depending on the driver, those + events can also be reported by one (or several) V4L2 device nodes. + + Sub-device drivers that want to use events need to set the + V4L2_SUBDEV_USES_EVENTS v4l2_subdev::flags and initialize + v4l2_subdev::nevents to events queue depth before registering the + sub-device. After registration events can be queued as usual on the + v4l2_subdev::devnode device node. + + To properly support events, the poll() file operation is also + implemented. + I2C sub-device drivers ---------------------- diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index da10430..0c67f5a 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -20,21 +20,66 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include +#include +#include #include #include #include #include +#include +#include static int subdev_open(struct file *file) { + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct v4l2_fh *vfh; + int ret; + + if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { + vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); + if (vfh == NULL) + return -ENOMEM; + + ret = v4l2_fh_init(vfh, vdev); + if (ret) + goto err; + + ret = v4l2_event_init(vfh); + if (ret) + goto err; + + ret = v4l2_event_alloc(vfh, sd->nevents); + if (ret) + goto err; + + v4l2_fh_add(vfh); + file->private_data = vfh; + } + return 0; + +err: + if (vfh != NULL) { + v4l2_fh_exit(vfh); + kfree(vfh); + } + + return ret; } static int subdev_close(struct file *file) { + struct v4l2_fh *vfh = file->private_data; + + if (vfh != NULL) { + v4l2_fh_del(vfh); + v4l2_fh_exit(vfh); + kfree(vfh); + } + return 0; } @@ -42,6 +87,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct v4l2_fh *fh = file->private_data; switch (cmd) { case VIDIOC_QUERYCTRL: @@ -65,6 +111,18 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_TRY_EXT_CTRLS: return v4l2_subdev_try_ext_ctrls(sd, arg); + case VIDIOC_DQEVENT: + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) + return -ENOIOCTLCMD; + + return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK); + + case VIDIOC_SUBSCRIBE_EVENT: + return v4l2_subdev_call(sd, core, subscribe_event, fh, arg); + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_subdev_call(sd, core, unsubscribe_event, fh, arg); + default: return -ENOIOCTLCMD; } @@ -78,11 +136,29 @@ static long subdev_ioctl(struct file *file, unsigned int cmd, return video_usercopy(file, cmd, arg, subdev_do_ioctl); } +static unsigned int subdev_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct v4l2_fh *fh = file->private_data; + + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) + return POLLERR; + + poll_wait(file, &fh->events->wait, wait); + + if (v4l2_event_pending(fh)) + return POLLPRI; + + return 0; +} + const struct v4l2_file_operations v4l2_subdev_fops = { .owner = THIS_MODULE, .open = subdev_open, .unlocked_ioctl = subdev_ioctl, .release = subdev_close, + .poll = subdev_poll, }; void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 3276065..0f9937b 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -37,6 +37,8 @@ struct v4l2_device; struct v4l2_ctrl_handler; +struct v4l2_event_subscription; +struct v4l2_fh; struct v4l2_subdev; struct tuner_setup; @@ -161,6 +163,10 @@ struct v4l2_subdev_core_ops { int (*s_power)(struct v4l2_subdev *sd, int on); int (*interrupt_service_routine)(struct v4l2_subdev *sd, u32 status, bool *handled); + int (*subscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); + int (*unsubscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); }; /* s_mode: switch the tuner to a specific tuner mode. Replacement of s_radio. @@ -437,6 +443,8 @@ struct v4l2_subdev_internal_ops { #define V4L2_SUBDEV_FL_IS_SPI (1U << 1) /* Set this flag if this subdev needs a device node. */ #define V4L2_SUBDEV_FL_HAS_DEVNODE (1U << 2) +/* Set this flag if this subdev generates events. */ +#define V4L2_SUBDEV_FL_HAS_EVENTS (1U << 3) /* Each instance of a subdev driver should create this struct, either stand-alone or embedded in a larger struct. @@ -460,6 +468,8 @@ struct v4l2_subdev { void *host_priv; /* subdev device node */ struct video_device devnode; + /* number of events to be allocated on open */ + unsigned int nevents; }; #define vdev_to_v4l2_subdev(vdev) \ -- cgit v0.10.2 From cf4b9211b5680cd9ca004232e517fb7ec5bf5316 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:39:56 -0300 Subject: [media] media: Media device node support The media_devnode structure provides support for registering and unregistering character devices using a dynamic major number. Reference counting is handled internally, making device drivers easier to write without having to solve the open/disconnect race condition issue over and over again. The code is based on video/v4l2-dev.c. [mchehab@redhat.com: Remove linux/smp_lock.h include to not break compilation on bisect] Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 81b3ba8..2466f2b 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -14,6 +14,19 @@ if MEDIA_SUPPORT comment "Multimedia core support" # +# Media controller +# + +config MEDIA_CONTROLLER + bool "Media Controller API (EXPERIMENTAL)" + depends on EXPERIMENTAL + ---help--- + Enable the media controller API used to query media devices internal + topology and configure it dynamically. + + This API is mostly used by camera interfaces in embedded platforms. + +# # V4L core and enabled API's # diff --git a/drivers/media/Makefile b/drivers/media/Makefile index b603ea6..d56349f 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -2,6 +2,12 @@ # Makefile for the kernel multimedia device drivers. # +media-objs := media-devnode.o + +ifeq ($(CONFIG_MEDIA_CONTROLLER),y) + obj-$(CONFIG_MEDIA_SUPPORT) += media.o +endif + obj-y += common/ rc/ video/ obj-$(CONFIG_VIDEO_DEV) += radio/ diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c new file mode 100644 index 0000000..af5263c --- /dev/null +++ b/drivers/media/media-devnode.c @@ -0,0 +1,320 @@ +/* + * Media device node + * + * Copyright (C) 2010 Nokia Corporation + * + * Based on drivers/media/video/v4l2_dev.c code authored by + * Mauro Carvalho Chehab (version 2) + * Alan Cox, (version 1) + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -- + * + * Generic media device node infrastructure to register and unregister + * character devices using a dynamic major number and proper reference + * counting. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MEDIA_NUM_DEVICES 256 +#define MEDIA_NAME "media" + +static dev_t media_dev_t; + +/* + * Active devices + */ +static DEFINE_MUTEX(media_devnode_lock); +static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); + +/* Called when the last user of the media device exits. */ +static void media_devnode_release(struct device *cd) +{ + struct media_devnode *mdev = to_media_devnode(cd); + + mutex_lock(&media_devnode_lock); + + /* Delete the cdev on this minor as well */ + cdev_del(&mdev->cdev); + + /* Mark device node number as free */ + clear_bit(mdev->minor, media_devnode_nums); + + mutex_unlock(&media_devnode_lock); + + /* Release media_devnode and perform other cleanups as needed. */ + if (mdev->release) + mdev->release(mdev); +} + +static struct bus_type media_bus_type = { + .name = MEDIA_NAME, +}; + +static ssize_t media_read(struct file *filp, char __user *buf, + size_t sz, loff_t *off) +{ + struct media_devnode *mdev = media_devnode_data(filp); + + if (!mdev->fops->read) + return -EINVAL; + if (!media_devnode_is_registered(mdev)) + return -EIO; + return mdev->fops->read(filp, buf, sz, off); +} + +static ssize_t media_write(struct file *filp, const char __user *buf, + size_t sz, loff_t *off) +{ + struct media_devnode *mdev = media_devnode_data(filp); + + if (!mdev->fops->write) + return -EINVAL; + if (!media_devnode_is_registered(mdev)) + return -EIO; + return mdev->fops->write(filp, buf, sz, off); +} + +static unsigned int media_poll(struct file *filp, + struct poll_table_struct *poll) +{ + struct media_devnode *mdev = media_devnode_data(filp); + + if (!media_devnode_is_registered(mdev)) + return POLLERR | POLLHUP; + if (!mdev->fops->poll) + return DEFAULT_POLLMASK; + return mdev->fops->poll(filp, poll); +} + +static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct media_devnode *mdev = media_devnode_data(filp); + + if (!mdev->fops->ioctl) + return -ENOTTY; + + if (!media_devnode_is_registered(mdev)) + return -EIO; + + return mdev->fops->ioctl(filp, cmd, arg); +} + +/* Override for the open function */ +static int media_open(struct inode *inode, struct file *filp) +{ + struct media_devnode *mdev; + int ret; + + /* Check if the media device is available. This needs to be done with + * the media_devnode_lock held to prevent an open/unregister race: + * without the lock, the device could be unregistered and freed between + * the media_devnode_is_registered() and get_device() calls, leading to + * a crash. + */ + mutex_lock(&media_devnode_lock); + mdev = container_of(inode->i_cdev, struct media_devnode, cdev); + /* return ENXIO if the media device has been removed + already or if it is not registered anymore. */ + if (!media_devnode_is_registered(mdev)) { + mutex_unlock(&media_devnode_lock); + return -ENXIO; + } + /* and increase the device refcount */ + get_device(&mdev->dev); + mutex_unlock(&media_devnode_lock); + + filp->private_data = mdev; + + if (mdev->fops->open) { + ret = mdev->fops->open(filp); + if (ret) { + put_device(&mdev->dev); + return ret; + } + } + + return 0; +} + +/* Override for the release function */ +static int media_release(struct inode *inode, struct file *filp) +{ + struct media_devnode *mdev = media_devnode_data(filp); + int ret = 0; + + if (mdev->fops->release) + mdev->fops->release(filp); + + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + put_device(&mdev->dev); + filp->private_data = NULL; + return ret; +} + +static const struct file_operations media_devnode_fops = { + .owner = THIS_MODULE, + .read = media_read, + .write = media_write, + .open = media_open, + .unlocked_ioctl = media_ioctl, + .release = media_release, + .poll = media_poll, + .llseek = no_llseek, +}; + +/** + * media_devnode_register - register a media device node + * @mdev: media device node structure we want to register + * + * The registration code assigns minor numbers and registers the new device node + * with the kernel. An error is returned if no free minor number can be found, + * or if the registration of the device node fails. + * + * Zero is returned on success. + * + * Note that if the media_devnode_register call fails, the release() callback of + * the media_devnode structure is *not* called, so the caller is responsible for + * freeing any data. + */ +int __must_check media_devnode_register(struct media_devnode *mdev) +{ + int minor; + int ret; + + /* Part 1: Find a free minor number */ + mutex_lock(&media_devnode_lock); + minor = find_next_zero_bit(media_devnode_nums, 0, MEDIA_NUM_DEVICES); + if (minor == MEDIA_NUM_DEVICES) { + mutex_unlock(&media_devnode_lock); + printk(KERN_ERR "could not get a free minor\n"); + return -ENFILE; + } + + set_bit(mdev->minor, media_devnode_nums); + mutex_unlock(&media_devnode_lock); + + mdev->minor = minor; + + /* Part 2: Initialize and register the character device */ + cdev_init(&mdev->cdev, &media_devnode_fops); + mdev->cdev.owner = mdev->fops->owner; + + ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1); + if (ret < 0) { + printk(KERN_ERR "%s: cdev_add failed\n", __func__); + goto error; + } + + /* Part 3: Register the media device */ + mdev->dev.bus = &media_bus_type; + mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor); + mdev->dev.release = media_devnode_release; + if (mdev->parent) + mdev->dev.parent = mdev->parent; + dev_set_name(&mdev->dev, "media%d", mdev->minor); + ret = device_register(&mdev->dev); + if (ret < 0) { + printk(KERN_ERR "%s: device_register failed\n", __func__); + goto error; + } + + /* Part 4: Activate this minor. The char device can now be used. */ + set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + + return 0; + +error: + cdev_del(&mdev->cdev); + clear_bit(mdev->minor, media_devnode_nums); + return ret; +} + +/** + * media_devnode_unregister - unregister a media device node + * @mdev: the device node to unregister + * + * This unregisters the passed device. Future open calls will be met with + * errors. + * + * This function can safely be called if the device node has never been + * registered or has already been unregistered. + */ +void media_devnode_unregister(struct media_devnode *mdev) +{ + /* Check if mdev was ever registered at all */ + if (!media_devnode_is_registered(mdev)) + return; + + mutex_lock(&media_devnode_lock); + clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + mutex_unlock(&media_devnode_lock); + device_unregister(&mdev->dev); +} + +/* + * Initialise media for linux + */ +static int __init media_devnode_init(void) +{ + int ret; + + printk(KERN_INFO "Linux media interface: v0.10\n"); + ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES, + MEDIA_NAME); + if (ret < 0) { + printk(KERN_WARNING "media: unable to allocate major\n"); + return ret; + } + + ret = bus_register(&media_bus_type); + if (ret < 0) { + unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); + printk(KERN_WARNING "media: bus_register failed\n"); + return -EIO; + } + + return 0; +} + +static void __exit media_devnode_exit(void) +{ + bus_unregister(&media_bus_type); + unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); +} + +module_init(media_devnode_init) +module_exit(media_devnode_exit) + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Device node registration for media drivers"); +MODULE_LICENSE("GPL"); diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h new file mode 100644 index 0000000..f6caafc --- /dev/null +++ b/include/media/media-devnode.h @@ -0,0 +1,97 @@ +/* + * Media device node + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -- + * + * Common functions for media-related drivers to register and unregister media + * device nodes. + */ + +#ifndef _MEDIA_DEVNODE_H +#define _MEDIA_DEVNODE_H + +#include +#include +#include +#include + +/* + * Flag to mark the media_devnode struct as registered. Drivers must not touch + * this flag directly, it will be set and cleared by media_devnode_register and + * media_devnode_unregister. + */ +#define MEDIA_FLAG_REGISTERED 0 + +struct media_file_operations { + struct module *owner; + ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); + ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); + unsigned int (*poll) (struct file *, struct poll_table_struct *); + long (*ioctl) (struct file *, unsigned int, unsigned long); + int (*open) (struct file *); + int (*release) (struct file *); +}; + +/** + * struct media_devnode - Media device node + * @parent: parent device + * @minor: device node minor number + * @flags: flags, combination of the MEDIA_FLAG_* constants + * + * This structure represents a media-related device node. + * + * The @parent is a physical device. It must be set by core or device drivers + * before registering the node. + */ +struct media_devnode { + /* device ops */ + const struct media_file_operations *fops; + + /* sysfs */ + struct device dev; /* media device */ + struct cdev cdev; /* character device */ + struct device *parent; /* device parent */ + + /* device info */ + int minor; + unsigned long flags; /* Use bitops to access flags */ + + /* callbacks */ + void (*release)(struct media_devnode *mdev); +}; + +/* dev to media_devnode */ +#define to_media_devnode(cd) container_of(cd, struct media_devnode, dev) + +int __must_check media_devnode_register(struct media_devnode *mdev); +void media_devnode_unregister(struct media_devnode *mdev); + +static inline struct media_devnode *media_devnode_data(struct file *filp) +{ + return filp->private_data; +} + +static inline int media_devnode_is_registered(struct media_devnode *mdev) +{ + return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); +} + +#endif /* _MEDIA_DEVNODE_H */ -- cgit v0.10.2 From 176fb0d108f7495ccf9aa127e1342a1a0d87e004 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:39:58 -0300 Subject: [media] media: Media device The media_device structure abstracts functions common to all kind of media devices (v4l2, dvb, alsa, ...). It manages media entities and offers a userspace API to discover and configure the media device internal topology. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/ABI/testing/sysfs-bus-media b/Documentation/ABI/testing/sysfs-bus-media new file mode 100644 index 0000000..7057e57 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-media @@ -0,0 +1,6 @@ +What: /sys/bus/media/devices/.../model +Date: January 2011 +Contact: Laurent Pinchart + linux-media@vger.kernel.org +Description: Contains the device model name in UTF-8. The device version is + is not be appended to the model name. diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index d2f99e5..c47897f 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -327,6 +327,8 @@ + + diff --git a/Documentation/DocBook/media.tmpl b/Documentation/DocBook/media.tmpl index a99088a..88f2cc6 100644 --- a/Documentation/DocBook/media.tmpl +++ b/Documentation/DocBook/media.tmpl @@ -106,6 +106,9 @@ Foundation. A copy of the license is included in the chapter entitled &sub-remote_controllers; + +&sub-media-controller; + &sub-fdl-appendix; diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml new file mode 100644 index 0000000..253ddb4 --- /dev/null +++ b/Documentation/DocBook/v4l/media-controller.xml @@ -0,0 +1,56 @@ + + + + Laurent + Pinchart +
laurent.pinchart@ideasonboard.com
+ Initial version. +
+
+ + 2010 + Laurent Pinchart + + + + + + 1.0.0 + 2010-11-10 + lp + Initial revision + + +
+ +Media Controller API + + + Media Controller + +
+ Introduction + Media devices increasingly handle multiple related functions. Many USB + cameras include microphones, video capture hardware can also output video, + or SoC camera interfaces also perform memory-to-memory operations similar to + video codecs. + Independent functions, even when implemented in the same hardware, can + be modelled as separate devices. A USB camera with a microphone will be + presented to userspace applications as V4L2 and ALSA capture devices. The + devices' relationships (when using a webcam, end-users shouldn't have to + manually select the associated USB microphone), while not made available + directly to applications by the drivers, can usually be retrieved from + sysfs. + With more and more advanced SoC devices being introduced, the current + approach will not scale. Device topologies are getting increasingly complex + and can't always be represented by a tree structure. Hardware blocks are + shared between different functions, creating dependencies between seemingly + unrelated devices. + Kernel abstraction APIs such as V4L2 and ALSA provide means for + applications to access hardware parameters. As newer hardware expose an + increasingly high number of those parameters, drivers need to guess what + applications really require based on limited information, thereby + implementing policies that belong to userspace. + The media controller API aims at solving those problems. +
+
diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt new file mode 100644 index 0000000..1844c3f1 --- /dev/null +++ b/Documentation/media-framework.txt @@ -0,0 +1,67 @@ +Linux kernel media framework +============================ + +This document describes the Linux kernel media framework, its data structures, +functions and their usage. + + +Introduction +------------ + +The media controller API is documented in DocBook format in +Documentation/DocBook/v4l/media-controller.xml. This document will focus on +the kernel-side implementation of the media framework. + + +Media device +------------ + +A media device is represented by a struct media_device instance, defined in +include/media/media-device.h. Allocation of the structure is handled by the +media device driver, usually by embedding the media_device instance in a +larger driver-specific structure. + +Drivers register media device instances by calling + + media_device_register(struct media_device *mdev); + +The caller is responsible for initializing the media_device structure before +registration. The following fields must be set: + + - dev must point to the parent device (usually a pci_dev, usb_interface or + platform_device instance). + + - model must be filled with the device model name as a NUL-terminated UTF-8 + string. The device/model revision must not be stored in this field. + +The following fields are optional: + + - serial is a unique serial number stored as a NUL-terminated ASCII string. + The field is big enough to store a GUID in text form. If the hardware + doesn't provide a unique serial number this field must be left empty. + + - bus_info represents the location of the device in the system as a + NUL-terminated ASCII string. For PCI/PCIe devices bus_info must be set to + "PCI:" (or "PCIe:") followed by the value of pci_name(). For USB devices, + the usb_make_path() function must be used. This field is used by + applications to distinguish between otherwise identical devices that don't + provide a serial number. + + - hw_revision is the hardware device revision in a driver-specific format. + When possible the revision should be formatted with the KERNEL_VERSION + macro. + + - driver_version is formatted with the KERNEL_VERSION macro. The version + minor must be incremented when new features are added to the userspace API + without breaking binary compatibility. The version major must be + incremented when binary compatibility is broken. + +Upon successful registration a character device named media[0-9]+ is created. +The device major and minor numbers are dynamic. The model name is exported as +a sysfs attribute. + +Drivers unregister media device instances by calling + + media_device_unregister(struct media_device *mdev); + +Unregistering a media device that hasn't been registered is *NOT* safe. diff --git a/drivers/media/Makefile b/drivers/media/Makefile index d56349f..36731aa 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -2,7 +2,7 @@ # Makefile for the kernel multimedia device drivers. # -media-objs := media-devnode.o +media-objs := media-device.o media-devnode.o ifeq ($(CONFIG_MEDIA_CONTROLLER),y) obj-$(CONFIG_MEDIA_SUPPORT) += media.o diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c new file mode 100644 index 0000000..bcd3985 --- /dev/null +++ b/drivers/media/media-device.c @@ -0,0 +1,100 @@ +/* + * Media device + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include + +static const struct media_file_operations media_device_fops = { + .owner = THIS_MODULE, +}; + +/* ----------------------------------------------------------------------------- + * sysfs + */ + +static ssize_t show_model(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct media_device *mdev = to_media_device(to_media_devnode(cd)); + + return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); +} + +static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); + +/* ----------------------------------------------------------------------------- + * Registration/unregistration + */ + +static void media_device_release(struct media_devnode *mdev) +{ +} + +/** + * media_device_register - register a media device + * @mdev: The media device + * + * The caller is responsible for initializing the media device before + * registration. The following fields must be set: + * + * - dev must point to the parent device + * - model must be filled with the device model name + */ +int __must_check media_device_register(struct media_device *mdev) +{ + int ret; + + if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) + return -EINVAL; + + /* Register the device node. */ + mdev->devnode.fops = &media_device_fops; + mdev->devnode.parent = mdev->dev; + mdev->devnode.release = media_device_release; + ret = media_devnode_register(&mdev->devnode); + if (ret < 0) + return ret; + + ret = device_create_file(&mdev->devnode.dev, &dev_attr_model); + if (ret < 0) { + media_devnode_unregister(&mdev->devnode); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(media_device_register); + +/** + * media_device_unregister - unregister a media device + * @mdev: The media device + * + */ +void media_device_unregister(struct media_device *mdev) +{ + device_remove_file(&mdev->devnode.dev, &dev_attr_model); + media_devnode_unregister(&mdev->devnode); +} +EXPORT_SYMBOL_GPL(media_device_unregister); diff --git a/include/media/media-device.h b/include/media/media-device.h new file mode 100644 index 0000000..30857f7 --- /dev/null +++ b/include/media/media-device.h @@ -0,0 +1,69 @@ +/* + * Media device + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MEDIA_DEVICE_H +#define _MEDIA_DEVICE_H + +#include +#include + +#include + +/** + * struct media_device - Media device + * @dev: Parent device + * @devnode: Media device node + * @model: Device model name + * @serial: Device serial number (optional) + * @bus_info: Unique and stable device location identifier + * @hw_revision: Hardware device revision + * @driver_version: Device driver version + * + * This structure represents an abstract high-level media device. It allows easy + * access to entities and provides basic media device-level support. The + * structure can be allocated directly or embedded in a larger structure. + * + * The parent @dev is a physical device. It must be set before registering the + * media device. + * + * @model is a descriptive model name exported through sysfs. It doesn't have to + * be unique. + */ +struct media_device { + /* dev->driver_data points to this struct. */ + struct device *dev; + struct media_devnode devnode; + + char model[32]; + char serial[40]; + char bus_info[32]; + u32 hw_revision; + u32 driver_version; +}; + +/* media_devnode to media_device */ +#define to_media_device(node) container_of(node, struct media_device, devnode) + +int __must_check media_device_register(struct media_device *mdev); +void media_device_unregister(struct media_device *mdev); + +#endif -- cgit v0.10.2 From 53e269c102fbaf77e7dc526b1606ad4a48e57200 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:40:00 -0300 Subject: [media] media: Entities, pads and links As video hardware pipelines become increasingly complex and configurable, the current hardware description through v4l2 subdevices reaches its limits. In addition to enumerating and configuring subdevices, video camera drivers need a way to discover and modify at runtime how those subdevices are connected. This is done through new elements called entities, pads and links. An entity is a basic media hardware building block. It can correspond to a large variety of logical blocks such as physical hardware devices (CMOS sensor for instance), logical hardware devices (a building block in a System-on-Chip image processing pipeline), DMA channels or physical connectors. A pad is a connection endpoint through which an entity can interact with other entities. Data (not restricted to video) produced by an entity flows from the entity's output to one or more entity inputs. Pads should not be confused with physical pins at chip boundaries. A link is a point-to-point oriented connection between two pads, either on the same entity or on different entities. Data flows from a source pad to a sink pad. Links are stored in the source entity. To make backwards graph walk faster, a copy of all links is also stored in the sink entity. The copy is known as a backlink and is only used to help graph traversal. The entity API is made of three functions: - media_entity_init() initializes an entity. The caller must provide an array of pads as well as an estimated number of links. The links array is allocated dynamically and will be reallocated if it grows beyond the initial estimate. - media_entity_cleanup() frees resources allocated for an entity. It must be called during the cleanup phase after unregistering the entity and before freeing it. - media_entity_create_link() creates a link between two entities. An entry in the link array of each entity is allocated and stores pointers to source and sink pads. When a media device is unregistered, all its entities are unregistered automatically. The code is based on Hans Verkuil initial work. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml index 253ddb4..f89228d 100644 --- a/Documentation/DocBook/v4l/media-controller.xml +++ b/Documentation/DocBook/v4l/media-controller.xml @@ -53,4 +53,24 @@ implementing policies that belong to userspace. The media controller API aims at solving those problems.
+ +
+ Media device model + Discovering a device internal topology, and configuring it at runtime, + is one of the goals of the media controller API. To achieve this, hardware + devices are modelled as an oriented graph of building blocks called entities + connected through pads. + An entity is a basic media hardware or software building block. It can + correspond to a large variety of logical blocks such as physical hardware + devices (CMOS sensor for instance), logical hardware devices (a building + block in a System-on-Chip image processing pipeline), DMA channels or + physical connectors. + A pad is a connection endpoint through which an entity can interact + with other entities. Data (not restricted to video) produced by an entity + flows from the entity's output to one or more entity inputs. Pads should not + be confused with physical pins at chip boundaries. + A link is a point-to-point oriented connection between two pads, + either on the same entity or on different entities. Data flows from a source + pad to a sink pad. +
diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 1844c3f1..0257bad 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -13,6 +13,30 @@ Documentation/DocBook/v4l/media-controller.xml. This document will focus on the kernel-side implementation of the media framework. +Abstract media device model +--------------------------- + +Discovering a device internal topology, and configuring it at runtime, is one +of the goals of the media framework. To achieve this, hardware devices are +modeled as an oriented graph of building blocks called entities connected +through pads. + +An entity is a basic media hardware building block. It can correspond to +a large variety of logical blocks such as physical hardware devices +(CMOS sensor for instance), logical hardware devices (a building block +in a System-on-Chip image processing pipeline), DMA channels or physical +connectors. + +A pad is a connection endpoint through which an entity can interact with +other entities. Data (not restricted to video) produced by an entity +flows from the entity's output to one or more entity inputs. Pads should +not be confused with physical pins at chip boundaries. + +A link is a point-to-point oriented connection between two pads, either +on the same entity or on different entities. Data flows from a source +pad to a sink pad. + + Media device ------------ @@ -65,3 +89,130 @@ Drivers unregister media device instances by calling media_device_unregister(struct media_device *mdev); Unregistering a media device that hasn't been registered is *NOT* safe. + + +Entities, pads and links +------------------------ + +- Entities + +Entities are represented by a struct media_entity instance, defined in +include/media/media-entity.h. The structure is usually embedded into a +higher-level structure, such as a v4l2_subdev or video_device instance, +although drivers can allocate entities directly. + +Drivers initialize entities by calling + + media_entity_init(struct media_entity *entity, u16 num_pads, + struct media_pad *pads, u16 extra_links); + +The media_entity name, type, flags, revision and group_id fields can be +initialized before or after calling media_entity_init. Entities embedded in +higher-level standard structures can have some of those fields set by the +higher-level framework. + +As the number of pads is known in advance, the pads array is not allocated +dynamically but is managed by the entity driver. Most drivers will embed the +pads array in a driver-specific structure, avoiding dynamic allocation. + +Drivers must set the direction of every pad in the pads array before calling +media_entity_init. The function will initialize the other pads fields. + +Unlike the number of pads, the total number of links isn't always known in +advance by the entity driver. As an initial estimate, media_entity_init +pre-allocates a number of links equal to the number of pads plus an optional +number of extra links. The links array will be reallocated if it grows beyond +the initial estimate. + +Drivers register entities with a media device by calling + + media_device_register_entity(struct media_device *mdev, + struct media_entity *entity); + +Entities are identified by a unique positive integer ID. Drivers can provide an +ID by filling the media_entity id field prior to registration, or request the +media controller framework to assign an ID automatically. Drivers that provide +IDs manually must ensure that all IDs are unique. IDs are not guaranteed to be +contiguous even when they are all assigned automatically by the framework. + +Drivers unregister entities by calling + + media_device_unregister_entity(struct media_entity *entity); + +Unregistering an entity will not change the IDs of the other entities, and the +ID will never be reused for a newly registered entity. + +When a media device is unregistered, all its entities are unregistered +automatically. No manual entities unregistration is then required. + +Drivers free resources associated with an entity by calling + + media_entity_cleanup(struct media_entity *entity); + +This function must be called during the cleanup phase after unregistering the +entity. Note that the media_entity instance itself must be freed explicitly by +the driver if required. + +Entities have flags that describe the entity capabilities and state. + + MEDIA_ENT_FL_DEFAULT indicates the default entity for a given type. + This can be used to report the default audio and video devices or the + default camera sensor. + +Logical entity groups can be defined by setting the group ID of all member +entities to the same non-zero value. An entity group serves no purpose in the +kernel, but is reported to userspace during entities enumeration. The group_id +field belongs to the media device driver and must not by touched by entity +drivers. + +Media device drivers should define groups if several entities are logically +bound together. Example usages include reporting + + - ALSA, VBI and video nodes that carry the same media stream + - lens and flash controllers associated with a sensor + +- Pads + +Pads are represented by a struct media_pad instance, defined in +include/media/media-entity.h. Each entity stores its pads in a pads array +managed by the entity driver. Drivers usually embed the array in a +driver-specific structure. + +Pads are identified by their entity and their 0-based index in the pads array. +Both information are stored in the media_pad structure, making the media_pad +pointer the canonical way to store and pass link references. + +Pads have flags that describe the pad capabilities and state. + + MEDIA_PAD_FL_SINK indicates that the pad supports sinking data. + MEDIA_PAD_FL_SOURCE indicates that the pad supports sourcing data. + +One and only one of MEDIA_PAD_FL_SINK and MEDIA_PAD_FL_SOURCE must be set for +each pad. + +- Links + +Links are represented by a struct media_link instance, defined in +include/media/media-entity.h. Each entity stores all links originating at or +targetting any of its pads in a links array. A given link is thus stored +twice, once in the source entity and once in the target entity. The array is +pre-allocated and grows dynamically as needed. + +Drivers create links by calling + + media_entity_create_link(struct media_entity *source, u16 source_pad, + struct media_entity *sink, u16 sink_pad, + u32 flags); + +An entry in the link array of each entity is allocated and stores pointers +to source and sink pads. + +Links have flags that describe the link capabilities and state. + + MEDIA_LNK_FL_ENABLED indicates that the link is enabled and can be used + to transfer media data. When two or more links target a sink pad, only + one of them can be enabled at a time. + MEDIA_LNK_FL_IMMUTABLE indicates that the link enabled state can't be + modified at runtime. If MEDIA_LNK_FL_IMMUTABLE is set, then + MEDIA_LNK_FL_ENABLED must also be set since an immutable link is always + enabled. diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 36731aa..64755c9 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -2,7 +2,7 @@ # Makefile for the kernel multimedia device drivers. # -media-objs := media-device.o media-devnode.o +media-objs := media-device.o media-devnode.o media-entity.o ifeq ($(CONFIG_MEDIA_CONTROLLER),y) obj-$(CONFIG_MEDIA_SUPPORT) += media.o diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index bcd3985..a36509a 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -25,6 +25,7 @@ #include #include +#include static const struct media_file_operations media_device_fops = { .owner = THIS_MODULE, @@ -69,6 +70,10 @@ int __must_check media_device_register(struct media_device *mdev) if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) return -EINVAL; + mdev->entity_id = 1; + INIT_LIST_HEAD(&mdev->entities); + spin_lock_init(&mdev->lock); + /* Register the device node. */ mdev->devnode.fops = &media_device_fops; mdev->devnode.parent = mdev->dev; @@ -94,7 +99,58 @@ EXPORT_SYMBOL_GPL(media_device_register); */ void media_device_unregister(struct media_device *mdev) { + struct media_entity *entity; + struct media_entity *next; + + list_for_each_entry_safe(entity, next, &mdev->entities, list) + media_device_unregister_entity(entity); + device_remove_file(&mdev->devnode.dev, &dev_attr_model); media_devnode_unregister(&mdev->devnode); } EXPORT_SYMBOL_GPL(media_device_unregister); + +/** + * media_device_register_entity - Register an entity with a media device + * @mdev: The media device + * @entity: The entity + */ +int __must_check media_device_register_entity(struct media_device *mdev, + struct media_entity *entity) +{ + /* Warn if we apparently re-register an entity */ + WARN_ON(entity->parent != NULL); + entity->parent = mdev; + + spin_lock(&mdev->lock); + if (entity->id == 0) + entity->id = mdev->entity_id++; + else + mdev->entity_id = max(entity->id + 1, mdev->entity_id); + list_add_tail(&entity->list, &mdev->entities); + spin_unlock(&mdev->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(media_device_register_entity); + +/** + * media_device_unregister_entity - Unregister an entity + * @entity: The entity + * + * If the entity has never been registered this function will return + * immediately. + */ +void media_device_unregister_entity(struct media_entity *entity) +{ + struct media_device *mdev = entity->parent; + + if (mdev == NULL) + return; + + spin_lock(&mdev->lock); + list_del(&entity->list); + spin_unlock(&mdev->lock); + entity->parent = NULL; +} +EXPORT_SYMBOL_GPL(media_device_unregister_entity); diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c new file mode 100644 index 0000000..5c31df9 --- /dev/null +++ b/drivers/media/media-entity.c @@ -0,0 +1,147 @@ +/* + * Media entity + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +/** + * media_entity_init - Initialize a media entity + * + * @num_pads: Total number of sink and source pads. + * @extra_links: Initial estimate of the number of extra links. + * @pads: Array of 'num_pads' pads. + * + * The total number of pads is an intrinsic property of entities known by the + * entity driver, while the total number of links depends on hardware design + * and is an extrinsic property unknown to the entity driver. However, in most + * use cases the entity driver can guess the number of links which can safely + * be assumed to be equal to or larger than the number of pads. + * + * For those reasons the links array can be preallocated based on the entity + * driver guess and will be reallocated later if extra links need to be + * created. + * + * This function allocates a links array with enough space to hold at least + * 'num_pads' + 'extra_links' elements. The media_entity::max_links field will + * be set to the number of allocated elements. + * + * The pads array is managed by the entity driver and passed to + * media_entity_init() where its pointer will be stored in the entity structure. + */ +int +media_entity_init(struct media_entity *entity, u16 num_pads, + struct media_pad *pads, u16 extra_links) +{ + struct media_link *links; + unsigned int max_links = num_pads + extra_links; + unsigned int i; + + links = kzalloc(max_links * sizeof(links[0]), GFP_KERNEL); + if (links == NULL) + return -ENOMEM; + + entity->group_id = 0; + entity->max_links = max_links; + entity->num_links = 0; + entity->num_backlinks = 0; + entity->num_pads = num_pads; + entity->pads = pads; + entity->links = links; + + for (i = 0; i < num_pads; i++) { + pads[i].entity = entity; + pads[i].index = i; + } + + return 0; +} +EXPORT_SYMBOL_GPL(media_entity_init); + +void +media_entity_cleanup(struct media_entity *entity) +{ + kfree(entity->links); +} +EXPORT_SYMBOL_GPL(media_entity_cleanup); + +static struct media_link *media_entity_add_link(struct media_entity *entity) +{ + if (entity->num_links >= entity->max_links) { + struct media_link *links = entity->links; + unsigned int max_links = entity->max_links + 2; + unsigned int i; + + links = krealloc(links, max_links * sizeof(*links), GFP_KERNEL); + if (links == NULL) + return NULL; + + for (i = 0; i < entity->num_links; i++) + links[i].reverse->reverse = &links[i]; + + entity->max_links = max_links; + entity->links = links; + } + + return &entity->links[entity->num_links++]; +} + +int +media_entity_create_link(struct media_entity *source, u16 source_pad, + struct media_entity *sink, u16 sink_pad, u32 flags) +{ + struct media_link *link; + struct media_link *backlink; + + BUG_ON(source == NULL || sink == NULL); + BUG_ON(source_pad >= source->num_pads); + BUG_ON(sink_pad >= sink->num_pads); + + link = media_entity_add_link(source); + if (link == NULL) + return -ENOMEM; + + link->source = &source->pads[source_pad]; + link->sink = &sink->pads[sink_pad]; + link->flags = flags; + + /* Create the backlink. Backlinks are used to help graph traversal and + * are not reported to userspace. + */ + backlink = media_entity_add_link(sink); + if (backlink == NULL) { + source->num_links--; + return -ENOMEM; + } + + backlink->source = &source->pads[source_pad]; + backlink->sink = &sink->pads[sink_pad]; + backlink->flags = flags; + + link->reverse = backlink; + backlink->reverse = link; + + sink->num_backlinks++; + + return 0; +} +EXPORT_SYMBOL_GPL(media_entity_create_link); diff --git a/include/media/media-device.h b/include/media/media-device.h index 30857f7..a8390fe 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -25,8 +25,10 @@ #include #include +#include #include +#include /** * struct media_device - Media device @@ -37,6 +39,9 @@ * @bus_info: Unique and stable device location identifier * @hw_revision: Hardware device revision * @driver_version: Device driver version + * @entity_id: ID of the next entity to be registered + * @entities: List of registered entities + * @lock: Entities list lock * * This structure represents an abstract high-level media device. It allows easy * access to entities and provides basic media device-level support. The @@ -58,6 +63,12 @@ struct media_device { char bus_info[32]; u32 hw_revision; u32 driver_version; + + u32 entity_id; + struct list_head entities; + + /* Protects the entities list */ + spinlock_t lock; }; /* media_devnode to media_device */ @@ -66,4 +77,12 @@ struct media_device { int __must_check media_device_register(struct media_device *mdev); void media_device_unregister(struct media_device *mdev); +int __must_check media_device_register_entity(struct media_device *mdev, + struct media_entity *entity); +void media_device_unregister_entity(struct media_entity *entity); + +/* Iterate over all entities. */ +#define media_device_for_each_entity(entity, mdev) \ + list_for_each_entry(entity, &(mdev)->entities, list) + #endif diff --git a/include/media/media-entity.h b/include/media/media-entity.h new file mode 100644 index 0000000..f6c856c --- /dev/null +++ b/include/media/media-entity.h @@ -0,0 +1,122 @@ +/* + * Media entity + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MEDIA_ENTITY_H +#define _MEDIA_ENTITY_H + +#include + +#define MEDIA_ENT_TYPE_SHIFT 16 +#define MEDIA_ENT_TYPE_MASK 0x00ff0000 +#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff + +#define MEDIA_ENT_T_DEVNODE (1 << MEDIA_ENT_TYPE_SHIFT) +#define MEDIA_ENT_T_DEVNODE_V4L (MEDIA_ENT_T_DEVNODE + 1) +#define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENT_T_DEVNODE + 2) +#define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENT_T_DEVNODE + 3) +#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENT_T_DEVNODE + 4) + +#define MEDIA_ENT_T_V4L2_SUBDEV (2 << MEDIA_ENT_TYPE_SHIFT) +#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENT_T_V4L2_SUBDEV + 1) +#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH (MEDIA_ENT_T_V4L2_SUBDEV + 2) +#define MEDIA_ENT_T_V4L2_SUBDEV_LENS (MEDIA_ENT_T_V4L2_SUBDEV + 3) + +#define MEDIA_ENT_FL_DEFAULT (1 << 0) + +#define MEDIA_LNK_FL_ENABLED (1 << 0) +#define MEDIA_LNK_FL_IMMUTABLE (1 << 1) + +#define MEDIA_PAD_FL_SINK (1 << 0) +#define MEDIA_PAD_FL_SOURCE (1 << 1) + +struct media_link { + struct media_pad *source; /* Source pad */ + struct media_pad *sink; /* Sink pad */ + struct media_link *reverse; /* Link in the reverse direction */ + unsigned long flags; /* Link flags (MEDIA_LNK_FL_*) */ +}; + +struct media_pad { + struct media_entity *entity; /* Entity this pad belongs to */ + u16 index; /* Pad index in the entity pads array */ + unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */ +}; + +struct media_entity { + struct list_head list; + struct media_device *parent; /* Media device this entity belongs to*/ + u32 id; /* Entity ID, unique in the parent media + * device context */ + const char *name; /* Entity name */ + u32 type; /* Entity type (MEDIA_ENT_T_*) */ + u32 revision; /* Entity revision, driver specific */ + unsigned long flags; /* Entity flags (MEDIA_ENT_FL_*) */ + u32 group_id; /* Entity group ID */ + + u16 num_pads; /* Number of sink and source pads */ + u16 num_links; /* Number of existing links, both + * enabled and disabled */ + u16 num_backlinks; /* Number of backlinks */ + u16 max_links; /* Maximum number of links */ + + struct media_pad *pads; /* Pads array (num_pads elements) */ + struct media_link *links; /* Links array (max_links elements)*/ + + union { + /* Node specifications */ + struct { + u32 major; + u32 minor; + } v4l; + struct { + u32 major; + u32 minor; + } fb; + struct { + u32 card; + u32 device; + u32 subdevice; + } alsa; + int dvb; + + /* Sub-device specifications */ + /* Nothing needed yet */ + }; +}; + +static inline u32 media_entity_type(struct media_entity *entity) +{ + return entity->type & MEDIA_ENT_TYPE_MASK; +} + +static inline u32 media_entity_subtype(struct media_entity *entity) +{ + return entity->type & MEDIA_ENT_SUBTYPE_MASK; +} + +int media_entity_init(struct media_entity *entity, u16 num_pads, + struct media_pad *pads, u16 extra_links); +void media_entity_cleanup(struct media_entity *entity); +int media_entity_create_link(struct media_entity *source, u16 source_pad, + struct media_entity *sink, u16 sink_pad, u32 flags); + +#endif -- cgit v0.10.2 From a5ccc48a7c48610e7f92fa599406738d69195d51 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 7 Mar 2010 16:14:14 -0300 Subject: [media] media: Entity graph traversal Add media entity graph traversal. The traversal follows enabled links by depth first. Traversing graph backwards is prevented by comparing the next possible entity in the graph with the previous one. Multiply connected graphs are thus not supported. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Vimarsh Zutshi Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 0257bad..ab17f33 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -216,3 +216,45 @@ Links have flags that describe the link capabilities and state. modified at runtime. If MEDIA_LNK_FL_IMMUTABLE is set, then MEDIA_LNK_FL_ENABLED must also be set since an immutable link is always enabled. + + +Graph traversal +--------------- + +The media framework provides APIs to iterate over entities in a graph. + +To iterate over all entities belonging to a media device, drivers can use the +media_device_for_each_entity macro, defined in include/media/media-device.h. + + struct media_entity *entity; + + media_device_for_each_entity(entity, mdev) { + /* entity will point to each entity in turn */ + ... + } + +Drivers might also need to iterate over all entities in a graph that can be +reached only through enabled links starting at a given entity. The media +framework provides a depth-first graph traversal API for that purpose. + +Note that graphs with cycles (whether directed or undirected) are *NOT* +supported by the graph traversal API. To prevent infinite loops, the graph +traversal code limits the maximum depth to MEDIA_ENTITY_ENUM_MAX_DEPTH, +currently defined as 16. + +Drivers initiate a graph traversal by calling + + media_entity_graph_walk_start(struct media_entity_graph *graph, + struct media_entity *entity); + +The graph structure, provided by the caller, is initialized to start graph +traversal at the given entity. + +Drivers can then retrieve the next entity by calling + + media_entity_graph_walk_next(struct media_entity_graph *graph); + +When the graph traversal is complete the function will return NULL. + +Graph traversal can be interrupted at any moment. No cleanup function call is +required and the graph structure can be freed normally. diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 5c31df9..166f2b5 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -84,6 +84,121 @@ media_entity_cleanup(struct media_entity *entity) } EXPORT_SYMBOL_GPL(media_entity_cleanup); +/* ----------------------------------------------------------------------------- + * Graph traversal + */ + +static struct media_entity * +media_entity_other(struct media_entity *entity, struct media_link *link) +{ + if (link->source->entity == entity) + return link->sink->entity; + else + return link->source->entity; +} + +/* push an entity to traversal stack */ +static void stack_push(struct media_entity_graph *graph, + struct media_entity *entity) +{ + if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) { + WARN_ON(1); + return; + } + graph->top++; + graph->stack[graph->top].link = 0; + graph->stack[graph->top].entity = entity; +} + +static struct media_entity *stack_pop(struct media_entity_graph *graph) +{ + struct media_entity *entity; + + entity = graph->stack[graph->top].entity; + graph->top--; + + return entity; +} + +#define stack_peek(en) ((en)->stack[(en)->top - 1].entity) +#define link_top(en) ((en)->stack[(en)->top].link) +#define stack_top(en) ((en)->stack[(en)->top].entity) + +/** + * media_entity_graph_walk_start - Start walking the media graph at a given entity + * @graph: Media graph structure that will be used to walk the graph + * @entity: Starting entity + * + * This function initializes the graph traversal structure to walk the entities + * graph starting at the given entity. The traversal structure must not be + * modified by the caller during graph traversal. When done the structure can + * safely be freed. + */ +void media_entity_graph_walk_start(struct media_entity_graph *graph, + struct media_entity *entity) +{ + graph->top = 0; + graph->stack[graph->top].entity = NULL; + stack_push(graph, entity); +} +EXPORT_SYMBOL_GPL(media_entity_graph_walk_start); + +/** + * media_entity_graph_walk_next - Get the next entity in the graph + * @graph: Media graph structure + * + * Perform a depth-first traversal of the given media entities graph. + * + * The graph structure must have been previously initialized with a call to + * media_entity_graph_walk_start(). + * + * Return the next entity in the graph or NULL if the whole graph have been + * traversed. + */ +struct media_entity * +media_entity_graph_walk_next(struct media_entity_graph *graph) +{ + if (stack_top(graph) == NULL) + return NULL; + + /* + * Depth first search. Push entity to stack and continue from + * top of the stack until no more entities on the level can be + * found. + */ + while (link_top(graph) < stack_top(graph)->num_links) { + struct media_entity *entity = stack_top(graph); + struct media_link *link = &entity->links[link_top(graph)]; + struct media_entity *next; + + /* The link is not enabled so we do not follow. */ + if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { + link_top(graph)++; + continue; + } + + /* Get the entity in the other end of the link . */ + next = media_entity_other(entity, link); + + /* Was it the entity we came here from? */ + if (next == stack_peek(graph)) { + link_top(graph)++; + continue; + } + + /* Push the new entity to stack and start over. */ + link_top(graph)++; + stack_push(graph, next); + } + + return stack_pop(graph); +} +EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); + +/* ----------------------------------------------------------------------------- + * Links management + */ + static struct media_link *media_entity_add_link(struct media_entity *entity) { if (entity->num_links >= entity->max_links) { diff --git a/include/media/media-entity.h b/include/media/media-entity.h index f6c856c..28f61f6 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -113,10 +113,25 @@ static inline u32 media_entity_subtype(struct media_entity *entity) return entity->type & MEDIA_ENT_SUBTYPE_MASK; } +#define MEDIA_ENTITY_ENUM_MAX_DEPTH 16 + +struct media_entity_graph { + struct { + struct media_entity *entity; + int link; + } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH]; + int top; +}; + int media_entity_init(struct media_entity *entity, u16 num_pads, struct media_pad *pads, u16 extra_links); void media_entity_cleanup(struct media_entity *entity); int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); +void media_entity_graph_walk_start(struct media_entity_graph *graph, + struct media_entity *entity); +struct media_entity * +media_entity_graph_walk_next(struct media_entity_graph *graph); + #endif -- cgit v0.10.2 From 503c3d829eaf48837dd5bff5d97ad66369bb955a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 7 Mar 2010 15:04:59 -0300 Subject: [media] media: Entity use count Due to the wide differences between drivers regarding power management needs, the media controller does not implement power management. However, the media_entity structure includes a use_count field that media drivers can use to track the number of users of every entity for power management needs. The use_count field is owned by media drivers and must not be touched by entity drivers. Access to the field must be protected by the media device graph_mutex lock. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index ab17f33..78ae020 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -258,3 +258,16 @@ When the graph traversal is complete the function will return NULL. Graph traversal can be interrupted at any moment. No cleanup function call is required and the graph structure can be freed normally. + + +Use count and power handling +---------------------------- + +Due to the wide differences between drivers regarding power management needs, +the media controller does not implement power management. However, the +media_entity structure includes a use_count field that media drivers can use to +track the number of users of every entity for power management needs. + +The use_count field is owned by media drivers and must not be touched by entity +drivers. Access to the field must be protected by the media device graph_mutex +lock. diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index a36509a..d2bc809 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -73,6 +73,7 @@ int __must_check media_device_register(struct media_device *mdev) mdev->entity_id = 1; INIT_LIST_HEAD(&mdev->entities); spin_lock_init(&mdev->lock); + mutex_init(&mdev->graph_mutex); /* Register the device node. */ mdev->devnode.fops = &media_device_fops; diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 166f2b5..3e7e2d5 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -23,6 +23,7 @@ #include #include #include +#include /** * media_entity_init - Initialize a media entity @@ -196,6 +197,51 @@ media_entity_graph_walk_next(struct media_entity_graph *graph) EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); /* ----------------------------------------------------------------------------- + * Module use count + */ + +/* + * media_entity_get - Get a reference to the parent module + * @entity: The entity + * + * Get a reference to the parent media device module. + * + * The function will return immediately if @entity is NULL. + * + * Return a pointer to the entity on success or NULL on failure. + */ +struct media_entity *media_entity_get(struct media_entity *entity) +{ + if (entity == NULL) + return NULL; + + if (entity->parent->dev && + !try_module_get(entity->parent->dev->driver->owner)) + return NULL; + + return entity; +} +EXPORT_SYMBOL_GPL(media_entity_get); + +/* + * media_entity_put - Release the reference to the parent module + * @entity: The entity + * + * Release the reference count acquired by media_entity_get(). + * + * The function will return immediately if @entity is NULL. + */ +void media_entity_put(struct media_entity *entity) +{ + if (entity == NULL) + return; + + if (entity->parent->dev) + module_put(entity->parent->dev->driver->owner); +} +EXPORT_SYMBOL_GPL(media_entity_put); + +/* ----------------------------------------------------------------------------- * Links management */ diff --git a/include/media/media-device.h b/include/media/media-device.h index a8390fe..5d2bff4 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -42,6 +43,7 @@ * @entity_id: ID of the next entity to be registered * @entities: List of registered entities * @lock: Entities list lock + * @graph_mutex: Entities graph operation lock * * This structure represents an abstract high-level media device. It allows easy * access to entities and provides basic media device-level support. The @@ -69,6 +71,8 @@ struct media_device { /* Protects the entities list */ spinlock_t lock; + /* Serializes graph operations. */ + struct mutex graph_mutex; }; /* media_devnode to media_device */ diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 28f61f6..a9b31d9 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -81,6 +81,12 @@ struct media_entity { struct media_pad *pads; /* Pads array (num_pads elements) */ struct media_link *links; /* Links array (max_links elements)*/ + /* Reference counts must never be negative, but are signed integers on + * purpose: a simple WARN_ON(<0) check can be used to detect reference + * count bugs that would make them negative. + */ + int use_count; /* Use count for the entity. */ + union { /* Node specifications */ struct { @@ -129,6 +135,9 @@ void media_entity_cleanup(struct media_entity *entity); int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); +struct media_entity *media_entity_get(struct media_entity *entity); +void media_entity_put(struct media_entity *entity); + void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity *entity); struct media_entity * -- cgit v0.10.2 From 140d88165c25137e871f9559e67986ed89251105 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 18 Aug 2010 11:41:22 -0300 Subject: [media] media: Media device information query Create the following ioctl and implement it at the media device level to query device information. - MEDIA_IOC_DEVICE_INFO: Query media device information The ioctl and its data structure are defined in the new kernel header linux/media.h available to userspace applications. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index c47897f..034f891 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -11,6 +11,10 @@ select()"> write()"> +close()"> +ioctl()"> +open()"> + VIDIOC_CROPCAP"> VIDIOC_DBG_G_CHIP_IDENT"> @@ -87,6 +91,8 @@ VIDIOC_TRY_FMT"> VIDIOC_UNSUBSCRIBE_EVENT"> +MEDIA_IOC_DEVICE_INFO"> + v4l2_std_id"> @@ -184,6 +190,8 @@ v4l2_vbi_format"> v4l2_window"> +media_device_info"> + EACCES error code"> EAGAIN error code"> @@ -328,6 +336,10 @@ + + + + diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml index f89228d..a46b786 100644 --- a/Documentation/DocBook/v4l/media-controller.xml +++ b/Documentation/DocBook/v4l/media-controller.xml @@ -74,3 +74,13 @@ pad to a sink pad.
+ + + Function Reference + + &sub-media-open; + &sub-media-close; + &sub-media-ioctl; + + &sub-media-ioc-device-info; + diff --git a/Documentation/DocBook/v4l/media-func-close.xml b/Documentation/DocBook/v4l/media-func-close.xml new file mode 100644 index 0000000..be149c8 --- /dev/null +++ b/Documentation/DocBook/v4l/media-func-close.xml @@ -0,0 +1,59 @@ + + + media close() + &manvol; + + + + media-close + Close a media device + + + + + #include <unistd.h> + + int close + int fd + + + + + + Arguments + + + + fd + + &fd; + + + + + + + Description + + Closes the media device. Resources associated with the file descriptor + are freed. The device configuration remain unchanged. + + + + Return Value + + close returns 0 on success. On error, -1 is + returned, and errno is set appropriately. Possible error + codes are: + + + + EBADF + + fd is not a valid open file descriptor. + + + + + + diff --git a/Documentation/DocBook/v4l/media-func-ioctl.xml b/Documentation/DocBook/v4l/media-func-ioctl.xml new file mode 100644 index 0000000..bda8604 --- /dev/null +++ b/Documentation/DocBook/v4l/media-func-ioctl.xml @@ -0,0 +1,116 @@ + + + media ioctl() + &manvol; + + + + media-ioctl + Control a media device + + + + + #include <sys/ioctl.h> + + int ioctl + int fd + int request + void *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + Media ioctl request code as defined in the media.h header file, + for example MEDIA_IOC_SETUP_LINK. + + + + argp + + Pointer to a request-specific structure. + + + + + + + Description + The ioctl() function manipulates media device + parameters. The argument fd must be an open file + descriptor. + The ioctl request code specifies the media + function to be called. It has encoded in it whether the argument is an + input, output or read/write parameter, and the size of the argument + argp in bytes. + Macros and structures definitions specifying media ioctl requests and + their parameters are located in the media.h header file. All media ioctl + requests, their respective function and parameters are specified in + . + + + + Return Value + + ioctl() returns 0 on + success. On failure, -1 is returned, and the + errno variable is set appropriately. Generic error codes + are listed below, and request-specific error codes are listed in the + individual requests descriptions. + When an ioctl that takes an output or read/write parameter fails, + the parameter remains unmodified. + + + + EBADF + + fd is not a valid open file descriptor. + + + + + EFAULT + + argp references an inaccessible memory + area. + + + + EINVAL + + The request or the data pointed to by + argp is not valid. This is a very common error + code, see the individual ioctl requests listed in + for actual causes. + + + + ENOMEM + + Insufficient kernel memory was available to complete the + request. + + + + ENOTTY + + fd is not associated with a character + special device. + + + + + diff --git a/Documentation/DocBook/v4l/media-func-open.xml b/Documentation/DocBook/v4l/media-func-open.xml new file mode 100644 index 0000000..f7df034 --- /dev/null +++ b/Documentation/DocBook/v4l/media-func-open.xml @@ -0,0 +1,94 @@ + + + media open() + &manvol; + + + + media-open + Open a media device + + + + + #include <fcntl.h> + + int open + const char *device_name + int flags + + + + + + Arguments + + + + device_name + + Device to be opened. + + + + flags + + Open flags. Access mode must be either O_RDONLY + or O_RDWR. Other flags have no effect. + + + + + + Description + To open a media device applications call open() + with the desired device name. The function has no side effects; the device + configuration remain unchanged. + When the device is opened in read-only mode, attemps to modify its + configuration will result in an error, and errno will be + set to EBADF. + + + Return Value + + open returns the new file descriptor on success. + On error, -1 is returned, and errno is set appropriately. + Possible error codes are: + + + + EACCES + + The requested access to the file is not allowed. + + + + EMFILE + + The process already has the maximum number of files open. + + + + + ENFILE + + The system limit on the total number of open files has been + reached. + + + + ENOMEM + + Insufficient kernel memory was available. + + + + ENXIO + + No device corresponding to this device special file exists. + + + + + + diff --git a/Documentation/DocBook/v4l/media-ioc-device-info.xml b/Documentation/DocBook/v4l/media-ioc-device-info.xml new file mode 100644 index 0000000..278a312 --- /dev/null +++ b/Documentation/DocBook/v4l/media-ioc-device-info.xml @@ -0,0 +1,132 @@ + + + ioctl MEDIA_IOC_DEVICE_INFO + &manvol; + + + + MEDIA_IOC_DEVICE_INFO + Query device information + + + + + + int ioctl + int fd + int request + struct media_device_info *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + MEDIA_IOC_DEVICE_INFO + + + + argp + + + + + + + + + Description + + All media devices must support the MEDIA_IOC_DEVICE_INFO + ioctl. To query device information, applications call the ioctl with a + pointer to a &media-device-info;. The driver fills the structure and returns + the information to the application. + The ioctl never fails. + + + struct <structname>media_device_info</structname> + + &cs-str; + + + char + driver[16] + Name of the driver implementing the media API as a + NUL-terminated ASCII string. The driver version is stored in the + driver_version field. + Driver specific applications can use this information to + verify the driver identity. It is also useful to work around + known bugs, or to identify drivers in error reports. + + + char + model[32] + Device model name as a NUL-terminated UTF-8 string. The + device version is stored in the device_version + field and is not be appended to the model name. + + + char + serial[40] + Serial number as a NUL-terminated ASCII string. + + + char + bus_info[32] + Location of the device in the system as a NUL-terminated + ASCII string. This includes the bus type name (PCI, USB, ...) and a + bus-specific identifier. + + + __u32 + media_version + Media API version, formatted with the + KERNEL_VERSION() macro. + + + __u32 + hw_revision + Hardware device revision in a driver-specific format. + + + __u32 + media_version + Media device driver version, formatted with the + KERNEL_VERSION() macro. Together with the + driver field this identifies a particular + driver. + + + __u32 + reserved[31] + Reserved for future extensions. Drivers and applications must + set this array to zero. + + + +
+ The serial and bus_info + fields can be used to distinguish between multiple instances of otherwise + identical hardware. The serial number takes precedence when provided and can + be assumed to be unique. If the serial number is an empty string, the + bus_info field can be used instead. The + bus_info field is guaranteed to be unique, but + can vary across reboots or device unplug/replug. +
+ + + Return value + This function doesn't return specific error codes. + +
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index d2bc809..92e0d4e 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -22,13 +22,70 @@ #include #include +#include #include #include #include +/* ----------------------------------------------------------------------------- + * Userspace API + */ + +static int media_device_open(struct file *filp) +{ + return 0; +} + +static int media_device_close(struct file *filp) +{ + return 0; +} + +static int media_device_get_info(struct media_device *dev, + struct media_device_info __user *__info) +{ + struct media_device_info info; + + memset(&info, 0, sizeof(info)); + + strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver)); + strlcpy(info.model, dev->model, sizeof(info.model)); + strlcpy(info.serial, dev->serial, sizeof(info.serial)); + strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info)); + + info.media_version = MEDIA_API_VERSION; + info.hw_revision = dev->hw_revision; + info.driver_version = dev->driver_version; + + return copy_to_user(__info, &info, sizeof(*__info)); +} + +static long media_device_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct media_devnode *devnode = media_devnode_data(filp); + struct media_device *dev = to_media_device(devnode); + long ret; + + switch (cmd) { + case MEDIA_IOC_DEVICE_INFO: + ret = media_device_get_info(dev, + (struct media_device_info __user *)arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + + return ret; +} + static const struct media_file_operations media_device_fops = { .owner = THIS_MODULE, + .open = media_device_open, + .ioctl = media_device_ioctl, + .release = media_device_close, }; /* ----------------------------------------------------------------------------- diff --git a/include/linux/Kbuild b/include/linux/Kbuild index b0ada6f..31eb174 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -233,6 +233,7 @@ header-y += magic.h header-y += major.h header-y += map_to_7segment.h header-y += matroxfb.h +header-y += media.h header-y += mempolicy.h header-y += meye.h header-y += mii.h diff --git a/include/linux/media.h b/include/linux/media.h new file mode 100644 index 0000000..64090db --- /dev/null +++ b/include/linux/media.h @@ -0,0 +1,45 @@ +/* + * Multimedia device API + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_MEDIA_H +#define __LINUX_MEDIA_H + +#include +#include +#include + +#define MEDIA_API_VERSION KERNEL_VERSION(0, 1, 0) + +struct media_device_info { + char driver[16]; + char model[32]; + char serial[40]; + char bus_info[32]; + __u32 media_version; + __u32 hw_revision; + __u32 driver_version; + __u32 reserved[31]; +}; + +#define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) + +#endif /* __LINUX_MEDIA_H */ -- cgit v0.10.2 From 1651333b09743887bc2dd3d158a11853a2be3fe7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:40:01 -0300 Subject: [media] media: Entities, pads and links enumeration Create the following two ioctls and implement them at the media device level to enumerate entities, pads and links. - MEDIA_IOC_ENUM_ENTITIES: Enumerate entities and their properties - MEDIA_IOC_ENUM_LINKS: Enumerate all pads and links for a given entity Entity IDs can be non-contiguous. Userspace applications should enumerate entities using the MEDIA_ENT_ID_FLAG_NEXT flag. When the flag is set in the entity ID, the MEDIA_IOC_ENUM_ENTITIES will return the next entity with an ID bigger than the requested one. Only forward links that originate at one of the entity's source pads are returned during the enumeration process. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 034f891..2bd7b27 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -92,6 +92,8 @@ VIDIOC_UNSUBSCRIBE_EVENT"> MEDIA_IOC_DEVICE_INFO"> +MEDIA_IOC_ENUM_ENTITIES"> +MEDIA_IOC_ENUM_LINKS"> v4l2_std_id"> @@ -191,6 +193,10 @@ v4l2_window"> media_device_info"> +media_entity_desc"> +media_links_enum"> +media_pad_desc"> +media_link_desc"> EACCES error code"> @@ -340,6 +346,8 @@ + + diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml index a46b786..2c4fd2b 100644 --- a/Documentation/DocBook/v4l/media-controller.xml +++ b/Documentation/DocBook/v4l/media-controller.xml @@ -83,4 +83,6 @@ &sub-media-ioctl; &sub-media-ioc-device-info; + &sub-media-ioc-enum-entities; + &sub-media-ioc-enum-links; diff --git a/Documentation/DocBook/v4l/media-ioc-device-info.xml b/Documentation/DocBook/v4l/media-ioc-device-info.xml index 278a312..1f32373 100644 --- a/Documentation/DocBook/v4l/media-ioc-device-info.xml +++ b/Documentation/DocBook/v4l/media-ioc-device-info.xml @@ -27,7 +27,8 @@ fd - &fd; + File descriptor returned by + open(). diff --git a/Documentation/DocBook/v4l/media-ioc-enum-entities.xml b/Documentation/DocBook/v4l/media-ioc-enum-entities.xml new file mode 100644 index 0000000..13d0cc4 --- /dev/null +++ b/Documentation/DocBook/v4l/media-ioc-enum-entities.xml @@ -0,0 +1,308 @@ + + + ioctl MEDIA_IOC_ENUM_ENTITIES + &manvol; + + + + MEDIA_IOC_ENUM_ENTITIES + Enumerate entities and their properties + + + + + + int ioctl + int fd + int request + struct media_entity_desc *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + MEDIA_IOC_ENUM_ENTITIES + + + + argp + + + + + + + + + Description + To query the attributes of an entity, applications set the id field + of a &media-entity-desc; structure and call the MEDIA_IOC_ENUM_ENTITIES + ioctl with a pointer to this structure. The driver fills the rest of the + structure or returns an &EINVAL; when the id is invalid. + Entities can be enumerated by or'ing the id with the + MEDIA_ENT_ID_FLAG_NEXT flag. The driver will return + information about the entity with the smallest id strictly larger than the + requested one ('next entity'), or the &EINVAL; if there is none. + Entity IDs can be non-contiguous. Applications must + not try to enumerate entities by calling + MEDIA_IOC_ENUM_ENTITIES with increasing id's until they get an error. + Two or more entities that share a common non-zero + group_id value are considered as logically + grouped. Groups are used to report + + ALSA, VBI and video nodes that carry the same media + stream + lens and flash controllers associated with a sensor + + + + + struct <structname>media_entity_desc</structname> + + + + + + + + + __u32 + id + + + Entity id, set by the application. When the id is or'ed with + MEDIA_ENT_ID_FLAG_NEXT, the driver clears the + flag and returns the first entity with a larger id. + + + char + name[32] + + + Entity name as an UTF-8 NULL-terminated string. + + + __u32 + type + + + Entity type, see for details. + + + __u32 + revision + + + Entity revision in a driver/hardware specific format. + + + __u32 + flags + + + Entity flags, see for details. + + + __u32 + group_id + + + Entity group ID + + + __u16 + pads + + + Number of pads + + + __u16 + links + + + Total number of outbound links. Inbound links are not counted + in this field. + + + union + + + + struct + v4l + + Valid for V4L sub-devices and nodes only. + + + + + __u32 + major + V4L device node major number. For V4L sub-devices with no + device node, set by the driver to 0. + + + + + __u32 + minor + V4L device node minor number. For V4L sub-devices with no + device node, set by the driver to 0. + + + + struct + fb + + Valid for frame buffer nodes only. + + + + + __u32 + major + Frame buffer device node major number. + + + + + __u32 + minor + Frame buffer device node minor number. + + + + struct + alsa + + Valid for ALSA devices only. + + + + + __u32 + card + ALSA card number + + + + + __u32 + device + ALSA device number + + + + + __u32 + subdevice + ALSA sub-device number + + + + int + dvb + + DVB card number + + + + __u8 + raw[180] + + + + + +
+ + + Media entity types + + + + + + MEDIA_ENT_T_DEVNODE + Unknown device node + + + MEDIA_ENT_T_DEVNODE_V4L + V4L video, radio or vbi device node + + + MEDIA_ENT_T_DEVNODE_FB + Frame buffer device node + + + MEDIA_ENT_T_DEVNODE_ALSA + ALSA card + + + MEDIA_ENT_T_DEVNODE_DVB + DVB card + + + MEDIA_ENT_T_V4L2_SUBDEV + Unknown V4L sub-device + + + MEDIA_ENT_T_V4L2_SUBDEV_SENSOR + Video sensor + + + MEDIA_ENT_T_V4L2_SUBDEV_FLASH + Flash controller + + + MEDIA_ENT_T_V4L2_SUBDEV_LENS + Lens controller + + + +
+ + + Media entity flags + + + + + + MEDIA_ENT_FL_DEFAULT + Default entity for its type. Used to discover the default + audio, VBI and video devices, the default camera sensor, ... + + + +
+
+ + + &return-value; + + + + EINVAL + + The &media-entity-desc; id references + a non-existing entity. + + + + +
diff --git a/Documentation/DocBook/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/v4l/media-ioc-enum-links.xml new file mode 100644 index 0000000..6da8841 --- /dev/null +++ b/Documentation/DocBook/v4l/media-ioc-enum-links.xml @@ -0,0 +1,202 @@ + + + ioctl MEDIA_IOC_ENUM_LINKS + &manvol; + + + + MEDIA_IOC_ENUM_LINKS + Enumerate all pads and links for a given entity + + + + + + int ioctl + int fd + int request + struct media_links_enum *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + MEDIA_IOC_ENUM_LINKS + + + + argp + + + + + + + + + Description + + To enumerate pads and/or links for a given entity, applications set + the entity field of a &media-links-enum; structure and initialize the + &media-pad-desc; and &media-link-desc; structure arrays pointed by the + pads and links fields. + They then call the MEDIA_IOC_ENUM_LINKS ioctl with a pointer to this + structure. + If the pads field is not NULL, the driver + fills the pads array with information about the + entity's pads. The array must have enough room to store all the entity's + pads. The number of pads can be retrieved with the &MEDIA-IOC-ENUM-ENTITIES; + ioctl. + If the links field is not NULL, the driver + fills the links array with information about the + entity's outbound links. The array must have enough room to store all the + entity's outbound links. The number of outbound links can be retrieved with + the &MEDIA-IOC-ENUM-ENTITIES; ioctl. + Only forward links that originate at one of the entity's source pads + are returned during the enumeration process. + + + struct <structname>media_links_enum</structname> + + &cs-str; + + + __u32 + entity + Entity id, set by the application. + + + struct &media-pad-desc; + *pads + Pointer to a pads array allocated by the application. Ignored + if NULL. + + + struct &media-link-desc; + *links + Pointer to a links array allocated by the application. Ignored + if NULL. + + + + + + + struct <structname>media_pad_desc</structname> + + &cs-str; + + + __u32 + entity + ID of the entity this pad belongs to. + + + __u16 + index + 0-based pad index. + + + __u32 + flags + Pad flags, see for more details. + + + +
+ + + Media pad flags + + + + + + MEDIA_PAD_FL_SINK + Input pad, relative to the entity. Input pads sink data and + are targets of links. + + + MEDIA_PAD_FL_SOURCE + Output pad, relative to the entity. Output pads source data + and are origins of links. + + + +
+ + + struct <structname>media_links_desc</structname> + + &cs-str; + + + struct &media-pad-desc; + source + Pad at the origin of this link. + + + struct &media-pad-desc; + sink + Pad at the target of this link. + + + __u32 + flags + Link flags, see for more details. + + + + + + + Media link flags + + + + + + MEDIA_LNK_FL_ENABLED + The link is enabled and can be used to transfer media data. + When two or more links target a sink pad, only one of them can be + enabled at a time. + + + MEDIA_LNK_FL_IMMUTABLE + The link enabled state can't be modified at runtime. An + immutable link is always enabled. + + + + + One and only one of MEDIA_PAD_FL_SINK and + MEDIA_PAD_FL_SOURCE must be set for every pad. +
+ + + &return-value; + + + + EINVAL + + The &media-links-enum; id references + a non-existing entity. + + + + +
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 92e0d4e..648a9d8 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -61,6 +61,117 @@ static int media_device_get_info(struct media_device *dev, return copy_to_user(__info, &info, sizeof(*__info)); } +static struct media_entity *find_entity(struct media_device *mdev, u32 id) +{ + struct media_entity *entity; + int next = id & MEDIA_ENT_ID_FLAG_NEXT; + + id &= ~MEDIA_ENT_ID_FLAG_NEXT; + + spin_lock(&mdev->lock); + + media_device_for_each_entity(entity, mdev) { + if ((entity->id == id && !next) || + (entity->id > id && next)) { + spin_unlock(&mdev->lock); + return entity; + } + } + + spin_unlock(&mdev->lock); + + return NULL; +} + +static long media_device_enum_entities(struct media_device *mdev, + struct media_entity_desc __user *uent) +{ + struct media_entity *ent; + struct media_entity_desc u_ent; + + if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id))) + return -EFAULT; + + ent = find_entity(mdev, u_ent.id); + + if (ent == NULL) + return -EINVAL; + + u_ent.id = ent->id; + u_ent.name[0] = '\0'; + if (ent->name) + strlcpy(u_ent.name, ent->name, sizeof(u_ent.name)); + u_ent.type = ent->type; + u_ent.revision = ent->revision; + u_ent.flags = ent->flags; + u_ent.group_id = ent->group_id; + u_ent.pads = ent->num_pads; + u_ent.links = ent->num_links - ent->num_backlinks; + u_ent.v4l.major = ent->v4l.major; + u_ent.v4l.minor = ent->v4l.minor; + if (copy_to_user(uent, &u_ent, sizeof(u_ent))) + return -EFAULT; + return 0; +} + +static void media_device_kpad_to_upad(const struct media_pad *kpad, + struct media_pad_desc *upad) +{ + upad->entity = kpad->entity->id; + upad->index = kpad->index; + upad->flags = kpad->flags; +} + +static long media_device_enum_links(struct media_device *mdev, + struct media_links_enum __user *ulinks) +{ + struct media_entity *entity; + struct media_links_enum links; + + if (copy_from_user(&links, ulinks, sizeof(links))) + return -EFAULT; + + entity = find_entity(mdev, links.entity); + if (entity == NULL) + return -EINVAL; + + if (links.pads) { + unsigned int p; + + for (p = 0; p < entity->num_pads; p++) { + struct media_pad_desc pad; + media_device_kpad_to_upad(&entity->pads[p], &pad); + if (copy_to_user(&links.pads[p], &pad, sizeof(pad))) + return -EFAULT; + } + } + + if (links.links) { + struct media_link_desc __user *ulink; + unsigned int l; + + for (l = 0, ulink = links.links; l < entity->num_links; l++) { + struct media_link_desc link; + + /* Ignore backlinks. */ + if (entity->links[l].source->entity != entity) + continue; + + media_device_kpad_to_upad(entity->links[l].source, + &link.source); + media_device_kpad_to_upad(entity->links[l].sink, + &link.sink); + link.flags = entity->links[l].flags; + if (copy_to_user(ulink, &link, sizeof(*ulink))) + return -EFAULT; + ulink++; + } + } + if (copy_to_user(ulinks, &links, sizeof(*ulinks))) + return -EFAULT; + return 0; +} + static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -74,6 +185,18 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, (struct media_device_info __user *)arg); break; + case MEDIA_IOC_ENUM_ENTITIES: + ret = media_device_enum_entities(dev, + (struct media_entity_desc __user *)arg); + break; + + case MEDIA_IOC_ENUM_LINKS: + mutex_lock(&dev->graph_mutex); + ret = media_device_enum_links(dev, + (struct media_links_enum __user *)arg); + mutex_unlock(&dev->graph_mutex); + break; + default: ret = -ENOIOCTLCMD; } diff --git a/include/linux/media.h b/include/linux/media.h index 64090db..17c93a4 100644 --- a/include/linux/media.h +++ b/include/linux/media.h @@ -40,6 +40,91 @@ struct media_device_info { __u32 reserved[31]; }; +#define MEDIA_ENT_ID_FLAG_NEXT (1 << 31) + +#define MEDIA_ENT_TYPE_SHIFT 16 +#define MEDIA_ENT_TYPE_MASK 0x00ff0000 +#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff + +#define MEDIA_ENT_T_DEVNODE (1 << MEDIA_ENT_TYPE_SHIFT) +#define MEDIA_ENT_T_DEVNODE_V4L (MEDIA_ENT_T_DEVNODE + 1) +#define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENT_T_DEVNODE + 2) +#define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENT_T_DEVNODE + 3) +#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENT_T_DEVNODE + 4) + +#define MEDIA_ENT_T_V4L2_SUBDEV (2 << MEDIA_ENT_TYPE_SHIFT) +#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENT_T_V4L2_SUBDEV + 1) +#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH (MEDIA_ENT_T_V4L2_SUBDEV + 2) +#define MEDIA_ENT_T_V4L2_SUBDEV_LENS (MEDIA_ENT_T_V4L2_SUBDEV + 3) + +#define MEDIA_ENT_FL_DEFAULT (1 << 0) + +struct media_entity_desc { + __u32 id; + char name[32]; + __u32 type; + __u32 revision; + __u32 flags; + __u32 group_id; + __u16 pads; + __u16 links; + + __u32 reserved[4]; + + union { + /* Node specifications */ + struct { + __u32 major; + __u32 minor; + } v4l; + struct { + __u32 major; + __u32 minor; + } fb; + struct { + __u32 card; + __u32 device; + __u32 subdevice; + } alsa; + int dvb; + + /* Sub-device specifications */ + /* Nothing needed yet */ + __u8 raw[184]; + }; +}; + +#define MEDIA_PAD_FL_SINK (1 << 0) +#define MEDIA_PAD_FL_SOURCE (1 << 1) + +struct media_pad_desc { + __u32 entity; /* entity ID */ + __u16 index; /* pad index */ + __u32 flags; /* pad flags */ + __u32 reserved[2]; +}; + +#define MEDIA_LNK_FL_ENABLED (1 << 0) +#define MEDIA_LNK_FL_IMMUTABLE (1 << 1) + +struct media_link_desc { + struct media_pad_desc source; + struct media_pad_desc sink; + __u32 flags; + __u32 reserved[2]; +}; + +struct media_links_enum { + __u32 entity; + /* Should have enough room for pads elements */ + struct media_pad_desc __user *pads; + /* Should have enough room for links elements */ + struct media_link_desc __user *links; + __u32 reserved[4]; +}; + #define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) +#define MEDIA_IOC_ENUM_ENTITIES _IOWR('M', 2, struct media_entity_desc) +#define MEDIA_IOC_ENUM_LINKS _IOWR('M', 3, struct media_links_enum) #endif /* __LINUX_MEDIA_H */ diff --git a/include/media/media-entity.h b/include/media/media-entity.h index a9b31d9..51bdafc 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -24,29 +24,7 @@ #define _MEDIA_ENTITY_H #include - -#define MEDIA_ENT_TYPE_SHIFT 16 -#define MEDIA_ENT_TYPE_MASK 0x00ff0000 -#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff - -#define MEDIA_ENT_T_DEVNODE (1 << MEDIA_ENT_TYPE_SHIFT) -#define MEDIA_ENT_T_DEVNODE_V4L (MEDIA_ENT_T_DEVNODE + 1) -#define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENT_T_DEVNODE + 2) -#define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENT_T_DEVNODE + 3) -#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENT_T_DEVNODE + 4) - -#define MEDIA_ENT_T_V4L2_SUBDEV (2 << MEDIA_ENT_TYPE_SHIFT) -#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENT_T_V4L2_SUBDEV + 1) -#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH (MEDIA_ENT_T_V4L2_SUBDEV + 2) -#define MEDIA_ENT_T_V4L2_SUBDEV_LENS (MEDIA_ENT_T_V4L2_SUBDEV + 3) - -#define MEDIA_ENT_FL_DEFAULT (1 << 0) - -#define MEDIA_LNK_FL_ENABLED (1 << 0) -#define MEDIA_LNK_FL_IMMUTABLE (1 << 1) - -#define MEDIA_PAD_FL_SINK (1 << 0) -#define MEDIA_PAD_FL_SOURCE (1 << 1) +#include struct media_link { struct media_pad *source; /* Source pad */ -- cgit v0.10.2 From 97548ed4c4661502cdfd1aabd5d3876fa4f5cc2e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:40:03 -0300 Subject: [media] media: Links setup Create the following ioctl and implement it at the media device level to setup links. - MEDIA_IOC_SETUP_LINK: Modify the properties of a given link The only property that can currently be modified is the ENABLED link flag to enable/disable a link. Links marked with the IMMUTABLE link flag can not be enabled or disabled. Enabling or disabling a link has effects on entities' use count. Those changes are automatically propagated through the graph. Signed-off-by: Laurent Pinchart Signed-off-by: Stanimir Varbanov Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 2bd7b27..121db15 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -94,6 +94,7 @@ MEDIA_IOC_DEVICE_INFO"> MEDIA_IOC_ENUM_ENTITIES"> MEDIA_IOC_ENUM_LINKS"> +MEDIA_IOC_SETUP_LINK"> v4l2_std_id"> @@ -348,6 +349,7 @@ + diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml index 2c4fd2b..2dc25e1 100644 --- a/Documentation/DocBook/v4l/media-controller.xml +++ b/Documentation/DocBook/v4l/media-controller.xml @@ -85,4 +85,5 @@ &sub-media-ioc-device-info; &sub-media-ioc-enum-entities; &sub-media-ioc-enum-links; + &sub-media-ioc-setup-link; diff --git a/Documentation/DocBook/v4l/media-ioc-setup-link.xml b/Documentation/DocBook/v4l/media-ioc-setup-link.xml new file mode 100644 index 0000000..09ab3d2 --- /dev/null +++ b/Documentation/DocBook/v4l/media-ioc-setup-link.xml @@ -0,0 +1,90 @@ + + + ioctl MEDIA_IOC_SETUP_LINK + &manvol; + + + + MEDIA_IOC_SETUP_LINK + Modify the properties of a link + + + + + + int ioctl + int fd + int request + struct media_link_desc *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + MEDIA_IOC_ENUM_LINKS + + + + argp + + + + + + + + + Description + + To change link properties applications fill a &media-link-desc; with + link identification information (source and sink pad) and the new requested + link flags. They then call the MEDIA_IOC_SETUP_LINK ioctl with a pointer to + that structure. + The only configurable property is the ENABLED + link flag to enable/disable a link. Links marked with the + IMMUTABLE link flag can not be enabled or disabled. + + Link configuration has no side effect on other links. If an enabled + link at the sink pad prevents the link from being enabled, the driver + returns with an &EBUSY;. + If the specified link can't be found the driver returns with an + &EINVAL;. + + + + &return-value; + + + + EBUSY + + The link properties can't be changed because the link is + currently busy. This can be caused, for instance, by an active media + stream (audio or video) on the link. The ioctl shouldn't be retried if + no other action is performed before to fix the problem. + + + + EINVAL + + The &media-link-desc; references a non-existing link, or the + link is immutable and an attempt to modify its configuration was made. + + + + + + diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 78ae020..4809221 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -259,6 +259,16 @@ When the graph traversal is complete the function will return NULL. Graph traversal can be interrupted at any moment. No cleanup function call is required and the graph structure can be freed normally. +Helper functions can be used to find a link between two given pads, or a pad +connected to another pad through an enabled link + + media_entity_find_link(struct media_pad *source, + struct media_pad *sink); + + media_entity_remote_source(struct media_pad *pad); + +Refer to the kerneldoc documentation for more information. + Use count and power handling ---------------------------- @@ -271,3 +281,35 @@ track the number of users of every entity for power management needs. The use_count field is owned by media drivers and must not be touched by entity drivers. Access to the field must be protected by the media device graph_mutex lock. + + +Links setup +----------- + +Link properties can be modified at runtime by calling + + media_entity_setup_link(struct media_link *link, u32 flags); + +The flags argument contains the requested new link flags. + +The only configurable property is the ENABLED link flag to enable/disable a +link. Links marked with the IMMUTABLE link flag can not be enabled or disabled. + +When a link is enabled or disabled, the media framework calls the +link_setup operation for the two entities at the source and sink of the link, +in that order. If the second link_setup call fails, another link_setup call is +made on the first entity to restore the original link flags. + +Media device drivers can be notified of link setup operations by setting the +media_device::link_notify pointer to a callback function. If provided, the +notification callback will be called before enabling and after disabling +links. + +Entity drivers must implement the link_setup operation if any of their links +is non-immutable. The operation must either configure the hardware or store +the configuration information to be applied later. + +Link configuration must not have any side effect on other links. If an enabled +link at a sink pad prevents another link at the same pad from being disabled, +the link_setup operation must return -EBUSY and can't implicitly disable the +first enabled link. diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 648a9d8..16b70b4 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -172,6 +172,44 @@ static long media_device_enum_links(struct media_device *mdev, return 0; } +static long media_device_setup_link(struct media_device *mdev, + struct media_link_desc __user *_ulink) +{ + struct media_link *link = NULL; + struct media_link_desc ulink; + struct media_entity *source; + struct media_entity *sink; + int ret; + + if (copy_from_user(&ulink, _ulink, sizeof(ulink))) + return -EFAULT; + + /* Find the source and sink entities and link. + */ + source = find_entity(mdev, ulink.source.entity); + sink = find_entity(mdev, ulink.sink.entity); + + if (source == NULL || sink == NULL) + return -EINVAL; + + if (ulink.source.index >= source->num_pads || + ulink.sink.index >= sink->num_pads) + return -EINVAL; + + link = media_entity_find_link(&source->pads[ulink.source.index], + &sink->pads[ulink.sink.index]); + if (link == NULL) + return -EINVAL; + + /* Setup the link on both entities. */ + ret = __media_entity_setup_link(link, ulink.flags); + + if (copy_to_user(_ulink, &ulink, sizeof(ulink))) + return -EFAULT; + + return ret; +} + static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -197,6 +235,13 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, mutex_unlock(&dev->graph_mutex); break; + case MEDIA_IOC_SETUP_LINK: + mutex_lock(&dev->graph_mutex); + ret = media_device_setup_link(dev, + (struct media_link_desc __user *)arg); + mutex_unlock(&dev->graph_mutex); + break; + default: ret = -ENOIOCTLCMD; } diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 3e7e2d5..6795c92 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -306,3 +306,158 @@ media_entity_create_link(struct media_entity *source, u16 source_pad, return 0; } EXPORT_SYMBOL_GPL(media_entity_create_link); + +static int __media_entity_setup_link_notify(struct media_link *link, u32 flags) +{ + const u32 mask = MEDIA_LNK_FL_ENABLED; + int ret; + + /* Notify both entities. */ + ret = media_entity_call(link->source->entity, link_setup, + link->source, link->sink, flags); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + ret = media_entity_call(link->sink->entity, link_setup, + link->sink, link->source, flags); + if (ret < 0 && ret != -ENOIOCTLCMD) { + media_entity_call(link->source->entity, link_setup, + link->source, link->sink, link->flags); + return ret; + } + + link->flags = (link->flags & ~mask) | (flags & mask); + link->reverse->flags = link->flags; + + return 0; +} + +/** + * __media_entity_setup_link - Configure a media link + * @link: The link being configured + * @flags: Link configuration flags + * + * The bulk of link setup is handled by the two entities connected through the + * link. This function notifies both entities of the link configuration change. + * + * If the link is immutable or if the current and new configuration are + * identical, return immediately. + * + * The user is expected to hold link->source->parent->mutex. If not, + * media_entity_setup_link() should be used instead. + */ +int __media_entity_setup_link(struct media_link *link, u32 flags) +{ + struct media_device *mdev; + struct media_entity *source, *sink; + int ret = -EBUSY; + + if (link == NULL) + return -EINVAL; + + if (link->flags & MEDIA_LNK_FL_IMMUTABLE) + return link->flags == flags ? 0 : -EINVAL; + + if (link->flags == flags) + return 0; + + source = link->source->entity; + sink = link->sink->entity; + + mdev = source->parent; + + if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) { + ret = mdev->link_notify(link->source, link->sink, + MEDIA_LNK_FL_ENABLED); + if (ret < 0) + return ret; + } + + ret = __media_entity_setup_link_notify(link, flags); + if (ret < 0) + goto err; + + if (!(flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) + mdev->link_notify(link->source, link->sink, 0); + + return 0; + +err: + if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) + mdev->link_notify(link->source, link->sink, 0); + + return ret; +} + +int media_entity_setup_link(struct media_link *link, u32 flags) +{ + int ret; + + mutex_lock(&link->source->entity->parent->graph_mutex); + ret = __media_entity_setup_link(link, flags); + mutex_unlock(&link->source->entity->parent->graph_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(media_entity_setup_link); + +/** + * media_entity_find_link - Find a link between two pads + * @source: Source pad + * @sink: Sink pad + * + * Return a pointer to the link between the two entities. If no such link + * exists, return NULL. + */ +struct media_link * +media_entity_find_link(struct media_pad *source, struct media_pad *sink) +{ + struct media_link *link; + unsigned int i; + + for (i = 0; i < source->entity->num_links; ++i) { + link = &source->entity->links[i]; + + if (link->source->entity == source->entity && + link->source->index == source->index && + link->sink->entity == sink->entity && + link->sink->index == sink->index) + return link; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(media_entity_find_link); + +/** + * media_entity_remote_source - Find the source pad at the remote end of a link + * @pad: Sink pad at the local end of the link + * + * Search for a remote source pad connected to the given sink pad by iterating + * over all links originating or terminating at that pad until an enabled link + * is found. + * + * Return a pointer to the pad at the remote end of the first found enabled + * link, or NULL if no enabled link has been found. + */ +struct media_pad *media_entity_remote_source(struct media_pad *pad) +{ + unsigned int i; + + for (i = 0; i < pad->entity->num_links; i++) { + struct media_link *link = &pad->entity->links[i]; + + if (!(link->flags & MEDIA_LNK_FL_ENABLED)) + continue; + + if (link->source == pad) + return link->sink; + + if (link->sink == pad) + return link->source; + } + + return NULL; + +} +EXPORT_SYMBOL_GPL(media_entity_remote_source); diff --git a/include/linux/media.h b/include/linux/media.h index 17c93a4..7c69913 100644 --- a/include/linux/media.h +++ b/include/linux/media.h @@ -126,5 +126,6 @@ struct media_links_enum { #define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) #define MEDIA_IOC_ENUM_ENTITIES _IOWR('M', 2, struct media_entity_desc) #define MEDIA_IOC_ENUM_LINKS _IOWR('M', 3, struct media_links_enum) +#define MEDIA_IOC_SETUP_LINK _IOWR('M', 4, struct media_link_desc) #endif /* __LINUX_MEDIA_H */ diff --git a/include/media/media-device.h b/include/media/media-device.h index 5d2bff4..6a27d91 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -73,6 +73,9 @@ struct media_device { spinlock_t lock; /* Serializes graph operations. */ struct mutex graph_mutex; + + int (*link_notify)(struct media_pad *source, + struct media_pad *sink, u32 flags); }; /* media_devnode to media_device */ diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 51bdafc..d889dcc 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -39,6 +39,12 @@ struct media_pad { unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */ }; +struct media_entity_operations { + int (*link_setup)(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags); +}; + struct media_entity { struct list_head list; struct media_device *parent; /* Media device this entity belongs to*/ @@ -59,6 +65,8 @@ struct media_entity { struct media_pad *pads; /* Pads array (num_pads elements) */ struct media_link *links; /* Links array (max_links elements)*/ + const struct media_entity_operations *ops; /* Entity operations */ + /* Reference counts must never be negative, but are signed integers on * purpose: a simple WARN_ON(<0) check can be used to detect reference * count bugs that would make them negative. @@ -112,6 +120,11 @@ int media_entity_init(struct media_entity *entity, u16 num_pads, void media_entity_cleanup(struct media_entity *entity); int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); +int __media_entity_setup_link(struct media_link *link, u32 flags); +int media_entity_setup_link(struct media_link *link, u32 flags); +struct media_link *media_entity_find_link(struct media_pad *source, + struct media_pad *sink); +struct media_pad *media_entity_remote_source(struct media_pad *pad); struct media_entity *media_entity_get(struct media_entity *entity); void media_entity_put(struct media_entity *entity); @@ -121,4 +134,8 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph); +#define media_entity_call(entity, operation, args...) \ + (((entity)->ops && (entity)->ops->operation) ? \ + (entity)->ops->operation((entity) , ##args) : -ENOIOCTLCMD) + #endif -- cgit v0.10.2 From e02188c90f6ef61f0844c42508fe603c5d4fa42b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 25 Aug 2010 09:00:41 -0300 Subject: [media] media: Pipelines and media streams Drivers often need to associate pipeline objects to entities, and to take stream state into account when configuring entities and links. The pipeline API helps drivers manage that information. When starting streaming, drivers call media_entity_pipeline_start(). The function marks all entities connected to the given entity through enabled links, either directly or indirectly, as streaming. Similarly, when stopping the stream, drivers call media_entity_pipeline_stop(). The media_entity_pipeline_start() function takes a pointer to a media pipeline and stores it in every entity in the graph. Drivers should embed the media_pipeline structure in higher-level pipeline structures and can then access the pipeline through the media_entity structure. Link configuration will fail with -EBUSY by default if either end of the link is a streaming entity, unless the link is marked with the MEDIA_LNK_FL_DYNAMIC flag. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/v4l/media-ioc-enum-links.xml index 6da8841..d2fc73e 100644 --- a/Documentation/DocBook/v4l/media-ioc-enum-links.xml +++ b/Documentation/DocBook/v4l/media-ioc-enum-links.xml @@ -179,6 +179,11 @@ The link enabled state can't be modified at runtime. An immutable link is always enabled. + + MEDIA_LNK_FL_DYNAMIC + The link enabled state can be modified during streaming. This + flag is set by drivers and is read-only for applications. + diff --git a/Documentation/DocBook/v4l/media-ioc-setup-link.xml b/Documentation/DocBook/v4l/media-ioc-setup-link.xml index 09ab3d2..2331e76 100644 --- a/Documentation/DocBook/v4l/media-ioc-setup-link.xml +++ b/Documentation/DocBook/v4l/media-ioc-setup-link.xml @@ -60,6 +60,9 @@ Link configuration has no side effect on other links. If an enabled link at the sink pad prevents the link from being enabled, the driver returns with an &EBUSY;. + Only links marked with the DYNAMIC link flag can + be enabled/disabled while streaming media data. Attempting to enable or + disable a streaming non-dynamic link will return an &EBUSY;. If the specified link can't be found the driver returns with an &EINVAL;. diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 4809221..fd48add 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -313,3 +313,41 @@ Link configuration must not have any side effect on other links. If an enabled link at a sink pad prevents another link at the same pad from being disabled, the link_setup operation must return -EBUSY and can't implicitly disable the first enabled link. + + +Pipelines and media streams +--------------------------- + +When starting streaming, drivers must notify all entities in the pipeline to +prevent link states from being modified during streaming by calling + + media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe); + +The function will mark all entities connected to the given entity through +enabled links, either directly or indirectly, as streaming. + +The media_pipeline instance pointed to by the pipe argument will be stored in +every entity in the pipeline. Drivers should embed the media_pipeline structure +in higher-level pipeline structures and can then access the pipeline through +the media_entity pipe field. + +Calls to media_entity_pipeline_start() can be nested. The pipeline pointer must +be identical for all nested calls to the function. + +When stopping the stream, drivers must notify the entities with + + media_entity_pipeline_stop(struct media_entity *entity); + +If multiple calls to media_entity_pipeline_start() have been made the same +number of media_entity_pipeline_stop() calls are required to stop streaming. The +media_entity pipe field is reset to NULL on the last nested stop call. + +Link configuration will fail with -EBUSY by default if either end of the link is +a streaming entity. Links that can be modified while streaming must be marked +with the MEDIA_LNK_FL_DYNAMIC flag. + +If other operations need to be disallowed on streaming entities (such as +changing entities configuration parameters) drivers can explictly check the +media_entity stream_count field to find out if an entity is streaming. This +operation must be done with the media_device graph_mutex held. diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 6795c92..23640ed 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -197,6 +197,75 @@ media_entity_graph_walk_next(struct media_entity_graph *graph) EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); /* ----------------------------------------------------------------------------- + * Pipeline management + */ + +/** + * media_entity_pipeline_start - Mark a pipeline as streaming + * @entity: Starting entity + * @pipe: Media pipeline to be assigned to all entities in the pipeline. + * + * Mark all entities connected to a given entity through enabled links, either + * directly or indirectly, as streaming. The given pipeline object is assigned to + * every entity in the pipeline and stored in the media_entity pipe field. + * + * Calls to this function can be nested, in which case the same number of + * media_entity_pipeline_stop() calls will be required to stop streaming. The + * pipeline pointer must be identical for all nested calls to + * media_entity_pipeline_start(). + */ +void media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe) +{ + struct media_device *mdev = entity->parent; + struct media_entity_graph graph; + + mutex_lock(&mdev->graph_mutex); + + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + entity->stream_count++; + WARN_ON(entity->pipe && entity->pipe != pipe); + entity->pipe = pipe; + } + + mutex_unlock(&mdev->graph_mutex); +} +EXPORT_SYMBOL_GPL(media_entity_pipeline_start); + +/** + * media_entity_pipeline_stop - Mark a pipeline as not streaming + * @entity: Starting entity + * + * Mark all entities connected to a given entity through enabled links, either + * directly or indirectly, as not streaming. The media_entity pipe field is + * reset to NULL. + * + * If multiple calls to media_entity_pipeline_start() have been made, the same + * number of calls to this function are required to mark the pipeline as not + * streaming. + */ +void media_entity_pipeline_stop(struct media_entity *entity) +{ + struct media_device *mdev = entity->parent; + struct media_entity_graph graph; + + mutex_lock(&mdev->graph_mutex); + + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + entity->stream_count--; + if (entity->stream_count == 0) + entity->pipe = NULL; + } + + mutex_unlock(&mdev->graph_mutex); +} +EXPORT_SYMBOL_GPL(media_entity_pipeline_stop); + +/* ----------------------------------------------------------------------------- * Module use count */ @@ -364,6 +433,10 @@ int __media_entity_setup_link(struct media_link *link, u32 flags) source = link->source->entity; sink = link->sink->entity; + if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) && + (source->stream_count || sink->stream_count)) + return -EBUSY; + mdev = source->parent; if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) { diff --git a/include/linux/media.h b/include/linux/media.h index 7c69913..7ed23b4 100644 --- a/include/linux/media.h +++ b/include/linux/media.h @@ -106,6 +106,7 @@ struct media_pad_desc { #define MEDIA_LNK_FL_ENABLED (1 << 0) #define MEDIA_LNK_FL_IMMUTABLE (1 << 1) +#define MEDIA_LNK_FL_DYNAMIC (1 << 2) struct media_link_desc { struct media_pad_desc source; diff --git a/include/media/media-entity.h b/include/media/media-entity.h index d889dcc..cd8bca6 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -26,6 +26,9 @@ #include #include +struct media_pipeline { +}; + struct media_link { struct media_pad *source; /* Source pad */ struct media_pad *sink; /* Sink pad */ @@ -71,8 +74,11 @@ struct media_entity { * purpose: a simple WARN_ON(<0) check can be used to detect reference * count bugs that would make them negative. */ + int stream_count; /* Stream count for the entity. */ int use_count; /* Use count for the entity. */ + struct media_pipeline *pipe; /* Pipeline this entity belongs to. */ + union { /* Node specifications */ struct { @@ -118,6 +124,7 @@ struct media_entity_graph { int media_entity_init(struct media_entity *entity, u16 num_pads, struct media_pad *pads, u16 extra_links); void media_entity_cleanup(struct media_entity *entity); + int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); int __media_entity_setup_link(struct media_link *link, u32 flags); @@ -133,6 +140,9 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity *entity); struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph); +void media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe); +void media_entity_pipeline_stop(struct media_entity *entity); #define media_entity_call(entity, operation, args...) \ (((entity)->ops && (entity)->ops->operation) ? \ -- cgit v0.10.2 From 95db3a60e0652a52df145aacade1a88c5acef659 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:40:05 -0300 Subject: [media] v4l: Add a media_device pointer to the v4l2_device structure The pointer will later be used to register/unregister media entities when registering/unregistering a v4l2_subdev or a video_device. With the introduction of media devices, device drivers need to store a pointer to a driver-specific structure in the device's drvdata. v4l2_device can't claim ownership of the drvdata anymore. To maintain compatibility with drivers that rely on v4l2_device storing a pointer to itself in the device's drvdata, v4l2_device_register() will keep doing so if the drvdata is NULL. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index eb84795..7de55cf 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -83,11 +83,17 @@ You must register the device instance: v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); -Registration will initialize the v4l2_device struct and link dev->driver_data -to v4l2_dev. If v4l2_dev->name is empty then it will be set to a value derived -from dev (driver name followed by the bus_id, to be precise). If you set it -up before calling v4l2_device_register then it will be untouched. If dev is -NULL, then you *must* setup v4l2_dev->name before calling v4l2_device_register. +Registration will initialize the v4l2_device struct. If the dev->driver_data +field is NULL, it will be linked to v4l2_dev. Drivers that use the media +device framework in addition to the V4L2 framework need to set +dev->driver_data manually to point to the driver-specific device structure +that embed the struct v4l2_device instance. This is achieved by a +dev_set_drvdata() call before registering the V4L2 device instance. + +If v4l2_dev->name is empty then it will be set to a value derived from dev +(driver name followed by the bus_id, to be precise). If you set it up before +calling v4l2_device_register then it will be untouched. If dev is NULL, then +you *must* setup v4l2_dev->name before calling v4l2_device_register. You can use v4l2_device_set_name() to set the name based on a driver name and a driver-global atomic_t instance. This will generate names like ivtv0, ivtv1, @@ -108,6 +114,7 @@ You unregister with: v4l2_device_unregister(struct v4l2_device *v4l2_dev); +If the dev->driver_data field points to v4l2_dev, it will be reset to NULL. Unregistering will also automatically unregister all subdevs from the device. If you have a hotpluggable device (e.g. a USB device), then when a disconnect diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 8c0ad8b..cfbd8a3 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -47,9 +47,8 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) if (!v4l2_dev->name[0]) snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", dev->driver->name, dev_name(dev)); - if (dev_get_drvdata(dev)) - v4l2_warn(v4l2_dev, "Non-NULL drvdata on register\n"); - dev_set_drvdata(dev, v4l2_dev); + if (!dev_get_drvdata(dev)) + dev_set_drvdata(dev, v4l2_dev); return 0; } EXPORT_SYMBOL_GPL(v4l2_device_register); @@ -72,10 +71,12 @@ EXPORT_SYMBOL_GPL(v4l2_device_set_name); void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) { - if (v4l2_dev->dev) { + if (v4l2_dev->dev == NULL) + return; + + if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev) dev_set_drvdata(v4l2_dev->dev, NULL); - v4l2_dev->dev = NULL; - } + v4l2_dev->dev = NULL; } EXPORT_SYMBOL_GPL(v4l2_device_disconnect); diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 78b11e5..0c2bd30 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -21,6 +21,7 @@ #ifndef _V4L2_DEVICE_H #define _V4L2_DEVICE_H +#include #include /* Each instance of a V4L2 device should create the v4l2_device struct, @@ -39,6 +40,9 @@ struct v4l2_device { Note: dev might be NULL if there is no parent device as is the case with e.g. ISA devices. */ struct device *dev; +#if defined(CONFIG_MEDIA_CONTROLLER) + struct media_device *mdev; +#endif /* used to keep track of the registered subdevs */ struct list_head subdevs; /* lock this struct; can be used by the driver as well if this -- cgit v0.10.2 From 2c0ab67be1b4197a4effac89bb0604832e38be8d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:40:10 -0300 Subject: [media] v4l: Make video_device inherit from media_entity V4L2 devices are media entities. As such they need to inherit from (include) the media_entity structure. When registering/unregistering the device, the media entity is automatically registered/unregistered. The entity is acquired on device open and released on device close. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 7de55cf..0627081 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -71,6 +71,10 @@ sub-device instances, the video_device struct stores V4L2 device node data and in the future a v4l2_fh struct will keep track of filehandle instances (this is not yet implemented). +The V4L2 framework also optionally integrates with the media framework. If a +driver sets the struct v4l2_device mdev field, sub-devices and video nodes +will automatically appear in the media framework as entities. + struct v4l2_device ------------------ @@ -84,11 +88,14 @@ You must register the device instance: v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); Registration will initialize the v4l2_device struct. If the dev->driver_data -field is NULL, it will be linked to v4l2_dev. Drivers that use the media -device framework in addition to the V4L2 framework need to set +field is NULL, it will be linked to v4l2_dev. + +Drivers that want integration with the media device framework need to set dev->driver_data manually to point to the driver-specific device structure that embed the struct v4l2_device instance. This is achieved by a -dev_set_drvdata() call before registering the V4L2 device instance. +dev_set_drvdata() call before registering the V4L2 device instance. They must +also set the struct v4l2_device mdev field to point to a properly initialized +and registered media_device instance. If v4l2_dev->name is empty then it will be set to a value derived from dev (driver name followed by the bus_id, to be precise). If you set it up before @@ -530,6 +537,21 @@ If you use v4l2_ioctl_ops, then you should set either .unlocked_ioctl or The v4l2_file_operations struct is a subset of file_operations. The main difference is that the inode argument is omitted since it is never used. +If integration with the media framework is needed, you must initialize the +media_entity struct embedded in the video_device struct (entity field) by +calling media_entity_init(): + + struct media_pad *pad = &my_vdev->pad; + int err; + + err = media_entity_init(&vdev->entity, 1, pad, 0); + +The pads array must have been previously initialized. There is no need to +manually set the struct media_entity type and name fields. + +A reference to the entity will be automatically acquired/released when the +video device is opened/closed. + v4l2_file_operations and locking -------------------------------- @@ -559,6 +581,9 @@ for you. return err; } +If the v4l2_device parent device has a non-NULL mdev field, the video device +entity will be automatically registered with the media device. + Which device is registered depends on the type argument. The following types exist: @@ -634,6 +659,13 @@ release, of course) will return an error as well. When the last user of the video device node exits, then the vdev->release() callback is called and you can do the final cleanup there. +Don't forget to cleanup the media entity associated with the video device if +it has been initialized: + + media_entity_cleanup(&vdev->entity); + +This can be done from the release callback. + video_device helper functions ----------------------------- diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index abe04ef..e405b80 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -303,6 +303,9 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) static int v4l2_open(struct inode *inode, struct file *filp) { struct video_device *vdev; +#if defined(CONFIG_MEDIA_CONTROLLER) + struct media_entity *entity = NULL; +#endif int ret = 0; /* Check if the video device is available */ @@ -316,6 +319,16 @@ static int v4l2_open(struct inode *inode, struct file *filp) /* and increase the device refcount */ video_get(vdev); mutex_unlock(&videodev_lock); +#if defined(CONFIG_MEDIA_CONTROLLER) + if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) { + entity = media_entity_get(&vdev->entity); + if (!entity) { + ret = -EBUSY; + video_put(vdev); + return ret; + } + } +#endif if (vdev->fops->open) { if (vdev->lock && mutex_lock_interruptible(vdev->lock)) { ret = -ERESTARTSYS; @@ -331,8 +344,13 @@ static int v4l2_open(struct inode *inode, struct file *filp) err: /* decrease the refcount in case of an error */ - if (ret) + if (ret) { +#if defined(CONFIG_MEDIA_CONTROLLER) + if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) + media_entity_put(entity); +#endif video_put(vdev); + } return ret; } @@ -349,7 +367,10 @@ static int v4l2_release(struct inode *inode, struct file *filp) if (vdev->lock) mutex_unlock(vdev->lock); } - +#if defined(CONFIG_MEDIA_CONTROLLER) + if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) + media_entity_put(&vdev->entity); +#endif /* decrease the refcount unconditionally since the release() return value is ignored. */ video_put(vdev); @@ -585,12 +606,27 @@ int __video_register_device(struct video_device *vdev, int type, int nr, if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__, name_base, nr, video_device_node_name(vdev)); - - /* Part 5: Activate this minor. The char device can now be used. */ +#if defined(CONFIG_MEDIA_CONTROLLER) + /* Part 5: Register the entity. */ + if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) { + vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; + vdev->entity.name = vdev->name; + vdev->entity.v4l.major = VIDEO_MAJOR; + vdev->entity.v4l.minor = vdev->minor; + ret = media_device_register_entity(vdev->v4l2_dev->mdev, + &vdev->entity); + if (ret < 0) + printk(KERN_WARNING + "%s: media_device_register_entity failed\n", + __func__); + } +#endif + /* Part 6: Activate this minor. The char device can now be used. */ set_bit(V4L2_FL_REGISTERED, &vdev->flags); mutex_lock(&videodev_lock); video_device[vdev->minor] = vdev; mutex_unlock(&videodev_lock); + return 0; cleanup: @@ -618,6 +654,11 @@ void video_unregister_device(struct video_device *vdev) if (!vdev || !video_is_registered(vdev)) return; +#if defined(CONFIG_MEDIA_CONTROLLER) + if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) + media_device_unregister_entity(&vdev->entity); +#endif + mutex_lock(&videodev_lock); /* This must be in a critical section to prevent a race with v4l2_open. * Once this bit has been cleared video_get may never be called again. diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 4fe6831..51b2c51 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -16,6 +16,8 @@ #include #include +#include + #define VIDEO_MAJOR 81 #define VFL_TYPE_GRABBER 0 @@ -55,6 +57,9 @@ struct v4l2_file_operations { struct video_device { +#if defined(CONFIG_MEDIA_CONTROLLER) + struct media_entity entity; +#endif /* device ops */ const struct v4l2_file_operations *fops; @@ -100,6 +105,8 @@ struct video_device struct mutex *lock; }; +#define media_entity_to_video_device(entity) \ + container_of(entity, struct video_device, entity) /* dev to video-device */ #define to_video_device(cd) container_of(cd, struct video_device, dev) -- cgit v0.10.2 From 61f5db549dde43fb91a8b337f3a4096e4076c2d9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:40:08 -0300 Subject: [media] v4l: Make v4l2_subdev inherit from media_entity V4L2 subdevices are media entities. As such they need to inherit from (include) the media_entity structure. When registering/unregistering the subdevice, the media entity is automatically registered/unregistered. The entity is acquired on device open and released on device close. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 0627081..77d96f4 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -268,6 +268,26 @@ A sub-device driver initializes the v4l2_subdev struct using: Afterwards you need to initialize subdev->name with a unique name and set the module owner. This is done for you if you use the i2c helper functions. +If integration with the media framework is needed, you must initialize the +media_entity struct embedded in the v4l2_subdev struct (entity field) by +calling media_entity_init(): + + struct media_pad *pads = &my_sd->pads; + int err; + + err = media_entity_init(&sd->entity, npads, pads, 0); + +The pads array must have been previously initialized. There is no need to +manually set the struct media_entity type and name fields, but the revision +field must be initialized if needed. + +A reference to the entity will be automatically acquired/released when the +subdev device node (if any) is opened/closed. + +Don't forget to cleanup the media entity before the sub-device is destroyed: + + media_entity_cleanup(&sd->entity); + A device (bridge) driver needs to register the v4l2_subdev with the v4l2_device: @@ -277,6 +297,9 @@ This can fail if the subdev module disappeared before it could be registered. After this function was called successfully the subdev->dev field points to the v4l2_device. +If the v4l2_device parent device has a non-NULL mdev field, the sub-device +entity will be automatically registered with the media device. + You can unregister a sub-device using: v4l2_device_unregister_subdev(sd); diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index cfbd8a3..a1afda3 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -118,8 +118,11 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) EXPORT_SYMBOL_GPL(v4l2_device_unregister); int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, - struct v4l2_subdev *sd) + struct v4l2_subdev *sd) { +#if defined(CONFIG_MEDIA_CONTROLLER) + struct media_entity *entity = &sd->entity; +#endif int err; /* Check for valid input */ @@ -147,6 +150,19 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, return err; } +#if defined(CONFIG_MEDIA_CONTROLLER) + /* Register the entity. */ + if (v4l2_dev->mdev) { + err = media_device_register_entity(v4l2_dev->mdev, entity); + if (err < 0) { + if (sd->internal_ops && sd->internal_ops->unregistered) + sd->internal_ops->unregistered(sd); + module_put(sd->owner); + return err; + } + } +#endif + spin_lock(&v4l2_dev->lock); list_add_tail(&sd->list, &v4l2_dev->subdevs); spin_unlock(&v4l2_dev->lock); @@ -177,25 +193,37 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) sd->owner); if (err < 0) return err; +#if defined(CONFIG_MEDIA_CONTROLLER) + sd->entity.v4l.major = VIDEO_MAJOR; + sd->entity.v4l.minor = vdev->minor; +#endif } - return 0; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) { + struct v4l2_device *v4l2_dev; + /* return if it isn't registered */ if (sd == NULL || sd->v4l2_dev == NULL) return; - spin_lock(&sd->v4l2_dev->lock); + v4l2_dev = sd->v4l2_dev; + + spin_lock(&v4l2_dev->lock); list_del(&sd->list); - spin_unlock(&sd->v4l2_dev->lock); + spin_unlock(&v4l2_dev->lock); + if (sd->internal_ops && sd->internal_ops->unregistered) sd->internal_ops->unregistered(sd); sd->v4l2_dev = NULL; +#if defined(CONFIG_MEDIA_CONTROLLER) + if (v4l2_dev->mdev) + media_device_unregister_entity(&sd->entity); +#endif video_unregister_device(&sd->devnode); module_put(sd->owner); } diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 0c67f5a..8557363 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -35,7 +35,10 @@ static int subdev_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); - struct v4l2_fh *vfh; +#if defined(CONFIG_MEDIA_CONTROLLER) + struct media_entity *entity; +#endif + struct v4l2_fh *vfh = NULL; int ret; if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { @@ -58,11 +61,20 @@ static int subdev_open(struct file *file) v4l2_fh_add(vfh); file->private_data = vfh; } - +#if defined(CONFIG_MEDIA_CONTROLLER) + if (sd->v4l2_dev->mdev) { + entity = media_entity_get(&sd->entity); + if (!entity) { + ret = -EBUSY; + goto err; + } + } +#endif return 0; err: if (vfh != NULL) { + v4l2_fh_del(vfh); v4l2_fh_exit(vfh); kfree(vfh); } @@ -72,8 +84,16 @@ err: static int subdev_close(struct file *file) { +#if defined(CONFIG_MEDIA_CONTROLLER) + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); +#endif struct v4l2_fh *vfh = file->private_data; +#if defined(CONFIG_MEDIA_CONTROLLER) + if (sd->v4l2_dev->mdev) + media_entity_put(&sd->entity); +#endif if (vfh != NULL) { v4l2_fh_del(vfh); v4l2_fh_exit(vfh); @@ -172,5 +192,9 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) sd->grp_id = 0; sd->dev_priv = NULL; sd->host_priv = NULL; +#if defined(CONFIG_MEDIA_CONTROLLER) + sd->entity.name = sd->name; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; +#endif } EXPORT_SYMBOL(v4l2_subdev_init); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 0f9937b..6f51ce4 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -21,6 +21,7 @@ #ifndef _V4L2_SUBDEV_H #define _V4L2_SUBDEV_H +#include #include #include #include @@ -450,6 +451,9 @@ struct v4l2_subdev_internal_ops { stand-alone or embedded in a larger struct. */ struct v4l2_subdev { +#if defined(CONFIG_MEDIA_CONTROLLER) + struct media_entity entity; +#endif struct list_head list; struct module *owner; u32 flags; @@ -472,6 +476,8 @@ struct v4l2_subdev { unsigned int nevents; }; +#define media_entity_to_v4l2_subdev(ent) \ + container_of(ent, struct v4l2_subdev, entity) #define vdev_to_v4l2_subdev(vdev) \ container_of(vdev, struct v4l2_subdev, devnode) -- cgit v0.10.2 From 2ef2d5a336891ee38fee7c7ad2396e31ac9d8aaa Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 15 Mar 2010 19:33:31 -0300 Subject: [media] v4l: Move the media/v4l2-mediabus.h header to include/linux The header defines the v4l2_mbus_framefmt structure which will be used by the V4L2 subdevs userspace API. Change the type of the v4l2_mbus_framefmt::code field to __u32, as enum sizes can differ between different ABIs on the same architectures. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 31eb174..c33f22a 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -371,6 +371,7 @@ header-y += unistd.h header-y += usbdevice_fs.h header-y += utime.h header-y += utsname.h +header-y += v4l2-mediabus.h header-y += veth.h header-y += vhost.h header-y += videodev2.h diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h new file mode 100644 index 0000000..a62cd64 --- /dev/null +++ b/include/linux/v4l2-mediabus.h @@ -0,0 +1,78 @@ +/* + * Media Bus API header + * + * Copyright (C) 2009, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_V4L2_MEDIABUS_H +#define __LINUX_V4L2_MEDIABUS_H + +#include +#include + +/* + * These pixel codes uniquely identify data formats on the media bus. Mostly + * they correspond to similarly named V4L2_PIX_FMT_* formats, format 0 is + * reserved, V4L2_MBUS_FMT_FIXED shall be used by host-client pairs, where the + * data format is fixed. Additionally, "2X8" means that one pixel is transferred + * in two 8-bit samples, "BE" or "LE" specify in which order those samples are + * transferred over the bus: "LE" means that the least significant bits are + * transferred first, "BE" means that the most significant bits are transferred + * first, and "PADHI" and "PADLO" define which bits - low or high, in the + * incomplete high byte, are filled with padding bits. + */ +enum v4l2_mbus_pixelcode { + V4L2_MBUS_FMT_FIXED = 1, + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_YVYU8_2X8, + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_VYUY8_2X8, + V4L2_MBUS_FMT_YVYU10_2X10, + V4L2_MBUS_FMT_YUYV10_2X10, + V4L2_MBUS_FMT_YVYU10_1X20, + V4L2_MBUS_FMT_YUYV10_1X20, + V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, + V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, + V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, + V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, + V4L2_MBUS_FMT_RGB565_2X8_LE, + V4L2_MBUS_FMT_RGB565_2X8_BE, + V4L2_MBUS_FMT_BGR565_2X8_LE, + V4L2_MBUS_FMT_BGR565_2X8_BE, + V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_GREY8_1X8, + V4L2_MBUS_FMT_Y10_1X10, + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, + V4L2_MBUS_FMT_SGRBG8_1X8, + V4L2_MBUS_FMT_SBGGR12_1X12, + V4L2_MBUS_FMT_YUYV8_1_5X8, + V4L2_MBUS_FMT_YVYU8_1_5X8, + V4L2_MBUS_FMT_UYVY8_1_5X8, + V4L2_MBUS_FMT_VYUY8_1_5X8, +}; + +/** + * struct v4l2_mbus_framefmt - frame format on the media bus + * @width: frame width + * @height: frame height + * @code: data format code + * @field: used interlacing type + * @colorspace: colorspace of the data + */ +struct v4l2_mbus_framefmt { + __u32 width; + __u32 height; + __u32 code; + enum v4l2_field field; + enum v4l2_colorspace colorspace; +}; + +#endif diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h index f5522b3..b338108 100644 --- a/include/media/soc_mediabus.h +++ b/include/media/soc_mediabus.h @@ -12,8 +12,7 @@ #define SOC_MEDIABUS_H #include - -#include +#include /** * enum soc_mbus_packing - data packing types on the media-bus diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 8e65598..971c7fa 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -11,66 +11,7 @@ #ifndef V4L2_MEDIABUS_H #define V4L2_MEDIABUS_H -/* - * These pixel codes uniquely identify data formats on the media bus. Mostly - * they correspond to similarly named V4L2_PIX_FMT_* formats, format 0 is - * reserved, V4L2_MBUS_FMT_FIXED shall be used by host-client pairs, where the - * data format is fixed. Additionally, "2X8" means that one pixel is transferred - * in two 8-bit samples, "BE" or "LE" specify in which order those samples are - * transferred over the bus: "LE" means that the least significant bits are - * transferred first, "BE" means that the most significant bits are transferred - * first, and "PADHI" and "PADLO" define which bits - low or high, in the - * incomplete high byte, are filled with padding bits. - */ -enum v4l2_mbus_pixelcode { - V4L2_MBUS_FMT_FIXED = 1, - V4L2_MBUS_FMT_YUYV8_2X8, - V4L2_MBUS_FMT_YVYU8_2X8, - V4L2_MBUS_FMT_UYVY8_2X8, - V4L2_MBUS_FMT_VYUY8_2X8, - V4L2_MBUS_FMT_YVYU10_2X10, - V4L2_MBUS_FMT_YUYV10_2X10, - V4L2_MBUS_FMT_YVYU10_1X20, - V4L2_MBUS_FMT_YUYV10_1X20, - V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, - V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, - V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, - V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, - V4L2_MBUS_FMT_RGB565_2X8_LE, - V4L2_MBUS_FMT_RGB565_2X8_BE, - V4L2_MBUS_FMT_BGR565_2X8_LE, - V4L2_MBUS_FMT_BGR565_2X8_BE, - V4L2_MBUS_FMT_SBGGR8_1X8, - V4L2_MBUS_FMT_SBGGR10_1X10, - V4L2_MBUS_FMT_GREY8_1X8, - V4L2_MBUS_FMT_Y10_1X10, - V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, - V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, - V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, - V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, - V4L2_MBUS_FMT_SGRBG8_1X8, - V4L2_MBUS_FMT_SBGGR12_1X12, - V4L2_MBUS_FMT_YUYV8_1_5X8, - V4L2_MBUS_FMT_YVYU8_1_5X8, - V4L2_MBUS_FMT_UYVY8_1_5X8, - V4L2_MBUS_FMT_VYUY8_1_5X8, -}; - -/** - * struct v4l2_mbus_framefmt - frame format on the media bus - * @width: frame width - * @height: frame height - * @code: data format code - * @field: used interlacing type - * @colorspace: colorspace of the data - */ -struct v4l2_mbus_framefmt { - __u32 width; - __u32 height; - enum v4l2_mbus_pixelcode code; - enum v4l2_field field; - enum v4l2_colorspace colorspace; -}; +#include static inline void v4l2_fill_pix_format(struct v4l2_pix_format *pix_fmt, const struct v4l2_mbus_framefmt *mbus_fmt) -- cgit v0.10.2 From 84d0688dfc7eedb2147532bf52ff7073d5c3c7b9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 6 Oct 2010 03:30:26 -0300 Subject: [media] v4l: Replace enums with fixed-sized fields in public structure The v4l2_mbus_framefmt structure will be part of the public userspace API and used (albeit indirectly) as an ioctl argument. As such, its size must be fixed across userspace ABIs. Replace the v4l2_field and v4l2_colorspace enums by __u32 fields and add padding for future enhancements. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h index a62cd64..feeb88c 100644 --- a/include/linux/v4l2-mediabus.h +++ b/include/linux/v4l2-mediabus.h @@ -63,16 +63,17 @@ enum v4l2_mbus_pixelcode { * struct v4l2_mbus_framefmt - frame format on the media bus * @width: frame width * @height: frame height - * @code: data format code - * @field: used interlacing type - * @colorspace: colorspace of the data + * @code: data format code (from enum v4l2_mbus_pixelcode) + * @field: used interlacing type (from enum v4l2_field) + * @colorspace: colorspace of the data (from enum v4l2_colorspace) */ struct v4l2_mbus_framefmt { - __u32 width; - __u32 height; - __u32 code; - enum v4l2_field field; - enum v4l2_colorspace colorspace; + __u32 width; + __u32 height; + __u32 code; + __u32 field; + __u32 colorspace; + __u32 reserved[7]; }; #endif -- cgit v0.10.2 From 076704332ca6da550cbc279918ef8b88b4ac1e45 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 28 Sep 2010 07:01:44 -0300 Subject: [media] v4l: Rename V4L2_MBUS_FMT_GREY8_1X8 to V4L2_MBUS_FMT_Y8_1X8 For consistency with the V4L2_MBUS_FMT_Y10_1X10 format. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index f7fc88d..e2bbd8c 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -79,7 +79,7 @@ static const struct mt9m001_datafmt mt9m001_colour_fmts[] = { static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = { /* Order important - see above */ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_GREY8_1X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, }; struct mt9m001 { diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 6a784c8..e313d83 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -95,7 +95,7 @@ static const struct mt9v022_datafmt mt9v022_colour_fmts[] = { static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { /* Order important - see above */ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_GREY8_1X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, }; struct mt9v022 { @@ -392,7 +392,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, * icd->try_fmt(), datawidth is from our supported format list */ switch (mf->code) { - case V4L2_MBUS_FMT_GREY8_1X8: + case V4L2_MBUS_FMT_Y8_1X8: case V4L2_MBUS_FMT_Y10_1X10: if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) return -EINVAL; diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c index cf93de9..fe8e3eb 100644 --- a/drivers/media/video/ov6650.c +++ b/drivers/media/video/ov6650.c @@ -207,7 +207,7 @@ static enum v4l2_mbus_pixelcode ov6650_codes[] = { V4L2_MBUS_FMT_YVYU8_2X8, V4L2_MBUS_FMT_VYUY8_2X8, V4L2_MBUS_FMT_SBGGR8_1X8, - V4L2_MBUS_FMT_GREY8_1X8, + V4L2_MBUS_FMT_Y8_1X8, }; static const struct v4l2_queryctrl ov6650_controls[] = { @@ -800,7 +800,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) /* select color matrix configuration for given color encoding */ switch (code) { - case V4L2_MBUS_FMT_GREY8_1X8: + case V4L2_MBUS_FMT_Y8_1X8: dev_dbg(&client->dev, "pixel format GREY8_1X8\n"); coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP; coma_set |= COMA_BW; @@ -846,7 +846,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) } priv->code = code; - if (code == V4L2_MBUS_FMT_GREY8_1X8 || + if (code == V4L2_MBUS_FMT_Y8_1X8 || code == V4L2_MBUS_FMT_SBGGR8_1X8) { coml_mask = COML_ONE_CHANNEL; coml_set = 0; @@ -936,8 +936,8 @@ static int ov6650_try_fmt(struct v4l2_subdev *sd, switch (mf->code) { case V4L2_MBUS_FMT_Y10_1X10: - mf->code = V4L2_MBUS_FMT_GREY8_1X8; - case V4L2_MBUS_FMT_GREY8_1X8: + mf->code = V4L2_MBUS_FMT_Y8_1X8; + case V4L2_MBUS_FMT_Y8_1X8: case V4L2_MBUS_FMT_YVYU8_2X8: case V4L2_MBUS_FMT_YUYV8_2X8: case V4L2_MBUS_FMT_VYUY8_2X8: diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c index 84a6468..dd1b81b 100644 --- a/drivers/media/video/sh_mobile_csi2.c +++ b/drivers/media/video/sh_mobile_csi2.c @@ -56,7 +56,7 @@ static int sh_csi2_try_fmt(struct v4l2_subdev *sd, switch (mf->code) { case V4L2_MBUS_FMT_UYVY8_2X8: /* YUV422 */ case V4L2_MBUS_FMT_YUYV8_1_5X8: /* YUV420 */ - case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ + case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */ case V4L2_MBUS_FMT_SBGGR8_1X8: case V4L2_MBUS_FMT_SGRBG8_1X8: break; @@ -67,7 +67,7 @@ static int sh_csi2_try_fmt(struct v4l2_subdev *sd, break; case SH_CSI2I: switch (mf->code) { - case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ + case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */ case V4L2_MBUS_FMT_SBGGR8_1X8: case V4L2_MBUS_FMT_SGRBG8_1X8: case V4L2_MBUS_FMT_SBGGR10_1X10: /* RAW10 */ @@ -111,7 +111,7 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd, case V4L2_MBUS_FMT_RGB565_2X8_BE: tmp |= 0x22; /* RGB565 */ break; - case V4L2_MBUS_FMT_GREY8_1X8: + case V4L2_MBUS_FMT_Y8_1X8: case V4L2_MBUS_FMT_SBGGR8_1X8: case V4L2_MBUS_FMT_SGRBG8_1X8: tmp |= 0x2a; /* RAW8 */ diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index 73b4138..ed77aa0 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -88,7 +88,7 @@ static const struct soc_mbus_pixelfmt mbus_fmt[] = { .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(GREY8_1X8)] = { + [MBUS_IDX(Y8_1X8)] = { .fourcc = V4L2_PIX_FMT_GREY, .name = "Grey", .bits_per_sample = 8, diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h index feeb88c..dc1d5c0 100644 --- a/include/linux/v4l2-mediabus.h +++ b/include/linux/v4l2-mediabus.h @@ -45,7 +45,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_BGR565_2X8_BE, V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR10_1X10, - V4L2_MBUS_FMT_GREY8_1X8, + V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, -- cgit v0.10.2 From dacdde78b39e49edf2f7af85be4b613978280b26 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Sep 2010 12:58:22 -0300 Subject: [media] v4l: Group media bus pixel codes by types and sort them alphabetically Adding new pixel codes at the end of the enumeration will soon create a mess, so group the pixel codes by type and sort them by bus_width, bits per component, samples per pixel and order of subsamples. As the codes are part of the kernel ABI their value can't change when a new code is inserted in the enumeration, so they are given an explicit numerical value. When inserting a new pixel code developers must use and update the next free value. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h index dc1d5c0..cccfa34 100644 --- a/include/linux/v4l2-mediabus.h +++ b/include/linux/v4l2-mediabus.h @@ -24,39 +24,54 @@ * transferred first, "BE" means that the most significant bits are transferred * first, and "PADHI" and "PADLO" define which bits - low or high, in the * incomplete high byte, are filled with padding bits. + * + * The pixel codes are grouped by type, bus_width, bits per component, samples + * per pixel and order of subsamples. Numerical values are sorted using generic + * numerical sort order (8 thus comes before 10). + * + * As their value can't change when a new pixel code is inserted in the + * enumeration, the pixel codes are explicitly given a numerical value. The next + * free values for each category are listed below, update them when inserting + * new pixel codes. */ enum v4l2_mbus_pixelcode { - V4L2_MBUS_FMT_FIXED = 1, - V4L2_MBUS_FMT_YUYV8_2X8, - V4L2_MBUS_FMT_YVYU8_2X8, - V4L2_MBUS_FMT_UYVY8_2X8, - V4L2_MBUS_FMT_VYUY8_2X8, - V4L2_MBUS_FMT_YVYU10_2X10, - V4L2_MBUS_FMT_YUYV10_2X10, - V4L2_MBUS_FMT_YVYU10_1X20, - V4L2_MBUS_FMT_YUYV10_1X20, - V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, - V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, - V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, - V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, - V4L2_MBUS_FMT_RGB565_2X8_LE, - V4L2_MBUS_FMT_RGB565_2X8_BE, - V4L2_MBUS_FMT_BGR565_2X8_LE, - V4L2_MBUS_FMT_BGR565_2X8_BE, - V4L2_MBUS_FMT_SBGGR8_1X8, - V4L2_MBUS_FMT_SBGGR10_1X10, - V4L2_MBUS_FMT_Y8_1X8, - V4L2_MBUS_FMT_Y10_1X10, - V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, - V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, - V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, - V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, - V4L2_MBUS_FMT_SGRBG8_1X8, - V4L2_MBUS_FMT_SBGGR12_1X12, - V4L2_MBUS_FMT_YUYV8_1_5X8, - V4L2_MBUS_FMT_YVYU8_1_5X8, - V4L2_MBUS_FMT_UYVY8_1_5X8, - V4L2_MBUS_FMT_VYUY8_1_5X8, + V4L2_MBUS_FMT_FIXED = 0x0001, + + /* RGB - next is 0x1009 */ + V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE = 0x1001, + V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE = 0x1002, + V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE = 0x1003, + V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE = 0x1004, + V4L2_MBUS_FMT_BGR565_2X8_BE = 0x1005, + V4L2_MBUS_FMT_BGR565_2X8_LE = 0x1006, + V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007, + V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008, + + /* YUV (including grey) - next is 0x200f */ + V4L2_MBUS_FMT_Y8_1X8 = 0x2001, + V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, + V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003, + V4L2_MBUS_FMT_YUYV8_1_5X8 = 0x2004, + V4L2_MBUS_FMT_YVYU8_1_5X8 = 0x2005, + V4L2_MBUS_FMT_UYVY8_2X8 = 0x2006, + V4L2_MBUS_FMT_VYUY8_2X8 = 0x2007, + V4L2_MBUS_FMT_YUYV8_2X8 = 0x2008, + V4L2_MBUS_FMT_YVYU8_2X8 = 0x2009, + V4L2_MBUS_FMT_Y10_1X10 = 0x200a, + V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b, + V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c, + V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, + V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, + + /* Bayer - next is 0x3009 */ + V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, + V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE = 0x3003, + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE = 0x3004, + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE = 0x3005, + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE = 0x3006, + V4L2_MBUS_FMT_SBGGR10_1X10 = 0x3007, + V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008, }; /** -- cgit v0.10.2 From 7cd5a16b22af7dc92190a60f336b6854a6fcb99d Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 21 May 2010 06:04:24 -0300 Subject: [media] v4l: Create v4l2 subdev file handle structure Used for storing subdev information per file handle and hold V4L2 file handle. Signed-off-by: Stanimir Varbanov Signed-off-by: Antti Koskipaa Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 2466f2b..6995940 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -53,6 +53,15 @@ config VIDEO_V4L2_COMMON depends on (I2C || I2C=n) && VIDEO_DEV default (I2C || I2C=n) && VIDEO_DEV +config VIDEO_V4L2_SUBDEV_API + bool "V4L2 sub-device userspace API (EXPERIMENTAL)" + depends on VIDEO_DEV && MEDIA_CONTROLLER && EXPERIMENTAL + ---help--- + Enables the V4L2 sub-device pad-level userspace API used to configure + video format, size and frame rate between hardware blocks. + + This API is mostly used by camera interfaces in embedded platforms. + # # DVB Core # diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 8557363..6cef6ad 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -31,36 +31,66 @@ #include #include +static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) +{ +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + /* Allocate try format and crop in the same memory block */ + fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) + * sd->entity.num_pads, GFP_KERNEL); + if (fh->try_fmt == NULL) + return -ENOMEM; + + fh->try_crop = (struct v4l2_rect *) + (fh->try_fmt + sd->entity.num_pads); +#endif + return 0; +} + +static void subdev_fh_free(struct v4l2_subdev_fh *fh) +{ +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + kfree(fh->try_fmt); + fh->try_fmt = NULL; + fh->try_crop = NULL; +#endif +} + static int subdev_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct v4l2_subdev_fh *subdev_fh; #if defined(CONFIG_MEDIA_CONTROLLER) struct media_entity *entity; #endif - struct v4l2_fh *vfh = NULL; int ret; - if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { - vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); - if (vfh == NULL) - return -ENOMEM; + subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL); + if (subdev_fh == NULL) + return -ENOMEM; - ret = v4l2_fh_init(vfh, vdev); - if (ret) - goto err; + ret = subdev_fh_init(subdev_fh, sd); + if (ret) { + kfree(subdev_fh); + return ret; + } + + ret = v4l2_fh_init(&subdev_fh->vfh, vdev); + if (ret) + goto err; - ret = v4l2_event_init(vfh); + if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { + ret = v4l2_event_init(&subdev_fh->vfh); if (ret) goto err; - ret = v4l2_event_alloc(vfh, sd->nevents); + ret = v4l2_event_alloc(&subdev_fh->vfh, sd->nevents); if (ret) goto err; - - v4l2_fh_add(vfh); - file->private_data = vfh; } + + v4l2_fh_add(&subdev_fh->vfh); + file->private_data = &subdev_fh->vfh; #if defined(CONFIG_MEDIA_CONTROLLER) if (sd->v4l2_dev->mdev) { entity = media_entity_get(&sd->entity); @@ -70,14 +100,14 @@ static int subdev_open(struct file *file) } } #endif + return 0; err: - if (vfh != NULL) { - v4l2_fh_del(vfh); - v4l2_fh_exit(vfh); - kfree(vfh); - } + v4l2_fh_del(&subdev_fh->vfh); + v4l2_fh_exit(&subdev_fh->vfh); + subdev_fh_free(subdev_fh); + kfree(subdev_fh); return ret; } @@ -89,16 +119,17 @@ static int subdev_close(struct file *file) struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); #endif struct v4l2_fh *vfh = file->private_data; + struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); #if defined(CONFIG_MEDIA_CONTROLLER) if (sd->v4l2_dev->mdev) media_entity_put(&sd->entity); #endif - if (vfh != NULL) { - v4l2_fh_del(vfh); - v4l2_fh_exit(vfh); - kfree(vfh); - } + v4l2_fh_del(vfh); + v4l2_fh_exit(vfh); + subdev_fh_free(subdev_fh); + kfree(subdev_fh); + file->private_data = NULL; return 0; } @@ -107,7 +138,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); - struct v4l2_fh *fh = file->private_data; + struct v4l2_fh *vfh = file->private_data; switch (cmd) { case VIDIOC_QUERYCTRL: @@ -135,13 +166,13 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) return -ENOIOCTLCMD; - return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK); + return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK); case VIDIOC_SUBSCRIBE_EVENT: - return v4l2_subdev_call(sd, core, subscribe_event, fh, arg); + return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg); case VIDIOC_UNSUBSCRIBE_EVENT: - return v4l2_subdev_call(sd, core, unsubscribe_event, fh, arg); + return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); default: return -ENOIOCTLCMD; diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 6f51ce4..72f49eb 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -24,6 +24,7 @@ #include #include #include +#include #include /* generic v4l2_device notify callback notification values */ @@ -481,6 +482,34 @@ struct v4l2_subdev { #define vdev_to_v4l2_subdev(vdev) \ container_of(vdev, struct v4l2_subdev, devnode) +/* + * Used for storing subdev information per file handle + */ +struct v4l2_subdev_fh { + struct v4l2_fh vfh; +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + struct v4l2_mbus_framefmt *try_fmt; + struct v4l2_rect *try_crop; +#endif +}; + +#define to_v4l2_subdev_fh(fh) \ + container_of(fh, struct v4l2_subdev_fh, vfh) + +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) +static inline struct v4l2_mbus_framefmt * +v4l2_subdev_get_try_format(struct v4l2_subdev_fh *fh, unsigned int pad) +{ + return &fh->try_fmt[pad]; +} + +static inline struct v4l2_rect * +v4l2_subdev_get_try_crop(struct v4l2_subdev_fh *fh, unsigned int pad) +{ + return &fh->try_crop[pad]; +} +#endif + extern const struct v4l2_file_operations v4l2_subdev_fops; static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) -- cgit v0.10.2 From f0beea8f46142ea7abd7ed3f44fd0967c603fae0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Aug 2010 19:05:09 -0300 Subject: [media] v4l: subdev: Add new file operations V4L2 sub-devices store pad formats and crop settings in the file handle. To let drivers initialize those settings properly, add an open operation that is called when the subdev is opened as well as a corresponding close operation. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 6cef6ad..bc763db 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -61,7 +61,7 @@ static int subdev_open(struct file *file) struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); struct v4l2_subdev_fh *subdev_fh; #if defined(CONFIG_MEDIA_CONTROLLER) - struct media_entity *entity; + struct media_entity *entity = NULL; #endif int ret; @@ -101,9 +101,19 @@ static int subdev_open(struct file *file) } #endif + if (sd->internal_ops && sd->internal_ops->open) { + ret = sd->internal_ops->open(sd, subdev_fh); + if (ret < 0) + goto err; + } + return 0; err: +#if defined(CONFIG_MEDIA_CONTROLLER) + if (entity) + media_entity_put(entity); +#endif v4l2_fh_del(&subdev_fh->vfh); v4l2_fh_exit(&subdev_fh->vfh); subdev_fh_free(subdev_fh); @@ -114,13 +124,13 @@ err: static int subdev_close(struct file *file) { -#if defined(CONFIG_MEDIA_CONTROLLER) struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); -#endif struct v4l2_fh *vfh = file->private_data; struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); + if (sd->internal_ops && sd->internal_ops->close) + sd->internal_ops->close(sd, subdev_fh); #if defined(CONFIG_MEDIA_CONTROLLER) if (sd->v4l2_dev->mdev) media_entity_put(&sd->entity); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 72f49eb..f5dddac 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -42,6 +42,7 @@ struct v4l2_ctrl_handler; struct v4l2_event_subscription; struct v4l2_fh; struct v4l2_subdev; +struct v4l2_subdev_fh; struct tuner_setup; /* decode_vbi_line */ @@ -431,10 +432,16 @@ struct v4l2_subdev_ops { * * unregistered: called when this subdev is unregistered. When called the * v4l2_dev field is still set to the correct v4l2_device. + * + * open: called when the subdev device node is opened by an application. + * + * close: called when the subdev device node is closed. */ struct v4l2_subdev_internal_ops { int (*registered)(struct v4l2_subdev *sd); void (*unregistered)(struct v4l2_subdev *sd); + int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); + int (*close)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); }; #define V4L2_SUBDEV_NAME_SIZE 32 -- cgit v0.10.2 From eb08f35480869f7a82e9be1f19bc4575452e7f98 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 08:39:52 -0300 Subject: [media] v4l: v4l2_subdev pad-level operations Add a v4l2_subdev_pad_ops structure for the operations that need to be performed at the pad level such as format-related operations. Pad format-related operations use v4l2_mbus_framefmt instead of v4l2_format. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f5dddac..d4d3653 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -413,6 +413,9 @@ struct v4l2_subdev_ir_ops { struct v4l2_subdev_ir_parameters *params); }; +struct v4l2_subdev_pad_ops { +}; + struct v4l2_subdev_ops { const struct v4l2_subdev_core_ops *core; const struct v4l2_subdev_tuner_ops *tuner; @@ -421,6 +424,7 @@ struct v4l2_subdev_ops { const struct v4l2_subdev_vbi_ops *vbi; const struct v4l2_subdev_ir_ops *ir; const struct v4l2_subdev_sensor_ops *sensor; + const struct v4l2_subdev_pad_ops *pad; }; /* -- cgit v0.10.2 From d3a7ed99759b18e4fd19b8ddd226f8085b8bd975 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Mar 2010 00:26:04 +0100 Subject: [media] v4l: v4l2_subdev userspace format API - documentation binary files Add images used by the V4L2 subdev userspace format API documentation. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/bayer.pdf b/Documentation/DocBook/v4l/bayer.pdf new file mode 100644 index 0000000..905e60e Binary files /dev/null and b/Documentation/DocBook/v4l/bayer.pdf differ diff --git a/Documentation/DocBook/v4l/bayer.png b/Documentation/DocBook/v4l/bayer.png new file mode 100644 index 0000000..9b15fb2 Binary files /dev/null and b/Documentation/DocBook/v4l/bayer.png differ diff --git a/Documentation/DocBook/v4l/pipeline.pdf b/Documentation/DocBook/v4l/pipeline.pdf new file mode 100644 index 0000000..ee3e37f Binary files /dev/null and b/Documentation/DocBook/v4l/pipeline.pdf differ diff --git a/Documentation/DocBook/v4l/pipeline.png b/Documentation/DocBook/v4l/pipeline.png new file mode 100644 index 0000000..f19b86c Binary files /dev/null and b/Documentation/DocBook/v4l/pipeline.png differ -- cgit v0.10.2 From 333c8b97785d5afd5085ba3720b4d259623290f6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 15 Mar 2010 20:26:04 -0300 Subject: [media] v4l: v4l2_subdev userspace format API Add a userspace API to get, set and enumerate the media format on a subdev pad. The format at the output of a subdev usually depends on the format at its input(s). The try format operation is thus not suitable for probing format at individual pads, as it can't modify the device state and thus can't remember the format tried at the input to compute the output format. To fix the problem, pass an extra argument to the get/set format operations to select the 'try' or 'active' format. The try format is used when probing the subdev. Setting the try format must not change the device configuration but can store data for later reuse. Data storage is provided at the file-handle level so applications probing the subdev concurently won't interfere with each other. The active format is used when configuring the subdev. It's identical to the format handled by the usual get/set operations. Signed-off-by: Laurent Pinchart Signed-off-by: Stanimir Varbanov Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 8b6e00a..2deb069 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -53,7 +53,10 @@ MAN := $(patsubst %.xml, %.9, $(BOOKS)) mandocs: $(MAN) build_images = mkdir -p $(objtree)/Documentation/DocBook/media/ && \ - cp $(srctree)/Documentation/DocBook/dvb/*.png $(srctree)/Documentation/DocBook/v4l/*.gif $(objtree)/Documentation/DocBook/media/ + cp $(srctree)/Documentation/DocBook/dvb/*.png \ + $(srctree)/Documentation/DocBook/v4l/*.gif \ + $(srctree)/Documentation/DocBook/v4l/*.png \ + $(objtree)/Documentation/DocBook/media/ xmldoclinks: ifneq ($(objtree),$(srctree)) diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 121db15..dbb9cb2 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -86,6 +86,10 @@ VIDIOC_S_PRIORITY"> VIDIOC_S_STD"> VIDIOC_S_TUNER"> +VIDIOC_SUBDEV_ENUM_FRAME_SIZE"> +VIDIOC_SUBDEV_ENUM_MBUS_CODE"> +VIDIOC_SUBDEV_G_FMT"> +VIDIOC_SUBDEV_S_FMT"> VIDIOC_TRY_ENCODER_CMD"> VIDIOC_TRY_EXT_CTRLS"> VIDIOC_TRY_FMT"> @@ -107,6 +111,7 @@ v4l2_field"> v4l2_frmivaltypes"> v4l2_frmsizetypes"> +v4l2_mbus_pixelcode"> v4l2_memory"> v4l2_mpeg_audio_ac3_bitrate"> v4l2_mpeg_audio_crc"> @@ -130,6 +135,7 @@ v4l2_mpeg_video_encoding"> v4l2_power_line_frequency"> v4l2_priority"> +v4l2_subdev_format_whence"> v4l2_tuner_type"> v4l2_preemphasis"> @@ -172,6 +178,7 @@ v4l2_hw_freq_seek"> v4l2_input"> v4l2_jpegcompression"> +v4l2_mbus_framefmt"> v4l2_modulator"> v4l2_mpeg_vbi_fmt_ivtv"> v4l2_output"> @@ -186,6 +193,9 @@ v4l2_sliced_vbi_cap"> v4l2_sliced_vbi_data"> v4l2_sliced_vbi_format"> +v4l2_subdev_frame_size_enum"> +v4l2_subdev_format"> +v4l2_subdev_mbus_code_enum"> v4l2_standard"> v4l2_streamparm"> v4l2_timecode"> @@ -215,6 +225,7 @@ ENXIO error code"> EMFILE error code"> EPERM error code"> +EPIPE error code"> ERANGE error code"> @@ -234,6 +245,7 @@ + @@ -319,6 +331,10 @@ + + + + diff --git a/Documentation/DocBook/v4l/dev-subdev.xml b/Documentation/DocBook/v4l/dev-subdev.xml new file mode 100644 index 0000000..fc62e65 --- /dev/null +++ b/Documentation/DocBook/v4l/dev-subdev.xml @@ -0,0 +1,280 @@ + Sub-device Interface + + + Experimental + This is an experimental + interface and may change in the future. + + + The complex nature of V4L2 devices, where hardware is often made of + several integrated circuits that need to interact with each other in a + controlled way, leads to complex V4L2 drivers. The drivers usually reflect + the hardware model in software, and model the different hardware components + as software blocks called sub-devices. + + V4L2 sub-devices are usually kernel-only objects. If the V4L2 driver + implements the media device API, they will automatically inherit from media + entities. Applications will be able to enumerate the sub-devices and discover + the hardware topology using the media entities, pads and links enumeration + API. + + In addition to make sub-devices discoverable, drivers can also choose + to make them directly configurable by applications. When both the sub-device + driver and the V4L2 device driver support this, sub-devices will feature a + character device node on which ioctls can be called to + + query, read and write sub-devices controls + subscribe and unsubscribe to events and retrieve them + negotiate image formats on individual pads + + + + Sub-device character device nodes, conventionally named + /dev/v4l-subdev*, use major number 81. + +
+ Controls + Most V4L2 controls are implemented by sub-device hardware. Drivers + usually merge all controls and expose them through video device nodes. + Applications can control all sub-devices through a single interface. + + Complex devices sometimes implement the same control in different + pieces of hardware. This situation is common in embedded platforms, where + both sensors and image processing hardware implement identical functions, + such as contrast adjustment, white balance or faulty pixels correction. As + the V4L2 controls API doesn't support several identical controls in a single + device, all but one of the identical controls are hidden. + + Applications can access those hidden controls through the sub-device + node with the V4L2 control API described in . The + ioctls behave identically as when issued on V4L2 device nodes, with the + exception that they deal only with controls implemented in the sub-device. + + + Depending on the driver, those controls might also be exposed through + one (or several) V4L2 device nodes. +
+ +
+ Events + V4L2 sub-devices can notify applications of events as described in + . The API behaves identically as when used on V4L2 + device nodes, with the exception that it only deals with events generated by + the sub-device. Depending on the driver, those events might also be reported + on one (or several) V4L2 device nodes. +
+ +
+ Pad-level Formats + + Pad-level formats are only applicable to very complex device that + need to expose low-level format configuration to user space. Generic V4L2 + applications do not need to use the API described in + this section. + + For the purpose of this section, the term + format means the combination of media bus data + format, frame width and frame height. + + Image formats are typically negotiated on video capture and output + devices using the cropping and scaling ioctls. + The driver is responsible for configuring every block in the video pipeline + according to the requested format at the pipeline input and/or + output. + + For complex devices, such as often found in embedded systems, + identical image sizes at the output of a pipeline can be achieved using + different hardware configurations. One such exemple is shown on + , where + image scaling can be performed on both the video sensor and the host image + processing hardware. + +
+ Image Format Negotation on Pipelines + + + + + + + + + High quality and high speed pipeline configuration + + +
+ + The sensor scaler is usually of less quality than the host scaler, but + scaling on the sensor is required to achieve higher frame rates. Depending + on the use case (quality vs. speed), the pipeline must be configured + differently. Applications need to configure the formats at every point in + the pipeline explicitly. + + Drivers that implement the media + API can expose pad-level image format configuration to applications. + When they do, applications can use the &VIDIOC-SUBDEV-G-FMT; and + &VIDIOC-SUBDEV-S-FMT; ioctls. to negotiate formats on a per-pad basis. + + Applications are responsible for configuring coherent parameters on + the whole pipeline and making sure that connected pads have compatible + formats. The pipeline is checked for formats mismatch at &VIDIOC-STREAMON; + time, and an &EPIPE; is then returned if the configuration is + invalid. + + Pad-level image format configuration support can be tested by calling + the &VIDIOC-SUBDEV-G-FMT; ioctl on pad 0. If the driver returns an &EINVAL; + pad-level format configuration is not supported by the sub-device. + +
+ Format Negotiation + + Acceptable formats on pads can (and usually do) depend on a number + of external parameters, such as formats on other pads, active links, or + even controls. Finding a combination of formats on all pads in a video + pipeline, acceptable to both application and driver, can't rely on formats + enumeration only. A format negotiation mechanism is required. + + Central to the format negotiation mechanism are the get/set format + operations. When called with the which argument + set to V4L2_SUBDEV_FORMAT_TRY, the + &VIDIOC-SUBDEV-G-FMT; and &VIDIOC-SUBDEV-S-FMT; ioctls operate on a set of + formats parameters that are not connected to the hardware configuration. + Modifying those 'try' formats leaves the device state untouched (this + applies to both the software state stored in the driver and the hardware + state stored in the device itself). + + While not kept as part of the device state, try formats are stored + in the sub-device file handles. A &VIDIOC-SUBDEV-G-FMT; call will return + the last try format set on the same sub-device file + handle. Several applications querying the same sub-device at + the same time will thus not interact with each other. + + To find out whether a particular format is supported by the device, + applications use the &VIDIOC-SUBDEV-S-FMT; ioctl. Drivers verify and, if + needed, change the requested format based on + device requirements and return the possibly modified value. Applications + can then choose to try a different format or accept the returned value and + continue. + + Formats returned by the driver during a negotiation iteration are + guaranteed to be supported by the device. In particular, drivers guarantee + that a returned format will not be further changed if passed to an + &VIDIOC-SUBDEV-S-FMT; call as-is (as long as external parameters, such as + formats on other pads or links' configuration are not changed). + + Drivers automatically propagate formats inside sub-devices. When a + try or active format is set on a pad, corresponding formats on other pads + of the same sub-device can be modified by the driver. Drivers are free to + modify formats as required by the device. However, they should comply with + the following rules when possible: + + Formats should be propagated from sink pads to source pads. + Modifying a format on a source pad should not modify the format on any + sink pad. + Sub-devices that scale frames using variable scaling factors + should reset the scale factors to default values when sink pads formats + are modified. If the 1:1 scaling ratio is supported, this means that + source pads formats should be reset to the sink pads formats. + + + + Formats are not propagated across links, as that would involve + propagating them from one sub-device file handle to another. Applications + must then take care to configure both ends of every link explicitly with + compatible formats. Identical formats on the two ends of a link are + guaranteed to be compatible. Drivers are free to accept different formats + matching device requirements as being compatible. + + + shows a sample configuration sequence for the pipeline described in + (table + columns list entity names and pad numbers). + + + Sample Pipeline Configuration + + + + + + + + + + + Sensor/0 + Frontend/0 + Frontend/1 + Scaler/0 + Scaler/1 + + + + + Initial state + 2048x1536 + - + - + - + - + + + Configure frontend input + 2048x1536 + 2048x1536 + 2046x1534 + - + - + + + Configure scaler input + 2048x1536 + 2048x1536 + 2046x1534 + 2046x1534 + 2046x1534 + + + Configure scaler output + 2048x1536 + 2048x1536 + 2046x1534 + 2046x1534 + 1280x960 + + + +
+ + + + Initial state. The sensor output is set to its native 3MP + resolution. Resolutions on the host frontend and scaler input and output + pads are undefined. + The application configures the frontend input pad resolution to + 2048x1536. The driver propagates the format to the frontend output pad. + Note that the propagated output format can be different, as in this case, + than the input format, as the hardware might need to crop pixels (for + instance when converting a Bayer filter pattern to RGB or YUV). + The application configures the scaler input pad resolution to + 2046x1534 to match the frontend output resolution. The driver propagates + the format to the scaler output pad. + The application configures the scaler output pad resolution to + 1280x960. + + + + When satisfied with the try results, applications can set the active + formats by setting the which argument to + V4L2_SUBDEV_FORMAT_TRY. Active formats are changed + exactly as try formats by drivers. To avoid modifying the hardware state + during format negotiation, applications should negotiate try formats first + and then modify the active settings using the try formats returned during + the last negotiation iteration. This guarantees that the active format + will be applied as-is by the driver without being modified. + +
+ +
+ + &sub-subdev-formats; diff --git a/Documentation/DocBook/v4l/subdev-formats.xml b/Documentation/DocBook/v4l/subdev-formats.xml new file mode 100644 index 0000000..0cae572 --- /dev/null +++ b/Documentation/DocBook/v4l/subdev-formats.xml @@ -0,0 +1,2416 @@ +
+ Media Bus Formats + + + struct <structname>v4l2_mbus_framefmt</structname> + + &cs-str; + + + __u32 + width + Image width, in pixels. + + + __u32 + height + Image height, in pixels. + + + __u32 + code + Format code, from &v4l2-mbus-pixelcode;. + + + __u32 + field + Field order, from &v4l2-field;. See + for details. + + + __u32 + colorspace + Image colorspace, from &v4l2-colorspace;. See + for details. + + + __u32 + reserved[7] + Reserved for future extensions. Applications and drivers must + set the array to zero. + + + +
+ +
+ Media Bus Pixel Codes + + The media bus pixel codes describe image formats as flowing over + physical busses (both between separate physical components and inside SoC + devices). This should not be confused with the V4L2 pixel formats that + describe, using four character codes, image formats as stored in memory. + + + While there is a relationship between image formats on busses and + image formats in memory (a raw Bayer image won't be magically converted to + JPEG just by storing it to memory), there is no one-to-one correspondance + between them. + +
+ Packed RGB Formats + + Those formats transfer pixel data as red, green and blue components. + The format code is made of the following information. + + The red, green and blue components order code, as encoded in a + pixel sample. Possible values are RGB and BGR. + The number of bits per component, for each component. The values + can be different for all components. Common values are 555 and 565. + + The number of bus samples per pixel. Pixels that are wider than + the bus width must be transferred in multiple samples. Common values are + 1 and 2. + The bus width. + For formats where the total number of bits per pixel is smaller + than the number of bus samples per pixel times the bus width, a padding + value stating if the bytes are padded in their most high order bits + (PADHI) or low order bits (PADLO). + For formats where the number of bus samples per pixel is larger + than 1, an endianness value stating if the pixel is transferred MSB first + (BE) or LSB first (LE). + + + + For instance, a format where pixels are encoded as 5-bits red, 5-bits + green and 5-bit blue values padded on the high bit, transferred as 2 8-bit + samples per pixel with the most significant bits (padding, red and half of + the green value) transferred first will be named + V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE. + + + The following tables list existing packet RGB formats. + + + RGB formats + + + + + + + + + + + + + + + + Identifier + Code + + Data organization + + + + + Bit + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + + + + + V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE + 0x1001 + + 0 + 0 + 0 + 0 + r3 + r2 + r1 + r0 + + + + + + g3 + g2 + g1 + g0 + b3 + b2 + b1 + b0 + + + V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE + 0x1002 + + g3 + g2 + g1 + g0 + b3 + b2 + b1 + b0 + + + + + + 0 + 0 + 0 + 0 + r3 + r2 + r1 + r0 + + + V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE + 0x1003 + + 0 + r4 + r3 + r2 + r1 + r0 + g4 + g3 + + + + + + g2 + g1 + g0 + b4 + b3 + b2 + b1 + b0 + + + V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE + 0x1004 + + g2 + g1 + g0 + b4 + b3 + b2 + b1 + b0 + + + + + + 0 + r4 + r3 + r2 + r1 + r0 + g4 + g3 + + + V4L2_MBUS_FMT_BGR565_2X8_BE + 0x1005 + + b4 + b3 + b2 + b1 + b0 + g5 + g4 + g3 + + + + + + g2 + g1 + g0 + r4 + r3 + r2 + r1 + r0 + + + V4L2_MBUS_FMT_BGR565_2X8_LE + 0x1006 + + g2 + g1 + g0 + r4 + r3 + r2 + r1 + r0 + + + + + + b4 + b3 + b2 + b1 + b0 + g5 + g4 + g3 + + + V4L2_MBUS_FMT_RGB565_2X8_BE + 0x1007 + + r4 + r3 + r2 + r1 + r0 + g5 + g4 + g3 + + + + + + g2 + g1 + g0 + b4 + b3 + b2 + b1 + b0 + + + V4L2_MBUS_FMT_RGB565_2X8_LE + 0x1008 + + g2 + g1 + g0 + b4 + b3 + b2 + b1 + b0 + + + + + + r4 + r3 + r2 + r1 + r0 + g5 + g4 + g3 + + + +
+
+ +
+ Bayer Formats + + Those formats transfer pixel data as red, green and blue components. + The format code is made of the following information. + + The red, green and blue components order code, as encoded in a + pixel sample. The possible values are shown in . + The number of bits per pixel component. All components are + transferred on the same number of bits. Common values are 8, 10 and 12. + + If the pixel components are DPCM-compressed, a mention of the + DPCM compression and the number of bits per compressed pixel component. + + The number of bus samples per pixel. Pixels that are wider than + the bus width must be transferred in multiple samples. Common values are + 1 and 2. + The bus width. + For formats where the total number of bits per pixel is smaller + than the number of bus samples per pixel times the bus width, a padding + value stating if the bytes are padded in their most high order bits + (PADHI) or low order bits (PADLO). + For formats where the number of bus samples per pixel is larger + than 1, an endianness value stating if the pixel is transferred MSB first + (BE) or LSB first (LE). + + + + For instance, a format with uncompressed 10-bit Bayer components + arranged in a red, green, green, blue pattern transferred as 2 8-bit + samples per pixel with the least significant bits transferred first will + be named V4L2_MBUS_FMT_SRGGB10_2X8_PADHI_LE. + + +
+ Bayer Patterns + + + + + + + + + Bayer filter color patterns + + +
+ + The following table lists existing packet Bayer formats. The data + organization is given as an example for the first pixel only. + + + Bayer Formats + + + + + + + + + + + + + + + + + + + + Identifier + Code + + Data organization + + + + + Bit + 11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + + + + + V4L2_MBUS_FMT_SBGGR8_1X8 + 0x3001 + + - + - + - + - + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + + V4L2_MBUS_FMT_SGRBG8_1X8 + 0x3002 + + - + - + - + - + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + + V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 + 0x300b + + - + - + - + - + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + + V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 + 0x300c + + - + - + - + - + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 + 0x3009 + + - + - + - + - + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + + V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 + 0x300d + + - + - + - + - + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE + 0x3003 + + - + - + - + - + 0 + 0 + 0 + 0 + 0 + 0 + b9 + b8 + + + + + + - + - + - + - + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE + 0x3004 + + - + - + - + - + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + + + + + - + - + - + - + 0 + 0 + 0 + 0 + 0 + 0 + b9 + b8 + + + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE + 0x3005 + + - + - + - + - + b9 + b8 + b7 + b6 + b5 + b4 + b3 + b2 + + + + + + - + - + - + - + b1 + b0 + 0 + 0 + 0 + 0 + 0 + 0 + + + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE + 0x3006 + + - + - + - + - + b1 + b0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + - + - + - + - + b9 + b8 + b7 + b6 + b5 + b4 + b3 + b2 + + + V4L2_MBUS_FMT_SBGGR10_1X10 + 0x3007 + + - + - + b9 + b8 + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + + V4L2_MBUS_FMT_SGBRG10_1X10 + 0x300e + + - + - + g9 + g8 + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + + V4L2_MBUS_FMT_SGRBG10_1X10 + 0x300a + + - + - + g9 + g8 + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + + V4L2_MBUS_FMT_SRGGB10_1X10 + 0x300f + + - + - + r9 + r8 + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + + V4L2_MBUS_FMT_SBGGR12_1X12 + 0x3008 + + b11 + b10 + b9 + b8 + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + + +
+
+ +
+ Packed YUV Formats + + Those data formats transfer pixel data as (possibly downsampled) Y, U + and V components. The format code is made of the following information. + + The Y, U and V components order code, as transferred on the + bus. Possible values are YUYV, UYVY, YVYU and VYUY. + The number of bits per pixel component. All components are + transferred on the same number of bits. Common values are 8, 10 and 12. + + The number of bus samples per pixel. Pixels that are wider than + the bus width must be transferred in multiple samples. Common values are + 1, 1.5 (encoded as 1_5) and 2. + The bus width. When the bus width is larger than the number of + bits per pixel component, several components are packed in a single bus + sample. The components are ordered as specified by the order code, with + components on the left of the code transferred in the high order bits. + Common values are 8 and 16. + + + + + For instance, a format where pixels are encoded as 8-bit YUV values + downsampled to 4:2:2 and transferred as 2 8-bit bus samples per pixel in the + U, Y, V, Y order will be named V4L2_MBUS_FMT_UYVY8_2X8. + + + The following table lisst existing packet YUV formats. + + + YUV Formats + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier + Code + + Data organization + + + + + Bit + 19 + 18 + 17 + 16 + 15 + 14 + 13 + 12 + 11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + + + + + V4L2_MBUS_FMT_Y8_1X8 + 0x2001 + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_UYVY8_1_5X8 + 0x2002 + + - + - + - + - + - + - + - + - + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY8_1_5X8 + 0x2003 + + - + - + - + - + - + - + - + - + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_YUYV8_1_5X8 + 0x2004 + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + V4L2_MBUS_FMT_YVYU8_1_5X8 + 0x2005 + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + V4L2_MBUS_FMT_UYVY8_2X8 + 0x2006 + + - + - + - + - + - + - + - + - + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY8_2X8 + 0x2007 + + - + - + - + - + - + - + - + - + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_YUYV8_2X8 + 0x2008 + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + V4L2_MBUS_FMT_YVYU8_2X8 + 0x2009 + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + V4L2_MBUS_FMT_Y10_1X10 + 0x200a + + - + - + - + - + - + - + - + - + - + - + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_YUYV10_2X10 + 0x200b + + - + - + - + - + - + - + - + - + - + - + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + - + - + - + - + - + - + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + V4L2_MBUS_FMT_YVYU10_2X10 + 0x200c + + - + - + - + - + - + - + - + - + - + - + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + - + - + - + - + - + - + - + - + - + - + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + - + - + - + - + - + - + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + V4L2_MBUS_FMT_UYVY8_1X16 + 0x200f + + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY8_1X16 + 0x2010 + + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_YUYV8_1X16 + 0x2011 + + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + V4L2_MBUS_FMT_YVYU8_1X16 + 0x2012 + + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + V4L2_MBUS_FMT_YUYV10_1X20 + 0x200d + + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + V4L2_MBUS_FMT_YVYU10_1X20 + 0x200e + + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + +
+
+
+
diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml index 7859d7d..2ada2f8 100644 --- a/Documentation/DocBook/v4l/v4l2.xml +++ b/Documentation/DocBook/v4l/v4l2.xml @@ -439,6 +439,7 @@ and discussions on the V4L mailing list.
&sub-dev-radio;
&sub-dev-rds;
&sub-dev-event;
+
&sub-dev-subdev;
@@ -506,6 +507,9 @@ and discussions on the V4L mailing list. &sub-reqbufs; &sub-s-hw-freq-seek; &sub-streamon; + &sub-subdev-enum-frame-size; + &sub-subdev-enum-mbus-code; + &sub-subdev-g-fmt; &sub-subscribe-event; &sub-mmap; diff --git a/Documentation/DocBook/v4l/vidioc-streamon.xml b/Documentation/DocBook/v4l/vidioc-streamon.xml index e42bff1..75ed39b 100644 --- a/Documentation/DocBook/v4l/vidioc-streamon.xml +++ b/Documentation/DocBook/v4l/vidioc-streamon.xml @@ -93,6 +93,15 @@ synchronize with other events. been allocated (memory mapping) or enqueued (output) yet.
+ + EPIPE + + The driver implements pad-level format configuration and + the pipeline configuration is invalid. + + + diff --git a/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-size.xml b/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-size.xml new file mode 100644 index 0000000..79ce42b --- /dev/null +++ b/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-size.xml @@ -0,0 +1,154 @@ + + + ioctl VIDIOC_SUBDEV_ENUM_FRAME_SIZE + &manvol; + + + + VIDIOC_SUBDEV_ENUM_FRAME_SIZE + Enumerate media bus frame sizes + + + + + + int ioctl + int fd + int request + struct v4l2_subdev_frame_size_enum * + argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_SUBDEV_ENUM_FRAME_SIZE + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + This ioctl allows applications to enumerate all frame sizes + supported by a sub-device on the given pad for the given media bus format. + Supported formats can be retrieved with the &VIDIOC-SUBDEV-ENUM-MBUS-CODE; + ioctl. + + To enumerate frame sizes applications initialize the + pad, code and + index fields of the + &v4l2-subdev-mbus-code-enum; and call the + VIDIOC_SUBDEV_ENUM_FRAME_SIZE ioctl with a pointer to + the structure. Drivers fill the minimum and maximum frame sizes or return + an &EINVAL; if one of the input parameters is invalid. + + Sub-devices that only support discrete frame sizes (such as most + sensors) will return one or more frame sizes with identical minimum and + maximum values. + + Not all possible sizes in given [minimum, maximum] ranges need to be + supported. For instance, a scaler that uses a fixed-point scaling ratio + might not be able to produce every frame size between the minimum and + maximum values. Applications must use the &VIDIOC-SUBDEV-S-FMT; ioctl to + try the sub-device for an exact supported frame size. + + Available frame sizes may depend on the current 'try' formats at other + pads of the sub-device, as well as on the current active links and the + current values of V4L2 controls. See &VIDIOC-SUBDEV-G-FMT; for more + information about try formats. + + + struct <structname>v4l2_subdev_frame_size_enum</structname> + + &cs-str; + + + __u32 + index + Number of the format in the enumeration, set by the + application. + + + __u32 + pad + Pad number as reported by the media controller API. + + + __u32 + code + The media bus format code, as defined in + . + + + __u32 + min_width + Minimum frame width, in pixels. + + + __u32 + max_width + Maximum frame width, in pixels. + + + __u32 + min_height + Minimum frame height, in pixels. + + + __u32 + max_height + Maximum frame height, in pixels. + + + __u32 + reserved[9] + Reserved for future extensions. Applications and drivers must + set the array to zero. + + + +
+
+ + + &return-value; + + + + EINVAL + + The &v4l2-subdev-frame-size-enum; pad + references a non-existing pad, the code is + invalid for the given pad or the index + field is out of bounds. + + + + +
diff --git a/Documentation/DocBook/v4l/vidioc-subdev-enum-mbus-code.xml b/Documentation/DocBook/v4l/vidioc-subdev-enum-mbus-code.xml new file mode 100644 index 0000000..a6b3432 --- /dev/null +++ b/Documentation/DocBook/v4l/vidioc-subdev-enum-mbus-code.xml @@ -0,0 +1,119 @@ + + + ioctl VIDIOC_SUBDEV_ENUM_MBUS_CODE + &manvol; + + + + VIDIOC_SUBDEV_ENUM_MBUS_CODE + Enumerate media bus formats + + + + + + int ioctl + int fd + int request + struct v4l2_subdev_mbus_code_enum * + argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_SUBDEV_ENUM_MBUS_CODE + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + To enumerate media bus formats available at a given sub-device pad + applications initialize the pad and + index fields of &v4l2-subdev-mbus-code-enum; and + call the VIDIOC_SUBDEV_ENUM_MBUS_CODE ioctl with a + pointer to this structure. Drivers fill the rest of the structure or return + an &EINVAL; if either the pad or + index are invalid. All media bus formats are + enumerable by beginning at index zero and incrementing by one until + EINVAL is returned. + + Available media bus formats may depend on the current 'try' formats + at other pads of the sub-device, as well as on the current active links. See + &VIDIOC-SUBDEV-G-FMT; for more information about the try formats. + + + struct <structname>v4l2_subdev_mbus_code_enum</structname> + + &cs-str; + + + __u32 + pad + Pad number as reported by the media controller API. + + + __u32 + index + Number of the format in the enumeration, set by the + application. + + + __u32 + code + The media bus format code, as defined in + . + + + __u32 + reserved[9] + Reserved for future extensions. Applications and drivers must + set the array to zero. + + + +
+
+ + + &return-value; + + + + EINVAL + + The &v4l2-subdev-mbus-code-enum; pad + references a non-existing pad, or the index + field is out of bounds. + + + + +
diff --git a/Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml b/Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml new file mode 100644 index 0000000..f367c57 --- /dev/null +++ b/Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml @@ -0,0 +1,180 @@ + + + ioctl VIDIOC_SUBDEV_G_FMT, VIDIOC_SUBDEV_S_FMT + &manvol; + + + + VIDIOC_SUBDEV_G_FMT + VIDIOC_SUBDEV_S_FMT + Get or set the data format on a subdev pad + + + + + + int ioctl + int fd + int request + struct v4l2_subdev_format *argp + + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_SUBDEV_G_FMT, VIDIOC_SUBDEV_S_FMT + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + These ioctls are used to negotiate the frame format at specific + subdev pads in the image pipeline. + + To retrieve the current format applications set the + pad field of a &v4l2-subdev-format; to the + desired pad number as reported by the media API and the + which field to + V4L2_SUBDEV_FORMAT_ACTIVE. When they call the + VIDIOC_SUBDEV_G_FMT ioctl with a pointer to this + structure the driver fills the members of the format + field. + + To change the current format applications set both the + pad and which fields + and all members of the format field. When they + call the VIDIOC_SUBDEV_S_FMT ioctl with a pointer to this + structure the driver verifies the requested format, adjusts it based on the + hardware capabilities and configures the device. Upon return the + &v4l2-subdev-format; contains the current format as would be returned by a + VIDIOC_SUBDEV_G_FMT call. + + Applications can query the device capabilities by setting the + which to + V4L2_SUBDEV_FORMAT_TRY. When set, 'try' formats are not + applied to the device by the driver, but are changed exactly as active + formats and stored in the sub-device file handle. Two applications querying + the same sub-device would thus not interact with each other. + + For instance, to try a format at the output pad of a sub-device, + applications would first set the try format at the sub-device input with the + VIDIOC_SUBDEV_S_FMT ioctl. They would then either + retrieve the default format at the output pad with the + VIDIOC_SUBDEV_G_FMT ioctl, or set the desired output + pad format with the VIDIOC_SUBDEV_S_FMT ioctl and check + the returned value. + + Try formats do not depend on active formats, but can depend on the + current links configuration or sub-device controls value. For instance, a + low-pass noise filter might crop pixels at the frame boundaries, modifying + its output frame size. + + Drivers must not return an error solely because the requested format + doesn't match the device capabilities. They must instead modify the format + to match what the hardware can provide. The modified format should be as + close as possible to the original request. + + + struct <structname>v4l2_subdev_format</structname> + + &cs-str; + + + __u32 + pad + Pad number as reported by the media controller API. + + + __u32 + which + Format to modified, from &v4l2-subdev-format-whence;. + + + &v4l2-mbus-framefmt; + format + Definition of an image format, see for details. + + + __u32 + reserved[8] + Reserved for future extensions. Applications and drivers must + set the array to zero. + + + +
+ + + enum <structname>v4l2_subdev_format_whence</structname> + + &cs-def; + + + V4L2_SUBDEV_FORMAT_TRY + 0 + Try formats, used for querying device capabilities. + + + V4L2_SUBDEV_FORMAT_ACTIVE + 1 + Active formats, applied to the hardware. + + + +
+
+ + + &return-value; + + + + EBUSY + + The format can't be changed because the pad is currently busy. + This can be caused, for instance, by an active video stream on the + pad. The ioctl must not be retried without performing another action + to fix the problem first. Only returned by + VIDIOC_SUBDEV_S_FMT + + + + EINVAL + + The &v4l2-subdev-format; pad + references a non-existing pad, or the which + field references a non-existing format. + + + + +
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index bc763db..207cc5c 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -149,6 +149,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); struct v4l2_fh *vfh = file->private_data; +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); +#endif switch (cmd) { case VIDIOC_QUERYCTRL: @@ -183,7 +186,53 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_UNSUBSCRIBE_EVENT: return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + case VIDIOC_SUBDEV_G_FMT: { + struct v4l2_subdev_format *format = arg; + + if (format->which != V4L2_SUBDEV_FORMAT_TRY && + format->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (format->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format); + } + + case VIDIOC_SUBDEV_S_FMT: { + struct v4l2_subdev_format *format = arg; + + if (format->which != V4L2_SUBDEV_FORMAT_TRY && + format->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (format->pad >= sd->entity.num_pads) + return -EINVAL; + return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format); + } + + case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { + struct v4l2_subdev_mbus_code_enum *code = arg; + + if (code->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh, + code); + } + + case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: { + struct v4l2_subdev_frame_size_enum *fse = arg; + + if (fse->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh, + fse); + } +#endif default: return -ENOIOCTLCMD; } diff --git a/include/linux/Kbuild b/include/linux/Kbuild index c33f22a..43918a3 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -372,6 +372,7 @@ header-y += usbdevice_fs.h header-y += utime.h header-y += utsname.h header-y += v4l2-mediabus.h +header-y += v4l2-subdev.h header-y += veth.h header-y += vhost.h header-y += videodev2.h diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h new file mode 100644 index 0000000..8dbb448 --- /dev/null +++ b/include/linux/v4l2-subdev.h @@ -0,0 +1,90 @@ +/* + * V4L2 subdev userspace API + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_V4L2_SUBDEV_H +#define __LINUX_V4L2_SUBDEV_H + +#include +#include +#include + +/** + * enum v4l2_subdev_format_whence - Media bus format type + * @V4L2_SUBDEV_FORMAT_TRY: try format, for negotiation only + * @V4L2_SUBDEV_FORMAT_ACTIVE: active format, applied to the device + */ +enum v4l2_subdev_format_whence { + V4L2_SUBDEV_FORMAT_TRY = 0, + V4L2_SUBDEV_FORMAT_ACTIVE = 1, +}; + +/** + * struct v4l2_subdev_format - Pad-level media bus format + * @which: format type (from enum v4l2_subdev_format_whence) + * @pad: pad number, as reported by the media API + * @format: media bus format (format code and frame size) + */ +struct v4l2_subdev_format { + __u32 which; + __u32 pad; + struct v4l2_mbus_framefmt format; + __u32 reserved[8]; +}; + +/** + * struct v4l2_subdev_mbus_code_enum - Media bus format enumeration + * @pad: pad number, as reported by the media API + * @index: format index during enumeration + * @code: format code (from enum v4l2_mbus_pixelcode) + */ +struct v4l2_subdev_mbus_code_enum { + __u32 pad; + __u32 index; + __u32 code; + __u32 reserved[9]; +}; + +/** + * struct v4l2_subdev_frame_size_enum - Media bus format enumeration + * @pad: pad number, as reported by the media API + * @index: format index during enumeration + * @code: format code (from enum v4l2_mbus_pixelcode) + */ +struct v4l2_subdev_frame_size_enum { + __u32 index; + __u32 pad; + __u32 code; + __u32 min_width; + __u32 max_width; + __u32 min_height; + __u32 max_height; + __u32 reserved[9]; +}; + +#define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format) +#define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format) +#define VIDIOC_SUBDEV_ENUM_MBUS_CODE \ + _IOWR('V', 2, struct v4l2_subdev_mbus_code_enum) +#define VIDIOC_SUBDEV_ENUM_FRAME_SIZE \ + _IOWR('V', 74, struct v4l2_subdev_frame_size_enum) + +#endif diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index d4d3653..4215650 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -21,6 +21,7 @@ #ifndef _V4L2_SUBDEV_H #define _V4L2_SUBDEV_H +#include #include #include #include @@ -414,6 +415,15 @@ struct v4l2_subdev_ir_ops { }; struct v4l2_subdev_pad_ops { + int (*enum_mbus_code)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code); + int (*enum_frame_size)(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse); + int (*get_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format); + int (*set_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format); }; struct v4l2_subdev_ops { -- cgit v0.10.2 From 35c3017a29d278c4405a7f3ab30b814999d156d3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 5 May 2010 11:38:35 -0300 Subject: [media] v4l: v4l2_subdev userspace frame interval API The three new ioctl VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, VIDIOC_SUBDEV_G_FRAME_INTERVAL and VIDIOC_SUBDEV_S_FRAME_INTERVAL can be used to enumerate and configure a subdev's frame rate from userspace. Two new video::g/s_frame_interval subdev operations are introduced to support those ioctls. The existing video::g/s_parm operations are deprecated and shouldn't be used anymore. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index dbb9cb2..a95eb57 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -89,7 +89,9 @@ VIDIOC_SUBDEV_ENUM_FRAME_SIZE"> VIDIOC_SUBDEV_ENUM_MBUS_CODE"> VIDIOC_SUBDEV_G_FMT"> +VIDIOC_SUBDEV_G_FRAME_INTERVAL"> VIDIOC_SUBDEV_S_FMT"> +VIDIOC_SUBDEV_S_FRAME_INTERVAL"> VIDIOC_TRY_ENCODER_CMD"> VIDIOC_TRY_EXT_CTRLS"> VIDIOC_TRY_FMT"> @@ -193,6 +195,8 @@ v4l2_sliced_vbi_cap"> v4l2_sliced_vbi_data"> v4l2_sliced_vbi_format"> +v4l2_subdev_frame_interval"> +v4l2_subdev_frame_interval_enum"> v4l2_subdev_frame_size_enum"> v4l2_subdev_format"> v4l2_subdev_mbus_code_enum"> @@ -331,10 +335,12 @@ + + diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml index 2ada2f8..a436716 100644 --- a/Documentation/DocBook/v4l/v4l2.xml +++ b/Documentation/DocBook/v4l/v4l2.xml @@ -507,9 +507,11 @@ and discussions on the V4L mailing list. &sub-reqbufs; &sub-s-hw-freq-seek; &sub-streamon; + &sub-subdev-enum-frame-interval; &sub-subdev-enum-frame-size; &sub-subdev-enum-mbus-code; &sub-subdev-g-fmt; + &sub-subdev-g-frame-interval; &sub-subscribe-event; &sub-mmap; diff --git a/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-interval.xml b/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-interval.xml new file mode 100644 index 0000000..2f8f4f0 --- /dev/null +++ b/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-interval.xml @@ -0,0 +1,152 @@ + + + ioctl VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL + &manvol; + + + + VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL + Enumerate frame intervals + + + + + + int ioctl + int fd + int request + struct v4l2_subdev_frame_interval_enum * + argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + This ioctl lets applications enumerate available frame intervals on a + given sub-device pad. Frame intervals only makes sense for sub-devices that + can control the frame period on their own. This includes, for instance, + image sensors and TV tuners. + + For the common use case of image sensors, the frame intervals + available on the sub-device output pad depend on the frame format and size + on the same pad. Applications must thus specify the desired format and size + when enumerating frame intervals. + + To enumerate frame intervals applications initialize the + index, pad, + code, width and + height fields of + &v4l2-subdev-frame-interval-enum; and call the + VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL ioctl with a pointer + to this structure. Drivers fill the rest of the structure or return + an &EINVAL; if one of the input fields is invalid. All frame intervals are + enumerable by beginning at index zero and incrementing by one until + EINVAL is returned. + + Available frame intervals may depend on the current 'try' formats + at other pads of the sub-device, as well as on the current active links. See + &VIDIOC-SUBDEV-G-FMT; for more information about the try formats. + + Sub-devices that support the frame interval enumeration ioctl should + implemented it on a single pad only. Its behaviour when supported on + multiple pads of the same sub-device is not defined. + + + struct <structname>v4l2_subdev_frame_interval_enum</structname> + + &cs-str; + + + __u32 + index + Number of the format in the enumeration, set by the + application. + + + __u32 + pad + Pad number as reported by the media controller API. + + + __u32 + code + The media bus format code, as defined in + . + + + __u32 + width + Frame width, in pixels. + + + __u32 + height + Frame height, in pixels. + + + &v4l2-fract; + interval + Period, in seconds, between consecutive video frames. + + + __u32 + reserved[9] + Reserved for future extensions. Applications and drivers must + set the array to zero. + + + +
+
+ + + &return-value; + + + + EINVAL + + The &v4l2-subdev-frame-interval-enum; + pad references a non-existing pad, one of + the code, width + or height fields are invalid for the given + pad or the index field is out of bounds. + + + + + +
diff --git a/Documentation/DocBook/v4l/vidioc-subdev-g-frame-interval.xml b/Documentation/DocBook/v4l/vidioc-subdev-g-frame-interval.xml new file mode 100644 index 0000000..0bc3ea22 --- /dev/null +++ b/Documentation/DocBook/v4l/vidioc-subdev-g-frame-interval.xml @@ -0,0 +1,141 @@ + + + ioctl VIDIOC_SUBDEV_G_FRAME_INTERVAL, VIDIOC_SUBDEV_S_FRAME_INTERVAL + &manvol; + + + + VIDIOC_SUBDEV_G_FRAME_INTERVAL + VIDIOC_SUBDEV_S_FRAME_INTERVAL + Get or set the frame interval on a subdev pad + + + + + + int ioctl + int fd + int request + struct v4l2_subdev_frame_interval *argp + + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_SUBDEV_G_FRAME_INTERVAL, VIDIOC_SUBDEV_S_FRAME_INTERVAL + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + These ioctls are used to get and set the frame interval at specific + subdev pads in the image pipeline. The frame interval only makes sense for + sub-devices that can control the frame period on their own. This includes, + for instance, image sensors and TV tuners. Sub-devices that don't support + frame intervals must not implement these ioctls. + + To retrieve the current frame interval applications set the + pad field of a &v4l2-subdev-frame-interval; to + the desired pad number as reported by the media controller API. When they + call the VIDIOC_SUBDEV_G_FRAME_INTERVAL ioctl with a + pointer to this structure the driver fills the members of the + interval field. + + To change the current frame interval applications set both the + pad field and all members of the + interval field. When they call the + VIDIOC_SUBDEV_S_FRAME_INTERVAL ioctl with a pointer to + this structure the driver verifies the requested interval, adjusts it based + on the hardware capabilities and configures the device. Upon return the + &v4l2-subdev-frame-interval; contains the current frame interval as would be + returned by a VIDIOC_SUBDEV_G_FRAME_INTERVAL call. + + + Drivers must not return an error solely because the requested interval + doesn't match the device capabilities. They must instead modify the interval + to match what the hardware can provide. The modified interval should be as + close as possible to the original request. + + Sub-devices that support the frame interval ioctls should implement + them on a single pad only. Their behaviour when supported on multiple pads + of the same sub-device is not defined. + + + struct <structname>v4l2_subdev_frame_interval</structname> + + &cs-str; + + + __u32 + pad + Pad number as reported by the media controller API. + + + &v4l2-fract; + interval + Period, in seconds, between consecutive video frames. + + + __u32 + reserved[9] + Reserved for future extensions. Applications and drivers must + set the array to zero. + + + +
+
+ + + &return-value; + + + + EBUSY + + The frame interval can't be changed because the pad is currently + busy. This can be caused, for instance, by an active video stream on + the pad. The ioctl must not be retried without performing another + action to fix the problem first. Only returned by + VIDIOC_SUBDEV_S_FRAME_INTERVAL + + + + EINVAL + + The &v4l2-subdev-frame-interval; pad + references a non-existing pad, or the pad doesn't support frame + intervals. + + + + +
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 207cc5c..a4a1256 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -232,6 +232,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh, fse); } + + case VIDIOC_SUBDEV_G_FRAME_INTERVAL: + return v4l2_subdev_call(sd, video, g_frame_interval, arg); + + case VIDIOC_SUBDEV_S_FRAME_INTERVAL: + return v4l2_subdev_call(sd, video, s_frame_interval, arg); + + case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: { + struct v4l2_subdev_frame_interval_enum *fie = arg; + + if (fie->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, + fie); + } #endif default: return -ENOIOCTLCMD; diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index 8dbb448..c55f9e9 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -80,11 +80,47 @@ struct v4l2_subdev_frame_size_enum { __u32 reserved[9]; }; +/** + * struct v4l2_subdev_frame_interval - Pad-level frame rate + * @pad: pad number, as reported by the media API + * @interval: frame interval in seconds + */ +struct v4l2_subdev_frame_interval { + __u32 pad; + struct v4l2_fract interval; + __u32 reserved[9]; +}; + +/** + * struct v4l2_subdev_frame_interval_enum - Frame interval enumeration + * @pad: pad number, as reported by the media API + * @index: frame interval index during enumeration + * @code: format code (from enum v4l2_mbus_pixelcode) + * @width: frame width in pixels + * @height: frame height in pixels + * @interval: frame interval in seconds + */ +struct v4l2_subdev_frame_interval_enum { + __u32 index; + __u32 pad; + __u32 code; + __u32 width; + __u32 height; + struct v4l2_fract interval; + __u32 reserved[9]; +}; + #define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format) #define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format) +#define VIDIOC_SUBDEV_G_FRAME_INTERVAL \ + _IOWR('V', 21, struct v4l2_subdev_frame_interval) +#define VIDIOC_SUBDEV_S_FRAME_INTERVAL \ + _IOWR('V', 22, struct v4l2_subdev_frame_interval) #define VIDIOC_SUBDEV_ENUM_MBUS_CODE \ _IOWR('V', 2, struct v4l2_subdev_mbus_code_enum) #define VIDIOC_SUBDEV_ENUM_FRAME_SIZE \ _IOWR('V', 74, struct v4l2_subdev_frame_size_enum) +#define VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL \ + _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum) #endif diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 4215650..8b7a78a 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -268,6 +268,10 @@ struct v4l2_subdev_video_ops { int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); + int (*g_frame_interval)(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval); + int (*s_frame_interval)(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval); int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize); int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival); int (*enum_dv_presets) (struct v4l2_subdev *sd, @@ -420,6 +424,9 @@ struct v4l2_subdev_pad_ops { int (*enum_frame_size)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_frame_size_enum *fse); + int (*enum_frame_interval)(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_interval_enum *fie); int (*get_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *format); int (*set_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, -- cgit v0.10.2 From f6a5cb1be894468cdc69ec557d47f40c28f64642 Mon Sep 17 00:00:00 2001 From: Antti Koskipaa Date: Wed, 23 Jun 2010 05:03:42 -0300 Subject: [media] v4l: v4l2_subdev userspace crop API This patch adds the VIDIOC_SUBDEV_S_CROP and G_CROP ioctls to the userland API. CROPCAP is not implemented because it's redundant. Signed-off-by: Antti Koskipaa Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index a95eb57..40158ee 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -88,8 +88,10 @@ VIDIOC_S_TUNER"> VIDIOC_SUBDEV_ENUM_FRAME_SIZE"> VIDIOC_SUBDEV_ENUM_MBUS_CODE"> +VIDIOC_SUBDEV_G_CROP"> VIDIOC_SUBDEV_G_FMT"> VIDIOC_SUBDEV_G_FRAME_INTERVAL"> +VIDIOC_SUBDEV_S_CROP"> VIDIOC_SUBDEV_S_FMT"> VIDIOC_SUBDEV_S_FRAME_INTERVAL"> VIDIOC_TRY_ENCODER_CMD"> @@ -198,6 +200,7 @@ v4l2_subdev_frame_interval"> v4l2_subdev_frame_interval_enum"> v4l2_subdev_frame_size_enum"> +v4l2_subdev_crop"> v4l2_subdev_format"> v4l2_subdev_mbus_code_enum"> v4l2_standard"> @@ -339,6 +342,7 @@ + diff --git a/Documentation/DocBook/v4l/dev-subdev.xml b/Documentation/DocBook/v4l/dev-subdev.xml index fc62e65..e9eb8af 100644 --- a/Documentation/DocBook/v4l/dev-subdev.xml +++ b/Documentation/DocBook/v4l/dev-subdev.xml @@ -275,6 +275,39 @@ +
+ Cropping and scaling + + Many sub-devices support cropping frames on their input or output + pads (or possible even on both). Cropping is used to select the area of + interest in an image, typically on a video sensor or video decoder. It can + also be used as part of digital zoom implementations to select the area of + the image that will be scaled up. + + Crop settings are defined by a crop rectangle and represented in a + &v4l2-rect; by the coordinates of the top left corner and the rectangle + size. Both the coordinates and sizes are expressed in pixels. + + The crop rectangle is retrieved and set using the + &VIDIOC-SUBDEV-G-CROP; and &VIDIOC-SUBDEV-S-CROP; ioctls. Like for pad + formats, drivers store try and active crop rectangles. The format + negotiation mechanism applies to crop settings as well. + + On input pads, cropping is applied relatively to the current pad + format. The pad format represents the image size as received by the + sub-device from the previous block in the pipeline, and the crop rectangle + represents the sub-image that will be transmitted further inside the + sub-device for processing. The crop rectangle be entirely containted + inside the input image size. + + Input crop rectangle are reset to their default value when the input + image format is modified. Drivers should use the input image size as the + crop rectangle default value, but hardware requirements may prevent this. + + + Cropping behaviour on output pads is not defined. + +
&sub-subdev-formats; diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml index a436716..3a6e3b5 100644 --- a/Documentation/DocBook/v4l/v4l2.xml +++ b/Documentation/DocBook/v4l/v4l2.xml @@ -510,6 +510,7 @@ and discussions on the V4L mailing list. &sub-subdev-enum-frame-interval; &sub-subdev-enum-frame-size; &sub-subdev-enum-mbus-code; + &sub-subdev-g-crop; &sub-subdev-g-fmt; &sub-subdev-g-frame-interval; &sub-subscribe-event; diff --git a/Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml b/Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml new file mode 100644 index 0000000..0619732 --- /dev/null +++ b/Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml @@ -0,0 +1,155 @@ + + + ioctl VIDIOC_SUBDEV_G_CROP, VIDIOC_SUBDEV_S_CROP + &manvol; + + + + VIDIOC_SUBDEV_G_CROP + VIDIOC_SUBDEV_S_CROP + Get or set the crop rectangle on a subdev pad + + + + + + int ioctl + int fd + int request + struct v4l2_subdev_crop *argp + + + + + int ioctl + int fd + int request + const struct v4l2_subdev_crop *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_SUBDEV_G_CROP, VIDIOC_SUBDEV_S_CROP + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + To retrieve the current crop rectangle applications set the + pad field of a &v4l2-subdev-crop; to the + desired pad number as reported by the media API and the + which field to + V4L2_SUBDEV_FORMAT_ACTIVE. They then call the + VIDIOC_SUBDEV_G_CROP ioctl with a pointer to this + structure. The driver fills the members of the rect + field or returns &EINVAL; if the input arguments are invalid, or if cropping + is not supported on the given pad. + + To change the current crop rectangle applications set both the + pad and which fields + and all members of the rect field. They then call + the VIDIOC_SUBDEV_S_CROP ioctl with a pointer to this + structure. The driver verifies the requested crop rectangle, adjusts it + based on the hardware capabilities and configures the device. Upon return + the &v4l2-subdev-crop; contains the current format as would be returned + by a VIDIOC_SUBDEV_G_CROP call. + + Applications can query the device capabilities by setting the + which to + V4L2_SUBDEV_FORMAT_TRY. When set, 'try' crop + rectangles are not applied to the device by the driver, but are mangled + exactly as active crop rectangles and stored in the sub-device file handle. + Two applications querying the same sub-device would thus not interact with + each other. + + Drivers must not return an error solely because the requested crop + rectangle doesn't match the device capabilities. They must instead modify + the rectangle to match what the hardware can provide. The modified format + should be as close as possible to the original request. + + + struct <structname>v4l2_subdev_crop</structname> + + &cs-str; + + + __u32 + pad + Pad number as reported by the media framework. + + + __u32 + which + Crop rectangle to get or set, from + &v4l2-subdev-format-whence;. + + + &v4l2-rect; + rect + Crop rectangle boundaries, in pixels. + + + __u32 + reserved[8] + Reserved for future extensions. Applications and drivers must + set the array to zero. + + + +
+
+ + + &return-value; + + + + EBUSY + + The crop rectangle can't be changed because the pad is currently + busy. This can be caused, for instance, by an active video stream on + the pad. The ioctl must not be retried without performing another + action to fix the problem first. Only returned by + VIDIOC_SUBDEV_S_CROP + + + + EINVAL + + The &v4l2-subdev-crop; pad + references a non-existing pad, the which + field references a non-existing format, or cropping is not supported + on the given subdev pad. + + + + +
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index a4a1256..6e76f73 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -213,6 +213,32 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format); } + case VIDIOC_SUBDEV_G_CROP: { + struct v4l2_subdev_crop *crop = arg; + + if (crop->which != V4L2_SUBDEV_FORMAT_TRY && + crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (crop->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); + } + + case VIDIOC_SUBDEV_S_CROP: { + struct v4l2_subdev_crop *crop = arg; + + if (crop->which != V4L2_SUBDEV_FORMAT_TRY && + crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (crop->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); + } + case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { struct v4l2_subdev_mbus_code_enum *code = arg; diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index c55f9e9..ed29cbb 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -51,6 +51,19 @@ struct v4l2_subdev_format { }; /** + * struct v4l2_subdev_crop - Pad-level crop settings + * @which: format type (from enum v4l2_subdev_format_whence) + * @pad: pad number, as reported by the media API + * @rect: pad crop rectangle boundaries + */ +struct v4l2_subdev_crop { + __u32 which; + __u32 pad; + struct v4l2_rect rect; + __u32 reserved[8]; +}; + +/** * struct v4l2_subdev_mbus_code_enum - Media bus format enumeration * @pad: pad number, as reported by the media API * @index: format index during enumeration @@ -122,5 +135,7 @@ struct v4l2_subdev_frame_interval_enum { _IOWR('V', 74, struct v4l2_subdev_frame_size_enum) #define VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL \ _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum) +#define VIDIOC_SUBDEV_G_CROP _IOWR('V', 59, struct v4l2_subdev_crop) +#define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) #endif diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 8b7a78a..edeaf39 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -431,6 +431,10 @@ struct v4l2_subdev_pad_ops { struct v4l2_subdev_format *format); int (*set_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *format); + int (*set_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop); + int (*get_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop); }; struct v4l2_subdev_ops { -- cgit v0.10.2 From f80a3ab083474670527df3ca849546a7059da766 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Nov 2010 01:21:06 -0300 Subject: [media] v4l: Add subdev sensor g_skip_frames operation Some buggy sensors generate corrupt frames when the stream is started. This new operation return the number of corrupt frames to skip when starting the stream. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index edeaf39..1562c4f 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -341,9 +341,13 @@ struct v4l2_subdev_vbi_ops { * This is needed for some sensors, which always corrupt * several top lines of the output image, or which send their * metadata in them. + * @g_skip_frames: number of frames to skip at stream start. This is needed for + * buggy sensors that generate faulty frames when they are + * turned on. */ struct v4l2_subdev_sensor_ops { int (*g_skip_top_lines)(struct v4l2_subdev *sd, u32 *lines); + int (*g_skip_frames)(struct v4l2_subdev *sd, u32 *frames); }; /* -- cgit v0.10.2 From 2d8deaf6b13e1b00fb65333bac9380b8c318adcf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Sep 2010 12:59:36 -0300 Subject: [media] v4l: Add 8-bit YUYV on 16-bit bus and SGRBG10 media bus pixel codes Add the following media bus format code definitions: - V4L2_MBUS_FMT_SGRBG10_1X10 for 10-bit GRBG Bayer - V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 for 10-bit DPCM compressed GRBG Bayer - V4L2_MBUS_FMT_YUYV16_1X16 for 8-bit YUYV on 16-bit bus - V4L2_MBUS_FMT_UYVY16_1X16 for 8-bit UYVY on 16-bit bus - V4L2_MBUS_FMT_YVYU16_1X16 for 8-bit YVYU on 16-bit bus - V4L2_MBUS_FMT_VYUY16_1X16 for 8-bit VYUY on 16-bit bus Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h index cccfa34..c4caca3 100644 --- a/include/linux/v4l2-mediabus.h +++ b/include/linux/v4l2-mediabus.h @@ -47,7 +47,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007, V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008, - /* YUV (including grey) - next is 0x200f */ + /* YUV (including grey) - next is 0x2013 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003, @@ -60,17 +60,23 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_Y10_1X10 = 0x200a, V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b, V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c, + V4L2_MBUS_FMT_UYVY8_1X16 = 0x200f, + V4L2_MBUS_FMT_VYUY8_1X16 = 0x2010, + V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011, + V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012, V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, - /* Bayer - next is 0x3009 */ + /* Bayer - next is 0x300b */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 = 0x3009, V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE = 0x3003, V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE = 0x3004, V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE = 0x3005, V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE = 0x3006, V4L2_MBUS_FMT_SBGGR10_1X10 = 0x3007, + V4L2_MBUS_FMT_SGRBG10_1X10 = 0x300a, V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008, }; -- cgit v0.10.2 From b1fd18cd9b8719f7668bb00e14f02a8e227e42e9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 3 Sep 2010 05:47:25 -0300 Subject: [media] v4l: Add remaining RAW10 patterns w DPCM pixel code variants This adds following formats: - V4L2_MBUS_FMT_SRGGB10_1X10 - V4L2_MBUS_FMT_SGBRG10_1X10 - V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 - V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 - V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 Signed-off-by: Sergio Aguirre Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h index c4caca3..5c64924 100644 --- a/include/linux/v4l2-mediabus.h +++ b/include/linux/v4l2-mediabus.h @@ -67,16 +67,21 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, - /* Bayer - next is 0x300b */ + /* Bayer - next is 0x3010 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, + V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 = 0x300b, + V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 = 0x300c, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 = 0x3009, + V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 = 0x300d, V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE = 0x3003, V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE = 0x3004, V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE = 0x3005, V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE = 0x3006, V4L2_MBUS_FMT_SBGGR10_1X10 = 0x3007, + V4L2_MBUS_FMT_SGBRG10_1X10 = 0x300e, V4L2_MBUS_FMT_SGRBG10_1X10 = 0x300a, + V4L2_MBUS_FMT_SRGGB10_1X10 = 0x300f, V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008, }; -- cgit v0.10.2 From 7140c55738561907a1f66abf533d6358bf69ed9f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 23 Dec 2010 11:14:49 -0300 Subject: [media] v4l: Add missing 12 bits bayer media bus formats Add codes and documentation for the following media bus formats: - V4L2_MBUS_FMT_SGBRG12_1X12 - V4L2_MBUS_FMT_SGRBG12_1X12 - V4L2_MBUS_FMT_SRGGB12_1X12 Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/subdev-formats.xml b/Documentation/DocBook/v4l/subdev-formats.xml index 0cae572..b5376e2 100644 --- a/Documentation/DocBook/v4l/subdev-formats.xml +++ b/Documentation/DocBook/v4l/subdev-formats.xml @@ -762,6 +762,57 @@ b1 b0 + + V4L2_MBUS_FMT_SGBRG12_1X12 + 0x3010 + + g11 + g10 + g9 + g8 + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + + V4L2_MBUS_FMT_SGRBG12_1X12 + 0x3011 + + g11 + g10 + g9 + g8 + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + + V4L2_MBUS_FMT_SRGGB12_1X12 + 0x3012 + + r11 + r10 + r9 + r8 + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h index 5c64924..7054a7a 100644 --- a/include/linux/v4l2-mediabus.h +++ b/include/linux/v4l2-mediabus.h @@ -67,7 +67,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, - /* Bayer - next is 0x3010 */ + /* Bayer - next is 0x3013 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 = 0x300b, @@ -83,6 +83,9 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_SGRBG10_1X10 = 0x300a, V4L2_MBUS_FMT_SRGGB10_1X10 = 0x300f, V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008, + V4L2_MBUS_FMT_SGBRG12_1X12 = 0x3010, + V4L2_MBUS_FMT_SGRBG12_1X12 = 0x3011, + V4L2_MBUS_FMT_SRGGB12_1X12 = 0x3012, }; /** -- cgit v0.10.2 From 39187e177dc6372a967aa17a49a79189dc4fa8de Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 23 Dec 2010 11:14:50 -0300 Subject: [media] v4l: Add 12 bits bayer pixel formats Add FCCs for the following pixel formats: - V4L2_PIX_FMT_SBGGR12 - V4L2_PIX_FMT_SGBRG12 - V4L2_PIX_FMT_SGRBG12 - V4L2_PIX_FMT_SRGGB12 Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/pixfmt-srggb12.xml b/Documentation/DocBook/v4l/pixfmt-srggb12.xml new file mode 100644 index 0000000..9ba4fb6 --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-srggb12.xml @@ -0,0 +1,90 @@ + + + V4L2_PIX_FMT_SRGGB12 ('RG12'), + V4L2_PIX_FMT_SGRBG12 ('BA12'), + V4L2_PIX_FMT_SGBRG12 ('GB12'), + V4L2_PIX_FMT_SBGGR12 ('BG12'), + + &manvol; + + + V4L2_PIX_FMT_SRGGB12 + V4L2_PIX_FMT_SGRBG12 + V4L2_PIX_FMT_SGBRG12 + V4L2_PIX_FMT_SBGGR12 + 12-bit Bayer formats expanded to 16 bits + + + Description + + The following four pixel formats are raw sRGB / Bayer formats with +12 bits per colour. Each colour component is stored in a 16-bit word, with 6 +unused high bits filled with zeros. Each n-pixel row contains n/2 green samples +and n/2 blue or red samples, with alternating red and blue rows. Bytes are +stored in memory in little endian order. They are conventionally described +as GRGR... BGBG..., RGRG... GBGB..., etc. Below is an example of one of these +formats + + + <constant>V4L2_PIX_FMT_SBGGR12</constant> 4 × 4 +pixel image + + + Byte Order. + Each cell is one byte, high 6 bits in high bytes are 0. + + + + + + start + 0: + B00low + B00high + G01low + G01high + B02low + B02high + G03low + G03high + + + start + 8: + G10low + G10high + R11low + R11high + G12low + G12high + R13low + R13high + + + start + 16: + B20low + B20high + G21low + G21high + B22low + B22high + G23low + G23high + + + start + 24: + G30low + G30high + R31low + R31high + G32low + G32high + R33low + R33high + + + + + + + + + diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index a94c4d5..8c80fd3 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -355,6 +355,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10 GRGR.. BGBG.. */ #define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10 RGRG.. GBGB.. */ +#define V4L2_PIX_FMT_SBGGR12 v4l2_fourcc('B', 'G', '1', '2') /* 12 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12 GBGB.. RGRG.. */ +#define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */ +#define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */ /* 10bit raw bayer DPCM compressed to 8 bits */ #define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0') /* -- cgit v0.10.2 From 6817a69a030b0a815e57470cf5d2e3b577cdcba9 Mon Sep 17 00:00:00 2001 From: Tuukka Toivonen Date: Tue, 2 Feb 2010 11:17:33 -0300 Subject: [media] ARM: OMAP3: Update Camera ISP definitions for OMAP3630 Add new/changed base address definitions and resources for OMAP3630 ISP. The OMAP3430 CSI2PHY block is same as the OMAP3630 CSIPHY2 block. But the later name is chosen as it gives more symmetry to the names. Signed-off-by: Tuukka Toivonen Signed-off-by: Vimarsh Zutshi Acked-by: Tony Lindgren Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 2c9c912..95c69f9 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -109,13 +109,33 @@ static struct resource omap3isp_resources[] = { .flags = IORESOURCE_MEM, }, { - .start = OMAP3430_ISP_CSI2A_BASE, - .end = OMAP3430_ISP_CSI2A_END, + .start = OMAP3430_ISP_CSI2A_REGS1_BASE, + .end = OMAP3430_ISP_CSI2A_REGS1_END, .flags = IORESOURCE_MEM, }, { - .start = OMAP3430_ISP_CSI2PHY_BASE, - .end = OMAP3430_ISP_CSI2PHY_END, + .start = OMAP3430_ISP_CSIPHY2_BASE, + .end = OMAP3430_ISP_CSIPHY2_END, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP3630_ISP_CSI2A_REGS2_BASE, + .end = OMAP3630_ISP_CSI2A_REGS2_END, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP3630_ISP_CSI2C_REGS1_BASE, + .end = OMAP3630_ISP_CSI2C_REGS1_END, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP3630_ISP_CSIPHY1_BASE, + .end = OMAP3630_ISP_CSIPHY1_END, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP3630_ISP_CSI2C_REGS2_BASE, + .end = OMAP3630_ISP_CSI2C_REGS2_END, .flags = IORESOURCE_MEM, }, { diff --git a/arch/arm/plat-omap/include/plat/omap34xx.h b/arch/arm/plat-omap/include/plat/omap34xx.h index 98fc8b4..b9e8588 100644 --- a/arch/arm/plat-omap/include/plat/omap34xx.h +++ b/arch/arm/plat-omap/include/plat/omap34xx.h @@ -56,8 +56,12 @@ #define OMAP3430_ISP_RESZ_BASE (OMAP3430_ISP_BASE + 0x1000) #define OMAP3430_ISP_SBL_BASE (OMAP3430_ISP_BASE + 0x1200) #define OMAP3430_ISP_MMU_BASE (OMAP3430_ISP_BASE + 0x1400) -#define OMAP3430_ISP_CSI2A_BASE (OMAP3430_ISP_BASE + 0x1800) -#define OMAP3430_ISP_CSI2PHY_BASE (OMAP3430_ISP_BASE + 0x1970) +#define OMAP3430_ISP_CSI2A_REGS1_BASE (OMAP3430_ISP_BASE + 0x1800) +#define OMAP3430_ISP_CSIPHY2_BASE (OMAP3430_ISP_BASE + 0x1970) +#define OMAP3630_ISP_CSI2A_REGS2_BASE (OMAP3430_ISP_BASE + 0x19C0) +#define OMAP3630_ISP_CSI2C_REGS1_BASE (OMAP3430_ISP_BASE + 0x1C00) +#define OMAP3630_ISP_CSIPHY1_BASE (OMAP3430_ISP_BASE + 0x1D70) +#define OMAP3630_ISP_CSI2C_REGS2_BASE (OMAP3430_ISP_BASE + 0x1DC0) #define OMAP3430_ISP_END (OMAP3430_ISP_BASE + 0x06F) #define OMAP3430_ISP_CBUFF_END (OMAP3430_ISP_CBUFF_BASE + 0x077) @@ -69,8 +73,12 @@ #define OMAP3430_ISP_RESZ_END (OMAP3430_ISP_RESZ_BASE + 0x0AB) #define OMAP3430_ISP_SBL_END (OMAP3430_ISP_SBL_BASE + 0x0FB) #define OMAP3430_ISP_MMU_END (OMAP3430_ISP_MMU_BASE + 0x06F) -#define OMAP3430_ISP_CSI2A_END (OMAP3430_ISP_CSI2A_BASE + 0x16F) -#define OMAP3430_ISP_CSI2PHY_END (OMAP3430_ISP_CSI2PHY_BASE + 0x007) +#define OMAP3430_ISP_CSI2A_REGS1_END (OMAP3430_ISP_CSI2A_REGS1_BASE + 0x16F) +#define OMAP3430_ISP_CSIPHY2_END (OMAP3430_ISP_CSIPHY2_BASE + 0x00B) +#define OMAP3630_ISP_CSI2A_REGS2_END (OMAP3630_ISP_CSI2A_REGS2_BASE + 0x3F) +#define OMAP3630_ISP_CSI2C_REGS1_END (OMAP3630_ISP_CSI2C_REGS1_BASE + 0x16F) +#define OMAP3630_ISP_CSIPHY1_END (OMAP3630_ISP_CSIPHY1_BASE + 0x00B) +#define OMAP3630_ISP_CSI2C_REGS2_END (OMAP3630_ISP_CSI2C_REGS2_BASE + 0x3F) #define OMAP34XX_HSUSB_OTG_BASE (L4_34XX_BASE + 0xAB000) #define OMAP34XX_USBTLL_BASE (L4_34XX_BASE + 0x62000) -- cgit v0.10.2 From 837c83283daa5ae3831c4dff6bdb75e26aeab4b0 Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Mon, 15 Nov 2010 11:29:56 -0300 Subject: [media] omap3: Remove unusued ISP CBUFF resource The ISP CBUFF module isn't use, its resource isn't needed. Signed-off-by: Sergio Aguirre Acked-by: Laurent Pinchart Acked-by: Tony Lindgren Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 95c69f9..d389756 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -69,11 +69,6 @@ static struct resource omap3isp_resources[] = { .flags = IORESOURCE_MEM, }, { - .start = OMAP3430_ISP_CBUFF_BASE, - .end = OMAP3430_ISP_CBUFF_END, - .flags = IORESOURCE_MEM, - }, - { .start = OMAP3430_ISP_CCP2_BASE, .end = OMAP3430_ISP_CCP2_END, .flags = IORESOURCE_MEM, -- cgit v0.10.2 From a11f6706ca4473c2df3f72a306d9b9fee6512a33 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 14 Dec 2009 08:09:07 -0300 Subject: [media] omap3: Add function to register omap3isp platform device structure The omap3isp platform device requires platform data. Instead of registering the device in omap2_init_devices(), export an omap3_init_camera() function to fill the device structure with the platform data pointer and register the device. Signed-off-by: Laurent Pinchart Acked-by: Tony Lindgren Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index d389756..6fd92eb 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -34,6 +34,8 @@ #include "mux.h" #include "control.h" +#include "devices.h" + #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE) static struct resource cam_resources[] = { @@ -59,8 +61,11 @@ static inline void omap_init_camera(void) { platform_device_register(&omap_cam_device); } - -#elif defined(CONFIG_VIDEO_OMAP3) || defined(CONFIG_VIDEO_OMAP3_MODULE) +#else +static inline void omap_init_camera(void) +{ +} +#endif static struct resource omap3isp_resources[] = { { @@ -146,15 +151,11 @@ static struct platform_device omap3isp_device = { .resource = omap3isp_resources, }; -static inline void omap_init_camera(void) -{ - platform_device_register(&omap3isp_device); -} -#else -static inline void omap_init_camera(void) +int omap3_init_camera(struct isp_platform_data *pdata) { + omap3isp_device.dev.platform_data = pdata; + return platform_device_register(&omap3isp_device); } -#endif #if defined(CONFIG_OMAP_MBOX_FWK) || defined(CONFIG_OMAP_MBOX_FWK_MODULE) diff --git a/arch/arm/mach-omap2/devices.h b/arch/arm/mach-omap2/devices.h new file mode 100644 index 0000000..f61eb6e --- /dev/null +++ b/arch/arm/mach-omap2/devices.h @@ -0,0 +1,19 @@ +/* + * arch/arm/mach-omap2/devices.h + * + * OMAP2 platform device setup/initialization + * + * 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 __ARCH_ARM_MACH_OMAP_DEVICES_H +#define __ARCH_ARM_MACH_OMAP_DEVICES_H + +struct isp_platform_data; + +int omap3_init_camera(struct isp_platform_data *pdata); + +#endif -- cgit v0.10.2 From cfe2cde6d33f15994ad34466ac4a08f5bb348948 Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Mon, 15 Nov 2010 11:29:54 -0300 Subject: [media] omap2: Fix camera resources for multiomap Make sure the kernel can be compiled with both OMAP2 and OMAP3 camera support linked in, and give public symbols proper omap2/omap3 prefixes. Signed-off-by: Sergio Aguirre Acked-by: Laurent Pinchart Acked-by: Tony Lindgren Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 6fd92eb..145c8f0 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -38,7 +38,7 @@ #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE) -static struct resource cam_resources[] = { +static struct resource omap2cam_resources[] = { { .start = OMAP24XX_CAMERA_BASE, .end = OMAP24XX_CAMERA_BASE + 0xfff, @@ -50,21 +50,12 @@ static struct resource cam_resources[] = { } }; -static struct platform_device omap_cam_device = { +static struct platform_device omap2cam_device = { .name = "omap24xxcam", .id = -1, - .num_resources = ARRAY_SIZE(cam_resources), - .resource = cam_resources, + .num_resources = ARRAY_SIZE(omap2cam_resources), + .resource = omap2cam_resources, }; - -static inline void omap_init_camera(void) -{ - platform_device_register(&omap_cam_device); -} -#else -static inline void omap_init_camera(void) -{ -} #endif static struct resource omap3isp_resources[] = { @@ -157,6 +148,14 @@ int omap3_init_camera(struct isp_platform_data *pdata) return platform_device_register(&omap3isp_device); } +static inline void omap_init_camera(void) +{ +#if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE) + if (cpu_is_omap24xx()) + platform_device_register(&omap2cam_device); +#endif +} + #if defined(CONFIG_OMAP_MBOX_FWK) || defined(CONFIG_OMAP_MBOX_FWK_MODULE) #define MBOX_REG_SIZE 0x120 -- cgit v0.10.2 From c30b46e58b31a0fc420049e21117444862fc7cb7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 26 Feb 2010 12:23:10 -0300 Subject: [media] v4l: subdev: Generic ioctl support Instead of returning an error when receiving an ioctl call with an unsupported command, forward the call to the subdev core::ioctl handler. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 77d96f4..f2df31b 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -405,6 +405,11 @@ VIDIOC_UNSUBSCRIBE_EVENT To properly support events, the poll() file operation is also implemented. +Private ioctls + + All ioctls not in the above list are passed directly to the sub-device + driver through the core::ioctl operation. + I2C sub-device drivers ---------------------- diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 6e76f73..0b80644 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -276,7 +276,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) } #endif default: - return -ENOIOCTLCMD; + return v4l2_subdev_call(sd, core, ioctl, cmd, arg); } return 0; -- cgit v0.10.2 From 448de7e7850b804bc8f5efa60305a83333c257bf Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 12 Feb 2011 18:05:06 -0300 Subject: [media] omap3isp: OMAP3 ISP core The Image Signal Processor provides the system interface and the processing capability to connect RAW or YUV image-sensor modules to the OMAP3. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: David Cohen Signed-off-by: Stanimir Varbanov Signed-off-by: Vimarsh Zutshi Signed-off-by: Tuukka Toivonen Signed-off-by: Sergio Aguirre Signed-off-by: Antti Koskipaa Signed-off-by: Ivan T. Ivanov Signed-off-by: RaniSuneela Signed-off-by: Atanas Filipov Signed-off-by: Gjorgji Rosikopulos Signed-off-by: Hiroshi DOYU Signed-off-by: Nayden Kanchev Signed-off-by: Phil Carmody Signed-off-by: Artem Bityutskiy Signed-off-by: Dominic Curran Signed-off-by: Ilkka Myllyperkio Signed-off-by: Pallavi Kulkarni Signed-off-by: Vaibhav Hiremath Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c new file mode 100644 index 0000000..1a9963bd --- /dev/null +++ b/drivers/media/video/omap3isp/isp.c @@ -0,0 +1,2220 @@ +/* + * isp.c + * + * TI OMAP3 ISP - Core + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2007-2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * Contributors: + * Laurent Pinchart + * Sakari Ailus + * David Cohen + * Stanimir Varbanov + * Vimarsh Zutshi + * Tuukka Toivonen + * Sergio Aguirre + * Antti Koskipaa + * Ivan T. Ivanov + * RaniSuneela + * Atanas Filipov + * Gjorgji Rosikopulos + * Hiroshi DOYU + * Nayden Kanchev + * Phil Carmody + * Artem Bityutskiy + * Dominic Curran + * Ilkka Myllyperkio + * Pallavi Kulkarni + * Vaibhav Hiremath + * Mohit Jalori + * Sameer Venkatraman + * Senthilvadivu Guruswamy + * Thara Gopinath + * Toni Leinonen + * Troy Laramy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispccdc.h" +#include "isppreview.h" +#include "ispresizer.h" +#include "ispcsi2.h" +#include "ispccp2.h" +#include "isph3a.h" +#include "isphist.h" + +static unsigned int autoidle; +module_param(autoidle, int, 0444); +MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support"); + +static void isp_save_ctx(struct isp_device *isp); + +static void isp_restore_ctx(struct isp_device *isp); + +static const struct isp_res_mapping isp_res_maps[] = { + { + .isp_rev = ISP_REVISION_2_0, + .map = 1 << OMAP3_ISP_IOMEM_MAIN | + 1 << OMAP3_ISP_IOMEM_CCP2 | + 1 << OMAP3_ISP_IOMEM_CCDC | + 1 << OMAP3_ISP_IOMEM_HIST | + 1 << OMAP3_ISP_IOMEM_H3A | + 1 << OMAP3_ISP_IOMEM_PREV | + 1 << OMAP3_ISP_IOMEM_RESZ | + 1 << OMAP3_ISP_IOMEM_SBL | + 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | + 1 << OMAP3_ISP_IOMEM_CSIPHY2, + }, + { + .isp_rev = ISP_REVISION_15_0, + .map = 1 << OMAP3_ISP_IOMEM_MAIN | + 1 << OMAP3_ISP_IOMEM_CCP2 | + 1 << OMAP3_ISP_IOMEM_CCDC | + 1 << OMAP3_ISP_IOMEM_HIST | + 1 << OMAP3_ISP_IOMEM_H3A | + 1 << OMAP3_ISP_IOMEM_PREV | + 1 << OMAP3_ISP_IOMEM_RESZ | + 1 << OMAP3_ISP_IOMEM_SBL | + 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | + 1 << OMAP3_ISP_IOMEM_CSIPHY2 | + 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 | + 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 | + 1 << OMAP3_ISP_IOMEM_CSIPHY1 | + 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2, + }, +}; + +/* Structure for saving/restoring ISP module registers */ +static struct isp_reg isp_reg_list[] = { + {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0}, + {0, ISP_TOK_TERM, 0} +}; + +/* + * omap3isp_flush - Post pending L3 bus writes by doing a register readback + * @isp: OMAP3 ISP device + * + * In order to force posting of pending writes, we need to write and + * readback the same register, in this case the revision register. + * + * See this link for reference: + * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html + */ +void omap3isp_flush(struct isp_device *isp) +{ + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); + isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); +} + +/* + * isp_enable_interrupts - Enable ISP interrupts. + * @isp: OMAP3 ISP device + */ +static void isp_enable_interrupts(struct isp_device *isp) +{ + static const u32 irq = IRQ0ENABLE_CSIA_IRQ + | IRQ0ENABLE_CSIB_IRQ + | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ + | IRQ0ENABLE_CCDC_LSC_DONE_IRQ + | IRQ0ENABLE_CCDC_VD0_IRQ + | IRQ0ENABLE_CCDC_VD1_IRQ + | IRQ0ENABLE_HS_VS_IRQ + | IRQ0ENABLE_HIST_DONE_IRQ + | IRQ0ENABLE_H3A_AWB_DONE_IRQ + | IRQ0ENABLE_H3A_AF_DONE_IRQ + | IRQ0ENABLE_PRV_DONE_IRQ + | IRQ0ENABLE_RSZ_DONE_IRQ; + + isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); +} + +/* + * isp_disable_interrupts - Disable ISP interrupts. + * @isp: OMAP3 ISP device + */ +static void isp_disable_interrupts(struct isp_device *isp) +{ + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); +} + +/** + * isp_set_xclk - Configures the specified cam_xclk to the desired frequency. + * @isp: OMAP3 ISP device + * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high + * @xclksel: XCLK to configure (0 = A, 1 = B). + * + * Configures the specified MCLK divisor in the ISP timing control register + * (TCTRL_CTRL) to generate the desired xclk clock value. + * + * Divisor = cam_mclk_hz / xclk + * + * Returns the final frequency that is actually being generated + **/ +static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel) +{ + u32 divisor; + u32 currentxclk; + unsigned long mclk_hz; + + if (!omap3isp_get(isp)) + return 0; + + mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); + + if (xclk >= mclk_hz) { + divisor = ISPTCTRL_CTRL_DIV_BYPASS; + currentxclk = mclk_hz; + } else if (xclk >= 2) { + divisor = mclk_hz / xclk; + if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS) + divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1; + currentxclk = mclk_hz / divisor; + } else { + divisor = xclk; + currentxclk = 0; + } + + switch (xclksel) { + case 0: + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, + ISPTCTRL_CTRL_DIVA_MASK, + divisor << ISPTCTRL_CTRL_DIVA_SHIFT); + dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n", + currentxclk); + break; + case 1: + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, + ISPTCTRL_CTRL_DIVB_MASK, + divisor << ISPTCTRL_CTRL_DIVB_SHIFT); + dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n", + currentxclk); + break; + default: + omap3isp_put(isp); + dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested " + "xclk. Must be 0 (A) or 1 (B).\n"); + return -EINVAL; + } + + /* Do we go from stable whatever to clock? */ + if (divisor >= 2 && isp->xclk_divisor[xclksel] < 2) + omap3isp_get(isp); + /* Stopping the clock. */ + else if (divisor < 2 && isp->xclk_divisor[xclksel] >= 2) + omap3isp_put(isp); + + isp->xclk_divisor[xclksel] = divisor; + + omap3isp_put(isp); + + return currentxclk; +} + +/* + * isp_power_settings - Sysconfig settings, for Power Management. + * @isp: OMAP3 ISP device + * @idle: Consider idle state. + * + * Sets the power settings for the ISP, and SBL bus. + */ +static void isp_power_settings(struct isp_device *isp, int idle) +{ + isp_reg_writel(isp, + ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY : + ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) << + ISP_SYSCONFIG_MIDLEMODE_SHIFT) | + ((isp->revision == ISP_REVISION_15_0) ? + ISP_SYSCONFIG_AUTOIDLE : 0), + OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); + + if (isp->autoidle) + isp_reg_writel(isp, ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN, + ISP_CTRL); +} + +/* + * Configure the bridge and lane shifter. Valid inputs are + * + * CCDC_INPUT_PARALLEL: Parallel interface + * CCDC_INPUT_CSI2A: CSI2a receiver + * CCDC_INPUT_CCP2B: CCP2b receiver + * CCDC_INPUT_CSI2C: CSI2c receiver + * + * The bridge and lane shifter are configured according to the selected input + * and the ISP platform data. + */ +void omap3isp_configure_bridge(struct isp_device *isp, + enum ccdc_input_entity input, + const struct isp_parallel_platform_data *pdata) +{ + u32 ispctrl_val; + + ispctrl_val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); + ispctrl_val &= ~ISPCTRL_SHIFT_MASK; + ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV; + ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK; + ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK; + + switch (input) { + case CCDC_INPUT_PARALLEL: + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; + ispctrl_val |= pdata->data_lane_shift << ISPCTRL_SHIFT_SHIFT; + ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; + ispctrl_val |= pdata->bridge << ISPCTRL_PAR_BRIDGE_SHIFT; + break; + + case CCDC_INPUT_CSI2A: + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA; + break; + + case CCDC_INPUT_CCP2B: + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB; + break; + + case CCDC_INPUT_CSI2C: + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC; + break; + + default: + return; + } + + ispctrl_val &= ~ISPCTRL_SYNC_DETECT_MASK; + ispctrl_val |= ISPCTRL_SYNC_DETECT_VSRISE; + + isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); +} + +/** + * isp_set_pixel_clock - Configures the ISP pixel clock + * @isp: OMAP3 ISP device + * @pixelclk: Average pixel clock in Hz + * + * Set the average pixel clock required by the sensor. The ISP will use the + * lowest possible memory bandwidth settings compatible with the clock. + **/ +static void isp_set_pixel_clock(struct isp_device *isp, unsigned int pixelclk) +{ + isp->isp_ccdc.vpcfg.pixelclk = pixelclk; +} + +void omap3isp_hist_dma_done(struct isp_device *isp) +{ + if (omap3isp_ccdc_busy(&isp->isp_ccdc) || + omap3isp_stat_pcr_busy(&isp->isp_hist)) { + /* Histogram cannot be enabled in this frame anymore */ + atomic_set(&isp->isp_hist.buf_err, 1); + dev_dbg(isp->dev, "hist: Out of synchronization with " + "CCDC. Ignoring next buffer.\n"); + } +} + +static inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus) +{ + static const char *name[] = { + "CSIA_IRQ", + "res1", + "res2", + "CSIB_LCM_IRQ", + "CSIB_IRQ", + "res5", + "res6", + "res7", + "CCDC_VD0_IRQ", + "CCDC_VD1_IRQ", + "CCDC_VD2_IRQ", + "CCDC_ERR_IRQ", + "H3A_AF_DONE_IRQ", + "H3A_AWB_DONE_IRQ", + "res14", + "res15", + "HIST_DONE_IRQ", + "CCDC_LSC_DONE", + "CCDC_LSC_PREFETCH_COMPLETED", + "CCDC_LSC_PREFETCH_ERROR", + "PRV_DONE_IRQ", + "CBUFF_IRQ", + "res22", + "res23", + "RSZ_DONE_IRQ", + "OVF_IRQ", + "res26", + "res27", + "MMU_ERR_IRQ", + "OCP_ERR_IRQ", + "SEC_ERR_IRQ", + "HS_VS_IRQ", + }; + int i; + + dev_dbg(isp->dev, ""); + + for (i = 0; i < ARRAY_SIZE(name); i++) { + if ((1 << i) & irqstatus) + printk(KERN_CONT "%s ", name[i]); + } + printk(KERN_CONT "\n"); +} + +static void isp_isr_sbl(struct isp_device *isp) +{ + struct device *dev = isp->dev; + u32 sbl_pcr; + + /* + * Handle shared buffer logic overflows for video buffers. + * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored. + */ + sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); + isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); + sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF; + + if (sbl_pcr) + dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr); + + if (sbl_pcr & (ISPSBL_PCR_CCDC_WBL_OVF | ISPSBL_PCR_CSIA_WBL_OVF + | ISPSBL_PCR_CSIB_WBL_OVF)) { + isp->isp_ccdc.error = 1; + if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) + isp->isp_prev.error = 1; + if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) + isp->isp_res.error = 1; + } + + if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) { + isp->isp_prev.error = 1; + if (isp->isp_res.input == RESIZER_INPUT_VP && + !(isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER)) + isp->isp_res.error = 1; + } + + if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF + | ISPSBL_PCR_RSZ2_WBL_OVF + | ISPSBL_PCR_RSZ3_WBL_OVF + | ISPSBL_PCR_RSZ4_WBL_OVF)) + isp->isp_res.error = 1; + + if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF) + omap3isp_stat_sbl_overflow(&isp->isp_af); + + if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF) + omap3isp_stat_sbl_overflow(&isp->isp_aewb); +} + +/* + * isp_isr - Interrupt Service Routine for Camera ISP module. + * @irq: Not used currently. + * @_isp: Pointer to the OMAP3 ISP device + * + * Handles the corresponding callback if plugged in. + * + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the + * IRQ wasn't handled. + */ +static irqreturn_t isp_isr(int irq, void *_isp) +{ + static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ | + IRQ0STATUS_CCDC_LSC_DONE_IRQ | + IRQ0STATUS_CCDC_VD0_IRQ | + IRQ0STATUS_CCDC_VD1_IRQ | + IRQ0STATUS_HS_VS_IRQ; + struct isp_device *isp = _isp; + u32 irqstatus; + int ret; + + irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + + isp_isr_sbl(isp); + + if (irqstatus & IRQ0STATUS_CSIA_IRQ) { + ret = omap3isp_csi2_isr(&isp->isp_csi2a); + if (ret) + isp->isp_ccdc.error = 1; + } + + if (irqstatus & IRQ0STATUS_CSIB_IRQ) { + ret = omap3isp_ccp2_isr(&isp->isp_ccp2); + if (ret) + isp->isp_ccdc.error = 1; + } + + if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) { + if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) + omap3isp_preview_isr_frame_sync(&isp->isp_prev); + if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) + omap3isp_resizer_isr_frame_sync(&isp->isp_res); + omap3isp_stat_isr_frame_sync(&isp->isp_aewb); + omap3isp_stat_isr_frame_sync(&isp->isp_af); + omap3isp_stat_isr_frame_sync(&isp->isp_hist); + } + + if (irqstatus & ccdc_events) + omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events); + + if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) { + if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER) + omap3isp_resizer_isr_frame_sync(&isp->isp_res); + omap3isp_preview_isr(&isp->isp_prev); + } + + if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ) + omap3isp_resizer_isr(&isp->isp_res); + + if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ) + omap3isp_stat_isr(&isp->isp_aewb); + + if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ) + omap3isp_stat_isr(&isp->isp_af); + + if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ) + omap3isp_stat_isr(&isp->isp_hist); + + omap3isp_flush(isp); + +#if defined(DEBUG) && defined(ISP_ISR_DEBUG) + isp_isr_dbg(isp, irqstatus); +#endif + + return IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * Pipeline power management + * + * Entities must be powered up when part of a pipeline that contains at least + * one open video device node. + * + * To achieve this use the entity use_count field to track the number of users. + * For entities corresponding to video device nodes the use_count field stores + * the users count of the node. For entities corresponding to subdevs the + * use_count field stores the total number of users of all video device nodes + * in the pipeline. + * + * The omap3isp_pipeline_pm_use() function must be called in the open() and + * close() handlers of video device nodes. It increments or decrements the use + * count of all subdev entities in the pipeline. + * + * To react to link management on powered pipelines, the link setup notification + * callback updates the use count of all entities in the source and sink sides + * of the link. + */ + +/* + * isp_pipeline_pm_use_count - Count the number of users of a pipeline + * @entity: The entity + * + * Return the total number of users of all video device nodes in the pipeline. + */ +static int isp_pipeline_pm_use_count(struct media_entity *entity) +{ + struct media_entity_graph graph; + int use = 0; + + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + use += entity->use_count; + } + + return use; +} + +/* + * isp_pipeline_pm_power_one - Apply power change to an entity + * @entity: The entity + * @change: Use count change + * + * Change the entity use count by @change. If the entity is a subdev update its + * power state by calling the core::s_power operation when the use count goes + * from 0 to != 0 or from != 0 to 0. + * + * Return 0 on success or a negative error code on failure. + */ +static int isp_pipeline_pm_power_one(struct media_entity *entity, int change) +{ + struct v4l2_subdev *subdev; + int ret; + + subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV + ? media_entity_to_v4l2_subdev(entity) : NULL; + + if (entity->use_count == 0 && change > 0 && subdev != NULL) { + ret = v4l2_subdev_call(subdev, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + if (entity->use_count == 0 && change < 0 && subdev != NULL) + v4l2_subdev_call(subdev, core, s_power, 0); + + return 0; +} + +/* + * isp_pipeline_pm_power - Apply power change to all entities in a pipeline + * @entity: The entity + * @change: Use count change + * + * Walk the pipeline to update the use count and the power state of all non-node + * entities. + * + * Return 0 on success or a negative error code on failure. + */ +static int isp_pipeline_pm_power(struct media_entity *entity, int change) +{ + struct media_entity_graph graph; + struct media_entity *first = entity; + int ret = 0; + + if (!change) + return 0; + + media_entity_graph_walk_start(&graph, entity); + + while (!ret && (entity = media_entity_graph_walk_next(&graph))) + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + ret = isp_pipeline_pm_power_one(entity, change); + + if (!ret) + return 0; + + media_entity_graph_walk_start(&graph, first); + + while ((first = media_entity_graph_walk_next(&graph)) + && first != entity) + if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) + isp_pipeline_pm_power_one(first, -change); + + return ret; +} + +/* + * omap3isp_pipeline_pm_use - Update the use count of an entity + * @entity: The entity + * @use: Use (1) or stop using (0) the entity + * + * Update the use count of all entities in the pipeline and power entities on or + * off accordingly. + * + * Return 0 on success or a negative error code on failure. Powering entities + * off is assumed to never fail. No failure can occur when the use parameter is + * set to 0. + */ +int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) +{ + int change = use ? 1 : -1; + int ret; + + mutex_lock(&entity->parent->graph_mutex); + + /* Apply use count to node. */ + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + /* Apply power change to connected non-nodes. */ + ret = isp_pipeline_pm_power(entity, change); + + mutex_unlock(&entity->parent->graph_mutex); + + return ret; +} + +/* + * isp_pipeline_link_notify - Link management notification callback + * @source: Pad at the start of the link + * @sink: Pad at the end of the link + * @flags: New link flags that will be applied + * + * React to link management on powered pipelines by updating the use count of + * all entities in the source and sink sides of the link. Entities are powered + * on or off accordingly. + * + * Return 0 on success or a negative error code on failure. Powering entities + * off is assumed to never fail. This function will not fail for disconnection + * events. + */ +static int isp_pipeline_link_notify(struct media_pad *source, + struct media_pad *sink, u32 flags) +{ + int source_use = isp_pipeline_pm_use_count(source->entity); + int sink_use = isp_pipeline_pm_use_count(sink->entity); + int ret; + + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + /* Powering off entities is assumed to never fail. */ + isp_pipeline_pm_power(source->entity, -sink_use); + isp_pipeline_pm_power(sink->entity, -source_use); + return 0; + } + + ret = isp_pipeline_pm_power(source->entity, sink_use); + if (ret < 0) + return ret; + + ret = isp_pipeline_pm_power(sink->entity, source_use); + if (ret < 0) + isp_pipeline_pm_power(source->entity, -sink_use); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Pipeline stream management + */ + +/* + * isp_pipeline_enable - Enable streaming on a pipeline + * @pipe: ISP pipeline + * @mode: Stream mode (single shot or continuous) + * + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int isp_pipeline_enable(struct isp_pipeline *pipe, + enum isp_pipeline_stream_state mode) +{ + struct isp_device *isp = pipe->output->isp; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); + spin_unlock_irqrestore(&pipe->lock, flags); + + pipe->do_propagation = false; + + entity = &pipe->output->video.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, mode); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + + if (subdev == &isp->isp_ccdc.subdev) { + v4l2_subdev_call(&isp->isp_aewb.subdev, video, + s_stream, mode); + v4l2_subdev_call(&isp->isp_af.subdev, video, + s_stream, mode); + v4l2_subdev_call(&isp->isp_hist.subdev, video, + s_stream, mode); + pipe->do_propagation = true; + } + } + + /* Frame number propagation. In continuous streaming mode the number + * is incremented in the frame start ISR. In mem-to-mem mode + * singleshot is used and frame start IRQs are not available. + * Thus we have to increment the number here. + */ + if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT) + atomic_inc(&pipe->frame_number); + + return ret; +} + +static int isp_pipeline_wait_resizer(struct isp_device *isp) +{ + return omap3isp_resizer_busy(&isp->isp_res); +} + +static int isp_pipeline_wait_preview(struct isp_device *isp) +{ + return omap3isp_preview_busy(&isp->isp_prev); +} + +static int isp_pipeline_wait_ccdc(struct isp_device *isp) +{ + return omap3isp_stat_busy(&isp->isp_af) + || omap3isp_stat_busy(&isp->isp_aewb) + || omap3isp_stat_busy(&isp->isp_hist) + || omap3isp_ccdc_busy(&isp->isp_ccdc); +} + +#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000) + +static int isp_pipeline_wait(struct isp_device *isp, + int(*busy)(struct isp_device *isp)) +{ + unsigned long timeout = jiffies + ISP_STOP_TIMEOUT; + + while (!time_after(jiffies, timeout)) { + if (!busy(isp)) + return 0; + } + + return 1; +} + +/* + * isp_pipeline_disable - Disable streaming on a pipeline + * @pipe: ISP pipeline + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. Wait synchronously for the modules to be stopped if + * necessary. + * + * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module + * can't be stopped (in which case a software reset of the ISP is probably + * necessary). + */ +static int isp_pipeline_disable(struct isp_pipeline *pipe) +{ + struct isp_device *isp = pipe->output->isp; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int failure = 0; + int ret; + + /* + * We need to stop all the modules after CCDC first or they'll + * never stop since they may not get a full frame from CCDC. + */ + entity = &pipe->output->video.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + if (subdev == &isp->isp_ccdc.subdev) { + v4l2_subdev_call(&isp->isp_aewb.subdev, + video, s_stream, 0); + v4l2_subdev_call(&isp->isp_af.subdev, + video, s_stream, 0); + v4l2_subdev_call(&isp->isp_hist.subdev, + video, s_stream, 0); + } + + v4l2_subdev_call(subdev, video, s_stream, 0); + + if (subdev == &isp->isp_res.subdev) + ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer); + else if (subdev == &isp->isp_prev.subdev) + ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview); + else if (subdev == &isp->isp_ccdc.subdev) + ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc); + else + ret = 0; + + if (ret) { + dev_info(isp->dev, "Unable to stop %s\n", subdev->name); + failure = -ETIMEDOUT; + } + } + + return failure; +} + +/* + * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline + * @pipe: ISP pipeline + * @state: Stream state (stopped, single shot or continuous) + * + * Set the pipeline to the given stream state. Pipelines can be started in + * single-shot or continuous mode. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, + enum isp_pipeline_stream_state state) +{ + int ret; + + if (state == ISP_PIPELINE_STREAM_STOPPED) + ret = isp_pipeline_disable(pipe); + else + ret = isp_pipeline_enable(pipe, state); + pipe->stream_state = state; + + return ret; +} + +/* + * isp_pipeline_resume - Resume streaming on a pipeline + * @pipe: ISP pipeline + * + * Resume video output and input and re-enable pipeline. + */ +static void isp_pipeline_resume(struct isp_pipeline *pipe) +{ + int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT; + + omap3isp_video_resume(pipe->output, !singleshot); + if (singleshot) + omap3isp_video_resume(pipe->input, 0); + isp_pipeline_enable(pipe, pipe->stream_state); +} + +/* + * isp_pipeline_suspend - Suspend streaming on a pipeline + * @pipe: ISP pipeline + * + * Suspend pipeline. + */ +static void isp_pipeline_suspend(struct isp_pipeline *pipe) +{ + isp_pipeline_disable(pipe); +} + +/* + * isp_pipeline_is_last - Verify if entity has an enabled link to the output + * video node + * @me: ISP module's media entity + * + * Returns 1 if the entity has an enabled link to the output video node or 0 + * otherwise. It's true only while pipeline can have no more than one output + * node. + */ +static int isp_pipeline_is_last(struct media_entity *me) +{ + struct isp_pipeline *pipe; + struct media_pad *pad; + + if (!me->pipe) + return 0; + pipe = to_isp_pipeline(me); + if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED) + return 0; + pad = media_entity_remote_source(&pipe->output->pad); + return pad->entity == me; +} + +/* + * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module + * @me: ISP module's media entity + * + * Suspend the whole pipeline if module's entity has an enabled link to the + * output video node. It works only while pipeline can have no more than one + * output node. + */ +static void isp_suspend_module_pipeline(struct media_entity *me) +{ + if (isp_pipeline_is_last(me)) + isp_pipeline_suspend(to_isp_pipeline(me)); +} + +/* + * isp_resume_module_pipeline - Resume pipeline to which belongs the module + * @me: ISP module's media entity + * + * Resume the whole pipeline if module's entity has an enabled link to the + * output video node. It works only while pipeline can have no more than one + * output node. + */ +static void isp_resume_module_pipeline(struct media_entity *me) +{ + if (isp_pipeline_is_last(me)) + isp_pipeline_resume(to_isp_pipeline(me)); +} + +/* + * isp_suspend_modules - Suspend ISP submodules. + * @isp: OMAP3 ISP device + * + * Returns 0 if suspend left in idle state all the submodules properly, + * or returns 1 if a general Reset is required to suspend the submodules. + */ +static int isp_suspend_modules(struct isp_device *isp) +{ + unsigned long timeout; + + omap3isp_stat_suspend(&isp->isp_aewb); + omap3isp_stat_suspend(&isp->isp_af); + omap3isp_stat_suspend(&isp->isp_hist); + isp_suspend_module_pipeline(&isp->isp_res.subdev.entity); + isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity); + isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity); + isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity); + isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity); + + timeout = jiffies + ISP_STOP_TIMEOUT; + while (omap3isp_stat_busy(&isp->isp_af) + || omap3isp_stat_busy(&isp->isp_aewb) + || omap3isp_stat_busy(&isp->isp_hist) + || omap3isp_preview_busy(&isp->isp_prev) + || omap3isp_resizer_busy(&isp->isp_res) + || omap3isp_ccdc_busy(&isp->isp_ccdc)) { + if (time_after(jiffies, timeout)) { + dev_info(isp->dev, "can't stop modules.\n"); + return 1; + } + msleep(1); + } + + return 0; +} + +/* + * isp_resume_modules - Resume ISP submodules. + * @isp: OMAP3 ISP device + */ +static void isp_resume_modules(struct isp_device *isp) +{ + omap3isp_stat_resume(&isp->isp_aewb); + omap3isp_stat_resume(&isp->isp_af); + omap3isp_stat_resume(&isp->isp_hist); + isp_resume_module_pipeline(&isp->isp_res.subdev.entity); + isp_resume_module_pipeline(&isp->isp_prev.subdev.entity); + isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity); + isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity); + isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity); +} + +/* + * isp_reset - Reset ISP with a timeout wait for idle. + * @isp: OMAP3 ISP device + */ +static int isp_reset(struct isp_device *isp) +{ + unsigned long timeout = 0; + + isp_reg_writel(isp, + isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG) + | ISP_SYSCONFIG_SOFTRESET, + OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); + while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, + ISP_SYSSTATUS) & 0x1)) { + if (timeout++ > 10000) { + dev_alert(isp->dev, "cannot reset ISP\n"); + return -ETIMEDOUT; + } + udelay(1); + } + + return 0; +} + +/* + * isp_save_context - Saves the values of the ISP module registers. + * @isp: OMAP3 ISP device + * @reg_list: Structure containing pairs of register address and value to + * modify on OMAP. + */ +static void +isp_save_context(struct isp_device *isp, struct isp_reg *reg_list) +{ + struct isp_reg *next = reg_list; + + for (; next->reg != ISP_TOK_TERM; next++) + next->val = isp_reg_readl(isp, next->mmio_range, next->reg); +} + +/* + * isp_restore_context - Restores the values of the ISP module registers. + * @isp: OMAP3 ISP device + * @reg_list: Structure containing pairs of register address and value to + * modify on OMAP. + */ +static void +isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list) +{ + struct isp_reg *next = reg_list; + + for (; next->reg != ISP_TOK_TERM; next++) + isp_reg_writel(isp, next->val, next->mmio_range, next->reg); +} + +/* + * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. + * @isp: OMAP3 ISP device + * + * Routine for saving the context of each module in the ISP. + * CCDC, HIST, H3A, PREV, RESZ and MMU. + */ +static void isp_save_ctx(struct isp_device *isp) +{ + isp_save_context(isp, isp_reg_list); + if (isp->iommu) + iommu_save_ctx(isp->iommu); +} + +/* + * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. + * @isp: OMAP3 ISP device + * + * Routine for restoring the context of each module in the ISP. + * CCDC, HIST, H3A, PREV, RESZ and MMU. + */ +static void isp_restore_ctx(struct isp_device *isp) +{ + isp_restore_context(isp, isp_reg_list); + if (isp->iommu) + iommu_restore_ctx(isp->iommu); + omap3isp_ccdc_restore_context(isp); + omap3isp_preview_restore_context(isp); +} + +/* ----------------------------------------------------------------------------- + * SBL resources management + */ +#define OMAP3_ISP_SBL_READ (OMAP3_ISP_SBL_CSI1_READ | \ + OMAP3_ISP_SBL_CCDC_LSC_READ | \ + OMAP3_ISP_SBL_PREVIEW_READ | \ + OMAP3_ISP_SBL_RESIZER_READ) +#define OMAP3_ISP_SBL_WRITE (OMAP3_ISP_SBL_CSI1_WRITE | \ + OMAP3_ISP_SBL_CSI2A_WRITE | \ + OMAP3_ISP_SBL_CSI2C_WRITE | \ + OMAP3_ISP_SBL_CCDC_WRITE | \ + OMAP3_ISP_SBL_PREVIEW_WRITE) + +void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res) +{ + u32 sbl = 0; + + isp->sbl_resources |= res; + + if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ) + sbl |= ISPCTRL_SBL_SHARED_RPORTA; + + if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ) + sbl |= ISPCTRL_SBL_SHARED_RPORTB; + + if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE) + sbl |= ISPCTRL_SBL_SHARED_WPORTC; + + if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE) + sbl |= ISPCTRL_SBL_WR0_RAM_EN; + + if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE) + sbl |= ISPCTRL_SBL_WR1_RAM_EN; + + if (isp->sbl_resources & OMAP3_ISP_SBL_READ) + sbl |= ISPCTRL_SBL_RD_RAM_EN; + + isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); +} + +void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res) +{ + u32 sbl = 0; + + isp->sbl_resources &= ~res; + + if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)) + sbl |= ISPCTRL_SBL_SHARED_RPORTA; + + if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)) + sbl |= ISPCTRL_SBL_SHARED_RPORTB; + + if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)) + sbl |= ISPCTRL_SBL_SHARED_WPORTC; + + if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)) + sbl |= ISPCTRL_SBL_WR0_RAM_EN; + + if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE)) + sbl |= ISPCTRL_SBL_WR1_RAM_EN; + + if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ)) + sbl |= ISPCTRL_SBL_RD_RAM_EN; + + isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); +} + +/* + * isp_module_sync_idle - Helper to sync module with its idle state + * @me: ISP submodule's media entity + * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization + * @stopping: flag which tells module wants to stop + * + * This function checks if ISP submodule needs to wait for next interrupt. If + * yes, makes the caller to sleep while waiting for such event. + */ +int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, + atomic_t *stopping) +{ + struct isp_pipeline *pipe = to_isp_pipeline(me); + + if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED || + (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT && + !isp_pipeline_ready(pipe))) + return 0; + + /* + * atomic_set() doesn't include memory barrier on ARM platform for SMP + * scenario. We'll call it here to avoid race conditions. + */ + atomic_set(stopping, 1); + smp_mb(); + + /* + * If module is the last one, it's writing to memory. In this case, + * it's necessary to check if the module is already paused due to + * DMA queue underrun or if it has to wait for next interrupt to be + * idle. + * If it isn't the last one, the function won't sleep but *stopping + * will still be set to warn next submodule caller's interrupt the + * module wants to be idle. + */ + if (isp_pipeline_is_last(me)) { + struct isp_video *video = pipe->output; + unsigned long flags; + spin_lock_irqsave(&video->queue->irqlock, flags); + if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { + spin_unlock_irqrestore(&video->queue->irqlock, flags); + atomic_set(stopping, 0); + smp_mb(); + return 0; + } + spin_unlock_irqrestore(&video->queue->irqlock, flags); + if (!wait_event_timeout(*wait, !atomic_read(stopping), + msecs_to_jiffies(1000))) { + atomic_set(stopping, 0); + smp_mb(); + return -ETIMEDOUT; + } + } + + return 0; +} + +/* + * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping + * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization + * @stopping: flag which tells module wants to stop + * + * This function checks if ISP submodule was stopping. In case of yes, it + * notices the caller by setting stopping to 0 and waking up the wait queue. + * Returns 1 if it was stopping or 0 otherwise. + */ +int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, + atomic_t *stopping) +{ + if (atomic_cmpxchg(stopping, 1, 0)) { + wake_up(wait); + return 1; + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Clock management + */ + +#define ISPCTRL_CLKS_MASK (ISPCTRL_H3A_CLK_EN | \ + ISPCTRL_HIST_CLK_EN | \ + ISPCTRL_RSZ_CLK_EN | \ + (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \ + (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN)) + +static void __isp_subclk_update(struct isp_device *isp) +{ + u32 clk = 0; + + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_H3A) + clk |= ISPCTRL_H3A_CLK_EN; + + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST) + clk |= ISPCTRL_HIST_CLK_EN; + + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER) + clk |= ISPCTRL_RSZ_CLK_EN; + + /* NOTE: For CCDC & Preview submodules, we need to affect internal + * RAM aswell. + */ + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC) + clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN; + + if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW) + clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN; + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ISPCTRL_CLKS_MASK, clk); +} + +void omap3isp_subclk_enable(struct isp_device *isp, + enum isp_subclk_resource res) +{ + isp->subclk_resources |= res; + + __isp_subclk_update(isp); +} + +void omap3isp_subclk_disable(struct isp_device *isp, + enum isp_subclk_resource res) +{ + isp->subclk_resources &= ~res; + + __isp_subclk_update(isp); +} + +/* + * isp_enable_clocks - Enable ISP clocks + * @isp: OMAP3 ISP device + * + * Return 0 if successful, or clk_enable return value if any of tthem fails. + */ +static int isp_enable_clocks(struct isp_device *isp) +{ + int r; + unsigned long rate; + int divisor; + + /* + * cam_mclk clock chain: + * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk + * + * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are + * set to the same value. Hence the rate set for dpll4_m5 + * has to be twice of what is set on OMAP3430 to get + * the required value for cam_mclk + */ + if (cpu_is_omap3630()) + divisor = 1; + else + divisor = 2; + + r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]); + if (r) { + dev_err(isp->dev, "clk_enable cam_ick failed\n"); + goto out_clk_enable_ick; + } + r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK], + CM_CAM_MCLK_HZ/divisor); + if (r) { + dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n"); + goto out_clk_enable_mclk; + } + r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]); + if (r) { + dev_err(isp->dev, "clk_enable cam_mclk failed\n"); + goto out_clk_enable_mclk; + } + rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); + if (rate != CM_CAM_MCLK_HZ) + dev_warn(isp->dev, "unexpected cam_mclk rate:\n" + " expected : %d\n" + " actual : %ld\n", CM_CAM_MCLK_HZ, rate); + r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]); + if (r) { + dev_err(isp->dev, "clk_enable csi2_fck failed\n"); + goto out_clk_enable_csi2_fclk; + } + return 0; + +out_clk_enable_csi2_fclk: + clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); +out_clk_enable_mclk: + clk_disable(isp->clock[ISP_CLK_CAM_ICK]); +out_clk_enable_ick: + return r; +} + +/* + * isp_disable_clocks - Disable ISP clocks + * @isp: OMAP3 ISP device + */ +static void isp_disable_clocks(struct isp_device *isp) +{ + clk_disable(isp->clock[ISP_CLK_CAM_ICK]); + clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); + clk_disable(isp->clock[ISP_CLK_CSI2_FCK]); +} + +static const char *isp_clocks[] = { + "cam_ick", + "cam_mclk", + "dpll4_m5_ck", + "csi2_96m_fck", + "l3_ick", +}; + +static void isp_put_clocks(struct isp_device *isp) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { + if (isp->clock[i]) { + clk_put(isp->clock[i]); + isp->clock[i] = NULL; + } + } +} + +static int isp_get_clocks(struct isp_device *isp) +{ + struct clk *clk; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { + clk = clk_get(isp->dev, isp_clocks[i]); + if (IS_ERR(clk)) { + dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]); + isp_put_clocks(isp); + return PTR_ERR(clk); + } + + isp->clock[i] = clk; + } + + return 0; +} + +/* + * omap3isp_get - Acquire the ISP resource. + * + * Initializes the clocks for the first acquire. + * + * Increment the reference count on the ISP. If the first reference is taken, + * enable clocks and power-up all submodules. + * + * Return a pointer to the ISP device structure, or NULL if an error occured. + */ +struct isp_device *omap3isp_get(struct isp_device *isp) +{ + struct isp_device *__isp = isp; + + if (isp == NULL) + return NULL; + + mutex_lock(&isp->isp_mutex); + if (isp->ref_count > 0) + goto out; + + if (isp_enable_clocks(isp) < 0) { + __isp = NULL; + goto out; + } + + /* We don't want to restore context before saving it! */ + if (isp->has_context) + isp_restore_ctx(isp); + else + isp->has_context = 1; + + isp_enable_interrupts(isp); + +out: + if (__isp != NULL) + isp->ref_count++; + mutex_unlock(&isp->isp_mutex); + + return __isp; +} + +/* + * omap3isp_put - Release the ISP + * + * Decrement the reference count on the ISP. If the last reference is released, + * power-down all submodules, disable clocks and free temporary buffers. + */ +void omap3isp_put(struct isp_device *isp) +{ + if (isp == NULL) + return; + + mutex_lock(&isp->isp_mutex); + BUG_ON(isp->ref_count == 0); + if (--isp->ref_count == 0) { + isp_disable_interrupts(isp); + isp_save_ctx(isp); + isp_disable_clocks(isp); + } + mutex_unlock(&isp->isp_mutex); +} + +/* -------------------------------------------------------------------------- + * Platform device driver + */ + +/* + * omap3isp_print_status - Prints the values of the ISP Control Module registers + * @isp: OMAP3 ISP device + */ +#define ISP_PRINT_REGISTER(isp, name)\ + dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \ + isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name)) +#define SBL_PRINT_REGISTER(isp, name)\ + dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \ + isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name)) + +void omap3isp_print_status(struct isp_device *isp) +{ + dev_dbg(isp->dev, "-------------ISP Register dump--------------\n"); + + ISP_PRINT_REGISTER(isp, SYSCONFIG); + ISP_PRINT_REGISTER(isp, SYSSTATUS); + ISP_PRINT_REGISTER(isp, IRQ0ENABLE); + ISP_PRINT_REGISTER(isp, IRQ0STATUS); + ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH); + ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY); + ISP_PRINT_REGISTER(isp, CTRL); + ISP_PRINT_REGISTER(isp, TCTRL_CTRL); + ISP_PRINT_REGISTER(isp, TCTRL_FRAME); + ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY); + ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY); + ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY); + ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH); + ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH); + ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH); + + SBL_PRINT_REGISTER(isp, PCR); + SBL_PRINT_REGISTER(isp, SDR_REQ_EXP); + + dev_dbg(isp->dev, "--------------------------------------------\n"); +} + +#ifdef CONFIG_PM + +/* + * Power management support. + * + * As the ISP can't properly handle an input video stream interruption on a non + * frame boundary, the ISP pipelines need to be stopped before sensors get + * suspended. However, as suspending the sensors can require a running clock, + * which can be provided by the ISP, the ISP can't be completely suspended + * before the sensor. + * + * To solve this problem power management support is split into prepare/complete + * and suspend/resume operations. The pipelines are stopped in prepare() and the + * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in + * resume(), and the the pipelines are restarted in complete(). + * + * TODO: PM dependencies between the ISP and sensors are not modeled explicitly + * yet. + */ +static int isp_pm_prepare(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + int reset; + + WARN_ON(mutex_is_locked(&isp->isp_mutex)); + + if (isp->ref_count == 0) + return 0; + + reset = isp_suspend_modules(isp); + isp_disable_interrupts(isp); + isp_save_ctx(isp); + if (reset) + isp_reset(isp); + + return 0; +} + +static int isp_pm_suspend(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + WARN_ON(mutex_is_locked(&isp->isp_mutex)); + + if (isp->ref_count) + isp_disable_clocks(isp); + + return 0; +} + +static int isp_pm_resume(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + if (isp->ref_count == 0) + return 0; + + return isp_enable_clocks(isp); +} + +static void isp_pm_complete(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + if (isp->ref_count == 0) + return; + + isp_restore_ctx(isp); + isp_enable_interrupts(isp); + isp_resume_modules(isp); +} + +#else + +#define isp_pm_prepare NULL +#define isp_pm_suspend NULL +#define isp_pm_resume NULL +#define isp_pm_complete NULL + +#endif /* CONFIG_PM */ + +static void isp_unregister_entities(struct isp_device *isp) +{ + omap3isp_csi2_unregister_entities(&isp->isp_csi2a); + omap3isp_ccp2_unregister_entities(&isp->isp_ccp2); + omap3isp_ccdc_unregister_entities(&isp->isp_ccdc); + omap3isp_preview_unregister_entities(&isp->isp_prev); + omap3isp_resizer_unregister_entities(&isp->isp_res); + omap3isp_stat_unregister_entities(&isp->isp_aewb); + omap3isp_stat_unregister_entities(&isp->isp_af); + omap3isp_stat_unregister_entities(&isp->isp_hist); + + v4l2_device_unregister(&isp->v4l2_dev); + media_device_unregister(&isp->media_dev); +} + +/* + * isp_register_subdev_group - Register a group of subdevices + * @isp: OMAP3 ISP device + * @board_info: I2C subdevs board information array + * + * Register all I2C subdevices in the board_info array. The array must be + * terminated by a NULL entry, and the first entry must be the sensor. + * + * Return a pointer to the sensor media entity if it has been successfully + * registered, or NULL otherwise. + */ +static struct v4l2_subdev * +isp_register_subdev_group(struct isp_device *isp, + struct isp_subdev_i2c_board_info *board_info) +{ + struct v4l2_subdev *sensor = NULL; + unsigned int first; + + if (board_info->board_info == NULL) + return NULL; + + for (first = 1; board_info->board_info; ++board_info, first = 0) { + struct v4l2_subdev *subdev; + struct i2c_adapter *adapter; + + adapter = i2c_get_adapter(board_info->i2c_adapter_id); + if (adapter == NULL) { + printk(KERN_ERR "%s: Unable to get I2C adapter %d for " + "device %s\n", __func__, + board_info->i2c_adapter_id, + board_info->board_info->type); + continue; + } + + subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, + board_info->board_info, NULL); + if (subdev == NULL) { + printk(KERN_ERR "%s: Unable to register subdev %s\n", + __func__, board_info->board_info->type); + continue; + } + + if (first) + sensor = subdev; + } + + return sensor; +} + +static int isp_register_entities(struct isp_device *isp) +{ + struct isp_platform_data *pdata = isp->pdata; + struct isp_v4l2_subdevs_group *subdevs; + int ret; + + isp->media_dev.dev = isp->dev; + strlcpy(isp->media_dev.model, "TI OMAP3 ISP", + sizeof(isp->media_dev.model)); + isp->media_dev.link_notify = isp_pipeline_link_notify; + ret = media_device_register(&isp->media_dev); + if (ret < 0) { + printk(KERN_ERR "%s: Media device registration failed (%d)\n", + __func__, ret); + return ret; + } + + isp->v4l2_dev.mdev = &isp->media_dev; + ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); + if (ret < 0) { + printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n", + __func__, ret); + goto done; + } + + /* Register internal entities */ + ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap3isp_preview_register_entities(&isp->isp_prev, + &isp->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev); + if (ret < 0) + goto done; + + /* Register external entities */ + for (subdevs = pdata->subdevs; subdevs->subdevs; ++subdevs) { + struct v4l2_subdev *sensor; + struct media_entity *input; + unsigned int flags; + unsigned int pad; + + sensor = isp_register_subdev_group(isp, subdevs->subdevs); + if (sensor == NULL) + continue; + + sensor->host_priv = subdevs; + + /* Connect the sensor to the correct interface module. Parallel + * sensors are connected directly to the CCDC, while serial + * sensors are connected to the CSI2a, CCP2b or CSI2c receiver + * through CSIPHY1 or CSIPHY2. + */ + switch (subdevs->interface) { + case ISP_INTERFACE_PARALLEL: + input = &isp->isp_ccdc.subdev.entity; + pad = CCDC_PAD_SINK; + flags = 0; + break; + + case ISP_INTERFACE_CSI2A_PHY2: + input = &isp->isp_csi2a.subdev.entity; + pad = CSI2_PAD_SINK; + flags = MEDIA_LNK_FL_IMMUTABLE + | MEDIA_LNK_FL_ENABLED; + break; + + case ISP_INTERFACE_CCP2B_PHY1: + case ISP_INTERFACE_CCP2B_PHY2: + input = &isp->isp_ccp2.subdev.entity; + pad = CCP2_PAD_SINK; + flags = 0; + break; + + case ISP_INTERFACE_CSI2C_PHY1: + input = &isp->isp_csi2c.subdev.entity; + pad = CSI2_PAD_SINK; + flags = MEDIA_LNK_FL_IMMUTABLE + | MEDIA_LNK_FL_ENABLED; + break; + + default: + printk(KERN_ERR "%s: invalid interface type %u\n", + __func__, subdevs->interface); + ret = -EINVAL; + goto done; + } + + ret = media_entity_create_link(&sensor->entity, 0, input, pad, + flags); + if (ret < 0) + goto done; + } + + ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); + +done: + if (ret < 0) + isp_unregister_entities(isp); + + return ret; +} + +static void isp_cleanup_modules(struct isp_device *isp) +{ + omap3isp_h3a_aewb_cleanup(isp); + omap3isp_h3a_af_cleanup(isp); + omap3isp_hist_cleanup(isp); + omap3isp_resizer_cleanup(isp); + omap3isp_preview_cleanup(isp); + omap3isp_ccdc_cleanup(isp); + omap3isp_ccp2_cleanup(isp); + omap3isp_csi2_cleanup(isp); +} + +static int isp_initialize_modules(struct isp_device *isp) +{ + int ret; + + ret = omap3isp_csiphy_init(isp); + if (ret < 0) { + dev_err(isp->dev, "CSI PHY initialization failed\n"); + goto error_csiphy; + } + + ret = omap3isp_csi2_init(isp); + if (ret < 0) { + dev_err(isp->dev, "CSI2 initialization failed\n"); + goto error_csi2; + } + + ret = omap3isp_ccp2_init(isp); + if (ret < 0) { + dev_err(isp->dev, "CCP2 initialization failed\n"); + goto error_ccp2; + } + + ret = omap3isp_ccdc_init(isp); + if (ret < 0) { + dev_err(isp->dev, "CCDC initialization failed\n"); + goto error_ccdc; + } + + ret = omap3isp_preview_init(isp); + if (ret < 0) { + dev_err(isp->dev, "Preview initialization failed\n"); + goto error_preview; + } + + ret = omap3isp_resizer_init(isp); + if (ret < 0) { + dev_err(isp->dev, "Resizer initialization failed\n"); + goto error_resizer; + } + + ret = omap3isp_hist_init(isp); + if (ret < 0) { + dev_err(isp->dev, "Histogram initialization failed\n"); + goto error_hist; + } + + ret = omap3isp_h3a_aewb_init(isp); + if (ret < 0) { + dev_err(isp->dev, "H3A AEWB initialization failed\n"); + goto error_h3a_aewb; + } + + ret = omap3isp_h3a_af_init(isp); + if (ret < 0) { + dev_err(isp->dev, "H3A AF initialization failed\n"); + goto error_h3a_af; + } + + /* Connect the submodules. */ + ret = media_entity_create_link( + &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_aewb.subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_af.subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_hist.subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + goto error_link; + + return 0; + +error_link: + omap3isp_h3a_af_cleanup(isp); +error_h3a_af: + omap3isp_h3a_aewb_cleanup(isp); +error_h3a_aewb: + omap3isp_hist_cleanup(isp); +error_hist: + omap3isp_resizer_cleanup(isp); +error_resizer: + omap3isp_preview_cleanup(isp); +error_preview: + omap3isp_ccdc_cleanup(isp); +error_ccdc: + omap3isp_ccp2_cleanup(isp); +error_ccp2: + omap3isp_csi2_cleanup(isp); +error_csi2: +error_csiphy: + return ret; +} + +/* + * isp_remove - Remove ISP platform device + * @pdev: Pointer to ISP platform device + * + * Always returns 0. + */ +static int isp_remove(struct platform_device *pdev) +{ + struct isp_device *isp = platform_get_drvdata(pdev); + int i; + + isp_unregister_entities(isp); + isp_cleanup_modules(isp); + + omap3isp_get(isp); + iommu_put(isp->iommu); + omap3isp_put(isp); + + free_irq(isp->irq_num, isp); + isp_put_clocks(isp); + + for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) { + if (isp->mmio_base[i]) { + iounmap(isp->mmio_base[i]); + isp->mmio_base[i] = NULL; + } + + if (isp->mmio_base_phys[i]) { + release_mem_region(isp->mmio_base_phys[i], + isp->mmio_size[i]); + isp->mmio_base_phys[i] = 0; + } + } + + regulator_put(isp->isp_csiphy1.vdd); + regulator_put(isp->isp_csiphy2.vdd); + kfree(isp); + + return 0; +} + +static int isp_map_mem_resource(struct platform_device *pdev, + struct isp_device *isp, + enum isp_mem_resources res) +{ + struct resource *mem; + + /* request the mem region for the camera registers */ + + mem = platform_get_resource(pdev, IORESOURCE_MEM, res); + if (!mem) { + dev_err(isp->dev, "no mem resource?\n"); + return -ENODEV; + } + + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { + dev_err(isp->dev, + "cannot reserve camera register I/O region\n"); + return -ENODEV; + } + isp->mmio_base_phys[res] = mem->start; + isp->mmio_size[res] = resource_size(mem); + + /* map the region */ + isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res], + isp->mmio_size[res]); + if (!isp->mmio_base[res]) { + dev_err(isp->dev, "cannot map camera register I/O region\n"); + return -ENODEV; + } + + return 0; +} + +/* + * isp_probe - Probe ISP platform device + * @pdev: Pointer to ISP platform device + * + * Returns 0 if successful, + * -ENOMEM if no memory available, + * -ENODEV if no platform device resources found + * or no space for remapping registers, + * -EINVAL if couldn't install ISR, + * or clk_get return error value. + */ +static int isp_probe(struct platform_device *pdev) +{ + struct isp_platform_data *pdata = pdev->dev.platform_data; + struct isp_device *isp; + int ret; + int i, m; + + if (pdata == NULL) + return -EINVAL; + + isp = kzalloc(sizeof(*isp), GFP_KERNEL); + if (!isp) { + dev_err(&pdev->dev, "could not allocate memory\n"); + return -ENOMEM; + } + + isp->autoidle = autoidle; + isp->platform_cb.set_xclk = isp_set_xclk; + isp->platform_cb.set_pixel_clock = isp_set_pixel_clock; + + mutex_init(&isp->isp_mutex); + spin_lock_init(&isp->stat_lock); + + isp->dev = &pdev->dev; + isp->pdata = pdata; + isp->ref_count = 0; + + isp->raw_dmamask = DMA_BIT_MASK(32); + isp->dev->dma_mask = &isp->raw_dmamask; + isp->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + platform_set_drvdata(pdev, isp); + + /* Regulators */ + isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1"); + isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2"); + + /* Clocks */ + ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN); + if (ret < 0) + goto error; + + ret = isp_get_clocks(isp); + if (ret < 0) + goto error; + + if (omap3isp_get(isp) == NULL) + goto error; + + ret = isp_reset(isp); + if (ret < 0) + goto error_isp; + + /* Memory resources */ + isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); + dev_info(isp->dev, "Revision %d.%d found\n", + (isp->revision & 0xf0) >> 4, isp->revision & 0x0f); + + for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++) + if (isp->revision == isp_res_maps[m].isp_rev) + break; + + if (m == ARRAY_SIZE(isp_res_maps)) { + dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n", + (isp->revision & 0xf0) >> 4, isp->revision & 0xf); + ret = -ENODEV; + goto error_isp; + } + + for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) { + if (isp_res_maps[m].map & 1 << i) { + ret = isp_map_mem_resource(pdev, isp, i); + if (ret) + goto error_isp; + } + } + + /* IOMMU */ + isp->iommu = iommu_get("isp"); + if (IS_ERR_OR_NULL(isp->iommu)) { + isp->iommu = NULL; + ret = -ENODEV; + goto error_isp; + } + + /* Interrupt */ + isp->irq_num = platform_get_irq(pdev, 0); + if (isp->irq_num <= 0) { + dev_err(isp->dev, "No IRQ resource\n"); + ret = -ENODEV; + goto error_isp; + } + + if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) { + dev_err(isp->dev, "Unable to request IRQ\n"); + ret = -EINVAL; + goto error_isp; + } + + /* Entities */ + ret = isp_initialize_modules(isp); + if (ret < 0) + goto error_irq; + + ret = isp_register_entities(isp); + if (ret < 0) + goto error_modules; + + isp_power_settings(isp, 1); + omap3isp_put(isp); + + return 0; + +error_modules: + isp_cleanup_modules(isp); +error_irq: + free_irq(isp->irq_num, isp); +error_isp: + iommu_put(isp->iommu); + omap3isp_put(isp); +error: + isp_put_clocks(isp); + + for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) { + if (isp->mmio_base[i]) { + iounmap(isp->mmio_base[i]); + isp->mmio_base[i] = NULL; + } + + if (isp->mmio_base_phys[i]) { + release_mem_region(isp->mmio_base_phys[i], + isp->mmio_size[i]); + isp->mmio_base_phys[i] = 0; + } + } + regulator_put(isp->isp_csiphy2.vdd); + regulator_put(isp->isp_csiphy1.vdd); + platform_set_drvdata(pdev, NULL); + kfree(isp); + + return ret; +} + +static const struct dev_pm_ops omap3isp_pm_ops = { + .prepare = isp_pm_prepare, + .suspend = isp_pm_suspend, + .resume = isp_pm_resume, + .complete = isp_pm_complete, +}; + +static struct platform_device_id omap3isp_id_table[] = { + { "omap3isp", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, omap3isp_id_table); + +static struct platform_driver omap3isp_driver = { + .probe = isp_probe, + .remove = isp_remove, + .id_table = omap3isp_id_table, + .driver = { + .owner = THIS_MODULE, + .name = "omap3isp", + .pm = &omap3isp_pm_ops, + }, +}; + +/* + * isp_init - ISP module initialization. + */ +static int __init isp_init(void) +{ + return platform_driver_register(&omap3isp_driver); +} + +/* + * isp_cleanup - ISP module cleanup. + */ +static void __exit isp_cleanup(void) +{ + platform_driver_unregister(&omap3isp_driver); +} + +module_init(isp_init); +module_exit(isp_cleanup); + +MODULE_AUTHOR("Nokia Corporation"); +MODULE_DESCRIPTION("TI OMAP3 ISP driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h new file mode 100644 index 0000000..a8dfea4 --- /dev/null +++ b/drivers/media/video/omap3isp/isp.h @@ -0,0 +1,430 @@ +/* + * isp.h + * + * TI OMAP3 ISP - Core + * + * Copyright (C) 2009-2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_CORE_H +#define OMAP3_ISP_CORE_H + +#include +#include +#include +#include +#include +#include +#include + +#include "ispstat.h" +#include "ispccdc.h" +#include "ispreg.h" +#include "ispresizer.h" +#include "isppreview.h" +#include "ispcsiphy.h" +#include "ispcsi2.h" +#include "ispccp2.h" + +#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) + +#define ISP_TOK_TERM 0xFFFFFFFF /* + * terminating token for ISP + * modules reg list + */ +#define to_isp_device(ptr_module) \ + container_of(ptr_module, struct isp_device, isp_##ptr_module) +#define to_device(ptr_module) \ + (to_isp_device(ptr_module)->dev) + +enum isp_mem_resources { + OMAP3_ISP_IOMEM_MAIN, + OMAP3_ISP_IOMEM_CCP2, + OMAP3_ISP_IOMEM_CCDC, + OMAP3_ISP_IOMEM_HIST, + OMAP3_ISP_IOMEM_H3A, + OMAP3_ISP_IOMEM_PREV, + OMAP3_ISP_IOMEM_RESZ, + OMAP3_ISP_IOMEM_SBL, + OMAP3_ISP_IOMEM_CSI2A_REGS1, + OMAP3_ISP_IOMEM_CSIPHY2, + OMAP3_ISP_IOMEM_CSI2A_REGS2, + OMAP3_ISP_IOMEM_CSI2C_REGS1, + OMAP3_ISP_IOMEM_CSIPHY1, + OMAP3_ISP_IOMEM_CSI2C_REGS2, + OMAP3_ISP_IOMEM_LAST +}; + +enum isp_sbl_resource { + OMAP3_ISP_SBL_CSI1_READ = 0x1, + OMAP3_ISP_SBL_CSI1_WRITE = 0x2, + OMAP3_ISP_SBL_CSI2A_WRITE = 0x4, + OMAP3_ISP_SBL_CSI2C_WRITE = 0x8, + OMAP3_ISP_SBL_CCDC_LSC_READ = 0x10, + OMAP3_ISP_SBL_CCDC_WRITE = 0x20, + OMAP3_ISP_SBL_PREVIEW_READ = 0x40, + OMAP3_ISP_SBL_PREVIEW_WRITE = 0x80, + OMAP3_ISP_SBL_RESIZER_READ = 0x100, + OMAP3_ISP_SBL_RESIZER_WRITE = 0x200, +}; + +enum isp_subclk_resource { + OMAP3_ISP_SUBCLK_CCDC = (1 << 0), + OMAP3_ISP_SUBCLK_H3A = (1 << 1), + OMAP3_ISP_SUBCLK_HIST = (1 << 2), + OMAP3_ISP_SUBCLK_PREVIEW = (1 << 3), + OMAP3_ISP_SUBCLK_RESIZER = (1 << 4), +}; + +enum isp_interface_type { + ISP_INTERFACE_PARALLEL, + ISP_INTERFACE_CSI2A_PHY2, + ISP_INTERFACE_CCP2B_PHY1, + ISP_INTERFACE_CCP2B_PHY2, + ISP_INTERFACE_CSI2C_PHY1, +}; + +/* ISP: OMAP 34xx ES 1.0 */ +#define ISP_REVISION_1_0 0x10 +/* ISP2: OMAP 34xx ES 2.0, 2.1 and 3.0 */ +#define ISP_REVISION_2_0 0x20 +/* ISP2P: OMAP 36xx */ +#define ISP_REVISION_15_0 0xF0 + +/* + * struct isp_res_mapping - Map ISP io resources to ISP revision. + * @isp_rev: ISP_REVISION_x_x + * @map: bitmap for enum isp_mem_resources + */ +struct isp_res_mapping { + u32 isp_rev; + u32 map; +}; + +/* + * struct isp_reg - Structure for ISP register values. + * @reg: 32-bit Register address. + * @val: 32-bit Register value. + */ +struct isp_reg { + enum isp_mem_resources mmio_range; + u32 reg; + u32 val; +}; + +/** + * struct isp_parallel_platform_data - Parallel interface platform data + * @width: Parallel bus width in bits (8, 10, 11 or 12) + * @data_lane_shift: Data lane shifter + * 0 - CAMEXT[13:0] -> CAM[13:0] + * 1 - CAMEXT[13:2] -> CAM[11:0] + * 2 - CAMEXT[13:4] -> CAM[9:0] + * 3 - CAMEXT[13:6] -> CAM[7:0] + * @clk_pol: Pixel clock polarity + * 0 - Non Inverted, 1 - Inverted + * @bridge: CCDC Bridge input control + * ISPCTRL_PAR_BRIDGE_DISABLE - Disable + * ISPCTRL_PAR_BRIDGE_LENDIAN - Little endian + * ISPCTRL_PAR_BRIDGE_BENDIAN - Big endian + */ +struct isp_parallel_platform_data { + unsigned int width; + unsigned int data_lane_shift:2; + unsigned int clk_pol:1; + unsigned int bridge:4; +}; + +/** + * struct isp_ccp2_platform_data - CCP2 interface platform data + * @strobe_clk_pol: Strobe/clock polarity + * 0 - Non Inverted, 1 - Inverted + * @crc: Enable the cyclic redundancy check + * @ccp2_mode: Enable CCP2 compatibility mode + * 0 - MIPI-CSI1 mode, 1 - CCP2 mode + * @phy_layer: Physical layer selection + * ISPCCP2_CTRL_PHY_SEL_CLOCK - Data/clock physical layer + * ISPCCP2_CTRL_PHY_SEL_STROBE - Data/strobe physical layer + * @vpclk_div: Video port output clock control + */ +struct isp_ccp2_platform_data { + unsigned int strobe_clk_pol:1; + unsigned int crc:1; + unsigned int ccp2_mode:1; + unsigned int phy_layer:1; + unsigned int vpclk_div:2; +}; + +/** + * struct isp_csi2_platform_data - CSI2 interface platform data + * @crc: Enable the cyclic redundancy check + * @vpclk_div: Video port output clock control + */ +struct isp_csi2_platform_data { + unsigned crc:1; + unsigned vpclk_div:2; +}; + +struct isp_subdev_i2c_board_info { + struct i2c_board_info *board_info; + int i2c_adapter_id; +}; + +struct isp_v4l2_subdevs_group { + struct isp_subdev_i2c_board_info *subdevs; + enum isp_interface_type interface; + union { + struct isp_parallel_platform_data parallel; + struct isp_ccp2_platform_data ccp2; + struct isp_csi2_platform_data csi2; + } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ +}; + +struct isp_platform_data { + struct isp_v4l2_subdevs_group *subdevs; +}; + +struct isp_platform_callback { + u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel); + int (*csiphy_config)(struct isp_csiphy *phy, + struct isp_csiphy_dphy_cfg *dphy, + struct isp_csiphy_lanes_cfg *lanes); + void (*set_pixel_clock)(struct isp_device *isp, unsigned int pixelclk); +}; + +/* + * struct isp_device - ISP device structure. + * @dev: Device pointer specific to the OMAP3 ISP. + * @revision: Stores current ISP module revision. + * @irq_num: Currently used IRQ number. + * @mmio_base: Array with kernel base addresses for ioremapped ISP register + * regions. + * @mmio_base_phys: Array with physical L4 bus addresses for ISP register + * regions. + * @mmio_size: Array with ISP register regions size in bytes. + * @raw_dmamask: Raw DMA mask + * @stat_lock: Spinlock for handling statistics + * @isp_mutex: Mutex for serializing requests to ISP. + * @has_context: Context has been saved at least once and can be restored. + * @ref_count: Reference count for handling multiple ISP requests. + * @cam_ick: Pointer to camera interface clock structure. + * @cam_mclk: Pointer to camera functional clock structure. + * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure. + * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. + * @l3_ick: Pointer to OMAP3 L3 bus interface clock. + * @irq: Currently attached ISP ISR callbacks information structure. + * @isp_af: Pointer to current settings for ISP AutoFocus SCM. + * @isp_hist: Pointer to current settings for ISP Histogram SCM. + * @isp_h3a: Pointer to current settings for ISP Auto Exposure and + * White Balance SCM. + * @isp_res: Pointer to current settings for ISP Resizer. + * @isp_prev: Pointer to current settings for ISP Preview. + * @isp_ccdc: Pointer to current settings for ISP CCDC. + * @iommu: Pointer to requested IOMMU instance for ISP. + * @platform_cb: ISP driver callback function pointers for platform code + * + * This structure is used to store the OMAP ISP Information. + */ +struct isp_device { + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct device *dev; + u32 revision; + + /* platform HW resources */ + struct isp_platform_data *pdata; + unsigned int irq_num; + + void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST]; + unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST]; + resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST]; + + u64 raw_dmamask; + + /* ISP Obj */ + spinlock_t stat_lock; /* common lock for statistic drivers */ + struct mutex isp_mutex; /* For handling ref_count field */ + int has_context; + int ref_count; + unsigned int autoidle; + u32 xclk_divisor[2]; /* Two clocks, a and b. */ +#define ISP_CLK_CAM_ICK 0 +#define ISP_CLK_CAM_MCLK 1 +#define ISP_CLK_DPLL4_M5_CK 2 +#define ISP_CLK_CSI2_FCK 3 +#define ISP_CLK_L3_ICK 4 + struct clk *clock[5]; + + /* ISP modules */ + struct ispstat isp_af; + struct ispstat isp_aewb; + struct ispstat isp_hist; + struct isp_res_device isp_res; + struct isp_prev_device isp_prev; + struct isp_ccdc_device isp_ccdc; + struct isp_csi2_device isp_csi2a; + struct isp_csi2_device isp_csi2c; + struct isp_ccp2_device isp_ccp2; + struct isp_csiphy isp_csiphy1; + struct isp_csiphy isp_csiphy2; + + unsigned int sbl_resources; + unsigned int subclk_resources; + + struct iommu *iommu; + + struct isp_platform_callback platform_cb; +}; + +#define v4l2_dev_to_isp_device(dev) \ + container_of(dev, struct isp_device, v4l2_dev) + +void omap3isp_hist_dma_done(struct isp_device *isp); + +void omap3isp_flush(struct isp_device *isp); + +int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, + atomic_t *stopping); + +int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, + atomic_t *stopping); + +int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, + enum isp_pipeline_stream_state state); +void omap3isp_configure_bridge(struct isp_device *isp, + enum ccdc_input_entity input, + const struct isp_parallel_platform_data *pdata); + +#define ISP_XCLK_NONE -1 +#define ISP_XCLK_A 0 +#define ISP_XCLK_B 1 + +struct isp_device *omap3isp_get(struct isp_device *isp); +void omap3isp_put(struct isp_device *isp); + +void omap3isp_print_status(struct isp_device *isp); + +void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res); +void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res); + +void omap3isp_subclk_enable(struct isp_device *isp, + enum isp_subclk_resource res); +void omap3isp_subclk_disable(struct isp_device *isp, + enum isp_subclk_resource res); + +int omap3isp_pipeline_pm_use(struct media_entity *entity, int use); + +int omap3isp_register_entities(struct platform_device *pdev, + struct v4l2_device *v4l2_dev); +void omap3isp_unregister_entities(struct platform_device *pdev); + +/* + * isp_reg_readl - Read value of an OMAP3 ISP register + * @dev: Device pointer specific to the OMAP3 ISP. + * @isp_mmio_range: Range to which the register offset refers to. + * @reg_offset: Register offset to read from. + * + * Returns an unsigned 32 bit value with the required register contents. + */ +static inline +u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range, + u32 reg_offset) +{ + return __raw_readl(isp->mmio_base[isp_mmio_range] + reg_offset); +} + +/* + * isp_reg_writel - Write value to an OMAP3 ISP register + * @dev: Device pointer specific to the OMAP3 ISP. + * @reg_value: 32 bit value to write to the register. + * @isp_mmio_range: Range to which the register offset refers to. + * @reg_offset: Register offset to write into. + */ +static inline +void isp_reg_writel(struct isp_device *isp, u32 reg_value, + enum isp_mem_resources isp_mmio_range, u32 reg_offset) +{ + __raw_writel(reg_value, isp->mmio_base[isp_mmio_range] + reg_offset); +} + +/* + * isp_reg_and - Clear individual bits in an OMAP3 ISP register + * @dev: Device pointer specific to the OMAP3 ISP. + * @mmio_range: Range to which the register offset refers to. + * @reg: Register offset to work on. + * @clr_bits: 32 bit value which would be cleared in the register. + */ +static inline +void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range, + u32 reg, u32 clr_bits) +{ + u32 v = isp_reg_readl(isp, mmio_range, reg); + + isp_reg_writel(isp, v & ~clr_bits, mmio_range, reg); +} + +/* + * isp_reg_set - Set individual bits in an OMAP3 ISP register + * @dev: Device pointer specific to the OMAP3 ISP. + * @mmio_range: Range to which the register offset refers to. + * @reg: Register offset to work on. + * @set_bits: 32 bit value which would be set in the register. + */ +static inline +void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range, + u32 reg, u32 set_bits) +{ + u32 v = isp_reg_readl(isp, mmio_range, reg); + + isp_reg_writel(isp, v | set_bits, mmio_range, reg); +} + +/* + * isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register + * @dev: Device pointer specific to the OMAP3 ISP. + * @mmio_range: Range to which the register offset refers to. + * @reg: Register offset to work on. + * @clr_bits: 32 bit value which would be cleared in the register. + * @set_bits: 32 bit value which would be set in the register. + * + * The clear operation is done first, and then the set operation. + */ +static inline +void isp_reg_clr_set(struct isp_device *isp, enum isp_mem_resources mmio_range, + u32 reg, u32 clr_bits, u32 set_bits) +{ + u32 v = isp_reg_readl(isp, mmio_range, reg); + + isp_reg_writel(isp, (v & ~clr_bits) | set_bits, mmio_range, reg); +} + +static inline enum v4l2_buf_type +isp_pad_buffer_type(const struct v4l2_subdev *subdev, int pad) +{ + if (pad >= subdev->entity.num_pads) + return 0; + + if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK) + return V4L2_BUF_TYPE_VIDEO_OUTPUT; + else + return V4L2_BUF_TYPE_VIDEO_CAPTURE; +} + +#endif /* OMAP3_ISP_CORE_H */ diff --git a/drivers/media/video/omap3isp/ispreg.h b/drivers/media/video/omap3isp/ispreg.h new file mode 100644 index 0000000..69f6af6 --- /dev/null +++ b/drivers/media/video/omap3isp/ispreg.h @@ -0,0 +1,1589 @@ +/* + * ispreg.h + * + * TI OMAP3 ISP - Registers definitions + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_REG_H +#define OMAP3_ISP_REG_H + +#include + + +#define CM_CAM_MCLK_HZ 172800000 /* Hz */ + +/* ISP Submodules offset */ + +#define OMAP3ISP_REG_BASE OMAP3430_ISP_BASE +#define OMAP3ISP_REG(offset) (OMAP3ISP_REG_BASE + (offset)) + +#define OMAP3ISP_CCP2_REG_OFFSET 0x0400 +#define OMAP3ISP_CCP2_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CCP2_REG_OFFSET) +#define OMAP3ISP_CCP2_REG(offset) (OMAP3ISP_CCP2_REG_BASE + (offset)) + +#define OMAP3ISP_CCDC_REG_OFFSET 0x0600 +#define OMAP3ISP_CCDC_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CCDC_REG_OFFSET) +#define OMAP3ISP_CCDC_REG(offset) (OMAP3ISP_CCDC_REG_BASE + (offset)) + +#define OMAP3ISP_HIST_REG_OFFSET 0x0A00 +#define OMAP3ISP_HIST_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_HIST_REG_OFFSET) +#define OMAP3ISP_HIST_REG(offset) (OMAP3ISP_HIST_REG_BASE + (offset)) + +#define OMAP3ISP_H3A_REG_OFFSET 0x0C00 +#define OMAP3ISP_H3A_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_H3A_REG_OFFSET) +#define OMAP3ISP_H3A_REG(offset) (OMAP3ISP_H3A_REG_BASE + (offset)) + +#define OMAP3ISP_PREV_REG_OFFSET 0x0E00 +#define OMAP3ISP_PREV_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_PREV_REG_OFFSET) +#define OMAP3ISP_PREV_REG(offset) (OMAP3ISP_PREV_REG_BASE + (offset)) + +#define OMAP3ISP_RESZ_REG_OFFSET 0x1000 +#define OMAP3ISP_RESZ_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_RESZ_REG_OFFSET) +#define OMAP3ISP_RESZ_REG(offset) (OMAP3ISP_RESZ_REG_BASE + (offset)) + +#define OMAP3ISP_SBL_REG_OFFSET 0x1200 +#define OMAP3ISP_SBL_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_SBL_REG_OFFSET) +#define OMAP3ISP_SBL_REG(offset) (OMAP3ISP_SBL_REG_BASE + (offset)) + +#define OMAP3ISP_CSI2A_REGS1_REG_OFFSET 0x1800 +#define OMAP3ISP_CSI2A_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CSI2A_REGS1_REG_OFFSET) +#define OMAP3ISP_CSI2A_REGS1_REG(offset) \ + (OMAP3ISP_CSI2A_REGS1_REG_BASE + (offset)) + +#define OMAP3ISP_CSIPHY2_REG_OFFSET 0x1970 +#define OMAP3ISP_CSIPHY2_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CSIPHY2_REG_OFFSET) +#define OMAP3ISP_CSIPHY2_REG(offset) (OMAP3ISP_CSIPHY2_REG_BASE + (offset)) + +#define OMAP3ISP_CSI2A_REGS2_REG_OFFSET 0x19C0 +#define OMAP3ISP_CSI2A_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CSI2A_REGS2_REG_OFFSET) +#define OMAP3ISP_CSI2A_REGS2_REG(offset) \ + (OMAP3ISP_CSI2A_REGS2_REG_BASE + (offset)) + +#define OMAP3ISP_CSI2C_REGS1_REG_OFFSET 0x1C00 +#define OMAP3ISP_CSI2C_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CSI2C_REGS1_REG_OFFSET) +#define OMAP3ISP_CSI2C_REGS1_REG(offset) \ + (OMAP3ISP_CSI2C_REGS1_REG_BASE + (offset)) + +#define OMAP3ISP_CSIPHY1_REG_OFFSET 0x1D70 +#define OMAP3ISP_CSIPHY1_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CSIPHY1_REG_OFFSET) +#define OMAP3ISP_CSIPHY1_REG(offset) (OMAP3ISP_CSIPHY1_REG_BASE + (offset)) + +#define OMAP3ISP_CSI2C_REGS2_REG_OFFSET 0x1DC0 +#define OMAP3ISP_CSI2C_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CSI2C_REGS2_REG_OFFSET) +#define OMAP3ISP_CSI2C_REGS2_REG(offset) \ + (OMAP3ISP_CSI2C_REGS2_REG_BASE + (offset)) + +/* ISP module register offset */ + +#define ISP_REVISION (0x000) +#define ISP_SYSCONFIG (0x004) +#define ISP_SYSSTATUS (0x008) +#define ISP_IRQ0ENABLE (0x00C) +#define ISP_IRQ0STATUS (0x010) +#define ISP_IRQ1ENABLE (0x014) +#define ISP_IRQ1STATUS (0x018) +#define ISP_TCTRL_GRESET_LENGTH (0x030) +#define ISP_TCTRL_PSTRB_REPLAY (0x034) +#define ISP_CTRL (0x040) +#define ISP_SECURE (0x044) +#define ISP_TCTRL_CTRL (0x050) +#define ISP_TCTRL_FRAME (0x054) +#define ISP_TCTRL_PSTRB_DELAY (0x058) +#define ISP_TCTRL_STRB_DELAY (0x05C) +#define ISP_TCTRL_SHUT_DELAY (0x060) +#define ISP_TCTRL_PSTRB_LENGTH (0x064) +#define ISP_TCTRL_STRB_LENGTH (0x068) +#define ISP_TCTRL_SHUT_LENGTH (0x06C) +#define ISP_PING_PONG_ADDR (0x070) +#define ISP_PING_PONG_MEM_RANGE (0x074) +#define ISP_PING_PONG_BUF_SIZE (0x078) + +/* CCP2 receiver registers */ + +#define ISPCCP2_REVISION (0x000) +#define ISPCCP2_SYSCONFIG (0x004) +#define ISPCCP2_SYSCONFIG_SOFT_RESET (1 << 1) +#define ISPCCP2_SYSCONFIG_AUTO_IDLE 0x1 +#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 +#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_FORCE \ + (0x0 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_NO \ + (0x1 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART \ + (0x2 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCCP2_SYSSTATUS (0x008) +#define ISPCCP2_SYSSTATUS_RESET_DONE (1 << 0) +#define ISPCCP2_LC01_IRQENABLE (0x00C) +#define ISPCCP2_LC01_IRQSTATUS (0x010) +#define ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ (1 << 11) +#define ISPCCP2_LC01_IRQSTATUS_LC0_LE_IRQ (1 << 10) +#define ISPCCP2_LC01_IRQSTATUS_LC0_LS_IRQ (1 << 9) +#define ISPCCP2_LC01_IRQSTATUS_LC0_FE_IRQ (1 << 8) +#define ISPCCP2_LC01_IRQSTATUS_LC0_COUNT_IRQ (1 << 7) +#define ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ (1 << 5) +#define ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ (1 << 4) +#define ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ (1 << 3) +#define ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ (1 << 2) +#define ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ (1 << 1) +#define ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ (1 << 0) + +#define ISPCCP2_LC23_IRQENABLE (0x014) +#define ISPCCP2_LC23_IRQSTATUS (0x018) +#define ISPCCP2_LCM_IRQENABLE (0x02C) +#define ISPCCP2_LCM_IRQSTATUS_EOF_IRQ (1 << 0) +#define ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ (1 << 1) +#define ISPCCP2_LCM_IRQSTATUS (0x030) +#define ISPCCP2_CTRL (0x040) +#define ISPCCP2_CTRL_IF_EN (1 << 0) +#define ISPCCP2_CTRL_PHY_SEL (1 << 1) +#define ISPCCP2_CTRL_PHY_SEL_CLOCK (0 << 1) +#define ISPCCP2_CTRL_PHY_SEL_STROBE (1 << 1) +#define ISPCCP2_CTRL_PHY_SEL_MASK 0x1 +#define ISPCCP2_CTRL_PHY_SEL_SHIFT 1 +#define ISPCCP2_CTRL_IO_OUT_SEL (1 << 2) +#define ISPCCP2_CTRL_MODE (1 << 4) +#define ISPCCP2_CTRL_VP_CLK_FORCE_ON (1 << 9) +#define ISPCCP2_CTRL_INV (1 << 10) +#define ISPCCP2_CTRL_INV_MASK 0x1 +#define ISPCCP2_CTRL_INV_SHIFT 10 +#define ISPCCP2_CTRL_VP_ONLY_EN (1 << 11) +#define ISPCCP2_CTRL_VP_CLK_POL (1 << 12) +#define ISPCCP2_CTRL_VPCLK_DIV_SHIFT 15 +#define ISPCCP2_CTRL_VPCLK_DIV_MASK 0x1ffff /* [31:15] */ +#define ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT 8 /* 3430 bits */ +#define ISPCCP2_CTRL_VP_OUT_CTRL_MASK 0x3 /* 3430 bits */ +#define ISPCCP2_DBG (0x044) +#define ISPCCP2_GNQ (0x048) +#define ISPCCP2_LCx_CTRL(x) ((0x050)+0x30*(x)) +#define ISPCCP2_LCx_CTRL_CHAN_EN (1 << 0) +#define ISPCCP2_LCx_CTRL_CRC_EN (1 << 19) +#define ISPCCP2_LCx_CTRL_CRC_MASK 0x1 +#define ISPCCP2_LCx_CTRL_CRC_SHIFT 2 +#define ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0 19 +#define ISPCCP2_LCx_CTRL_REGION_EN (1 << 1) +#define ISPCCP2_LCx_CTRL_REGION_MASK 0x1 +#define ISPCCP2_LCx_CTRL_REGION_SHIFT 1 +#define ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0 0x3f +#define ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0 0x2 +#define ISPCCP2_LCx_CTRL_FORMAT_MASK 0x1f +#define ISPCCP2_LCx_CTRL_FORMAT_SHIFT 0x3 +#define ISPCCP2_LCx_CODE(x) ((0x054)+0x30*(x)) +#define ISPCCP2_LCx_STAT_START(x) ((0x058)+0x30*(x)) +#define ISPCCP2_LCx_STAT_SIZE(x) ((0x05C)+0x30*(x)) +#define ISPCCP2_LCx_SOF_ADDR(x) ((0x060)+0x30*(x)) +#define ISPCCP2_LCx_EOF_ADDR(x) ((0x064)+0x30*(x)) +#define ISPCCP2_LCx_DAT_START(x) ((0x068)+0x30*(x)) +#define ISPCCP2_LCx_DAT_SIZE(x) ((0x06C)+0x30*(x)) +#define ISPCCP2_LCx_DAT_MASK 0xFFF +#define ISPCCP2_LCx_DAT_SHIFT 16 +#define ISPCCP2_LCx_DAT_PING_ADDR(x) ((0x070)+0x30*(x)) +#define ISPCCP2_LCx_DAT_PONG_ADDR(x) ((0x074)+0x30*(x)) +#define ISPCCP2_LCx_DAT_OFST(x) ((0x078)+0x30*(x)) +#define ISPCCP2_LCM_CTRL (0x1D0) +#define ISPCCP2_LCM_CTRL_CHAN_EN (1 << 0) +#define ISPCCP2_LCM_CTRL_DST_PORT (1 << 2) +#define ISPCCP2_LCM_CTRL_DST_PORT_SHIFT 2 +#define ISPCCP2_LCM_CTRL_READ_THROTTLE_SHIFT 3 +#define ISPCCP2_LCM_CTRL_READ_THROTTLE_MASK 0x11 +#define ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT 5 +#define ISPCCP2_LCM_CTRL_BURST_SIZE_MASK 0x7 +#define ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT 16 +#define ISPCCP2_LCM_CTRL_SRC_FORMAT_MASK 0x7 +#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT 20 +#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_MASK 0x3 +#define ISPCCP2_LCM_CTRL_SRC_DPCM_PRED (1 << 22) +#define ISPCCP2_LCM_CTRL_SRC_PACK (1 << 23) +#define ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT 24 +#define ISPCCP2_LCM_CTRL_DST_FORMAT_MASK 0x7 +#define ISPCCP2_LCM_VSIZE (0x1D4) +#define ISPCCP2_LCM_VSIZE_SHIFT 16 +#define ISPCCP2_LCM_HSIZE (0x1D8) +#define ISPCCP2_LCM_HSIZE_SHIFT 16 +#define ISPCCP2_LCM_PREFETCH (0x1DC) +#define ISPCCP2_LCM_PREFETCH_SHIFT 3 +#define ISPCCP2_LCM_SRC_ADDR (0x1E0) +#define ISPCCP2_LCM_SRC_OFST (0x1E4) +#define ISPCCP2_LCM_DST_ADDR (0x1E8) +#define ISPCCP2_LCM_DST_OFST (0x1EC) + +/* CCDC module register offset */ + +#define ISPCCDC_PID (0x000) +#define ISPCCDC_PCR (0x004) +#define ISPCCDC_SYN_MODE (0x008) +#define ISPCCDC_HD_VD_WID (0x00C) +#define ISPCCDC_PIX_LINES (0x010) +#define ISPCCDC_HORZ_INFO (0x014) +#define ISPCCDC_VERT_START (0x018) +#define ISPCCDC_VERT_LINES (0x01C) +#define ISPCCDC_CULLING (0x020) +#define ISPCCDC_HSIZE_OFF (0x024) +#define ISPCCDC_SDOFST (0x028) +#define ISPCCDC_SDR_ADDR (0x02C) +#define ISPCCDC_CLAMP (0x030) +#define ISPCCDC_DCSUB (0x034) +#define ISPCCDC_COLPTN (0x038) +#define ISPCCDC_BLKCMP (0x03C) +#define ISPCCDC_FPC (0x040) +#define ISPCCDC_FPC_ADDR (0x044) +#define ISPCCDC_VDINT (0x048) +#define ISPCCDC_ALAW (0x04C) +#define ISPCCDC_REC656IF (0x050) +#define ISPCCDC_CFG (0x054) +#define ISPCCDC_FMTCFG (0x058) +#define ISPCCDC_FMT_HORZ (0x05C) +#define ISPCCDC_FMT_VERT (0x060) +#define ISPCCDC_FMT_ADDR0 (0x064) +#define ISPCCDC_FMT_ADDR1 (0x068) +#define ISPCCDC_FMT_ADDR2 (0x06C) +#define ISPCCDC_FMT_ADDR3 (0x070) +#define ISPCCDC_FMT_ADDR4 (0x074) +#define ISPCCDC_FMT_ADDR5 (0x078) +#define ISPCCDC_FMT_ADDR6 (0x07C) +#define ISPCCDC_FMT_ADDR7 (0x080) +#define ISPCCDC_PRGEVEN0 (0x084) +#define ISPCCDC_PRGEVEN1 (0x088) +#define ISPCCDC_PRGODD0 (0x08C) +#define ISPCCDC_PRGODD1 (0x090) +#define ISPCCDC_VP_OUT (0x094) + +#define ISPCCDC_LSC_CONFIG (0x098) +#define ISPCCDC_LSC_INITIAL (0x09C) +#define ISPCCDC_LSC_TABLE_BASE (0x0A0) +#define ISPCCDC_LSC_TABLE_OFFSET (0x0A4) + +/* SBL */ +#define ISPSBL_PCR 0x4 +#define ISPSBL_PCR_H3A_AEAWB_WBL_OVF (1 << 16) +#define ISPSBL_PCR_H3A_AF_WBL_OVF (1 << 17) +#define ISPSBL_PCR_RSZ4_WBL_OVF (1 << 18) +#define ISPSBL_PCR_RSZ3_WBL_OVF (1 << 19) +#define ISPSBL_PCR_RSZ2_WBL_OVF (1 << 20) +#define ISPSBL_PCR_RSZ1_WBL_OVF (1 << 21) +#define ISPSBL_PCR_PRV_WBL_OVF (1 << 22) +#define ISPSBL_PCR_CCDC_WBL_OVF (1 << 23) +#define ISPSBL_PCR_CCDCPRV_2_RSZ_OVF (1 << 24) +#define ISPSBL_PCR_CSIA_WBL_OVF (1 << 25) +#define ISPSBL_PCR_CSIB_WBL_OVF (1 << 26) +#define ISPSBL_CCDC_WR_0 (0x028) +#define ISPSBL_CCDC_WR_0_DATA_READY (1 << 21) +#define ISPSBL_CCDC_WR_1 (0x02C) +#define ISPSBL_CCDC_WR_2 (0x030) +#define ISPSBL_CCDC_WR_3 (0x034) + +#define ISPSBL_SDR_REQ_EXP 0xF8 +#define ISPSBL_SDR_REQ_HIST_EXP_SHIFT 0 +#define ISPSBL_SDR_REQ_HIST_EXP_MASK (0x3FF) +#define ISPSBL_SDR_REQ_RSZ_EXP_SHIFT 10 +#define ISPSBL_SDR_REQ_RSZ_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT) +#define ISPSBL_SDR_REQ_PRV_EXP_SHIFT 20 +#define ISPSBL_SDR_REQ_PRV_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_PRV_EXP_SHIFT) + +/* Histogram registers */ +#define ISPHIST_PID (0x000) +#define ISPHIST_PCR (0x004) +#define ISPHIST_CNT (0x008) +#define ISPHIST_WB_GAIN (0x00C) +#define ISPHIST_R0_HORZ (0x010) +#define ISPHIST_R0_VERT (0x014) +#define ISPHIST_R1_HORZ (0x018) +#define ISPHIST_R1_VERT (0x01C) +#define ISPHIST_R2_HORZ (0x020) +#define ISPHIST_R2_VERT (0x024) +#define ISPHIST_R3_HORZ (0x028) +#define ISPHIST_R3_VERT (0x02C) +#define ISPHIST_ADDR (0x030) +#define ISPHIST_DATA (0x034) +#define ISPHIST_RADD (0x038) +#define ISPHIST_RADD_OFF (0x03C) +#define ISPHIST_H_V_INFO (0x040) + +/* H3A module registers */ +#define ISPH3A_PID (0x000) +#define ISPH3A_PCR (0x004) +#define ISPH3A_AEWWIN1 (0x04C) +#define ISPH3A_AEWINSTART (0x050) +#define ISPH3A_AEWINBLK (0x054) +#define ISPH3A_AEWSUBWIN (0x058) +#define ISPH3A_AEWBUFST (0x05C) +#define ISPH3A_AFPAX1 (0x008) +#define ISPH3A_AFPAX2 (0x00C) +#define ISPH3A_AFPAXSTART (0x010) +#define ISPH3A_AFIIRSH (0x014) +#define ISPH3A_AFBUFST (0x018) +#define ISPH3A_AFCOEF010 (0x01C) +#define ISPH3A_AFCOEF032 (0x020) +#define ISPH3A_AFCOEF054 (0x024) +#define ISPH3A_AFCOEF076 (0x028) +#define ISPH3A_AFCOEF098 (0x02C) +#define ISPH3A_AFCOEF0010 (0x030) +#define ISPH3A_AFCOEF110 (0x034) +#define ISPH3A_AFCOEF132 (0x038) +#define ISPH3A_AFCOEF154 (0x03C) +#define ISPH3A_AFCOEF176 (0x040) +#define ISPH3A_AFCOEF198 (0x044) +#define ISPH3A_AFCOEF1010 (0x048) + +#define ISPPRV_PCR (0x004) +#define ISPPRV_HORZ_INFO (0x008) +#define ISPPRV_VERT_INFO (0x00C) +#define ISPPRV_RSDR_ADDR (0x010) +#define ISPPRV_RADR_OFFSET (0x014) +#define ISPPRV_DSDR_ADDR (0x018) +#define ISPPRV_DRKF_OFFSET (0x01C) +#define ISPPRV_WSDR_ADDR (0x020) +#define ISPPRV_WADD_OFFSET (0x024) +#define ISPPRV_AVE (0x028) +#define ISPPRV_HMED (0x02C) +#define ISPPRV_NF (0x030) +#define ISPPRV_WB_DGAIN (0x034) +#define ISPPRV_WBGAIN (0x038) +#define ISPPRV_WBSEL (0x03C) +#define ISPPRV_CFA (0x040) +#define ISPPRV_BLKADJOFF (0x044) +#define ISPPRV_RGB_MAT1 (0x048) +#define ISPPRV_RGB_MAT2 (0x04C) +#define ISPPRV_RGB_MAT3 (0x050) +#define ISPPRV_RGB_MAT4 (0x054) +#define ISPPRV_RGB_MAT5 (0x058) +#define ISPPRV_RGB_OFF1 (0x05C) +#define ISPPRV_RGB_OFF2 (0x060) +#define ISPPRV_CSC0 (0x064) +#define ISPPRV_CSC1 (0x068) +#define ISPPRV_CSC2 (0x06C) +#define ISPPRV_CSC_OFFSET (0x070) +#define ISPPRV_CNT_BRT (0x074) +#define ISPPRV_CSUP (0x078) +#define ISPPRV_SETUP_YC (0x07C) +#define ISPPRV_SET_TBL_ADDR (0x080) +#define ISPPRV_SET_TBL_DATA (0x084) +#define ISPPRV_CDC_THR0 (0x090) +#define ISPPRV_CDC_THR1 (ISPPRV_CDC_THR0 + (0x4)) +#define ISPPRV_CDC_THR2 (ISPPRV_CDC_THR0 + (0x4) * 2) +#define ISPPRV_CDC_THR3 (ISPPRV_CDC_THR0 + (0x4) * 3) + +#define ISPPRV_REDGAMMA_TABLE_ADDR 0x0000 +#define ISPPRV_GREENGAMMA_TABLE_ADDR 0x0400 +#define ISPPRV_BLUEGAMMA_TABLE_ADDR 0x0800 +#define ISPPRV_NF_TABLE_ADDR 0x0C00 +#define ISPPRV_YENH_TABLE_ADDR 0x1000 +#define ISPPRV_CFA_TABLE_ADDR 0x1400 + +#define ISPPRV_MAXOUTPUT_WIDTH 1280 +#define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300 +#define ISPPRV_MAXOUTPUT_WIDTH_3630 4096 +#define ISPRSZ_MIN_OUTPUT 64 +#define ISPRSZ_MAX_OUTPUT 3312 + +/* Resizer module register offset */ +#define ISPRSZ_PID (0x000) +#define ISPRSZ_PCR (0x004) +#define ISPRSZ_CNT (0x008) +#define ISPRSZ_OUT_SIZE (0x00C) +#define ISPRSZ_IN_START (0x010) +#define ISPRSZ_IN_SIZE (0x014) +#define ISPRSZ_SDR_INADD (0x018) +#define ISPRSZ_SDR_INOFF (0x01C) +#define ISPRSZ_SDR_OUTADD (0x020) +#define ISPRSZ_SDR_OUTOFF (0x024) +#define ISPRSZ_HFILT10 (0x028) +#define ISPRSZ_HFILT32 (0x02C) +#define ISPRSZ_HFILT54 (0x030) +#define ISPRSZ_HFILT76 (0x034) +#define ISPRSZ_HFILT98 (0x038) +#define ISPRSZ_HFILT1110 (0x03C) +#define ISPRSZ_HFILT1312 (0x040) +#define ISPRSZ_HFILT1514 (0x044) +#define ISPRSZ_HFILT1716 (0x048) +#define ISPRSZ_HFILT1918 (0x04C) +#define ISPRSZ_HFILT2120 (0x050) +#define ISPRSZ_HFILT2322 (0x054) +#define ISPRSZ_HFILT2524 (0x058) +#define ISPRSZ_HFILT2726 (0x05C) +#define ISPRSZ_HFILT2928 (0x060) +#define ISPRSZ_HFILT3130 (0x064) +#define ISPRSZ_VFILT10 (0x068) +#define ISPRSZ_VFILT32 (0x06C) +#define ISPRSZ_VFILT54 (0x070) +#define ISPRSZ_VFILT76 (0x074) +#define ISPRSZ_VFILT98 (0x078) +#define ISPRSZ_VFILT1110 (0x07C) +#define ISPRSZ_VFILT1312 (0x080) +#define ISPRSZ_VFILT1514 (0x084) +#define ISPRSZ_VFILT1716 (0x088) +#define ISPRSZ_VFILT1918 (0x08C) +#define ISPRSZ_VFILT2120 (0x090) +#define ISPRSZ_VFILT2322 (0x094) +#define ISPRSZ_VFILT2524 (0x098) +#define ISPRSZ_VFILT2726 (0x09C) +#define ISPRSZ_VFILT2928 (0x0A0) +#define ISPRSZ_VFILT3130 (0x0A4) +#define ISPRSZ_YENH (0x0A8) + +#define ISP_INT_CLR 0xFF113F11 +#define ISPPRV_PCR_EN 1 +#define ISPPRV_PCR_BUSY (1 << 1) +#define ISPPRV_PCR_SOURCE (1 << 2) +#define ISPPRV_PCR_ONESHOT (1 << 3) +#define ISPPRV_PCR_WIDTH (1 << 4) +#define ISPPRV_PCR_INVALAW (1 << 5) +#define ISPPRV_PCR_DRKFEN (1 << 6) +#define ISPPRV_PCR_DRKFCAP (1 << 7) +#define ISPPRV_PCR_HMEDEN (1 << 8) +#define ISPPRV_PCR_NFEN (1 << 9) +#define ISPPRV_PCR_CFAEN (1 << 10) +#define ISPPRV_PCR_CFAFMT_SHIFT 11 +#define ISPPRV_PCR_CFAFMT_MASK 0x7800 +#define ISPPRV_PCR_CFAFMT_BAYER (0 << 11) +#define ISPPRV_PCR_CFAFMT_SONYVGA (1 << 11) +#define ISPPRV_PCR_CFAFMT_RGBFOVEON (2 << 11) +#define ISPPRV_PCR_CFAFMT_DNSPL (3 << 11) +#define ISPPRV_PCR_CFAFMT_HONEYCOMB (4 << 11) +#define ISPPRV_PCR_CFAFMT_RRGGBBFOVEON (5 << 11) +#define ISPPRV_PCR_YNENHEN (1 << 15) +#define ISPPRV_PCR_SUPEN (1 << 16) +#define ISPPRV_PCR_YCPOS_SHIFT 17 +#define ISPPRV_PCR_YCPOS_YCrYCb (0 << 17) +#define ISPPRV_PCR_YCPOS_YCbYCr (1 << 17) +#define ISPPRV_PCR_YCPOS_CbYCrY (2 << 17) +#define ISPPRV_PCR_YCPOS_CrYCbY (3 << 17) +#define ISPPRV_PCR_RSZPORT (1 << 19) +#define ISPPRV_PCR_SDRPORT (1 << 20) +#define ISPPRV_PCR_SCOMP_EN (1 << 21) +#define ISPPRV_PCR_SCOMP_SFT_SHIFT (22) +#define ISPPRV_PCR_SCOMP_SFT_MASK (7 << 22) +#define ISPPRV_PCR_GAMMA_BYPASS (1 << 26) +#define ISPPRV_PCR_DCOREN (1 << 27) +#define ISPPRV_PCR_DCCOUP (1 << 28) +#define ISPPRV_PCR_DRK_FAIL (1 << 31) + +#define ISPPRV_HORZ_INFO_EPH_SHIFT 0 +#define ISPPRV_HORZ_INFO_EPH_MASK 0x3fff +#define ISPPRV_HORZ_INFO_SPH_SHIFT 16 +#define ISPPRV_HORZ_INFO_SPH_MASK 0x3fff0 + +#define ISPPRV_VERT_INFO_ELV_SHIFT 0 +#define ISPPRV_VERT_INFO_ELV_MASK 0x3fff +#define ISPPRV_VERT_INFO_SLV_SHIFT 16 +#define ISPPRV_VERT_INFO_SLV_MASK 0x3fff0 + +#define ISPPRV_AVE_EVENDIST_SHIFT 2 +#define ISPPRV_AVE_EVENDIST_1 0x0 +#define ISPPRV_AVE_EVENDIST_2 0x1 +#define ISPPRV_AVE_EVENDIST_3 0x2 +#define ISPPRV_AVE_EVENDIST_4 0x3 +#define ISPPRV_AVE_ODDDIST_SHIFT 4 +#define ISPPRV_AVE_ODDDIST_1 0x0 +#define ISPPRV_AVE_ODDDIST_2 0x1 +#define ISPPRV_AVE_ODDDIST_3 0x2 +#define ISPPRV_AVE_ODDDIST_4 0x3 + +#define ISPPRV_HMED_THRESHOLD_SHIFT 0 +#define ISPPRV_HMED_EVENDIST (1 << 8) +#define ISPPRV_HMED_ODDDIST (1 << 9) + +#define ISPPRV_WBGAIN_COEF0_SHIFT 0 +#define ISPPRV_WBGAIN_COEF1_SHIFT 8 +#define ISPPRV_WBGAIN_COEF2_SHIFT 16 +#define ISPPRV_WBGAIN_COEF3_SHIFT 24 + +#define ISPPRV_WBSEL_COEF0 0x0 +#define ISPPRV_WBSEL_COEF1 0x1 +#define ISPPRV_WBSEL_COEF2 0x2 +#define ISPPRV_WBSEL_COEF3 0x3 + +#define ISPPRV_WBSEL_N0_0_SHIFT 0 +#define ISPPRV_WBSEL_N0_1_SHIFT 2 +#define ISPPRV_WBSEL_N0_2_SHIFT 4 +#define ISPPRV_WBSEL_N0_3_SHIFT 6 +#define ISPPRV_WBSEL_N1_0_SHIFT 8 +#define ISPPRV_WBSEL_N1_1_SHIFT 10 +#define ISPPRV_WBSEL_N1_2_SHIFT 12 +#define ISPPRV_WBSEL_N1_3_SHIFT 14 +#define ISPPRV_WBSEL_N2_0_SHIFT 16 +#define ISPPRV_WBSEL_N2_1_SHIFT 18 +#define ISPPRV_WBSEL_N2_2_SHIFT 20 +#define ISPPRV_WBSEL_N2_3_SHIFT 22 +#define ISPPRV_WBSEL_N3_0_SHIFT 24 +#define ISPPRV_WBSEL_N3_1_SHIFT 26 +#define ISPPRV_WBSEL_N3_2_SHIFT 28 +#define ISPPRV_WBSEL_N3_3_SHIFT 30 + +#define ISPPRV_CFA_GRADTH_HOR_SHIFT 0 +#define ISPPRV_CFA_GRADTH_VER_SHIFT 8 + +#define ISPPRV_BLKADJOFF_B_SHIFT 0 +#define ISPPRV_BLKADJOFF_G_SHIFT 8 +#define ISPPRV_BLKADJOFF_R_SHIFT 16 + +#define ISPPRV_RGB_MAT1_MTX_RR_SHIFT 0 +#define ISPPRV_RGB_MAT1_MTX_GR_SHIFT 16 + +#define ISPPRV_RGB_MAT2_MTX_BR_SHIFT 0 +#define ISPPRV_RGB_MAT2_MTX_RG_SHIFT 16 + +#define ISPPRV_RGB_MAT3_MTX_GG_SHIFT 0 +#define ISPPRV_RGB_MAT3_MTX_BG_SHIFT 16 + +#define ISPPRV_RGB_MAT4_MTX_RB_SHIFT 0 +#define ISPPRV_RGB_MAT4_MTX_GB_SHIFT 16 + +#define ISPPRV_RGB_MAT5_MTX_BB_SHIFT 0 + +#define ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT 0 +#define ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT 16 + +#define ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT 0 + +#define ISPPRV_CSC0_RY_SHIFT 0 +#define ISPPRV_CSC0_GY_SHIFT 10 +#define ISPPRV_CSC0_BY_SHIFT 20 + +#define ISPPRV_CSC1_RCB_SHIFT 0 +#define ISPPRV_CSC1_GCB_SHIFT 10 +#define ISPPRV_CSC1_BCB_SHIFT 20 + +#define ISPPRV_CSC2_RCR_SHIFT 0 +#define ISPPRV_CSC2_GCR_SHIFT 10 +#define ISPPRV_CSC2_BCR_SHIFT 20 + +#define ISPPRV_CSC_OFFSET_CR_SHIFT 0 +#define ISPPRV_CSC_OFFSET_CB_SHIFT 8 +#define ISPPRV_CSC_OFFSET_Y_SHIFT 16 + +#define ISPPRV_CNT_BRT_BRT_SHIFT 0 +#define ISPPRV_CNT_BRT_CNT_SHIFT 8 + +#define ISPPRV_CONTRAST_MAX 0x10 +#define ISPPRV_CONTRAST_MIN 0xFF +#define ISPPRV_BRIGHT_MIN 0x00 +#define ISPPRV_BRIGHT_MAX 0xFF + +#define ISPPRV_CSUP_CSUPG_SHIFT 0 +#define ISPPRV_CSUP_THRES_SHIFT 8 +#define ISPPRV_CSUP_HPYF_SHIFT 16 + +#define ISPPRV_SETUP_YC_MINC_SHIFT 0 +#define ISPPRV_SETUP_YC_MAXC_SHIFT 8 +#define ISPPRV_SETUP_YC_MINY_SHIFT 16 +#define ISPPRV_SETUP_YC_MAXY_SHIFT 24 +#define ISPPRV_YC_MAX 0xFF +#define ISPPRV_YC_MIN 0x0 + +/* Define bit fields within selected registers */ +#define ISP_REVISION_SHIFT 0 + +#define ISP_SYSCONFIG_AUTOIDLE (1 << 0) +#define ISP_SYSCONFIG_SOFTRESET (1 << 1) +#define ISP_SYSCONFIG_MIDLEMODE_SHIFT 12 +#define ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY 0x0 +#define ISP_SYSCONFIG_MIDLEMODE_NOSTANBY 0x1 +#define ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY 0x2 + +#define ISP_SYSSTATUS_RESETDONE 0 + +#define IRQ0ENABLE_CSIA_IRQ (1 << 0) +#define IRQ0ENABLE_CSIC_IRQ (1 << 1) +#define IRQ0ENABLE_CCP2_LCM_IRQ (1 << 3) +#define IRQ0ENABLE_CCP2_LC0_IRQ (1 << 4) +#define IRQ0ENABLE_CCP2_LC1_IRQ (1 << 5) +#define IRQ0ENABLE_CCP2_LC2_IRQ (1 << 6) +#define IRQ0ENABLE_CCP2_LC3_IRQ (1 << 7) +#define IRQ0ENABLE_CSIB_IRQ (IRQ0ENABLE_CCP2_LCM_IRQ | \ + IRQ0ENABLE_CCP2_LC0_IRQ | \ + IRQ0ENABLE_CCP2_LC1_IRQ | \ + IRQ0ENABLE_CCP2_LC2_IRQ | \ + IRQ0ENABLE_CCP2_LC3_IRQ) + +#define IRQ0ENABLE_CCDC_VD0_IRQ (1 << 8) +#define IRQ0ENABLE_CCDC_VD1_IRQ (1 << 9) +#define IRQ0ENABLE_CCDC_VD2_IRQ (1 << 10) +#define IRQ0ENABLE_CCDC_ERR_IRQ (1 << 11) +#define IRQ0ENABLE_H3A_AF_DONE_IRQ (1 << 12) +#define IRQ0ENABLE_H3A_AWB_DONE_IRQ (1 << 13) +#define IRQ0ENABLE_HIST_DONE_IRQ (1 << 16) +#define IRQ0ENABLE_CCDC_LSC_DONE_IRQ (1 << 17) +#define IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ (1 << 18) +#define IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ (1 << 19) +#define IRQ0ENABLE_PRV_DONE_IRQ (1 << 20) +#define IRQ0ENABLE_RSZ_DONE_IRQ (1 << 24) +#define IRQ0ENABLE_OVF_IRQ (1 << 25) +#define IRQ0ENABLE_PING_IRQ (1 << 26) +#define IRQ0ENABLE_PONG_IRQ (1 << 27) +#define IRQ0ENABLE_MMU_ERR_IRQ (1 << 28) +#define IRQ0ENABLE_OCP_ERR_IRQ (1 << 29) +#define IRQ0ENABLE_SEC_ERR_IRQ (1 << 30) +#define IRQ0ENABLE_HS_VS_IRQ (1 << 31) + +#define IRQ0STATUS_CSIA_IRQ (1 << 0) +#define IRQ0STATUS_CSI2C_IRQ (1 << 1) +#define IRQ0STATUS_CCP2_LCM_IRQ (1 << 3) +#define IRQ0STATUS_CCP2_LC0_IRQ (1 << 4) +#define IRQ0STATUS_CSIB_IRQ (IRQ0STATUS_CCP2_LCM_IRQ | \ + IRQ0STATUS_CCP2_LC0_IRQ) + +#define IRQ0STATUS_CSIB_LC1_IRQ (1 << 5) +#define IRQ0STATUS_CSIB_LC2_IRQ (1 << 6) +#define IRQ0STATUS_CSIB_LC3_IRQ (1 << 7) +#define IRQ0STATUS_CCDC_VD0_IRQ (1 << 8) +#define IRQ0STATUS_CCDC_VD1_IRQ (1 << 9) +#define IRQ0STATUS_CCDC_VD2_IRQ (1 << 10) +#define IRQ0STATUS_CCDC_ERR_IRQ (1 << 11) +#define IRQ0STATUS_H3A_AF_DONE_IRQ (1 << 12) +#define IRQ0STATUS_H3A_AWB_DONE_IRQ (1 << 13) +#define IRQ0STATUS_HIST_DONE_IRQ (1 << 16) +#define IRQ0STATUS_CCDC_LSC_DONE_IRQ (1 << 17) +#define IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ (1 << 18) +#define IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ (1 << 19) +#define IRQ0STATUS_PRV_DONE_IRQ (1 << 20) +#define IRQ0STATUS_RSZ_DONE_IRQ (1 << 24) +#define IRQ0STATUS_OVF_IRQ (1 << 25) +#define IRQ0STATUS_PING_IRQ (1 << 26) +#define IRQ0STATUS_PONG_IRQ (1 << 27) +#define IRQ0STATUS_MMU_ERR_IRQ (1 << 28) +#define IRQ0STATUS_OCP_ERR_IRQ (1 << 29) +#define IRQ0STATUS_SEC_ERR_IRQ (1 << 30) +#define IRQ0STATUS_HS_VS_IRQ (1 << 31) + +#define TCTRL_GRESET_LEN 0 + +#define TCTRL_PSTRB_REPLAY_DELAY 0 +#define TCTRL_PSTRB_REPLAY_COUNTER_SHIFT 25 + +#define ISPCTRL_PAR_SER_CLK_SEL_PARALLEL 0x0 +#define ISPCTRL_PAR_SER_CLK_SEL_CSIA 0x1 +#define ISPCTRL_PAR_SER_CLK_SEL_CSIB 0x2 +#define ISPCTRL_PAR_SER_CLK_SEL_CSIC 0x3 +#define ISPCTRL_PAR_SER_CLK_SEL_MASK 0x3 + +#define ISPCTRL_PAR_BRIDGE_SHIFT 2 +#define ISPCTRL_PAR_BRIDGE_DISABLE (0x0 << 2) +#define ISPCTRL_PAR_BRIDGE_LENDIAN (0x2 << 2) +#define ISPCTRL_PAR_BRIDGE_BENDIAN (0x3 << 2) +#define ISPCTRL_PAR_BRIDGE_MASK (0x3 << 2) + +#define ISPCTRL_PAR_CLK_POL_SHIFT 4 +#define ISPCTRL_PAR_CLK_POL_INV (1 << 4) +#define ISPCTRL_PING_PONG_EN (1 << 5) +#define ISPCTRL_SHIFT_SHIFT 6 +#define ISPCTRL_SHIFT_0 (0x0 << 6) +#define ISPCTRL_SHIFT_2 (0x1 << 6) +#define ISPCTRL_SHIFT_4 (0x2 << 6) +#define ISPCTRL_SHIFT_MASK (0x3 << 6) + +#define ISPCTRL_CCDC_CLK_EN (1 << 8) +#define ISPCTRL_SCMP_CLK_EN (1 << 9) +#define ISPCTRL_H3A_CLK_EN (1 << 10) +#define ISPCTRL_HIST_CLK_EN (1 << 11) +#define ISPCTRL_PREV_CLK_EN (1 << 12) +#define ISPCTRL_RSZ_CLK_EN (1 << 13) +#define ISPCTRL_SYNC_DETECT_SHIFT 14 +#define ISPCTRL_SYNC_DETECT_HSFALL (0x0 << ISPCTRL_SYNC_DETECT_SHIFT) +#define ISPCTRL_SYNC_DETECT_HSRISE (0x1 << ISPCTRL_SYNC_DETECT_SHIFT) +#define ISPCTRL_SYNC_DETECT_VSFALL (0x2 << ISPCTRL_SYNC_DETECT_SHIFT) +#define ISPCTRL_SYNC_DETECT_VSRISE (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) +#define ISPCTRL_SYNC_DETECT_MASK (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) + +#define ISPCTRL_CCDC_RAM_EN (1 << 16) +#define ISPCTRL_PREV_RAM_EN (1 << 17) +#define ISPCTRL_SBL_RD_RAM_EN (1 << 18) +#define ISPCTRL_SBL_WR1_RAM_EN (1 << 19) +#define ISPCTRL_SBL_WR0_RAM_EN (1 << 20) +#define ISPCTRL_SBL_AUTOIDLE (1 << 21) +#define ISPCTRL_SBL_SHARED_WPORTC (1 << 26) +#define ISPCTRL_SBL_SHARED_RPORTA (1 << 27) +#define ISPCTRL_SBL_SHARED_RPORTB (1 << 28) +#define ISPCTRL_JPEG_FLUSH (1 << 30) +#define ISPCTRL_CCDC_FLUSH (1 << 31) + +#define ISPSECURE_SECUREMODE 0 + +#define ISPTCTRL_CTRL_DIV_LOW 0x0 +#define ISPTCTRL_CTRL_DIV_HIGH 0x1 +#define ISPTCTRL_CTRL_DIV_BYPASS 0x1F + +#define ISPTCTRL_CTRL_DIVA_SHIFT 0 +#define ISPTCTRL_CTRL_DIVA_MASK (0x1F << ISPTCTRL_CTRL_DIVA_SHIFT) + +#define ISPTCTRL_CTRL_DIVB_SHIFT 5 +#define ISPTCTRL_CTRL_DIVB_MASK (0x1F << ISPTCTRL_CTRL_DIVB_SHIFT) + +#define ISPTCTRL_CTRL_DIVC_SHIFT 10 +#define ISPTCTRL_CTRL_DIVC_NOCLOCK (0x0 << 10) + +#define ISPTCTRL_CTRL_SHUTEN (1 << 21) +#define ISPTCTRL_CTRL_PSTRBEN (1 << 22) +#define ISPTCTRL_CTRL_STRBEN (1 << 23) +#define ISPTCTRL_CTRL_SHUTPOL (1 << 24) +#define ISPTCTRL_CTRL_STRBPSTRBPOL (1 << 26) + +#define ISPTCTRL_CTRL_INSEL_SHIFT 27 +#define ISPTCTRL_CTRL_INSEL_PARALLEL (0x0 << 27) +#define ISPTCTRL_CTRL_INSEL_CSIA (0x1 << 27) +#define ISPTCTRL_CTRL_INSEL_CSIB (0x2 << 27) + +#define ISPTCTRL_CTRL_GRESETEn (1 << 29) +#define ISPTCTRL_CTRL_GRESETPOL (1 << 30) +#define ISPTCTRL_CTRL_GRESETDIR (1 << 31) + +#define ISPTCTRL_FRAME_SHUT_SHIFT 0 +#define ISPTCTRL_FRAME_PSTRB_SHIFT 6 +#define ISPTCTRL_FRAME_STRB_SHIFT 12 + +#define ISPCCDC_PID_PREV_SHIFT 0 +#define ISPCCDC_PID_CID_SHIFT 8 +#define ISPCCDC_PID_TID_SHIFT 16 + +#define ISPCCDC_PCR_EN 1 +#define ISPCCDC_PCR_BUSY (1 << 1) + +#define ISPCCDC_SYN_MODE_VDHDOUT 0x1 +#define ISPCCDC_SYN_MODE_FLDOUT (1 << 1) +#define ISPCCDC_SYN_MODE_VDPOL (1 << 2) +#define ISPCCDC_SYN_MODE_HDPOL (1 << 3) +#define ISPCCDC_SYN_MODE_FLDPOL (1 << 4) +#define ISPCCDC_SYN_MODE_EXWEN (1 << 5) +#define ISPCCDC_SYN_MODE_DATAPOL (1 << 6) +#define ISPCCDC_SYN_MODE_FLDMODE (1 << 7) +#define ISPCCDC_SYN_MODE_DATSIZ_MASK (0x7 << 8) +#define ISPCCDC_SYN_MODE_DATSIZ_8_16 (0x0 << 8) +#define ISPCCDC_SYN_MODE_DATSIZ_12 (0x4 << 8) +#define ISPCCDC_SYN_MODE_DATSIZ_11 (0x5 << 8) +#define ISPCCDC_SYN_MODE_DATSIZ_10 (0x6 << 8) +#define ISPCCDC_SYN_MODE_DATSIZ_8 (0x7 << 8) +#define ISPCCDC_SYN_MODE_PACK8 (1 << 11) +#define ISPCCDC_SYN_MODE_INPMOD_MASK (3 << 12) +#define ISPCCDC_SYN_MODE_INPMOD_RAW (0 << 12) +#define ISPCCDC_SYN_MODE_INPMOD_YCBCR16 (1 << 12) +#define ISPCCDC_SYN_MODE_INPMOD_YCBCR8 (2 << 12) +#define ISPCCDC_SYN_MODE_LPF (1 << 14) +#define ISPCCDC_SYN_MODE_FLDSTAT (1 << 15) +#define ISPCCDC_SYN_MODE_VDHDEN (1 << 16) +#define ISPCCDC_SYN_MODE_WEN (1 << 17) +#define ISPCCDC_SYN_MODE_VP2SDR (1 << 18) +#define ISPCCDC_SYN_MODE_SDR2RSZ (1 << 19) + +#define ISPCCDC_HD_VD_WID_VDW_SHIFT 0 +#define ISPCCDC_HD_VD_WID_HDW_SHIFT 16 + +#define ISPCCDC_PIX_LINES_HLPRF_SHIFT 0 +#define ISPCCDC_PIX_LINES_PPLN_SHIFT 16 + +#define ISPCCDC_HORZ_INFO_NPH_SHIFT 0 +#define ISPCCDC_HORZ_INFO_NPH_MASK 0x00007fff +#define ISPCCDC_HORZ_INFO_SPH_SHIFT 16 +#define ISPCCDC_HORZ_INFO_SPH_MASK 0x7fff0000 + +#define ISPCCDC_VERT_START_SLV1_SHIFT 0 +#define ISPCCDC_VERT_START_SLV0_SHIFT 16 +#define ISPCCDC_VERT_START_SLV0_MASK 0x7fff0000 + +#define ISPCCDC_VERT_LINES_NLV_SHIFT 0 +#define ISPCCDC_VERT_LINES_NLV_MASK 0x00007fff + +#define ISPCCDC_CULLING_CULV_SHIFT 0 +#define ISPCCDC_CULLING_CULHODD_SHIFT 16 +#define ISPCCDC_CULLING_CULHEVN_SHIFT 24 + +#define ISPCCDC_HSIZE_OFF_SHIFT 0 + +#define ISPCCDC_SDOFST_FINV (1 << 14) +#define ISPCCDC_SDOFST_FOFST_1L 0 +#define ISPCCDC_SDOFST_FOFST_4L (3 << 12) +#define ISPCCDC_SDOFST_LOFST3_SHIFT 0 +#define ISPCCDC_SDOFST_LOFST2_SHIFT 3 +#define ISPCCDC_SDOFST_LOFST1_SHIFT 6 +#define ISPCCDC_SDOFST_LOFST0_SHIFT 9 +#define EVENEVEN 1 +#define ODDEVEN 2 +#define EVENODD 3 +#define ODDODD 4 + +#define ISPCCDC_CLAMP_OBGAIN_SHIFT 0 +#define ISPCCDC_CLAMP_OBST_SHIFT 10 +#define ISPCCDC_CLAMP_OBSLN_SHIFT 25 +#define ISPCCDC_CLAMP_OBSLEN_SHIFT 28 +#define ISPCCDC_CLAMP_CLAMPEN (1 << 31) + +#define ISPCCDC_COLPTN_R_Ye 0x0 +#define ISPCCDC_COLPTN_Gr_Cy 0x1 +#define ISPCCDC_COLPTN_Gb_G 0x2 +#define ISPCCDC_COLPTN_B_Mg 0x3 +#define ISPCCDC_COLPTN_CP0PLC0_SHIFT 0 +#define ISPCCDC_COLPTN_CP0PLC1_SHIFT 2 +#define ISPCCDC_COLPTN_CP0PLC2_SHIFT 4 +#define ISPCCDC_COLPTN_CP0PLC3_SHIFT 6 +#define ISPCCDC_COLPTN_CP1PLC0_SHIFT 8 +#define ISPCCDC_COLPTN_CP1PLC1_SHIFT 10 +#define ISPCCDC_COLPTN_CP1PLC2_SHIFT 12 +#define ISPCCDC_COLPTN_CP1PLC3_SHIFT 14 +#define ISPCCDC_COLPTN_CP2PLC0_SHIFT 16 +#define ISPCCDC_COLPTN_CP2PLC1_SHIFT 18 +#define ISPCCDC_COLPTN_CP2PLC2_SHIFT 20 +#define ISPCCDC_COLPTN_CP2PLC3_SHIFT 22 +#define ISPCCDC_COLPTN_CP3PLC0_SHIFT 24 +#define ISPCCDC_COLPTN_CP3PLC1_SHIFT 26 +#define ISPCCDC_COLPTN_CP3PLC2_SHIFT 28 +#define ISPCCDC_COLPTN_CP3PLC3_SHIFT 30 + +#define ISPCCDC_BLKCMP_B_MG_SHIFT 0 +#define ISPCCDC_BLKCMP_GB_G_SHIFT 8 +#define ISPCCDC_BLKCMP_GR_CY_SHIFT 16 +#define ISPCCDC_BLKCMP_R_YE_SHIFT 24 + +#define ISPCCDC_FPC_FPNUM_SHIFT 0 +#define ISPCCDC_FPC_FPCEN (1 << 15) +#define ISPCCDC_FPC_FPERR (1 << 16) + +#define ISPCCDC_VDINT_1_SHIFT 0 +#define ISPCCDC_VDINT_1_MASK 0x00007fff +#define ISPCCDC_VDINT_0_SHIFT 16 +#define ISPCCDC_VDINT_0_MASK 0x7fff0000 + +#define ISPCCDC_ALAW_GWDI_12_3 (0x3 << 0) +#define ISPCCDC_ALAW_GWDI_11_2 (0x4 << 0) +#define ISPCCDC_ALAW_GWDI_10_1 (0x5 << 0) +#define ISPCCDC_ALAW_GWDI_9_0 (0x6 << 0) +#define ISPCCDC_ALAW_CCDTBL (1 << 3) + +#define ISPCCDC_REC656IF_R656ON 1 +#define ISPCCDC_REC656IF_ECCFVH (1 << 1) + +#define ISPCCDC_CFG_BW656 (1 << 5) +#define ISPCCDC_CFG_FIDMD_SHIFT 6 +#define ISPCCDC_CFG_WENLOG (1 << 8) +#define ISPCCDC_CFG_WENLOG_AND (0 << 8) +#define ISPCCDC_CFG_WENLOG_OR (1 << 8) +#define ISPCCDC_CFG_Y8POS (1 << 11) +#define ISPCCDC_CFG_BSWD (1 << 12) +#define ISPCCDC_CFG_MSBINVI (1 << 13) +#define ISPCCDC_CFG_VDLC (1 << 15) + +#define ISPCCDC_FMTCFG_FMTEN 0x1 +#define ISPCCDC_FMTCFG_LNALT (1 << 1) +#define ISPCCDC_FMTCFG_LNUM_SHIFT 2 +#define ISPCCDC_FMTCFG_PLEN_ODD_SHIFT 4 +#define ISPCCDC_FMTCFG_PLEN_EVEN_SHIFT 8 +#define ISPCCDC_FMTCFG_VPIN_MASK 0x00007000 +#define ISPCCDC_FMTCFG_VPIN_12_3 (0x3 << 12) +#define ISPCCDC_FMTCFG_VPIN_11_2 (0x4 << 12) +#define ISPCCDC_FMTCFG_VPIN_10_1 (0x5 << 12) +#define ISPCCDC_FMTCFG_VPIN_9_0 (0x6 << 12) +#define ISPCCDC_FMTCFG_VPEN (1 << 15) + +#define ISPCCDC_FMTCFG_VPIF_FRQ_MASK 0x003f0000 +#define ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT 16 +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY2 (0x0 << 16) +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY3 (0x1 << 16) +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY4 (0x2 << 16) +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY5 (0x3 << 16) +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY6 (0x4 << 16) + +#define ISPCCDC_FMT_HORZ_FMTLNH_SHIFT 0 +#define ISPCCDC_FMT_HORZ_FMTSPH_SHIFT 16 + +#define ISPCCDC_FMT_VERT_FMTLNV_SHIFT 0 +#define ISPCCDC_FMT_VERT_FMTSLV_SHIFT 16 + +#define ISPCCDC_FMT_HORZ_FMTSPH_MASK 0x1fff0000 +#define ISPCCDC_FMT_HORZ_FMTLNH_MASK 0x00001fff + +#define ISPCCDC_FMT_VERT_FMTSLV_MASK 0x1fff0000 +#define ISPCCDC_FMT_VERT_FMTLNV_MASK 0x00001fff + +#define ISPCCDC_VP_OUT_HORZ_ST_SHIFT 0 +#define ISPCCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define ISPCCDC_VP_OUT_VERT_NUM_SHIFT 17 + +#define ISPRSZ_PID_PREV_SHIFT 0 +#define ISPRSZ_PID_CID_SHIFT 8 +#define ISPRSZ_PID_TID_SHIFT 16 + +#define ISPRSZ_PCR_ENABLE (1 << 0) +#define ISPRSZ_PCR_BUSY (1 << 1) +#define ISPRSZ_PCR_ONESHOT (1 << 2) + +#define ISPRSZ_CNT_HRSZ_SHIFT 0 +#define ISPRSZ_CNT_HRSZ_MASK \ + (0x3FF << ISPRSZ_CNT_HRSZ_SHIFT) +#define ISPRSZ_CNT_VRSZ_SHIFT 10 +#define ISPRSZ_CNT_VRSZ_MASK \ + (0x3FF << ISPRSZ_CNT_VRSZ_SHIFT) +#define ISPRSZ_CNT_HSTPH_SHIFT 20 +#define ISPRSZ_CNT_HSTPH_MASK (0x7 << ISPRSZ_CNT_HSTPH_SHIFT) +#define ISPRSZ_CNT_VSTPH_SHIFT 23 +#define ISPRSZ_CNT_VSTPH_MASK (0x7 << ISPRSZ_CNT_VSTPH_SHIFT) +#define ISPRSZ_CNT_YCPOS (1 << 26) +#define ISPRSZ_CNT_INPTYP (1 << 27) +#define ISPRSZ_CNT_INPSRC (1 << 28) +#define ISPRSZ_CNT_CBILIN (1 << 29) + +#define ISPRSZ_OUT_SIZE_HORZ_SHIFT 0 +#define ISPRSZ_OUT_SIZE_HORZ_MASK \ + (0xFFF << ISPRSZ_OUT_SIZE_HORZ_SHIFT) +#define ISPRSZ_OUT_SIZE_VERT_SHIFT 16 +#define ISPRSZ_OUT_SIZE_VERT_MASK \ + (0xFFF << ISPRSZ_OUT_SIZE_VERT_SHIFT) + +#define ISPRSZ_IN_START_HORZ_ST_SHIFT 0 +#define ISPRSZ_IN_START_HORZ_ST_MASK \ + (0x1FFF << ISPRSZ_IN_START_HORZ_ST_SHIFT) +#define ISPRSZ_IN_START_VERT_ST_SHIFT 16 +#define ISPRSZ_IN_START_VERT_ST_MASK \ + (0x1FFF << ISPRSZ_IN_START_VERT_ST_SHIFT) + +#define ISPRSZ_IN_SIZE_HORZ_SHIFT 0 +#define ISPRSZ_IN_SIZE_HORZ_MASK \ + (0x1FFF << ISPRSZ_IN_SIZE_HORZ_SHIFT) +#define ISPRSZ_IN_SIZE_VERT_SHIFT 16 +#define ISPRSZ_IN_SIZE_VERT_MASK \ + (0x1FFF << ISPRSZ_IN_SIZE_VERT_SHIFT) + +#define ISPRSZ_SDR_INADD_ADDR_SHIFT 0 +#define ISPRSZ_SDR_INADD_ADDR_MASK 0xFFFFFFFF + +#define ISPRSZ_SDR_INOFF_OFFSET_SHIFT 0 +#define ISPRSZ_SDR_INOFF_OFFSET_MASK \ + (0xFFFF << ISPRSZ_SDR_INOFF_OFFSET_SHIFT) + +#define ISPRSZ_SDR_OUTADD_ADDR_SHIFT 0 +#define ISPRSZ_SDR_OUTADD_ADDR_MASK 0xFFFFFFFF + + +#define ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT 0 +#define ISPRSZ_SDR_OUTOFF_OFFSET_MASK \ + (0xFFFF << ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT) + +#define ISPRSZ_HFILT_COEF0_SHIFT 0 +#define ISPRSZ_HFILT_COEF0_MASK \ + (0x3FF << ISPRSZ_HFILT_COEF0_SHIFT) +#define ISPRSZ_HFILT_COEF1_SHIFT 16 +#define ISPRSZ_HFILT_COEF1_MASK \ + (0x3FF << ISPRSZ_HFILT_COEF1_SHIFT) + +#define ISPRSZ_HFILT32_COEF2_SHIFT 0 +#define ISPRSZ_HFILT32_COEF2_MASK 0x3FF +#define ISPRSZ_HFILT32_COEF3_SHIFT 16 +#define ISPRSZ_HFILT32_COEF3_MASK 0x3FF0000 + +#define ISPRSZ_HFILT54_COEF4_SHIFT 0 +#define ISPRSZ_HFILT54_COEF4_MASK 0x3FF +#define ISPRSZ_HFILT54_COEF5_SHIFT 16 +#define ISPRSZ_HFILT54_COEF5_MASK 0x3FF0000 + +#define ISPRSZ_HFILT76_COEFF6_SHIFT 0 +#define ISPRSZ_HFILT76_COEFF6_MASK 0x3FF +#define ISPRSZ_HFILT76_COEFF7_SHIFT 16 +#define ISPRSZ_HFILT76_COEFF7_MASK 0x3FF0000 + +#define ISPRSZ_HFILT98_COEFF8_SHIFT 0 +#define ISPRSZ_HFILT98_COEFF8_MASK 0x3FF +#define ISPRSZ_HFILT98_COEFF9_SHIFT 16 +#define ISPRSZ_HFILT98_COEFF9_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1110_COEF10_SHIFT 0 +#define ISPRSZ_HFILT1110_COEF10_MASK 0x3FF +#define ISPRSZ_HFILT1110_COEF11_SHIFT 16 +#define ISPRSZ_HFILT1110_COEF11_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1312_COEFF12_SHIFT 0 +#define ISPRSZ_HFILT1312_COEFF12_MASK 0x3FF +#define ISPRSZ_HFILT1312_COEFF13_SHIFT 16 +#define ISPRSZ_HFILT1312_COEFF13_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1514_COEFF14_SHIFT 0 +#define ISPRSZ_HFILT1514_COEFF14_MASK 0x3FF +#define ISPRSZ_HFILT1514_COEFF15_SHIFT 16 +#define ISPRSZ_HFILT1514_COEFF15_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1716_COEF16_SHIFT 0 +#define ISPRSZ_HFILT1716_COEF16_MASK 0x3FF +#define ISPRSZ_HFILT1716_COEF17_SHIFT 16 +#define ISPRSZ_HFILT1716_COEF17_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1918_COEF18_SHIFT 0 +#define ISPRSZ_HFILT1918_COEF18_MASK 0x3FF +#define ISPRSZ_HFILT1918_COEF19_SHIFT 16 +#define ISPRSZ_HFILT1918_COEF19_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2120_COEF20_SHIFT 0 +#define ISPRSZ_HFILT2120_COEF20_MASK 0x3FF +#define ISPRSZ_HFILT2120_COEF21_SHIFT 16 +#define ISPRSZ_HFILT2120_COEF21_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2322_COEF22_SHIFT 0 +#define ISPRSZ_HFILT2322_COEF22_MASK 0x3FF +#define ISPRSZ_HFILT2322_COEF23_SHIFT 16 +#define ISPRSZ_HFILT2322_COEF23_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2524_COEF24_SHIFT 0 +#define ISPRSZ_HFILT2524_COEF24_MASK 0x3FF +#define ISPRSZ_HFILT2524_COEF25_SHIFT 16 +#define ISPRSZ_HFILT2524_COEF25_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2726_COEF26_SHIFT 0 +#define ISPRSZ_HFILT2726_COEF26_MASK 0x3FF +#define ISPRSZ_HFILT2726_COEF27_SHIFT 16 +#define ISPRSZ_HFILT2726_COEF27_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2928_COEF28_SHIFT 0 +#define ISPRSZ_HFILT2928_COEF28_MASK 0x3FF +#define ISPRSZ_HFILT2928_COEF29_SHIFT 16 +#define ISPRSZ_HFILT2928_COEF29_MASK 0x3FF0000 + +#define ISPRSZ_HFILT3130_COEF30_SHIFT 0 +#define ISPRSZ_HFILT3130_COEF30_MASK 0x3FF +#define ISPRSZ_HFILT3130_COEF31_SHIFT 16 +#define ISPRSZ_HFILT3130_COEF31_MASK 0x3FF0000 + +#define ISPRSZ_VFILT_COEF0_SHIFT 0 +#define ISPRSZ_VFILT_COEF0_MASK \ + (0x3FF << ISPRSZ_VFILT_COEF0_SHIFT) +#define ISPRSZ_VFILT_COEF1_SHIFT 16 +#define ISPRSZ_VFILT_COEF1_MASK \ + (0x3FF << ISPRSZ_VFILT_COEF1_SHIFT) + +#define ISPRSZ_VFILT10_COEF0_SHIFT 0 +#define ISPRSZ_VFILT10_COEF0_MASK 0x3FF +#define ISPRSZ_VFILT10_COEF1_SHIFT 16 +#define ISPRSZ_VFILT10_COEF1_MASK 0x3FF0000 + +#define ISPRSZ_VFILT32_COEF2_SHIFT 0 +#define ISPRSZ_VFILT32_COEF2_MASK 0x3FF +#define ISPRSZ_VFILT32_COEF3_SHIFT 16 +#define ISPRSZ_VFILT32_COEF3_MASK 0x3FF0000 + +#define ISPRSZ_VFILT54_COEF4_SHIFT 0 +#define ISPRSZ_VFILT54_COEF4_MASK 0x3FF +#define ISPRSZ_VFILT54_COEF5_SHIFT 16 +#define ISPRSZ_VFILT54_COEF5_MASK 0x3FF0000 + +#define ISPRSZ_VFILT76_COEFF6_SHIFT 0 +#define ISPRSZ_VFILT76_COEFF6_MASK 0x3FF +#define ISPRSZ_VFILT76_COEFF7_SHIFT 16 +#define ISPRSZ_VFILT76_COEFF7_MASK 0x3FF0000 + +#define ISPRSZ_VFILT98_COEFF8_SHIFT 0 +#define ISPRSZ_VFILT98_COEFF8_MASK 0x3FF +#define ISPRSZ_VFILT98_COEFF9_SHIFT 16 +#define ISPRSZ_VFILT98_COEFF9_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1110_COEF10_SHIFT 0 +#define ISPRSZ_VFILT1110_COEF10_MASK 0x3FF +#define ISPRSZ_VFILT1110_COEF11_SHIFT 16 +#define ISPRSZ_VFILT1110_COEF11_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1312_COEFF12_SHIFT 0 +#define ISPRSZ_VFILT1312_COEFF12_MASK 0x3FF +#define ISPRSZ_VFILT1312_COEFF13_SHIFT 16 +#define ISPRSZ_VFILT1312_COEFF13_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1514_COEFF14_SHIFT 0 +#define ISPRSZ_VFILT1514_COEFF14_MASK 0x3FF +#define ISPRSZ_VFILT1514_COEFF15_SHIFT 16 +#define ISPRSZ_VFILT1514_COEFF15_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1716_COEF16_SHIFT 0 +#define ISPRSZ_VFILT1716_COEF16_MASK 0x3FF +#define ISPRSZ_VFILT1716_COEF17_SHIFT 16 +#define ISPRSZ_VFILT1716_COEF17_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1918_COEF18_SHIFT 0 +#define ISPRSZ_VFILT1918_COEF18_MASK 0x3FF +#define ISPRSZ_VFILT1918_COEF19_SHIFT 16 +#define ISPRSZ_VFILT1918_COEF19_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2120_COEF20_SHIFT 0 +#define ISPRSZ_VFILT2120_COEF20_MASK 0x3FF +#define ISPRSZ_VFILT2120_COEF21_SHIFT 16 +#define ISPRSZ_VFILT2120_COEF21_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2322_COEF22_SHIFT 0 +#define ISPRSZ_VFILT2322_COEF22_MASK 0x3FF +#define ISPRSZ_VFILT2322_COEF23_SHIFT 16 +#define ISPRSZ_VFILT2322_COEF23_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2524_COEF24_SHIFT 0 +#define ISPRSZ_VFILT2524_COEF24_MASK 0x3FF +#define ISPRSZ_VFILT2524_COEF25_SHIFT 16 +#define ISPRSZ_VFILT2524_COEF25_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2726_COEF26_SHIFT 0 +#define ISPRSZ_VFILT2726_COEF26_MASK 0x3FF +#define ISPRSZ_VFILT2726_COEF27_SHIFT 16 +#define ISPRSZ_VFILT2726_COEF27_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2928_COEF28_SHIFT 0 +#define ISPRSZ_VFILT2928_COEF28_MASK 0x3FF +#define ISPRSZ_VFILT2928_COEF29_SHIFT 16 +#define ISPRSZ_VFILT2928_COEF29_MASK 0x3FF0000 + +#define ISPRSZ_VFILT3130_COEF30_SHIFT 0 +#define ISPRSZ_VFILT3130_COEF30_MASK 0x3FF +#define ISPRSZ_VFILT3130_COEF31_SHIFT 16 +#define ISPRSZ_VFILT3130_COEF31_MASK 0x3FF0000 + +#define ISPRSZ_YENH_CORE_SHIFT 0 +#define ISPRSZ_YENH_CORE_MASK \ + (0xFF << ISPRSZ_YENH_CORE_SHIFT) +#define ISPRSZ_YENH_SLOP_SHIFT 8 +#define ISPRSZ_YENH_SLOP_MASK \ + (0xF << ISPRSZ_YENH_SLOP_SHIFT) +#define ISPRSZ_YENH_GAIN_SHIFT 12 +#define ISPRSZ_YENH_GAIN_MASK \ + (0xF << ISPRSZ_YENH_GAIN_SHIFT) +#define ISPRSZ_YENH_ALGO_SHIFT 16 +#define ISPRSZ_YENH_ALGO_MASK \ + (0x3 << ISPRSZ_YENH_ALGO_SHIFT) + +#define ISPH3A_PCR_AEW_ALAW_EN_SHIFT 1 +#define ISPH3A_PCR_AF_MED_TH_SHIFT 3 +#define ISPH3A_PCR_AF_RGBPOS_SHIFT 11 +#define ISPH3A_PCR_AEW_AVE2LMT_SHIFT 22 +#define ISPH3A_PCR_AEW_AVE2LMT_MASK 0xFFC00000 +#define ISPH3A_PCR_BUSYAF (1 << 15) +#define ISPH3A_PCR_BUSYAEAWB (1 << 18) + +#define ISPH3A_AEWWIN1_WINHC_SHIFT 0 +#define ISPH3A_AEWWIN1_WINHC_MASK 0x3F +#define ISPH3A_AEWWIN1_WINVC_SHIFT 6 +#define ISPH3A_AEWWIN1_WINVC_MASK 0x1FC0 +#define ISPH3A_AEWWIN1_WINW_SHIFT 13 +#define ISPH3A_AEWWIN1_WINW_MASK 0xFE000 +#define ISPH3A_AEWWIN1_WINH_SHIFT 24 +#define ISPH3A_AEWWIN1_WINH_MASK 0x7F000000 + +#define ISPH3A_AEWINSTART_WINSH_SHIFT 0 +#define ISPH3A_AEWINSTART_WINSH_MASK 0x0FFF +#define ISPH3A_AEWINSTART_WINSV_SHIFT 16 +#define ISPH3A_AEWINSTART_WINSV_MASK 0x0FFF0000 + +#define ISPH3A_AEWINBLK_WINH_SHIFT 0 +#define ISPH3A_AEWINBLK_WINH_MASK 0x7F +#define ISPH3A_AEWINBLK_WINSV_SHIFT 16 +#define ISPH3A_AEWINBLK_WINSV_MASK 0x0FFF0000 + +#define ISPH3A_AEWSUBWIN_AEWINCH_SHIFT 0 +#define ISPH3A_AEWSUBWIN_AEWINCH_MASK 0x0F +#define ISPH3A_AEWSUBWIN_AEWINCV_SHIFT 8 +#define ISPH3A_AEWSUBWIN_AEWINCV_MASK 0x0F00 + +#define ISPHIST_PCR_ENABLE_SHIFT 0 +#define ISPHIST_PCR_ENABLE_MASK 0x01 +#define ISPHIST_PCR_ENABLE (1 << ISPHIST_PCR_ENABLE_SHIFT) +#define ISPHIST_PCR_BUSY 0x02 + +#define ISPHIST_CNT_DATASIZE_SHIFT 8 +#define ISPHIST_CNT_DATASIZE_MASK 0x0100 +#define ISPHIST_CNT_CLEAR_SHIFT 7 +#define ISPHIST_CNT_CLEAR_MASK 0x080 +#define ISPHIST_CNT_CLEAR (1 << ISPHIST_CNT_CLEAR_SHIFT) +#define ISPHIST_CNT_CFA_SHIFT 6 +#define ISPHIST_CNT_CFA_MASK 0x040 +#define ISPHIST_CNT_BINS_SHIFT 4 +#define ISPHIST_CNT_BINS_MASK 0x030 +#define ISPHIST_CNT_SOURCE_SHIFT 3 +#define ISPHIST_CNT_SOURCE_MASK 0x08 +#define ISPHIST_CNT_SHIFT_SHIFT 0 +#define ISPHIST_CNT_SHIFT_MASK 0x07 + +#define ISPHIST_WB_GAIN_WG00_SHIFT 24 +#define ISPHIST_WB_GAIN_WG00_MASK 0xFF000000 +#define ISPHIST_WB_GAIN_WG01_SHIFT 16 +#define ISPHIST_WB_GAIN_WG01_MASK 0xFF0000 +#define ISPHIST_WB_GAIN_WG02_SHIFT 8 +#define ISPHIST_WB_GAIN_WG02_MASK 0xFF00 +#define ISPHIST_WB_GAIN_WG03_SHIFT 0 +#define ISPHIST_WB_GAIN_WG03_MASK 0xFF + +#define ISPHIST_REG_START_END_MASK 0x3FFF +#define ISPHIST_REG_START_SHIFT 16 +#define ISPHIST_REG_END_SHIFT 0 +#define ISPHIST_REG_START_MASK (ISPHIST_REG_START_END_MASK << \ + ISPHIST_REG_START_SHIFT) +#define ISPHIST_REG_END_MASK (ISPHIST_REG_START_END_MASK << \ + ISPHIST_REG_END_SHIFT) + +#define ISPHIST_REG_MASK (ISPHIST_REG_START_MASK | \ + ISPHIST_REG_END_MASK) + +#define ISPHIST_ADDR_SHIFT 0 +#define ISPHIST_ADDR_MASK 0x3FF + +#define ISPHIST_DATA_SHIFT 0 +#define ISPHIST_DATA_MASK 0xFFFFF + +#define ISPHIST_RADD_SHIFT 0 +#define ISPHIST_RADD_MASK 0xFFFFFFFF + +#define ISPHIST_RADD_OFF_SHIFT 0 +#define ISPHIST_RADD_OFF_MASK 0xFFFF + +#define ISPHIST_HV_INFO_HSIZE_SHIFT 16 +#define ISPHIST_HV_INFO_HSIZE_MASK 0x3FFF0000 +#define ISPHIST_HV_INFO_VSIZE_SHIFT 0 +#define ISPHIST_HV_INFO_VSIZE_MASK 0x3FFF + +#define ISPHIST_HV_INFO_MASK 0x3FFF3FFF + +#define ISPCCDC_LSC_ENABLE 1 +#define ISPCCDC_LSC_BUSY (1 << 7) +#define ISPCCDC_LSC_GAIN_MODE_N_MASK 0x700 +#define ISPCCDC_LSC_GAIN_MODE_N_SHIFT 8 +#define ISPCCDC_LSC_GAIN_MODE_M_MASK 0x3800 +#define ISPCCDC_LSC_GAIN_MODE_M_SHIFT 12 +#define ISPCCDC_LSC_GAIN_FORMAT_MASK 0xE +#define ISPCCDC_LSC_GAIN_FORMAT_SHIFT 1 +#define ISPCCDC_LSC_AFTER_REFORMATTER_MASK (1<<6) + +#define ISPCCDC_LSC_INITIAL_X_MASK 0x3F +#define ISPCCDC_LSC_INITIAL_X_SHIFT 0 +#define ISPCCDC_LSC_INITIAL_Y_MASK 0x3F0000 +#define ISPCCDC_LSC_INITIAL_Y_SHIFT 16 + +/* ----------------------------------------------------------------------------- + * CSI2 receiver registers (ES2.0) + */ + +#define ISPCSI2_REVISION (0x000) +#define ISPCSI2_SYSCONFIG (0x010) +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK \ + (0x3 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_FORCE \ + (0x0 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO \ + (0x1 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART \ + (0x2 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCSI2_SYSCONFIG_SOFT_RESET (1 << 1) +#define ISPCSI2_SYSCONFIG_AUTO_IDLE (1 << 0) + +#define ISPCSI2_SYSSTATUS (0x014) +#define ISPCSI2_SYSSTATUS_RESET_DONE (1 << 0) + +#define ISPCSI2_IRQSTATUS (0x018) +#define ISPCSI2_IRQSTATUS_OCP_ERR_IRQ (1 << 14) +#define ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ (1 << 13) +#define ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 12) +#define ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ (1 << 11) +#define ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ (1 << 10) +#define ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ (1 << 9) +#define ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ (1 << 8) +#define ISPCSI2_IRQSTATUS_CONTEXT(n) (1 << (n)) + +#define ISPCSI2_IRQENABLE (0x01c) +#define ISPCSI2_CTRL (0x040) +#define ISPCSI2_CTRL_VP_CLK_EN (1 << 15) +#define ISPCSI2_CTRL_VP_ONLY_EN (1 << 11) +#define ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT 8 +#define ISPCSI2_CTRL_VP_OUT_CTRL_MASK \ + (3 << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) +#define ISPCSI2_CTRL_DBG_EN (1 << 7) +#define ISPCSI2_CTRL_BURST_SIZE_SHIFT 5 +#define ISPCSI2_CTRL_BURST_SIZE_MASK \ + (3 << ISPCSI2_CTRL_BURST_SIZE_SHIFT) +#define ISPCSI2_CTRL_FRAME (1 << 3) +#define ISPCSI2_CTRL_ECC_EN (1 << 2) +#define ISPCSI2_CTRL_SECURE (1 << 1) +#define ISPCSI2_CTRL_IF_EN (1 << 0) + +#define ISPCSI2_DBG_H (0x044) +#define ISPCSI2_GNQ (0x048) +#define ISPCSI2_PHY_CFG (0x050) +#define ISPCSI2_PHY_CFG_RESET_CTRL (1 << 30) +#define ISPCSI2_PHY_CFG_RESET_DONE (1 << 29) +#define ISPCSI2_PHY_CFG_PWR_CMD_SHIFT 27 +#define ISPCSI2_PHY_CFG_PWR_CMD_MASK \ + (0x3 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) +#define ISPCSI2_PHY_CFG_PWR_CMD_OFF \ + (0x0 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) +#define ISPCSI2_PHY_CFG_PWR_CMD_ON \ + (0x1 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) +#define ISPCSI2_PHY_CFG_PWR_CMD_ULPW \ + (0x2 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) +#define ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT 25 +#define ISPCSI2_PHY_CFG_PWR_STATUS_MASK \ + (0x3 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) +#define ISPCSI2_PHY_CFG_PWR_STATUS_OFF \ + (0x0 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) +#define ISPCSI2_PHY_CFG_PWR_STATUS_ON \ + (0x1 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) +#define ISPCSI2_PHY_CFG_PWR_STATUS_ULPW \ + (0x2 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) +#define ISPCSI2_PHY_CFG_PWR_AUTO (1 << 24) + +#define ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n) (3 + ((n) * 4)) +#define ISPCSI2_PHY_CFG_DATA_POL_MASK(n) \ + (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) +#define ISPCSI2_PHY_CFG_DATA_POL_PN(n) \ + (0x0 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) +#define ISPCSI2_PHY_CFG_DATA_POL_NP(n) \ + (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) + +#define ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n) ((n) * 4) +#define ISPCSI2_PHY_CFG_DATA_POSITION_MASK(n) \ + (0x7 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_PHY_CFG_DATA_POSITION_NC(n) \ + (0x0 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_PHY_CFG_DATA_POSITION_1(n) \ + (0x1 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_PHY_CFG_DATA_POSITION_2(n) \ + (0x2 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_PHY_CFG_DATA_POSITION_3(n) \ + (0x3 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_PHY_CFG_DATA_POSITION_4(n) \ + (0x4 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_PHY_CFG_DATA_POSITION_5(n) \ + (0x5 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) + +#define ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT 3 +#define ISPCSI2_PHY_CFG_CLOCK_POL_MASK \ + (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) +#define ISPCSI2_PHY_CFG_CLOCK_POL_PN \ + (0x0 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) +#define ISPCSI2_PHY_CFG_CLOCK_POL_NP \ + (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) + +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT 0 +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK \ + (0x7 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_1 \ + (0x1 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_2 \ + (0x2 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_3 \ + (0x3 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_4 \ + (0x4 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) +#define ISPCSI2_PHY_CFG_CLOCK_POSITION_5 \ + (0x5 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) + +#define ISPCSI2_PHY_IRQSTATUS (0x054) +#define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMEXIT (1 << 26) +#define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMENTER (1 << 25) +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM5 (1 << 24) +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM4 (1 << 23) +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM3 (1 << 22) +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM2 (1 << 21) +#define ISPCSI2_PHY_IRQSTATUS_STATEULPM1 (1 << 20) +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL5 (1 << 19) +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL4 (1 << 18) +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL3 (1 << 17) +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL2 (1 << 16) +#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL1 (1 << 15) +#define ISPCSI2_PHY_IRQSTATUS_ERRESC5 (1 << 14) +#define ISPCSI2_PHY_IRQSTATUS_ERRESC4 (1 << 13) +#define ISPCSI2_PHY_IRQSTATUS_ERRESC3 (1 << 12) +#define ISPCSI2_PHY_IRQSTATUS_ERRESC2 (1 << 11) +#define ISPCSI2_PHY_IRQSTATUS_ERRESC1 (1 << 10) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS5 (1 << 9) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS4 (1 << 8) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS3 (1 << 7) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS2 (1 << 6) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS1 (1 << 5) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS5 (1 << 4) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS4 (1 << 3) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS3 (1 << 2) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS2 (1 << 1) +#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS1 1 + +#define ISPCSI2_SHORT_PACKET (0x05c) +#define ISPCSI2_PHY_IRQENABLE (0x060) +#define ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT (1 << 26) +#define ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER (1 << 25) +#define ISPCSI2_PHY_IRQENABLE_STATEULPM5 (1 << 24) +#define ISPCSI2_PHY_IRQENABLE_STATEULPM4 (1 << 23) +#define ISPCSI2_PHY_IRQENABLE_STATEULPM3 (1 << 22) +#define ISPCSI2_PHY_IRQENABLE_STATEULPM2 (1 << 21) +#define ISPCSI2_PHY_IRQENABLE_STATEULPM1 (1 << 20) +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 (1 << 19) +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 (1 << 18) +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 (1 << 17) +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 (1 << 16) +#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 (1 << 15) +#define ISPCSI2_PHY_IRQENABLE_ERRESC5 (1 << 14) +#define ISPCSI2_PHY_IRQENABLE_ERRESC4 (1 << 13) +#define ISPCSI2_PHY_IRQENABLE_ERRESC3 (1 << 12) +#define ISPCSI2_PHY_IRQENABLE_ERRESC2 (1 << 11) +#define ISPCSI2_PHY_IRQENABLE_ERRESC1 (1 << 10) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 (1 << 9) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 (1 << 8) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 (1 << 7) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 (1 << 6) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 (1 << 5) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 (1 << 4) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 (1 << 3) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 (1 << 2) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 (1 << 1) +#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS1 (1 << 0) + +#define ISPCSI2_DBG_P (0x068) +#define ISPCSI2_TIMING (0x06c) +#define ISPCSI2_TIMING_FORCE_RX_MODE_IO(n) (1 << ((16 * ((n) - 1)) + 15)) +#define ISPCSI2_TIMING_STOP_STATE_X16_IO(n) (1 << ((16 * ((n) - 1)) + 14)) +#define ISPCSI2_TIMING_STOP_STATE_X4_IO(n) (1 << ((16 * ((n) - 1)) + 13)) +#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n) (16 * ((n) - 1)) +#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(n) \ + (0x1fff << ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n)) + +#define ISPCSI2_CTX_CTRL1(n) ((0x070) + 0x20 * (n)) +#define ISPCSI2_CTX_CTRL1_COUNT_SHIFT 8 +#define ISPCSI2_CTX_CTRL1_COUNT_MASK \ + (0xff << ISPCSI2_CTX_CTRL1_COUNT_SHIFT) +#define ISPCSI2_CTX_CTRL1_EOF_EN (1 << 7) +#define ISPCSI2_CTX_CTRL1_EOL_EN (1 << 6) +#define ISPCSI2_CTX_CTRL1_CS_EN (1 << 5) +#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK (1 << 4) +#define ISPCSI2_CTX_CTRL1_PING_PONG (1 << 3) +#define ISPCSI2_CTX_CTRL1_CTX_EN (1 << 0) + +#define ISPCSI2_CTX_CTRL2(n) ((0x074) + 0x20 * (n)) +#define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13 +#define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK \ + (0x3 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT) +#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11 +#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK \ + (0x3 << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT) +#define ISPCSI2_CTX_CTRL2_DPCM_PRED (1 << 10) +#define ISPCSI2_CTX_CTRL2_FORMAT_SHIFT 0 +#define ISPCSI2_CTX_CTRL2_FORMAT_MASK \ + (0x3ff << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT) +#define ISPCSI2_CTX_CTRL2_FRAME_SHIFT 16 +#define ISPCSI2_CTX_CTRL2_FRAME_MASK \ + (0xffff << ISPCSI2_CTX_CTRL2_FRAME_SHIFT) + +#define ISPCSI2_CTX_DAT_OFST(n) ((0x078) + 0x20 * (n)) +#define ISPCSI2_CTX_DAT_OFST_OFST_SHIFT 0 +#define ISPCSI2_CTX_DAT_OFST_OFST_MASK \ + (0x1ffe0 << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT) + +#define ISPCSI2_CTX_DAT_PING_ADDR(n) ((0x07c) + 0x20 * (n)) +#define ISPCSI2_CTX_DAT_PONG_ADDR(n) ((0x080) + 0x20 * (n)) +#define ISPCSI2_CTX_IRQENABLE(n) ((0x084) + 0x20 * (n)) +#define ISPCSI2_CTX_IRQENABLE_ECC_CORRECTION_IRQ (1 << 8) +#define ISPCSI2_CTX_IRQENABLE_LINE_NUMBER_IRQ (1 << 7) +#define ISPCSI2_CTX_IRQENABLE_FRAME_NUMBER_IRQ (1 << 6) +#define ISPCSI2_CTX_IRQENABLE_CS_IRQ (1 << 5) +#define ISPCSI2_CTX_IRQENABLE_LE_IRQ (1 << 3) +#define ISPCSI2_CTX_IRQENABLE_LS_IRQ (1 << 2) +#define ISPCSI2_CTX_IRQENABLE_FE_IRQ (1 << 1) +#define ISPCSI2_CTX_IRQENABLE_FS_IRQ (1 << 0) + +#define ISPCSI2_CTX_IRQSTATUS(n) ((0x088) + 0x20 * (n)) +#define ISPCSI2_CTX_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 8) +#define ISPCSI2_CTX_IRQSTATUS_LINE_NUMBER_IRQ (1 << 7) +#define ISPCSI2_CTX_IRQSTATUS_FRAME_NUMBER_IRQ (1 << 6) +#define ISPCSI2_CTX_IRQSTATUS_CS_IRQ (1 << 5) +#define ISPCSI2_CTX_IRQSTATUS_LE_IRQ (1 << 3) +#define ISPCSI2_CTX_IRQSTATUS_LS_IRQ (1 << 2) +#define ISPCSI2_CTX_IRQSTATUS_FE_IRQ (1 << 1) +#define ISPCSI2_CTX_IRQSTATUS_FS_IRQ (1 << 0) + +#define ISPCSI2_CTX_CTRL3(n) ((0x08c) + 0x20 * (n)) +#define ISPCSI2_CTX_CTRL3_ALPHA_SHIFT 5 +#define ISPCSI2_CTX_CTRL3_ALPHA_MASK \ + (0x3fff << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT) + +/* This instance is for OMAP3630 only */ +#define ISPCSI2_CTX_TRANSCODEH(n) (0x000 + 0x8 * (n)) +#define ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT 16 +#define ISPCSI2_CTX_TRANSCODEH_HCOUNT_MASK \ + (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT) +#define ISPCSI2_CTX_TRANSCODEH_HSKIP_SHIFT 0 +#define ISPCSI2_CTX_TRANSCODEH_HSKIP_MASK \ + (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT) +#define ISPCSI2_CTX_TRANSCODEV(n) (0x004 + 0x8 * (n)) +#define ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT 16 +#define ISPCSI2_CTX_TRANSCODEV_VCOUNT_MASK \ + (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT) +#define ISPCSI2_CTX_TRANSCODEV_VSKIP_SHIFT 0 +#define ISPCSI2_CTX_TRANSCODEV_VSKIP_MASK \ + (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT) + +/* ----------------------------------------------------------------------------- + * CSI PHY registers + */ + +#define ISPCSIPHY_REG0 (0x000) +#define ISPCSIPHY_REG0_THS_TERM_SHIFT 8 +#define ISPCSIPHY_REG0_THS_TERM_MASK \ + (0xff << ISPCSIPHY_REG0_THS_TERM_SHIFT) +#define ISPCSIPHY_REG0_THS_SETTLE_SHIFT 0 +#define ISPCSIPHY_REG0_THS_SETTLE_MASK \ + (0xff << ISPCSIPHY_REG0_THS_SETTLE_SHIFT) + +#define ISPCSIPHY_REG1 (0x004) +#define ISPCSIPHY_REG1_RESET_DONE_CTRLCLK (1 << 29) +/* This field is for OMAP3630 only */ +#define ISPCSIPHY_REG1_CLOCK_MISS_DETECTOR_STATUS (1 << 25) +#define ISPCSIPHY_REG1_TCLK_TERM_SHIFT 18 +#define ISPCSIPHY_REG1_TCLK_TERM_MASK \ + (0x7f << ISPCSIPHY_REG1_TCLK_TERM_SHIFT) +#define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_SHIFT 10 +#define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_MASK \ + (0xff << ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN) +/* This field is for OMAP3430 only */ +#define ISPCSIPHY_REG1_TCLK_MISS_SHIFT 8 +#define ISPCSIPHY_REG1_TCLK_MISS_MASK \ + (0x3 << ISPCSIPHY_REG1_TCLK_MISS_SHIFT) +/* This field is for OMAP3630 only */ +#define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT 8 +#define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_MASK \ + (0x3 << ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT) +#define ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT 0 +#define ISPCSIPHY_REG1_TCLK_SETTLE_MASK \ + (0xff << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT) + +/* This register is for OMAP3630 only */ +#define ISPCSIPHY_REG2 (0x008) +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT 30 +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK \ + (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT) +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT 28 +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK \ + (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT) +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT 26 +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK \ + (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT) +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT 24 +#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK \ + (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT) +#define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT 0 +#define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_MASK \ + (0x7fffff << ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT) + +#endif /* OMAP3_ISP_REG_H */ diff --git a/include/linux/omap3isp.h b/include/linux/omap3isp.h new file mode 100644 index 0000000..150822b --- /dev/null +++ b/include/linux/omap3isp.h @@ -0,0 +1,646 @@ +/* + * omap3isp.h + * + * TI OMAP3 ISP - User-space API + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_USER_H +#define OMAP3_ISP_USER_H + +#include + +/* + * Private IOCTLs + * + * VIDIOC_OMAP3ISP_CCDC_CFG: Set CCDC configuration + * VIDIOC_OMAP3ISP_PRV_CFG: Set preview engine configuration + * VIDIOC_OMAP3ISP_AEWB_CFG: Set AEWB module configuration + * VIDIOC_OMAP3ISP_HIST_CFG: Set histogram module configuration + * VIDIOC_OMAP3ISP_AF_CFG: Set auto-focus module configuration + * VIDIOC_OMAP3ISP_STAT_REQ: Read statistics (AEWB/AF/histogram) data + * VIDIOC_OMAP3ISP_STAT_EN: Enable/disable a statistics module + */ + +#define VIDIOC_OMAP3ISP_CCDC_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct omap3isp_ccdc_update_config) +#define VIDIOC_OMAP3ISP_PRV_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct omap3isp_prev_update_config) +#define VIDIOC_OMAP3ISP_AEWB_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct omap3isp_h3a_aewb_config) +#define VIDIOC_OMAP3ISP_HIST_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct omap3isp_hist_config) +#define VIDIOC_OMAP3ISP_AF_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct omap3isp_h3a_af_config) +#define VIDIOC_OMAP3ISP_STAT_REQ \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct omap3isp_stat_data) +#define VIDIOC_OMAP3ISP_STAT_EN \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 7, unsigned long) + +/* + * Events + * + * V4L2_EVENT_OMAP3ISP_AEWB: AEWB statistics data ready + * V4L2_EVENT_OMAP3ISP_AF: AF statistics data ready + * V4L2_EVENT_OMAP3ISP_HIST: Histogram statistics data ready + * V4L2_EVENT_OMAP3ISP_HS_VS: Horizontal/vertical synchronization detected + */ + +#define V4L2_EVENT_OMAP3ISP_CLASS (V4L2_EVENT_PRIVATE_START | 0x100) +#define V4L2_EVENT_OMAP3ISP_AEWB (V4L2_EVENT_OMAP3ISP_CLASS | 0x1) +#define V4L2_EVENT_OMAP3ISP_AF (V4L2_EVENT_OMAP3ISP_CLASS | 0x2) +#define V4L2_EVENT_OMAP3ISP_HIST (V4L2_EVENT_OMAP3ISP_CLASS | 0x3) +#define V4L2_EVENT_OMAP3ISP_HS_VS (V4L2_EVENT_OMAP3ISP_CLASS | 0x4) + +struct omap3isp_stat_event_status { + __u32 frame_number; + __u16 config_counter; + __u8 buf_err; +}; + +/* AE/AWB related structures and flags*/ + +/* H3A Range Constants */ +#define OMAP3ISP_AEWB_MAX_SATURATION_LIM 1023 +#define OMAP3ISP_AEWB_MIN_WIN_H 2 +#define OMAP3ISP_AEWB_MAX_WIN_H 256 +#define OMAP3ISP_AEWB_MIN_WIN_W 6 +#define OMAP3ISP_AEWB_MAX_WIN_W 256 +#define OMAP3ISP_AEWB_MIN_WINVC 1 +#define OMAP3ISP_AEWB_MIN_WINHC 1 +#define OMAP3ISP_AEWB_MAX_WINVC 128 +#define OMAP3ISP_AEWB_MAX_WINHC 36 +#define OMAP3ISP_AEWB_MAX_WINSTART 4095 +#define OMAP3ISP_AEWB_MIN_SUB_INC 2 +#define OMAP3ISP_AEWB_MAX_SUB_INC 32 +#define OMAP3ISP_AEWB_MAX_BUF_SIZE 83600 + +#define OMAP3ISP_AF_IIRSH_MIN 0 +#define OMAP3ISP_AF_IIRSH_MAX 4095 +#define OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN 1 +#define OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX 36 +#define OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN 1 +#define OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX 128 +#define OMAP3ISP_AF_PAXEL_INCREMENT_MIN 2 +#define OMAP3ISP_AF_PAXEL_INCREMENT_MAX 32 +#define OMAP3ISP_AF_PAXEL_HEIGHT_MIN 2 +#define OMAP3ISP_AF_PAXEL_HEIGHT_MAX 256 +#define OMAP3ISP_AF_PAXEL_WIDTH_MIN 16 +#define OMAP3ISP_AF_PAXEL_WIDTH_MAX 256 +#define OMAP3ISP_AF_PAXEL_HZSTART_MIN 1 +#define OMAP3ISP_AF_PAXEL_HZSTART_MAX 4095 +#define OMAP3ISP_AF_PAXEL_VTSTART_MIN 0 +#define OMAP3ISP_AF_PAXEL_VTSTART_MAX 4095 +#define OMAP3ISP_AF_THRESHOLD_MAX 255 +#define OMAP3ISP_AF_COEF_MAX 4095 +#define OMAP3ISP_AF_PAXEL_SIZE 48 +#define OMAP3ISP_AF_MAX_BUF_SIZE 221184 + +/** + * struct omap3isp_h3a_aewb_config - AE AWB configuration reset values + * saturation_limit: Saturation limit. + * @win_height: Window Height. Range 2 - 256, even values only. + * @win_width: Window Width. Range 6 - 256, even values only. + * @ver_win_count: Vertical Window Count. Range 1 - 128. + * @hor_win_count: Horizontal Window Count. Range 1 - 36. + * @ver_win_start: Vertical Window Start. Range 0 - 4095. + * @hor_win_start: Horizontal Window Start. Range 0 - 4095. + * @blk_ver_win_start: Black Vertical Windows Start. Range 0 - 4095. + * @blk_win_height: Black Window Height. Range 2 - 256, even values only. + * @subsample_ver_inc: Subsample Vertical points increment Range 2 - 32, even + * values only. + * @subsample_hor_inc: Subsample Horizontal points increment Range 2 - 32, even + * values only. + * @alaw_enable: AEW ALAW EN flag. + */ +struct omap3isp_h3a_aewb_config { + /* + * Common fields. + * They should be the first ones and must be in the same order as in + * ispstat_generic_config struct. + */ + __u32 buf_size; + __u16 config_counter; + + /* Private fields */ + __u16 saturation_limit; + __u16 win_height; + __u16 win_width; + __u16 ver_win_count; + __u16 hor_win_count; + __u16 ver_win_start; + __u16 hor_win_start; + __u16 blk_ver_win_start; + __u16 blk_win_height; + __u16 subsample_ver_inc; + __u16 subsample_hor_inc; + __u8 alaw_enable; +}; + +/** + * struct omap3isp_stat_data - Statistic data sent to or received from user + * @ts: Timestamp of returned framestats. + * @buf: Pointer to pass to user. + * @frame_number: Frame number of requested stats. + * @cur_frame: Current frame number being processed. + * @config_counter: Number of the configuration associated with the data. + */ +struct omap3isp_stat_data { + struct timeval ts; + void __user *buf; + __u32 buf_size; + __u16 frame_number; + __u16 cur_frame; + __u16 config_counter; +}; + + +/* Histogram related structs */ + +/* Flags for number of bins */ +#define OMAP3ISP_HIST_BINS_32 0 +#define OMAP3ISP_HIST_BINS_64 1 +#define OMAP3ISP_HIST_BINS_128 2 +#define OMAP3ISP_HIST_BINS_256 3 + +/* Number of bins * 4 colors * 4-bytes word */ +#define OMAP3ISP_HIST_MEM_SIZE_BINS(n) ((1 << ((n)+5))*4*4) + +#define OMAP3ISP_HIST_MEM_SIZE 1024 +#define OMAP3ISP_HIST_MIN_REGIONS 1 +#define OMAP3ISP_HIST_MAX_REGIONS 4 +#define OMAP3ISP_HIST_MAX_WB_GAIN 255 +#define OMAP3ISP_HIST_MIN_WB_GAIN 0 +#define OMAP3ISP_HIST_MAX_BIT_WIDTH 14 +#define OMAP3ISP_HIST_MIN_BIT_WIDTH 8 +#define OMAP3ISP_HIST_MAX_WG 4 +#define OMAP3ISP_HIST_MAX_BUF_SIZE 4096 + +/* Source */ +#define OMAP3ISP_HIST_SOURCE_CCDC 0 +#define OMAP3ISP_HIST_SOURCE_MEM 1 + +/* CFA pattern */ +#define OMAP3ISP_HIST_CFA_BAYER 0 +#define OMAP3ISP_HIST_CFA_FOVEONX3 1 + +struct omap3isp_hist_region { + __u16 h_start; + __u16 h_end; + __u16 v_start; + __u16 v_end; +}; + +struct omap3isp_hist_config { + /* + * Common fields. + * They should be the first ones and must be in the same order as in + * ispstat_generic_config struct. + */ + __u32 buf_size; + __u16 config_counter; + + __u8 num_acc_frames; /* Num of image frames to be processed and + accumulated for each histogram frame */ + __u16 hist_bins; /* number of bins: 32, 64, 128, or 256 */ + __u8 cfa; /* BAYER or FOVEON X3 */ + __u8 wg[OMAP3ISP_HIST_MAX_WG]; /* White Balance Gain */ + __u8 num_regions; /* number of regions to be configured */ + struct omap3isp_hist_region region[OMAP3ISP_HIST_MAX_REGIONS]; +}; + +/* Auto Focus related structs */ + +#define OMAP3ISP_AF_NUM_COEF 11 + +enum omap3isp_h3a_af_fvmode { + OMAP3ISP_AF_MODE_SUMMED = 0, + OMAP3ISP_AF_MODE_PEAK = 1 +}; + +/* Red, Green, and blue pixel location in the AF windows */ +enum omap3isp_h3a_af_rgbpos { + OMAP3ISP_AF_GR_GB_BAYER = 0, /* GR and GB as Bayer pattern */ + OMAP3ISP_AF_RG_GB_BAYER = 1, /* RG and GB as Bayer pattern */ + OMAP3ISP_AF_GR_BG_BAYER = 2, /* GR and BG as Bayer pattern */ + OMAP3ISP_AF_RG_BG_BAYER = 3, /* RG and BG as Bayer pattern */ + OMAP3ISP_AF_GG_RB_CUSTOM = 4, /* GG and RB as custom pattern */ + OMAP3ISP_AF_RB_GG_CUSTOM = 5 /* RB and GG as custom pattern */ +}; + +/* Contains the information regarding the Horizontal Median Filter */ +struct omap3isp_h3a_af_hmf { + __u8 enable; /* Status of Horizontal Median Filter */ + __u8 threshold; /* Threshhold Value for Horizontal Median Filter */ +}; + +/* Contains the information regarding the IIR Filters */ +struct omap3isp_h3a_af_iir { + __u16 h_start; /* IIR horizontal start */ + __u16 coeff_set0[OMAP3ISP_AF_NUM_COEF]; /* Filter coefficient, set 0 */ + __u16 coeff_set1[OMAP3ISP_AF_NUM_COEF]; /* Filter coefficient, set 1 */ +}; + +/* Contains the information regarding the Paxels Structure in AF Engine */ +struct omap3isp_h3a_af_paxel { + __u16 h_start; /* Horizontal Start Position */ + __u16 v_start; /* Vertical Start Position */ + __u8 width; /* Width of the Paxel */ + __u8 height; /* Height of the Paxel */ + __u8 h_cnt; /* Horizontal Count */ + __u8 v_cnt; /* vertical Count */ + __u8 line_inc; /* Line Increment */ +}; + +/* Contains the parameters required for hardware set up of AF Engine */ +struct omap3isp_h3a_af_config { + /* + * Common fields. + * They should be the first ones and must be in the same order as in + * ispstat_generic_config struct. + */ + __u32 buf_size; + __u16 config_counter; + + struct omap3isp_h3a_af_hmf hmf; /* HMF configurations */ + struct omap3isp_h3a_af_iir iir; /* IIR filter configurations */ + struct omap3isp_h3a_af_paxel paxel; /* Paxel parameters */ + enum omap3isp_h3a_af_rgbpos rgb_pos; /* RGB Positions */ + enum omap3isp_h3a_af_fvmode fvmode; /* Accumulator mode */ + __u8 alaw_enable; /* AF ALAW status */ +}; + +/* ISP CCDC structs */ + +/* Abstraction layer CCDC configurations */ +#define OMAP3ISP_CCDC_ALAW (1 << 0) +#define OMAP3ISP_CCDC_LPF (1 << 1) +#define OMAP3ISP_CCDC_BLCLAMP (1 << 2) +#define OMAP3ISP_CCDC_BCOMP (1 << 3) +#define OMAP3ISP_CCDC_FPC (1 << 4) +#define OMAP3ISP_CCDC_CULL (1 << 5) +#define OMAP3ISP_CCDC_CONFIG_LSC (1 << 7) +#define OMAP3ISP_CCDC_TBL_LSC (1 << 8) + +#define OMAP3ISP_RGB_MAX 3 + +/* Enumeration constants for Alaw input width */ +enum omap3isp_alaw_ipwidth { + OMAP3ISP_ALAW_BIT12_3 = 0x3, + OMAP3ISP_ALAW_BIT11_2 = 0x4, + OMAP3ISP_ALAW_BIT10_1 = 0x5, + OMAP3ISP_ALAW_BIT9_0 = 0x6 +}; + +/** + * struct omap3isp_ccdc_lsc_config - LSC configuration + * @offset: Table Offset of the gain table. + * @gain_mode_n: Vertical dimension of a paxel in LSC configuration. + * @gain_mode_m: Horizontal dimension of a paxel in LSC configuration. + * @gain_format: Gain table format. + * @fmtsph: Start pixel horizontal from start of the HS sync pulse. + * @fmtlnh: Number of pixels in horizontal direction to use for the data + * reformatter. + * @fmtslv: Start line from start of VS sync pulse for the data reformatter. + * @fmtlnv: Number of lines in vertical direction for the data reformatter. + * @initial_x: X position, in pixels, of the first active pixel in reference + * to the first active paxel. Must be an even number. + * @initial_y: Y position, in pixels, of the first active pixel in reference + * to the first active paxel. Must be an even number. + * @size: Size of LSC gain table. Filled when loaded from userspace. + */ +struct omap3isp_ccdc_lsc_config { + __u16 offset; + __u8 gain_mode_n; + __u8 gain_mode_m; + __u8 gain_format; + __u16 fmtsph; + __u16 fmtlnh; + __u16 fmtslv; + __u16 fmtlnv; + __u8 initial_x; + __u8 initial_y; + __u32 size; +}; + +/** + * struct omap3isp_ccdc_bclamp - Optical & Digital black clamp subtract + * @obgain: Optical black average gain. + * @obstpixel: Start Pixel w.r.t. HS pulse in Optical black sample. + * @oblines: Optical Black Sample lines. + * @oblen: Optical Black Sample Length. + * @dcsubval: Digital Black Clamp subtract value. + */ +struct omap3isp_ccdc_bclamp { + __u8 obgain; + __u8 obstpixel; + __u8 oblines; + __u8 oblen; + __u16 dcsubval; +}; + +/** + * struct omap3isp_ccdc_fpc - Faulty Pixels Correction + * @fpnum: Number of faulty pixels to be corrected in the frame. + * @fpcaddr: Memory address of the FPC Table + */ +struct omap3isp_ccdc_fpc { + __u16 fpnum; + __u32 fpcaddr; +}; + +/** + * struct omap3isp_ccdc_blcomp - Black Level Compensation parameters + * @b_mg: B/Mg pixels. 2's complement. -128 to +127. + * @gb_g: Gb/G pixels. 2's complement. -128 to +127. + * @gr_cy: Gr/Cy pixels. 2's complement. -128 to +127. + * @r_ye: R/Ye pixels. 2's complement. -128 to +127. + */ +struct omap3isp_ccdc_blcomp { + __u8 b_mg; + __u8 gb_g; + __u8 gr_cy; + __u8 r_ye; +}; + +/** + * omap3isp_ccdc_culling - Culling parameters + * @v_pattern: Vertical culling pattern. + * @h_odd: Horizontal Culling pattern for odd lines. + * @h_even: Horizontal Culling pattern for even lines. + */ +struct omap3isp_ccdc_culling { + __u8 v_pattern; + __u16 h_odd; + __u16 h_even; +}; + +/** + * omap3isp_ccdc_update_config - CCDC configuration + * @update: Specifies which CCDC registers should be updated. + * @flag: Specifies which CCDC functions should be enabled. + * @alawip: Enable/Disable A-Law compression. + * @bclamp: Black clamp control register. + * @blcomp: Black level compensation value for RGrGbB Pixels. 2's complement. + * @fpc: Number of faulty pixels corrected in the frame, address of FPC table. + * @cull: Cull control register. + * @lsc: Pointer to LSC gain table. + */ +struct omap3isp_ccdc_update_config { + __u16 update; + __u16 flag; + enum omap3isp_alaw_ipwidth alawip; + struct omap3isp_ccdc_bclamp __user *bclamp; + struct omap3isp_ccdc_blcomp __user *blcomp; + struct omap3isp_ccdc_fpc __user *fpc; + struct omap3isp_ccdc_lsc_config __user *lsc_cfg; + struct omap3isp_ccdc_culling __user *cull; + __u8 __user *lsc; +}; + +/* Preview configurations */ +#define OMAP3ISP_PREV_LUMAENH (1 << 0) +#define OMAP3ISP_PREV_INVALAW (1 << 1) +#define OMAP3ISP_PREV_HRZ_MED (1 << 2) +#define OMAP3ISP_PREV_CFA (1 << 3) +#define OMAP3ISP_PREV_CHROMA_SUPP (1 << 4) +#define OMAP3ISP_PREV_WB (1 << 5) +#define OMAP3ISP_PREV_BLKADJ (1 << 6) +#define OMAP3ISP_PREV_RGB2RGB (1 << 7) +#define OMAP3ISP_PREV_COLOR_CONV (1 << 8) +#define OMAP3ISP_PREV_YC_LIMIT (1 << 9) +#define OMAP3ISP_PREV_DEFECT_COR (1 << 10) +#define OMAP3ISP_PREV_GAMMABYPASS (1 << 11) +#define OMAP3ISP_PREV_DRK_FRM_CAPTURE (1 << 12) +#define OMAP3ISP_PREV_DRK_FRM_SUBTRACT (1 << 13) +#define OMAP3ISP_PREV_LENS_SHADING (1 << 14) +#define OMAP3ISP_PREV_NF (1 << 15) +#define OMAP3ISP_PREV_GAMMA (1 << 16) + +#define OMAP3ISP_PREV_NF_TBL_SIZE 64 +#define OMAP3ISP_PREV_CFA_TBL_SIZE 576 +#define OMAP3ISP_PREV_GAMMA_TBL_SIZE 1024 +#define OMAP3ISP_PREV_YENH_TBL_SIZE 128 + +#define OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS 4 + +/** + * struct omap3isp_prev_hmed - Horizontal Median Filter + * @odddist: Distance between consecutive pixels of same color in the odd line. + * @evendist: Distance between consecutive pixels of same color in the even + * line. + * @thres: Horizontal median filter threshold. + */ +struct omap3isp_prev_hmed { + __u8 odddist; + __u8 evendist; + __u8 thres; +}; + +/* + * Enumeration for CFA Formats supported by preview + */ +enum omap3isp_cfa_fmt { + OMAP3ISP_CFAFMT_BAYER, + OMAP3ISP_CFAFMT_SONYVGA, + OMAP3ISP_CFAFMT_RGBFOVEON, + OMAP3ISP_CFAFMT_DNSPL, + OMAP3ISP_CFAFMT_HONEYCOMB, + OMAP3ISP_CFAFMT_RRGGBBFOVEON +}; + +/** + * struct omap3isp_prev_cfa - CFA Interpolation + * @format: CFA Format Enum value supported by preview. + * @gradthrs_vert: CFA Gradient Threshold - Vertical. + * @gradthrs_horz: CFA Gradient Threshold - Horizontal. + * @table: Pointer to the CFA table. + */ +struct omap3isp_prev_cfa { + enum omap3isp_cfa_fmt format; + __u8 gradthrs_vert; + __u8 gradthrs_horz; + __u32 table[OMAP3ISP_PREV_CFA_TBL_SIZE]; +}; + +/** + * struct omap3isp_prev_csup - Chrominance Suppression + * @gain: Gain. + * @thres: Threshold. + * @hypf_en: Flag to enable/disable the High Pass Filter. + */ +struct omap3isp_prev_csup { + __u8 gain; + __u8 thres; + __u8 hypf_en; +}; + +/** + * struct omap3isp_prev_wbal - White Balance + * @dgain: Digital gain (U10Q8). + * @coef3: White balance gain - COEF 3 (U8Q5). + * @coef2: White balance gain - COEF 2 (U8Q5). + * @coef1: White balance gain - COEF 1 (U8Q5). + * @coef0: White balance gain - COEF 0 (U8Q5). + */ +struct omap3isp_prev_wbal { + __u16 dgain; + __u8 coef3; + __u8 coef2; + __u8 coef1; + __u8 coef0; +}; + +/** + * struct omap3isp_prev_blkadj - Black Level Adjustment + * @red: Black level offset adjustment for Red in 2's complement format + * @green: Black level offset adjustment for Green in 2's complement format + * @blue: Black level offset adjustment for Blue in 2's complement format + */ +struct omap3isp_prev_blkadj { + /*Black level offset adjustment for Red in 2's complement format */ + __u8 red; + /*Black level offset adjustment for Green in 2's complement format */ + __u8 green; + /* Black level offset adjustment for Blue in 2's complement format */ + __u8 blue; +}; + +/** + * struct omap3isp_prev_rgbtorgb - RGB to RGB Blending + * @matrix: Blending values(S12Q8 format) + * [RR] [GR] [BR] + * [RG] [GG] [BG] + * [RB] [GB] [BB] + * @offset: Blending offset value for R,G,B in 2's complement integer format. + */ +struct omap3isp_prev_rgbtorgb { + __u16 matrix[OMAP3ISP_RGB_MAX][OMAP3ISP_RGB_MAX]; + __u16 offset[OMAP3ISP_RGB_MAX]; +}; + +/** + * struct omap3isp_prev_csc - Color Space Conversion from RGB-YCbYCr + * @matrix: Color space conversion coefficients(S10Q8) + * [CSCRY] [CSCGY] [CSCBY] + * [CSCRCB] [CSCGCB] [CSCBCB] + * [CSCRCR] [CSCGCR] [CSCBCR] + * @offset: CSC offset values for Y offset, CB offset and CR offset respectively + */ +struct omap3isp_prev_csc { + __u16 matrix[OMAP3ISP_RGB_MAX][OMAP3ISP_RGB_MAX]; + __s16 offset[OMAP3ISP_RGB_MAX]; +}; + +/** + * struct omap3isp_prev_yclimit - Y, C Value Limit + * @minC: Minimum C value + * @maxC: Maximum C value + * @minY: Minimum Y value + * @maxY: Maximum Y value + */ +struct omap3isp_prev_yclimit { + __u8 minC; + __u8 maxC; + __u8 minY; + __u8 maxY; +}; + +/** + * struct omap3isp_prev_dcor - Defect correction + * @couplet_mode_en: Flag to enable or disable the couplet dc Correction in NF + * @detect_correct: Thresholds for correction bit 0:10 detect 16:25 correct + */ +struct omap3isp_prev_dcor { + __u8 couplet_mode_en; + __u32 detect_correct[OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS]; +}; + +/** + * struct omap3isp_prev_nf - Noise Filter + * @spread: Spread value to be used in Noise Filter + * @table: Pointer to the Noise Filter table + */ +struct omap3isp_prev_nf { + __u8 spread; + __u32 table[OMAP3ISP_PREV_NF_TBL_SIZE]; +}; + +/** + * struct omap3isp_prev_gtables - Gamma correction tables + * @red: Array for red gamma table. + * @green: Array for green gamma table. + * @blue: Array for blue gamma table. + */ +struct omap3isp_prev_gtables { + __u32 red[OMAP3ISP_PREV_GAMMA_TBL_SIZE]; + __u32 green[OMAP3ISP_PREV_GAMMA_TBL_SIZE]; + __u32 blue[OMAP3ISP_PREV_GAMMA_TBL_SIZE]; +}; + +/** + * struct omap3isp_prev_luma - Luma enhancement + * @table: Array for luma enhancement table. + */ +struct omap3isp_prev_luma { + __u32 table[OMAP3ISP_PREV_YENH_TBL_SIZE]; +}; + +/** + * struct omap3isp_prev_update_config - Preview engine configuration (user) + * @update: Specifies which ISP Preview registers should be updated. + * @flag: Specifies which ISP Preview functions should be enabled. + * @shading_shift: 3bit value of shift used in shading compensation. + * @luma: Pointer to luma enhancement structure. + * @hmed: Pointer to structure containing the odd and even distance. + * between the pixels in the image along with the filter threshold. + * @cfa: Pointer to structure containing the CFA interpolation table, CFA. + * format in the image, vertical and horizontal gradient threshold. + * @csup: Pointer to Structure for Chrominance Suppression coefficients. + * @wbal: Pointer to structure for White Balance. + * @blkadj: Pointer to structure for Black Adjustment. + * @rgb2rgb: Pointer to structure for RGB to RGB Blending. + * @csc: Pointer to structure for Color Space Conversion from RGB-YCbYCr. + * @yclimit: Pointer to structure for Y, C Value Limit. + * @dcor: Pointer to structure for defect correction. + * @nf: Pointer to structure for Noise Filter + * @gamma: Pointer to gamma structure. + */ +struct omap3isp_prev_update_config { + __u32 update; + __u32 flag; + __u32 shading_shift; + struct omap3isp_prev_luma __user *luma; + struct omap3isp_prev_hmed __user *hmed; + struct omap3isp_prev_cfa __user *cfa; + struct omap3isp_prev_csup __user *csup; + struct omap3isp_prev_wbal __user *wbal; + struct omap3isp_prev_blkadj __user *blkadj; + struct omap3isp_prev_rgbtorgb __user *rgb2rgb; + struct omap3isp_prev_csc __user *csc; + struct omap3isp_prev_yclimit __user *yclimit; + struct omap3isp_prev_dcor __user *dcor; + struct omap3isp_prev_nf __user *nf; + struct omap3isp_prev_gtables __user *gamma; +}; + +#endif /* OMAP3_ISP_USER_H */ -- cgit v0.10.2 From ad614acb7eca429891313f57bafbe5b3924a5aae Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 12 Feb 2011 18:05:06 -0300 Subject: [media] omap3isp: Video devices and buffers queue The OMAP3 ISP video devices and buffers queue modules implement the V4L2 API on all the ISP video nodes. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: David Cohen Signed-off-by: Stanimir Varbanov Signed-off-by: Vimarsh Zutshi Signed-off-by: Tuukka Toivonen Signed-off-by: Sergio Aguirre Signed-off-by: Antti Koskipaa Signed-off-by: Ivan T. Ivanov Signed-off-by: RaniSuneela Signed-off-by: Atanas Filipov Signed-off-by: Gjorgji Rosikopulos Signed-off-by: Hiroshi DOYU Signed-off-by: Nayden Kanchev Signed-off-by: Phil Carmody Signed-off-by: Artem Bityutskiy Signed-off-by: Dominic Curran Signed-off-by: Ilkka Myllyperkio Signed-off-by: Pallavi Kulkarni Signed-off-by: Vaibhav Hiremath Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap3isp/ispqueue.c b/drivers/media/video/omap3isp/ispqueue.c new file mode 100644 index 0000000..8fddc58 --- /dev/null +++ b/drivers/media/video/omap3isp/ispqueue.c @@ -0,0 +1,1153 @@ +/* + * ispqueue.c + * + * TI OMAP3 ISP - Video buffers queue handling + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ispqueue.h" + +/* ----------------------------------------------------------------------------- + * Video buffers management + */ + +/* + * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP + * + * The typical operation required here is Cache Invalidation across + * the (user space) buffer address range. And this _must_ be done + * at QBUF stage (and *only* at QBUF). + * + * We try to use optimal cache invalidation function: + * - dmac_map_area: + * - used when the number of pages are _low_. + * - it becomes quite slow as the number of pages increase. + * - for 648x492 viewfinder (150 pages) it takes 1.3 ms. + * - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms. + * + * - flush_cache_all: + * - used when the number of pages are _high_. + * - time taken in the range of 500-900 us. + * - has a higher penalty but, as whole dcache + icache is invalidated + */ +/* + * FIXME: dmac_inv_range crashes randomly on the user space buffer + * address. Fall back to flush_cache_all for now. + */ +#define ISP_CACHE_FLUSH_PAGES_MAX 0 + +static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf) +{ + if (buf->skip_cache) + return; + + if (buf->vbuf.m.userptr == 0 || buf->npages == 0 || + buf->npages > ISP_CACHE_FLUSH_PAGES_MAX) + flush_cache_all(); + else { + dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length, + DMA_FROM_DEVICE); + outer_inv_range(buf->vbuf.m.userptr, + buf->vbuf.m.userptr + buf->vbuf.length); + } +} + +/* + * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped + * + * Lock the VMAs underlying the given buffer into memory. This avoids the + * userspace buffer mapping from being swapped out, making VIPT cache handling + * easier. + * + * Note that the pages will not be freed as the buffers have been locked to + * memory using by a call to get_user_pages(), but the userspace mapping could + * still disappear if the VMAs are not locked. This is caused by the memory + * management code trying to be as lock-less as possible, which results in the + * userspace mapping manager not finding out that the pages are locked under + * some conditions. + */ +static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock) +{ + struct vm_area_struct *vma; + unsigned long start; + unsigned long end; + int ret = 0; + + if (buf->vbuf.memory == V4L2_MEMORY_MMAP) + return 0; + + /* We can be called from workqueue context if the current task dies to + * unlock the VMAs. In that case there's no current memory management + * context so unlocking can't be performed, but the VMAs have been or + * are getting destroyed anyway so it doesn't really matter. + */ + if (!current || !current->mm) + return lock ? -EINVAL : 0; + + start = buf->vbuf.m.userptr; + end = buf->vbuf.m.userptr + buf->vbuf.length - 1; + + down_write(¤t->mm->mmap_sem); + spin_lock(¤t->mm->page_table_lock); + + do { + vma = find_vma(current->mm, start); + if (vma == NULL) { + ret = -EFAULT; + goto out; + } + + if (lock) + vma->vm_flags |= VM_LOCKED; + else + vma->vm_flags &= ~VM_LOCKED; + + start = vma->vm_end + 1; + } while (vma->vm_end < end); + + if (lock) + buf->vm_flags |= VM_LOCKED; + else + buf->vm_flags &= ~VM_LOCKED; + +out: + spin_unlock(¤t->mm->page_table_lock); + up_write(¤t->mm->mmap_sem); + return ret; +} + +/* + * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer + * + * Iterate over the vmalloc'ed area and create a scatter list entry for every + * page. + */ +static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf) +{ + struct scatterlist *sglist; + unsigned int npages; + unsigned int i; + void *addr; + + addr = buf->vaddr; + npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT; + + sglist = vmalloc(npages * sizeof(*sglist)); + if (sglist == NULL) + return -ENOMEM; + + sg_init_table(sglist, npages); + + for (i = 0; i < npages; ++i, addr += PAGE_SIZE) { + struct page *page = vmalloc_to_page(addr); + + if (page == NULL || PageHighMem(page)) { + vfree(sglist); + return -EINVAL; + } + + sg_set_page(&sglist[i], page, PAGE_SIZE, 0); + } + + buf->sglen = npages; + buf->sglist = sglist; + + return 0; +} + +/* + * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer + * + * Walk the buffer pages list and create a 1:1 mapping to a scatter list. + */ +static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf) +{ + struct scatterlist *sglist; + unsigned int offset = buf->offset; + unsigned int i; + + sglist = vmalloc(buf->npages * sizeof(*sglist)); + if (sglist == NULL) + return -ENOMEM; + + sg_init_table(sglist, buf->npages); + + for (i = 0; i < buf->npages; ++i) { + if (PageHighMem(buf->pages[i])) { + vfree(sglist); + return -EINVAL; + } + + sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset, + offset); + offset = 0; + } + + buf->sglen = buf->npages; + buf->sglist = sglist; + + return 0; +} + +/* + * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer + * + * Create a scatter list of physically contiguous pages starting at the buffer + * memory physical address. + */ +static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf) +{ + struct scatterlist *sglist; + unsigned int offset = buf->offset; + unsigned long pfn = buf->paddr >> PAGE_SHIFT; + unsigned int i; + + sglist = vmalloc(buf->npages * sizeof(*sglist)); + if (sglist == NULL) + return -ENOMEM; + + sg_init_table(sglist, buf->npages); + + for (i = 0; i < buf->npages; ++i, ++pfn) { + sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset, + offset); + /* PFNMAP buffers will not get DMA-mapped, set the DMA address + * manually. + */ + sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset; + offset = 0; + } + + buf->sglen = buf->npages; + buf->sglist = sglist; + + return 0; +} + +/* + * isp_video_buffer_cleanup - Release pages for a userspace VMA. + * + * Release pages locked by a call isp_video_buffer_prepare_user and free the + * pages table. + */ +static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) +{ + enum dma_data_direction direction; + unsigned int i; + + if (buf->queue->ops->buffer_cleanup) + buf->queue->ops->buffer_cleanup(buf); + + if (!(buf->vm_flags & VM_PFNMAP)) { + direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE + ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen, + direction); + } + + vfree(buf->sglist); + buf->sglist = NULL; + buf->sglen = 0; + + if (buf->pages != NULL) { + isp_video_buffer_lock_vma(buf, 0); + + for (i = 0; i < buf->npages; ++i) + page_cache_release(buf->pages[i]); + + vfree(buf->pages); + buf->pages = NULL; + } + + buf->npages = 0; + buf->skip_cache = false; +} + +/* + * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory. + * + * This function creates a list of pages for a userspace VMA. The number of + * pages is first computed based on the buffer size, and pages are then + * retrieved by a call to get_user_pages. + * + * Pages are pinned to memory by get_user_pages, making them available for DMA + * transfers. However, due to memory management optimization, it seems the + * get_user_pages doesn't guarantee that the pinned pages will not be written + * to swap and removed from the userspace mapping(s). When this happens, a page + * fault can be generated when accessing those unmapped pages. + * + * If the fault is triggered by a page table walk caused by VIPT cache + * management operations, the page fault handler might oops if the MM semaphore + * is held, as it can't handle kernel page faults in that case. To fix that, a + * fixup entry needs to be added to the cache management code, or the userspace + * VMA must be locked to avoid removing pages from the userspace mapping in the + * first place. + * + * If the number of pages retrieved is smaller than the number required by the + * buffer size, the function returns -EFAULT. + */ +static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf) +{ + unsigned long data; + unsigned int first; + unsigned int last; + int ret; + + data = buf->vbuf.m.userptr; + first = (data & PAGE_MASK) >> PAGE_SHIFT; + last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT; + + buf->offset = data & ~PAGE_MASK; + buf->npages = last - first + 1; + buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0])); + if (buf->pages == NULL) + return -ENOMEM; + + down_read(¤t->mm->mmap_sem); + ret = get_user_pages(current, current->mm, data & PAGE_MASK, + buf->npages, + buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, + buf->pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (ret != buf->npages) { + buf->npages = ret; + isp_video_buffer_cleanup(buf); + return -EFAULT; + } + + ret = isp_video_buffer_lock_vma(buf, 1); + if (ret < 0) + isp_video_buffer_cleanup(buf); + + return ret; +} + +/* + * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer + * + * Userspace VM_PFNMAP buffers are supported only if they are contiguous in + * memory and if they span a single VMA. + * + * Return 0 if the buffer is valid, or -EFAULT otherwise. + */ +static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) +{ + struct vm_area_struct *vma; + unsigned long prev_pfn; + unsigned long this_pfn; + unsigned long start; + unsigned long end; + dma_addr_t pa; + int ret = -EFAULT; + + start = buf->vbuf.m.userptr; + end = buf->vbuf.m.userptr + buf->vbuf.length - 1; + + buf->offset = start & ~PAGE_MASK; + buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; + buf->pages = NULL; + + down_read(¤t->mm->mmap_sem); + vma = find_vma(current->mm, start); + if (vma == NULL || vma->vm_end < end) + goto done; + + for (prev_pfn = 0; start <= end; start += PAGE_SIZE) { + ret = follow_pfn(vma, start, &this_pfn); + if (ret) + goto done; + + if (prev_pfn == 0) + pa = this_pfn << PAGE_SHIFT; + else if (this_pfn != prev_pfn + 1) { + ret = -EFAULT; + goto done; + } + + prev_pfn = this_pfn; + } + + buf->paddr = pa + buf->offset; + ret = 0; + +done: + up_read(¤t->mm->mmap_sem); + return ret; +} + +/* + * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address + * + * This function locates the VMAs for the buffer's userspace address and checks + * that their flags match. The onlflag that we need to care for at the moment is + * VM_PFNMAP. + * + * The buffer vm_flags field is set to the first VMA flags. + * + * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs + * have incompatible flags. + */ +static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf) +{ + struct vm_area_struct *vma; + pgprot_t vm_page_prot; + unsigned long start; + unsigned long end; + int ret = -EFAULT; + + start = buf->vbuf.m.userptr; + end = buf->vbuf.m.userptr + buf->vbuf.length - 1; + + down_read(¤t->mm->mmap_sem); + + do { + vma = find_vma(current->mm, start); + if (vma == NULL) + goto done; + + if (start == buf->vbuf.m.userptr) { + buf->vm_flags = vma->vm_flags; + vm_page_prot = vma->vm_page_prot; + } + + if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP) + goto done; + + if (vm_page_prot != vma->vm_page_prot) + goto done; + + start = vma->vm_end + 1; + } while (vma->vm_end < end); + + /* Skip cache management to enhance performances for non-cached or + * write-combining buffers. + */ + if (vm_page_prot == pgprot_noncached(vm_page_prot) || + vm_page_prot == pgprot_writecombine(vm_page_prot)) + buf->skip_cache = true; + + ret = 0; + +done: + up_read(¤t->mm->mmap_sem); + return ret; +} + +/* + * isp_video_buffer_prepare - Make a buffer ready for operation + * + * Preparing a buffer involves: + * + * - validating VMAs (userspace buffers only) + * - locking pages and VMAs into memory (userspace buffers only) + * - building page and scatter-gather lists + * - mapping buffers for DMA operation + * - performing driver-specific preparation + * + * The function must be called in userspace context with a valid mm context + * (this excludes cleanup paths such as sys_close when the userspace process + * segfaults). + */ +static int isp_video_buffer_prepare(struct isp_video_buffer *buf) +{ + enum dma_data_direction direction; + int ret; + + switch (buf->vbuf.memory) { + case V4L2_MEMORY_MMAP: + ret = isp_video_buffer_sglist_kernel(buf); + break; + + case V4L2_MEMORY_USERPTR: + ret = isp_video_buffer_prepare_vm_flags(buf); + if (ret < 0) + return ret; + + if (buf->vm_flags & VM_PFNMAP) { + ret = isp_video_buffer_prepare_pfnmap(buf); + if (ret < 0) + return ret; + + ret = isp_video_buffer_sglist_pfnmap(buf); + } else { + ret = isp_video_buffer_prepare_user(buf); + if (ret < 0) + return ret; + + ret = isp_video_buffer_sglist_user(buf); + } + break; + + default: + return -EINVAL; + } + + if (ret < 0) + goto done; + + if (!(buf->vm_flags & VM_PFNMAP)) { + direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE + ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen, + direction); + if (ret != buf->sglen) { + ret = -EFAULT; + goto done; + } + } + + if (buf->queue->ops->buffer_prepare) + ret = buf->queue->ops->buffer_prepare(buf); + +done: + if (ret < 0) { + isp_video_buffer_cleanup(buf); + return ret; + } + + return ret; +} + +/* + * isp_video_queue_query - Query the status of a given buffer + * + * Locking: must be called with the queue lock held. + */ +static void isp_video_buffer_query(struct isp_video_buffer *buf, + struct v4l2_buffer *vbuf) +{ + memcpy(vbuf, &buf->vbuf, sizeof(*vbuf)); + + if (buf->vma_use_count) + vbuf->flags |= V4L2_BUF_FLAG_MAPPED; + + switch (buf->state) { + case ISP_BUF_STATE_ERROR: + vbuf->flags |= V4L2_BUF_FLAG_ERROR; + case ISP_BUF_STATE_DONE: + vbuf->flags |= V4L2_BUF_FLAG_DONE; + case ISP_BUF_STATE_QUEUED: + case ISP_BUF_STATE_ACTIVE: + vbuf->flags |= V4L2_BUF_FLAG_QUEUED; + break; + case ISP_BUF_STATE_IDLE: + default: + break; + } +} + +/* + * isp_video_buffer_wait - Wait for a buffer to be ready + * + * In non-blocking mode, return immediately with 0 if the buffer is ready or + * -EAGAIN if the buffer is in the QUEUED or ACTIVE state. + * + * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait + * queue using the same condition. + */ +static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking) +{ + if (nonblocking) { + return (buf->state != ISP_BUF_STATE_QUEUED && + buf->state != ISP_BUF_STATE_ACTIVE) + ? 0 : -EAGAIN; + } + + return wait_event_interruptible(buf->wait, + buf->state != ISP_BUF_STATE_QUEUED && + buf->state != ISP_BUF_STATE_ACTIVE); +} + +/* ----------------------------------------------------------------------------- + * Queue management + */ + +/* + * isp_video_queue_free - Free video buffers memory + * + * Buffers can only be freed if the queue isn't streaming and if no buffer is + * mapped to userspace. Return -EBUSY if those conditions aren't statisfied. + * + * This function must be called with the queue lock held. + */ +static int isp_video_queue_free(struct isp_video_queue *queue) +{ + unsigned int i; + + if (queue->streaming) + return -EBUSY; + + for (i = 0; i < queue->count; ++i) { + if (queue->buffers[i]->vma_use_count != 0) + return -EBUSY; + } + + for (i = 0; i < queue->count; ++i) { + struct isp_video_buffer *buf = queue->buffers[i]; + + isp_video_buffer_cleanup(buf); + + vfree(buf->vaddr); + buf->vaddr = NULL; + + kfree(buf); + queue->buffers[i] = NULL; + } + + INIT_LIST_HEAD(&queue->queue); + queue->count = 0; + return 0; +} + +/* + * isp_video_queue_alloc - Allocate video buffers memory + * + * This function must be called with the queue lock held. + */ +static int isp_video_queue_alloc(struct isp_video_queue *queue, + unsigned int nbuffers, + unsigned int size, enum v4l2_memory memory) +{ + struct isp_video_buffer *buf; + unsigned int i; + void *mem; + int ret; + + /* Start by freeing the buffers. */ + ret = isp_video_queue_free(queue); + if (ret < 0) + return ret; + + /* Bail out of no buffers should be allocated. */ + if (nbuffers == 0) + return 0; + + /* Initialize the allocated buffers. */ + for (i = 0; i < nbuffers; ++i) { + buf = kzalloc(queue->bufsize, GFP_KERNEL); + if (buf == NULL) + break; + + if (memory == V4L2_MEMORY_MMAP) { + /* Allocate video buffers memory for mmap mode. Align + * the size to the page size. + */ + mem = vmalloc_32_user(PAGE_ALIGN(size)); + if (mem == NULL) { + kfree(buf); + break; + } + + buf->vbuf.m.offset = i * PAGE_ALIGN(size); + buf->vaddr = mem; + } + + buf->vbuf.index = i; + buf->vbuf.length = size; + buf->vbuf.type = queue->type; + buf->vbuf.field = V4L2_FIELD_NONE; + buf->vbuf.memory = memory; + + buf->queue = queue; + init_waitqueue_head(&buf->wait); + + queue->buffers[i] = buf; + } + + if (i == 0) + return -ENOMEM; + + queue->count = i; + return nbuffers; +} + +/** + * omap3isp_video_queue_cleanup - Clean up the video buffers queue + * @queue: Video buffers queue + * + * Free all allocated resources and clean up the video buffers queue. The queue + * must not be busy (no ongoing video stream) and buffers must have been + * unmapped. + * + * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been + * unmapped. + */ +int omap3isp_video_queue_cleanup(struct isp_video_queue *queue) +{ + return isp_video_queue_free(queue); +} + +/** + * omap3isp_video_queue_init - Initialize the video buffers queue + * @queue: Video buffers queue + * @type: V4L2 buffer type (capture or output) + * @ops: Driver-specific queue operations + * @dev: Device used for DMA operations + * @bufsize: Size of the driver-specific buffer structure + * + * Initialize the video buffers queue with the supplied parameters. + * + * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or + * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet. + * + * Buffer objects will be allocated using the given buffer size to allow room + * for driver-specific fields. Driver-specific buffer structures must start + * with a struct isp_video_buffer field. Drivers with no driver-specific buffer + * structure must pass the size of the isp_video_buffer structure in the bufsize + * parameter. + * + * Return 0 on success. + */ +int omap3isp_video_queue_init(struct isp_video_queue *queue, + enum v4l2_buf_type type, + const struct isp_video_queue_operations *ops, + struct device *dev, unsigned int bufsize) +{ + INIT_LIST_HEAD(&queue->queue); + mutex_init(&queue->lock); + spin_lock_init(&queue->irqlock); + + queue->type = type; + queue->ops = ops; + queue->dev = dev; + queue->bufsize = bufsize; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 operations + */ + +/** + * omap3isp_video_queue_reqbufs - Allocate video buffers memory + * + * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It + * allocated video buffer objects and, for MMAP buffers, buffer memory. + * + * If the number of buffers is 0, all buffers are freed and the function returns + * without performing any allocation. + * + * If the number of buffers is not 0, currently allocated buffers (if any) are + * freed and the requested number of buffers are allocated. Depending on + * driver-specific requirements and on memory availability, a number of buffer + * smaller or bigger than requested can be allocated. This isn't considered as + * an error. + * + * Return 0 on success or one of the following error codes: + * + * -EINVAL if the buffer type or index are invalid + * -EBUSY if the queue is busy (streaming or buffers mapped) + * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition + */ +int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, + struct v4l2_requestbuffers *rb) +{ + unsigned int nbuffers = rb->count; + unsigned int size; + int ret; + + if (rb->type != queue->type) + return -EINVAL; + + queue->ops->queue_prepare(queue, &nbuffers, &size); + if (size == 0) + return -EINVAL; + + nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS); + + mutex_lock(&queue->lock); + + ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory); + if (ret < 0) + goto done; + + rb->count = ret; + ret = 0; + +done: + mutex_unlock(&queue->lock); + return ret; +} + +/** + * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue + * + * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It + * returns the status of a given video buffer. + * + * Return 0 on success or -EINVAL if the buffer type or index are invalid. + */ +int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, + struct v4l2_buffer *vbuf) +{ + struct isp_video_buffer *buf; + int ret = 0; + + if (vbuf->type != queue->type) + return -EINVAL; + + mutex_lock(&queue->lock); + + if (vbuf->index >= queue->count) { + ret = -EINVAL; + goto done; + } + + buf = queue->buffers[vbuf->index]; + isp_video_buffer_query(buf, vbuf); + +done: + mutex_unlock(&queue->lock); + return ret; +} + +/** + * omap3isp_video_queue_qbuf - Queue a buffer + * + * This function is intended to be used as a VIDIOC_QBUF ioctl handler. + * + * The v4l2_buffer structure passed from userspace is first sanity tested. If + * sane, the buffer is then processed and added to the main queue and, if the + * queue is streaming, to the IRQ queue. + * + * Before being enqueued, USERPTR buffers are checked for address changes. If + * the buffer has a different userspace address, the old memory area is unlocked + * and the new memory area is locked. + */ +int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, + struct v4l2_buffer *vbuf) +{ + struct isp_video_buffer *buf; + unsigned long flags; + int ret = -EINVAL; + + if (vbuf->type != queue->type) + goto done; + + mutex_lock(&queue->lock); + + if (vbuf->index >= queue->count) + goto done; + + buf = queue->buffers[vbuf->index]; + + if (vbuf->memory != buf->vbuf.memory) + goto done; + + if (buf->state != ISP_BUF_STATE_IDLE) + goto done; + + if (vbuf->memory == V4L2_MEMORY_USERPTR && + vbuf->m.userptr != buf->vbuf.m.userptr) { + isp_video_buffer_cleanup(buf); + buf->vbuf.m.userptr = vbuf->m.userptr; + buf->prepared = 0; + } + + if (!buf->prepared) { + ret = isp_video_buffer_prepare(buf); + if (ret < 0) + goto done; + buf->prepared = 1; + } + + isp_video_buffer_cache_sync(buf); + + buf->state = ISP_BUF_STATE_QUEUED; + list_add_tail(&buf->stream, &queue->queue); + + if (queue->streaming) { + spin_lock_irqsave(&queue->irqlock, flags); + queue->ops->buffer_queue(buf); + spin_unlock_irqrestore(&queue->irqlock, flags); + } + + ret = 0; + +done: + mutex_unlock(&queue->lock); + return ret; +} + +/** + * omap3isp_video_queue_dqbuf - Dequeue a buffer + * + * This function is intended to be used as a VIDIOC_DQBUF ioctl handler. + * + * The v4l2_buffer structure passed from userspace is first sanity tested. If + * sane, the buffer is then processed and added to the main queue and, if the + * queue is streaming, to the IRQ queue. + * + * Before being enqueued, USERPTR buffers are checked for address changes. If + * the buffer has a different userspace address, the old memory area is unlocked + * and the new memory area is locked. + */ +int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, + struct v4l2_buffer *vbuf, int nonblocking) +{ + struct isp_video_buffer *buf; + int ret; + + if (vbuf->type != queue->type) + return -EINVAL; + + mutex_lock(&queue->lock); + + if (list_empty(&queue->queue)) { + ret = -EINVAL; + goto done; + } + + buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); + ret = isp_video_buffer_wait(buf, nonblocking); + if (ret < 0) + goto done; + + list_del(&buf->stream); + + isp_video_buffer_query(buf, vbuf); + buf->state = ISP_BUF_STATE_IDLE; + vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED; + +done: + mutex_unlock(&queue->lock); + return ret; +} + +/** + * omap3isp_video_queue_streamon - Start streaming + * + * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It + * starts streaming on the queue and calls the buffer_queue operation for all + * queued buffers. + * + * Return 0 on success. + */ +int omap3isp_video_queue_streamon(struct isp_video_queue *queue) +{ + struct isp_video_buffer *buf; + unsigned long flags; + + mutex_lock(&queue->lock); + + if (queue->streaming) + goto done; + + queue->streaming = 1; + + spin_lock_irqsave(&queue->irqlock, flags); + list_for_each_entry(buf, &queue->queue, stream) + queue->ops->buffer_queue(buf); + spin_unlock_irqrestore(&queue->irqlock, flags); + +done: + mutex_unlock(&queue->lock); + return 0; +} + +/** + * omap3isp_video_queue_streamoff - Stop streaming + * + * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It + * stops streaming on the queue and wakes up all the buffers. + * + * Drivers must stop the hardware and synchronize with interrupt handlers and/or + * delayed works before calling this function to make sure no buffer will be + * touched by the driver and/or hardware. + */ +void omap3isp_video_queue_streamoff(struct isp_video_queue *queue) +{ + struct isp_video_buffer *buf; + unsigned long flags; + unsigned int i; + + mutex_lock(&queue->lock); + + if (!queue->streaming) + goto done; + + queue->streaming = 0; + + spin_lock_irqsave(&queue->irqlock, flags); + for (i = 0; i < queue->count; ++i) { + buf = queue->buffers[i]; + + if (buf->state == ISP_BUF_STATE_ACTIVE) + wake_up(&buf->wait); + + buf->state = ISP_BUF_STATE_IDLE; + } + spin_unlock_irqrestore(&queue->irqlock, flags); + + INIT_LIST_HEAD(&queue->queue); + +done: + mutex_unlock(&queue->lock); +} + +/** + * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE + * + * This function is intended to be used with suspend/resume operations. It + * discards all 'done' buffers as they would be too old to be requested after + * resume. + * + * Drivers must stop the hardware and synchronize with interrupt handlers and/or + * delayed works before calling this function to make sure no buffer will be + * touched by the driver and/or hardware. + */ +void omap3isp_video_queue_discard_done(struct isp_video_queue *queue) +{ + struct isp_video_buffer *buf; + unsigned int i; + + mutex_lock(&queue->lock); + + if (!queue->streaming) + goto done; + + for (i = 0; i < queue->count; ++i) { + buf = queue->buffers[i]; + + if (buf->state == ISP_BUF_STATE_DONE) + buf->state = ISP_BUF_STATE_ERROR; + } + +done: + mutex_unlock(&queue->lock); +} + +static void isp_video_queue_vm_open(struct vm_area_struct *vma) +{ + struct isp_video_buffer *buf = vma->vm_private_data; + + buf->vma_use_count++; +} + +static void isp_video_queue_vm_close(struct vm_area_struct *vma) +{ + struct isp_video_buffer *buf = vma->vm_private_data; + + buf->vma_use_count--; +} + +static const struct vm_operations_struct isp_video_queue_vm_ops = { + .open = isp_video_queue_vm_open, + .close = isp_video_queue_vm_close, +}; + +/** + * omap3isp_video_queue_mmap - Map buffers to userspace + * + * This function is intended to be used as an mmap() file operation handler. It + * maps a buffer to userspace based on the VMA offset. + * + * Only buffers of memory type MMAP are supported. + */ +int omap3isp_video_queue_mmap(struct isp_video_queue *queue, + struct vm_area_struct *vma) +{ + struct isp_video_buffer *uninitialized_var(buf); + unsigned long size; + unsigned int i; + int ret = 0; + + mutex_lock(&queue->lock); + + for (i = 0; i < queue->count; ++i) { + buf = queue->buffers[i]; + if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) + break; + } + + if (i == queue->count) { + ret = -EINVAL; + goto done; + } + + size = vma->vm_end - vma->vm_start; + + if (buf->vbuf.memory != V4L2_MEMORY_MMAP || + size != PAGE_ALIGN(buf->vbuf.length)) { + ret = -EINVAL; + goto done; + } + + ret = remap_vmalloc_range(vma, buf->vaddr, 0); + if (ret < 0) + goto done; + + vma->vm_ops = &isp_video_queue_vm_ops; + vma->vm_private_data = buf; + isp_video_queue_vm_open(vma); + +done: + mutex_unlock(&queue->lock); + return ret; +} + +/** + * omap3isp_video_queue_poll - Poll video queue state + * + * This function is intended to be used as a poll() file operation handler. It + * polls the state of the video buffer at the front of the queue and returns an + * events mask. + * + * If no buffer is present at the front of the queue, POLLERR is returned. + */ +unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, + struct file *file, poll_table *wait) +{ + struct isp_video_buffer *buf; + unsigned int mask = 0; + + mutex_lock(&queue->lock); + if (list_empty(&queue->queue)) { + mask |= POLLERR; + goto done; + } + buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); + + poll_wait(file, &buf->wait, wait); + if (buf->state == ISP_BUF_STATE_DONE || + buf->state == ISP_BUF_STATE_ERROR) { + if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + mask |= POLLIN | POLLRDNORM; + else + mask |= POLLOUT | POLLWRNORM; + } + +done: + mutex_unlock(&queue->lock); + return mask; +} diff --git a/drivers/media/video/omap3isp/ispqueue.h b/drivers/media/video/omap3isp/ispqueue.h new file mode 100644 index 0000000..251de3e --- /dev/null +++ b/drivers/media/video/omap3isp/ispqueue.h @@ -0,0 +1,187 @@ +/* + * ispqueue.h + * + * TI OMAP3 ISP - Video buffers queue handling + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_QUEUE_H +#define OMAP3_ISP_QUEUE_H + +#include +#include +#include +#include +#include + +struct isp_video_queue; +struct page; +struct scatterlist; + +#define ISP_VIDEO_MAX_BUFFERS 16 + +/** + * enum isp_video_buffer_state - ISP video buffer state + * @ISP_BUF_STATE_IDLE: The buffer is under userspace control (dequeued + * or not queued yet). + * @ISP_BUF_STATE_QUEUED: The buffer has been queued but isn't used by the + * device yet. + * @ISP_BUF_STATE_ACTIVE: The buffer is in use for an active video transfer. + * @ISP_BUF_STATE_ERROR: The device is done with the buffer and an error + * occured. For capture device the buffer likely contains corrupted data or + * no data at all. + * @ISP_BUF_STATE_DONE: The device is done with the buffer and no error occured. + * For capture devices the buffer contains valid data. + */ +enum isp_video_buffer_state { + ISP_BUF_STATE_IDLE, + ISP_BUF_STATE_QUEUED, + ISP_BUF_STATE_ACTIVE, + ISP_BUF_STATE_ERROR, + ISP_BUF_STATE_DONE, +}; + +/** + * struct isp_video_buffer - ISP video buffer + * @vma_use_count: Number of times the buffer is mmap'ed to userspace + * @stream: List head for insertion into main queue + * @queue: ISP buffers queue this buffer belongs to + * @prepared: Whether the buffer has been prepared + * @skip_cache: Whether to skip cache management operations for this buffer + * @vaddr: Memory virtual address (for kernel buffers) + * @vm_flags: Buffer VMA flags (for userspace buffers) + * @offset: Offset inside the first page (for userspace buffers) + * @npages: Number of pages (for userspace buffers) + * @pages: Pages table (for userspace non-VM_PFNMAP buffers) + * @paddr: Memory physical address (for userspace VM_PFNMAP buffers) + * @sglen: Number of elements in the scatter list (for non-VM_PFNMAP buffers) + * @sglist: Scatter list (for non-VM_PFNMAP buffers) + * @vbuf: V4L2 buffer + * @irqlist: List head for insertion into IRQ queue + * @state: Current buffer state + * @wait: Wait queue to signal buffer completion + */ +struct isp_video_buffer { + unsigned long vma_use_count; + struct list_head stream; + struct isp_video_queue *queue; + unsigned int prepared:1; + bool skip_cache; + + /* For kernel buffers. */ + void *vaddr; + + /* For userspace buffers. */ + unsigned long vm_flags; + unsigned long offset; + unsigned int npages; + struct page **pages; + dma_addr_t paddr; + + /* For all buffers except VM_PFNMAP. */ + unsigned int sglen; + struct scatterlist *sglist; + + /* Touched by the interrupt handler. */ + struct v4l2_buffer vbuf; + struct list_head irqlist; + enum isp_video_buffer_state state; + wait_queue_head_t wait; +}; + +#define to_isp_video_buffer(vb) container_of(vb, struct isp_video_buffer, vb) + +/** + * struct isp_video_queue_operations - Driver-specific operations + * @queue_prepare: Called before allocating buffers. Drivers should clamp the + * number of buffers according to their requirements, and must return the + * buffer size in bytes. + * @buffer_prepare: Called the first time a buffer is queued, or after changing + * the userspace memory address for a USERPTR buffer, with the queue lock + * held. Drivers should perform device-specific buffer preparation (such as + * mapping the buffer memory in an IOMMU). This operation is optional. + * @buffer_queue: Called when a buffer is being added to the queue with the + * queue irqlock spinlock held. + * @buffer_cleanup: Called before freeing buffers, or before changing the + * userspace memory address for a USERPTR buffer, with the queue lock held. + * Drivers must perform cleanup operations required to undo the + * buffer_prepare call. This operation is optional. + */ +struct isp_video_queue_operations { + void (*queue_prepare)(struct isp_video_queue *queue, + unsigned int *nbuffers, unsigned int *size); + int (*buffer_prepare)(struct isp_video_buffer *buf); + void (*buffer_queue)(struct isp_video_buffer *buf); + void (*buffer_cleanup)(struct isp_video_buffer *buf); +}; + +/** + * struct isp_video_queue - ISP video buffers queue + * @type: Type of video buffers handled by this queue + * @ops: Queue operations + * @dev: Device used for DMA operations + * @bufsize: Size of a driver-specific buffer object + * @count: Number of currently allocated buffers + * @buffers: ISP video buffers + * @lock: Mutex to protect access to the buffers, main queue and state + * @irqlock: Spinlock to protect access to the IRQ queue + * @streaming: Queue state, indicates whether the queue is streaming + * @queue: List of all queued buffers + */ +struct isp_video_queue { + enum v4l2_buf_type type; + const struct isp_video_queue_operations *ops; + struct device *dev; + unsigned int bufsize; + + unsigned int count; + struct isp_video_buffer *buffers[ISP_VIDEO_MAX_BUFFERS]; + struct mutex lock; + spinlock_t irqlock; + + unsigned int streaming:1; + + struct list_head queue; +}; + +int omap3isp_video_queue_cleanup(struct isp_video_queue *queue); +int omap3isp_video_queue_init(struct isp_video_queue *queue, + enum v4l2_buf_type type, + const struct isp_video_queue_operations *ops, + struct device *dev, unsigned int bufsize); + +int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, + struct v4l2_requestbuffers *rb); +int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, + struct v4l2_buffer *vbuf); +int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, + struct v4l2_buffer *vbuf); +int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, + struct v4l2_buffer *vbuf, int nonblocking); +int omap3isp_video_queue_streamon(struct isp_video_queue *queue); +void omap3isp_video_queue_streamoff(struct isp_video_queue *queue); +void omap3isp_video_queue_discard_done(struct isp_video_queue *queue); +int omap3isp_video_queue_mmap(struct isp_video_queue *queue, + struct vm_area_struct *vma); +unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, + struct file *file, poll_table *wait); + +#endif /* OMAP3_ISP_QUEUE_H */ diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c new file mode 100644 index 0000000..517a24d --- /dev/null +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -0,0 +1,1264 @@ +/* + * ispvideo.c + * + * TI OMAP3 ISP - Generic video node + * + * Copyright (C) 2009-2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ispvideo.h" +#include "isp.h" + + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static struct isp_format_info formats[] = { + { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 8, }, + { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, + { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 10, }, + { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10, + V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 10, }, + { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 10, }, + { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 10, }, + { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 12, }, + { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10, + V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 12, }, + { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 12, }, + { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 12, }, + { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16, + V4L2_MBUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 16, }, + { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16, + V4L2_MBUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 16, }, +}; + +const struct isp_format_info * +omap3isp_video_format_info(enum v4l2_mbus_pixelcode code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + if (formats[i].code == code) + return &formats[i]; + } + + return NULL; +} + +/* + * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format + * @video: ISP video instance + * @mbus: v4l2_mbus_framefmt format (input) + * @pix: v4l2_pix_format format (output) + * + * Fill the output pix structure with information from the input mbus format. + * The bytesperline and sizeimage fields are computed from the requested bytes + * per line value in the pix format and information from the video instance. + * + * Return the number of padding bytes at end of line. + */ +static unsigned int isp_video_mbus_to_pix(const struct isp_video *video, + const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix) +{ + unsigned int bpl = pix->bytesperline; + unsigned int min_bpl; + unsigned int i; + + memset(pix, 0, sizeof(*pix)); + pix->width = mbus->width; + pix->height = mbus->height; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + if (formats[i].code == mbus->code) + break; + } + + if (WARN_ON(i == ARRAY_SIZE(formats))) + return 0; + + min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8; + + /* Clamp the requested bytes per line value. If the maximum bytes per + * line value is zero, the module doesn't support user configurable line + * sizes. Override the requested value with the minimum in that case. + */ + if (video->bpl_max) + bpl = clamp(bpl, min_bpl, video->bpl_max); + else + bpl = min_bpl; + + if (!video->bpl_zero_padding || bpl != min_bpl) + bpl = ALIGN(bpl, video->bpl_alignment); + + pix->pixelformat = formats[i].pixelformat; + pix->bytesperline = bpl; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = mbus->colorspace; + pix->field = mbus->field; + + return bpl - min_bpl; +} + +static void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix, + struct v4l2_mbus_framefmt *mbus) +{ + unsigned int i; + + memset(mbus, 0, sizeof(*mbus)); + mbus->width = pix->width; + mbus->height = pix->height; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + if (formats[i].pixelformat == pix->pixelformat) + break; + } + + if (WARN_ON(i == ARRAY_SIZE(formats))) + return; + + mbus->code = formats[i].code; + mbus->colorspace = pix->colorspace; + mbus->field = pix->field; +} + +static struct v4l2_subdev * +isp_video_remote_subdev(struct isp_video *video, u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_source(&video->pad); + + if (remote == NULL || + media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +/* Return a pointer to the ISP video instance at the far end of the pipeline. */ +static struct isp_video * +isp_video_far_end(struct isp_video *video) +{ + struct media_entity_graph graph; + struct media_entity *entity = &video->video.entity; + struct media_device *mdev = entity->parent; + struct isp_video *far_end = NULL; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + if (entity == &video->video.entity) + continue; + + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + continue; + + far_end = to_isp_video(media_entity_to_video_device(entity)); + if (far_end->type != video->type) + break; + + far_end = NULL; + } + + mutex_unlock(&mdev->graph_mutex); + return far_end; +} + +/* + * Validate a pipeline by checking both ends of all links for format + * discrepancies. + * + * Compute the minimum time per frame value as the maximum of time per frame + * limits reported by every block in the pipeline. + * + * Return 0 if all formats match, or -EPIPE if at least one link is found with + * different formats on its two ends. + */ +static int isp_video_validate_pipeline(struct isp_pipeline *pipe) +{ + struct isp_device *isp = pipe->output->isp; + struct v4l2_subdev_format fmt_source; + struct v4l2_subdev_format fmt_sink; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int ret; + + pipe->max_rate = pipe->l3_ick; + + subdev = isp_video_remote_subdev(pipe->output, NULL); + if (subdev == NULL) + return -EPIPE; + + while (1) { + /* Retrieve the sink format */ + pad = &subdev->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + fmt_sink.pad = pad->index; + fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Update the maximum frame rate */ + if (subdev == &isp->isp_res.subdev) + omap3isp_resizer_max_rate(&isp->isp_res, + &pipe->max_rate); + + /* Check ccdc maximum data rate when data comes from sensor + * TODO: Include ccdc rate in pipe->max_rate and compare the + * total pipe rate with the input data rate from sensor. + */ + if (subdev == &isp->isp_ccdc.subdev && pipe->input == NULL) { + unsigned int rate = UINT_MAX; + + omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); + if (isp->isp_ccdc.vpcfg.pixelclk > rate) + return -ENOSPC; + } + + /* Retrieve the source format */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + fmt_source.pad = pad->index; + fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Check if the two ends match */ + if (fmt_source.format.code != fmt_sink.format.code || + fmt_source.format.width != fmt_sink.format.width || + fmt_source.format.height != fmt_sink.format.height) + return -EPIPE; + } + + return 0; +} + +static int +__isp_video_get_format(struct isp_video *video, struct v4l2_format *format) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + u32 pad; + int ret; + + subdev = isp_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + mutex_lock(&video->mutex); + + fmt.pad = pad; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret == -ENOIOCTLCMD) + ret = -EINVAL; + + mutex_unlock(&video->mutex); + + if (ret) + return ret; + + format->type = video->type; + return isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); +} + +static int +isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) +{ + struct v4l2_format format; + int ret; + + memcpy(&format, &vfh->format, sizeof(format)); + ret = __isp_video_get_format(video, &format); + if (ret < 0) + return ret; + + if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat || + vfh->format.fmt.pix.height != format.fmt.pix.height || + vfh->format.fmt.pix.width != format.fmt.pix.width || + vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || + vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage) + return -EINVAL; + + return ret; +} + +/* ----------------------------------------------------------------------------- + * IOMMU management + */ + +#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) + +/* + * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list + * @dev: Device pointer specific to the OMAP3 ISP. + * @sglist: Pointer to source Scatter gather list to allocate. + * @sglen: Number of elements of the scatter-gatter list. + * + * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if + * we ran out of memory. + */ +static dma_addr_t +ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen) +{ + struct sg_table *sgt; + u32 da; + + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (sgt == NULL) + return -ENOMEM; + + sgt->sgl = (struct scatterlist *)sglist; + sgt->nents = sglen; + sgt->orig_nents = sglen; + + da = iommu_vmap(isp->iommu, 0, sgt, IOMMU_FLAG); + if (IS_ERR_VALUE(da)) + kfree(sgt); + + return da; +} + +/* + * ispmmu_vunmap - Unmap a device address from the ISP MMU + * @dev: Device pointer specific to the OMAP3 ISP. + * @da: Device address generated from a ispmmu_vmap call. + */ +static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da) +{ + struct sg_table *sgt; + + sgt = iommu_vunmap(isp->iommu, (u32)da); + kfree(sgt); +} + +/* ----------------------------------------------------------------------------- + * Video queue operations + */ + +static void isp_video_queue_prepare(struct isp_video_queue *queue, + unsigned int *nbuffers, unsigned int *size) +{ + struct isp_video_fh *vfh = + container_of(queue, struct isp_video_fh, queue); + struct isp_video *video = vfh->video; + + *size = vfh->format.fmt.pix.sizeimage; + if (*size == 0) + return; + + *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size)); +} + +static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) +{ + struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); + struct isp_buffer *buffer = to_isp_buffer(buf); + struct isp_video *video = vfh->video; + + if (buffer->isp_addr) { + ispmmu_vunmap(video->isp, buffer->isp_addr); + buffer->isp_addr = 0; + } +} + +static int isp_video_buffer_prepare(struct isp_video_buffer *buf) +{ + struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); + struct isp_buffer *buffer = to_isp_buffer(buf); + struct isp_video *video = vfh->video; + unsigned long addr; + + addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen); + if (IS_ERR_VALUE(addr)) + return -EIO; + + if (!IS_ALIGNED(addr, 32)) { + dev_dbg(video->isp->dev, "Buffer address must be " + "aligned to 32 bytes boundary.\n"); + ispmmu_vunmap(video->isp, buffer->isp_addr); + return -EINVAL; + } + + buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage; + buffer->isp_addr = addr; + return 0; +} + +/* + * isp_video_buffer_queue - Add buffer to streaming queue + * @buf: Video buffer + * + * In memory-to-memory mode, start streaming on the pipeline if buffers are + * queued on both the input and the output, if the pipeline isn't already busy. + * If the pipeline is busy, it will be restarted in the output module interrupt + * handler. + */ +static void isp_video_buffer_queue(struct isp_video_buffer *buf) +{ + struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); + struct isp_buffer *buffer = to_isp_buffer(buf); + struct isp_video *video = vfh->video; + struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); + enum isp_pipeline_state state; + unsigned long flags; + unsigned int empty; + unsigned int start; + + empty = list_empty(&video->dmaqueue); + list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue); + + if (empty) { + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + state = ISP_PIPELINE_QUEUE_OUTPUT; + else + state = ISP_PIPELINE_QUEUE_INPUT; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state |= state; + video->ops->queue(video, buffer); + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; + + start = isp_pipeline_ready(pipe); + if (start) + pipe->state |= ISP_PIPELINE_STREAM; + spin_unlock_irqrestore(&pipe->lock, flags); + + if (start) + omap3isp_pipeline_set_stream(pipe, + ISP_PIPELINE_STREAM_SINGLESHOT); + } +} + +static const struct isp_video_queue_operations isp_video_queue_ops = { + .queue_prepare = &isp_video_queue_prepare, + .buffer_prepare = &isp_video_buffer_prepare, + .buffer_queue = &isp_video_buffer_queue, + .buffer_cleanup = &isp_video_buffer_cleanup, +}; + +/* + * omap3isp_video_buffer_next - Complete the current buffer and return the next + * @video: ISP video object + * @error: Whether an error occured during capture + * + * Remove the current video buffer from the DMA queue and fill its timestamp, + * field count and state fields before waking up its completion handler. + * + * The buffer state is set to VIDEOBUF_DONE if no error occured (@error is 0) + * or VIDEOBUF_ERROR otherwise (@error is non-zero). + * + * The DMA queue is expected to contain at least one buffer. + * + * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is + * empty. + */ +struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video, + unsigned int error) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); + struct isp_video_queue *queue = video->queue; + enum isp_pipeline_state state; + struct isp_video_buffer *buf; + unsigned long flags; + struct timespec ts; + + spin_lock_irqsave(&queue->irqlock, flags); + if (WARN_ON(list_empty(&video->dmaqueue))) { + spin_unlock_irqrestore(&queue->irqlock, flags); + return NULL; + } + + buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, + irqlist); + list_del(&buf->irqlist); + spin_unlock_irqrestore(&queue->irqlock, flags); + + ktime_get_ts(&ts); + buf->vbuf.timestamp.tv_sec = ts.tv_sec; + buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; + + /* Do frame number propagation only if this is the output video node. + * Frame number either comes from the CSI receivers or it gets + * incremented here if H3A is not active. + * Note: There is no guarantee that the output buffer will finish + * first, so the input number might lag behind by 1 in some cases. + */ + if (video == pipe->output && !pipe->do_propagation) + buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number); + else + buf->vbuf.sequence = atomic_read(&pipe->frame_number); + + buf->state = error ? ISP_BUF_STATE_ERROR : ISP_BUF_STATE_DONE; + + wake_up(&buf->wait); + + if (list_empty(&video->dmaqueue)) { + if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + state = ISP_PIPELINE_QUEUE_OUTPUT + | ISP_PIPELINE_STREAM; + else + state = ISP_PIPELINE_QUEUE_INPUT + | ISP_PIPELINE_STREAM; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~state; + if (video->pipe.stream_state == ISP_PIPELINE_STREAM_CONTINUOUS) + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; + spin_unlock_irqrestore(&pipe->lock, flags); + return NULL; + } + + if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~ISP_PIPELINE_STREAM; + spin_unlock_irqrestore(&pipe->lock, flags); + } + + buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, + irqlist); + buf->state = ISP_BUF_STATE_ACTIVE; + return to_isp_buffer(buf); +} + +/* + * omap3isp_video_resume - Perform resume operation on the buffers + * @video: ISP video object + * @continuous: Pipeline is in single shot mode if 0 or continous mode otherwise + * + * This function is intended to be used on suspend/resume scenario. It + * requests video queue layer to discard buffers marked as DONE if it's in + * continuous mode and requests ISP modules to queue again the ACTIVE buffer + * if there's any. + */ +void omap3isp_video_resume(struct isp_video *video, int continuous) +{ + struct isp_buffer *buf = NULL; + + if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + omap3isp_video_queue_discard_done(video->queue); + + if (!list_empty(&video->dmaqueue)) { + buf = list_first_entry(&video->dmaqueue, + struct isp_buffer, buffer.irqlist); + video->ops->queue(video, buf); + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; + } else { + if (continuous) + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; + } +} + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int +isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct isp_video *video = video_drvdata(file); + + strlcpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver)); + strlcpy(cap->card, video->video.name, sizeof(cap->card)); + strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); + cap->version = ISP_VIDEO_DRIVER_VERSION; + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + return 0; +} + +static int +isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + + if (format->type != video->type) + return -EINVAL; + + mutex_lock(&video->mutex); + *format = vfh->format; + mutex_unlock(&video->mutex); + + return 0; +} + +static int +isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + struct v4l2_mbus_framefmt fmt; + + if (format->type != video->type) + return -EINVAL; + + mutex_lock(&video->mutex); + + /* Fill the bytesperline and sizeimage fields by converting to media bus + * format and back to pixel format. + */ + isp_video_pix_to_mbus(&format->fmt.pix, &fmt); + isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix); + + vfh->format = *format; + + mutex_unlock(&video->mutex); + return 0; +} + +static int +isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct isp_video *video = video_drvdata(file); + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + u32 pad; + int ret; + + if (format->type != video->type) + return -EINVAL; + + subdev = isp_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format); + + fmt.pad = pad; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); + return 0; +} + +static int +isp_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) +{ + struct isp_video *video = video_drvdata(file); + struct v4l2_subdev *subdev; + int ret; + + subdev = isp_video_remote_subdev(video, NULL); + if (subdev == NULL) + return -EINVAL; + + mutex_lock(&video->mutex); + ret = v4l2_subdev_call(subdev, video, cropcap, cropcap); + mutex_unlock(&video->mutex); + + return ret == -ENOIOCTLCMD ? -EINVAL : ret; +} + +static int +isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct isp_video *video = video_drvdata(file); + struct v4l2_subdev_format format; + struct v4l2_subdev *subdev; + u32 pad; + int ret; + + subdev = isp_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + /* Try the get crop operation first and fallback to get format if not + * implemented. + */ + ret = v4l2_subdev_call(subdev, video, g_crop, crop); + if (ret != -ENOIOCTLCMD) + return ret; + + format.pad = pad; + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + crop->c.left = 0; + crop->c.top = 0; + crop->c.width = format.format.width; + crop->c.height = format.format.height; + + return 0; +} + +static int +isp_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct isp_video *video = video_drvdata(file); + struct v4l2_subdev *subdev; + int ret; + + subdev = isp_video_remote_subdev(video, NULL); + if (subdev == NULL) + return -EINVAL; + + mutex_lock(&video->mutex); + ret = v4l2_subdev_call(subdev, video, s_crop, crop); + mutex_unlock(&video->mutex); + + return ret == -ENOIOCTLCMD ? -EINVAL : ret; +} + +static int +isp_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + + if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + video->type != a->type) + return -EINVAL; + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + a->parm.output.timeperframe = vfh->timeperframe; + + return 0; +} + +static int +isp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + + if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + video->type != a->type) + return -EINVAL; + + if (a->parm.output.timeperframe.denominator == 0) + a->parm.output.timeperframe.denominator = 1; + + vfh->timeperframe = a->parm.output.timeperframe; + + return 0; +} + +static int +isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + + return omap3isp_video_queue_reqbufs(&vfh->queue, rb); +} + +static int +isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + + return omap3isp_video_queue_querybuf(&vfh->queue, b); +} + +static int +isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + + return omap3isp_video_queue_qbuf(&vfh->queue, b); +} + +static int +isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + + return omap3isp_video_queue_dqbuf(&vfh->queue, b, + file->f_flags & O_NONBLOCK); +} + +/* + * Stream management + * + * Every ISP pipeline has a single input and a single output. The input can be + * either a sensor or a video node. The output is always a video node. + * + * As every pipeline has an output video node, the ISP video objects at the + * pipeline output stores the pipeline state. It tracks the streaming state of + * both the input and output, as well as the availability of buffers. + * + * In sensor-to-memory mode, frames are always available at the pipeline input. + * Starting the sensor usually requires I2C transfers and must be done in + * interruptible context. The pipeline is started and stopped synchronously + * to the stream on/off commands. All modules in the pipeline will get their + * subdev set stream handler called. The module at the end of the pipeline must + * delay starting the hardware until buffers are available at its output. + * + * In memory-to-memory mode, starting/stopping the stream requires + * synchronization between the input and output. ISP modules can't be stopped + * in the middle of a frame, and at least some of the modules seem to become + * busy as soon as they're started, even if they don't receive a frame start + * event. For that reason frames need to be processed in single-shot mode. The + * driver needs to wait until a frame is completely processed and written to + * memory before restarting the pipeline for the next frame. Pipelined + * processing might be possible but requires more testing. + * + * Stream start must be delayed until buffers are available at both the input + * and output. The pipeline must be started in the videobuf queue callback with + * the buffers queue spinlock held. The modules subdev set stream operation must + * not sleep. + */ +static int +isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + enum isp_pipeline_state state; + struct isp_pipeline *pipe; + struct isp_video *far_end; + unsigned long flags; + int ret; + + if (type != video->type) + return -EINVAL; + + mutex_lock(&video->stream_lock); + + if (video->streaming) { + mutex_unlock(&video->stream_lock); + return -EBUSY; + } + + /* Start streaming on the pipeline. No link touching an entity in the + * pipeline can be activated or deactivated once streaming is started. + */ + pipe = video->video.entity.pipe + ? to_isp_pipeline(&video->video.entity) : &video->pipe; + media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + + /* Verify that the currently configured format matches the output of + * the connected subdev. + */ + ret = isp_video_check_format(video, vfh); + if (ret < 0) + goto error; + + video->bpl_padding = ret; + video->bpl_value = vfh->format.fmt.pix.bytesperline; + + /* Find the ISP video node connected at the far end of the pipeline and + * update the pipeline. + */ + far_end = isp_video_far_end(video); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT; + pipe->input = far_end; + pipe->output = video; + } else { + if (far_end == NULL) { + ret = -EPIPE; + goto error; + } + + state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; + pipe->input = video; + pipe->output = far_end; + } + + /* Make sure the interconnect clock runs fast enough. + * + * Formula from: resource34xx.c set_opp() + * If MPU freq is above 500MHz, make sure the interconnect + * is at 100Mhz or above. + * throughput in KiB/s for 100 Mhz = 100 * 1000 * 4. + * + * We want to be fast enough then set OCP clock to be max as + * possible, in that case 185Mhz then: + * throughput in KiB/s for 185Mhz = 185 * 1000 * 4 = 740000 KiB/s + */ + omap_pm_set_min_bus_tput(video->isp->dev, OCP_INITIATOR_AGENT, 740000); + pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); + + /* Validate the pipeline and update its state. */ + ret = isp_video_validate_pipeline(pipe); + if (ret < 0) + goto error; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~ISP_PIPELINE_STREAM; + pipe->state |= state; + spin_unlock_irqrestore(&pipe->lock, flags); + + /* Set the maximum time per frame as the value requested by userspace. + * This is a soft limit that can be overridden if the hardware doesn't + * support the request limit. + */ + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->max_timeperframe = vfh->timeperframe; + + video->queue = &vfh->queue; + INIT_LIST_HEAD(&video->dmaqueue); + atomic_set(&pipe->frame_number, -1); + + ret = omap3isp_video_queue_streamon(&vfh->queue); + if (ret < 0) + goto error; + + /* In sensor-to-memory mode, the stream can be started synchronously + * to the stream on command. In memory-to-memory mode, it will be + * started when buffers are queued on both the input and output. + */ + if (pipe->input == NULL) { + ret = omap3isp_pipeline_set_stream(pipe, + ISP_PIPELINE_STREAM_CONTINUOUS); + if (ret < 0) + goto error; + spin_lock_irqsave(&video->queue->irqlock, flags); + if (list_empty(&video->dmaqueue)) + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; + spin_unlock_irqrestore(&video->queue->irqlock, flags); + } + +error: + if (ret < 0) { + omap3isp_video_queue_streamoff(&vfh->queue); + omap_pm_set_min_bus_tput(video->isp->dev, + OCP_INITIATOR_AGENT, 0); + media_entity_pipeline_stop(&video->video.entity); + video->queue = NULL; + } + + if (!ret) + video->streaming = 1; + + mutex_unlock(&video->stream_lock); + return ret; +} + +static int +isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); + enum isp_pipeline_state state; + unsigned int streaming; + unsigned long flags; + + if (type != video->type) + return -EINVAL; + + mutex_lock(&video->stream_lock); + + /* Make sure we're not streaming yet. */ + mutex_lock(&vfh->queue.lock); + streaming = vfh->queue.streaming; + mutex_unlock(&vfh->queue.lock); + + if (!streaming) + goto done; + + /* Update the pipeline state. */ + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + state = ISP_PIPELINE_STREAM_OUTPUT + | ISP_PIPELINE_QUEUE_OUTPUT; + else + state = ISP_PIPELINE_STREAM_INPUT + | ISP_PIPELINE_QUEUE_INPUT; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~state; + spin_unlock_irqrestore(&pipe->lock, flags); + + /* Stop the stream. */ + omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED); + omap3isp_video_queue_streamoff(&vfh->queue); + video->queue = NULL; + video->streaming = 0; + + omap_pm_set_min_bus_tput(video->isp->dev, OCP_INITIATOR_AGENT, 0); + media_entity_pipeline_stop(&video->video.entity); + +done: + mutex_unlock(&video->stream_lock); + return 0; +} + +static int +isp_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + strlcpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int +isp_video_g_input(struct file *file, void *fh, unsigned int *input) +{ + *input = 0; + + return 0; +} + +static int +isp_video_s_input(struct file *file, void *fh, unsigned int input) +{ + return input == 0 ? 0 : -EINVAL; +} + +static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { + .vidioc_querycap = isp_video_querycap, + .vidioc_g_fmt_vid_cap = isp_video_get_format, + .vidioc_s_fmt_vid_cap = isp_video_set_format, + .vidioc_try_fmt_vid_cap = isp_video_try_format, + .vidioc_g_fmt_vid_out = isp_video_get_format, + .vidioc_s_fmt_vid_out = isp_video_set_format, + .vidioc_try_fmt_vid_out = isp_video_try_format, + .vidioc_cropcap = isp_video_cropcap, + .vidioc_g_crop = isp_video_get_crop, + .vidioc_s_crop = isp_video_set_crop, + .vidioc_g_parm = isp_video_get_param, + .vidioc_s_parm = isp_video_set_param, + .vidioc_reqbufs = isp_video_reqbufs, + .vidioc_querybuf = isp_video_querybuf, + .vidioc_qbuf = isp_video_qbuf, + .vidioc_dqbuf = isp_video_dqbuf, + .vidioc_streamon = isp_video_streamon, + .vidioc_streamoff = isp_video_streamoff, + .vidioc_enum_input = isp_video_enum_input, + .vidioc_g_input = isp_video_g_input, + .vidioc_s_input = isp_video_s_input, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 file operations + */ + +static int isp_video_open(struct file *file) +{ + struct isp_video *video = video_drvdata(file); + struct isp_video_fh *handle; + int ret = 0; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (handle == NULL) + return -ENOMEM; + + v4l2_fh_init(&handle->vfh, &video->video); + v4l2_fh_add(&handle->vfh); + + /* If this is the first user, initialise the pipeline. */ + if (omap3isp_get(video->isp) == NULL) { + ret = -EBUSY; + goto done; + } + + ret = omap3isp_pipeline_pm_use(&video->video.entity, 1); + if (ret < 0) { + omap3isp_put(video->isp); + goto done; + } + + omap3isp_video_queue_init(&handle->queue, video->type, + &isp_video_queue_ops, video->isp->dev, + sizeof(struct isp_buffer)); + + memset(&handle->format, 0, sizeof(handle->format)); + handle->format.type = video->type; + handle->timeperframe.denominator = 1; + + handle->video = video; + file->private_data = &handle->vfh; + +done: + if (ret < 0) { + v4l2_fh_del(&handle->vfh); + kfree(handle); + } + + return ret; +} + +static int isp_video_release(struct file *file) +{ + struct isp_video *video = video_drvdata(file); + struct v4l2_fh *vfh = file->private_data; + struct isp_video_fh *handle = to_isp_video_fh(vfh); + + /* Disable streaming and free the buffers queue resources. */ + isp_video_streamoff(file, vfh, video->type); + + mutex_lock(&handle->queue.lock); + omap3isp_video_queue_cleanup(&handle->queue); + mutex_unlock(&handle->queue.lock); + + omap3isp_pipeline_pm_use(&video->video.entity, 0); + + /* Release the file handle. */ + v4l2_fh_del(vfh); + kfree(handle); + file->private_data = NULL; + + omap3isp_put(video->isp); + + return 0; +} + +static unsigned int isp_video_poll(struct file *file, poll_table *wait) +{ + struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); + struct isp_video_queue *queue = &vfh->queue; + + return omap3isp_video_queue_poll(queue, file, wait); +} + +static int isp_video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); + + return omap3isp_video_queue_mmap(&vfh->queue, vma); +} + +static struct v4l2_file_operations isp_video_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = isp_video_open, + .release = isp_video_release, + .poll = isp_video_poll, + .mmap = isp_video_mmap, +}; + +/* ----------------------------------------------------------------------------- + * ISP video core + */ + +static const struct isp_video_operations isp_video_dummy_ops = { +}; + +int omap3isp_video_init(struct isp_video *video, const char *name) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_SINK; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_SOURCE; + break; + + default: + return -EINVAL; + } + + ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + if (ret < 0) + return ret; + + mutex_init(&video->mutex); + atomic_set(&video->active, 0); + + spin_lock_init(&video->pipe.lock); + mutex_init(&video->stream_lock); + + /* Initialize the video device. */ + if (video->ops == NULL) + video->ops = &isp_video_dummy_ops; + + video->video.fops = &isp_video_fops; + snprintf(video->video.name, sizeof(video->video.name), + "OMAP3 ISP %s %s", name, direction); + video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.release = video_device_release_empty; + video->video.ioctl_ops = &isp_video_ioctl_ops; + video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED; + + video_set_drvdata(&video->video, video); + + return 0; +} + +int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) +{ + int ret; + + video->video.v4l2_dev = vdev; + + ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + if (ret < 0) + printk(KERN_ERR "%s: could not register video device (%d)\n", + __func__, ret); + + return ret; +} + +void omap3isp_video_unregister(struct isp_video *video) +{ + if (video_is_registered(&video->video)) { + media_entity_cleanup(&video->video.entity); + video_unregister_device(&video->video); + } +} diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h new file mode 100644 index 0000000..524a1acd --- /dev/null +++ b/drivers/media/video/omap3isp/ispvideo.h @@ -0,0 +1,202 @@ +/* + * ispvideo.h + * + * TI OMAP3 ISP - Generic video node + * + * Copyright (C) 2009-2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_VIDEO_H +#define OMAP3_ISP_VIDEO_H + +#include +#include +#include +#include +#include + +#include "ispqueue.h" + +#define ISP_VIDEO_DRIVER_NAME "ispvideo" +#define ISP_VIDEO_DRIVER_VERSION KERNEL_VERSION(0, 0, 1) + +struct isp_device; +struct isp_video; +struct v4l2_mbus_framefmt; +struct v4l2_pix_format; + +/* + * struct isp_format_info - ISP media bus format information + * @code: V4L2 media bus format code + * @truncated: V4L2 media bus format code for the same format truncated to 10 + * bits. Identical to @code if the format is 10 bits wide or less. + * @uncompressed: V4L2 media bus format code for the corresponding uncompressed + * format. Identical to @code if the format is not DPCM compressed. + * @pixelformat: V4L2 pixel format FCC identifier + * @bpp: Bits per pixel + */ +struct isp_format_info { + enum v4l2_mbus_pixelcode code; + enum v4l2_mbus_pixelcode truncated; + enum v4l2_mbus_pixelcode uncompressed; + u32 pixelformat; + unsigned int bpp; +}; + +enum isp_pipeline_stream_state { + ISP_PIPELINE_STREAM_STOPPED = 0, + ISP_PIPELINE_STREAM_CONTINUOUS = 1, + ISP_PIPELINE_STREAM_SINGLESHOT = 2, +}; + +enum isp_pipeline_state { + /* The stream has been started on the input video node. */ + ISP_PIPELINE_STREAM_INPUT = 1, + /* The stream has been started on the output video node. */ + ISP_PIPELINE_STREAM_OUTPUT = 2, + /* At least one buffer is queued on the input video node. */ + ISP_PIPELINE_QUEUE_INPUT = 4, + /* At least one buffer is queued on the output video node. */ + ISP_PIPELINE_QUEUE_OUTPUT = 8, + /* The input entity is idle, ready to be started. */ + ISP_PIPELINE_IDLE_INPUT = 16, + /* The output entity is idle, ready to be started. */ + ISP_PIPELINE_IDLE_OUTPUT = 32, + /* The pipeline is currently streaming. */ + ISP_PIPELINE_STREAM = 64, +}; + +struct isp_pipeline { + struct media_pipeline pipe; + spinlock_t lock; /* Pipeline state and queue flags */ + unsigned int state; + enum isp_pipeline_stream_state stream_state; + struct isp_video *input; + struct isp_video *output; + unsigned long l3_ick; + unsigned int max_rate; + atomic_t frame_number; + bool do_propagation; /* of frame number */ + struct v4l2_fract max_timeperframe; +}; + +#define to_isp_pipeline(__e) \ + container_of((__e)->pipe, struct isp_pipeline, pipe) + +static inline int isp_pipeline_ready(struct isp_pipeline *pipe) +{ + return pipe->state == (ISP_PIPELINE_STREAM_INPUT | + ISP_PIPELINE_STREAM_OUTPUT | + ISP_PIPELINE_QUEUE_INPUT | + ISP_PIPELINE_QUEUE_OUTPUT | + ISP_PIPELINE_IDLE_INPUT | + ISP_PIPELINE_IDLE_OUTPUT); +} + +/* + * struct isp_buffer - ISP buffer + * @buffer: ISP video buffer + * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer. + */ +struct isp_buffer { + struct isp_video_buffer buffer; + dma_addr_t isp_addr; +}; + +#define to_isp_buffer(buf) container_of(buf, struct isp_buffer, buffer) + +enum isp_video_dmaqueue_flags { + /* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */ + ISP_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0), + /* Set when queuing buffer to an empty DMA queue */ + ISP_VIDEO_DMAQUEUE_QUEUED = (1 << 1), +}; + +#define isp_video_dmaqueue_flags_clr(video) \ + ({ (video)->dmaqueue_flags = 0; }) + +/* + * struct isp_video_operations - ISP video operations + * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF + * if there was no buffer previously queued. + */ +struct isp_video_operations { + int(*queue)(struct isp_video *video, struct isp_buffer *buffer); +}; + +struct isp_video { + struct video_device video; + enum v4l2_buf_type type; + struct media_pad pad; + + struct mutex mutex; /* format and crop settings */ + atomic_t active; + + struct isp_device *isp; + + unsigned int capture_mem; + unsigned int bpl_alignment; /* alignment value */ + unsigned int bpl_zero_padding; /* whether the alignment is optional */ + unsigned int bpl_max; /* maximum bytes per line value */ + unsigned int bpl_value; /* bytes per line value */ + unsigned int bpl_padding; /* padding at end of line */ + + /* Entity video node streaming */ + unsigned int streaming:1; + + /* Pipeline state */ + struct isp_pipeline pipe; + struct mutex stream_lock; /* pipeline and stream states */ + + /* Video buffers queue */ + struct isp_video_queue *queue; + struct list_head dmaqueue; + enum isp_video_dmaqueue_flags dmaqueue_flags; + + const struct isp_video_operations *ops; +}; + +#define to_isp_video(vdev) container_of(vdev, struct isp_video, video) + +struct isp_video_fh { + struct v4l2_fh vfh; + struct isp_video *video; + struct isp_video_queue queue; + struct v4l2_format format; + struct v4l2_fract timeperframe; +}; + +#define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh) +#define isp_video_queue_to_isp_video_fh(q) \ + container_of(q, struct isp_video_fh, queue) + +int omap3isp_video_init(struct isp_video *video, const char *name); +int omap3isp_video_register(struct isp_video *video, + struct v4l2_device *vdev); +void omap3isp_video_unregister(struct isp_video *video); +struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video, + unsigned int error); +void omap3isp_video_resume(struct isp_video *video, int continuous); +struct media_pad *omap3isp_video_remote_pad(struct isp_video *video); + +const struct isp_format_info * +omap3isp_video_format_info(enum v4l2_mbus_pixelcode code); + +#endif /* OMAP3_ISP_VIDEO_H */ -- cgit v0.10.2 From 121e9f1c4c49c76aac2425e3a9baef70f73f50a4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 12 Feb 2011 18:05:06 -0300 Subject: [media] omap3isp: CCP2/CSI2 receivers The OMAP3 ISP CCP2 and CSI2 receivers provide an interface to connect serial MIPI sensors to the device. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: David Cohen Signed-off-by: Stanimir Varbanov Signed-off-by: Vimarsh Zutshi Signed-off-by: Tuukka Toivonen Signed-off-by: Sergio Aguirre Signed-off-by: Antti Koskipaa Signed-off-by: Ivan T. Ivanov Signed-off-by: RaniSuneela Signed-off-by: Atanas Filipov Signed-off-by: Gjorgji Rosikopulos Signed-off-by: Hiroshi DOYU Signed-off-by: Nayden Kanchev Signed-off-by: Phil Carmody Signed-off-by: Artem Bityutskiy Signed-off-by: Dominic Curran Signed-off-by: Ilkka Myllyperkio Signed-off-by: Pallavi Kulkarni Signed-off-by: Vaibhav Hiremath Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c new file mode 100644 index 0000000..0efef2e --- /dev/null +++ b/drivers/media/video/omap3isp/ispccp2.c @@ -0,0 +1,1173 @@ +/* + * ispccp2.c + * + * TI OMAP3 ISP - CCP2 module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2010 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispccp2.h" + +/* Number of LCX channels */ +#define CCP2_LCx_CHANS_NUM 3 +/* Max/Min size for CCP2 video port */ +#define ISPCCP2_DAT_START_MIN 0 +#define ISPCCP2_DAT_START_MAX 4095 +#define ISPCCP2_DAT_SIZE_MIN 0 +#define ISPCCP2_DAT_SIZE_MAX 4095 +#define ISPCCP2_VPCLK_FRACDIV 65536 +#define ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP 0x12 +#define ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP 0x16 +/* Max/Min size for CCP2 memory channel */ +#define ISPCCP2_LCM_HSIZE_COUNT_MIN 16 +#define ISPCCP2_LCM_HSIZE_COUNT_MAX 8191 +#define ISPCCP2_LCM_HSIZE_SKIP_MIN 0 +#define ISPCCP2_LCM_HSIZE_SKIP_MAX 8191 +#define ISPCCP2_LCM_VSIZE_MIN 1 +#define ISPCCP2_LCM_VSIZE_MAX 8191 +#define ISPCCP2_LCM_HWORDS_MIN 1 +#define ISPCCP2_LCM_HWORDS_MAX 4095 +#define ISPCCP2_LCM_CTRL_BURST_SIZE_32X 5 +#define ISPCCP2_LCM_CTRL_READ_THROTTLE_FULL 0 +#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 2 +#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 2 +#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 3 +#define ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 3 +#define ISPCCP2_LCM_CTRL_DST_PORT_VP 0 +#define ISPCCP2_LCM_CTRL_DST_PORT_MEM 1 + +/* Set only the required bits */ +#define BIT_SET(var, shift, mask, val) \ + do { \ + var = ((var) & ~((mask) << (shift))) \ + | ((val) << (shift)); \ + } while (0) + +/* + * ccp2_print_status - Print current CCP2 module register values. + */ +#define CCP2_PRINT_REGISTER(isp, name)\ + dev_dbg(isp->dev, "###CCP2 " #name "=0x%08x\n", \ + isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_##name)) + +static void ccp2_print_status(struct isp_ccp2_device *ccp2) +{ + struct isp_device *isp = to_isp_device(ccp2); + + dev_dbg(isp->dev, "-------------CCP2 Register dump-------------\n"); + + CCP2_PRINT_REGISTER(isp, SYSCONFIG); + CCP2_PRINT_REGISTER(isp, SYSSTATUS); + CCP2_PRINT_REGISTER(isp, LC01_IRQENABLE); + CCP2_PRINT_REGISTER(isp, LC01_IRQSTATUS); + CCP2_PRINT_REGISTER(isp, LC23_IRQENABLE); + CCP2_PRINT_REGISTER(isp, LC23_IRQSTATUS); + CCP2_PRINT_REGISTER(isp, LCM_IRQENABLE); + CCP2_PRINT_REGISTER(isp, LCM_IRQSTATUS); + CCP2_PRINT_REGISTER(isp, CTRL); + CCP2_PRINT_REGISTER(isp, LCx_CTRL(0)); + CCP2_PRINT_REGISTER(isp, LCx_CODE(0)); + CCP2_PRINT_REGISTER(isp, LCx_STAT_START(0)); + CCP2_PRINT_REGISTER(isp, LCx_STAT_SIZE(0)); + CCP2_PRINT_REGISTER(isp, LCx_SOF_ADDR(0)); + CCP2_PRINT_REGISTER(isp, LCx_EOF_ADDR(0)); + CCP2_PRINT_REGISTER(isp, LCx_DAT_START(0)); + CCP2_PRINT_REGISTER(isp, LCx_DAT_SIZE(0)); + CCP2_PRINT_REGISTER(isp, LCx_DAT_PING_ADDR(0)); + CCP2_PRINT_REGISTER(isp, LCx_DAT_PONG_ADDR(0)); + CCP2_PRINT_REGISTER(isp, LCx_DAT_OFST(0)); + CCP2_PRINT_REGISTER(isp, LCM_CTRL); + CCP2_PRINT_REGISTER(isp, LCM_VSIZE); + CCP2_PRINT_REGISTER(isp, LCM_HSIZE); + CCP2_PRINT_REGISTER(isp, LCM_PREFETCH); + CCP2_PRINT_REGISTER(isp, LCM_SRC_ADDR); + CCP2_PRINT_REGISTER(isp, LCM_SRC_OFST); + CCP2_PRINT_REGISTER(isp, LCM_DST_ADDR); + CCP2_PRINT_REGISTER(isp, LCM_DST_OFST); + + dev_dbg(isp->dev, "--------------------------------------------\n"); +} + +/* + * ccp2_reset - Reset the CCP2 + * @ccp2: pointer to ISP CCP2 device + */ +static void ccp2_reset(struct isp_ccp2_device *ccp2) +{ + struct isp_device *isp = to_isp_device(ccp2); + int i = 0; + + /* Reset the CSI1/CCP2B and wait for reset to complete */ + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG, + ISPCCP2_SYSCONFIG_SOFT_RESET); + while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSSTATUS) & + ISPCCP2_SYSSTATUS_RESET_DONE)) { + udelay(10); + if (i++ > 10) { /* try read 10 times */ + dev_warn(isp->dev, + "omap3_isp: timeout waiting for ccp2 reset\n"); + break; + } + } +} + +/* + * ccp2_pwr_cfg - Configure the power mode settings + * @ccp2: pointer to ISP CCP2 device + */ +static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2) +{ + struct isp_device *isp = to_isp_device(ccp2); + + isp_reg_writel(isp, ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART | + ((isp->revision == ISP_REVISION_15_0 && isp->autoidle) ? + ISPCCP2_SYSCONFIG_AUTO_IDLE : 0), + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG); +} + +/* + * ccp2_if_enable - Enable CCP2 interface. + * @ccp2: pointer to ISP CCP2 device + * @enable: enable/disable flag + */ +static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) +{ + struct isp_device *isp = to_isp_device(ccp2); + struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); + int i; + + /* Enable/Disable all the LCx channels */ + for (i = 0; i < CCP2_LCx_CHANS_NUM; i++) + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(i), + ISPCCP2_LCx_CTRL_CHAN_EN, + enable ? ISPCCP2_LCx_CTRL_CHAN_EN : 0); + + /* Enable/Disable ccp2 interface in ccp2 mode */ + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, + ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN, + enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0); + + /* For frame count propagation */ + if (pipe->do_propagation) { + /* We may want the Frame Start IRQ from LC0 */ + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, + ISPCCP2_LC01_IRQENABLE, + ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCP2, + ISPCCP2_LC01_IRQENABLE, + ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); + } +} + +/* + * ccp2_mem_enable - Enable CCP2 memory interface. + * @ccp2: pointer to ISP CCP2 device + * @enable: enable/disable flag + */ +static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable) +{ + struct isp_device *isp = to_isp_device(ccp2); + + if (enable) + ccp2_if_enable(ccp2, 0); + + /* Enable/Disable ccp2 interface in ccp2 mode */ + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, + ISPCCP2_CTRL_MODE, enable ? ISPCCP2_CTRL_MODE : 0); + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL, + ISPCCP2_LCM_CTRL_CHAN_EN, + enable ? ISPCCP2_LCM_CTRL_CHAN_EN : 0); +} + +/* + * ccp2_phyif_config - Initialize CCP2 phy interface config + * @ccp2: Pointer to ISP CCP2 device + * @config: CCP2 platform data + * + * Configure the CCP2 physical interface module from platform data. + * + * Returns -EIO if strobe is chosen in CSI1 mode, or 0 on success. + */ +static int ccp2_phyif_config(struct isp_ccp2_device *ccp2, + const struct isp_ccp2_platform_data *pdata) +{ + struct isp_device *isp = to_isp_device(ccp2); + u32 val; + + /* CCP2B mode */ + val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL) | + ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE; + /* Data/strobe physical layer */ + BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK, + pdata->phy_layer); + BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK, + pdata->strobe_clk_pol); + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); + + val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); + if (!(val & ISPCCP2_CTRL_MODE)) { + if (pdata->ccp2_mode) + dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n"); + if (pdata->phy_layer == ISPCCP2_CTRL_PHY_SEL_STROBE) + /* Strobe mode requires CCP2 */ + return -EIO; + } + + return 0; +} + +/* + * ccp2_vp_config - Initialize CCP2 video port interface. + * @ccp2: Pointer to ISP CCP2 device + * @vpclk_div: Video port divisor + * + * Configure the CCP2 video port with the given clock divisor. The valid divisor + * values depend on the ISP revision: + * + * - revision 1.0 and 2.0 1 to 4 + * - revision 15.0 1 to 65536 + * + * The exact divisor value used might differ from the requested value, as ISP + * revision 15.0 represent the divisor by 65536 divided by an integer. + */ +static void ccp2_vp_config(struct isp_ccp2_device *ccp2, + unsigned int vpclk_div) +{ + struct isp_device *isp = to_isp_device(ccp2); + u32 val; + + /* ISPCCP2_CTRL Video port */ + val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); + val |= ISPCCP2_CTRL_VP_ONLY_EN; /* Disable the memory write port */ + + if (isp->revision == ISP_REVISION_15_0) { + vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 65536); + vpclk_div = min(ISPCCP2_VPCLK_FRACDIV / vpclk_div, 65535U); + BIT_SET(val, ISPCCP2_CTRL_VPCLK_DIV_SHIFT, + ISPCCP2_CTRL_VPCLK_DIV_MASK, vpclk_div); + } else { + vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 4); + BIT_SET(val, ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT, + ISPCCP2_CTRL_VP_OUT_CTRL_MASK, vpclk_div - 1); + } + + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); +} + +/* + * ccp2_lcx_config - Initialize CCP2 logical channel interface. + * @ccp2: Pointer to ISP CCP2 device + * @config: Pointer to ISP LCx config structure. + * + * This will analyze the parameters passed by the interface config + * and configure CSI1/CCP2 logical channel + * + */ +static void ccp2_lcx_config(struct isp_ccp2_device *ccp2, + struct isp_interface_lcx_config *config) +{ + struct isp_device *isp = to_isp_device(ccp2); + u32 val, format; + + switch (config->format) { + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + format = ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP; + break; + case V4L2_MBUS_FMT_SGRBG10_1X10: + default: + format = ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP; /* RAW10+VP */ + break; + } + /* ISPCCP2_LCx_CTRL logical channel #0 */ + val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0)) + | (ISPCCP2_LCx_CTRL_REGION_EN); /* Region */ + + if (isp->revision == ISP_REVISION_15_0) { + /* CRC */ + BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0, + ISPCCP2_LCx_CTRL_CRC_MASK, + config->crc); + /* Format = RAW10+VP or RAW8+DPCM10+VP*/ + BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0, + ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0, format); + } else { + BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT, + ISPCCP2_LCx_CTRL_CRC_MASK, + config->crc); + + BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT, + ISPCCP2_LCx_CTRL_FORMAT_MASK, format); + } + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0)); + + /* ISPCCP2_DAT_START for logical channel #0 */ + isp_reg_writel(isp, config->data_start << ISPCCP2_LCx_DAT_SHIFT, + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_START(0)); + + /* ISPCCP2_DAT_SIZE for logical channel #0 */ + isp_reg_writel(isp, config->data_size << ISPCCP2_LCx_DAT_SHIFT, + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_SIZE(0)); + + /* Enable error IRQs for logical channel #0 */ + val = ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; + + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQSTATUS); + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQENABLE, val); +} + +/* + * ccp2_if_configure - Configure ccp2 with data from sensor + * @ccp2: Pointer to ISP CCP2 device + * + * Return 0 on success or a negative error code + */ +static int ccp2_if_configure(struct isp_ccp2_device *ccp2) +{ + const struct isp_v4l2_subdevs_group *pdata; + struct v4l2_mbus_framefmt *format; + struct media_pad *pad; + struct v4l2_subdev *sensor; + u32 lines = 0; + int ret; + + ccp2_pwr_cfg(ccp2); + + pad = media_entity_remote_source(&ccp2->pads[CCP2_PAD_SINK]); + sensor = media_entity_to_v4l2_subdev(pad->entity); + pdata = sensor->host_priv; + + ret = ccp2_phyif_config(ccp2, &pdata->bus.ccp2); + if (ret < 0) + return ret; + + ccp2_vp_config(ccp2, pdata->bus.ccp2.vpclk_div + 1); + + v4l2_subdev_call(sensor, sensor, g_skip_top_lines, &lines); + + format = &ccp2->formats[CCP2_PAD_SINK]; + + ccp2->if_cfg.data_start = lines; + ccp2->if_cfg.crc = pdata->bus.ccp2.crc; + ccp2->if_cfg.format = format->code; + ccp2->if_cfg.data_size = format->height; + + ccp2_lcx_config(ccp2, &ccp2->if_cfg); + + return 0; +} + +static int ccp2_adjust_bandwidth(struct isp_ccp2_device *ccp2) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); + struct isp_device *isp = to_isp_device(ccp2); + const struct v4l2_mbus_framefmt *ofmt = &ccp2->formats[CCP2_PAD_SOURCE]; + unsigned long l3_ick = pipe->l3_ick; + struct v4l2_fract *timeperframe; + unsigned int vpclk_div = 2; + unsigned int value; + u64 bound; + u64 area; + + /* Compute the minimum clock divisor, based on the pipeline maximum + * data rate. This is an absolute lower bound if we don't want SBL + * overflows, so round the value up. + */ + vpclk_div = max_t(unsigned int, DIV_ROUND_UP(l3_ick, pipe->max_rate), + vpclk_div); + + /* Compute the maximum clock divisor, based on the requested frame rate. + * This is a soft lower bound to achieve a frame rate equal or higher + * than the requested value, so round the value down. + */ + timeperframe = &pipe->max_timeperframe; + + if (timeperframe->numerator) { + area = ofmt->width * ofmt->height; + bound = div_u64(area * timeperframe->denominator, + timeperframe->numerator); + value = min_t(u64, bound, l3_ick); + vpclk_div = max_t(unsigned int, l3_ick / value, vpclk_div); + } + + dev_dbg(isp->dev, "%s: minimum clock divisor = %u\n", __func__, + vpclk_div); + + return vpclk_div; +} + +/* + * ccp2_mem_configure - Initialize CCP2 memory input/output interface + * @ccp2: Pointer to ISP CCP2 device + * @config: Pointer to ISP mem interface config structure + * + * This will analyze the parameters passed by the interface config + * structure, and configure the respective registers for proper + * CSI1/CCP2 memory input. + */ +static void ccp2_mem_configure(struct isp_ccp2_device *ccp2, + struct isp_interface_mem_config *config) +{ + struct isp_device *isp = to_isp_device(ccp2); + u32 sink_pixcode = ccp2->formats[CCP2_PAD_SINK].code; + u32 source_pixcode = ccp2->formats[CCP2_PAD_SOURCE].code; + unsigned int dpcm_decompress = 0; + u32 val, hwords; + + if (sink_pixcode != source_pixcode && + sink_pixcode == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) + dpcm_decompress = 1; + + ccp2_pwr_cfg(ccp2); + + /* Hsize, Skip */ + isp_reg_writel(isp, ISPCCP2_LCM_HSIZE_SKIP_MIN | + (config->hsize_count << ISPCCP2_LCM_HSIZE_SHIFT), + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_HSIZE); + + /* Vsize, no. of lines */ + isp_reg_writel(isp, config->vsize_count << ISPCCP2_LCM_VSIZE_SHIFT, + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_VSIZE); + + if (ccp2->video_in.bpl_padding == 0) + config->src_ofst = 0; + else + config->src_ofst = ccp2->video_in.bpl_value; + + isp_reg_writel(isp, config->src_ofst, OMAP3_ISP_IOMEM_CCP2, + ISPCCP2_LCM_SRC_OFST); + + /* Source and Destination formats */ + val = ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 << + ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT; + + if (dpcm_decompress) { + /* source format is RAW8 */ + val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 << + ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT; + + /* RAW8 + DPCM10 - simple predictor */ + val |= ISPCCP2_LCM_CTRL_SRC_DPCM_PRED; + + /* enable source DPCM decompression */ + val |= ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 << + ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT; + } else { + /* source format is RAW10 */ + val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 << + ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT; + } + + /* Burst size to 32x64 */ + val |= ISPCCP2_LCM_CTRL_BURST_SIZE_32X << + ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT; + + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL); + + /* Prefetch setup */ + if (dpcm_decompress) + hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN + + config->hsize_count) >> 3; + else + hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN + + config->hsize_count) >> 2; + + isp_reg_writel(isp, hwords << ISPCCP2_LCM_PREFETCH_SHIFT, + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_PREFETCH); + + /* Video port */ + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, + ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE); + ccp2_vp_config(ccp2, ccp2_adjust_bandwidth(ccp2)); + + /* Clear LCM interrupts */ + isp_reg_writel(isp, ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ | + ISPCCP2_LCM_IRQSTATUS_EOF_IRQ, + OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQSTATUS); + + /* Enable LCM interupts */ + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQENABLE, + ISPCCP2_LCM_IRQSTATUS_EOF_IRQ | + ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ); +} + +/* + * ccp2_set_inaddr - Sets memory address of input frame. + * @ccp2: Pointer to ISP CCP2 device + * @addr: 32bit memory address aligned on 32byte boundary. + * + * Configures the memory address from which the input frame is to be read. + */ +static void ccp2_set_inaddr(struct isp_ccp2_device *ccp2, u32 addr) +{ + struct isp_device *isp = to_isp_device(ccp2); + + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_SRC_ADDR); +} + +/* ----------------------------------------------------------------------------- + * Interrupt handling + */ + +static void ccp2_isr_buffer(struct isp_ccp2_device *ccp2) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); + struct isp_buffer *buffer; + + buffer = omap3isp_video_buffer_next(&ccp2->video_in, ccp2->error); + if (buffer != NULL) + ccp2_set_inaddr(ccp2, buffer->isp_addr); + + pipe->state |= ISP_PIPELINE_IDLE_INPUT; + + if (ccp2->state == ISP_PIPELINE_STREAM_SINGLESHOT) { + if (isp_pipeline_ready(pipe)) + omap3isp_pipeline_set_stream(pipe, + ISP_PIPELINE_STREAM_SINGLESHOT); + } + + ccp2->error = 0; +} + +/* + * omap3isp_ccp2_isr - Handle ISP CCP2 interrupts + * @ccp2: Pointer to ISP CCP2 device + * + * This will handle the CCP2 interrupts + * + * Returns -EIO in case of error, or 0 on success. + */ +int omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2) +{ + struct isp_device *isp = to_isp_device(ccp2); + int ret = 0; + static const u32 ISPCCP2_LC01_ERROR = + ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | + ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; + u32 lcx_irqstatus, lcm_irqstatus; + + /* First clear the interrupts */ + lcx_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, + ISPCCP2_LC01_IRQSTATUS); + isp_reg_writel(isp, lcx_irqstatus, OMAP3_ISP_IOMEM_CCP2, + ISPCCP2_LC01_IRQSTATUS); + + lcm_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, + ISPCCP2_LCM_IRQSTATUS); + isp_reg_writel(isp, lcm_irqstatus, OMAP3_ISP_IOMEM_CCP2, + ISPCCP2_LCM_IRQSTATUS); + /* Errors */ + if (lcx_irqstatus & ISPCCP2_LC01_ERROR) { + ccp2->error = 1; + dev_dbg(isp->dev, "CCP2 err:%x\n", lcx_irqstatus); + return -EIO; + } + + if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ) { + ccp2->error = 1; + dev_dbg(isp->dev, "CCP2 OCP err:%x\n", lcm_irqstatus); + ret = -EIO; + } + + if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping)) + return 0; + + /* Frame number propagation */ + if (lcx_irqstatus & ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ) { + struct isp_pipeline *pipe = + to_isp_pipeline(&ccp2->subdev.entity); + if (pipe->do_propagation) + atomic_inc(&pipe->frame_number); + } + + /* Handle queued buffers on frame end interrupts */ + if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ) + ccp2_isr_buffer(ccp2); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static const unsigned int ccp2_fmts[] = { + V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, +}; + +/* + * __ccp2_get_format - helper function for getting ccp2 format + * @ccp2 : Pointer to ISP CCP2 device + * @fh : V4L2 subdev file handle + * @pad : pad number + * @which : wanted subdev format + * return format structure or NULL on error + */ +static struct v4l2_mbus_framefmt * +__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + else + return &ccp2->formats[pad]; +} + +/* + * ccp2_try_format - Handle try format by pad subdev method + * @ccp2 : Pointer to ISP CCP2 device + * @fh : V4L2 subdev file handle + * @pad : pad num + * @fmt : pointer to v4l2 mbus format structure + * @which : wanted subdev format + */ +static void ccp2_try_format(struct isp_ccp2_device *ccp2, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *format; + + switch (pad) { + case CCP2_PAD_SINK: + if (fmt->code != V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + if (ccp2->input == CCP2_INPUT_SENSOR) { + fmt->width = clamp_t(u32, fmt->width, + ISPCCP2_DAT_START_MIN, + ISPCCP2_DAT_START_MAX); + fmt->height = clamp_t(u32, fmt->height, + ISPCCP2_DAT_SIZE_MIN, + ISPCCP2_DAT_SIZE_MAX); + } else if (ccp2->input == CCP2_INPUT_MEMORY) { + fmt->width = clamp_t(u32, fmt->width, + ISPCCP2_LCM_HSIZE_COUNT_MIN, + ISPCCP2_LCM_HSIZE_COUNT_MAX); + fmt->height = clamp_t(u32, fmt->height, + ISPCCP2_LCM_VSIZE_MIN, + ISPCCP2_LCM_VSIZE_MAX); + } + break; + + case CCP2_PAD_SOURCE: + /* Source format - copy sink format and change pixel code + * to SGRBG10_1X10 as we don't support CCP2 write to memory. + * When CCP2 write to memory feature will be added this + * should be changed properly. + */ + format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + break; + } + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +/* + * ccp2_enum_mbus_code - Handle pixel format enumeration + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @code : pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (code->pad == CCP2_PAD_SINK) { + if (code->index >= ARRAY_SIZE(ccp2_fmts)) + return -EINVAL; + + code->code = ccp2_fmts[code->index]; + } else { + if (code->index != 0) + return -EINVAL; + + format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, + V4L2_SUBDEV_FORMAT_TRY); + code->code = format->code; + } + + return 0; +} + +static int ccp2_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * ccp2_get_format - Handle get format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt : pointer to v4l2 subdev format structure + * return -EINVAL or zero on sucess + */ +static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +/* + * ccp2_set_format - Handle set format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt : pointer to v4l2 subdev format structure + * returns zero + */ +static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ccp2_try_format(ccp2, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == CCP2_PAD_SINK) { + format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SOURCE, + fmt->which); + *format = fmt->format; + ccp2_try_format(ccp2, fh, CCP2_PAD_SOURCE, format, fmt->which); + } + + return 0; +} + +/* + * ccp2_init_formats - Initialize formats on all pads + * @sd: ISP CCP2 V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int ccp2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = CCP2_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + format.format.width = 4096; + format.format.height = 4096; + ccp2_set_format(sd, fh, &format); + + return 0; +} + +/* + * ccp2_s_stream - Enable/Disable streaming on ccp2 subdev + * @sd : pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + * return zero + */ +static int ccp2_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); + struct isp_device *isp = to_isp_device(ccp2); + struct device *dev = to_device(ccp2); + int ret; + + if (ccp2->state == ISP_PIPELINE_STREAM_STOPPED) { + if (enable == ISP_PIPELINE_STREAM_STOPPED) + return 0; + atomic_set(&ccp2->stopping, 0); + ccp2->error = 0; + } + + switch (enable) { + case ISP_PIPELINE_STREAM_CONTINUOUS: + if (ccp2->phy) { + ret = omap3isp_csiphy_acquire(ccp2->phy); + if (ret < 0) + return ret; + } + + ccp2_if_configure(ccp2); + ccp2_print_status(ccp2); + + /* Enable CSI1/CCP2 interface */ + ccp2_if_enable(ccp2, 1); + break; + + case ISP_PIPELINE_STREAM_SINGLESHOT: + if (ccp2->state != ISP_PIPELINE_STREAM_SINGLESHOT) { + struct v4l2_mbus_framefmt *format; + + format = &ccp2->formats[CCP2_PAD_SINK]; + + ccp2->mem_cfg.hsize_count = format->width; + ccp2->mem_cfg.vsize_count = format->height; + ccp2->mem_cfg.src_ofst = 0; + + ccp2_mem_configure(ccp2, &ccp2->mem_cfg); + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI1_READ); + ccp2_print_status(ccp2); + } + ccp2_mem_enable(ccp2, 1); + break; + + case ISP_PIPELINE_STREAM_STOPPED: + if (omap3isp_module_sync_idle(&sd->entity, &ccp2->wait, + &ccp2->stopping)) + dev_dbg(dev, "%s: module stop timeout.\n", sd->name); + if (ccp2->input == CCP2_INPUT_MEMORY) { + ccp2_mem_enable(ccp2, 0); + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI1_READ); + } else if (ccp2->input == CCP2_INPUT_SENSOR) { + /* Disable CSI1/CCP2 interface */ + ccp2_if_enable(ccp2, 0); + if (ccp2->phy) + omap3isp_csiphy_release(ccp2->phy); + } + break; + } + + ccp2->state = enable; + return 0; +} + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ccp2_sd_video_ops = { + .s_stream = ccp2_s_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ccp2_sd_pad_ops = { + .enum_mbus_code = ccp2_enum_mbus_code, + .enum_frame_size = ccp2_enum_frame_size, + .get_fmt = ccp2_get_format, + .set_fmt = ccp2_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops ccp2_sd_ops = { + .video = &ccp2_sd_video_ops, + .pad = &ccp2_sd_pad_ops, +}; + +/* subdev internal operations */ +static const struct v4l2_subdev_internal_ops ccp2_sd_internal_ops = { + .open = ccp2_init_formats, +}; + +/* -------------------------------------------------------------------------- + * ISP ccp2 video device node + */ + +/* + * ccp2_video_queue - Queue video buffer. + * @video : Pointer to isp video structure + * @buffer: Pointer to isp_buffer structure + * return -EIO or zero on success + */ +static int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer) +{ + struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2; + + ccp2_set_inaddr(ccp2, buffer->isp_addr); + return 0; +} + +static const struct isp_video_operations ccp2_video_ops = { + .queue = ccp2_video_queue, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +/* + * ccp2_link_setup - Setup ccp2 connections. + * @entity : Pointer to media entity structure + * @local : Pointer to local pad array + * @remote : Pointer to remote pad array + * @flags : Link flags + * return -EINVAL on error or zero on success + */ +static int ccp2_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case CCP2_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* read from memory */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (ccp2->input == CCP2_INPUT_SENSOR) + return -EBUSY; + ccp2->input = CCP2_INPUT_MEMORY; + } else { + if (ccp2->input == CCP2_INPUT_MEMORY) + ccp2->input = CCP2_INPUT_NONE; + } + break; + + case CCP2_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from sensor/phy */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (ccp2->input == CCP2_INPUT_MEMORY) + return -EBUSY; + ccp2->input = CCP2_INPUT_SENSOR; + } else { + if (ccp2->input == CCP2_INPUT_SENSOR) + ccp2->input = CCP2_INPUT_NONE; + } break; + + case CCP2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + /* write to video port/ccdc */ + if (flags & MEDIA_LNK_FL_ENABLED) + ccp2->output = CCP2_OUTPUT_CCDC; + else + ccp2->output = CCP2_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* media operations */ +static const struct media_entity_operations ccp2_media_ops = { + .link_setup = ccp2_link_setup, +}; + +/* + * ccp2_init_entities - Initialize ccp2 subdev and media entity. + * @ccp2: Pointer to ISP CCP2 device + * return negative error code or zero on success + */ +static int ccp2_init_entities(struct isp_ccp2_device *ccp2) +{ + struct v4l2_subdev *sd = &ccp2->subdev; + struct media_pad *pads = ccp2->pads; + struct media_entity *me = &sd->entity; + int ret; + + ccp2->input = CCP2_INPUT_NONE; + ccp2->output = CCP2_OUTPUT_NONE; + + v4l2_subdev_init(sd, &ccp2_sd_ops); + sd->internal_ops = &ccp2_sd_internal_ops; + strlcpy(sd->name, "OMAP3 ISP CCP2", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ + v4l2_set_subdevdata(sd, ccp2); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + me->ops = &ccp2_media_ops; + ret = media_entity_init(me, CCP2_PADS_NUM, pads, 0); + if (ret < 0) + return ret; + + ccp2_init_formats(sd, NULL); + + /* + * The CCP2 has weird line alignment requirements, possibly caused by + * DPCM8 decompression. Line length for data read from memory must be a + * multiple of 128 bits (16 bytes) in continuous mode (when no padding + * is present at end of lines). Additionally, if padding is used, the + * padded line length must be a multiple of 32 bytes. To simplify the + * implementation we use a fixed 32 bytes alignment regardless of the + * input format and width. If strict 128 bits alignment support is + * required ispvideo will need to be made aware of this special dual + * alignement requirements. + */ + ccp2->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ccp2->video_in.bpl_alignment = 32; + ccp2->video_in.bpl_max = 0xffffffe0; + ccp2->video_in.isp = to_isp_device(ccp2); + ccp2->video_in.ops = &ccp2_video_ops; + ccp2->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; + + ret = omap3isp_video_init(&ccp2->video_in, "CCP2"); + if (ret < 0) + return ret; + + /* Connect the video node to the ccp2 subdev. */ + ret = media_entity_create_link(&ccp2->video_in.video.entity, 0, + &ccp2->subdev.entity, CCP2_PAD_SINK, 0); + if (ret < 0) + return ret; + + return 0; +} + +/* + * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev + * @ccp2: Pointer to ISP CCP2 device + */ +void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2) +{ + media_entity_cleanup(&ccp2->subdev.entity); + + v4l2_device_unregister_subdev(&ccp2->subdev); + omap3isp_video_unregister(&ccp2->video_in); +} + +/* + * omap3isp_ccp2_register_entities - Register the subdev media entity + * @ccp2: Pointer to ISP CCP2 device + * @vdev: Pointer to v4l device + * return negative error code or zero on success + */ + +int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video nodes. */ + ret = v4l2_device_register_subdev(vdev, &ccp2->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&ccp2->video_in, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_ccp2_unregister_entities(ccp2); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP ccp2 initialisation and cleanup + */ + +/* + * omap3isp_ccp2_cleanup - CCP2 un-initialization + * @isp : Pointer to ISP device + */ +void omap3isp_ccp2_cleanup(struct isp_device *isp) +{ +} + +/* + * omap3isp_ccp2_init - CCP2 initialization. + * @isp : Pointer to ISP device + * return negative error code or zero on success + */ +int omap3isp_ccp2_init(struct isp_device *isp) +{ + struct isp_ccp2_device *ccp2 = &isp->isp_ccp2; + int ret; + + init_waitqueue_head(&ccp2->wait); + + /* On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with + * the CSI2c or CSI2a receivers. The PHY then needs to be explicitly + * configured. + * + * TODO: Don't hardcode the usage of PHY1 (shared with CSI2c). + */ + if (isp->revision == ISP_REVISION_15_0) + ccp2->phy = &isp->isp_csiphy1; + + ret = ccp2_init_entities(ccp2); + if (ret < 0) + goto out; + + ccp2_reset(ccp2); +out: + if (ret) + omap3isp_ccp2_cleanup(isp); + + return ret; +} diff --git a/drivers/media/video/omap3isp/ispccp2.h b/drivers/media/video/omap3isp/ispccp2.h new file mode 100644 index 0000000..5505a86 --- /dev/null +++ b/drivers/media/video/omap3isp/ispccp2.h @@ -0,0 +1,98 @@ +/* + * ispccp2.h + * + * TI OMAP3 ISP - CCP2 module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2010 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_CCP2_H +#define OMAP3_ISP_CCP2_H + +#include + +struct isp_device; +struct isp_csiphy; + +/* Sink and source ccp2 pads */ +#define CCP2_PAD_SINK 0 +#define CCP2_PAD_SOURCE 1 +#define CCP2_PADS_NUM 2 + +/* CCP2 input media entity */ +enum ccp2_input_entity { + CCP2_INPUT_NONE, + CCP2_INPUT_SENSOR, + CCP2_INPUT_MEMORY, +}; + +/* CCP2 output media entity */ +enum ccp2_output_entity { + CCP2_OUTPUT_NONE, + CCP2_OUTPUT_CCDC, + CCP2_OUTPUT_MEMORY, +}; + + +/* Logical channel configuration */ +struct isp_interface_lcx_config { + int crc; + u32 data_start; + u32 data_size; + u32 format; +}; + +/* Memory channel configuration */ +struct isp_interface_mem_config { + u32 dst_port; + u32 vsize_count; + u32 hsize_count; + u32 src_ofst; + u32 dst_ofst; +}; + +/* CCP2 device */ +struct isp_ccp2_device { + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt formats[CCP2_PADS_NUM]; + struct media_pad pads[CCP2_PADS_NUM]; + + enum ccp2_input_entity input; + enum ccp2_output_entity output; + struct isp_interface_lcx_config if_cfg; + struct isp_interface_mem_config mem_cfg; + struct isp_video video_in; + struct isp_csiphy *phy; + unsigned int error; + enum isp_pipeline_stream_state state; + wait_queue_head_t wait; + atomic_t stopping; +}; + +/* Function declarations */ +int omap3isp_ccp2_init(struct isp_device *isp); +void omap3isp_ccp2_cleanup(struct isp_device *isp); +int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, + struct v4l2_device *vdev); +void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2); +int omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2); + +#endif /* OMAP3_ISP_CCP2_H */ diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c new file mode 100644 index 0000000..fb503f3 --- /dev/null +++ b/drivers/media/video/omap3isp/ispcsi2.c @@ -0,0 +1,1317 @@ +/* + * ispcsi2.c + * + * TI OMAP3 ISP - CSI2 module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispcsi2.h" + +/* + * csi2_if_enable - Enable CSI2 Receiver interface. + * @enable: enable flag + * + */ +static void csi2_if_enable(struct isp_device *isp, + struct isp_csi2_device *csi2, u8 enable) +{ + struct isp_csi2_ctrl_cfg *currctrl = &csi2->ctrl; + + isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_CTRL, ISPCSI2_CTRL_IF_EN, + enable ? ISPCSI2_CTRL_IF_EN : 0); + + currctrl->if_enable = enable; +} + +/* + * csi2_recv_config - CSI2 receiver module configuration. + * @currctrl: isp_csi2_ctrl_cfg structure + * + */ +static void csi2_recv_config(struct isp_device *isp, + struct isp_csi2_device *csi2, + struct isp_csi2_ctrl_cfg *currctrl) +{ + u32 reg; + + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTRL); + + if (currctrl->frame_mode) + reg |= ISPCSI2_CTRL_FRAME; + else + reg &= ~ISPCSI2_CTRL_FRAME; + + if (currctrl->vp_clk_enable) + reg |= ISPCSI2_CTRL_VP_CLK_EN; + else + reg &= ~ISPCSI2_CTRL_VP_CLK_EN; + + if (currctrl->vp_only_enable) + reg |= ISPCSI2_CTRL_VP_ONLY_EN; + else + reg &= ~ISPCSI2_CTRL_VP_ONLY_EN; + + reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK; + reg |= currctrl->vp_out_ctrl << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT; + + if (currctrl->ecc_enable) + reg |= ISPCSI2_CTRL_ECC_EN; + else + reg &= ~ISPCSI2_CTRL_ECC_EN; + + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTRL); +} + +static const unsigned int csi2_input_fmts[] = { + V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, + V4L2_MBUS_FMT_SGBRG10_1X10, + V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, +}; + +/* To set the format on the CSI2 requires a mapping function that takes + * the following inputs: + * - 2 different formats (at this time) + * - 2 destinations (mem, vp+mem) (vp only handled separately) + * - 2 decompression options (on, off) + * - 2 isp revisions (certain format must be handled differently on OMAP3630) + * Output should be CSI2 frame format code + * Array indices as follows: [format][dest][decompr][is_3630] + * Not all combinations are valid. 0 means invalid. + */ +static const u16 __csi2_fmt_map[2][2][2][2] = { + /* RAW10 formats */ + { + /* Output to memory */ + { + /* No DPCM decompression */ + { CSI2_PIX_FMT_RAW10_EXP16, CSI2_PIX_FMT_RAW10_EXP16 }, + /* DPCM decompression */ + { 0, 0 }, + }, + /* Output to both */ + { + /* No DPCM decompression */ + { CSI2_PIX_FMT_RAW10_EXP16_VP, + CSI2_PIX_FMT_RAW10_EXP16_VP }, + /* DPCM decompression */ + { 0, 0 }, + }, + }, + /* RAW10 DPCM8 formats */ + { + /* Output to memory */ + { + /* No DPCM decompression */ + { CSI2_PIX_FMT_RAW8, CSI2_USERDEF_8BIT_DATA1 }, + /* DPCM decompression */ + { CSI2_PIX_FMT_RAW8_DPCM10_EXP16, + CSI2_USERDEF_8BIT_DATA1_DPCM10 }, + }, + /* Output to both */ + { + /* No DPCM decompression */ + { CSI2_PIX_FMT_RAW8_VP, + CSI2_PIX_FMT_RAW8_VP }, + /* DPCM decompression */ + { CSI2_PIX_FMT_RAW8_DPCM10_VP, + CSI2_USERDEF_8BIT_DATA1_DPCM10_VP }, + }, + }, +}; + +/* + * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID + * @csi2: ISP CSI2 device + * + * Returns CSI2 physical format id + */ +static u16 csi2_ctx_map_format(struct isp_csi2_device *csi2) +{ + const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK]; + int fmtidx, destidx, is_3630; + + switch (fmt->code) { + case V4L2_MBUS_FMT_SGRBG10_1X10: + case V4L2_MBUS_FMT_SRGGB10_1X10: + case V4L2_MBUS_FMT_SBGGR10_1X10: + case V4L2_MBUS_FMT_SGBRG10_1X10: + fmtidx = 0; + break; + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8: + case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8: + fmtidx = 1; + break; + default: + WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n", + fmt->code); + return 0; + } + + if (!(csi2->output & CSI2_OUTPUT_CCDC) && + !(csi2->output & CSI2_OUTPUT_MEMORY)) { + /* Neither output enabled is a valid combination */ + return CSI2_PIX_FMT_OTHERS; + } + + /* If we need to skip frames at the beginning of the stream disable the + * video port to avoid sending the skipped frames to the CCDC. + */ + destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_CCDC); + is_3630 = csi2->isp->revision == ISP_REVISION_15_0; + + return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress][is_3630]; +} + +/* + * csi2_set_outaddr - Set memory address to save output image + * @csi2: Pointer to ISP CSI2a device. + * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. + * + * Sets the memory address where the output will be saved. + * + * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte + * boundary. + */ +static void csi2_set_outaddr(struct isp_csi2_device *csi2, u32 addr) +{ + struct isp_device *isp = csi2->isp; + struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[0]; + + ctx->ping_addr = addr; + ctx->pong_addr = addr; + isp_reg_writel(isp, ctx->ping_addr, + csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); + isp_reg_writel(isp, ctx->pong_addr, + csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); +} + +/* + * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should + * be enabled by CSI2. + * @format_id: mapped format id + * + */ +static inline int is_usr_def_mapping(u32 format_id) +{ + return (format_id & 0x40) ? 1 : 0; +} + +/* + * csi2_ctx_enable - Enable specified CSI2 context + * @ctxnum: Context number, valid between 0 and 7 values. + * @enable: enable + * + */ +static void csi2_ctx_enable(struct isp_device *isp, + struct isp_csi2_device *csi2, u8 ctxnum, u8 enable) +{ + struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum]; + unsigned int skip = 0; + u32 reg; + + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); + + if (enable) { + if (csi2->frame_skip) + skip = csi2->frame_skip; + else if (csi2->output & CSI2_OUTPUT_MEMORY) + skip = 1; + + reg &= ~ISPCSI2_CTX_CTRL1_COUNT_MASK; + reg |= ISPCSI2_CTX_CTRL1_COUNT_UNLOCK + | (skip << ISPCSI2_CTX_CTRL1_COUNT_SHIFT) + | ISPCSI2_CTX_CTRL1_CTX_EN; + } else { + reg &= ~ISPCSI2_CTX_CTRL1_CTX_EN; + } + + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); + ctx->enabled = enable; +} + +/* + * csi2_ctx_config - CSI2 context configuration. + * @ctx: context configuration + * + */ +static void csi2_ctx_config(struct isp_device *isp, + struct isp_csi2_device *csi2, + struct isp_csi2_ctx_cfg *ctx) +{ + u32 reg; + + /* Set up CSI2_CTx_CTRL1 */ + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); + + if (ctx->eof_enabled) + reg |= ISPCSI2_CTX_CTRL1_EOF_EN; + else + reg &= ~ISPCSI2_CTX_CTRL1_EOF_EN; + + if (ctx->eol_enabled) + reg |= ISPCSI2_CTX_CTRL1_EOL_EN; + else + reg &= ~ISPCSI2_CTX_CTRL1_EOL_EN; + + if (ctx->checksum_enabled) + reg |= ISPCSI2_CTX_CTRL1_CS_EN; + else + reg &= ~ISPCSI2_CTX_CTRL1_CS_EN; + + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); + + /* Set up CSI2_CTx_CTRL2 */ + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); + + reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK); + reg |= ctx->virtual_id << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; + + reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK); + reg |= ctx->format_id << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT; + + if (ctx->dpcm_decompress) { + if (ctx->dpcm_predictor) + reg |= ISPCSI2_CTX_CTRL2_DPCM_PRED; + else + reg &= ~ISPCSI2_CTX_CTRL2_DPCM_PRED; + } + + if (is_usr_def_mapping(ctx->format_id)) { + reg &= ~ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK; + reg |= 2 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT; + } + + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); + + /* Set up CSI2_CTx_CTRL3 */ + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); + reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK); + reg |= (ctx->alpha << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT); + + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); + + /* Set up CSI2_CTx_DAT_OFST */ + reg = isp_reg_readl(isp, csi2->regs1, + ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); + reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK; + reg |= ctx->data_offset << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT; + isp_reg_writel(isp, reg, csi2->regs1, + ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); + + isp_reg_writel(isp, ctx->ping_addr, + csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); + + isp_reg_writel(isp, ctx->pong_addr, + csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); +} + +/* + * csi2_timing_config - CSI2 timing configuration. + * @timing: csi2_timing_cfg structure + */ +static void csi2_timing_config(struct isp_device *isp, + struct isp_csi2_device *csi2, + struct isp_csi2_timing_cfg *timing) +{ + u32 reg; + + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_TIMING); + + if (timing->force_rx_mode) + reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); + else + reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); + + if (timing->stop_state_16x) + reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); + else + reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); + + if (timing->stop_state_4x) + reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); + else + reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); + + reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(timing->ionum); + reg |= timing->stop_state_counter << + ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(timing->ionum); + + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_TIMING); +} + +/* + * csi2_irq_ctx_set - Enables CSI2 Context IRQs. + * @enable: Enable/disable CSI2 Context interrupts + */ +static void csi2_irq_ctx_set(struct isp_device *isp, + struct isp_csi2_device *csi2, int enable) +{ + u32 reg = ISPCSI2_CTX_IRQSTATUS_FE_IRQ; + int i; + + if (csi2->use_fs_irq) + reg |= ISPCSI2_CTX_IRQSTATUS_FS_IRQ; + + for (i = 0; i < 8; i++) { + isp_reg_writel(isp, reg, csi2->regs1, + ISPCSI2_CTX_IRQSTATUS(i)); + if (enable) + isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), + reg); + else + isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), + reg); + } +} + +/* + * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs. + * @enable: Enable/disable CSI2 ComplexIO #1 interrupts + */ +static void csi2_irq_complexio1_set(struct isp_device *isp, + struct isp_csi2_device *csi2, int enable) +{ + u32 reg; + reg = ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT | + ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER | + ISPCSI2_PHY_IRQENABLE_STATEULPM5 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 | + ISPCSI2_PHY_IRQENABLE_ERRESC5 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 | + ISPCSI2_PHY_IRQENABLE_STATEULPM4 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 | + ISPCSI2_PHY_IRQENABLE_ERRESC4 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 | + ISPCSI2_PHY_IRQENABLE_STATEULPM3 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 | + ISPCSI2_PHY_IRQENABLE_ERRESC3 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 | + ISPCSI2_PHY_IRQENABLE_STATEULPM2 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 | + ISPCSI2_PHY_IRQENABLE_ERRESC2 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 | + ISPCSI2_PHY_IRQENABLE_STATEULPM1 | + ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 | + ISPCSI2_PHY_IRQENABLE_ERRESC1 | + ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 | + ISPCSI2_PHY_IRQENABLE_ERRSOTHS1; + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQSTATUS); + if (enable) + reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_PHY_IRQENABLE); + else + reg = 0; + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQENABLE); +} + +/* + * csi2_irq_status_set - Enables CSI2 Status IRQs. + * @enable: Enable/disable CSI2 Status interrupts + */ +static void csi2_irq_status_set(struct isp_device *isp, + struct isp_csi2_device *csi2, int enable) +{ + u32 reg; + reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | + ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | + ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ | + ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | + ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | + ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ | + ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ | + ISPCSI2_IRQSTATUS_CONTEXT(0); + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQSTATUS); + if (enable) + reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQENABLE); + else + reg = 0; + + isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQENABLE); +} + +/* + * omap3isp_csi2_reset - Resets the CSI2 module. + * + * Must be called with the phy lock held. + * + * Returns 0 if successful, or -EBUSY if power command didn't respond. + */ +int omap3isp_csi2_reset(struct isp_csi2_device *csi2) +{ + struct isp_device *isp = csi2->isp; + u8 soft_reset_retries = 0; + u32 reg; + int i; + + if (!csi2->available) + return -ENODEV; + + if (csi2->phy->phy_in_use) + return -EBUSY; + + isp_reg_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, + ISPCSI2_SYSCONFIG_SOFT_RESET); + + do { + reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_SYSSTATUS) & + ISPCSI2_SYSSTATUS_RESET_DONE; + if (reg == ISPCSI2_SYSSTATUS_RESET_DONE) + break; + soft_reset_retries++; + if (soft_reset_retries < 5) + udelay(100); + } while (soft_reset_retries < 5); + + if (soft_reset_retries == 5) { + printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n"); + return -EBUSY; + } + + if (isp->revision == ISP_REVISION_15_0) + isp_reg_set(isp, csi2->regs1, ISPCSI2_PHY_CFG, + ISPCSI2_PHY_CFG_RESET_CTRL); + + i = 100; + do { + reg = isp_reg_readl(isp, csi2->phy->phy_regs, ISPCSIPHY_REG1) + & ISPCSIPHY_REG1_RESET_DONE_CTRLCLK; + if (reg == ISPCSIPHY_REG1_RESET_DONE_CTRLCLK) + break; + udelay(100); + } while (--i > 0); + + if (i == 0) { + printk(KERN_ERR + "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); + return -EBUSY; + } + + if (isp->autoidle) + isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, + ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | + ISPCSI2_SYSCONFIG_AUTO_IDLE, + ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART | + ((isp->revision == ISP_REVISION_15_0) ? + ISPCSI2_SYSCONFIG_AUTO_IDLE : 0)); + else + isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, + ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | + ISPCSI2_SYSCONFIG_AUTO_IDLE, + ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO); + + return 0; +} + +static int csi2_configure(struct isp_csi2_device *csi2) +{ + const struct isp_v4l2_subdevs_group *pdata; + struct isp_device *isp = csi2->isp; + struct isp_csi2_timing_cfg *timing = &csi2->timing[0]; + struct v4l2_subdev *sensor; + struct media_pad *pad; + + /* + * CSI2 fields that can be updated while the context has + * been enabled or the interface has been enabled are not + * updated dynamically currently. So we do not allow to + * reconfigure if either has been enabled + */ + if (csi2->contexts[0].enabled || csi2->ctrl.if_enable) + return -EBUSY; + + pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]); + sensor = media_entity_to_v4l2_subdev(pad->entity); + pdata = sensor->host_priv; + + csi2->frame_skip = 0; + v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); + + csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div; + csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE; + csi2->ctrl.ecc_enable = pdata->bus.csi2.crc; + + timing->ionum = 1; + timing->force_rx_mode = 1; + timing->stop_state_16x = 1; + timing->stop_state_4x = 1; + timing->stop_state_counter = 0x1FF; + + /* + * The CSI2 receiver can't do any format conversion except DPCM + * decompression, so every set_format call configures both pads + * and enables DPCM decompression as a special case: + */ + if (csi2->formats[CSI2_PAD_SINK].code != + csi2->formats[CSI2_PAD_SOURCE].code) + csi2->dpcm_decompress = true; + else + csi2->dpcm_decompress = false; + + csi2->contexts[0].format_id = csi2_ctx_map_format(csi2); + + if (csi2->video_out.bpl_padding == 0) + csi2->contexts[0].data_offset = 0; + else + csi2->contexts[0].data_offset = csi2->video_out.bpl_value; + + /* + * Enable end of frame and end of line signals generation for + * context 0. These signals are generated from CSI2 receiver to + * qualify the last pixel of a frame and the last pixel of a line. + * Without enabling the signals CSI2 receiver writes data to memory + * beyond buffer size and/or data line offset is not handled correctly. + */ + csi2->contexts[0].eof_enabled = 1; + csi2->contexts[0].eol_enabled = 1; + + csi2_irq_complexio1_set(isp, csi2, 1); + csi2_irq_ctx_set(isp, csi2, 1); + csi2_irq_status_set(isp, csi2, 1); + + /* Set configuration (timings, format and links) */ + csi2_timing_config(isp, csi2, timing); + csi2_recv_config(isp, csi2, &csi2->ctrl); + csi2_ctx_config(isp, csi2, &csi2->contexts[0]); + + return 0; +} + +/* + * csi2_print_status - Prints CSI2 debug information. + */ +#define CSI2_PRINT_REGISTER(isp, regs, name)\ + dev_dbg(isp->dev, "###CSI2 " #name "=0x%08x\n", \ + isp_reg_readl(isp, regs, ISPCSI2_##name)) + +static void csi2_print_status(struct isp_csi2_device *csi2) +{ + struct isp_device *isp = csi2->isp; + + if (!csi2->available) + return; + + dev_dbg(isp->dev, "-------------CSI2 Register dump-------------\n"); + + CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSCONFIG); + CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSSTATUS); + CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQENABLE); + CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQSTATUS); + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTRL); + CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_H); + CSI2_PRINT_REGISTER(isp, csi2->regs1, GNQ); + CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_CFG); + CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQSTATUS); + CSI2_PRINT_REGISTER(isp, csi2->regs1, SHORT_PACKET); + CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQENABLE); + CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_P); + CSI2_PRINT_REGISTER(isp, csi2->regs1, TIMING); + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL1(0)); + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL2(0)); + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_OFST(0)); + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PING_ADDR(0)); + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PONG_ADDR(0)); + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQENABLE(0)); + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQSTATUS(0)); + CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL3(0)); + + dev_dbg(isp->dev, "--------------------------------------------\n"); +} + +/* ----------------------------------------------------------------------------- + * Interrupt handling + */ + +/* + * csi2_isr_buffer - Does buffer handling at end-of-frame + * when writing to memory. + */ +static void csi2_isr_buffer(struct isp_csi2_device *csi2) +{ + struct isp_device *isp = csi2->isp; + struct isp_buffer *buffer; + + csi2_ctx_enable(isp, csi2, 0, 0); + + buffer = omap3isp_video_buffer_next(&csi2->video_out, 0); + + /* + * Let video queue operation restart engine if there is an underrun + * condition. + */ + if (buffer == NULL) + return; + + csi2_set_outaddr(csi2, buffer->isp_addr); + csi2_ctx_enable(isp, csi2, 0, 1); +} + +static void csi2_isr_ctx(struct isp_csi2_device *csi2, + struct isp_csi2_ctx_cfg *ctx) +{ + struct isp_device *isp = csi2->isp; + unsigned int n = ctx->ctxnum; + u32 status; + + status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); + isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); + + /* Propagate frame number */ + if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) { + struct isp_pipeline *pipe = + to_isp_pipeline(&csi2->subdev.entity); + if (pipe->do_propagation) + atomic_inc(&pipe->frame_number); + } + + if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ)) + return; + + /* Skip interrupts until we reach the frame skip count. The CSI2 will be + * automatically disabled, as the frame skip count has been programmed + * in the CSI2_CTx_CTRL1::COUNT field, so reenable it. + * + * It would have been nice to rely on the FRAME_NUMBER interrupt instead + * but it turned out that the interrupt is only generated when the CSI2 + * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased + * correctly and reaches 0 when data is forwarded to the video port only + * but no interrupt arrives). Maybe a CSI2 hardware bug. + */ + if (csi2->frame_skip) { + csi2->frame_skip--; + if (csi2->frame_skip == 0) { + ctx->format_id = csi2_ctx_map_format(csi2); + csi2_ctx_config(isp, csi2, ctx); + csi2_ctx_enable(isp, csi2, n, 1); + } + return; + } + + if (csi2->output & CSI2_OUTPUT_MEMORY) + csi2_isr_buffer(csi2); +} + +/* + * omap3isp_csi2_isr - CSI2 interrupt handling. + * + * Return -EIO on Transmission error + */ +int omap3isp_csi2_isr(struct isp_csi2_device *csi2) +{ + u32 csi2_irqstatus, cpxio1_irqstatus; + struct isp_device *isp = csi2->isp; + int retval = 0; + + if (!csi2->available) + return -ENODEV; + + csi2_irqstatus = isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQSTATUS); + isp_reg_writel(isp, csi2_irqstatus, csi2->regs1, ISPCSI2_IRQSTATUS); + + /* Failure Cases */ + if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) { + cpxio1_irqstatus = isp_reg_readl(isp, csi2->regs1, + ISPCSI2_PHY_IRQSTATUS); + isp_reg_writel(isp, cpxio1_irqstatus, + csi2->regs1, ISPCSI2_PHY_IRQSTATUS); + dev_dbg(isp->dev, "CSI2: ComplexIO Error IRQ " + "%x\n", cpxio1_irqstatus); + retval = -EIO; + } + + if (csi2_irqstatus & (ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | + ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | + ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | + ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | + ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ)) { + dev_dbg(isp->dev, "CSI2 Err:" + " OCP:%d," + " Short_pack:%d," + " ECC:%d," + " CPXIO2:%d," + " FIFO_OVF:%d," + "\n", + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ? 1 : 0, + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ? 1 : 0, + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ? 1 : 0, + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ? 1 : 0, + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ? 1 : 0); + retval = -EIO; + } + + if (omap3isp_module_sync_is_stopping(&csi2->wait, &csi2->stopping)) + return 0; + + /* Successful cases */ + if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0)) + csi2_isr_ctx(csi2, &csi2->contexts[0]); + + if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ) + dev_dbg(isp->dev, "CSI2: ECC correction done\n"); + + return retval; +} + +/* ----------------------------------------------------------------------------- + * ISP video operations + */ + +/* + * csi2_queue - Queues the first buffer when using memory output + * @video: The video node + * @buffer: buffer to queue + */ +static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer) +{ + struct isp_device *isp = video->isp; + struct isp_csi2_device *csi2 = &isp->isp_csi2a; + + csi2_set_outaddr(csi2, buffer->isp_addr); + + /* + * If streaming was enabled before there was a buffer queued + * or underrun happened in the ISR, the hardware was not enabled + * and DMA queue flag ISP_VIDEO_DMAQUEUE_UNDERRUN is still set. + * Enable it now. + */ + if (csi2->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { + /* Enable / disable context 0 and IRQs */ + csi2_if_enable(isp, csi2, 1); + csi2_ctx_enable(isp, csi2, 0, 1); + isp_video_dmaqueue_flags_clr(&csi2->video_out); + } + + return 0; +} + +static const struct isp_video_operations csi2_ispvideo_ops = { + .queue = csi2_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +static struct v4l2_mbus_framefmt * +__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + else + return &csi2->formats[pad]; +} + +static void +csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + enum v4l2_mbus_pixelcode pixelcode; + struct v4l2_mbus_framefmt *format; + const struct isp_format_info *info; + unsigned int i; + + switch (pad) { + case CSI2_PAD_SINK: + /* Clamp the width and height to valid range (1-8191). */ + for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) { + if (fmt->code == csi2_input_fmts[i]) + break; + } + + /* If not found, use SGRBG10 as default */ + if (i >= ARRAY_SIZE(csi2_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + fmt->width = clamp_t(u32, fmt->width, 1, 8191); + fmt->height = clamp_t(u32, fmt->height, 1, 8191); + break; + + case CSI2_PAD_SOURCE: + /* Source format same as sink format, except for DPCM + * compression. + */ + pixelcode = fmt->code; + format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + + /* + * Only Allow DPCM decompression, and check that the + * pattern is preserved + */ + info = omap3isp_video_format_info(fmt->code); + if (info->uncompressed == pixelcode) + fmt->code = pixelcode; + break; + } + + /* RGB, non-interlaced */ + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->field = V4L2_FIELD_NONE; +} + +/* + * csi2_enum_mbus_code - Handle pixel format enumeration + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @code : pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int csi2_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + const struct isp_format_info *info; + + if (code->pad == CSI2_PAD_SINK) { + if (code->index >= ARRAY_SIZE(csi2_input_fmts)) + return -EINVAL; + + code->code = csi2_input_fmts[code->index]; + } else { + format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, + V4L2_SUBDEV_FORMAT_TRY); + switch (code->index) { + case 0: + /* Passthrough sink pad code */ + code->code = format->code; + break; + case 1: + /* Uncompressed code */ + info = omap3isp_video_format_info(format->code); + if (info->uncompressed == format->code) + return -EINVAL; + + code->code = info->uncompressed; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static int csi2_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * csi2_get_format - Handle get format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on sucess + */ +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +/* + * csi2_set_format - Handle set format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == CSI2_PAD_SINK) { + format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE, + fmt->which); + *format = fmt->format; + csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which); + } + + return 0; +} + +/* + * csi2_init_formats - Initialize formats on all pads + * @sd: ISP CSI2 V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = CSI2_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + format.format.width = 4096; + format.format.height = 4096; + csi2_set_format(sd, fh, &format); + + return 0; +} + +/* + * csi2_set_stream - Enable/Disable streaming on the CSI2 module + * @sd: ISP CSI2 V4L2 subdevice + * @enable: ISP pipeline stream state + * + * Return 0 on success or a negative error code otherwise. + */ +static int csi2_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct isp_device *isp = csi2->isp; + struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); + struct isp_video *video_out = &csi2->video_out; + + switch (enable) { + case ISP_PIPELINE_STREAM_CONTINUOUS: + if (omap3isp_csiphy_acquire(csi2->phy) < 0) + return -ENODEV; + csi2->use_fs_irq = pipe->do_propagation; + if (csi2->output & CSI2_OUTPUT_MEMORY) + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); + csi2_configure(csi2); + csi2_print_status(csi2); + + /* + * When outputting to memory with no buffer available, let the + * buffer queue handler start the hardware. A DMA queue flag + * ISP_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is + * a buffer available. + */ + if (csi2->output & CSI2_OUTPUT_MEMORY && + !(video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED)) + break; + /* Enable context 0 and IRQs */ + atomic_set(&csi2->stopping, 0); + csi2_ctx_enable(isp, csi2, 0, 1); + csi2_if_enable(isp, csi2, 1); + isp_video_dmaqueue_flags_clr(video_out); + break; + + case ISP_PIPELINE_STREAM_STOPPED: + if (csi2->state == ISP_PIPELINE_STREAM_STOPPED) + return 0; + if (omap3isp_module_sync_idle(&sd->entity, &csi2->wait, + &csi2->stopping)) + dev_dbg(isp->dev, "%s: module stop timeout.\n", + sd->name); + csi2_ctx_enable(isp, csi2, 0, 0); + csi2_if_enable(isp, csi2, 0); + csi2_irq_ctx_set(isp, csi2, 0); + omap3isp_csiphy_release(csi2->phy); + isp_video_dmaqueue_flags_clr(video_out); + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); + break; + } + + csi2->state = enable; + return 0; +} + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops csi2_video_ops = { + .s_stream = csi2_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops csi2_pad_ops = { + .enum_mbus_code = csi2_enum_mbus_code, + .enum_frame_size = csi2_enum_frame_size, + .get_fmt = csi2_get_format, + .set_fmt = csi2_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops csi2_ops = { + .video = &csi2_video_ops, + .pad = &csi2_pad_ops, +}; + +/* subdev internal operations */ +static const struct v4l2_subdev_internal_ops csi2_internal_ops = { + .open = csi2_init_formats, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +/* + * csi2_link_setup - Setup CSI2 connections. + * @entity : Pointer to media entity structure + * @local : Pointer to local pad array + * @remote : Pointer to remote pad array + * @flags : Link flags + * return -EINVAL or zero on success + */ +static int csi2_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); + struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl; + + /* + * The ISP core doesn't support pipelines with multiple video outputs. + * Revisit this when it will be implemented, and return -EBUSY for now. + */ + + switch (local->index | media_entity_type(remote->entity)) { + case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (flags & MEDIA_LNK_FL_ENABLED) { + if (csi2->output & ~CSI2_OUTPUT_MEMORY) + return -EBUSY; + csi2->output |= CSI2_OUTPUT_MEMORY; + } else { + csi2->output &= ~CSI2_OUTPUT_MEMORY; + } + break; + + case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (flags & MEDIA_LNK_FL_ENABLED) { + if (csi2->output & ~CSI2_OUTPUT_CCDC) + return -EBUSY; + csi2->output |= CSI2_OUTPUT_CCDC; + } else { + csi2->output &= ~CSI2_OUTPUT_CCDC; + } + break; + + default: + /* Link from camera to CSI2 is fixed... */ + return -EINVAL; + } + + ctrl->vp_only_enable = + (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true; + ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_CCDC); + + return 0; +} + +/* media operations */ +static const struct media_entity_operations csi2_media_ops = { + .link_setup = csi2_link_setup, +}; + +/* + * csi2_init_entities - Initialize subdev and media entity. + * @csi2: Pointer to csi2 structure. + * return -ENOMEM or zero on success + */ +static int csi2_init_entities(struct isp_csi2_device *csi2) +{ + struct v4l2_subdev *sd = &csi2->subdev; + struct media_pad *pads = csi2->pads; + struct media_entity *me = &sd->entity; + int ret; + + v4l2_subdev_init(sd, &csi2_ops); + sd->internal_ops = &csi2_internal_ops; + strlcpy(sd->name, "OMAP3 ISP CSI2a", sizeof(sd->name)); + + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ + v4l2_set_subdevdata(sd, csi2); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + me->ops = &csi2_media_ops; + ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); + if (ret < 0) + return ret; + + csi2_init_formats(sd, NULL); + + /* Video device node */ + csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + csi2->video_out.ops = &csi2_ispvideo_ops; + csi2->video_out.bpl_alignment = 32; + csi2->video_out.bpl_zero_padding = 1; + csi2->video_out.bpl_max = 0x1ffe0; + csi2->video_out.isp = csi2->isp; + csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; + + ret = omap3isp_video_init(&csi2->video_out, "CSI2a"); + if (ret < 0) + return ret; + + /* Connect the CSI2 subdev to the video node. */ + ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, + &csi2->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + return 0; +} + +void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) +{ + media_entity_cleanup(&csi2->subdev.entity); + + v4l2_device_unregister_subdev(&csi2->subdev); + omap3isp_video_unregister(&csi2->video_out); +} + +int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video nodes. */ + ret = v4l2_device_register_subdev(vdev, &csi2->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&csi2->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_csi2_unregister_entities(csi2); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP CSI2 initialisation and cleanup + */ + +/* + * omap3isp_csi2_cleanup - Routine for module driver cleanup + */ +void omap3isp_csi2_cleanup(struct isp_device *isp) +{ +} + +/* + * omap3isp_csi2_init - Routine for module driver init + */ +int omap3isp_csi2_init(struct isp_device *isp) +{ + struct isp_csi2_device *csi2a = &isp->isp_csi2a; + struct isp_csi2_device *csi2c = &isp->isp_csi2c; + int ret; + + csi2a->isp = isp; + csi2a->available = 1; + csi2a->regs1 = OMAP3_ISP_IOMEM_CSI2A_REGS1; + csi2a->regs2 = OMAP3_ISP_IOMEM_CSI2A_REGS2; + csi2a->phy = &isp->isp_csiphy2; + csi2a->state = ISP_PIPELINE_STREAM_STOPPED; + init_waitqueue_head(&csi2a->wait); + + ret = csi2_init_entities(csi2a); + if (ret < 0) + goto fail; + + if (isp->revision == ISP_REVISION_15_0) { + csi2c->isp = isp; + csi2c->available = 1; + csi2c->regs1 = OMAP3_ISP_IOMEM_CSI2C_REGS1; + csi2c->regs2 = OMAP3_ISP_IOMEM_CSI2C_REGS2; + csi2c->phy = &isp->isp_csiphy1; + csi2c->state = ISP_PIPELINE_STREAM_STOPPED; + init_waitqueue_head(&csi2c->wait); + } + + return 0; +fail: + omap3isp_csi2_cleanup(isp); + return ret; +} diff --git a/drivers/media/video/omap3isp/ispcsi2.h b/drivers/media/video/omap3isp/ispcsi2.h new file mode 100644 index 0000000..456fb7f --- /dev/null +++ b/drivers/media/video/omap3isp/ispcsi2.h @@ -0,0 +1,166 @@ +/* + * ispcsi2.h + * + * TI OMAP3 ISP - CSI2 module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_CSI2_H +#define OMAP3_ISP_CSI2_H + +#include +#include + +struct isp_csiphy; + +/* This is not an exhaustive list */ +enum isp_csi2_pix_formats { + CSI2_PIX_FMT_OTHERS = 0, + CSI2_PIX_FMT_YUV422_8BIT = 0x1e, + CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e, + CSI2_PIX_FMT_RAW10_EXP16 = 0xab, + CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f, + CSI2_PIX_FMT_RAW8 = 0x2a, + CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa, + CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a, + CSI2_PIX_FMT_RAW8_VP = 0x12a, + CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340, + CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0, + CSI2_USERDEF_8BIT_DATA1 = 0x40, +}; + +enum isp_csi2_irqevents { + OCP_ERR_IRQ = 0x4000, + SHORT_PACKET_IRQ = 0x2000, + ECC_CORRECTION_IRQ = 0x1000, + ECC_NO_CORRECTION_IRQ = 0x800, + COMPLEXIO2_ERR_IRQ = 0x400, + COMPLEXIO1_ERR_IRQ = 0x200, + FIFO_OVF_IRQ = 0x100, + CONTEXT7 = 0x80, + CONTEXT6 = 0x40, + CONTEXT5 = 0x20, + CONTEXT4 = 0x10, + CONTEXT3 = 0x8, + CONTEXT2 = 0x4, + CONTEXT1 = 0x2, + CONTEXT0 = 0x1, +}; + +enum isp_csi2_ctx_irqevents { + CTX_ECC_CORRECTION = 0x100, + CTX_LINE_NUMBER = 0x80, + CTX_FRAME_NUMBER = 0x40, + CTX_CS = 0x20, + CTX_LE = 0x8, + CTX_LS = 0x4, + CTX_FE = 0x2, + CTX_FS = 0x1, +}; + +enum isp_csi2_frame_mode { + ISP_CSI2_FRAME_IMMEDIATE, + ISP_CSI2_FRAME_AFTERFEC, +}; + +#define ISP_CSI2_MAX_CTX_NUM 7 + +struct isp_csi2_ctx_cfg { + u8 ctxnum; /* context number 0 - 7 */ + u8 dpcm_decompress; + + /* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */ + u8 virtual_id; + u16 format_id; /* as in CSI2_CTx_CTRL2[9:0] */ + u8 dpcm_predictor; /* 1: simple, 0: advanced */ + + /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */ + u16 alpha; + u16 data_offset; + u32 ping_addr; + u32 pong_addr; + u8 eof_enabled; + u8 eol_enabled; + u8 checksum_enabled; + u8 enabled; +}; + +struct isp_csi2_timing_cfg { + u8 ionum; /* IO1 or IO2 as in CSI2_TIMING */ + unsigned force_rx_mode:1; + unsigned stop_state_16x:1; + unsigned stop_state_4x:1; + u16 stop_state_counter; +}; + +struct isp_csi2_ctrl_cfg { + bool vp_clk_enable; + bool vp_only_enable; + u8 vp_out_ctrl; + enum isp_csi2_frame_mode frame_mode; + bool ecc_enable; + bool if_enable; +}; + +#define CSI2_PAD_SINK 0 +#define CSI2_PAD_SOURCE 1 +#define CSI2_PADS_NUM 2 + +#define CSI2_OUTPUT_CCDC (1 << 0) +#define CSI2_OUTPUT_MEMORY (1 << 1) + +struct isp_csi2_device { + struct v4l2_subdev subdev; + struct media_pad pads[CSI2_PADS_NUM]; + struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM]; + + struct isp_video video_out; + struct isp_device *isp; + + u8 available; /* Is the IP present on the silicon? */ + + /* mem resources - enums as defined in enum isp_mem_resources */ + u8 regs1; + u8 regs2; + + u32 output; /* output to CCDC, memory or both? */ + bool dpcm_decompress; + unsigned int frame_skip; + bool use_fs_irq; + + struct isp_csiphy *phy; + struct isp_csi2_ctx_cfg contexts[ISP_CSI2_MAX_CTX_NUM + 1]; + struct isp_csi2_timing_cfg timing[2]; + struct isp_csi2_ctrl_cfg ctrl; + enum isp_pipeline_stream_state state; + wait_queue_head_t wait; + atomic_t stopping; +}; + +int omap3isp_csi2_isr(struct isp_csi2_device *csi2); +int omap3isp_csi2_reset(struct isp_csi2_device *csi2); +int omap3isp_csi2_init(struct isp_device *isp); +void omap3isp_csi2_cleanup(struct isp_device *isp); +void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2); +int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, + struct v4l2_device *vdev); +#endif /* OMAP3_ISP_CSI2_H */ diff --git a/drivers/media/video/omap3isp/ispcsiphy.c b/drivers/media/video/omap3isp/ispcsiphy.c new file mode 100644 index 0000000..5be37ce --- /dev/null +++ b/drivers/media/video/omap3isp/ispcsiphy.c @@ -0,0 +1,247 @@ +/* + * ispcsiphy.c + * + * TI OMAP3 ISP - CSI PHY module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispcsiphy.h" + +/* + * csiphy_lanes_config - Configuration of CSIPHY lanes. + * + * Updates HW configuration. + * Called with phy->mutex taken. + */ +static void csiphy_lanes_config(struct isp_csiphy *phy) +{ + unsigned int i; + u32 reg; + + reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG); + + for (i = 0; i < phy->num_data_lanes; i++) { + reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) | + ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1)); + reg |= (phy->lanes.data[i].pol << + ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1)); + reg |= (phy->lanes.data[i].pos << + ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1)); + } + + reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK | + ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK); + reg |= phy->lanes.clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT; + reg |= phy->lanes.clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT; + + isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); +} + +/* + * csiphy_power_autoswitch_enable + * @enable: Sets or clears the autoswitch function enable flag. + */ +static void csiphy_power_autoswitch_enable(struct isp_csiphy *phy, bool enable) +{ + isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, + ISPCSI2_PHY_CFG_PWR_AUTO, + enable ? ISPCSI2_PHY_CFG_PWR_AUTO : 0); +} + +/* + * csiphy_set_power + * @power: Power state to be set. + * + * Returns 0 if successful, or -EBUSY if the retry count is exceeded. + */ +static int csiphy_set_power(struct isp_csiphy *phy, u32 power) +{ + u32 reg; + u8 retry_count; + + isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, + ISPCSI2_PHY_CFG_PWR_CMD_MASK, power); + + retry_count = 0; + do { + udelay(50); + reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG) & + ISPCSI2_PHY_CFG_PWR_STATUS_MASK; + + if (reg != power >> 2) + retry_count++; + + } while ((reg != power >> 2) && (retry_count < 100)); + + if (retry_count == 100) { + printk(KERN_ERR "CSI2 CIO set power failed!\n"); + return -EBUSY; + } + + return 0; +} + +/* + * csiphy_dphy_config - Configure CSI2 D-PHY parameters. + * + * Called with phy->mutex taken. + */ +static void csiphy_dphy_config(struct isp_csiphy *phy) +{ + u32 reg; + + /* Set up ISPCSIPHY_REG0 */ + reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0); + + reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK | + ISPCSIPHY_REG0_THS_SETTLE_MASK); + reg |= phy->dphy.ths_term << ISPCSIPHY_REG0_THS_TERM_SHIFT; + reg |= phy->dphy.ths_settle << ISPCSIPHY_REG0_THS_SETTLE_SHIFT; + + isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0); + + /* Set up ISPCSIPHY_REG1 */ + reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1); + + reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK | + ISPCSIPHY_REG1_TCLK_MISS_MASK | + ISPCSIPHY_REG1_TCLK_SETTLE_MASK); + reg |= phy->dphy.tclk_term << ISPCSIPHY_REG1_TCLK_TERM_SHIFT; + reg |= phy->dphy.tclk_miss << ISPCSIPHY_REG1_TCLK_MISS_SHIFT; + reg |= phy->dphy.tclk_settle << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT; + + isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); +} + +static int csiphy_config(struct isp_csiphy *phy, + struct isp_csiphy_dphy_cfg *dphy, + struct isp_csiphy_lanes_cfg *lanes) +{ + unsigned int used_lanes = 0; + unsigned int i; + + /* Clock and data lanes verification */ + for (i = 0; i < phy->num_data_lanes; i++) { + if (lanes->data[i].pol > 1 || lanes->data[i].pos > 3) + return -EINVAL; + + if (used_lanes & (1 << lanes->data[i].pos)) + return -EINVAL; + + used_lanes |= 1 << lanes->data[i].pos; + } + + if (lanes->clk.pol > 1 || lanes->clk.pos > 3) + return -EINVAL; + + if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) + return -EINVAL; + + mutex_lock(&phy->mutex); + phy->dphy = *dphy; + phy->lanes = *lanes; + mutex_unlock(&phy->mutex); + + return 0; +} + +int omap3isp_csiphy_acquire(struct isp_csiphy *phy) +{ + int rval; + + if (phy->vdd == NULL) { + dev_err(phy->isp->dev, "Power regulator for CSI PHY not " + "available\n"); + return -ENODEV; + } + + mutex_lock(&phy->mutex); + + rval = regulator_enable(phy->vdd); + if (rval < 0) + goto done; + + omap3isp_csi2_reset(phy->csi2); + + csiphy_dphy_config(phy); + csiphy_lanes_config(phy); + + rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON); + if (rval) { + regulator_disable(phy->vdd); + goto done; + } + + csiphy_power_autoswitch_enable(phy, true); + phy->phy_in_use = 1; + +done: + mutex_unlock(&phy->mutex); + return rval; +} + +void omap3isp_csiphy_release(struct isp_csiphy *phy) +{ + mutex_lock(&phy->mutex); + if (phy->phy_in_use) { + csiphy_power_autoswitch_enable(phy, false); + csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); + regulator_disable(phy->vdd); + phy->phy_in_use = 0; + } + mutex_unlock(&phy->mutex); +} + +/* + * omap3isp_csiphy_init - Initialize the CSI PHY frontends + */ +int omap3isp_csiphy_init(struct isp_device *isp) +{ + struct isp_csiphy *phy1 = &isp->isp_csiphy1; + struct isp_csiphy *phy2 = &isp->isp_csiphy2; + + isp->platform_cb.csiphy_config = csiphy_config; + + phy2->isp = isp; + phy2->csi2 = &isp->isp_csi2a; + phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES; + phy2->cfg_regs = OMAP3_ISP_IOMEM_CSI2A_REGS1; + phy2->phy_regs = OMAP3_ISP_IOMEM_CSIPHY2; + mutex_init(&phy2->mutex); + + if (isp->revision == ISP_REVISION_15_0) { + phy1->isp = isp; + phy1->csi2 = &isp->isp_csi2c; + phy1->num_data_lanes = ISP_CSIPHY1_NUM_DATA_LANES; + phy1->cfg_regs = OMAP3_ISP_IOMEM_CSI2C_REGS1; + phy1->phy_regs = OMAP3_ISP_IOMEM_CSIPHY1; + mutex_init(&phy1->mutex); + } + + return 0; +} diff --git a/drivers/media/video/omap3isp/ispcsiphy.h b/drivers/media/video/omap3isp/ispcsiphy.h new file mode 100644 index 0000000..9596dc6 --- /dev/null +++ b/drivers/media/video/omap3isp/ispcsiphy.h @@ -0,0 +1,74 @@ +/* + * ispcsiphy.h + * + * TI OMAP3 ISP - CSI PHY module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_CSI_PHY_H +#define OMAP3_ISP_CSI_PHY_H + +struct isp_csi2_device; +struct regulator; + +struct csiphy_lane { + u8 pos; + u8 pol; +}; + +#define ISP_CSIPHY2_NUM_DATA_LANES 2 +#define ISP_CSIPHY1_NUM_DATA_LANES 1 + +struct isp_csiphy_lanes_cfg { + struct csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; + struct csiphy_lane clk; +}; + +struct isp_csiphy_dphy_cfg { + u8 ths_term; + u8 ths_settle; + u8 tclk_term; + unsigned tclk_miss:1; + u8 tclk_settle; +}; + +struct isp_csiphy { + struct isp_device *isp; + struct mutex mutex; /* serialize csiphy configuration */ + u8 phy_in_use; + struct isp_csi2_device *csi2; + struct regulator *vdd; + + /* mem resources - enums as defined in enum isp_mem_resources */ + unsigned int cfg_regs; + unsigned int phy_regs; + + u8 num_data_lanes; /* number of CSI2 Data Lanes supported */ + struct isp_csiphy_lanes_cfg lanes; + struct isp_csiphy_dphy_cfg dphy; +}; + +int omap3isp_csiphy_acquire(struct isp_csiphy *phy); +void omap3isp_csiphy_release(struct isp_csiphy *phy); +int omap3isp_csiphy_init(struct isp_device *isp); + +#endif /* OMAP3_ISP_CSI_PHY_H */ -- cgit v0.10.2 From de1135d44f4f25bee5a22d92e7cf2de64ded0dca Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 12 Feb 2011 18:05:06 -0300 Subject: [media] omap3isp: CCDC, preview engine and resizer The OMAP3 ISP CCDC, preview engine and resizer entities perform image processing and scaling. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: David Cohen Signed-off-by: Stanimir Varbanov Signed-off-by: Vimarsh Zutshi Signed-off-by: Tuukka Toivonen Signed-off-by: Sergio Aguirre Signed-off-by: Antti Koskipaa Signed-off-by: Ivan T. Ivanov Signed-off-by: RaniSuneela Signed-off-by: Atanas Filipov Signed-off-by: Gjorgji Rosikopulos Signed-off-by: Hiroshi DOYU Signed-off-by: Nayden Kanchev Signed-off-by: Phil Carmody Signed-off-by: Artem Bityutskiy Signed-off-by: Dominic Curran Signed-off-by: Ilkka Myllyperkio Signed-off-by: Pallavi Kulkarni Signed-off-by: Vaibhav Hiremath Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap3isp/cfa_coef_table.h b/drivers/media/video/omap3isp/cfa_coef_table.h new file mode 100644 index 0000000..c60df0e --- /dev/null +++ b/drivers/media/video/omap3isp/cfa_coef_table.h @@ -0,0 +1,61 @@ +/* + * cfa_coef_table.h + * + * TI OMAP3 ISP - CFA coefficients table + * + * Copyright (C) 2009-2010 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244, +248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250, +247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248, +244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244, +248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250, +247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248, +244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244, +248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250, +247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248, + 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4, + 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0, + 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0, + 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4, + 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0, + 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0, + 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4, + 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0, + 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0, + 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0, + 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12, + 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0, + 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0, + 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12, + 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0, + 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0, + 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12, + 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0, +244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244, +248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247, +250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248, +244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244, +248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247, +250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248, +244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244, +248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247, +250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248 diff --git a/drivers/media/video/omap3isp/gamma_table.h b/drivers/media/video/omap3isp/gamma_table.h new file mode 100644 index 0000000..78deebf --- /dev/null +++ b/drivers/media/video/omap3isp/gamma_table.h @@ -0,0 +1,90 @@ +/* + * gamma_table.h + * + * TI OMAP3 ISP - Default gamma table for all components + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + + 0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, + 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 36, 37, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 63, 64, 65, 66, 66, 67, 68, 69, 69, 70, + 71, 72, 72, 73, 74, 75, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, + 83, 84, 84, 85, 86, 87, 88, 88, 89, 90, 91, 91, 92, 93, 94, 94, + 95, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 103, 104, 104, +105, 106, 107, 108, 108, 109, 110, 111, 111, 112, 113, 114, 114, 115, 116, 117, +117, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, +126, 126, 127, 127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, +134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, +142, 142, 143, 143, 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, +150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 154, 154, 154, 154, 155, 155, +156, 156, 157, 157, 158, 158, 158, 159, 159, 159, 160, 160, 160, 161, 161, 162, +162, 163, 163, 164, 164, 164, 164, 165, 165, 165, 165, 166, 166, 167, 167, 168, +168, 169, 169, 170, 170, 170, 170, 171, 171, 171, 171, 172, 172, 173, 173, 174, +174, 175, 175, 176, 176, 176, 176, 177, 177, 177, 177, 178, 178, 178, 178, 179, +179, 179, 179, 180, 180, 180, 180, 181, 181, 181, 181, 182, 182, 182, 182, 183, +183, 183, 183, 184, 184, 184, 184, 185, 185, 185, 185, 186, 186, 186, 186, 187, +187, 187, 187, 188, 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, +191, 191, 191, 192, 192, 192, 192, 193, 193, 193, 193, 194, 194, 194, 194, 195, +195, 195, 195, 196, 196, 196, 196, 197, 197, 197, 197, 198, 198, 198, 198, 199, +199, 199, 199, 200, 200, 200, 200, 201, 201, 201, 201, 202, 202, 202, 203, 203, +203, 203, 204, 204, 204, 204, 205, 205, 205, 205, 206, 206, 206, 206, 207, 207, +207, 207, 208, 208, 208, 208, 209, 209, 209, 209, 210, 210, 210, 210, 210, 210, +210, 210, 210, 210, 210, 210, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, +211, 212, 212, 212, 212, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, +213, 214, 214, 214, 214, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, +216, 216, 216, 216, 217, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 219, +219, 219, 219, 219, 219, 219, 219, 219, 220, 220, 220, 220, 221, 221, 221, 221, +221, 221, 221, 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 223, +223, 223, 223, 223, 223, 223, 223, 224, 224, 224, 224, 225, 225, 225, 225, 225, +225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 226, 226, +226, 226, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 228, 228, +228, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 230, 230, 230, +230, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 232, 232, 232, +232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, +233, 233, 233, 233, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 235, +235, 235, 235, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, +236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 238, 238, 238, 238, 238, 238, +238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, +238, 238, 238, 238, 238, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 240, +240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, +240, 240, 240, 240, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, 242, +242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, +242, 242, 243, 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, +244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, +244, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, +246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, +246, 246, 246, 246, 246, 246, 246, 247, 247, 247, 247, 248, 248, 248, 248, 248, +248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, +248, 248, 248, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 250, 250, +250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +250, 250, 250, 250, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, +252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, +252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, +252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, +252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, +253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, +253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, +253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, +253, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c new file mode 100644 index 0000000..5ff9d14 --- /dev/null +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -0,0 +1,2268 @@ +/* + * ispccdc.c + * + * TI OMAP3 ISP - CCDC module + * + * Copyright (C) 2009-2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispccdc.h" + +static struct v4l2_mbus_framefmt * +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which); + +static const unsigned int ccdc_fmts[] = { + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SGBRG10_1X10, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_SRGGB12_1X12, + V4L2_MBUS_FMT_SBGGR12_1X12, + V4L2_MBUS_FMT_SGBRG12_1X12, +}; + +/* + * ccdc_print_status - Print current CCDC Module register values. + * @ccdc: Pointer to ISP CCDC device. + * + * Also prints other debug information stored in the CCDC module. + */ +#define CCDC_PRINT_REGISTER(isp, name)\ + dev_dbg(isp->dev, "###CCDC " #name "=0x%08x\n", \ + isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name)) + +static void ccdc_print_status(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + + dev_dbg(isp->dev, "-------------CCDC Register dump-------------\n"); + + CCDC_PRINT_REGISTER(isp, PCR); + CCDC_PRINT_REGISTER(isp, SYN_MODE); + CCDC_PRINT_REGISTER(isp, HD_VD_WID); + CCDC_PRINT_REGISTER(isp, PIX_LINES); + CCDC_PRINT_REGISTER(isp, HORZ_INFO); + CCDC_PRINT_REGISTER(isp, VERT_START); + CCDC_PRINT_REGISTER(isp, VERT_LINES); + CCDC_PRINT_REGISTER(isp, CULLING); + CCDC_PRINT_REGISTER(isp, HSIZE_OFF); + CCDC_PRINT_REGISTER(isp, SDOFST); + CCDC_PRINT_REGISTER(isp, SDR_ADDR); + CCDC_PRINT_REGISTER(isp, CLAMP); + CCDC_PRINT_REGISTER(isp, DCSUB); + CCDC_PRINT_REGISTER(isp, COLPTN); + CCDC_PRINT_REGISTER(isp, BLKCMP); + CCDC_PRINT_REGISTER(isp, FPC); + CCDC_PRINT_REGISTER(isp, FPC_ADDR); + CCDC_PRINT_REGISTER(isp, VDINT); + CCDC_PRINT_REGISTER(isp, ALAW); + CCDC_PRINT_REGISTER(isp, REC656IF); + CCDC_PRINT_REGISTER(isp, CFG); + CCDC_PRINT_REGISTER(isp, FMTCFG); + CCDC_PRINT_REGISTER(isp, FMT_HORZ); + CCDC_PRINT_REGISTER(isp, FMT_VERT); + CCDC_PRINT_REGISTER(isp, PRGEVEN0); + CCDC_PRINT_REGISTER(isp, PRGEVEN1); + CCDC_PRINT_REGISTER(isp, PRGODD0); + CCDC_PRINT_REGISTER(isp, PRGODD1); + CCDC_PRINT_REGISTER(isp, VP_OUT); + CCDC_PRINT_REGISTER(isp, LSC_CONFIG); + CCDC_PRINT_REGISTER(isp, LSC_INITIAL); + CCDC_PRINT_REGISTER(isp, LSC_TABLE_BASE); + CCDC_PRINT_REGISTER(isp, LSC_TABLE_OFFSET); + + dev_dbg(isp->dev, "--------------------------------------------\n"); +} + +/* + * omap3isp_ccdc_busy - Get busy state of the CCDC. + * @ccdc: Pointer to ISP CCDC device. + */ +int omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + + return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) & + ISPCCDC_PCR_BUSY; +} + +/* ----------------------------------------------------------------------------- + * Lens Shading Compensation + */ + +/* + * ccdc_lsc_validate_config - Check that LSC configuration is valid. + * @ccdc: Pointer to ISP CCDC device. + * @lsc_cfg: the LSC configuration to check. + * + * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid. + */ +static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc, + struct omap3isp_ccdc_lsc_config *lsc_cfg) +{ + struct isp_device *isp = to_isp_device(ccdc); + struct v4l2_mbus_framefmt *format; + unsigned int paxel_width, paxel_height; + unsigned int paxel_shift_x, paxel_shift_y; + unsigned int min_width, min_height, min_size; + unsigned int input_width, input_height; + + paxel_shift_x = lsc_cfg->gain_mode_m; + paxel_shift_y = lsc_cfg->gain_mode_n; + + if ((paxel_shift_x < 2) || (paxel_shift_x > 6) || + (paxel_shift_y < 2) || (paxel_shift_y > 6)) { + dev_dbg(isp->dev, "CCDC: LSC: Invalid paxel size\n"); + return -EINVAL; + } + + if (lsc_cfg->offset & 3) { + dev_dbg(isp->dev, "CCDC: LSC: Offset must be a multiple of " + "4\n"); + return -EINVAL; + } + + if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) { + dev_dbg(isp->dev, "CCDC: LSC: initial_x and y must be even\n"); + return -EINVAL; + } + + format = __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, + V4L2_SUBDEV_FORMAT_ACTIVE); + input_width = format->width; + input_height = format->height; + + /* Calculate minimum bytesize for validation */ + paxel_width = 1 << paxel_shift_x; + min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1) + >> paxel_shift_x) + 1; + + paxel_height = 1 << paxel_shift_y; + min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1) + >> paxel_shift_y) + 1; + + min_size = 4 * min_width * min_height; + if (min_size > lsc_cfg->size) { + dev_dbg(isp->dev, "CCDC: LSC: too small table\n"); + return -EINVAL; + } + if (lsc_cfg->offset < (min_width * 4)) { + dev_dbg(isp->dev, "CCDC: LSC: Offset is too small\n"); + return -EINVAL; + } + if ((lsc_cfg->size / lsc_cfg->offset) < min_height) { + dev_dbg(isp->dev, "CCDC: LSC: Wrong size/offset combination\n"); + return -EINVAL; + } + return 0; +} + +/* + * ccdc_lsc_program_table - Program Lens Shading Compensation table address. + * @ccdc: Pointer to ISP CCDC device. + */ +static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr) +{ + isp_reg_writel(to_isp_device(ccdc), addr, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE); +} + +/* + * ccdc_lsc_setup_regs - Configures the lens shading compensation module + * @ccdc: Pointer to ISP CCDC device. + */ +static void ccdc_lsc_setup_regs(struct isp_ccdc_device *ccdc, + struct omap3isp_ccdc_lsc_config *cfg) +{ + struct isp_device *isp = to_isp_device(ccdc); + int reg; + + isp_reg_writel(isp, cfg->offset, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_TABLE_OFFSET); + + reg = 0; + reg |= cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT; + reg |= cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT; + reg |= cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT; + isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG); + + reg = 0; + reg &= ~ISPCCDC_LSC_INITIAL_X_MASK; + reg |= cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT; + reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK; + reg |= cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT; + isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_INITIAL); +} + +static int ccdc_lsc_wait_prefetch(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + unsigned int wait; + + isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + + /* timeout 1 ms */ + for (wait = 0; wait < 1000; wait++) { + if (isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS) & + IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ) { + isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + return 0; + } + + rmb(); + udelay(1); + } + + return -ETIMEDOUT; +} + +/* + * __ccdc_lsc_enable - Enables/Disables the Lens Shading Compensation module. + * @ccdc: Pointer to ISP CCDC device. + * @enable: 0 Disables LSC, 1 Enables LSC. + */ +static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable) +{ + struct isp_device *isp = to_isp_device(ccdc); + const struct v4l2_mbus_framefmt *format = + __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, + V4L2_SUBDEV_FORMAT_ACTIVE); + + if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) && + (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) && + (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) && + (format->code != V4L2_MBUS_FMT_SGBRG10_1X10)) + return -EINVAL; + + if (enable) + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_LSC_READ); + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, + ISPCCDC_LSC_ENABLE, enable ? ISPCCDC_LSC_ENABLE : 0); + + if (enable) { + if (ccdc_lsc_wait_prefetch(ccdc) < 0) { + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE); + ccdc->lsc.state = LSC_STATE_STOPPED; + dev_warn(to_device(ccdc), "LSC prefecth timeout\n"); + return -ETIMEDOUT; + } + ccdc->lsc.state = LSC_STATE_RUNNING; + } else { + ccdc->lsc.state = LSC_STATE_STOPPING; + } + + return 0; +} + +static int ccdc_lsc_busy(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + + return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG) & + ISPCCDC_LSC_BUSY; +} + +/* __ccdc_lsc_configure - Apply a new configuration to the LSC engine + * @ccdc: Pointer to ISP CCDC device + * @req: New configuration request + * + * context: in_interrupt() + */ +static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc, + struct ispccdc_lsc_config_req *req) +{ + if (!req->enable) + return -EINVAL; + + if (ccdc_lsc_validate_config(ccdc, &req->config) < 0) { + dev_dbg(to_device(ccdc), "Discard LSC configuration\n"); + return -EINVAL; + } + + if (ccdc_lsc_busy(ccdc)) + return -EBUSY; + + ccdc_lsc_setup_regs(ccdc, &req->config); + ccdc_lsc_program_table(ccdc, req->table); + return 0; +} + +/* + * ccdc_lsc_error_handler - Handle LSC prefetch error scenario. + * @ccdc: Pointer to ISP CCDC device. + * + * Disables LSC, and defers enablement to shadow registers update time. + */ +static void ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + /* + * From OMAP3 TRM: When this event is pending, the module + * goes into transparent mode (output =input). Normal + * operation can be resumed at the start of the next frame + * after: + * 1) Clearing this event + * 2) Disabling the LSC module + * 3) Enabling it + */ + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, + ISPCCDC_LSC_ENABLE); + ccdc->lsc.state = LSC_STATE_STOPPED; +} + +static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc, + struct ispccdc_lsc_config_req *req) +{ + struct isp_device *isp = to_isp_device(ccdc); + + if (req == NULL) + return; + + if (req->iovm) + dma_unmap_sg(isp->dev, req->iovm->sgt->sgl, + req->iovm->sgt->nents, DMA_TO_DEVICE); + if (req->table) + iommu_vfree(isp->iommu, req->table); + kfree(req); +} + +static void ccdc_lsc_free_queue(struct isp_ccdc_device *ccdc, + struct list_head *queue) +{ + struct ispccdc_lsc_config_req *req, *n; + unsigned long flags; + + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); + list_for_each_entry_safe(req, n, queue, list) { + list_del(&req->list); + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); + ccdc_lsc_free_request(ccdc, req); + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); + } + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); +} + +static void ccdc_lsc_free_table_work(struct work_struct *work) +{ + struct isp_ccdc_device *ccdc; + struct ispccdc_lsc *lsc; + + lsc = container_of(work, struct ispccdc_lsc, table_work); + ccdc = container_of(lsc, struct isp_ccdc_device, lsc); + + ccdc_lsc_free_queue(ccdc, &lsc->free_queue); +} + +/* + * ccdc_lsc_config - Configure the LSC module from a userspace request + * + * Store the request LSC configuration in the LSC engine request pointer. The + * configuration will be applied to the hardware when the CCDC will be enabled, + * or at the next LSC interrupt if the CCDC is already running. + */ +static int ccdc_lsc_config(struct isp_ccdc_device *ccdc, + struct omap3isp_ccdc_update_config *config) +{ + struct isp_device *isp = to_isp_device(ccdc); + struct ispccdc_lsc_config_req *req; + unsigned long flags; + void *table; + u16 update; + int ret; + + update = config->update & + (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC); + if (!update) + return 0; + + if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) { + dev_dbg(to_device(ccdc), "%s: Both LSC configuration and table " + "need to be supplied\n", __func__); + return -EINVAL; + } + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (req == NULL) + return -ENOMEM; + + if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) { + if (copy_from_user(&req->config, config->lsc_cfg, + sizeof(req->config))) { + ret = -EFAULT; + goto done; + } + + req->enable = 1; + + req->table = iommu_vmalloc(isp->iommu, 0, req->config.size, + IOMMU_FLAG); + if (IS_ERR_VALUE(req->table)) { + req->table = 0; + ret = -ENOMEM; + goto done; + } + + req->iovm = find_iovm_area(isp->iommu, req->table); + if (req->iovm == NULL) { + ret = -ENOMEM; + goto done; + } + + if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl, + req->iovm->sgt->nents, DMA_TO_DEVICE)) { + ret = -ENOMEM; + req->iovm = NULL; + goto done; + } + + dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl, + req->iovm->sgt->nents, DMA_TO_DEVICE); + + table = da_to_va(isp->iommu, req->table); + if (copy_from_user(table, config->lsc, req->config.size)) { + ret = -EFAULT; + goto done; + } + + dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl, + req->iovm->sgt->nents, DMA_TO_DEVICE); + } + + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); + if (ccdc->lsc.request) { + list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); + schedule_work(&ccdc->lsc.table_work); + } + ccdc->lsc.request = req; + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); + + ret = 0; + +done: + if (ret < 0) + ccdc_lsc_free_request(ccdc, req); + + return ret; +} + +static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc) +{ + unsigned long flags; + + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); + if (ccdc->lsc.active) { + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); + return 1; + } + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); + return 0; +} + +static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc) +{ + struct ispccdc_lsc *lsc = &ccdc->lsc; + + if (lsc->state != LSC_STATE_STOPPED) + return -EINVAL; + + if (lsc->active) { + list_add_tail(&lsc->active->list, &lsc->free_queue); + lsc->active = NULL; + } + + if (__ccdc_lsc_configure(ccdc, lsc->request) < 0) { + omap3isp_sbl_disable(to_isp_device(ccdc), + OMAP3_ISP_SBL_CCDC_LSC_READ); + list_add_tail(&lsc->request->list, &lsc->free_queue); + lsc->request = NULL; + goto done; + } + + lsc->active = lsc->request; + lsc->request = NULL; + __ccdc_lsc_enable(ccdc, 1); + +done: + if (!list_empty(&lsc->free_queue)) + schedule_work(&lsc->table_work); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Parameters configuration + */ + +/* + * ccdc_configure_clamp - Configure optical-black or digital clamping + * @ccdc: Pointer to ISP CCDC device. + * + * The CCDC performs either optical-black or digital clamp. Configure and enable + * the selected clamp method. + */ +static void ccdc_configure_clamp(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + u32 clamp; + + if (ccdc->obclamp) { + clamp = ccdc->clamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT; + clamp |= ccdc->clamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT; + clamp |= ccdc->clamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT; + clamp |= ccdc->clamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT; + isp_reg_writel(isp, clamp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP); + } else { + isp_reg_writel(isp, ccdc->clamp.dcsubval, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB); + } + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, + ISPCCDC_CLAMP_CLAMPEN, + ccdc->obclamp ? ISPCCDC_CLAMP_CLAMPEN : 0); +} + +/* + * ccdc_configure_fpc - Configure Faulty Pixel Correction + * @ccdc: Pointer to ISP CCDC device. + */ +static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ISPCCDC_FPC_FPCEN); + + if (!ccdc->fpc_en) + return; + + isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FPC_ADDR); + /* The FPNUM field must be set before enabling FPC. */ + isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); + isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) | + ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); +} + +/* + * ccdc_configure_black_comp - Configure Black Level Compensation. + * @ccdc: Pointer to ISP CCDC device. + */ +static void ccdc_configure_black_comp(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + u32 blcomp; + + blcomp = ccdc->blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT; + blcomp |= ccdc->blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT; + blcomp |= ccdc->blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT; + blcomp |= ccdc->blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT; + + isp_reg_writel(isp, blcomp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP); +} + +/* + * ccdc_configure_lpf - Configure Low-Pass Filter (LPF). + * @ccdc: Pointer to ISP CCDC device. + */ +static void ccdc_configure_lpf(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, + ISPCCDC_SYN_MODE_LPF, + ccdc->lpf ? ISPCCDC_SYN_MODE_LPF : 0); +} + +/* + * ccdc_configure_alaw - Configure A-law compression. + * @ccdc: Pointer to ISP CCDC device. + */ +static void ccdc_configure_alaw(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + u32 alaw = 0; + + switch (ccdc->syncif.datsz) { + case 8: + return; + + case 10: + alaw = ISPCCDC_ALAW_GWDI_9_0; + break; + case 11: + alaw = ISPCCDC_ALAW_GWDI_10_1; + break; + case 12: + alaw = ISPCCDC_ALAW_GWDI_11_2; + break; + case 13: + alaw = ISPCCDC_ALAW_GWDI_12_3; + break; + } + + if (ccdc->alaw) + alaw |= ISPCCDC_ALAW_CCDTBL; + + isp_reg_writel(isp, alaw, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW); +} + +/* + * ccdc_config_imgattr - Configure sensor image specific attributes. + * @ccdc: Pointer to ISP CCDC device. + * @colptn: Color pattern of the sensor. + */ +static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn) +{ + struct isp_device *isp = to_isp_device(ccdc); + + isp_reg_writel(isp, colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN); +} + +/* + * ccdc_config - Set CCDC configuration from userspace + * @ccdc: Pointer to ISP CCDC device. + * @userspace_add: Structure containing CCDC configuration sent from userspace. + * + * Returns 0 if successful, -EINVAL if the pointer to the configuration + * structure is null, or the copy_from_user function fails to copy user space + * memory to kernel space memory. + */ +static int ccdc_config(struct isp_ccdc_device *ccdc, + struct omap3isp_ccdc_update_config *ccdc_struct) +{ + struct isp_device *isp = to_isp_device(ccdc); + unsigned long flags; + + spin_lock_irqsave(&ccdc->lock, flags); + ccdc->shadow_update = 1; + spin_unlock_irqrestore(&ccdc->lock, flags); + + if (OMAP3ISP_CCDC_ALAW & ccdc_struct->update) { + ccdc->alaw = !!(OMAP3ISP_CCDC_ALAW & ccdc_struct->flag); + ccdc->update |= OMAP3ISP_CCDC_ALAW; + } + + if (OMAP3ISP_CCDC_LPF & ccdc_struct->update) { + ccdc->lpf = !!(OMAP3ISP_CCDC_LPF & ccdc_struct->flag); + ccdc->update |= OMAP3ISP_CCDC_LPF; + } + + if (OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->update) { + if (copy_from_user(&ccdc->clamp, ccdc_struct->bclamp, + sizeof(ccdc->clamp))) { + ccdc->shadow_update = 0; + return -EFAULT; + } + + ccdc->obclamp = !!(OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->flag); + ccdc->update |= OMAP3ISP_CCDC_BLCLAMP; + } + + if (OMAP3ISP_CCDC_BCOMP & ccdc_struct->update) { + if (copy_from_user(&ccdc->blcomp, ccdc_struct->blcomp, + sizeof(ccdc->blcomp))) { + ccdc->shadow_update = 0; + return -EFAULT; + } + + ccdc->update |= OMAP3ISP_CCDC_BCOMP; + } + + ccdc->shadow_update = 0; + + if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) { + u32 table_old = 0; + u32 table_new; + u32 size; + + if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) + return -EBUSY; + + ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag); + + if (ccdc->fpc_en) { + if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc, + sizeof(ccdc->fpc))) + return -EFAULT; + + /* + * table_new must be 64-bytes aligned, but it's + * already done by iommu_vmalloc(). + */ + size = ccdc->fpc.fpnum * 4; + table_new = iommu_vmalloc(isp->iommu, 0, size, + IOMMU_FLAG); + if (IS_ERR_VALUE(table_new)) + return -ENOMEM; + + if (copy_from_user(da_to_va(isp->iommu, table_new), + (__force void __user *) + ccdc->fpc.fpcaddr, size)) { + iommu_vfree(isp->iommu, table_new); + return -EFAULT; + } + + table_old = ccdc->fpc.fpcaddr; + ccdc->fpc.fpcaddr = table_new; + } + + ccdc_configure_fpc(ccdc); + if (table_old != 0) + iommu_vfree(isp->iommu, table_old); + } + + return ccdc_lsc_config(ccdc, ccdc_struct); +} + +static void ccdc_apply_controls(struct isp_ccdc_device *ccdc) +{ + if (ccdc->update & OMAP3ISP_CCDC_ALAW) { + ccdc_configure_alaw(ccdc); + ccdc->update &= ~OMAP3ISP_CCDC_ALAW; + } + + if (ccdc->update & OMAP3ISP_CCDC_LPF) { + ccdc_configure_lpf(ccdc); + ccdc->update &= ~OMAP3ISP_CCDC_LPF; + } + + if (ccdc->update & OMAP3ISP_CCDC_BLCLAMP) { + ccdc_configure_clamp(ccdc); + ccdc->update &= ~OMAP3ISP_CCDC_BLCLAMP; + } + + if (ccdc->update & OMAP3ISP_CCDC_BCOMP) { + ccdc_configure_black_comp(ccdc); + ccdc->update &= ~OMAP3ISP_CCDC_BCOMP; + } +} + +/* + * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers + * @dev: Pointer to ISP device + */ +void omap3isp_ccdc_restore_context(struct isp_device *isp) +{ + struct isp_ccdc_device *ccdc = &isp->isp_ccdc; + + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC); + + ccdc->update = OMAP3ISP_CCDC_ALAW | OMAP3ISP_CCDC_LPF + | OMAP3ISP_CCDC_BLCLAMP | OMAP3ISP_CCDC_BCOMP; + ccdc_apply_controls(ccdc); + ccdc_configure_fpc(ccdc); +} + +/* ----------------------------------------------------------------------------- + * Format- and pipeline-related configuration helpers + */ + +/* + * ccdc_config_vp - Configure the Video Port. + * @ccdc: Pointer to ISP CCDC device. + */ +static void ccdc_config_vp(struct isp_ccdc_device *ccdc) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); + struct isp_device *isp = to_isp_device(ccdc); + unsigned long l3_ick = pipe->l3_ick; + unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8; + unsigned int div = 0; + u32 fmtcfg_vp; + + fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG) + & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK); + + switch (ccdc->syncif.datsz) { + case 8: + case 10: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; + break; + case 11: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; + break; + case 12: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; + break; + case 13: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; + break; + }; + + if (pipe->input) + div = DIV_ROUND_UP(l3_ick, pipe->max_rate); + else if (ccdc->vpcfg.pixelclk) + div = l3_ick / ccdc->vpcfg.pixelclk; + + div = clamp(div, 2U, max_div); + fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; + + isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); +} + +/* + * ccdc_enable_vp - Enable Video Port. + * @ccdc: Pointer to ISP CCDC device. + * @enable: 0 Disables VP, 1 Enables VP + * + * This is needed for outputting image to Preview, H3A and HIST ISP submodules. + */ +static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable) +{ + struct isp_device *isp = to_isp_device(ccdc); + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, + ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0); +} + +/* + * ccdc_config_outlineoffset - Configure memory saving output line offset + * @ccdc: Pointer to ISP CCDC device. + * @offset: Address offset to start a new line. Must be twice the + * Output width and aligned on 32 byte boundary + * @oddeven: Specifies the odd/even line pattern to be chosen to store the + * output. + * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. + * + * - Configures the output line offset when stored in memory + * - Sets the odd/even line pattern to store the output + * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) + * - Configures the number of even and odd line fields in case of rearranging + * the lines. + */ +static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, + u32 offset, u8 oddeven, u8 numlines) +{ + struct isp_device *isp = to_isp_device(ccdc); + + isp_reg_writel(isp, offset & 0xffff, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); + + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, + ISPCCDC_SDOFST_FINV); + + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, + ISPCCDC_SDOFST_FOFST_4L); + + switch (oddeven) { + case EVENEVEN: + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); + break; + case ODDEVEN: + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); + break; + case EVENODD: + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); + break; + case ODDODD: + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); + break; + default: + break; + } +} + +/* + * ccdc_set_outaddr - Set memory address to save output image + * @ccdc: Pointer to ISP CCDC device. + * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. + * + * Sets the memory address where the output will be saved. + */ +static void ccdc_set_outaddr(struct isp_ccdc_device *ccdc, u32 addr) +{ + struct isp_device *isp = to_isp_device(ccdc); + + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR); +} + +/* + * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input + * @ccdc: Pointer to ISP CCDC device. + * @max_rate: Maximum calculated data rate. + * + * Returns in *max_rate less value between calculated and passed + */ +void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, + unsigned int *max_rate) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); + unsigned int rate; + + if (pipe == NULL) + return; + + /* + * TRM says that for parallel sensors the maximum data rate + * should be 90% form L3/2 clock, otherwise just L3/2. + */ + if (ccdc->input == CCDC_INPUT_PARALLEL) + rate = pipe->l3_ick / 2 * 9 / 10; + else + rate = pipe->l3_ick / 2; + + *max_rate = min(*max_rate, rate); +} + +/* + * ccdc_config_sync_if - Set CCDC sync interface configuration + * @ccdc: Pointer to ISP CCDC device. + * @syncif: Structure containing the sync parameters like field state, CCDC in + * master/slave mode, raw/yuv data, polarity of data, field, hs, vs + * signals. + */ +static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, + struct ispccdc_syncif *syncif) +{ + struct isp_device *isp = to_isp_device(ccdc); + u32 syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SYN_MODE); + + syn_mode |= ISPCCDC_SYN_MODE_VDHDEN; + + if (syncif->fldstat) + syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT; + else + syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT; + + syn_mode &= ~ISPCCDC_SYN_MODE_DATSIZ_MASK; + switch (syncif->datsz) { + case 8: + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; + break; + case 10: + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10; + break; + case 11: + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11; + break; + case 12: + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12; + break; + }; + + if (syncif->fldmode) + syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; + else + syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE; + + if (syncif->datapol) + syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; + else + syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL; + + if (syncif->fldpol) + syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; + else + syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL; + + if (syncif->hdpol) + syn_mode |= ISPCCDC_SYN_MODE_HDPOL; + else + syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL; + + if (syncif->vdpol) + syn_mode |= ISPCCDC_SYN_MODE_VDPOL; + else + syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL; + + if (syncif->ccdc_mastermode) { + syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT; + isp_reg_writel(isp, + syncif->hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT + | syncif->vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HD_VD_WID); + + isp_reg_writel(isp, + syncif->ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT + | syncif->hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_PIX_LINES); + } else + syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT | + ISPCCDC_SYN_MODE_VDHDOUT); + + isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); + + if (!syncif->bt_r656_en) + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, + ISPCCDC_REC656IF_R656ON); +} + +/* CCDC formats descriptions */ +static const u32 ccdc_sgrbg_pattern = + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT; + +static const u32 ccdc_srggb_pattern = + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC0_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC1_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC2_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC3_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC0_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC1_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC2_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC3_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC0_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC1_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC2_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC3_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC0_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC1_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC2_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC3_SHIFT; + +static const u32 ccdc_sbggr_pattern = + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC0_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC1_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC2_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC3_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC0_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC1_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC2_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC3_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC0_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC1_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC2_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC3_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC0_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC1_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC2_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC3_SHIFT; + +static const u32 ccdc_sgbrg_pattern = + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC0_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC1_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC2_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC3_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC0_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC1_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC2_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC3_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC0_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC1_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC2_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC3_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC0_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC1_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC2_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC3_SHIFT; + +static void ccdc_configure(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + struct isp_parallel_platform_data *pdata = NULL; + struct v4l2_subdev *sensor; + struct v4l2_mbus_framefmt *format; + struct media_pad *pad; + unsigned long flags; + u32 syn_mode; + u32 ccdc_pattern; + + if (ccdc->input == CCDC_INPUT_PARALLEL) { + pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]); + sensor = media_entity_to_v4l2_subdev(pad->entity); + pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) + ->bus.parallel; + } + + omap3isp_configure_bridge(isp, ccdc->input, pdata); + + ccdc->syncif.datsz = pdata ? pdata->width : 10; + ccdc_config_sync_if(ccdc, &ccdc->syncif); + + /* CCDC_PAD_SINK */ + format = &ccdc->formats[CCDC_PAD_SINK]; + + syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); + + /* Use the raw, unprocessed data when writing to memory. The H3A and + * histogram modules are still fed with lens shading corrected data. + */ + syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; + + if (ccdc->output & CCDC_OUTPUT_MEMORY) + syn_mode |= ISPCCDC_SYN_MODE_WEN; + else + syn_mode &= ~ISPCCDC_SYN_MODE_WEN; + + if (ccdc->output & CCDC_OUTPUT_RESIZER) + syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; + else + syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; + + /* Use PACK8 mode for 1byte per pixel formats. */ + if (omap3isp_video_format_info(format->code)->bpp <= 8) + syn_mode |= ISPCCDC_SYN_MODE_PACK8; + else + syn_mode &= ~ISPCCDC_SYN_MODE_PACK8; + + isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); + + /* Mosaic filter */ + switch (format->code) { + case V4L2_MBUS_FMT_SRGGB10_1X10: + case V4L2_MBUS_FMT_SRGGB12_1X12: + ccdc_pattern = ccdc_srggb_pattern; + break; + case V4L2_MBUS_FMT_SBGGR10_1X10: + case V4L2_MBUS_FMT_SBGGR12_1X12: + ccdc_pattern = ccdc_sbggr_pattern; + break; + case V4L2_MBUS_FMT_SGBRG10_1X10: + case V4L2_MBUS_FMT_SGBRG12_1X12: + ccdc_pattern = ccdc_sgbrg_pattern; + break; + default: + /* Use GRBG */ + ccdc_pattern = ccdc_sgrbg_pattern; + break; + } + ccdc_config_imgattr(ccdc, ccdc_pattern); + + /* Generate VD0 on the last line of the image and VD1 on the + * 2/3 height line. + */ + isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) | + ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); + + /* CCDC_PAD_SOURCE_OF */ + format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; + + isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | + ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); + isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); + isp_reg_writel(isp, (format->height - 1) + << ISPCCDC_VERT_LINES_NLV_SHIFT, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); + + ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0); + + /* CCDC_PAD_SOURCE_VP */ + format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; + + isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | + (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ); + isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | + ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT); + + isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | + (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); + + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); + if (ccdc->lsc.request == NULL) + goto unlock; + + WARN_ON(ccdc->lsc.active); + + /* Get last good LSC configuration. If it is not supported for + * the current active resolution discard it. + */ + if (ccdc->lsc.active == NULL && + __ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) { + ccdc->lsc.active = ccdc->lsc.request; + } else { + list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); + schedule_work(&ccdc->lsc.table_work); + } + + ccdc->lsc.request = NULL; + +unlock: + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); + + ccdc_apply_controls(ccdc); +} + +static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable) +{ + struct isp_device *isp = to_isp_device(ccdc); + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, + ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); +} + +static int ccdc_disable(struct isp_ccdc_device *ccdc) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ccdc->lock, flags); + if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS) + ccdc->stopping = CCDC_STOP_REQUEST; + spin_unlock_irqrestore(&ccdc->lock, flags); + + ret = wait_event_timeout(ccdc->wait, + ccdc->stopping == CCDC_STOP_FINISHED, + msecs_to_jiffies(2000)); + if (ret == 0) { + ret = -ETIMEDOUT; + dev_warn(to_device(ccdc), "CCDC stop timeout!\n"); + } + + omap3isp_sbl_disable(to_isp_device(ccdc), OMAP3_ISP_SBL_CCDC_LSC_READ); + + mutex_lock(&ccdc->ioctl_lock); + ccdc_lsc_free_request(ccdc, ccdc->lsc.request); + ccdc->lsc.request = ccdc->lsc.active; + ccdc->lsc.active = NULL; + cancel_work_sync(&ccdc->lsc.table_work); + ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); + mutex_unlock(&ccdc->ioctl_lock); + + ccdc->stopping = CCDC_STOP_NOT_REQUESTED; + + return ret > 0 ? 0 : ret; +} + +static void ccdc_enable(struct isp_ccdc_device *ccdc) +{ + if (ccdc_lsc_is_configured(ccdc)) + __ccdc_lsc_enable(ccdc, 1); + __ccdc_enable(ccdc, 1); +} + +/* ----------------------------------------------------------------------------- + * Interrupt handling + */ + +/* + * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits + * @ccdc: Pointer to ISP CCDC device. + * + * Returns zero if the CCDC is idle and the image has been written to + * memory, too. + */ +static int ccdc_sbl_busy(struct isp_ccdc_device *ccdc) +{ + struct isp_device *isp = to_isp_device(ccdc); + + return omap3isp_ccdc_busy(ccdc) + | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) & + ISPSBL_CCDC_WR_0_DATA_READY) + | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) & + ISPSBL_CCDC_WR_0_DATA_READY) + | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) & + ISPSBL_CCDC_WR_0_DATA_READY) + | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) & + ISPSBL_CCDC_WR_0_DATA_READY); +} + +/* + * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle + * @ccdc: Pointer to ISP CCDC device. + * @max_wait: Max retry count in us for wait for idle/busy transition. + */ +static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc, + unsigned int max_wait) +{ + unsigned int wait = 0; + + if (max_wait == 0) + max_wait = 10000; /* 10 ms */ + + for (wait = 0; wait <= max_wait; wait++) { + if (!ccdc_sbl_busy(ccdc)) + return 0; + + rmb(); + udelay(1); + } + + return -EBUSY; +} + +/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence + * @ccdc: Pointer to ISP CCDC device. + * @event: Pointing which event trigger handler + * + * Return 1 when the event and stopping request combination is satisfyied, + * zero otherwise. + */ +static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) +{ + int rval = 0; + + switch ((ccdc->stopping & 3) | event) { + case CCDC_STOP_REQUEST | CCDC_EVENT_VD1: + if (ccdc->lsc.state != LSC_STATE_STOPPED) + __ccdc_lsc_enable(ccdc, 0); + __ccdc_enable(ccdc, 0); + ccdc->stopping = CCDC_STOP_EXECUTED; + return 1; + + case CCDC_STOP_EXECUTED | CCDC_EVENT_VD0: + ccdc->stopping |= CCDC_STOP_CCDC_FINISHED; + if (ccdc->lsc.state == LSC_STATE_STOPPED) + ccdc->stopping |= CCDC_STOP_LSC_FINISHED; + rval = 1; + break; + + case CCDC_STOP_EXECUTED | CCDC_EVENT_LSC_DONE: + ccdc->stopping |= CCDC_STOP_LSC_FINISHED; + rval = 1; + break; + + case CCDC_STOP_EXECUTED | CCDC_EVENT_VD1: + return 1; + } + + if (ccdc->stopping == CCDC_STOP_FINISHED) { + wake_up(&ccdc->wait); + rval = 1; + } + + return rval; +} + +static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc) +{ + struct video_device *vdev = &ccdc->subdev.devnode; + struct v4l2_event event; + + memset(&event, 0, sizeof(event)); + event.type = V4L2_EVENT_OMAP3ISP_HS_VS; + + v4l2_event_queue(vdev, &event); +} + +/* + * ccdc_lsc_isr - Handle LSC events + * @ccdc: Pointer to ISP CCDC device. + * @events: LSC events + */ +static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events) +{ + unsigned long flags; + + if (events & IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ) { + ccdc_lsc_error_handler(ccdc); + ccdc->error = 1; + dev_dbg(to_device(ccdc), "lsc prefetch error\n"); + } + + if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ)) + return; + + /* LSC_DONE interrupt occur, there are two cases + * 1. stopping for reconfiguration + * 2. stopping because of STREAM OFF command + */ + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); + + if (ccdc->lsc.state == LSC_STATE_STOPPING) + ccdc->lsc.state = LSC_STATE_STOPPED; + + if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) + goto done; + + if (ccdc->lsc.state != LSC_STATE_RECONFIG) + goto done; + + /* LSC is in STOPPING state, change to the new state */ + ccdc->lsc.state = LSC_STATE_STOPPED; + + /* This is an exception. Start of frame and LSC_DONE interrupt + * have been received on the same time. Skip this event and wait + * for better times. + */ + if (events & IRQ0STATUS_HS_VS_IRQ) + goto done; + + /* The LSC engine is stopped at this point. Enable it if there's a + * pending request. + */ + if (ccdc->lsc.request == NULL) + goto done; + + ccdc_lsc_enable(ccdc); + +done: + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); +} + +static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); + struct isp_device *isp = to_isp_device(ccdc); + struct isp_buffer *buffer; + int restart = 0; + + /* The CCDC generates VD0 interrupts even when disabled (the datasheet + * doesn't explicitly state if that's supposed to happen or not, so it + * can be considered as a hardware bug or as a feature, but we have to + * deal with it anyway). Disabling the CCDC when no buffer is available + * would thus not be enough, we need to handle the situation explicitly. + */ + if (list_empty(&ccdc->video_out.dmaqueue)) + goto done; + + /* We're in continuous mode, and memory writes were disabled due to a + * buffer underrun. Reenable them now that we have a buffer. The buffer + * address has been set in ccdc_video_queue. + */ + if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) { + restart = 1; + ccdc->underrun = 0; + goto done; + } + + if (ccdc_sbl_wait_idle(ccdc, 1000)) { + dev_info(isp->dev, "CCDC won't become idle!\n"); + goto done; + } + + buffer = omap3isp_video_buffer_next(&ccdc->video_out, ccdc->error); + if (buffer != NULL) { + ccdc_set_outaddr(ccdc, buffer->isp_addr); + restart = 1; + } + + pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; + + if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT && + isp_pipeline_ready(pipe)) + omap3isp_pipeline_set_stream(pipe, + ISP_PIPELINE_STREAM_SINGLESHOT); + +done: + ccdc->error = 0; + return restart; +} + +/* + * ccdc_vd0_isr - Handle VD0 event + * @ccdc: Pointer to ISP CCDC device. + * + * Executes LSC deferred enablement before next frame starts. + */ +static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc) +{ + unsigned long flags; + int restart = 0; + + if (ccdc->output & CCDC_OUTPUT_MEMORY) + restart = ccdc_isr_buffer(ccdc); + + spin_lock_irqsave(&ccdc->lock, flags); + if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) { + spin_unlock_irqrestore(&ccdc->lock, flags); + return; + } + + if (!ccdc->shadow_update) + ccdc_apply_controls(ccdc); + spin_unlock_irqrestore(&ccdc->lock, flags); + + if (restart) + ccdc_enable(ccdc); +} + +/* + * ccdc_vd1_isr - Handle VD1 event + * @ccdc: Pointer to ISP CCDC device. + */ +static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc) +{ + unsigned long flags; + + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); + + /* + * Depending on the CCDC pipeline state, CCDC stopping should be + * handled differently. In SINGLESHOT we emulate an internal CCDC + * stopping because the CCDC hw works only in continuous mode. + * When CONTINUOUS pipeline state is used and the CCDC writes it's + * data to memory the CCDC and LSC are stopped immediately but + * without change the CCDC stopping state machine. The CCDC + * stopping state machine should be used only when user request + * for stopping is received (SINGLESHOT is an exeption). + */ + switch (ccdc->state) { + case ISP_PIPELINE_STREAM_SINGLESHOT: + ccdc->stopping = CCDC_STOP_REQUEST; + break; + + case ISP_PIPELINE_STREAM_CONTINUOUS: + if (ccdc->output & CCDC_OUTPUT_MEMORY) { + if (ccdc->lsc.state != LSC_STATE_STOPPED) + __ccdc_lsc_enable(ccdc, 0); + __ccdc_enable(ccdc, 0); + } + break; + + case ISP_PIPELINE_STREAM_STOPPED: + break; + } + + if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) + goto done; + + if (ccdc->lsc.request == NULL) + goto done; + + /* + * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ + * do the appropriate changes in registers + */ + if (ccdc->lsc.state == LSC_STATE_RUNNING) { + __ccdc_lsc_enable(ccdc, 0); + ccdc->lsc.state = LSC_STATE_RECONFIG; + goto done; + } + + /* LSC has been in STOPPED state, enable it */ + if (ccdc->lsc.state == LSC_STATE_STOPPED) + ccdc_lsc_enable(ccdc); + +done: + spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); +} + +/* + * omap3isp_ccdc_isr - Configure CCDC during interframe time. + * @ccdc: Pointer to ISP CCDC device. + * @events: CCDC events + */ +int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events) +{ + if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) + return 0; + + if (events & IRQ0STATUS_CCDC_VD1_IRQ) + ccdc_vd1_isr(ccdc); + + ccdc_lsc_isr(ccdc, events); + + if (events & IRQ0STATUS_CCDC_VD0_IRQ) + ccdc_vd0_isr(ccdc); + + if (events & IRQ0STATUS_HS_VS_IRQ) + ccdc_hs_vs_isr(ccdc); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * ISP video operations + */ + +static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer) +{ + struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc; + + if (!(ccdc->output & CCDC_OUTPUT_MEMORY)) + return -ENODEV; + + ccdc_set_outaddr(ccdc, buffer->isp_addr); + + /* We now have a buffer queued on the output, restart the pipeline in + * on the next CCDC interrupt if running in continuous mode (or when + * starting the stream). + */ + ccdc->underrun = 1; + + return 0; +} + +static const struct isp_video_operations ccdc_video_ops = { + .queue = ccdc_video_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +/* + * ccdc_ioctl - CCDC module private ioctl's + * @sd: ISP CCDC V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + int ret; + + switch (cmd) { + case VIDIOC_OMAP3ISP_CCDC_CFG: + mutex_lock(&ccdc->ioctl_lock); + ret = ccdc_config(ccdc, arg); + mutex_unlock(&ccdc->ioctl_lock); + break; + + default: + return -ENOIOCTLCMD; + } + + return ret; +} + +static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + if (sub->type != V4L2_EVENT_OMAP3ISP_HS_VS) + return -EINVAL; + + return v4l2_event_subscribe(fh, sub); +} + +static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} + +/* + * ccdc_set_stream - Enable/Disable streaming on the CCDC module + * @sd: ISP CCDC V4L2 subdevice + * @enable: Enable/disable stream + * + * When writing to memory, the CCDC hardware can't be enabled without a memory + * buffer to write to. As the s_stream operation is called in response to a + * STREAMON call without any buffer queued yet, just update the enabled field + * and return immediately. The CCDC will be enabled in ccdc_isr_buffer(). + * + * When not writing to memory enable the CCDC immediately. + */ +static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct isp_device *isp = to_isp_device(ccdc); + int ret = 0; + + if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) { + if (enable == ISP_PIPELINE_STREAM_STOPPED) + return 0; + + omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_CCDC); + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, + ISPCCDC_CFG_VDLC); + + ccdc_configure(ccdc); + + /* TODO: Don't configure the video port if all of its output + * links are inactive. + */ + ccdc_config_vp(ccdc); + ccdc_enable_vp(ccdc, 1); + ccdc->error = 0; + ccdc_print_status(ccdc); + } + + switch (enable) { + case ISP_PIPELINE_STREAM_CONTINUOUS: + if (ccdc->output & CCDC_OUTPUT_MEMORY) + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); + + if (ccdc->underrun || !(ccdc->output & CCDC_OUTPUT_MEMORY)) + ccdc_enable(ccdc); + + ccdc->underrun = 0; + break; + + case ISP_PIPELINE_STREAM_SINGLESHOT: + if (ccdc->output & CCDC_OUTPUT_MEMORY && + ccdc->state != ISP_PIPELINE_STREAM_SINGLESHOT) + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); + + ccdc_enable(ccdc); + break; + + case ISP_PIPELINE_STREAM_STOPPED: + ret = ccdc_disable(ccdc); + if (ccdc->output & CCDC_OUTPUT_MEMORY) + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE); + omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC); + ccdc->underrun = 0; + break; + } + + ccdc->state = enable; + return ret; +} + +static struct v4l2_mbus_framefmt * +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + else + return &ccdc->formats[pad]; +} + +/* + * ccdc_try_format - Try video format on a pad + * @ccdc: ISP CCDC device + * @fh : V4L2 subdev file handle + * @pad: Pad number + * @fmt: Format + */ +static void +ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *format; + const struct isp_format_info *info; + unsigned int width = fmt->width; + unsigned int height = fmt->height; + unsigned int i; + + switch (pad) { + case CCDC_PAD_SINK: + /* TODO: If the CCDC output formatter pad is connected directly + * to the resizer, only YUV formats can be used. + */ + for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) { + if (fmt->code == ccdc_fmts[i]) + break; + } + + /* If not found, use SGRBG10 as default */ + if (i >= ARRAY_SIZE(ccdc_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + /* Clamp the input size. */ + fmt->width = clamp_t(u32, width, 32, 4096); + fmt->height = clamp_t(u32, height, 32, 4096); + break; + + case CCDC_PAD_SOURCE_OF: + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + + /* The data formatter truncates the number of horizontal output + * pixels to a multiple of 16. To avoid clipping data, allow + * callers to request an output size bigger than the input size + * up to the nearest multiple of 16. + */ + fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15); + fmt->width &= ~15; + fmt->height = clamp_t(u32, height, 32, fmt->height); + break; + + case CCDC_PAD_SOURCE_VP: + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + + /* The video port interface truncates the data to 10 bits. */ + info = omap3isp_video_format_info(fmt->code); + fmt->code = info->truncated; + + /* The number of lines that can be clocked out from the video + * port output must be at least one line less than the number + * of input lines. + */ + fmt->width = clamp_t(u32, width, 32, fmt->width); + fmt->height = clamp_t(u32, height, 32, fmt->height - 1); + break; + } + + /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is + * stored on 2 bytes. + */ + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->field = V4L2_FIELD_NONE; +} + +/* + * ccdc_enum_mbus_code - Handle pixel format enumeration + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @code : pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + switch (code->pad) { + case CCDC_PAD_SINK: + if (code->index >= ARRAY_SIZE(ccdc_fmts)) + return -EINVAL; + + code->code = ccdc_fmts[code->index]; + break; + + case CCDC_PAD_SOURCE_OF: + case CCDC_PAD_SOURCE_VP: + /* No format conversion inside CCDC */ + if (code->index != 0) + return -EINVAL; + + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, + V4L2_SUBDEV_FORMAT_TRY); + + code->code = format->code; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int ccdc_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * ccdc_get_format - Retrieve the video format on a pad + * @sd : ISP CCDC V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +/* + * ccdc_set_format - Set the video format on a pad + * @sd : ISP CCDC V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == CCDC_PAD_SINK) { + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, + fmt->which); + *format = fmt->format; + ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, + fmt->which); + + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP, + fmt->which); + *format = fmt->format; + ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format, + fmt->which); + } + + return 0; +} + +/* + * ccdc_init_formats - Initialize formats on all pads + * @sd: ISP CCDC V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = CCDC_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + format.format.width = 4096; + format.format.height = 4096; + ccdc_set_format(sd, fh, &format); + + return 0; +} + +/* V4L2 subdev core operations */ +static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = { + .ioctl = ccdc_ioctl, + .subscribe_event = ccdc_subscribe_event, + .unsubscribe_event = ccdc_unsubscribe_event, +}; + +/* V4L2 subdev video operations */ +static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = { + .s_stream = ccdc_set_stream, +}; + +/* V4L2 subdev pad operations */ +static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { + .enum_mbus_code = ccdc_enum_mbus_code, + .enum_frame_size = ccdc_enum_frame_size, + .get_fmt = ccdc_get_format, + .set_fmt = ccdc_set_format, +}; + +/* V4L2 subdev operations */ +static const struct v4l2_subdev_ops ccdc_v4l2_ops = { + .core = &ccdc_v4l2_core_ops, + .video = &ccdc_v4l2_video_ops, + .pad = &ccdc_v4l2_pad_ops, +}; + +/* V4L2 subdev internal operations */ +static const struct v4l2_subdev_internal_ops ccdc_v4l2_internal_ops = { + .open = ccdc_init_formats, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +/* + * ccdc_link_setup - Setup CCDC connections + * @entity: CCDC media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int ccdc_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct isp_device *isp = to_isp_device(ccdc); + + switch (local->index | media_entity_type(remote->entity)) { + case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* Read from the sensor (parallel interface), CCP2, CSI2a or + * CSI2c. + */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ccdc->input = CCDC_INPUT_NONE; + break; + } + + if (ccdc->input != CCDC_INPUT_NONE) + return -EBUSY; + + if (remote->entity == &isp->isp_ccp2.subdev.entity) + ccdc->input = CCDC_INPUT_CCP2B; + else if (remote->entity == &isp->isp_csi2a.subdev.entity) + ccdc->input = CCDC_INPUT_CSI2A; + else if (remote->entity == &isp->isp_csi2c.subdev.entity) + ccdc->input = CCDC_INPUT_CSI2C; + else + ccdc->input = CCDC_INPUT_PARALLEL; + + break; + + /* + * The ISP core doesn't support pipelines with multiple video outputs. + * Revisit this when it will be implemented, and return -EBUSY for now. + */ + + case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: + /* Write to preview engine, histogram and H3A. When none of + * those links are active, the video port can be disabled. + */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (ccdc->output & ~CCDC_OUTPUT_PREVIEW) + return -EBUSY; + ccdc->output |= CCDC_OUTPUT_PREVIEW; + } else { + ccdc->output &= ~CCDC_OUTPUT_PREVIEW; + } + break; + + case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE: + /* Write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (ccdc->output & ~CCDC_OUTPUT_MEMORY) + return -EBUSY; + ccdc->output |= CCDC_OUTPUT_MEMORY; + } else { + ccdc->output &= ~CCDC_OUTPUT_MEMORY; + } + break; + + case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV: + /* Write to resizer */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (ccdc->output & ~CCDC_OUTPUT_RESIZER) + return -EBUSY; + ccdc->output |= CCDC_OUTPUT_RESIZER; + } else { + ccdc->output &= ~CCDC_OUTPUT_RESIZER; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* media operations */ +static const struct media_entity_operations ccdc_media_ops = { + .link_setup = ccdc_link_setup, +}; + +/* + * ccdc_init_entities - Initialize V4L2 subdev and media entity + * @ccdc: ISP CCDC module + * + * Return 0 on success and a negative error code on failure. + */ +static int ccdc_init_entities(struct isp_ccdc_device *ccdc) +{ + struct v4l2_subdev *sd = &ccdc->subdev; + struct media_pad *pads = ccdc->pads; + struct media_entity *me = &sd->entity; + int ret; + + ccdc->input = CCDC_INPUT_NONE; + + v4l2_subdev_init(sd, &ccdc_v4l2_ops); + sd->internal_ops = &ccdc_v4l2_internal_ops; + strlcpy(sd->name, "OMAP3 ISP CCDC", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ + v4l2_set_subdevdata(sd, ccdc); + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->nevents = OMAP3ISP_CCDC_NEVENTS; + + pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; + pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE; + + me->ops = &ccdc_media_ops; + ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0); + if (ret < 0) + return ret; + + ccdc_init_formats(sd, NULL); + + ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ccdc->video_out.ops = &ccdc_video_ops; + ccdc->video_out.isp = to_isp_device(ccdc); + ccdc->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; + ccdc->video_out.bpl_alignment = 32; + + ret = omap3isp_video_init(&ccdc->video_out, "CCDC"); + if (ret < 0) + return ret; + + /* Connect the CCDC subdev to the video node. */ + ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF, + &ccdc->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + return 0; +} + +void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) +{ + media_entity_cleanup(&ccdc->subdev.entity); + + v4l2_device_unregister_subdev(&ccdc->subdev); + omap3isp_video_unregister(&ccdc->video_out); +} + +int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video node. */ + ret = v4l2_device_register_subdev(vdev, &ccdc->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&ccdc->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_ccdc_unregister_entities(ccdc); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP CCDC initialisation and cleanup + */ + +/* + * omap3isp_ccdc_init - CCDC module initialization. + * @dev: Device pointer specific to the OMAP3 ISP. + * + * TODO: Get the initialisation values from platform data. + * + * Return 0 on success or a negative error code otherwise. + */ +int omap3isp_ccdc_init(struct isp_device *isp) +{ + struct isp_ccdc_device *ccdc = &isp->isp_ccdc; + + spin_lock_init(&ccdc->lock); + init_waitqueue_head(&ccdc->wait); + mutex_init(&ccdc->ioctl_lock); + + ccdc->stopping = CCDC_STOP_NOT_REQUESTED; + + INIT_WORK(&ccdc->lsc.table_work, ccdc_lsc_free_table_work); + ccdc->lsc.state = LSC_STATE_STOPPED; + INIT_LIST_HEAD(&ccdc->lsc.free_queue); + spin_lock_init(&ccdc->lsc.req_lock); + + ccdc->syncif.ccdc_mastermode = 0; + ccdc->syncif.datapol = 0; + ccdc->syncif.datsz = 0; + ccdc->syncif.fldmode = 0; + ccdc->syncif.fldout = 0; + ccdc->syncif.fldpol = 0; + ccdc->syncif.fldstat = 0; + ccdc->syncif.hdpol = 0; + ccdc->syncif.vdpol = 0; + + ccdc->clamp.oblen = 0; + ccdc->clamp.dcsubval = 0; + + ccdc->vpcfg.pixelclk = 0; + + ccdc->update = OMAP3ISP_CCDC_BLCLAMP; + ccdc_apply_controls(ccdc); + + return ccdc_init_entities(ccdc); +} + +/* + * omap3isp_ccdc_cleanup - CCDC module cleanup. + * @dev: Device pointer specific to the OMAP3 ISP. + */ +void omap3isp_ccdc_cleanup(struct isp_device *isp) +{ + struct isp_ccdc_device *ccdc = &isp->isp_ccdc; + + /* Free LSC requests. As the CCDC is stopped there's no active request, + * so only the pending request and the free queue need to be handled. + */ + ccdc_lsc_free_request(ccdc, ccdc->lsc.request); + cancel_work_sync(&ccdc->lsc.table_work); + ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); + + if (ccdc->fpc.fpcaddr != 0) + iommu_vfree(isp->iommu, ccdc->fpc.fpcaddr); +} diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h new file mode 100644 index 0000000..d403af5 --- /dev/null +++ b/drivers/media/video/omap3isp/ispccdc.h @@ -0,0 +1,219 @@ +/* + * ispccdc.h + * + * TI OMAP3 ISP - CCDC module + * + * Copyright (C) 2009-2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_CCDC_H +#define OMAP3_ISP_CCDC_H + +#include +#include + +#include "ispvideo.h" + +enum ccdc_input_entity { + CCDC_INPUT_NONE, + CCDC_INPUT_PARALLEL, + CCDC_INPUT_CSI2A, + CCDC_INPUT_CCP2B, + CCDC_INPUT_CSI2C +}; + +#define CCDC_OUTPUT_MEMORY (1 << 0) +#define CCDC_OUTPUT_PREVIEW (1 << 1) +#define CCDC_OUTPUT_RESIZER (1 << 2) + +#define OMAP3ISP_CCDC_NEVENTS 16 + +/* + * struct ispccdc_syncif - Structure for Sync Interface between sensor and CCDC + * @ccdc_mastermode: Master mode. 1 - Master, 0 - Slave. + * @fldstat: Field state. 0 - Odd Field, 1 - Even Field. + * @datsz: Data size. + * @fldmode: 0 - Progressive, 1 - Interlaced. + * @datapol: 0 - Positive, 1 - Negative. + * @fldpol: 0 - Positive, 1 - Negative. + * @hdpol: 0 - Positive, 1 - Negative. + * @vdpol: 0 - Positive, 1 - Negative. + * @fldout: 0 - Input, 1 - Output. + * @hs_width: Width of the Horizontal Sync pulse, used for HS/VS Output. + * @vs_width: Width of the Vertical Sync pulse, used for HS/VS Output. + * @ppln: Number of pixels per line, used for HS/VS Output. + * @hlprf: Number of half lines per frame, used for HS/VS Output. + * @bt_r656_en: 1 - Enable ITU-R BT656 mode, 0 - Sync mode. + */ +struct ispccdc_syncif { + u8 ccdc_mastermode; + u8 fldstat; + u8 datsz; + u8 fldmode; + u8 datapol; + u8 fldpol; + u8 hdpol; + u8 vdpol; + u8 fldout; + u8 hs_width; + u8 vs_width; + u8 ppln; + u8 hlprf; + u8 bt_r656_en; +}; + +/* + * struct ispccdc_vp - Structure for Video Port parameters + * @pixelclk: Input pixel clock in Hz + */ +struct ispccdc_vp { + unsigned int pixelclk; +}; + +enum ispccdc_lsc_state { + LSC_STATE_STOPPED = 0, + LSC_STATE_STOPPING = 1, + LSC_STATE_RUNNING = 2, + LSC_STATE_RECONFIG = 3, +}; + +struct ispccdc_lsc_config_req { + struct list_head list; + struct omap3isp_ccdc_lsc_config config; + unsigned char enable; + u32 table; + struct iovm_struct *iovm; +}; + +/* + * ispccdc_lsc - CCDC LSC parameters + * @update_config: Set when user changes config + * @request_enable: Whether LSC is requested to be enabled + * @config: LSC config set by user + * @update_table: Set when user provides a new LSC table to table_new + * @table_new: LSC table set by user, ISP address + * @table_inuse: LSC table currently in use, ISP address + */ +struct ispccdc_lsc { + enum ispccdc_lsc_state state; + struct work_struct table_work; + + /* LSC queue of configurations */ + spinlock_t req_lock; + struct ispccdc_lsc_config_req *request; /* requested configuration */ + struct ispccdc_lsc_config_req *active; /* active configuration */ + struct list_head free_queue; /* configurations for freeing */ +}; + +#define CCDC_STOP_NOT_REQUESTED 0x00 +#define CCDC_STOP_REQUEST 0x01 +#define CCDC_STOP_EXECUTED (0x02 | CCDC_STOP_REQUEST) +#define CCDC_STOP_CCDC_FINISHED 0x04 +#define CCDC_STOP_LSC_FINISHED 0x08 +#define CCDC_STOP_FINISHED \ + (CCDC_STOP_EXECUTED | CCDC_STOP_CCDC_FINISHED | CCDC_STOP_LSC_FINISHED) + +#define CCDC_EVENT_VD1 0x10 +#define CCDC_EVENT_VD0 0x20 +#define CCDC_EVENT_LSC_DONE 0x40 + +/* Sink and source CCDC pads */ +#define CCDC_PAD_SINK 0 +#define CCDC_PAD_SOURCE_OF 1 +#define CCDC_PAD_SOURCE_VP 2 +#define CCDC_PADS_NUM 3 + +/* + * struct isp_ccdc_device - Structure for the CCDC module to store its own + * information + * @subdev: V4L2 subdevice + * @pads: Sink and source media entity pads + * @formats: Active video formats + * @input: Active input + * @output: Active outputs + * @video_out: Output video node + * @error: A hardware error occured during capture + * @alaw: A-law compression enabled (1) or disabled (0) + * @lpf: Low pass filter enabled (1) or disabled (0) + * @obclamp: Optical-black clamp enabled (1) or disabled (0) + * @fpc_en: Faulty pixels correction enabled (1) or disabled (0) + * @blcomp: Black level compensation configuration + * @clamp: Optical-black or digital clamp configuration + * @fpc: Faulty pixels correction configuration + * @lsc: Lens shading compensation configuration + * @update: Bitmask of controls to update during the next interrupt + * @shadow_update: Controls update in progress by userspace + * @syncif: Interface synchronization configuration + * @vpcfg: Video port configuration + * @underrun: A buffer underrun occured and a new buffer has been queued + * @state: Streaming state + * @lock: Serializes shadow_update with interrupt handler + * @wait: Wait queue used to stop the module + * @stopping: Stopping state + * @ioctl_lock: Serializes ioctl calls and LSC requests freeing + */ +struct isp_ccdc_device { + struct v4l2_subdev subdev; + struct media_pad pads[CCDC_PADS_NUM]; + struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; + + enum ccdc_input_entity input; + unsigned int output; + struct isp_video video_out; + unsigned int error; + + unsigned int alaw:1, + lpf:1, + obclamp:1, + fpc_en:1; + struct omap3isp_ccdc_blcomp blcomp; + struct omap3isp_ccdc_bclamp clamp; + struct omap3isp_ccdc_fpc fpc; + struct ispccdc_lsc lsc; + unsigned int update; + unsigned int shadow_update; + + struct ispccdc_syncif syncif; + struct ispccdc_vp vpcfg; + + unsigned int underrun:1; + enum isp_pipeline_stream_state state; + spinlock_t lock; + wait_queue_head_t wait; + unsigned int stopping; + struct mutex ioctl_lock; +}; + +struct isp_device; + +int omap3isp_ccdc_init(struct isp_device *isp); +void omap3isp_ccdc_cleanup(struct isp_device *isp); +int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, + struct v4l2_device *vdev); +void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc); + +int omap3isp_ccdc_busy(struct isp_ccdc_device *isp_ccdc); +int omap3isp_ccdc_isr(struct isp_ccdc_device *isp_ccdc, u32 events); +void omap3isp_ccdc_restore_context(struct isp_device *isp); +void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, + unsigned int *max_rate); + +#endif /* OMAP3_ISP_CCDC_H */ diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c new file mode 100644 index 0000000..baf9374 --- /dev/null +++ b/drivers/media/video/omap3isp/isppreview.c @@ -0,0 +1,2113 @@ +/* + * isppreview.c + * + * TI OMAP3 ISP driver - Preview module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "isppreview.h" + +/* Default values in Office Flourescent Light for RGBtoRGB Blending */ +static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = { + { /* RGB-RGB Matrix */ + {0x01E2, 0x0F30, 0x0FEE}, + {0x0F9B, 0x01AC, 0x0FB9}, + {0x0FE0, 0x0EC0, 0x0260} + }, /* RGB Offset */ + {0x0000, 0x0000, 0x0000} +}; + +/* Default values in Office Flourescent Light for RGB to YUV Conversion*/ +static struct omap3isp_prev_csc flr_prev_csc = { + { /* CSC Coef Matrix */ + {66, 129, 25}, + {-38, -75, 112}, + {112, -94 , -18} + }, /* CSC Offset */ + {0x0, 0x0, 0x0} +}; + +/* Default values in Office Flourescent Light for CFA Gradient*/ +#define FLR_CFA_GRADTHRS_HORZ 0x28 +#define FLR_CFA_GRADTHRS_VERT 0x28 + +/* Default values in Office Flourescent Light for Chroma Suppression*/ +#define FLR_CSUP_GAIN 0x0D +#define FLR_CSUP_THRES 0xEB + +/* Default values in Office Flourescent Light for Noise Filter*/ +#define FLR_NF_STRGTH 0x03 + +/* Default values for White Balance */ +#define FLR_WBAL_DGAIN 0x100 +#define FLR_WBAL_COEF 0x20 + +/* Default values in Office Flourescent Light for Black Adjustment*/ +#define FLR_BLKADJ_BLUE 0x0 +#define FLR_BLKADJ_GREEN 0x0 +#define FLR_BLKADJ_RED 0x0 + +#define DEF_DETECT_CORRECT_VAL 0xe + +#define PREV_MIN_WIDTH 64 +#define PREV_MIN_HEIGHT 8 +#define PREV_MAX_HEIGHT 16384 + +/* + * Coeficient Tables for the submodules in Preview. + * Array is initialised with the values from.the tables text file. + */ + +/* + * CFA Filter Coefficient Table + * + */ +static u32 cfa_coef_table[] = { +#include "cfa_coef_table.h" +}; + +/* + * Default Gamma Correction Table - All components + */ +static u32 gamma_table[] = { +#include "gamma_table.h" +}; + +/* + * Noise Filter Threshold table + */ +static u32 noise_filter_table[] = { +#include "noise_filter_table.h" +}; + +/* + * Luminance Enhancement Table + */ +static u32 luma_enhance_table[] = { +#include "luma_enhance_table.h" +}; + +/* + * preview_enable_invalaw - Enable/Disable Inverse A-Law module in Preview. + * @enable: 1 - Reverse the A-Law done in CCDC. + */ +static void +preview_enable_invalaw(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); +} + +/* + * preview_enable_drkframe_capture - Enable/Disable of the darkframe capture. + * @prev - + * @enable: 1 - Enable, 0 - Disable + * + * NOTE: PRV_WSDR_ADDR and PRV_WADD_OFFSET must be set also + * The proccess is applied for each captured frame. + */ +static void +preview_enable_drkframe_capture(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_DRKFCAP); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_DRKFCAP); +} + +/* + * preview_enable_drkframe - Enable/Disable of the darkframe subtract. + * @enable: 1 - Acquires memory bandwidth since the pixels in each frame is + * subtracted with the pixels in the current frame. + * + * The proccess is applied for each captured frame. + */ +static void +preview_enable_drkframe(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_DRKFEN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_DRKFEN); +} + +/* + * preview_config_drkf_shadcomp - Configures shift value in shading comp. + * @scomp_shtval: 3bit value of shift used in shading compensation. + */ +static void +preview_config_drkf_shadcomp(struct isp_prev_device *prev, + const void *scomp_shtval) +{ + struct isp_device *isp = to_isp_device(prev); + const u32 *shtval = scomp_shtval; + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_SCOMP_SFT_MASK, + *shtval << ISPPRV_PCR_SCOMP_SFT_SHIFT); +} + +/* + * preview_enable_hmed - Enables/Disables of the Horizontal Median Filter. + * @enable: 1 - Enables Horizontal Median Filter. + */ +static void +preview_enable_hmed(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_HMEDEN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_HMEDEN); +} + +/* + * preview_config_hmed - Configures the Horizontal Median Filter. + * @prev_hmed: Structure containing the odd and even distance between the + * pixels in the image along with the filter threshold. + */ +static void +preview_config_hmed(struct isp_prev_device *prev, const void *prev_hmed) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_hmed *hmed = prev_hmed; + + isp_reg_writel(isp, (hmed->odddist == 1 ? 0 : ISPPRV_HMED_ODDDIST) | + (hmed->evendist == 1 ? 0 : ISPPRV_HMED_EVENDIST) | + (hmed->thres << ISPPRV_HMED_THRESHOLD_SHIFT), + OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED); +} + +/* + * preview_config_noisefilter - Configures the Noise Filter. + * @prev_nf: Structure containing the noisefilter table, strength to be used + * for the noise filter and the defect correction enable flag. + */ +static void +preview_config_noisefilter(struct isp_prev_device *prev, const void *prev_nf) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_nf *nf = prev_nf; + unsigned int i; + + isp_reg_writel(isp, nf->spread, OMAP3_ISP_IOMEM_PREV, ISPPRV_NF); + isp_reg_writel(isp, ISPPRV_NF_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < OMAP3ISP_PREV_NF_TBL_SIZE; i++) { + isp_reg_writel(isp, nf->table[i], + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); + } +} + +/* + * preview_config_dcor - Configures the defect correction + * @prev_dcor: Structure containing the defect correct thresholds + */ +static void +preview_config_dcor(struct isp_prev_device *prev, const void *prev_dcor) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_dcor *dcor = prev_dcor; + + isp_reg_writel(isp, dcor->detect_correct[0], + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0); + isp_reg_writel(isp, dcor->detect_correct[1], + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1); + isp_reg_writel(isp, dcor->detect_correct[2], + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2); + isp_reg_writel(isp, dcor->detect_correct[3], + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3); + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_DCCOUP, + dcor->couplet_mode_en ? ISPPRV_PCR_DCCOUP : 0); +} + +/* + * preview_config_cfa - Configures the CFA Interpolation parameters. + * @prev_cfa: Structure containing the CFA interpolation table, CFA format + * in the image, vertical and horizontal gradient threshold. + */ +static void +preview_config_cfa(struct isp_prev_device *prev, const void *prev_cfa) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_cfa *cfa = prev_cfa; + unsigned int i; + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAFMT_MASK, + cfa->format << ISPPRV_PCR_CFAFMT_SHIFT); + + isp_reg_writel(isp, + (cfa->gradthrs_vert << ISPPRV_CFA_GRADTH_VER_SHIFT) | + (cfa->gradthrs_horz << ISPPRV_CFA_GRADTH_HOR_SHIFT), + OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA); + + isp_reg_writel(isp, ISPPRV_CFA_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + + for (i = 0; i < OMAP3ISP_PREV_CFA_TBL_SIZE; i++) { + isp_reg_writel(isp, cfa->table[i], + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); + } +} + +/* + * preview_config_gammacorrn - Configures the Gamma Correction table values + * @gtable: Structure containing the table for red, blue, green gamma table. + */ +static void +preview_config_gammacorrn(struct isp_prev_device *prev, const void *gtable) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_gtables *gt = gtable; + unsigned int i; + + isp_reg_writel(isp, ISPPRV_REDGAMMA_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) + isp_reg_writel(isp, gt->red[i], OMAP3_ISP_IOMEM_PREV, + ISPPRV_SET_TBL_DATA); + + isp_reg_writel(isp, ISPPRV_GREENGAMMA_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) + isp_reg_writel(isp, gt->green[i], OMAP3_ISP_IOMEM_PREV, + ISPPRV_SET_TBL_DATA); + + isp_reg_writel(isp, ISPPRV_BLUEGAMMA_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) + isp_reg_writel(isp, gt->blue[i], OMAP3_ISP_IOMEM_PREV, + ISPPRV_SET_TBL_DATA); +} + +/* + * preview_config_luma_enhancement - Sets the Luminance Enhancement table. + * @ytable: Structure containing the table for Luminance Enhancement table. + */ +static void +preview_config_luma_enhancement(struct isp_prev_device *prev, + const void *ytable) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_luma *yt = ytable; + unsigned int i; + + isp_reg_writel(isp, ISPPRV_YENH_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < OMAP3ISP_PREV_YENH_TBL_SIZE; i++) { + isp_reg_writel(isp, yt->table[i], + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); + } +} + +/* + * preview_config_chroma_suppression - Configures the Chroma Suppression. + * @csup: Structure containing the threshold value for suppression + * and the hypass filter enable flag. + */ +static void +preview_config_chroma_suppression(struct isp_prev_device *prev, + const void *csup) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_csup *cs = csup; + + isp_reg_writel(isp, + cs->gain | (cs->thres << ISPPRV_CSUP_THRES_SHIFT) | + (cs->hypf_en << ISPPRV_CSUP_HPYF_SHIFT), + OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP); +} + +/* + * preview_enable_noisefilter - Enables/Disables the Noise Filter. + * @enable: 1 - Enables the Noise Filter. + */ +static void +preview_enable_noisefilter(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_NFEN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_NFEN); +} + +/* + * preview_enable_dcor - Enables/Disables the defect correction. + * @enable: 1 - Enables the defect correction. + */ +static void +preview_enable_dcor(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_DCOREN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_DCOREN); +} + +/* + * preview_enable_cfa - Enable/Disable the CFA Interpolation. + * @enable: 1 - Enables the CFA. + */ +static void +preview_enable_cfa(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAEN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAEN); +} + +/* + * preview_enable_gammabypass - Enables/Disables the GammaByPass + * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB. + * 0 - Goes through Gamma Correction. input and output is 10bit. + */ +static void +preview_enable_gammabypass(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_GAMMA_BYPASS); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_GAMMA_BYPASS); +} + +/* + * preview_enable_luma_enhancement - Enables/Disables Luminance Enhancement + * @enable: 1 - Enable the Luminance Enhancement. + */ +static void +preview_enable_luma_enhancement(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_YNENHEN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_YNENHEN); +} + +/* + * preview_enable_chroma_suppression - Enables/Disables Chrominance Suppr. + * @enable: 1 - Enable the Chrominance Suppression. + */ +static void +preview_enable_chroma_suppression(struct isp_prev_device *prev, u8 enable) +{ + struct isp_device *isp = to_isp_device(prev); + + if (enable) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_SUPEN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_SUPEN); +} + +/* + * preview_config_whitebalance - Configures the White Balance parameters. + * @prev_wbal: Structure containing the digital gain and white balance + * coefficient. + * + * Coefficient matrix always with default values. + */ +static void +preview_config_whitebalance(struct isp_prev_device *prev, const void *prev_wbal) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_wbal *wbal = prev_wbal; + u32 val; + + isp_reg_writel(isp, wbal->dgain, OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN); + + val = wbal->coef0 << ISPPRV_WBGAIN_COEF0_SHIFT; + val |= wbal->coef1 << ISPPRV_WBGAIN_COEF1_SHIFT; + val |= wbal->coef2 << ISPPRV_WBGAIN_COEF2_SHIFT; + val |= wbal->coef3 << ISPPRV_WBGAIN_COEF3_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN); + + isp_reg_writel(isp, + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_0_SHIFT | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_1_SHIFT | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_2_SHIFT | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_3_SHIFT | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_0_SHIFT | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_1_SHIFT | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_2_SHIFT | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_3_SHIFT | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_0_SHIFT | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_1_SHIFT | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_2_SHIFT | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_3_SHIFT | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_0_SHIFT | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_1_SHIFT | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_2_SHIFT | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_3_SHIFT, + OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL); +} + +/* + * preview_config_blkadj - Configures the Black Adjustment parameters. + * @prev_blkadj: Structure containing the black adjustment towards red, green, + * blue. + */ +static void +preview_config_blkadj(struct isp_prev_device *prev, const void *prev_blkadj) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_blkadj *blkadj = prev_blkadj; + + isp_reg_writel(isp, (blkadj->blue << ISPPRV_BLKADJOFF_B_SHIFT) | + (blkadj->green << ISPPRV_BLKADJOFF_G_SHIFT) | + (blkadj->red << ISPPRV_BLKADJOFF_R_SHIFT), + OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF); +} + +/* + * preview_config_rgb_blending - Configures the RGB-RGB Blending matrix. + * @rgb2rgb: Structure containing the rgb to rgb blending matrix and the rgb + * offset. + */ +static void +preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_rgbtorgb *rgbrgb = rgb2rgb; + u32 val; + + val = (rgbrgb->matrix[0][0] & 0xfff) << ISPPRV_RGB_MAT1_MTX_RR_SHIFT; + val |= (rgbrgb->matrix[0][1] & 0xfff) << ISPPRV_RGB_MAT1_MTX_GR_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1); + + val = (rgbrgb->matrix[0][2] & 0xfff) << ISPPRV_RGB_MAT2_MTX_BR_SHIFT; + val |= (rgbrgb->matrix[1][0] & 0xfff) << ISPPRV_RGB_MAT2_MTX_RG_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2); + + val = (rgbrgb->matrix[1][1] & 0xfff) << ISPPRV_RGB_MAT3_MTX_GG_SHIFT; + val |= (rgbrgb->matrix[1][2] & 0xfff) << ISPPRV_RGB_MAT3_MTX_BG_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3); + + val = (rgbrgb->matrix[2][0] & 0xfff) << ISPPRV_RGB_MAT4_MTX_RB_SHIFT; + val |= (rgbrgb->matrix[2][1] & 0xfff) << ISPPRV_RGB_MAT4_MTX_GB_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4); + + val = (rgbrgb->matrix[2][2] & 0xfff) << ISPPRV_RGB_MAT5_MTX_BB_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5); + + val = (rgbrgb->offset[0] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT; + val |= (rgbrgb->offset[1] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1); + + val = (rgbrgb->offset[2] & 0x3ff) << ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2); +} + +/* + * Configures the RGB-YCbYCr conversion matrix + * @prev_csc: Structure containing the RGB to YCbYCr matrix and the + * YCbCr offset. + */ +static void +preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_csc *csc = prev_csc; + u32 val; + + val = (csc->matrix[0][0] & 0x3ff) << ISPPRV_CSC0_RY_SHIFT; + val |= (csc->matrix[0][1] & 0x3ff) << ISPPRV_CSC0_GY_SHIFT; + val |= (csc->matrix[0][2] & 0x3ff) << ISPPRV_CSC0_BY_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0); + + val = (csc->matrix[1][0] & 0x3ff) << ISPPRV_CSC1_RCB_SHIFT; + val |= (csc->matrix[1][1] & 0x3ff) << ISPPRV_CSC1_GCB_SHIFT; + val |= (csc->matrix[1][2] & 0x3ff) << ISPPRV_CSC1_BCB_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1); + + val = (csc->matrix[2][0] & 0x3ff) << ISPPRV_CSC2_RCR_SHIFT; + val |= (csc->matrix[2][1] & 0x3ff) << ISPPRV_CSC2_GCR_SHIFT; + val |= (csc->matrix[2][2] & 0x3ff) << ISPPRV_CSC2_BCR_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2); + + val = (csc->offset[0] & 0xff) << ISPPRV_CSC_OFFSET_Y_SHIFT; + val |= (csc->offset[1] & 0xff) << ISPPRV_CSC_OFFSET_CB_SHIFT; + val |= (csc->offset[2] & 0xff) << ISPPRV_CSC_OFFSET_CR_SHIFT; + isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET); +} + +/* + * preview_update_contrast - Updates the contrast. + * @contrast: Pointer to hold the current programmed contrast value. + * + * Value should be programmed before enabling the module. + */ +static void +preview_update_contrast(struct isp_prev_device *prev, u8 contrast) +{ + struct prev_params *params = &prev->params; + + if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) { + params->contrast = contrast * ISPPRV_CONTRAST_UNITS; + prev->update |= PREV_CONTRAST; + } +} + +/* + * preview_config_contrast - Configures the Contrast. + * @params: Contrast value (u8 pointer, U8Q0 format). + * + * Value should be programmed before enabling the module. + */ +static void +preview_config_contrast(struct isp_prev_device *prev, const void *params) +{ + struct isp_device *isp = to_isp_device(prev); + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, + 0xff << ISPPRV_CNT_BRT_CNT_SHIFT, + *(u8 *)params << ISPPRV_CNT_BRT_CNT_SHIFT); +} + +/* + * preview_update_brightness - Updates the brightness in preview module. + * @brightness: Pointer to hold the current programmed brightness value. + * + */ +static void +preview_update_brightness(struct isp_prev_device *prev, u8 brightness) +{ + struct prev_params *params = &prev->params; + + if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) { + params->brightness = brightness * ISPPRV_BRIGHT_UNITS; + prev->update |= PREV_BRIGHTNESS; + } +} + +/* + * preview_config_brightness - Configures the brightness. + * @params: Brightness value (u8 pointer, U8Q0 format). + */ +static void +preview_config_brightness(struct isp_prev_device *prev, const void *params) +{ + struct isp_device *isp = to_isp_device(prev); + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, + 0xff << ISPPRV_CNT_BRT_BRT_SHIFT, + *(u8 *)params << ISPPRV_CNT_BRT_BRT_SHIFT); +} + +/* + * preview_config_yc_range - Configures the max and min Y and C values. + * @yclimit: Structure containing the range of Y and C values. + */ +static void +preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) +{ + struct isp_device *isp = to_isp_device(prev); + const struct omap3isp_prev_yclimit *yc = yclimit; + + isp_reg_writel(isp, + yc->maxC << ISPPRV_SETUP_YC_MAXC_SHIFT | + yc->maxY << ISPPRV_SETUP_YC_MAXY_SHIFT | + yc->minC << ISPPRV_SETUP_YC_MINC_SHIFT | + yc->minY << ISPPRV_SETUP_YC_MINY_SHIFT, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC); +} + +/* preview parameters update structure */ +struct preview_update { + int cfg_bit; + int feature_bit; + void (*config)(struct isp_prev_device *, const void *); + void (*enable)(struct isp_prev_device *, u8); +}; + +static struct preview_update update_attrs[] = { + {OMAP3ISP_PREV_LUMAENH, PREV_LUMA_ENHANCE, + preview_config_luma_enhancement, + preview_enable_luma_enhancement}, + {OMAP3ISP_PREV_INVALAW, PREV_INVERSE_ALAW, + NULL, + preview_enable_invalaw}, + {OMAP3ISP_PREV_HRZ_MED, PREV_HORZ_MEDIAN_FILTER, + preview_config_hmed, + preview_enable_hmed}, + {OMAP3ISP_PREV_CFA, PREV_CFA, + preview_config_cfa, + preview_enable_cfa}, + {OMAP3ISP_PREV_CHROMA_SUPP, PREV_CHROMA_SUPPRESS, + preview_config_chroma_suppression, + preview_enable_chroma_suppression}, + {OMAP3ISP_PREV_WB, PREV_WB, + preview_config_whitebalance, + NULL}, + {OMAP3ISP_PREV_BLKADJ, PREV_BLKADJ, + preview_config_blkadj, + NULL}, + {OMAP3ISP_PREV_RGB2RGB, PREV_RGB2RGB, + preview_config_rgb_blending, + NULL}, + {OMAP3ISP_PREV_COLOR_CONV, PREV_COLOR_CONV, + preview_config_rgb_to_ycbcr, + NULL}, + {OMAP3ISP_PREV_YC_LIMIT, PREV_YCLIMITS, + preview_config_yc_range, + NULL}, + {OMAP3ISP_PREV_DEFECT_COR, PREV_DEFECT_COR, + preview_config_dcor, + preview_enable_dcor}, + {OMAP3ISP_PREV_GAMMABYPASS, PREV_GAMMA_BYPASS, + NULL, + preview_enable_gammabypass}, + {OMAP3ISP_PREV_DRK_FRM_CAPTURE, PREV_DARK_FRAME_CAPTURE, + NULL, + preview_enable_drkframe_capture}, + {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, PREV_DARK_FRAME_SUBTRACT, + NULL, + preview_enable_drkframe}, + {OMAP3ISP_PREV_LENS_SHADING, PREV_LENS_SHADING, + preview_config_drkf_shadcomp, + preview_enable_drkframe}, + {OMAP3ISP_PREV_NF, PREV_NOISE_FILTER, + preview_config_noisefilter, + preview_enable_noisefilter}, + {OMAP3ISP_PREV_GAMMA, PREV_GAMMA, + preview_config_gammacorrn, + NULL}, + {-1, PREV_CONTRAST, + preview_config_contrast, + NULL}, + {-1, PREV_BRIGHTNESS, + preview_config_brightness, + NULL}, +}; + +/* + * __preview_get_ptrs - helper function which return pointers to members + * of params and config structures. + * @params - pointer to preview_params structure. + * @param - return pointer to appropriate structure field. + * @configs - pointer to update config structure. + * @config - return pointer to appropriate structure field. + * @bit - for which feature to return pointers. + * Return size of coresponding prev_params member + */ +static u32 +__preview_get_ptrs(struct prev_params *params, void **param, + struct omap3isp_prev_update_config *configs, + void __user **config, u32 bit) +{ +#define CHKARG(cfgs, cfg, field) \ + if (cfgs && cfg) { \ + *(cfg) = (cfgs)->field; \ + } + + switch (bit) { + case PREV_HORZ_MEDIAN_FILTER: + *param = ¶ms->hmed; + CHKARG(configs, config, hmed) + return sizeof(params->hmed); + case PREV_NOISE_FILTER: + *param = ¶ms->nf; + CHKARG(configs, config, nf) + return sizeof(params->nf); + break; + case PREV_CFA: + *param = ¶ms->cfa; + CHKARG(configs, config, cfa) + return sizeof(params->cfa); + case PREV_LUMA_ENHANCE: + *param = ¶ms->luma; + CHKARG(configs, config, luma) + return sizeof(params->luma); + case PREV_CHROMA_SUPPRESS: + *param = ¶ms->csup; + CHKARG(configs, config, csup) + return sizeof(params->csup); + case PREV_DEFECT_COR: + *param = ¶ms->dcor; + CHKARG(configs, config, dcor) + return sizeof(params->dcor); + case PREV_BLKADJ: + *param = ¶ms->blk_adj; + CHKARG(configs, config, blkadj) + return sizeof(params->blk_adj); + case PREV_YCLIMITS: + *param = ¶ms->yclimit; + CHKARG(configs, config, yclimit) + return sizeof(params->yclimit); + case PREV_RGB2RGB: + *param = ¶ms->rgb2rgb; + CHKARG(configs, config, rgb2rgb) + return sizeof(params->rgb2rgb); + case PREV_COLOR_CONV: + *param = ¶ms->rgb2ycbcr; + CHKARG(configs, config, csc) + return sizeof(params->rgb2ycbcr); + case PREV_WB: + *param = ¶ms->wbal; + CHKARG(configs, config, wbal) + return sizeof(params->wbal); + case PREV_GAMMA: + *param = ¶ms->gamma; + CHKARG(configs, config, gamma) + return sizeof(params->gamma); + case PREV_CONTRAST: + *param = ¶ms->contrast; + return 0; + case PREV_BRIGHTNESS: + *param = ¶ms->brightness; + return 0; + default: + *param = NULL; + *config = NULL; + break; + } + return 0; +} + +/* + * preview_config - Copy and update local structure with userspace preview + * configuration. + * @prev: ISP preview engine + * @cfg: Configuration + * + * Return zero if success or -EFAULT if the configuration can't be copied from + * userspace. + */ +static int preview_config(struct isp_prev_device *prev, + struct omap3isp_prev_update_config *cfg) +{ + struct prev_params *params; + struct preview_update *attr; + int i, bit, rval = 0; + + params = &prev->params; + + if (prev->state != ISP_PIPELINE_STREAM_STOPPED) { + unsigned long flags; + + spin_lock_irqsave(&prev->lock, flags); + prev->shadow_update = 1; + spin_unlock_irqrestore(&prev->lock, flags); + } + + for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { + attr = &update_attrs[i]; + bit = 0; + + if (!(cfg->update & attr->cfg_bit)) + continue; + + bit = cfg->flag & attr->cfg_bit; + if (bit) { + void *to = NULL, __user *from = NULL; + unsigned long sz = 0; + + sz = __preview_get_ptrs(params, &to, cfg, &from, + bit); + if (to && from && sz) { + if (copy_from_user(to, from, sz)) { + rval = -EFAULT; + break; + } + } + params->features |= attr->feature_bit; + } else { + params->features &= ~attr->feature_bit; + } + + prev->update |= attr->feature_bit; + } + + prev->shadow_update = 0; + return rval; +} + +/* + * preview_setup_hw - Setup preview registers and/or internal memory + * @prev: pointer to preview private structure + * Note: can be called from interrupt context + * Return none + */ +static void preview_setup_hw(struct isp_prev_device *prev) +{ + struct prev_params *params = &prev->params; + struct preview_update *attr; + int i, bit; + void *param_ptr; + + for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { + attr = &update_attrs[i]; + + if (!(prev->update & attr->feature_bit)) + continue; + bit = params->features & attr->feature_bit; + if (bit) { + if (attr->config) { + __preview_get_ptrs(params, ¶m_ptr, NULL, + NULL, bit); + attr->config(prev, param_ptr); + } + if (attr->enable) + attr->enable(prev, 1); + } else + if (attr->enable) + attr->enable(prev, 0); + + prev->update &= ~attr->feature_bit; + } +} + +/* + * preview_config_ycpos - Configure byte layout of YUV image. + * @mode: Indicates the required byte layout. + */ +static void +preview_config_ycpos(struct isp_prev_device *prev, + enum v4l2_mbus_pixelcode pixelcode) +{ + struct isp_device *isp = to_isp_device(prev); + enum preview_ycpos_mode mode; + + switch (pixelcode) { + case V4L2_MBUS_FMT_YUYV8_1X16: + mode = YCPOS_CrYCbY; + break; + case V4L2_MBUS_FMT_UYVY8_1X16: + mode = YCPOS_YCrYCb; + break; + default: + return; + } + + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_YCPOS_CrYCbY, + mode << ISPPRV_PCR_YCPOS_SHIFT); +} + +/* + * preview_config_averager - Enable / disable / configure averager + * @average: Average value to be configured. + */ +static void preview_config_averager(struct isp_prev_device *prev, u8 average) +{ + struct isp_device *isp = to_isp_device(prev); + int reg = 0; + + if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER) + reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT | + ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT | + average; + else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) + reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT | + ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT | + average; + isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); +} + +/* + * preview_config_input_size - Configure the input frame size + * + * The preview engine crops several rows and columns internally depending on + * which processing blocks are enabled. The driver assumes all those blocks are + * enabled when reporting source pad formats to userspace. If this assumption is + * not true, rows and columns must be manually cropped at the preview engine + * input to avoid overflows at the end of lines and frames. + */ +static void preview_config_input_size(struct isp_prev_device *prev) +{ + struct isp_device *isp = to_isp_device(prev); + struct prev_params *params = &prev->params; + struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK]; + unsigned int sph = 0; + unsigned int eph = format->width - 1; + unsigned int slv = 0; + unsigned int elv = format->height - 1; + + if (prev->input == PREVIEW_INPUT_CCDC) { + sph += 2; + eph -= 2; + } + + /* + * Median filter 4 pixels + * Noise filter 4 pixels, 4 lines + * or faulty pixels correction + * CFA filter 4 pixels, 4 lines in Bayer mode + * 2 lines in other modes + * Color suppression 2 pixels + * or luma enhancement + * ------------------------------------------------------------- + * Maximum total 14 pixels, 8 lines + */ + + if (!(params->features & PREV_CFA)) { + sph += 2; + eph -= 2; + slv += 2; + elv -= 2; + } + if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) { + sph += 2; + eph -= 2; + slv += 2; + elv -= 2; + } + if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) { + sph += 2; + eph -= 2; + } + if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))) + sph += 2; + + isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, + OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO); + isp_reg_writel(isp, (slv << ISPPRV_VERT_INFO_SLV_SHIFT) | elv, + OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO); +} + +/* + * preview_config_inlineoffset - Configures the Read address line offset. + * @prev: Preview module + * @offset: Line offset + * + * According to the TRM, the line offset must be aligned on a 32 bytes boundary. + * However, a hardware bug requires the memory start address to be aligned on a + * 64 bytes boundary, so the offset probably should be aligned on 64 bytes as + * well. + */ +static void +preview_config_inlineoffset(struct isp_prev_device *prev, u32 offset) +{ + struct isp_device *isp = to_isp_device(prev); + + isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RADR_OFFSET); +} + +/* + * preview_set_inaddr - Sets memory address of input frame. + * @addr: 32bit memory address aligned on 32byte boundary. + * + * Configures the memory address from which the input frame is to be read. + */ +static void preview_set_inaddr(struct isp_prev_device *prev, u32 addr) +{ + struct isp_device *isp = to_isp_device(prev); + + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR); +} + +/* + * preview_config_outlineoffset - Configures the Write address line offset. + * @offset: Line Offset for the preview output. + * + * The offset must be a multiple of 32 bytes. + */ +static void preview_config_outlineoffset(struct isp_prev_device *prev, + u32 offset) +{ + struct isp_device *isp = to_isp_device(prev); + + isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WADD_OFFSET); +} + +/* + * preview_set_outaddr - Sets the memory address to store output frame + * @addr: 32bit memory address aligned on 32byte boundary. + * + * Configures the memory address to which the output frame is written. + */ +static void preview_set_outaddr(struct isp_prev_device *prev, u32 addr) +{ + struct isp_device *isp = to_isp_device(prev); + + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR); +} + +static void preview_adjust_bandwidth(struct isp_prev_device *prev) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity); + struct isp_device *isp = to_isp_device(prev); + const struct v4l2_mbus_framefmt *ifmt = &prev->formats[PREV_PAD_SINK]; + unsigned long l3_ick = pipe->l3_ick; + struct v4l2_fract *timeperframe; + unsigned int cycles_per_frame; + unsigned int requests_per_frame; + unsigned int cycles_per_request; + unsigned int minimum; + unsigned int maximum; + unsigned int value; + + if (prev->input != PREVIEW_INPUT_MEMORY) { + isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, + ISPSBL_SDR_REQ_PRV_EXP_MASK); + return; + } + + /* Compute the minimum number of cycles per request, based on the + * pipeline maximum data rate. This is an absolute lower bound if we + * don't want SBL overflows, so round the value up. + */ + cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1, + pipe->max_rate); + minimum = DIV_ROUND_UP(cycles_per_request, 32); + + /* Compute the maximum number of cycles per request, based on the + * requested frame rate. This is a soft upper bound to achieve a frame + * rate equal or higher than the requested value, so round the value + * down. + */ + timeperframe = &pipe->max_timeperframe; + + requests_per_frame = DIV_ROUND_UP(ifmt->width * 2, 256) * ifmt->height; + cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator, + timeperframe->denominator); + cycles_per_request = cycles_per_frame / requests_per_frame; + + maximum = cycles_per_request / 32; + + value = max(minimum, maximum); + + dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value); + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, + ISPSBL_SDR_REQ_PRV_EXP_MASK, + value << ISPSBL_SDR_REQ_PRV_EXP_SHIFT); +} + +/* + * omap3isp_preview_busy - Gets busy state of preview module. + */ +int omap3isp_preview_busy(struct isp_prev_device *prev) +{ + struct isp_device *isp = to_isp_device(prev); + + return isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR) + & ISPPRV_PCR_BUSY; +} + +/* + * omap3isp_preview_restore_context - Restores the values of preview registers + */ +void omap3isp_preview_restore_context(struct isp_device *isp) +{ + isp->isp_prev.update = PREV_FEATURES_END - 1; + preview_setup_hw(&isp->isp_prev); +} + +/* + * preview_print_status - Dump preview module registers to the kernel log + */ +#define PREV_PRINT_REGISTER(isp, name)\ + dev_dbg(isp->dev, "###PRV " #name "=0x%08x\n", \ + isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_##name)) + +static void preview_print_status(struct isp_prev_device *prev) +{ + struct isp_device *isp = to_isp_device(prev); + + dev_dbg(isp->dev, "-------------Preview Register dump----------\n"); + + PREV_PRINT_REGISTER(isp, PCR); + PREV_PRINT_REGISTER(isp, HORZ_INFO); + PREV_PRINT_REGISTER(isp, VERT_INFO); + PREV_PRINT_REGISTER(isp, RSDR_ADDR); + PREV_PRINT_REGISTER(isp, RADR_OFFSET); + PREV_PRINT_REGISTER(isp, DSDR_ADDR); + PREV_PRINT_REGISTER(isp, DRKF_OFFSET); + PREV_PRINT_REGISTER(isp, WSDR_ADDR); + PREV_PRINT_REGISTER(isp, WADD_OFFSET); + PREV_PRINT_REGISTER(isp, AVE); + PREV_PRINT_REGISTER(isp, HMED); + PREV_PRINT_REGISTER(isp, NF); + PREV_PRINT_REGISTER(isp, WB_DGAIN); + PREV_PRINT_REGISTER(isp, WBGAIN); + PREV_PRINT_REGISTER(isp, WBSEL); + PREV_PRINT_REGISTER(isp, CFA); + PREV_PRINT_REGISTER(isp, BLKADJOFF); + PREV_PRINT_REGISTER(isp, RGB_MAT1); + PREV_PRINT_REGISTER(isp, RGB_MAT2); + PREV_PRINT_REGISTER(isp, RGB_MAT3); + PREV_PRINT_REGISTER(isp, RGB_MAT4); + PREV_PRINT_REGISTER(isp, RGB_MAT5); + PREV_PRINT_REGISTER(isp, RGB_OFF1); + PREV_PRINT_REGISTER(isp, RGB_OFF2); + PREV_PRINT_REGISTER(isp, CSC0); + PREV_PRINT_REGISTER(isp, CSC1); + PREV_PRINT_REGISTER(isp, CSC2); + PREV_PRINT_REGISTER(isp, CSC_OFFSET); + PREV_PRINT_REGISTER(isp, CNT_BRT); + PREV_PRINT_REGISTER(isp, CSUP); + PREV_PRINT_REGISTER(isp, SETUP_YC); + PREV_PRINT_REGISTER(isp, SET_TBL_ADDR); + PREV_PRINT_REGISTER(isp, CDC_THR0); + PREV_PRINT_REGISTER(isp, CDC_THR1); + PREV_PRINT_REGISTER(isp, CDC_THR2); + PREV_PRINT_REGISTER(isp, CDC_THR3); + + dev_dbg(isp->dev, "--------------------------------------------\n"); +} + +/* + * preview_init_params - init image processing parameters. + * @prev: pointer to previewer private structure + * return none + */ +static void preview_init_params(struct isp_prev_device *prev) +{ + struct prev_params *params = &prev->params; + int i = 0; + + /* Init values */ + params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS; + params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS; + params->average = NO_AVE; + params->cfa.format = OMAP3ISP_CFAFMT_BAYER; + memcpy(params->cfa.table, cfa_coef_table, + sizeof(params->cfa.table)); + params->cfa.gradthrs_horz = FLR_CFA_GRADTHRS_HORZ; + params->cfa.gradthrs_vert = FLR_CFA_GRADTHRS_VERT; + params->csup.gain = FLR_CSUP_GAIN; + params->csup.thres = FLR_CSUP_THRES; + params->csup.hypf_en = 0; + memcpy(params->luma.table, luma_enhance_table, + sizeof(params->luma.table)); + params->nf.spread = FLR_NF_STRGTH; + memcpy(params->nf.table, noise_filter_table, sizeof(params->nf.table)); + params->dcor.couplet_mode_en = 1; + for (i = 0; i < OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS; i++) + params->dcor.detect_correct[i] = DEF_DETECT_CORRECT_VAL; + memcpy(params->gamma.blue, gamma_table, sizeof(params->gamma.blue)); + memcpy(params->gamma.green, gamma_table, sizeof(params->gamma.green)); + memcpy(params->gamma.red, gamma_table, sizeof(params->gamma.red)); + params->wbal.dgain = FLR_WBAL_DGAIN; + params->wbal.coef0 = FLR_WBAL_COEF; + params->wbal.coef1 = FLR_WBAL_COEF; + params->wbal.coef2 = FLR_WBAL_COEF; + params->wbal.coef3 = FLR_WBAL_COEF; + params->blk_adj.red = FLR_BLKADJ_RED; + params->blk_adj.green = FLR_BLKADJ_GREEN; + params->blk_adj.blue = FLR_BLKADJ_BLUE; + params->rgb2rgb = flr_rgb2rgb; + params->rgb2ycbcr = flr_prev_csc; + params->yclimit.minC = ISPPRV_YC_MIN; + params->yclimit.maxC = ISPPRV_YC_MAX; + params->yclimit.minY = ISPPRV_YC_MIN; + params->yclimit.maxY = ISPPRV_YC_MAX; + + params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER + | PREV_GAMMA | PREV_BLKADJ | PREV_YCLIMITS + | PREV_RGB2RGB | PREV_COLOR_CONV | PREV_WB + | PREV_BRIGHTNESS | PREV_CONTRAST; + + prev->update = PREV_FEATURES_END - 1; +} + +/* + * preview_max_out_width - Handle previewer hardware ouput limitations + * @isp_revision : ISP revision + * returns maximum width output for current isp revision + */ +static unsigned int preview_max_out_width(struct isp_prev_device *prev) +{ + struct isp_device *isp = to_isp_device(prev); + + switch (isp->revision) { + case ISP_REVISION_1_0: + return ISPPRV_MAXOUTPUT_WIDTH; + + case ISP_REVISION_2_0: + default: + return ISPPRV_MAXOUTPUT_WIDTH_ES2; + + case ISP_REVISION_15_0: + return ISPPRV_MAXOUTPUT_WIDTH_3630; + } +} + +static void preview_configure(struct isp_prev_device *prev) +{ + struct isp_device *isp = to_isp_device(prev); + struct v4l2_mbus_framefmt *format; + unsigned int max_out_width; + unsigned int format_avg; + + preview_setup_hw(prev); + + if (prev->output & PREVIEW_OUTPUT_MEMORY) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_SDRPORT); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_SDRPORT); + + if (prev->output & PREVIEW_OUTPUT_RESIZER) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_RSZPORT); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_RSZPORT); + + /* PREV_PAD_SINK */ + format = &prev->formats[PREV_PAD_SINK]; + + preview_adjust_bandwidth(prev); + + preview_config_input_size(prev); + + if (prev->input == PREVIEW_INPUT_CCDC) + preview_config_inlineoffset(prev, 0); + else + preview_config_inlineoffset(prev, + ALIGN(format->width, 0x20) * 2); + + /* PREV_PAD_SOURCE */ + format = &prev->formats[PREV_PAD_SOURCE]; + + if (prev->output & PREVIEW_OUTPUT_MEMORY) + preview_config_outlineoffset(prev, + ALIGN(format->width, 0x10) * 2); + + max_out_width = preview_max_out_width(prev); + + format_avg = fls(DIV_ROUND_UP(format->width, max_out_width) - 1); + preview_config_averager(prev, format_avg); + preview_config_ycpos(prev, format->code); +} + +/* ----------------------------------------------------------------------------- + * Interrupt handling + */ + +static void preview_enable_oneshot(struct isp_prev_device *prev) +{ + struct isp_device *isp = to_isp_device(prev); + + /* The PCR.SOURCE bit is automatically reset to 0 when the PCR.ENABLE + * bit is set. As the preview engine is used in single-shot mode, we + * need to set PCR.SOURCE before enabling the preview engine. + */ + if (prev->input == PREVIEW_INPUT_MEMORY) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_SOURCE); + + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_EN | ISPPRV_PCR_ONESHOT); +} + +void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev) +{ + /* + * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun + * condition, the module was paused and now we have a buffer queued + * on the output again. Restart the pipeline if running in continuous + * mode. + */ + if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS && + prev->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { + preview_enable_oneshot(prev); + isp_video_dmaqueue_flags_clr(&prev->video_out); + } +} + +static void preview_isr_buffer(struct isp_prev_device *prev) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity); + struct isp_buffer *buffer; + int restart = 0; + + if (prev->input == PREVIEW_INPUT_MEMORY) { + buffer = omap3isp_video_buffer_next(&prev->video_in, + prev->error); + if (buffer != NULL) + preview_set_inaddr(prev, buffer->isp_addr); + pipe->state |= ISP_PIPELINE_IDLE_INPUT; + } + + if (prev->output & PREVIEW_OUTPUT_MEMORY) { + buffer = omap3isp_video_buffer_next(&prev->video_out, + prev->error); + if (buffer != NULL) { + preview_set_outaddr(prev, buffer->isp_addr); + restart = 1; + } + pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; + } + + switch (prev->state) { + case ISP_PIPELINE_STREAM_SINGLESHOT: + if (isp_pipeline_ready(pipe)) + omap3isp_pipeline_set_stream(pipe, + ISP_PIPELINE_STREAM_SINGLESHOT); + break; + + case ISP_PIPELINE_STREAM_CONTINUOUS: + /* If an underrun occurs, the video queue operation handler will + * restart the preview engine. Otherwise restart it immediately. + */ + if (restart) + preview_enable_oneshot(prev); + break; + + case ISP_PIPELINE_STREAM_STOPPED: + default: + return; + } + + prev->error = 0; +} + +/* + * omap3isp_preview_isr - ISP preview engine interrupt handler + * + * Manage the preview engine video buffers and configure shadowed registers. + */ +void omap3isp_preview_isr(struct isp_prev_device *prev) +{ + unsigned long flags; + + if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping)) + return; + + spin_lock_irqsave(&prev->lock, flags); + if (prev->shadow_update) + goto done; + + preview_setup_hw(prev); + preview_config_input_size(prev); + +done: + spin_unlock_irqrestore(&prev->lock, flags); + + if (prev->input == PREVIEW_INPUT_MEMORY || + prev->output & PREVIEW_OUTPUT_MEMORY) + preview_isr_buffer(prev); + else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS) + preview_enable_oneshot(prev); +} + +/* ----------------------------------------------------------------------------- + * ISP video operations + */ + +static int preview_video_queue(struct isp_video *video, + struct isp_buffer *buffer) +{ + struct isp_prev_device *prev = &video->isp->isp_prev; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + preview_set_inaddr(prev, buffer->isp_addr); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + preview_set_outaddr(prev, buffer->isp_addr); + + return 0; +} + +static const struct isp_video_operations preview_video_ops = { + .queue = preview_video_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +/* + * preview_s_ctrl - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + */ +static int preview_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isp_prev_device *prev = + container_of(ctrl->handler, struct isp_prev_device, ctrls); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + preview_update_brightness(prev, ctrl->val); + break; + case V4L2_CID_CONTRAST: + preview_update_contrast(prev, ctrl->val); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops preview_ctrl_ops = { + .s_ctrl = preview_s_ctrl, +}; + +/* + * preview_ioctl - Handle preview module private ioctl's + * @prev: pointer to preview context structure + * @cmd: configuration command + * @arg: configuration argument + * return -EINVAL or zero on success + */ +static long preview_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + + switch (cmd) { + case VIDIOC_OMAP3ISP_PRV_CFG: + return preview_config(prev, arg); + + default: + return -ENOIOCTLCMD; + } +} + +/* + * preview_set_stream - Enable/Disable streaming on preview subdev + * @sd : pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + * return -EINVAL or zero on sucess + */ +static int preview_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + struct isp_video *video_out = &prev->video_out; + struct isp_device *isp = to_isp_device(prev); + struct device *dev = to_device(prev); + unsigned long flags; + + if (prev->state == ISP_PIPELINE_STREAM_STOPPED) { + if (enable == ISP_PIPELINE_STREAM_STOPPED) + return 0; + + omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_PREVIEW); + preview_configure(prev); + atomic_set(&prev->stopping, 0); + prev->error = 0; + preview_print_status(prev); + } + + switch (enable) { + case ISP_PIPELINE_STREAM_CONTINUOUS: + if (prev->output & PREVIEW_OUTPUT_MEMORY) + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); + + if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED || + !(prev->output & PREVIEW_OUTPUT_MEMORY)) + preview_enable_oneshot(prev); + + isp_video_dmaqueue_flags_clr(video_out); + break; + + case ISP_PIPELINE_STREAM_SINGLESHOT: + if (prev->input == PREVIEW_INPUT_MEMORY) + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_READ); + if (prev->output & PREVIEW_OUTPUT_MEMORY) + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); + + preview_enable_oneshot(prev); + break; + + case ISP_PIPELINE_STREAM_STOPPED: + if (omap3isp_module_sync_idle(&sd->entity, &prev->wait, + &prev->stopping)) + dev_dbg(dev, "%s: stop timeout.\n", sd->name); + spin_lock_irqsave(&prev->lock, flags); + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ); + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); + omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW); + spin_unlock_irqrestore(&prev->lock, flags); + isp_video_dmaqueue_flags_clr(video_out); + break; + } + + prev->state = enable; + return 0; +} + +static struct v4l2_mbus_framefmt * +__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + else + return &prev->formats[pad]; +} + +/* previewer format descriptions */ +static const unsigned int preview_input_fmts[] = { + V4L2_MBUS_FMT_SGRBG10_1X10, + V4L2_MBUS_FMT_SRGGB10_1X10, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SGBRG10_1X10, +}; + +static const unsigned int preview_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_1X16, + V4L2_MBUS_FMT_YUYV8_1X16, +}; + +/* + * preview_try_format - Handle try format by pad subdev method + * @prev: ISP preview device + * @fh : V4L2 subdev file handle + * @pad: pad num + * @fmt: pointer to v4l2 format structure + */ +static void preview_try_format(struct isp_prev_device *prev, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *format; + unsigned int max_out_width; + enum v4l2_mbus_pixelcode pixelcode; + unsigned int i; + + max_out_width = preview_max_out_width(prev); + + switch (pad) { + case PREV_PAD_SINK: + /* When reading data from the CCDC, the input size has already + * been mangled by the CCDC output pad so it can be accepted + * as-is. + * + * When reading data from memory, clamp the requested width and + * height. The TRM doesn't specify a minimum input height, make + * sure we got enough lines to enable the noise filter and color + * filter array interpolation. + */ + if (prev->input == PREVIEW_INPUT_MEMORY) { + fmt->width = clamp_t(u32, fmt->width, PREV_MIN_WIDTH, + max_out_width * 8); + fmt->height = clamp_t(u32, fmt->height, PREV_MIN_HEIGHT, + PREV_MAX_HEIGHT); + } + + fmt->colorspace = V4L2_COLORSPACE_SRGB; + + for (i = 0; i < ARRAY_SIZE(preview_input_fmts); i++) { + if (fmt->code == preview_input_fmts[i]) + break; + } + + /* If not found, use SGRBG10 as default */ + if (i >= ARRAY_SIZE(preview_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + break; + + case PREV_PAD_SOURCE: + pixelcode = fmt->code; + format = __preview_get_format(prev, fh, PREV_PAD_SINK, which); + memcpy(fmt, format, sizeof(*fmt)); + + /* The preview module output size is configurable through the + * input interface (horizontal and vertical cropping) and the + * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In + * spite of this, hardcode the output size to the biggest + * possible value for simplicity reasons. + */ + switch (pixelcode) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_UYVY8_1X16: + fmt->code = pixelcode; + break; + + default: + fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; + break; + } + + /* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped + * from the left and right sides when the input source is the + * CCDC. This seems not to be needed in practice, investigation + * is required. + */ + if (prev->input == PREVIEW_INPUT_CCDC) + fmt->width -= 4; + + /* The preview module can output a maximum of 3312 pixels + * horizontally due to fixed memory-line sizes. Compute the + * horizontal averaging factor accordingly. Note that the limit + * applies to the noise filter and CFA interpolation blocks, so + * it doesn't take cropping by further blocks into account. + * + * ES 1.0 hardware revision is limited to 1280 pixels + * horizontally. + */ + fmt->width >>= fls(DIV_ROUND_UP(fmt->width, max_out_width) - 1); + + /* Assume that all blocks are enabled and crop pixels and lines + * accordingly. See preview_config_input_size() for more + * information. + */ + fmt->width -= 14; + fmt->height -= 8; + + fmt->colorspace = V4L2_COLORSPACE_JPEG; + break; + } + + fmt->field = V4L2_FIELD_NONE; +} + +/* + * preview_enum_mbus_code - Handle pixel format enumeration + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @code : pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int preview_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case PREV_PAD_SINK: + if (code->index >= ARRAY_SIZE(preview_input_fmts)) + return -EINVAL; + + code->code = preview_input_fmts[code->index]; + break; + case PREV_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(preview_output_fmts)) + return -EINVAL; + + code->code = preview_output_fmts[code->index]; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int preview_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * preview_get_format - Handle get format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on sucess + */ +static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __preview_get_format(prev, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +/* + * preview_set_format - Handle set format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __preview_get_format(prev, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + /* Propagate the format from sink to source */ + if (fmt->pad == PREV_PAD_SINK) { + format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, + fmt->which); + *format = fmt->format; + preview_try_format(prev, fh, PREV_PAD_SOURCE, format, + fmt->which); + } + + return 0; +} + +/* + * preview_init_formats - Initialize formats on all pads + * @sd: ISP preview V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int preview_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = PREV_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + format.format.width = 4096; + format.format.height = 4096; + preview_set_format(sd, fh, &format); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops preview_v4l2_core_ops = { + .ioctl = preview_ioctl, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops preview_v4l2_video_ops = { + .s_stream = preview_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = { + .enum_mbus_code = preview_enum_mbus_code, + .enum_frame_size = preview_enum_frame_size, + .get_fmt = preview_get_format, + .set_fmt = preview_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops preview_v4l2_ops = { + .core = &preview_v4l2_core_ops, + .video = &preview_v4l2_video_ops, + .pad = &preview_v4l2_pad_ops, +}; + +/* subdev internal operations */ +static const struct v4l2_subdev_internal_ops preview_v4l2_internal_ops = { + .open = preview_init_formats, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +/* + * preview_link_setup - Setup previewer connections. + * @entity : Pointer to media entity structure + * @local : Pointer to local pad array + * @remote : Pointer to remote pad array + * @flags : Link flags + * return -EINVAL or zero on success + */ +static int preview_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* read from memory */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (prev->input == PREVIEW_INPUT_CCDC) + return -EBUSY; + prev->input = PREVIEW_INPUT_MEMORY; + } else { + if (prev->input == PREVIEW_INPUT_MEMORY) + prev->input = PREVIEW_INPUT_NONE; + } + break; + + case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from ccdc */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (prev->input == PREVIEW_INPUT_MEMORY) + return -EBUSY; + prev->input = PREVIEW_INPUT_CCDC; + } else { + if (prev->input == PREVIEW_INPUT_CCDC) + prev->input = PREVIEW_INPUT_NONE; + } + break; + + /* + * The ISP core doesn't support pipelines with multiple video outputs. + * Revisit this when it will be implemented, and return -EBUSY for now. + */ + + case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (prev->output & ~PREVIEW_OUTPUT_MEMORY) + return -EBUSY; + prev->output |= PREVIEW_OUTPUT_MEMORY; + } else { + prev->output &= ~PREVIEW_OUTPUT_MEMORY; + } + break; + + case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + /* write to resizer */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (prev->output & ~PREVIEW_OUTPUT_RESIZER) + return -EBUSY; + prev->output |= PREVIEW_OUTPUT_RESIZER; + } else { + prev->output &= ~PREVIEW_OUTPUT_RESIZER; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* media operations */ +static const struct media_entity_operations preview_media_ops = { + .link_setup = preview_link_setup, +}; + +/* + * review_init_entities - Initialize subdev and media entity. + * @prev : Pointer to preview structure + * return -ENOMEM or zero on success + */ +static int preview_init_entities(struct isp_prev_device *prev) +{ + struct v4l2_subdev *sd = &prev->subdev; + struct media_pad *pads = prev->pads; + struct media_entity *me = &sd->entity; + int ret; + + prev->input = PREVIEW_INPUT_NONE; + + v4l2_subdev_init(sd, &preview_v4l2_ops); + sd->internal_ops = &preview_v4l2_internal_ops; + strlcpy(sd->name, "OMAP3 ISP preview", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ + v4l2_set_subdevdata(sd, prev); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&prev->ctrls, 2); + v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_BRIGHTNESS, + ISPPRV_BRIGHT_LOW, ISPPRV_BRIGHT_HIGH, + ISPPRV_BRIGHT_STEP, ISPPRV_BRIGHT_DEF); + v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_CONTRAST, + ISPPRV_CONTRAST_LOW, ISPPRV_CONTRAST_HIGH, + ISPPRV_CONTRAST_STEP, ISPPRV_CONTRAST_DEF); + v4l2_ctrl_handler_setup(&prev->ctrls); + sd->ctrl_handler = &prev->ctrls; + + pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + me->ops = &preview_media_ops; + ret = media_entity_init(me, PREV_PADS_NUM, pads, 0); + if (ret < 0) + return ret; + + preview_init_formats(sd, NULL); + + /* According to the OMAP34xx TRM, video buffers need to be aligned on a + * 32 bytes boundary. However, an undocumented hardware bug requires a + * 64 bytes boundary at the preview engine input. + */ + prev->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + prev->video_in.ops = &preview_video_ops; + prev->video_in.isp = to_isp_device(prev); + prev->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; + prev->video_in.bpl_alignment = 64; + prev->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + prev->video_out.ops = &preview_video_ops; + prev->video_out.isp = to_isp_device(prev); + prev->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; + prev->video_out.bpl_alignment = 32; + + ret = omap3isp_video_init(&prev->video_in, "preview"); + if (ret < 0) + return ret; + + ret = omap3isp_video_init(&prev->video_out, "preview"); + if (ret < 0) + return ret; + + /* Connect the video nodes to the previewer subdev. */ + ret = media_entity_create_link(&prev->video_in.video.entity, 0, + &prev->subdev.entity, PREV_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE, + &prev->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + return 0; +} + +void omap3isp_preview_unregister_entities(struct isp_prev_device *prev) +{ + media_entity_cleanup(&prev->subdev.entity); + + v4l2_device_unregister_subdev(&prev->subdev); + v4l2_ctrl_handler_free(&prev->ctrls); + omap3isp_video_unregister(&prev->video_in); + omap3isp_video_unregister(&prev->video_out); +} + +int omap3isp_preview_register_entities(struct isp_prev_device *prev, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video nodes. */ + ret = v4l2_device_register_subdev(vdev, &prev->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&prev->video_in, vdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&prev->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_preview_unregister_entities(prev); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP previewer initialisation and cleanup + */ + +void omap3isp_preview_cleanup(struct isp_device *isp) +{ +} + +/* + * isp_preview_init - Previewer initialization. + * @dev : Pointer to ISP device + * return -ENOMEM or zero on success + */ +int omap3isp_preview_init(struct isp_device *isp) +{ + struct isp_prev_device *prev = &isp->isp_prev; + int ret; + + spin_lock_init(&prev->lock); + init_waitqueue_head(&prev->wait); + preview_init_params(prev); + + ret = preview_init_entities(prev); + if (ret < 0) + goto out; + +out: + if (ret) + omap3isp_preview_cleanup(isp); + + return ret; +} diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h new file mode 100644 index 0000000..f2d63ca --- /dev/null +++ b/drivers/media/video/omap3isp/isppreview.h @@ -0,0 +1,214 @@ +/* + * isppreview.h + * + * TI OMAP3 ISP - Preview module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_PREVIEW_H +#define OMAP3_ISP_PREVIEW_H + +#include +#include +#include + +#include "ispvideo.h" + +#define ISPPRV_BRIGHT_STEP 0x1 +#define ISPPRV_BRIGHT_DEF 0x0 +#define ISPPRV_BRIGHT_LOW 0x0 +#define ISPPRV_BRIGHT_HIGH 0xFF +#define ISPPRV_BRIGHT_UNITS 0x1 + +#define ISPPRV_CONTRAST_STEP 0x1 +#define ISPPRV_CONTRAST_DEF 0x10 +#define ISPPRV_CONTRAST_LOW 0x0 +#define ISPPRV_CONTRAST_HIGH 0xFF +#define ISPPRV_CONTRAST_UNITS 0x1 + +#define NO_AVE 0x0 +#define AVE_2_PIX 0x1 +#define AVE_4_PIX 0x2 +#define AVE_8_PIX 0x3 + +/* Features list */ +#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH +#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW +#define PREV_HORZ_MEDIAN_FILTER OMAP3ISP_PREV_HRZ_MED +#define PREV_CFA OMAP3ISP_PREV_CFA +#define PREV_CHROMA_SUPPRESS OMAP3ISP_PREV_CHROMA_SUPP +#define PREV_WB OMAP3ISP_PREV_WB +#define PREV_BLKADJ OMAP3ISP_PREV_BLKADJ +#define PREV_RGB2RGB OMAP3ISP_PREV_RGB2RGB +#define PREV_COLOR_CONV OMAP3ISP_PREV_COLOR_CONV +#define PREV_YCLIMITS OMAP3ISP_PREV_YC_LIMIT +#define PREV_DEFECT_COR OMAP3ISP_PREV_DEFECT_COR +#define PREV_GAMMA_BYPASS OMAP3ISP_PREV_GAMMABYPASS +#define PREV_DARK_FRAME_CAPTURE OMAP3ISP_PREV_DRK_FRM_CAPTURE +#define PREV_DARK_FRAME_SUBTRACT OMAP3ISP_PREV_DRK_FRM_SUBTRACT +#define PREV_LENS_SHADING OMAP3ISP_PREV_LENS_SHADING +#define PREV_NOISE_FILTER OMAP3ISP_PREV_NF +#define PREV_GAMMA OMAP3ISP_PREV_GAMMA + +#define PREV_CONTRAST (1 << 17) +#define PREV_BRIGHTNESS (1 << 18) +#define PREV_AVERAGER (1 << 19) +#define PREV_FEATURES_END (1 << 20) + +enum preview_input_entity { + PREVIEW_INPUT_NONE, + PREVIEW_INPUT_CCDC, + PREVIEW_INPUT_MEMORY, +}; + +#define PREVIEW_OUTPUT_RESIZER (1 << 1) +#define PREVIEW_OUTPUT_MEMORY (1 << 2) + +/* Configure byte layout of YUV image */ +enum preview_ycpos_mode { + YCPOS_YCrYCb = 0, + YCPOS_YCbYCr = 1, + YCPOS_CbYCrY = 2, + YCPOS_CrYCbY = 3 +}; + +/* + * struct prev_params - Structure for all configuration + * @features: Set of features enabled. + * @cfa: CFA coefficients. + * @csup: Chroma suppression coefficients. + * @luma: Luma enhancement coefficients. + * @nf: Noise filter coefficients. + * @dcor: Noise filter coefficients. + * @gamma: Gamma coefficients. + * @wbal: White Balance parameters. + * @blk_adj: Black adjustment parameters. + * @rgb2rgb: RGB blending parameters. + * @rgb2ycbcr: RGB to ycbcr parameters. + * @hmed: Horizontal median filter. + * @yclimit: YC limits parameters. + * @average: Downsampling rate for averager. + * @contrast: Contrast. + * @brightness: Brightness. + */ +struct prev_params { + u32 features; + struct omap3isp_prev_cfa cfa; + struct omap3isp_prev_csup csup; + struct omap3isp_prev_luma luma; + struct omap3isp_prev_nf nf; + struct omap3isp_prev_dcor dcor; + struct omap3isp_prev_gtables gamma; + struct omap3isp_prev_wbal wbal; + struct omap3isp_prev_blkadj blk_adj; + struct omap3isp_prev_rgbtorgb rgb2rgb; + struct omap3isp_prev_csc rgb2ycbcr; + struct omap3isp_prev_hmed hmed; + struct omap3isp_prev_yclimit yclimit; + u8 average; + u8 contrast; + u8 brightness; +}; + +/* + * struct isptables_update - Structure for Table Configuration. + * @update: Specifies which tables should be updated. + * @flag: Specifies which tables should be enabled. + * @nf: Pointer to structure for Noise Filter + * @lsc: Pointer to LSC gain table. (currently not used) + * @gamma: Pointer to gamma correction tables. + * @cfa: Pointer to color filter array configuration. + * @wbal: Pointer to colour and digital gain configuration. + */ +struct isptables_update { + u32 update; + u32 flag; + struct omap3isp_prev_nf *nf; + u32 *lsc; + struct omap3isp_prev_gtables *gamma; + struct omap3isp_prev_cfa *cfa; + struct omap3isp_prev_wbal *wbal; +}; + +/* Sink and source previewer pads */ +#define PREV_PAD_SINK 0 +#define PREV_PAD_SOURCE 1 +#define PREV_PADS_NUM 2 + +/* + * struct isp_prev_device - Structure for storing ISP Preview module information + * @subdev: V4L2 subdevice + * @pads: Media entity pads + * @formats: Active formats at the subdev pad + * @input: Module currently connected to the input pad + * @output: Bitmask of the active output + * @video_in: Input video entity + * @video_out: Output video entity + * @error: A hardware error occured during capture + * @params: Module configuration data + * @shadow_update: If set, update the hardware configured in the next interrupt + * @underrun: Whether the preview entity has queued buffers on the output + * @state: Current preview pipeline state + * @lock: Shadow update lock + * @update: Bitmask of the parameters to be updated + * + * This structure is used to store the OMAP ISP Preview module Information. + */ +struct isp_prev_device { + struct v4l2_subdev subdev; + struct media_pad pads[PREV_PADS_NUM]; + struct v4l2_mbus_framefmt formats[PREV_PADS_NUM]; + + struct v4l2_ctrl_handler ctrls; + + enum preview_input_entity input; + unsigned int output; + struct isp_video video_in; + struct isp_video video_out; + unsigned int error; + + struct prev_params params; + unsigned int shadow_update:1; + enum isp_pipeline_stream_state state; + wait_queue_head_t wait; + atomic_t stopping; + spinlock_t lock; + u32 update; +}; + +struct isp_device; + +int omap3isp_preview_init(struct isp_device *isp); +void omap3isp_preview_cleanup(struct isp_device *isp); + +int omap3isp_preview_register_entities(struct isp_prev_device *prv, + struct v4l2_device *vdev); +void omap3isp_preview_unregister_entities(struct isp_prev_device *prv); + +void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev); +void omap3isp_preview_isr(struct isp_prev_device *prev); + +int omap3isp_preview_busy(struct isp_prev_device *isp_prev); + +void omap3isp_preview_restore_context(struct isp_device *isp); + +#endif /* OMAP3_ISP_PREVIEW_H */ diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c new file mode 100644 index 0000000..75d39b1 --- /dev/null +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -0,0 +1,1693 @@ +/* + * ispresizer.c + * + * TI OMAP3 ISP - Resizer module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispresizer.h" + +/* + * Resizer Constants + */ +#define MIN_RESIZE_VALUE 64 +#define MID_RESIZE_VALUE 512 +#define MAX_RESIZE_VALUE 1024 + +#define MIN_IN_WIDTH 32 +#define MIN_IN_HEIGHT 32 +#define MAX_IN_WIDTH_MEMORY_MODE 4095 +#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES1 1280 +#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2 4095 +#define MAX_IN_HEIGHT 4095 + +#define MIN_OUT_WIDTH 16 +#define MIN_OUT_HEIGHT 2 +#define MAX_OUT_HEIGHT 4095 + +/* + * Resizer Use Constraints + * "TRM ES3.1, table 12-46" + */ +#define MAX_4TAP_OUT_WIDTH_ES1 1280 +#define MAX_7TAP_OUT_WIDTH_ES1 640 +#define MAX_4TAP_OUT_WIDTH_ES2 3312 +#define MAX_7TAP_OUT_WIDTH_ES2 1650 +#define MAX_4TAP_OUT_WIDTH_3630 4096 +#define MAX_7TAP_OUT_WIDTH_3630 2048 + +/* + * Constants for ratio calculation + */ +#define RESIZE_DIVISOR 256 +#define DEFAULT_PHASE 1 + +/* + * Default (and only) configuration of filter coefficients. + * 7-tap mode is for scale factors 0.25x to 0.5x. + * 4-tap mode is for scale factors 0.5x to 4.0x. + * There shouldn't be any reason to recalculate these, EVER. + */ +static const struct isprsz_coef filter_coefs = { + /* For 8-phase 4-tap horizontal filter: */ + { + 0x0000, 0x0100, 0x0000, 0x0000, + 0x03FA, 0x00F6, 0x0010, 0x0000, + 0x03F9, 0x00DB, 0x002C, 0x0000, + 0x03FB, 0x00B3, 0x0053, 0x03FF, + 0x03FD, 0x0082, 0x0084, 0x03FD, + 0x03FF, 0x0053, 0x00B3, 0x03FB, + 0x0000, 0x002C, 0x00DB, 0x03F9, + 0x0000, 0x0010, 0x00F6, 0x03FA + }, + /* For 8-phase 4-tap vertical filter: */ + { + 0x0000, 0x0100, 0x0000, 0x0000, + 0x03FA, 0x00F6, 0x0010, 0x0000, + 0x03F9, 0x00DB, 0x002C, 0x0000, + 0x03FB, 0x00B3, 0x0053, 0x03FF, + 0x03FD, 0x0082, 0x0084, 0x03FD, + 0x03FF, 0x0053, 0x00B3, 0x03FB, + 0x0000, 0x002C, 0x00DB, 0x03F9, + 0x0000, 0x0010, 0x00F6, 0x03FA + }, + /* For 4-phase 7-tap horizontal filter: */ + #define DUMMY 0 + { + 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY, + 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY, + 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY, + 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY + }, + /* For 4-phase 7-tap vertical filter: */ + { + 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY, + 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY, + 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY, + 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY + } + /* + * The dummy padding is required in 7-tap mode because of how the + * registers are arranged physically. + */ + #undef DUMMY +}; + +/* + * __resizer_get_format - helper function for getting resizer format + * @res : pointer to resizer private structure + * @pad : pad number + * @fh : V4L2 subdev file handle + * @which : wanted subdev format + * return zero + */ +static struct v4l2_mbus_framefmt * +__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + else + return &res->formats[pad]; +} + +/* + * __resizer_get_crop - helper function for getting resizer crop rectangle + * @res : pointer to resizer private structure + * @fh : V4L2 subdev file handle + * @which : wanted subdev crop rectangle + */ +static struct v4l2_rect * +__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK); + else + return &res->crop.request; +} + +/* + * resizer_set_filters - Set resizer filters + * @res: Device context. + * @h_coeff: horizontal coefficient + * @v_coeff: vertical coefficient + * Return none + */ +static void resizer_set_filters(struct isp_res_device *res, const u16 *h_coeff, + const u16 *v_coeff) +{ + struct isp_device *isp = to_isp_device(res); + u32 startaddr_h, startaddr_v, tmp_h, tmp_v; + int i; + + startaddr_h = ISPRSZ_HFILT10; + startaddr_v = ISPRSZ_VFILT10; + + for (i = 0; i < COEFF_CNT; i += 2) { + tmp_h = h_coeff[i] | + (h_coeff[i + 1] << ISPRSZ_HFILT_COEF1_SHIFT); + tmp_v = v_coeff[i] | + (v_coeff[i + 1] << ISPRSZ_VFILT_COEF1_SHIFT); + isp_reg_writel(isp, tmp_h, OMAP3_ISP_IOMEM_RESZ, startaddr_h); + isp_reg_writel(isp, tmp_v, OMAP3_ISP_IOMEM_RESZ, startaddr_v); + startaddr_h += 4; + startaddr_v += 4; + } +} + +/* + * resizer_set_bilinear - Chrominance horizontal algorithm select + * @res: Device context. + * @type: Filtering interpolation type. + * + * Filtering that is same as luminance processing is + * intended only for downsampling, and bilinear interpolation + * is intended only for upsampling. + */ +static void resizer_set_bilinear(struct isp_res_device *res, + enum resizer_chroma_algo type) +{ + struct isp_device *isp = to_isp_device(res); + + if (type == RSZ_BILINEAR) + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ISPRSZ_CNT_CBILIN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ISPRSZ_CNT_CBILIN); +} + +/* + * resizer_set_ycpos - Luminance and chrominance order + * @res: Device context. + * @order: order type. + */ +static void resizer_set_ycpos(struct isp_res_device *res, + enum v4l2_mbus_pixelcode pixelcode) +{ + struct isp_device *isp = to_isp_device(res); + + switch (pixelcode) { + case V4L2_MBUS_FMT_YUYV8_1X16: + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ISPRSZ_CNT_YCPOS); + break; + case V4L2_MBUS_FMT_UYVY8_1X16: + isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ISPRSZ_CNT_YCPOS); + break; + default: + return; + } +} + +/* + * resizer_set_phase - Setup horizontal and vertical starting phase + * @res: Device context. + * @h_phase: horizontal phase parameters. + * @v_phase: vertical phase parameters. + * + * Horizontal and vertical phase range is 0 to 7 + */ +static void resizer_set_phase(struct isp_res_device *res, u32 h_phase, + u32 v_phase) +{ + struct isp_device *isp = to_isp_device(res); + u32 rgval = 0; + + rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & + ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK); + rgval |= (h_phase << ISPRSZ_CNT_HSTPH_SHIFT) & ISPRSZ_CNT_HSTPH_MASK; + rgval |= (v_phase << ISPRSZ_CNT_VSTPH_SHIFT) & ISPRSZ_CNT_VSTPH_MASK; + + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT); +} + +/* + * resizer_set_luma - Setup luminance enhancer parameters + * @res: Device context. + * @luma: Structure for luminance enhancer parameters. + * + * Algorithm select: + * 0x0: Disable + * 0x1: [-1 2 -1]/2 high-pass filter + * 0x2: [-1 -2 6 -2 -1]/4 high-pass filter + * + * Maximum gain: + * The data is coded in U4Q4 representation. + * + * Slope: + * The data is coded in U4Q4 representation. + * + * Coring offset: + * The data is coded in U8Q0 representation. + * + * The new luminance value is computed as: + * Y += HPF(Y) x max(GAIN, (HPF(Y) - CORE) x SLOP + 8) >> 4. + */ +static void resizer_set_luma(struct isp_res_device *res, + struct resizer_luma_yenh *luma) +{ + struct isp_device *isp = to_isp_device(res); + u32 rgval = 0; + + rgval = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT) + & ISPRSZ_YENH_ALGO_MASK; + rgval |= (luma->gain << ISPRSZ_YENH_GAIN_SHIFT) + & ISPRSZ_YENH_GAIN_MASK; + rgval |= (luma->slope << ISPRSZ_YENH_SLOP_SHIFT) + & ISPRSZ_YENH_SLOP_MASK; + rgval |= (luma->core << ISPRSZ_YENH_CORE_SHIFT) + & ISPRSZ_YENH_CORE_MASK; + + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH); +} + +/* + * resizer_set_source - Input source select + * @res: Device context. + * @source: Input source type + * + * If this field is set to RESIZER_INPUT_VP, the resizer input is fed from + * Preview/CCDC engine, otherwise from memory. + */ +static void resizer_set_source(struct isp_res_device *res, + enum resizer_input_entity source) +{ + struct isp_device *isp = to_isp_device(res); + + if (source == RESIZER_INPUT_MEMORY) + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ISPRSZ_CNT_INPSRC); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ISPRSZ_CNT_INPSRC); +} + +/* + * resizer_set_ratio - Setup horizontal and vertical resizing value + * @res: Device context. + * @ratio: Structure for ratio parameters. + * + * Resizing range from 64 to 1024 + */ +static void resizer_set_ratio(struct isp_res_device *res, + const struct resizer_ratio *ratio) +{ + struct isp_device *isp = to_isp_device(res); + const u16 *h_filter, *v_filter; + u32 rgval = 0; + + rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & + ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK); + rgval |= ((ratio->horz - 1) << ISPRSZ_CNT_HRSZ_SHIFT) + & ISPRSZ_CNT_HRSZ_MASK; + rgval |= ((ratio->vert - 1) << ISPRSZ_CNT_VRSZ_SHIFT) + & ISPRSZ_CNT_VRSZ_MASK; + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT); + + /* prepare horizontal filter coefficients */ + if (ratio->horz > MID_RESIZE_VALUE) + h_filter = &filter_coefs.h_filter_coef_7tap[0]; + else + h_filter = &filter_coefs.h_filter_coef_4tap[0]; + + /* prepare vertical filter coefficients */ + if (ratio->vert > MID_RESIZE_VALUE) + v_filter = &filter_coefs.v_filter_coef_7tap[0]; + else + v_filter = &filter_coefs.v_filter_coef_4tap[0]; + + resizer_set_filters(res, h_filter, v_filter); +} + +/* + * resizer_set_dst_size - Setup the output height and width + * @res: Device context. + * @width: Output width. + * @height: Output height. + * + * Width : + * The value must be EVEN. + * + * Height: + * The number of bytes written to SDRAM must be + * a multiple of 16-bytes if the vertical resizing factor + * is greater than 1x (upsizing) + */ +static void resizer_set_output_size(struct isp_res_device *res, + u32 width, u32 height) +{ + struct isp_device *isp = to_isp_device(res); + u32 rgval = 0; + + dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height); + rgval = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT) + & ISPRSZ_OUT_SIZE_HORZ_MASK; + rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT) + & ISPRSZ_OUT_SIZE_VERT_MASK; + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE); +} + +/* + * resizer_set_output_offset - Setup memory offset for the output lines. + * @res: Device context. + * @offset: Memory offset. + * + * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte + * boundary; the 5 LSBs are read-only. For optimal use of SDRAM bandwidth, + * the SDRAM line offset must be set on a 256-byte boundary + */ +static void resizer_set_output_offset(struct isp_res_device *res, u32 offset) +{ + struct isp_device *isp = to_isp_device(res); + + isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF); +} + +/* + * resizer_set_start - Setup vertical and horizontal start position + * @res: Device context. + * @left: Horizontal start position. + * @top: Vertical start position. + * + * Vertical start line: + * This field makes sense only when the resizer obtains its input + * from the preview engine/CCDC + * + * Horizontal start pixel: + * Pixels are coded on 16 bits for YUV and 8 bits for color separate data. + * When the resizer gets its input from SDRAM, this field must be set + * to <= 15 for YUV 16-bit data and <= 31 for 8-bit color separate data + */ +static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top) +{ + struct isp_device *isp = to_isp_device(res); + u32 rgval = 0; + + rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT) + & ISPRSZ_IN_START_HORZ_ST_MASK; + rgval |= (top << ISPRSZ_IN_START_VERT_ST_SHIFT) + & ISPRSZ_IN_START_VERT_ST_MASK; + + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START); +} + +/* + * resizer_set_input_size - Setup the input size + * @res: Device context. + * @width: The range is 0 to 4095 pixels + * @height: The range is 0 to 4095 lines + */ +static void resizer_set_input_size(struct isp_res_device *res, + u32 width, u32 height) +{ + struct isp_device *isp = to_isp_device(res); + u32 rgval = 0; + + dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height); + + rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT) + & ISPRSZ_IN_SIZE_HORZ_MASK; + rgval |= (height << ISPRSZ_IN_SIZE_VERT_SHIFT) + & ISPRSZ_IN_SIZE_VERT_MASK; + + isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE); +} + +/* + * resizer_set_src_offs - Setup the memory offset for the input lines + * @res: Device context. + * @offset: Memory offset. + * + * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte + * boundary; the 5 LSBs are read-only. This field must be programmed to be + * 0x0 if the resizer input is from preview engine/CCDC. + */ +static void resizer_set_input_offset(struct isp_res_device *res, u32 offset) +{ + struct isp_device *isp = to_isp_device(res); + + isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF); +} + +/* + * resizer_set_intype - Input type select + * @res: Device context. + * @type: Pixel format type. + */ +static void resizer_set_intype(struct isp_res_device *res, + enum resizer_colors_type type) +{ + struct isp_device *isp = to_isp_device(res); + + if (type == RSZ_COLOR8) + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ISPRSZ_CNT_INPTYP); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ISPRSZ_CNT_INPTYP); +} + +/* + * __resizer_set_inaddr - Helper function for set input address + * @res : pointer to resizer private data structure + * @addr: input address + * return none + */ +static void __resizer_set_inaddr(struct isp_res_device *res, u32 addr) +{ + struct isp_device *isp = to_isp_device(res); + + isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD); +} + +/* + * The data rate at the horizontal resizer output must not exceed half the + * functional clock or 100 MP/s, whichever is lower. According to the TRM + * there's no similar requirement for the vertical resizer output. However + * experience showed that vertical upscaling by 4 leads to SBL overflows (with + * data rates at the resizer output exceeding 300 MP/s). Limiting the resizer + * output data rate to the functional clock or 200 MP/s, whichever is lower, + * seems to get rid of SBL overflows. + * + * The maximum data rate at the output of the horizontal resizer can thus be + * computed with + * + * max intermediate rate <= L3 clock * input height / output height + * max intermediate rate <= L3 clock / 2 + * + * The maximum data rate at the resizer input is then + * + * max input rate <= max intermediate rate * input width / output width + * + * where the input width and height are the resizer input crop rectangle size. + * The TRM doesn't clearly explain if that's a maximum instant data rate or a + * maximum average data rate. + */ +void omap3isp_resizer_max_rate(struct isp_res_device *res, + unsigned int *max_rate) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); + const struct v4l2_mbus_framefmt *ofmt = &res->formats[RESZ_PAD_SOURCE]; + unsigned long limit = min(pipe->l3_ick, 200000000UL); + unsigned long clock; + + clock = div_u64((u64)limit * res->crop.active.height, ofmt->height); + clock = min(clock, limit / 2); + *max_rate = div_u64((u64)clock * res->crop.active.width, ofmt->width); +} + +/* + * When the resizer processes images from memory, the driver must slow down read + * requests on the input to at least comply with the internal data rate + * requirements. If the application real-time requirements can cope with slower + * processing, the resizer can be slowed down even more to put less pressure on + * the overall system. + * + * When the resizer processes images on the fly (either from the CCDC or the + * preview module), the same data rate requirements apply but they can't be + * enforced at the resizer level. The image input module (sensor, CCP2 or + * preview module) must not provide image data faster than the resizer can + * process. + * + * For live image pipelines, the data rate is set by the frame format, size and + * rate. The sensor output frame rate must not exceed the maximum resizer data + * rate. + * + * The resizer slows down read requests by inserting wait cycles in the SBL + * requests. The maximum number of 256-byte requests per second can be computed + * as (the data rate is multiplied by 2 to convert from pixels per second to + * bytes per second) + * + * request per second = data rate * 2 / 256 + * cycles per request = cycles per second / requests per second + * + * The number of cycles per second is controlled by the L3 clock, leading to + * + * cycles per request = L3 frequency / 2 * 256 / data rate + */ +static void resizer_adjust_bandwidth(struct isp_res_device *res) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); + struct isp_device *isp = to_isp_device(res); + unsigned long l3_ick = pipe->l3_ick; + struct v4l2_fract *timeperframe; + unsigned int cycles_per_frame; + unsigned int requests_per_frame; + unsigned int cycles_per_request; + unsigned int granularity; + unsigned int minimum; + unsigned int maximum; + unsigned int value; + + if (res->input != RESIZER_INPUT_MEMORY) { + isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, + ISPSBL_SDR_REQ_RSZ_EXP_MASK); + return; + } + + switch (isp->revision) { + case ISP_REVISION_1_0: + case ISP_REVISION_2_0: + default: + granularity = 1024; + break; + + case ISP_REVISION_15_0: + granularity = 32; + break; + } + + /* Compute the minimum number of cycles per request, based on the + * pipeline maximum data rate. This is an absolute lower bound if we + * don't want SBL overflows, so round the value up. + */ + cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1, + pipe->max_rate); + minimum = DIV_ROUND_UP(cycles_per_request, granularity); + + /* Compute the maximum number of cycles per request, based on the + * requested frame rate. This is a soft upper bound to achieve a frame + * rate equal or higher than the requested value, so round the value + * down. + */ + timeperframe = &pipe->max_timeperframe; + + requests_per_frame = DIV_ROUND_UP(res->crop.active.width * 2, 256) + * res->crop.active.height; + cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator, + timeperframe->denominator); + cycles_per_request = cycles_per_frame / requests_per_frame; + + maximum = cycles_per_request / granularity; + + value = max(minimum, maximum); + + dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value); + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, + ISPSBL_SDR_REQ_RSZ_EXP_MASK, + value << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT); +} + +/* + * omap3isp_resizer_busy - Checks if ISP resizer is busy. + * + * Returns busy field from ISPRSZ_PCR register. + */ +int omap3isp_resizer_busy(struct isp_res_device *res) +{ + struct isp_device *isp = to_isp_device(res); + + return isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & + ISPRSZ_PCR_BUSY; +} + +/* + * resizer_set_inaddr - Sets the memory address of the input frame. + * @addr: 32bit memory address aligned on 32byte boundary. + */ +static void resizer_set_inaddr(struct isp_res_device *res, u32 addr) +{ + res->addr_base = addr; + + /* This will handle crop settings in stream off state */ + if (res->crop_offset) + addr += res->crop_offset & ~0x1f; + + __resizer_set_inaddr(res, addr); +} + +/* + * Configures the memory address to which the output frame is written. + * @addr: 32bit memory address aligned on 32byte boundary. + * Note: For SBL efficiency reasons the address should be on a 256-byte + * boundary. + */ +static void resizer_set_outaddr(struct isp_res_device *res, u32 addr) +{ + struct isp_device *isp = to_isp_device(res); + + /* + * Set output address. This needs to be in its own function + * because it changes often. + */ + isp_reg_writel(isp, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD); +} + +/* + * resizer_print_status - Prints the values of the resizer module registers. + */ +#define RSZ_PRINT_REGISTER(isp, name)\ + dev_dbg(isp->dev, "###RSZ " #name "=0x%08x\n", \ + isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_##name)) + +static void resizer_print_status(struct isp_res_device *res) +{ + struct isp_device *isp = to_isp_device(res); + + dev_dbg(isp->dev, "-------------Resizer Register dump----------\n"); + + RSZ_PRINT_REGISTER(isp, PCR); + RSZ_PRINT_REGISTER(isp, CNT); + RSZ_PRINT_REGISTER(isp, OUT_SIZE); + RSZ_PRINT_REGISTER(isp, IN_START); + RSZ_PRINT_REGISTER(isp, IN_SIZE); + RSZ_PRINT_REGISTER(isp, SDR_INADD); + RSZ_PRINT_REGISTER(isp, SDR_INOFF); + RSZ_PRINT_REGISTER(isp, SDR_OUTADD); + RSZ_PRINT_REGISTER(isp, SDR_OUTOFF); + RSZ_PRINT_REGISTER(isp, YENH); + + dev_dbg(isp->dev, "--------------------------------------------\n"); +} + +/* + * resizer_calc_ratios - Helper function for calculate resizer ratios + * @res: pointer to resizer private data structure + * @input: input frame size + * @output: output frame size + * @ratio : return calculated ratios + * return none + * + * The resizer uses a polyphase sample rate converter. The upsampling filter + * has a fixed number of phases that depend on the resizing ratio. As the ratio + * computation depends on the number of phases, we need to compute a first + * approximation and then refine it. + * + * The input/output/ratio relationship is given by the OMAP34xx TRM: + * + * - 8-phase, 4-tap mode (RSZ = 64 ~ 512) + * iw = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7 + * ih = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4 + * - 4-phase, 7-tap mode (RSZ = 513 ~ 1024) + * iw = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7 + * ih = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7 + * + * iw and ih are the input width and height after cropping. Those equations need + * to be satisfied exactly for the resizer to work correctly. + * + * Reverting the equations, we can compute the resizing ratios with + * + * - 8-phase, 4-tap mode + * hrsz = ((iw - 7) * 256 - 16 - 32 * sph) / (ow - 1) + * vrsz = ((ih - 4) * 256 - 16 - 32 * spv) / (oh - 1) + * - 4-phase, 7-tap mode + * hrsz = ((iw - 7) * 256 - 32 - 64 * sph) / (ow - 1) + * vrsz = ((ih - 7) * 256 - 32 - 64 * spv) / (oh - 1) + * + * The ratios are integer values, and must be rounded down to ensure that the + * cropped input size is not bigger than the uncropped input size. As the ratio + * in 7-tap mode is always smaller than the ratio in 4-tap mode, we can use the + * 7-tap mode equations to compute a ratio approximation. + * + * We first clamp the output size according to the hardware capabilitie to avoid + * auto-cropping the input more than required to satisfy the TRM equations. The + * minimum output size is achieved with a scaling factor of 1024. It is thus + * computed using the 7-tap equations. + * + * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1 + * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1 + * + * Similarly, the maximum output size is achieved with a scaling factor of 64 + * and computed using the 4-tap equations. + * + * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1 + * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1 + * + * The additional +255 term compensates for the round down operation performed + * by the TRM equations when shifting the value right by 8 bits. + * + * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to + * the maximum value guarantees that the ratio value will never be smaller than + * the minimum, but it could still slightly exceed the maximum. Clamping the + * ratio will thus result in a resizing factor slightly larger than the + * requested value. + * + * To accomodate that, and make sure the TRM equations are satisfied exactly, we + * compute the input crop rectangle as the last step. + * + * As if the situation wasn't complex enough, the maximum output width depends + * on the vertical resizing ratio. Fortunately, the output height doesn't + * depend on the horizontal resizing ratio. We can then start by computing the + * output height and the vertical ratio, and then move to computing the output + * width and the horizontal ratio. + */ +static void resizer_calc_ratios(struct isp_res_device *res, + struct v4l2_rect *input, + struct v4l2_mbus_framefmt *output, + struct resizer_ratio *ratio) +{ + struct isp_device *isp = to_isp_device(res); + const unsigned int spv = DEFAULT_PHASE; + const unsigned int sph = DEFAULT_PHASE; + unsigned int upscaled_width; + unsigned int upscaled_height; + unsigned int min_width; + unsigned int min_height; + unsigned int max_width; + unsigned int max_height; + unsigned int width_alignment; + + /* + * Clamp the output height based on the hardware capabilities and + * compute the vertical resizing ratio. + */ + min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1; + min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT); + max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1; + max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT); + output->height = clamp(output->height, min_height, max_height); + + ratio->vert = ((input->height - 7) * 256 - 32 - 64 * spv) + / (output->height - 1); + ratio->vert = clamp_t(unsigned int, ratio->vert, + MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); + + if (ratio->vert <= MID_RESIZE_VALUE) { + upscaled_height = (output->height - 1) * ratio->vert + + 32 * spv + 16; + input->height = (upscaled_height >> 8) + 4; + } else { + upscaled_height = (output->height - 1) * ratio->vert + + 64 * spv + 32; + input->height = (upscaled_height >> 8) + 7; + } + + /* + * Compute the minimum and maximum output widths based on the hardware + * capabilities. The maximum depends on the vertical resizing ratio. + */ + min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1; + min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH); + + if (ratio->vert <= MID_RESIZE_VALUE) { + switch (isp->revision) { + case ISP_REVISION_1_0: + max_width = MAX_4TAP_OUT_WIDTH_ES1; + break; + + case ISP_REVISION_2_0: + default: + max_width = MAX_4TAP_OUT_WIDTH_ES2; + break; + + case ISP_REVISION_15_0: + max_width = MAX_4TAP_OUT_WIDTH_3630; + break; + } + } else { + switch (isp->revision) { + case ISP_REVISION_1_0: + max_width = MAX_7TAP_OUT_WIDTH_ES1; + break; + + case ISP_REVISION_2_0: + default: + max_width = MAX_7TAP_OUT_WIDTH_ES2; + break; + + case ISP_REVISION_15_0: + max_width = MAX_7TAP_OUT_WIDTH_3630; + break; + } + } + max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64 + + 1, max_width); + + /* + * The output width must be even, and must be a multiple of 16 bytes + * when upscaling vertically. Clamp the output width to the valid range. + * Take the alignment into account (the maximum width in 7-tap mode on + * ES2 isn't a multiple of 8) and align the result up to make sure it + * won't be smaller than the minimum. + */ + width_alignment = ratio->vert < 256 ? 8 : 2; + output->width = clamp(output->width, min_width, + max_width & ~(width_alignment - 1)); + output->width = ALIGN(output->width, width_alignment); + + ratio->horz = ((input->width - 7) * 256 - 32 - 64 * sph) + / (output->width - 1); + ratio->horz = clamp_t(unsigned int, ratio->horz, + MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); + + if (ratio->horz <= MID_RESIZE_VALUE) { + upscaled_width = (output->width - 1) * ratio->horz + + 32 * sph + 16; + input->width = (upscaled_width >> 8) + 7; + } else { + upscaled_width = (output->width - 1) * ratio->horz + + 64 * sph + 32; + input->width = (upscaled_width >> 8) + 7; + } +} + +/* + * resizer_set_crop_params - Setup hardware with cropping parameters + * @res : resizer private structure + * @crop_rect : current crop rectangle + * @ratio : resizer ratios + * return none + */ +static void resizer_set_crop_params(struct isp_res_device *res, + const struct v4l2_mbus_framefmt *input, + const struct v4l2_mbus_framefmt *output) +{ + resizer_set_ratio(res, &res->ratio); + + /* Set chrominance horizontal algorithm */ + if (res->ratio.horz >= RESIZE_DIVISOR) + resizer_set_bilinear(res, RSZ_THE_SAME); + else + resizer_set_bilinear(res, RSZ_BILINEAR); + + resizer_adjust_bandwidth(res); + + if (res->input == RESIZER_INPUT_MEMORY) { + /* Calculate additional offset for crop */ + res->crop_offset = (res->crop.active.top * input->width + + res->crop.active.left) * 2; + /* + * Write lowest 4 bits of horizontal pixel offset (in pixels), + * vertical start must be 0. + */ + resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0); + + /* + * Set start (read) address for cropping, in bytes. + * Lowest 5 bits must be zero. + */ + __resizer_set_inaddr(res, + res->addr_base + (res->crop_offset & ~0x1f)); + } else { + /* + * Set vertical start line and horizontal starting pixel. + * If the input is from CCDC/PREV, horizontal start field is + * in bytes (twice number of pixels). + */ + resizer_set_start(res, res->crop.active.left * 2, + res->crop.active.top); + /* Input address and offset must be 0 for preview/ccdc input */ + __resizer_set_inaddr(res, 0); + resizer_set_input_offset(res, 0); + } + + /* Set the input size */ + resizer_set_input_size(res, res->crop.active.width, + res->crop.active.height); +} + +static void resizer_configure(struct isp_res_device *res) +{ + struct v4l2_mbus_framefmt *informat, *outformat; + struct resizer_luma_yenh luma = {0, 0, 0, 0}; + + resizer_set_source(res, res->input); + + informat = &res->formats[RESZ_PAD_SINK]; + outformat = &res->formats[RESZ_PAD_SOURCE]; + + /* RESZ_PAD_SINK */ + if (res->input == RESIZER_INPUT_VP) + resizer_set_input_offset(res, 0); + else + resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2); + + /* YUV422 interleaved, default phase, no luma enhancement */ + resizer_set_intype(res, RSZ_YUV422); + resizer_set_ycpos(res, informat->code); + resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE); + resizer_set_luma(res, &luma); + + /* RESZ_PAD_SOURCE */ + resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32)); + resizer_set_output_size(res, outformat->width, outformat->height); + + resizer_set_crop_params(res, informat, outformat); +} + +/* ----------------------------------------------------------------------------- + * Interrupt handling + */ + +static void resizer_enable_oneshot(struct isp_res_device *res) +{ + struct isp_device *isp = to_isp_device(res); + + isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR, + ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT); +} + +void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res) +{ + /* + * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun + * condition, the module was paused and now we have a buffer queued + * on the output again. Restart the pipeline if running in continuous + * mode. + */ + if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS && + res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { + resizer_enable_oneshot(res); + isp_video_dmaqueue_flags_clr(&res->video_out); + } +} + +static void resizer_isr_buffer(struct isp_res_device *res) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); + struct isp_buffer *buffer; + int restart = 0; + + if (res->state == ISP_PIPELINE_STREAM_STOPPED) + return; + + /* Complete the output buffer and, if reading from memory, the input + * buffer. + */ + buffer = omap3isp_video_buffer_next(&res->video_out, res->error); + if (buffer != NULL) { + resizer_set_outaddr(res, buffer->isp_addr); + restart = 1; + } + + pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; + + if (res->input == RESIZER_INPUT_MEMORY) { + buffer = omap3isp_video_buffer_next(&res->video_in, 0); + if (buffer != NULL) + resizer_set_inaddr(res, buffer->isp_addr); + pipe->state |= ISP_PIPELINE_IDLE_INPUT; + } + + if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) { + if (isp_pipeline_ready(pipe)) + omap3isp_pipeline_set_stream(pipe, + ISP_PIPELINE_STREAM_SINGLESHOT); + } else { + /* If an underrun occurs, the video queue operation handler will + * restart the resizer. Otherwise restart it immediately. + */ + if (restart) + resizer_enable_oneshot(res); + } + + res->error = 0; +} + +/* + * omap3isp_resizer_isr - ISP resizer interrupt handler + * + * Manage the resizer video buffers and configure shadowed and busy-locked + * registers. + */ +void omap3isp_resizer_isr(struct isp_res_device *res) +{ + struct v4l2_mbus_framefmt *informat, *outformat; + + if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping)) + return; + + if (res->applycrop) { + outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE, + V4L2_SUBDEV_FORMAT_ACTIVE); + informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK, + V4L2_SUBDEV_FORMAT_ACTIVE); + resizer_set_crop_params(res, informat, outformat); + res->applycrop = 0; + } + + resizer_isr_buffer(res); +} + +/* ----------------------------------------------------------------------------- + * ISP video operations + */ + +static int resizer_video_queue(struct isp_video *video, + struct isp_buffer *buffer) +{ + struct isp_res_device *res = &video->isp->isp_res; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + resizer_set_inaddr(res, buffer->isp_addr); + + /* + * We now have a buffer queued on the output. Despite what the + * TRM says, the resizer can't be restarted immediately. + * Enabling it in one shot mode in the middle of a frame (or at + * least asynchronously to the frame) results in the output + * being shifted randomly left/right and up/down, as if the + * hardware didn't synchronize itself to the beginning of the + * frame correctly. + * + * Restart the resizer on the next sync interrupt if running in + * continuous mode or when starting the stream. + */ + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + resizer_set_outaddr(res, buffer->isp_addr); + + return 0; +} + +static const struct isp_video_operations resizer_video_ops = { + .queue = resizer_video_queue, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev operations + */ + +/* + * resizer_set_stream - Enable/Disable streaming on resizer subdev + * @sd: ISP resizer V4L2 subdev + * @enable: 1 == Enable, 0 == Disable + * + * The resizer hardware can't be enabled without a memory buffer to write to. + * As the s_stream operation is called in response to a STREAMON call without + * any buffer queued yet, just update the state field and return immediately. + * The resizer will be enabled in resizer_video_queue(). + */ +static int resizer_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct isp_video *video_out = &res->video_out; + struct isp_device *isp = to_isp_device(res); + struct device *dev = to_device(res); + + if (res->state == ISP_PIPELINE_STREAM_STOPPED) { + if (enable == ISP_PIPELINE_STREAM_STOPPED) + return 0; + + omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER); + resizer_configure(res); + res->error = 0; + resizer_print_status(res); + } + + switch (enable) { + case ISP_PIPELINE_STREAM_CONTINUOUS: + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); + if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { + resizer_enable_oneshot(res); + isp_video_dmaqueue_flags_clr(video_out); + } + break; + + case ISP_PIPELINE_STREAM_SINGLESHOT: + if (res->input == RESIZER_INPUT_MEMORY) + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ); + omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); + + resizer_enable_oneshot(res); + break; + + case ISP_PIPELINE_STREAM_STOPPED: + if (omap3isp_module_sync_idle(&sd->entity, &res->wait, + &res->stopping)) + dev_dbg(dev, "%s: module stop timeout.\n", sd->name); + omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ | + OMAP3_ISP_SBL_RESIZER_WRITE); + omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER); + isp_video_dmaqueue_flags_clr(video_out); + break; + } + + res->state = enable; + return 0; +} + +/* + * resizer_g_crop - handle get crop subdev operation + * @sd : pointer to v4l2 subdev structure + * @pad : subdev pad + * @crop : pointer to crop structure + * @which : active or try format + * return zero + */ +static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + struct resizer_ratio ratio; + + /* Only sink pad has crop capability */ + if (crop->pad != RESZ_PAD_SINK) + return -EINVAL; + + format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which); + crop->rect = *__resizer_get_crop(res, fh, crop->which); + resizer_calc_ratios(res, &crop->rect, format, &ratio); + + return 0; +} + +/* + * resizer_try_crop - mangles crop parameters. + */ +static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, + const struct v4l2_mbus_framefmt *source, + struct v4l2_rect *crop) +{ + const unsigned int spv = DEFAULT_PHASE; + const unsigned int sph = DEFAULT_PHASE; + + /* Crop rectangle is constrained to the output size so that zoom ratio + * cannot exceed +/-4.0. + */ + unsigned int min_width = + ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7; + unsigned int min_height = + ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4; + unsigned int max_width = + ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7; + unsigned int max_height = + ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7; + + crop->width = clamp_t(u32, crop->width, min_width, max_width); + crop->height = clamp_t(u32, crop->height, min_height, max_height); + + /* Crop can not go beyond of the input rectangle */ + crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH); + crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH, + sink->width - crop->left); + crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT); + crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT, + sink->height - crop->top); +} + +/* + * resizer_s_crop - handle set crop subdev operation + * @sd : pointer to v4l2 subdev structure + * @pad : subdev pad + * @crop : pointer to crop structure + * @which : active or try format + * return -EINVAL or zero when succeed + */ +static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct isp_device *isp = to_isp_device(res); + struct v4l2_mbus_framefmt *format_sink, *format_source; + struct resizer_ratio ratio; + + /* Only sink pad has crop capability */ + if (crop->pad != RESZ_PAD_SINK) + return -EINVAL; + + format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, + crop->which); + format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, + crop->which); + + dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, + crop->rect.left, crop->rect.top, crop->rect.width, + crop->rect.height, crop->which); + + dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, + format_sink->width, format_sink->height, + format_source->width, format_source->height); + + resizer_try_crop(format_sink, format_source, &crop->rect); + *__resizer_get_crop(res, fh, crop->which) = crop->rect; + resizer_calc_ratios(res, &crop->rect, format_source, &ratio); + + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + res->ratio = ratio; + res->crop.active = crop->rect; + + /* + * s_crop can be called while streaming is on. In this case + * the crop values will be set in the next IRQ. + */ + if (res->state != ISP_PIPELINE_STREAM_STOPPED) + res->applycrop = 1; + + return 0; +} + +/* resizer pixel formats */ +static const unsigned int resizer_formats[] = { + V4L2_MBUS_FMT_UYVY8_1X16, + V4L2_MBUS_FMT_YUYV8_1X16, +}; + +static unsigned int resizer_max_in_width(struct isp_res_device *res) +{ + struct isp_device *isp = to_isp_device(res); + + if (res->input == RESIZER_INPUT_MEMORY) { + return MAX_IN_WIDTH_MEMORY_MODE; + } else { + if (isp->revision == ISP_REVISION_1_0) + return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1; + else + return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2; + } +} + +/* + * resizer_try_format - Handle try format by pad subdev method + * @res : ISP resizer device + * @fh : V4L2 subdev file handle + * @pad : pad num + * @fmt : pointer to v4l2 format structure + * @which : wanted subdev format + */ +static void resizer_try_format(struct isp_res_device *res, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *format; + struct resizer_ratio ratio; + struct v4l2_rect crop; + + switch (pad) { + case RESZ_PAD_SINK: + if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 && + fmt->code != V4L2_MBUS_FMT_UYVY8_1X16) + fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; + + fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, + resizer_max_in_width(res)); + fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, + MAX_IN_HEIGHT); + break; + + case RESZ_PAD_SOURCE: + format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which); + fmt->code = format->code; + + crop = *__resizer_get_crop(res, fh, which); + resizer_calc_ratios(res, &crop, fmt, &ratio); + break; + } + + fmt->colorspace = V4L2_COLORSPACE_JPEG; + fmt->field = V4L2_FIELD_NONE; +} + +/* + * resizer_enum_mbus_code - Handle pixel format enumeration + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @code : pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int resizer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (code->pad == RESZ_PAD_SINK) { + if (code->index >= ARRAY_SIZE(resizer_formats)) + return -EINVAL; + + code->code = resizer_formats[code->index]; + } else { + if (code->index != 0) + return -EINVAL; + + format = __resizer_get_format(res, fh, RESZ_PAD_SINK, + V4L2_SUBDEV_FORMAT_TRY); + code->code = format->code; + } + + return 0; +} + +static int resizer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * resizer_get_format - Handle get format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt : pointer to v4l2 subdev format structure + * return -EINVAL or zero on sucess + */ +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(res, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +/* + * resizer_set_format - Handle set format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @fh : V4L2 subdev file handle + * @fmt : pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + format = __resizer_get_format(res, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->pad == RESZ_PAD_SINK) { + /* reset crop rectangle */ + crop = __resizer_get_crop(res, fh, fmt->which); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + /* Propagate the format from sink to source */ + format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, + fmt->which); + *format = fmt->format; + resizer_try_format(res, fh, RESZ_PAD_SOURCE, format, + fmt->which); + } + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + /* Compute and store the active crop rectangle and resizer + * ratios. format already points to the source pad active + * format. + */ + res->crop.active = res->crop.request; + resizer_calc_ratios(res, &res->crop.active, format, + &res->ratio); + } + + return 0; +} + +/* + * resizer_init_formats - Initialize formats on all pads + * @sd: ISP resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int resizer_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = RESZ_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_YUYV8_1X16; + format.format.width = 4096; + format.format.height = 4096; + resizer_set_format(sd, fh, &format); + + return 0; +} + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { + .s_stream = resizer_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { + .enum_mbus_code = resizer_enum_mbus_code, + .enum_frame_size = resizer_enum_frame_size, + .get_fmt = resizer_get_format, + .set_fmt = resizer_set_format, + .get_crop = resizer_g_crop, + .set_crop = resizer_s_crop, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops resizer_v4l2_ops = { + .video = &resizer_v4l2_video_ops, + .pad = &resizer_v4l2_pad_ops, +}; + +/* subdev internal operations */ +static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { + .open = resizer_init_formats, +}; + +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +/* + * resizer_link_setup - Setup resizer connections. + * @entity : Pointer to media entity structure + * @local : Pointer to local pad array + * @remote : Pointer to remote pad array + * @flags : Link flags + * return -EINVAL or zero on success + */ +static int resizer_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct isp_res_device *res = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* read from memory */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (res->input == RESIZER_INPUT_VP) + return -EBUSY; + res->input = RESIZER_INPUT_MEMORY; + } else { + if (res->input == RESIZER_INPUT_MEMORY) + res->input = RESIZER_INPUT_NONE; + } + break; + + case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from ccdc or previewer */ + if (flags & MEDIA_LNK_FL_ENABLED) { + if (res->input == RESIZER_INPUT_MEMORY) + return -EBUSY; + res->input = RESIZER_INPUT_VP; + } else { + if (res->input == RESIZER_INPUT_VP) + res->input = RESIZER_INPUT_NONE; + } + break; + + case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* resizer always write to memory */ + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* media operations */ +static const struct media_entity_operations resizer_media_ops = { + .link_setup = resizer_link_setup, +}; + +/* + * resizer_init_entities - Initialize resizer subdev and media entity. + * @res : Pointer to resizer device structure + * return -ENOMEM or zero on success + */ +static int resizer_init_entities(struct isp_res_device *res) +{ + struct v4l2_subdev *sd = &res->subdev; + struct media_pad *pads = res->pads; + struct media_entity *me = &sd->entity; + int ret; + + res->input = RESIZER_INPUT_NONE; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for isp subdevs */ + v4l2_set_subdevdata(sd, res); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0); + if (ret < 0) + return ret; + + resizer_init_formats(sd, NULL); + + res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + res->video_in.ops = &resizer_video_ops; + res->video_in.isp = to_isp_device(res); + res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; + res->video_in.bpl_alignment = 32; + res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + res->video_out.ops = &resizer_video_ops; + res->video_out.isp = to_isp_device(res); + res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; + res->video_out.bpl_alignment = 32; + + ret = omap3isp_video_init(&res->video_in, "resizer"); + if (ret < 0) + return ret; + + ret = omap3isp_video_init(&res->video_out, "resizer"); + if (ret < 0) + return ret; + + /* Connect the video nodes to the resizer subdev. */ + ret = media_entity_create_link(&res->video_in.video.entity, 0, + &res->subdev.entity, RESZ_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE, + &res->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + return 0; +} + +void omap3isp_resizer_unregister_entities(struct isp_res_device *res) +{ + media_entity_cleanup(&res->subdev.entity); + + v4l2_device_unregister_subdev(&res->subdev); + omap3isp_video_unregister(&res->video_in); + omap3isp_video_unregister(&res->video_out); +} + +int omap3isp_resizer_register_entities(struct isp_res_device *res, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video nodes. */ + ret = v4l2_device_register_subdev(vdev, &res->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&res->video_in, vdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&res->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_resizer_unregister_entities(res); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP resizer initialization and cleanup + */ + +void omap3isp_resizer_cleanup(struct isp_device *isp) +{ +} + +/* + * isp_resizer_init - Resizer initialization. + * @isp : Pointer to ISP device + * return -ENOMEM or zero on success + */ +int omap3isp_resizer_init(struct isp_device *isp) +{ + struct isp_res_device *res = &isp->isp_res; + int ret; + + init_waitqueue_head(&res->wait); + atomic_set(&res->stopping, 0); + ret = resizer_init_entities(res); + if (ret < 0) + goto out; + +out: + if (ret) + omap3isp_resizer_cleanup(isp); + + return ret; +} diff --git a/drivers/media/video/omap3isp/ispresizer.h b/drivers/media/video/omap3isp/ispresizer.h new file mode 100644 index 0000000..76abc2e --- /dev/null +++ b/drivers/media/video/omap3isp/ispresizer.h @@ -0,0 +1,147 @@ +/* + * ispresizer.h + * + * TI OMAP3 ISP - Resizer module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_RESIZER_H +#define OMAP3_ISP_RESIZER_H + +#include + +/* + * Constants for filter coefficents count + */ +#define COEFF_CNT 32 + +/* + * struct isprsz_coef - Structure for resizer filter coeffcients. + * @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap + * mode (.5x-4x) + * @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap + * mode (.5x-4x) + * @h_filter_coef_7tap: Horizontal filter coefficients for 4-phase/7-tap + * mode (.25x-.5x) + * @v_filter_coef_7tap: Vertical filter coefficients for 4-phase/7-tap + * mode (.25x-.5x) + */ +struct isprsz_coef { + u16 h_filter_coef_4tap[32]; + u16 v_filter_coef_4tap[32]; + /* Every 8th value is a dummy value in the following arrays: */ + u16 h_filter_coef_7tap[32]; + u16 v_filter_coef_7tap[32]; +}; + +/* Chrominance horizontal algorithm */ +enum resizer_chroma_algo { + RSZ_THE_SAME = 0, /* Chrominance the same as Luminance */ + RSZ_BILINEAR = 1, /* Chrominance uses bilinear interpolation */ +}; + +/* Resizer input type select */ +enum resizer_colors_type { + RSZ_YUV422 = 0, /* YUV422 color is interleaved */ + RSZ_COLOR8 = 1, /* Color separate data on 8 bits */ +}; + +/* + * Structure for horizontal and vertical resizing value + */ +struct resizer_ratio { + u32 horz; + u32 vert; +}; + +/* + * Structure for luminance enhancer parameters. + */ +struct resizer_luma_yenh { + u8 algo; /* algorithm select. */ + u8 gain; /* maximum gain. */ + u8 slope; /* slope. */ + u8 core; /* core offset. */ +}; + +enum resizer_input_entity { + RESIZER_INPUT_NONE, + RESIZER_INPUT_VP, /* input video port - prev or ccdc */ + RESIZER_INPUT_MEMORY, +}; + +/* Sink and source resizer pads */ +#define RESZ_PAD_SINK 0 +#define RESZ_PAD_SOURCE 1 +#define RESZ_PADS_NUM 2 + +/* + * struct isp_res_device - OMAP3 ISP resizer module + * @crop.request: Crop rectangle requested by the user + * @crop.active: Active crop rectangle (based on hardware requirements) + */ +struct isp_res_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESZ_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESZ_PADS_NUM]; + + enum resizer_input_entity input; + struct isp_video video_in; + struct isp_video video_out; + unsigned int error; + + u32 addr_base; /* stored source buffer address in memory mode */ + u32 crop_offset; /* additional offset for crop in memory mode */ + struct resizer_ratio ratio; + int pm_state; + unsigned int applycrop:1; + enum isp_pipeline_stream_state state; + wait_queue_head_t wait; + atomic_t stopping; + + struct { + struct v4l2_rect request; + struct v4l2_rect active; + } crop; +}; + +struct isp_device; + +int omap3isp_resizer_init(struct isp_device *isp); +void omap3isp_resizer_cleanup(struct isp_device *isp); + +int omap3isp_resizer_register_entities(struct isp_res_device *res, + struct v4l2_device *vdev); +void omap3isp_resizer_unregister_entities(struct isp_res_device *res); +void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res); +void omap3isp_resizer_isr(struct isp_res_device *isp_res); + +void omap3isp_resizer_max_rate(struct isp_res_device *res, + unsigned int *max_rate); + +void omap3isp_resizer_suspend(struct isp_res_device *isp_res); + +void omap3isp_resizer_resume(struct isp_res_device *isp_res); + +int omap3isp_resizer_busy(struct isp_res_device *isp_res); + +#endif /* OMAP3_ISP_RESIZER_H */ diff --git a/drivers/media/video/omap3isp/luma_enhance_table.h b/drivers/media/video/omap3isp/luma_enhance_table.h new file mode 100644 index 0000000..098b45e --- /dev/null +++ b/drivers/media/video/omap3isp/luma_enhance_table.h @@ -0,0 +1,42 @@ +/* + * luma_enhance_table.h + * + * TI OMAP3 ISP - Luminance enhancement table + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, +1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, +1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, +1047552, 1047552, 1047552, 1047552, 1048575, 1047551, 1046527, 1045503, +1044479, 1043455, 1042431, 1041407, 1040383, 1039359, 1038335, 1037311, +1036287, 1035263, 1034239, 1033215, 1032191, 1031167, 1030143, 1028096, +1028096, 1028096, 1028096, 1028096, 1028096, 1028096, 1028096, 1028096, +1028096, 1028100, 1032196, 1036292, 1040388, 1044484, 0, 0, + 0, 5, 5125, 10245, 15365, 20485, 25605, 30720, + 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, + 30720, 30720, 31743, 30719, 29695, 28671, 27647, 26623, + 25599, 24575, 23551, 22527, 21503, 20479, 19455, 18431, + 17407, 16383, 15359, 14335, 13311, 12287, 11263, 10239, + 9215, 8191, 7167, 6143, 5119, 4095, 3071, 1024, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024 diff --git a/drivers/media/video/omap3isp/noise_filter_table.h b/drivers/media/video/omap3isp/noise_filter_table.h new file mode 100644 index 0000000..d50451a --- /dev/null +++ b/drivers/media/video/omap3isp/noise_filter_table.h @@ -0,0 +1,30 @@ +/* + * noise_filter_table.h + * + * TI OMAP3 ISP - Noise filter table + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, +16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, +31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, +31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31 -- cgit v0.10.2 From 68e342b3068cae62f3378cb4a1d385734ea52784 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Sat, 12 Feb 2011 18:05:06 -0300 Subject: [media] omap3isp: Statistics The OMAP3 ISP statistics entities compute histogram and H3A statistics information from capture images. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: David Cohen Signed-off-by: Stanimir Varbanov Signed-off-by: Vimarsh Zutshi Signed-off-by: Tuukka Toivonen Signed-off-by: Sergio Aguirre Signed-off-by: Antti Koskipaa Signed-off-by: Ivan T. Ivanov Signed-off-by: RaniSuneela Signed-off-by: Atanas Filipov Signed-off-by: Gjorgji Rosikopulos Signed-off-by: Hiroshi DOYU Signed-off-by: Nayden Kanchev Signed-off-by: Phil Carmody Signed-off-by: Artem Bityutskiy Signed-off-by: Dominic Curran Signed-off-by: Ilkka Myllyperkio Signed-off-by: Pallavi Kulkarni Signed-off-by: Vaibhav Hiremath Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap3isp/isph3a.h b/drivers/media/video/omap3isp/isph3a.h new file mode 100644 index 0000000..fb09fd4 --- /dev/null +++ b/drivers/media/video/omap3isp/isph3a.h @@ -0,0 +1,117 @@ +/* + * isph3a.h + * + * TI OMAP3 ISP - H3A AF module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: David Cohen + * Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_H3A_H +#define OMAP3_ISP_H3A_H + +#include + +/* + * ---------- + * -H3A AEWB- + * ---------- + */ + +#define AEWB_PACKET_SIZE 16 +#define AEWB_SATURATION_LIMIT 0x3ff + +/* Flags for changed registers */ +#define PCR_CHNG (1 << 0) +#define AEWWIN1_CHNG (1 << 1) +#define AEWINSTART_CHNG (1 << 2) +#define AEWINBLK_CHNG (1 << 3) +#define AEWSUBWIN_CHNG (1 << 4) +#define PRV_WBDGAIN_CHNG (1 << 5) +#define PRV_WBGAIN_CHNG (1 << 6) + +/* ISPH3A REGISTERS bits */ +#define ISPH3A_PCR_AF_EN (1 << 0) +#define ISPH3A_PCR_AF_ALAW_EN (1 << 1) +#define ISPH3A_PCR_AF_MED_EN (1 << 2) +#define ISPH3A_PCR_AF_BUSY (1 << 15) +#define ISPH3A_PCR_AEW_EN (1 << 16) +#define ISPH3A_PCR_AEW_ALAW_EN (1 << 17) +#define ISPH3A_PCR_AEW_BUSY (1 << 18) +#define ISPH3A_PCR_AEW_MASK (ISPH3A_PCR_AEW_ALAW_EN | \ + ISPH3A_PCR_AEW_AVE2LMT_MASK) + +/* + * -------- + * -H3A AF- + * -------- + */ + +/* Peripheral Revision */ +#define AFPID 0x0 + +#define AFCOEF_OFFSET 0x00000004 /* COEF base address */ + +/* PCR fields */ +#define AF_BUSYAF (1 << 15) +#define AF_FVMODE (1 << 14) +#define AF_RGBPOS (0x7 << 11) +#define AF_MED_TH (0xFF << 3) +#define AF_MED_EN (1 << 2) +#define AF_ALAW_EN (1 << 1) +#define AF_EN (1 << 0) +#define AF_PCR_MASK (AF_FVMODE | AF_RGBPOS | AF_MED_TH | \ + AF_MED_EN | AF_ALAW_EN) + +/* AFPAX1 fields */ +#define AF_PAXW (0x7F << 16) +#define AF_PAXH 0x7F + +/* AFPAX2 fields */ +#define AF_AFINCV (0xF << 13) +#define AF_PAXVC (0x7F << 6) +#define AF_PAXHC 0x3F + +/* AFPAXSTART fields */ +#define AF_PAXSH (0xFFF<<16) +#define AF_PAXSV 0xFFF + +/* COEFFICIENT MASK */ +#define AF_COEF_MASK0 0xFFF +#define AF_COEF_MASK1 (0xFFF<<16) + +/* BIT SHIFTS */ +#define AF_RGBPOS_SHIFT 11 +#define AF_MED_TH_SHIFT 3 +#define AF_PAXW_SHIFT 16 +#define AF_LINE_INCR_SHIFT 13 +#define AF_VT_COUNT_SHIFT 6 +#define AF_HZ_START_SHIFT 16 +#define AF_COEF_SHIFT 16 + +/* Init and cleanup functions */ +int omap3isp_h3a_aewb_init(struct isp_device *isp); +int omap3isp_h3a_af_init(struct isp_device *isp); + +void omap3isp_h3a_aewb_cleanup(struct isp_device *isp); +void omap3isp_h3a_af_cleanup(struct isp_device *isp); + +#endif /* OMAP3_ISP_H3A_H */ diff --git a/drivers/media/video/omap3isp/isph3a_aewb.c b/drivers/media/video/omap3isp/isph3a_aewb.c new file mode 100644 index 0000000..8068cef --- /dev/null +++ b/drivers/media/video/omap3isp/isph3a_aewb.c @@ -0,0 +1,374 @@ +/* + * isph3a.c + * + * TI OMAP3 ISP - H3A module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: David Cohen + * Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include + +#include "isp.h" +#include "isph3a.h" +#include "ispstat.h" + +/* + * h3a_aewb_update_regs - Helper function to update h3a registers. + */ +static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv) +{ + struct omap3isp_h3a_aewb_config *conf = priv; + u32 pcr; + u32 win1; + u32 start; + u32 blk; + u32 subwin; + + if (aewb->state == ISPSTAT_DISABLED) + return; + + isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr, + OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST); + + if (!aewb->update) + return; + + /* Converting config metadata into reg values */ + pcr = conf->saturation_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT; + pcr |= !!conf->alaw_enable << ISPH3A_PCR_AEW_ALAW_EN_SHIFT; + + win1 = ((conf->win_height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT; + win1 |= ((conf->win_width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT; + win1 |= (conf->ver_win_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT; + win1 |= (conf->hor_win_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT; + + start = conf->hor_win_start << ISPH3A_AEWINSTART_WINSH_SHIFT; + start |= conf->ver_win_start << ISPH3A_AEWINSTART_WINSV_SHIFT; + + blk = conf->blk_ver_win_start << ISPH3A_AEWINBLK_WINSV_SHIFT; + blk |= ((conf->blk_win_height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT; + + subwin = ((conf->subsample_ver_inc >> 1) - 1) << + ISPH3A_AEWSUBWIN_AEWINCV_SHIFT; + subwin |= ((conf->subsample_hor_inc >> 1) - 1) << + ISPH3A_AEWSUBWIN_AEWINCH_SHIFT; + + isp_reg_writel(aewb->isp, win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1); + isp_reg_writel(aewb->isp, start, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWINSTART); + isp_reg_writel(aewb->isp, blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK); + isp_reg_writel(aewb->isp, subwin, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWSUBWIN); + isp_reg_clr_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, + ISPH3A_PCR_AEW_MASK, pcr); + + aewb->update = 0; + aewb->config_counter += aewb->inc_config; + aewb->inc_config = 0; + aewb->buf_size = conf->buf_size; +} + +static void h3a_aewb_enable(struct ispstat *aewb, int enable) +{ + if (enable) { + isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, + ISPH3A_PCR_AEW_EN); + /* This bit is already set if AF is enabled */ + if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) + isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ISPCTRL_H3A_CLK_EN); + } else { + isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, + ISPH3A_PCR_AEW_EN); + /* This bit can't be cleared if AF is enabled */ + if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) + isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ISPCTRL_H3A_CLK_EN); + } +} + +static int h3a_aewb_busy(struct ispstat *aewb) +{ + return isp_reg_readl(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) + & ISPH3A_PCR_BUSYAEAWB; +} + +static u32 h3a_aewb_get_buf_size(struct omap3isp_h3a_aewb_config *conf) +{ + /* Number of configured windows + extra row for black data */ + u32 win_count = (conf->ver_win_count + 1) * conf->hor_win_count; + + /* + * Unsaturated block counts for each 8 windows. + * 1 extra for the last (win_count % 8) windows if win_count is not + * divisible by 8. + */ + win_count += (win_count + 7) / 8; + + return win_count * AEWB_PACKET_SIZE; +} + +static int h3a_aewb_validate_params(struct ispstat *aewb, void *new_conf) +{ + struct omap3isp_h3a_aewb_config *user_cfg = new_conf; + u32 buf_size; + + if (unlikely(user_cfg->saturation_limit > + OMAP3ISP_AEWB_MAX_SATURATION_LIM)) + return -EINVAL; + + if (unlikely(user_cfg->win_height < OMAP3ISP_AEWB_MIN_WIN_H || + user_cfg->win_height > OMAP3ISP_AEWB_MAX_WIN_H || + user_cfg->win_height & 0x01)) + return -EINVAL; + + if (unlikely(user_cfg->win_width < OMAP3ISP_AEWB_MIN_WIN_W || + user_cfg->win_width > OMAP3ISP_AEWB_MAX_WIN_W || + user_cfg->win_width & 0x01)) + return -EINVAL; + + if (unlikely(user_cfg->ver_win_count < OMAP3ISP_AEWB_MIN_WINVC || + user_cfg->ver_win_count > OMAP3ISP_AEWB_MAX_WINVC)) + return -EINVAL; + + if (unlikely(user_cfg->hor_win_count < OMAP3ISP_AEWB_MIN_WINHC || + user_cfg->hor_win_count > OMAP3ISP_AEWB_MAX_WINHC)) + return -EINVAL; + + if (unlikely(user_cfg->ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) + return -EINVAL; + + if (unlikely(user_cfg->hor_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) + return -EINVAL; + + if (unlikely(user_cfg->blk_ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) + return -EINVAL; + + if (unlikely(user_cfg->blk_win_height < OMAP3ISP_AEWB_MIN_WIN_H || + user_cfg->blk_win_height > OMAP3ISP_AEWB_MAX_WIN_H || + user_cfg->blk_win_height & 0x01)) + return -EINVAL; + + if (unlikely(user_cfg->subsample_ver_inc < OMAP3ISP_AEWB_MIN_SUB_INC || + user_cfg->subsample_ver_inc > OMAP3ISP_AEWB_MAX_SUB_INC || + user_cfg->subsample_ver_inc & 0x01)) + return -EINVAL; + + if (unlikely(user_cfg->subsample_hor_inc < OMAP3ISP_AEWB_MIN_SUB_INC || + user_cfg->subsample_hor_inc > OMAP3ISP_AEWB_MAX_SUB_INC || + user_cfg->subsample_hor_inc & 0x01)) + return -EINVAL; + + buf_size = h3a_aewb_get_buf_size(user_cfg); + if (buf_size > user_cfg->buf_size) + user_cfg->buf_size = buf_size; + else if (user_cfg->buf_size > OMAP3ISP_AEWB_MAX_BUF_SIZE) + user_cfg->buf_size = OMAP3ISP_AEWB_MAX_BUF_SIZE; + + return 0; +} + +/* + * h3a_aewb_set_params - Helper function to check & store user given params. + * @new_conf: Pointer to AE and AWB parameters struct. + * + * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to + * program them during ISR. + */ +static void h3a_aewb_set_params(struct ispstat *aewb, void *new_conf) +{ + struct omap3isp_h3a_aewb_config *user_cfg = new_conf; + struct omap3isp_h3a_aewb_config *cur_cfg = aewb->priv; + int update = 0; + + if (cur_cfg->saturation_limit != user_cfg->saturation_limit) { + cur_cfg->saturation_limit = user_cfg->saturation_limit; + update = 1; + } + if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { + cur_cfg->alaw_enable = user_cfg->alaw_enable; + update = 1; + } + if (cur_cfg->win_height != user_cfg->win_height) { + cur_cfg->win_height = user_cfg->win_height; + update = 1; + } + if (cur_cfg->win_width != user_cfg->win_width) { + cur_cfg->win_width = user_cfg->win_width; + update = 1; + } + if (cur_cfg->ver_win_count != user_cfg->ver_win_count) { + cur_cfg->ver_win_count = user_cfg->ver_win_count; + update = 1; + } + if (cur_cfg->hor_win_count != user_cfg->hor_win_count) { + cur_cfg->hor_win_count = user_cfg->hor_win_count; + update = 1; + } + if (cur_cfg->ver_win_start != user_cfg->ver_win_start) { + cur_cfg->ver_win_start = user_cfg->ver_win_start; + update = 1; + } + if (cur_cfg->hor_win_start != user_cfg->hor_win_start) { + cur_cfg->hor_win_start = user_cfg->hor_win_start; + update = 1; + } + if (cur_cfg->blk_ver_win_start != user_cfg->blk_ver_win_start) { + cur_cfg->blk_ver_win_start = user_cfg->blk_ver_win_start; + update = 1; + } + if (cur_cfg->blk_win_height != user_cfg->blk_win_height) { + cur_cfg->blk_win_height = user_cfg->blk_win_height; + update = 1; + } + if (cur_cfg->subsample_ver_inc != user_cfg->subsample_ver_inc) { + cur_cfg->subsample_ver_inc = user_cfg->subsample_ver_inc; + update = 1; + } + if (cur_cfg->subsample_hor_inc != user_cfg->subsample_hor_inc) { + cur_cfg->subsample_hor_inc = user_cfg->subsample_hor_inc; + update = 1; + } + + if (update || !aewb->configured) { + aewb->inc_config++; + aewb->update = 1; + cur_cfg->buf_size = h3a_aewb_get_buf_size(cur_cfg); + } +} + +static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ispstat *stat = v4l2_get_subdevdata(sd); + + switch (cmd) { + case VIDIOC_OMAP3ISP_AEWB_CFG: + return omap3isp_stat_config(stat, arg); + case VIDIOC_OMAP3ISP_STAT_REQ: + return omap3isp_stat_request_statistics(stat, arg); + case VIDIOC_OMAP3ISP_STAT_EN: { + unsigned long *en = arg; + return omap3isp_stat_enable(stat, !!*en); + } + } + + return -ENOIOCTLCMD; +} + +static const struct ispstat_ops h3a_aewb_ops = { + .validate_params = h3a_aewb_validate_params, + .set_params = h3a_aewb_set_params, + .setup_regs = h3a_aewb_setup_regs, + .enable = h3a_aewb_enable, + .busy = h3a_aewb_busy, +}; + +static const struct v4l2_subdev_core_ops h3a_aewb_subdev_core_ops = { + .ioctl = h3a_aewb_ioctl, + .subscribe_event = omap3isp_stat_subscribe_event, + .unsubscribe_event = omap3isp_stat_unsubscribe_event, +}; + +static const struct v4l2_subdev_video_ops h3a_aewb_subdev_video_ops = { + .s_stream = omap3isp_stat_s_stream, +}; + +static const struct v4l2_subdev_ops h3a_aewb_subdev_ops = { + .core = &h3a_aewb_subdev_core_ops, + .video = &h3a_aewb_subdev_video_ops, +}; + +/* + * omap3isp_h3a_aewb_init - Module Initialisation. + */ +int omap3isp_h3a_aewb_init(struct isp_device *isp) +{ + struct ispstat *aewb = &isp->isp_aewb; + struct omap3isp_h3a_aewb_config *aewb_cfg; + struct omap3isp_h3a_aewb_config *aewb_recover_cfg; + int ret; + + aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL); + if (!aewb_cfg) + return -ENOMEM; + + memset(aewb, 0, sizeof(*aewb)); + aewb->ops = &h3a_aewb_ops; + aewb->priv = aewb_cfg; + aewb->dma_ch = -1; + aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB; + aewb->isp = isp; + + /* Set recover state configuration */ + aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL); + if (!aewb_recover_cfg) { + dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for " + "recover configuration.\n"); + ret = -ENOMEM; + goto err_recover_alloc; + } + + aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM; + aewb_recover_cfg->win_height = OMAP3ISP_AEWB_MIN_WIN_H; + aewb_recover_cfg->win_width = OMAP3ISP_AEWB_MIN_WIN_W; + aewb_recover_cfg->ver_win_count = OMAP3ISP_AEWB_MIN_WINVC; + aewb_recover_cfg->hor_win_count = OMAP3ISP_AEWB_MIN_WINHC; + aewb_recover_cfg->blk_ver_win_start = aewb_recover_cfg->ver_win_start + + aewb_recover_cfg->win_height * aewb_recover_cfg->ver_win_count; + aewb_recover_cfg->blk_win_height = OMAP3ISP_AEWB_MIN_WIN_H; + aewb_recover_cfg->subsample_ver_inc = OMAP3ISP_AEWB_MIN_SUB_INC; + aewb_recover_cfg->subsample_hor_inc = OMAP3ISP_AEWB_MIN_SUB_INC; + + if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) { + dev_err(aewb->isp->dev, "AEWB: recover configuration is " + "invalid.\n"); + ret = -EINVAL; + goto err_conf; + } + + aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg); + aewb->recover_priv = aewb_recover_cfg; + + ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); + if (ret) + goto err_conf; + + return 0; + +err_conf: + kfree(aewb_recover_cfg); +err_recover_alloc: + kfree(aewb_cfg); + + return ret; +} + +/* + * omap3isp_h3a_aewb_cleanup - Module exit. + */ +void omap3isp_h3a_aewb_cleanup(struct isp_device *isp) +{ + kfree(isp->isp_aewb.priv); + kfree(isp->isp_aewb.recover_priv); + omap3isp_stat_free(&isp->isp_aewb); +} diff --git a/drivers/media/video/omap3isp/isph3a_af.c b/drivers/media/video/omap3isp/isph3a_af.c new file mode 100644 index 0000000..ba54d0a --- /dev/null +++ b/drivers/media/video/omap3isp/isph3a_af.c @@ -0,0 +1,429 @@ +/* + * isph3a_af.c + * + * TI OMAP3 ISP - H3A AF module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: David Cohen + * Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +/* Linux specific include files */ +#include +#include + +#include "isp.h" +#include "isph3a.h" +#include "ispstat.h" + +#define IS_OUT_OF_BOUNDS(value, min, max) \ + (((value) < (min)) || ((value) > (max))) + +static void h3a_af_setup_regs(struct ispstat *af, void *priv) +{ + struct omap3isp_h3a_af_config *conf = priv; + u32 pcr; + u32 pax1; + u32 pax2; + u32 paxstart; + u32 coef; + u32 base_coef_set0; + u32 base_coef_set1; + int index; + + if (af->state == ISPSTAT_DISABLED) + return; + + isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AFBUFST); + + if (!af->update) + return; + + /* Configure Hardware Registers */ + pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT; + /* Set height in AFPAX1 */ + pax1 |= (conf->paxel.height >> 1) - 1; + isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1); + + /* Configure AFPAX2 Register */ + /* Set Line Increment in AFPAX2 Register */ + pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT; + /* Set Vertical Count */ + pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT; + /* Set Horizontal Count */ + pax2 |= (conf->paxel.h_cnt - 1); + isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2); + + /* Configure PAXSTART Register */ + /*Configure Horizontal Start */ + paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT; + /* Configure Vertical Start */ + paxstart |= conf->paxel.v_start; + isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AFPAXSTART); + + /*SetIIRSH Register */ + isp_reg_writel(af->isp, conf->iir.h_start, + OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH); + + base_coef_set0 = ISPH3A_AFCOEF010; + base_coef_set1 = ISPH3A_AFCOEF110; + for (index = 0; index <= 8; index += 2) { + /*Set IIR Filter0 Coefficients */ + coef = 0; + coef |= conf->iir.coeff_set0[index]; + coef |= conf->iir.coeff_set0[index + 1] << + AF_COEF_SHIFT; + isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, + base_coef_set0); + base_coef_set0 += AFCOEF_OFFSET; + + /*Set IIR Filter1 Coefficients */ + coef = 0; + coef |= conf->iir.coeff_set1[index]; + coef |= conf->iir.coeff_set1[index + 1] << + AF_COEF_SHIFT; + isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, + base_coef_set1); + base_coef_set1 += AFCOEF_OFFSET; + } + /* set AFCOEF0010 Register */ + isp_reg_writel(af->isp, conf->iir.coeff_set0[10], + OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010); + /* set AFCOEF1010 Register */ + isp_reg_writel(af->isp, conf->iir.coeff_set1[10], + OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010); + + /* PCR Register */ + /* Set RGB Position */ + pcr = conf->rgb_pos << AF_RGBPOS_SHIFT; + /* Set Accumulator Mode */ + if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK) + pcr |= AF_FVMODE; + /* Set A-law */ + if (conf->alaw_enable) + pcr |= AF_ALAW_EN; + /* HMF Configurations */ + if (conf->hmf.enable) { + /* Enable HMF */ + pcr |= AF_MED_EN; + /* Set Median Threshold */ + pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT; + } + /* Set PCR Register */ + isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, + AF_PCR_MASK, pcr); + + af->update = 0; + af->config_counter += af->inc_config; + af->inc_config = 0; + af->buf_size = conf->buf_size; +} + +static void h3a_af_enable(struct ispstat *af, int enable) +{ + if (enable) { + isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, + ISPH3A_PCR_AF_EN); + /* This bit is already set if AEWB is enabled */ + if (af->isp->isp_aewb.state != ISPSTAT_ENABLED) + isp_reg_set(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ISPCTRL_H3A_CLK_EN); + } else { + isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, + ISPH3A_PCR_AF_EN); + /* This bit can't be cleared if AEWB is enabled */ + if (af->isp->isp_aewb.state != ISPSTAT_ENABLED) + isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ISPCTRL_H3A_CLK_EN); + } +} + +static int h3a_af_busy(struct ispstat *af) +{ + return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) + & ISPH3A_PCR_BUSYAF; +} + +static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf) +{ + return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE; +} + +/* Function to check paxel parameters */ +static int h3a_af_validate_params(struct ispstat *af, void *new_conf) +{ + struct omap3isp_h3a_af_config *user_cfg = new_conf; + struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel; + struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir; + int index; + u32 buf_size; + + /* Check horizontal Count */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt, + OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN, + OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX)) + return -EINVAL; + + /* Check Vertical Count */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt, + OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN, + OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX)) + return -EINVAL; + + if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN, + OMAP3ISP_AF_PAXEL_HEIGHT_MAX) || + paxel_cfg->height % 2) + return -EINVAL; + + /* Check width */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN, + OMAP3ISP_AF_PAXEL_WIDTH_MAX) || + paxel_cfg->width % 2) + return -EINVAL; + + /* Check Line Increment */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc, + OMAP3ISP_AF_PAXEL_INCREMENT_MIN, + OMAP3ISP_AF_PAXEL_INCREMENT_MAX) || + paxel_cfg->line_inc % 2) + return -EINVAL; + + /* Check Horizontal Start */ + if ((paxel_cfg->h_start < iir_cfg->h_start) || + IS_OUT_OF_BOUNDS(paxel_cfg->h_start, + OMAP3ISP_AF_PAXEL_HZSTART_MIN, + OMAP3ISP_AF_PAXEL_HZSTART_MAX)) + return -EINVAL; + + /* Check IIR */ + for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { + if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX) + return -EINVAL; + + if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX) + return -EINVAL; + } + + if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN, + OMAP3ISP_AF_IIRSH_MAX)) + return -EINVAL; + + /* Hack: If paxel size is 12, the 10th AF window may be corrupted */ + if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) && + (paxel_cfg->width * paxel_cfg->height == 12)) + return -EINVAL; + + buf_size = h3a_af_get_buf_size(user_cfg); + if (buf_size > user_cfg->buf_size) + /* User buf_size request wasn't enough */ + user_cfg->buf_size = buf_size; + else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE) + user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE; + + return 0; +} + +/* Update local parameters */ +static void h3a_af_set_params(struct ispstat *af, void *new_conf) +{ + struct omap3isp_h3a_af_config *user_cfg = new_conf; + struct omap3isp_h3a_af_config *cur_cfg = af->priv; + int update = 0; + int index; + + /* alaw */ + if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { + update = 1; + goto out; + } + + /* hmf */ + if (cur_cfg->hmf.enable != user_cfg->hmf.enable) { + update = 1; + goto out; + } + if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) { + update = 1; + goto out; + } + + /* rgbpos */ + if (cur_cfg->rgb_pos != user_cfg->rgb_pos) { + update = 1; + goto out; + } + + /* iir */ + if (cur_cfg->iir.h_start != user_cfg->iir.h_start) { + update = 1; + goto out; + } + for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { + if (cur_cfg->iir.coeff_set0[index] != + user_cfg->iir.coeff_set0[index]) { + update = 1; + goto out; + } + if (cur_cfg->iir.coeff_set1[index] != + user_cfg->iir.coeff_set1[index]) { + update = 1; + goto out; + } + } + + /* paxel */ + if ((cur_cfg->paxel.width != user_cfg->paxel.width) || + (cur_cfg->paxel.height != user_cfg->paxel.height) || + (cur_cfg->paxel.h_start != user_cfg->paxel.h_start) || + (cur_cfg->paxel.v_start != user_cfg->paxel.v_start) || + (cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) || + (cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) || + (cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) { + update = 1; + goto out; + } + + /* af_mode */ + if (cur_cfg->fvmode != user_cfg->fvmode) + update = 1; + +out: + if (update || !af->configured) { + memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg)); + af->inc_config++; + af->update = 1; + /* + * User might be asked for a bigger buffer than necessary for + * this configuration. In order to return the right amount of + * data during buffer request, let's calculate the size here + * instead of stick with user_cfg->buf_size. + */ + cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg); + } +} + +static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ispstat *stat = v4l2_get_subdevdata(sd); + + switch (cmd) { + case VIDIOC_OMAP3ISP_AF_CFG: + return omap3isp_stat_config(stat, arg); + case VIDIOC_OMAP3ISP_STAT_REQ: + return omap3isp_stat_request_statistics(stat, arg); + case VIDIOC_OMAP3ISP_STAT_EN: { + int *en = arg; + return omap3isp_stat_enable(stat, !!*en); + } + } + + return -ENOIOCTLCMD; + +} + +static const struct ispstat_ops h3a_af_ops = { + .validate_params = h3a_af_validate_params, + .set_params = h3a_af_set_params, + .setup_regs = h3a_af_setup_regs, + .enable = h3a_af_enable, + .busy = h3a_af_busy, +}; + +static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = { + .ioctl = h3a_af_ioctl, + .subscribe_event = omap3isp_stat_subscribe_event, + .unsubscribe_event = omap3isp_stat_unsubscribe_event, +}; + +static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = { + .s_stream = omap3isp_stat_s_stream, +}; + +static const struct v4l2_subdev_ops h3a_af_subdev_ops = { + .core = &h3a_af_subdev_core_ops, + .video = &h3a_af_subdev_video_ops, +}; + +/* Function to register the AF character device driver. */ +int omap3isp_h3a_af_init(struct isp_device *isp) +{ + struct ispstat *af = &isp->isp_af; + struct omap3isp_h3a_af_config *af_cfg; + struct omap3isp_h3a_af_config *af_recover_cfg; + int ret; + + af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL); + if (af_cfg == NULL) + return -ENOMEM; + + memset(af, 0, sizeof(*af)); + af->ops = &h3a_af_ops; + af->priv = af_cfg; + af->dma_ch = -1; + af->event_type = V4L2_EVENT_OMAP3ISP_AF; + af->isp = isp; + + /* Set recover state configuration */ + af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL); + if (!af_recover_cfg) { + dev_err(af->isp->dev, "AF: cannot allocate memory for recover " + "configuration.\n"); + ret = -ENOMEM; + goto err_recover_alloc; + } + + af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN; + af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN; + af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN; + af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN; + af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN; + af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN; + if (h3a_af_validate_params(af, af_recover_cfg)) { + dev_err(af->isp->dev, "AF: recover configuration is " + "invalid.\n"); + ret = -EINVAL; + goto err_conf; + } + + af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg); + af->recover_priv = af_recover_cfg; + + ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops); + if (ret) + goto err_conf; + + return 0; + +err_conf: + kfree(af_recover_cfg); +err_recover_alloc: + kfree(af_cfg); + + return ret; +} + +void omap3isp_h3a_af_cleanup(struct isp_device *isp) +{ + kfree(isp->isp_af.priv); + kfree(isp->isp_af.recover_priv); + omap3isp_stat_free(&isp->isp_af); +} diff --git a/drivers/media/video/omap3isp/isphist.c b/drivers/media/video/omap3isp/isphist.c new file mode 100644 index 0000000..1743856 --- /dev/null +++ b/drivers/media/video/omap3isp/isphist.c @@ -0,0 +1,520 @@ +/* + * isphist.c + * + * TI OMAP3 ISP - Histogram module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: David Cohen + * Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "isphist.h" + +#define HIST_CONFIG_DMA 1 + +#define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0) + +/* + * hist_reset_mem - clear Histogram memory before start stats engine. + */ +static void hist_reset_mem(struct ispstat *hist) +{ + struct isp_device *isp = hist->isp; + struct omap3isp_hist_config *conf = hist->priv; + unsigned int i; + + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); + + /* + * By setting it, the histogram internal buffer is being cleared at the + * same time it's being read. This bit must be cleared afterwards. + */ + isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); + + /* + * We'll clear 4 words at each iteration for optimization. It avoids + * 3/4 of the jumps. We also know HIST_MEM_SIZE is divisible by 4. + */ + for (i = OMAP3ISP_HIST_MEM_SIZE / 4; i > 0; i--) { + isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); + isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); + isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); + isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); + } + isp_reg_clr(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); + + hist->wait_acc_frames = conf->num_acc_frames; +} + +static void hist_dma_config(struct ispstat *hist) +{ + hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32; + hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT; + hist->dma_config.frame_count = 1; + hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT; + hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA; + hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC; + hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC; +} + +/* + * hist_setup_regs - Helper function to update Histogram registers. + */ +static void hist_setup_regs(struct ispstat *hist, void *priv) +{ + struct isp_device *isp = hist->isp; + struct omap3isp_hist_config *conf = priv; + int c; + u32 cnt; + u32 wb_gain; + u32 reg_hor[OMAP3ISP_HIST_MAX_REGIONS]; + u32 reg_ver[OMAP3ISP_HIST_MAX_REGIONS]; + + if (!hist->update || hist->state == ISPSTAT_DISABLED || + hist->state == ISPSTAT_DISABLING) + return; + + cnt = conf->cfa << ISPHIST_CNT_CFA_SHIFT; + + wb_gain = conf->wg[0] << ISPHIST_WB_GAIN_WG00_SHIFT; + wb_gain |= conf->wg[1] << ISPHIST_WB_GAIN_WG01_SHIFT; + wb_gain |= conf->wg[2] << ISPHIST_WB_GAIN_WG02_SHIFT; + if (conf->cfa == OMAP3ISP_HIST_CFA_BAYER) + wb_gain |= conf->wg[3] << ISPHIST_WB_GAIN_WG03_SHIFT; + + /* Regions size and position */ + for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) { + if (c < conf->num_regions) { + reg_hor[c] = conf->region[c].h_start << + ISPHIST_REG_START_SHIFT; + reg_hor[c] = conf->region[c].h_end << + ISPHIST_REG_END_SHIFT; + reg_ver[c] = conf->region[c].v_start << + ISPHIST_REG_START_SHIFT; + reg_ver[c] = conf->region[c].v_end << + ISPHIST_REG_END_SHIFT; + } else { + reg_hor[c] = 0; + reg_ver[c] = 0; + } + } + + cnt |= conf->hist_bins << ISPHIST_CNT_BINS_SHIFT; + switch (conf->hist_bins) { + case OMAP3ISP_HIST_BINS_256: + cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 8) << + ISPHIST_CNT_SHIFT_SHIFT; + break; + case OMAP3ISP_HIST_BINS_128: + cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 7) << + ISPHIST_CNT_SHIFT_SHIFT; + break; + case OMAP3ISP_HIST_BINS_64: + cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 6) << + ISPHIST_CNT_SHIFT_SHIFT; + break; + default: /* OMAP3ISP_HIST_BINS_32 */ + cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 5) << + ISPHIST_CNT_SHIFT_SHIFT; + break; + } + + hist_reset_mem(hist); + + isp_reg_writel(isp, cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT); + isp_reg_writel(isp, wb_gain, OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN); + isp_reg_writel(isp, reg_hor[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ); + isp_reg_writel(isp, reg_ver[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT); + isp_reg_writel(isp, reg_hor[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ); + isp_reg_writel(isp, reg_ver[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT); + isp_reg_writel(isp, reg_hor[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ); + isp_reg_writel(isp, reg_ver[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT); + isp_reg_writel(isp, reg_hor[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ); + isp_reg_writel(isp, reg_ver[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT); + + hist->update = 0; + hist->config_counter += hist->inc_config; + hist->inc_config = 0; + hist->buf_size = conf->buf_size; +} + +static void hist_enable(struct ispstat *hist, int enable) +{ + if (enable) { + isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, + ISPHIST_PCR_ENABLE); + isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ISPCTRL_HIST_CLK_EN); + } else { + isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, + ISPHIST_PCR_ENABLE); + isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ISPCTRL_HIST_CLK_EN); + } +} + +static int hist_busy(struct ispstat *hist) +{ + return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR) + & ISPHIST_PCR_BUSY; +} + +static void hist_dma_cb(int lch, u16 ch_status, void *data) +{ + struct ispstat *hist = data; + + if (ch_status & ~OMAP_DMA_BLOCK_IRQ) { + dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n", + ch_status); + omap_stop_dma(lch); + hist_reset_mem(hist); + atomic_set(&hist->buf_err, 1); + } + isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, + ISPHIST_CNT_CLEAR); + + omap3isp_stat_dma_isr(hist); + if (hist->state != ISPSTAT_DISABLED) + omap3isp_hist_dma_done(hist->isp); +} + +static int hist_buf_dma(struct ispstat *hist) +{ + dma_addr_t dma_addr = hist->active_buf->dma_addr; + + if (unlikely(!dma_addr)) { + dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n"); + hist_reset_mem(hist); + return STAT_NO_BUF; + } + + isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); + isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, + ISPHIST_CNT_CLEAR); + omap3isp_flush(hist->isp); + hist->dma_config.dst_start = dma_addr; + hist->dma_config.elem_count = hist->buf_size / sizeof(u32); + omap_set_dma_params(hist->dma_ch, &hist->dma_config); + + omap_start_dma(hist->dma_ch); + + return STAT_BUF_WAITING_DMA; +} + +static int hist_buf_pio(struct ispstat *hist) +{ + struct isp_device *isp = hist->isp; + u32 *buf = hist->active_buf->virt_addr; + unsigned int i; + + if (!buf) { + dev_dbg(isp->dev, "hist: invalid PIO buffer address\n"); + hist_reset_mem(hist); + return STAT_NO_BUF; + } + + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); + + /* + * By setting it, the histogram internal buffer is being cleared at the + * same time it's being read. This bit must be cleared just after all + * data is acquired. + */ + isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); + + /* + * We'll read 4 times a 4-bytes-word at each iteration for + * optimization. It avoids 3/4 of the jumps. We also know buf_size is + * divisible by 16. + */ + for (i = hist->buf_size / 16; i > 0; i--) { + *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); + *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); + *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); + *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); + } + isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, + ISPHIST_CNT_CLEAR); + + return STAT_BUF_DONE; +} + +/* + * hist_buf_process - Callback from ISP driver for HIST interrupt. + */ +static int hist_buf_process(struct ispstat *hist) +{ + struct omap3isp_hist_config *user_cfg = hist->priv; + int ret; + + if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) { + hist_reset_mem(hist); + return STAT_NO_BUF; + } + + if (--(hist->wait_acc_frames)) + return STAT_NO_BUF; + + if (HIST_USING_DMA(hist)) + ret = hist_buf_dma(hist); + else + ret = hist_buf_pio(hist); + + hist->wait_acc_frames = user_cfg->num_acc_frames; + + return ret; +} + +static u32 hist_get_buf_size(struct omap3isp_hist_config *conf) +{ + return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions; +} + +/* + * hist_validate_params - Helper function to check user given params. + * @user_cfg: Pointer to user configuration structure. + * + * Returns 0 on success configuration. + */ +static int hist_validate_params(struct ispstat *hist, void *new_conf) +{ + struct omap3isp_hist_config *user_cfg = new_conf; + int c; + u32 buf_size; + + if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3) + return -EINVAL; + + /* Regions size and position */ + + if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) || + (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS)) + return -EINVAL; + + /* Regions */ + for (c = 0; c < user_cfg->num_regions; c++) { + if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK) + return -EINVAL; + if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK) + return -EINVAL; + if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK) + return -EINVAL; + if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK) + return -EINVAL; + if (user_cfg->region[c].h_start > user_cfg->region[c].h_end) + return -EINVAL; + if (user_cfg->region[c].v_start > user_cfg->region[c].v_end) + return -EINVAL; + } + + switch (user_cfg->num_regions) { + case 1: + if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256) + return -EINVAL; + break; + case 2: + if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128) + return -EINVAL; + break; + default: /* 3 or 4 */ + if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64) + return -EINVAL; + break; + } + + buf_size = hist_get_buf_size(user_cfg); + if (buf_size > user_cfg->buf_size) + /* User's buf_size request wasn't enoght */ + user_cfg->buf_size = buf_size; + else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE) + user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE; + + return 0; +} + +static int hist_comp_params(struct ispstat *hist, + struct omap3isp_hist_config *user_cfg) +{ + struct omap3isp_hist_config *cur_cfg = hist->priv; + int c; + + if (cur_cfg->cfa != user_cfg->cfa) + return 1; + + if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames) + return 1; + + if (cur_cfg->hist_bins != user_cfg->hist_bins) + return 1; + + for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) { + if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3) + break; + else if (cur_cfg->wg[c] != user_cfg->wg[c]) + return 1; + } + + if (cur_cfg->num_regions != user_cfg->num_regions) + return 1; + + /* Regions */ + for (c = 0; c < user_cfg->num_regions; c++) { + if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start) + return 1; + if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end) + return 1; + if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start) + return 1; + if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end) + return 1; + } + + return 0; +} + +/* + * hist_update_params - Helper function to check and store user given params. + * @new_conf: Pointer to user configuration structure. + */ +static void hist_set_params(struct ispstat *hist, void *new_conf) +{ + struct omap3isp_hist_config *user_cfg = new_conf; + struct omap3isp_hist_config *cur_cfg = hist->priv; + + if (!hist->configured || hist_comp_params(hist, user_cfg)) { + memcpy(cur_cfg, user_cfg, sizeof(*user_cfg)); + if (user_cfg->num_acc_frames == 0) + user_cfg->num_acc_frames = 1; + hist->inc_config++; + hist->update = 1; + /* + * User might be asked for a bigger buffer than necessary for + * this configuration. In order to return the right amount of + * data during buffer request, let's calculate the size here + * instead of stick with user_cfg->buf_size. + */ + cur_cfg->buf_size = hist_get_buf_size(cur_cfg); + + } +} + +static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ispstat *stat = v4l2_get_subdevdata(sd); + + switch (cmd) { + case VIDIOC_OMAP3ISP_HIST_CFG: + return omap3isp_stat_config(stat, arg); + case VIDIOC_OMAP3ISP_STAT_REQ: + return omap3isp_stat_request_statistics(stat, arg); + case VIDIOC_OMAP3ISP_STAT_EN: { + int *en = arg; + return omap3isp_stat_enable(stat, !!*en); + } + } + + return -ENOIOCTLCMD; + +} + +static const struct ispstat_ops hist_ops = { + .validate_params = hist_validate_params, + .set_params = hist_set_params, + .setup_regs = hist_setup_regs, + .enable = hist_enable, + .busy = hist_busy, + .buf_process = hist_buf_process, +}; + +static const struct v4l2_subdev_core_ops hist_subdev_core_ops = { + .ioctl = hist_ioctl, + .subscribe_event = omap3isp_stat_subscribe_event, + .unsubscribe_event = omap3isp_stat_unsubscribe_event, +}; + +static const struct v4l2_subdev_video_ops hist_subdev_video_ops = { + .s_stream = omap3isp_stat_s_stream, +}; + +static const struct v4l2_subdev_ops hist_subdev_ops = { + .core = &hist_subdev_core_ops, + .video = &hist_subdev_video_ops, +}; + +/* + * omap3isp_hist_init - Module Initialization. + */ +int omap3isp_hist_init(struct isp_device *isp) +{ + struct ispstat *hist = &isp->isp_hist; + struct omap3isp_hist_config *hist_cfg; + int ret = -1; + + hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL); + if (hist_cfg == NULL) + return -ENOMEM; + + memset(hist, 0, sizeof(*hist)); + if (HIST_CONFIG_DMA) + ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST", + hist_dma_cb, hist, &hist->dma_ch); + if (ret) { + if (HIST_CONFIG_DMA) + dev_warn(isp->dev, "hist: DMA request channel failed. " + "Using PIO only.\n"); + hist->dma_ch = -1; + } else { + dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch); + hist_dma_config(hist); + omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ); + } + + hist->ops = &hist_ops; + hist->priv = hist_cfg; + hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; + hist->isp = isp; + + ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); + if (ret) { + kfree(hist_cfg); + if (HIST_USING_DMA(hist)) + omap_free_dma(hist->dma_ch); + } + + return ret; +} + +/* + * omap3isp_hist_cleanup - Module cleanup. + */ +void omap3isp_hist_cleanup(struct isp_device *isp) +{ + if (HIST_USING_DMA(&isp->isp_hist)) + omap_free_dma(isp->isp_hist.dma_ch); + kfree(isp->isp_hist.priv); + omap3isp_stat_free(&isp->isp_hist); +} diff --git a/drivers/media/video/omap3isp/isphist.h b/drivers/media/video/omap3isp/isphist.h new file mode 100644 index 0000000..0b2a38e --- /dev/null +++ b/drivers/media/video/omap3isp/isphist.h @@ -0,0 +1,40 @@ +/* + * isphist.h + * + * TI OMAP3 ISP - Histogram module + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contacts: David Cohen + * Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_HIST_H +#define OMAP3_ISP_HIST_H + +#include + +#define ISPHIST_IN_BIT_WIDTH_CCDC 10 + +struct isp_device; + +int omap3isp_hist_init(struct isp_device *isp); +void omap3isp_hist_cleanup(struct isp_device *isp); + +#endif /* OMAP3_ISP_HIST */ diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c new file mode 100644 index 0000000..b44cb68 --- /dev/null +++ b/drivers/media/video/omap3isp/ispstat.c @@ -0,0 +1,1092 @@ +/* + * ispstat.c + * + * TI OMAP3 ISP - Statistics core + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc + * + * Contacts: David Cohen + * Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include + +#include "isp.h" + +#define IS_COHERENT_BUF(stat) ((stat)->dma_ch >= 0) + +/* + * MAGIC_SIZE must always be the greatest common divisor of + * AEWB_PACKET_SIZE and AF_PAXEL_SIZE. + */ +#define MAGIC_SIZE 16 +#define MAGIC_NUM 0x55 + +/* HACK: AF module seems to be writing one more paxel data than it should. */ +#define AF_EXTRA_DATA OMAP3ISP_AF_PAXEL_SIZE + +/* + * HACK: H3A modules go to an invalid state after have a SBL overflow. It makes + * the next buffer to start to be written in the same point where the overflow + * occurred instead of the configured address. The only known way to make it to + * go back to a valid state is having a valid buffer processing. Of course it + * requires at least a doubled buffer size to avoid an access to invalid memory + * region. But it does not fix everything. It may happen more than one + * consecutive SBL overflows. In that case, it might be unpredictable how many + * buffers the allocated memory should fit. For that case, a recover + * configuration was created. It produces the minimum buffer size for each H3A + * module and decrease the change for more SBL overflows. This recover state + * will be enabled every time a SBL overflow occur. As the output buffer size + * isn't big, it's possible to have an extra size able to fit many recover + * buffers making it extreamily unlikely to have an access to invalid memory + * region. + */ +#define NUM_H3A_RECOVER_BUFS 10 + +/* + * HACK: Because of HW issues the generic layer sometimes need to have + * different behaviour for different statistic modules. + */ +#define IS_H3A_AF(stat) ((stat) == &(stat)->isp->isp_af) +#define IS_H3A_AEWB(stat) ((stat) == &(stat)->isp->isp_aewb) +#define IS_H3A(stat) (IS_H3A_AF(stat) || IS_H3A_AEWB(stat)) + +static void __isp_stat_buf_sync_magic(struct ispstat *stat, + struct ispstat_buffer *buf, + u32 buf_size, enum dma_data_direction dir, + void (*dma_sync)(struct device *, + dma_addr_t, unsigned long, size_t, + enum dma_data_direction)) +{ + struct device *dev = stat->isp->dev; + struct page *pg; + dma_addr_t dma_addr; + u32 offset; + + /* Initial magic words */ + pg = vmalloc_to_page(buf->virt_addr); + dma_addr = pfn_to_dma(dev, page_to_pfn(pg)); + dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir); + + /* Final magic words */ + pg = vmalloc_to_page(buf->virt_addr + buf_size); + dma_addr = pfn_to_dma(dev, page_to_pfn(pg)); + offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK; + dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir); +} + +static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat, + struct ispstat_buffer *buf, + u32 buf_size, + enum dma_data_direction dir) +{ + if (IS_COHERENT_BUF(stat)) + return; + + __isp_stat_buf_sync_magic(stat, buf, buf_size, dir, + dma_sync_single_range_for_device); +} + +static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat, + struct ispstat_buffer *buf, + u32 buf_size, + enum dma_data_direction dir) +{ + if (IS_COHERENT_BUF(stat)) + return; + + __isp_stat_buf_sync_magic(stat, buf, buf_size, dir, + dma_sync_single_range_for_cpu); +} + +static int isp_stat_buf_check_magic(struct ispstat *stat, + struct ispstat_buffer *buf) +{ + const u32 buf_size = IS_H3A_AF(stat) ? + buf->buf_size + AF_EXTRA_DATA : buf->buf_size; + u8 *w; + u8 *end; + int ret = -EINVAL; + + isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE); + + /* Checking initial magic numbers. They shouldn't be here anymore. */ + for (w = buf->virt_addr, end = w + MAGIC_SIZE; w < end; w++) + if (likely(*w != MAGIC_NUM)) + ret = 0; + + if (ret) { + dev_dbg(stat->isp->dev, "%s: beginning magic check does not " + "match.\n", stat->subdev.name); + return ret; + } + + /* Checking magic numbers at the end. They must be still here. */ + for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE; + w < end; w++) { + if (unlikely(*w != MAGIC_NUM)) { + dev_dbg(stat->isp->dev, "%s: endding magic check does " + "not match.\n", stat->subdev.name); + return -EINVAL; + } + } + + isp_stat_buf_sync_magic_for_device(stat, buf, buf_size, + DMA_FROM_DEVICE); + + return 0; +} + +static void isp_stat_buf_insert_magic(struct ispstat *stat, + struct ispstat_buffer *buf) +{ + const u32 buf_size = IS_H3A_AF(stat) ? + stat->buf_size + AF_EXTRA_DATA : stat->buf_size; + + isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE); + + /* + * Inserting MAGIC_NUM at the beginning and end of the buffer. + * buf->buf_size is set only after the buffer is queued. For now the + * right buf_size for the current configuration is pointed by + * stat->buf_size. + */ + memset(buf->virt_addr, MAGIC_NUM, MAGIC_SIZE); + memset(buf->virt_addr + buf_size, MAGIC_NUM, MAGIC_SIZE); + + isp_stat_buf_sync_magic_for_device(stat, buf, buf_size, + DMA_BIDIRECTIONAL); +} + +static void isp_stat_buf_sync_for_device(struct ispstat *stat, + struct ispstat_buffer *buf) +{ + if (IS_COHERENT_BUF(stat)) + return; + + dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl, + buf->iovm->sgt->nents, DMA_FROM_DEVICE); +} + +static void isp_stat_buf_sync_for_cpu(struct ispstat *stat, + struct ispstat_buffer *buf) +{ + if (IS_COHERENT_BUF(stat)) + return; + + dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl, + buf->iovm->sgt->nents, DMA_FROM_DEVICE); +} + +static void isp_stat_buf_clear(struct ispstat *stat) +{ + int i; + + for (i = 0; i < STAT_MAX_BUFS; i++) + stat->buf[i].empty = 1; +} + +static struct ispstat_buffer * +__isp_stat_buf_find(struct ispstat *stat, int look_empty) +{ + struct ispstat_buffer *found = NULL; + int i; + + for (i = 0; i < STAT_MAX_BUFS; i++) { + struct ispstat_buffer *curr = &stat->buf[i]; + + /* + * Don't select the buffer which is being copied to + * userspace or used by the module. + */ + if (curr == stat->locked_buf || curr == stat->active_buf) + continue; + + /* Don't select uninitialised buffers if it's not required */ + if (!look_empty && curr->empty) + continue; + + /* Pick uninitialised buffer over anything else if look_empty */ + if (curr->empty) { + found = curr; + break; + } + + /* Choose the oldest buffer */ + if (!found || + (s32)curr->frame_number - (s32)found->frame_number < 0) + found = curr; + } + + return found; +} + +static inline struct ispstat_buffer * +isp_stat_buf_find_oldest(struct ispstat *stat) +{ + return __isp_stat_buf_find(stat, 0); +} + +static inline struct ispstat_buffer * +isp_stat_buf_find_oldest_or_empty(struct ispstat *stat) +{ + return __isp_stat_buf_find(stat, 1); +} + +static int isp_stat_buf_queue(struct ispstat *stat) +{ + if (!stat->active_buf) + return STAT_NO_BUF; + + do_gettimeofday(&stat->active_buf->ts); + + stat->active_buf->buf_size = stat->buf_size; + if (isp_stat_buf_check_magic(stat, stat->active_buf)) { + dev_dbg(stat->isp->dev, "%s: data wasn't properly written.\n", + stat->subdev.name); + return STAT_NO_BUF; + } + stat->active_buf->config_counter = stat->config_counter; + stat->active_buf->frame_number = stat->frame_number; + stat->active_buf->empty = 0; + stat->active_buf = NULL; + + return STAT_BUF_DONE; +} + +/* Get next free buffer to write the statistics to and mark it active. */ +static void isp_stat_buf_next(struct ispstat *stat) +{ + if (unlikely(stat->active_buf)) + /* Overwriting unused active buffer */ + dev_dbg(stat->isp->dev, "%s: new buffer requested without " + "queuing active one.\n", + stat->subdev.name); + else + stat->active_buf = isp_stat_buf_find_oldest_or_empty(stat); +} + +static void isp_stat_buf_release(struct ispstat *stat) +{ + unsigned long flags; + + isp_stat_buf_sync_for_device(stat, stat->locked_buf); + spin_lock_irqsave(&stat->isp->stat_lock, flags); + stat->locked_buf = NULL; + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); +} + +/* Get buffer to userspace. */ +static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat, + struct omap3isp_stat_data *data) +{ + int rval = 0; + unsigned long flags; + struct ispstat_buffer *buf; + + spin_lock_irqsave(&stat->isp->stat_lock, flags); + + while (1) { + buf = isp_stat_buf_find_oldest(stat); + if (!buf) { + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); + dev_dbg(stat->isp->dev, "%s: cannot find a buffer.\n", + stat->subdev.name); + return ERR_PTR(-EBUSY); + } + if (isp_stat_buf_check_magic(stat, buf)) { + dev_dbg(stat->isp->dev, "%s: current buffer has " + "corrupted data\n.", stat->subdev.name); + /* Mark empty because it doesn't have valid data. */ + buf->empty = 1; + } else { + /* Buffer isn't corrupted. */ + break; + } + } + + stat->locked_buf = buf; + + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); + + if (buf->buf_size > data->buf_size) { + dev_warn(stat->isp->dev, "%s: userspace's buffer size is " + "not enough.\n", stat->subdev.name); + isp_stat_buf_release(stat); + return ERR_PTR(-EINVAL); + } + + isp_stat_buf_sync_for_cpu(stat, buf); + + rval = copy_to_user(data->buf, + buf->virt_addr, + buf->buf_size); + + if (rval) { + dev_info(stat->isp->dev, + "%s: failed copying %d bytes of stat data\n", + stat->subdev.name, rval); + buf = ERR_PTR(-EFAULT); + isp_stat_buf_release(stat); + } + + return buf; +} + +static void isp_stat_bufs_free(struct ispstat *stat) +{ + struct isp_device *isp = stat->isp; + int i; + + for (i = 0; i < STAT_MAX_BUFS; i++) { + struct ispstat_buffer *buf = &stat->buf[i]; + + if (!IS_COHERENT_BUF(stat)) { + if (IS_ERR_OR_NULL((void *)buf->iommu_addr)) + continue; + if (buf->iovm) + dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl, + buf->iovm->sgt->nents, + DMA_FROM_DEVICE); + iommu_vfree(isp->iommu, buf->iommu_addr); + } else { + if (!buf->virt_addr) + continue; + dma_free_coherent(stat->isp->dev, stat->buf_alloc_size, + buf->virt_addr, buf->dma_addr); + } + buf->iommu_addr = 0; + buf->iovm = NULL; + buf->dma_addr = 0; + buf->virt_addr = NULL; + buf->empty = 1; + } + + dev_dbg(stat->isp->dev, "%s: all buffers were freed.\n", + stat->subdev.name); + + stat->buf_alloc_size = 0; + stat->active_buf = NULL; +} + +static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size) +{ + struct isp_device *isp = stat->isp; + int i; + + stat->buf_alloc_size = size; + + for (i = 0; i < STAT_MAX_BUFS; i++) { + struct ispstat_buffer *buf = &stat->buf[i]; + struct iovm_struct *iovm; + + WARN_ON(buf->dma_addr); + buf->iommu_addr = iommu_vmalloc(isp->iommu, 0, size, + IOMMU_FLAG); + if (IS_ERR((void *)buf->iommu_addr)) { + dev_err(stat->isp->dev, + "%s: Can't acquire memory for " + "buffer %d\n", stat->subdev.name, i); + isp_stat_bufs_free(stat); + return -ENOMEM; + } + + iovm = find_iovm_area(isp->iommu, buf->iommu_addr); + if (!iovm || + !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents, + DMA_FROM_DEVICE)) { + isp_stat_bufs_free(stat); + return -ENOMEM; + } + buf->iovm = iovm; + + buf->virt_addr = da_to_va(stat->isp->iommu, + (u32)buf->iommu_addr); + buf->empty = 1; + dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." + "iommu_addr=0x%08lx virt_addr=0x%08lx", + stat->subdev.name, i, buf->iommu_addr, + (unsigned long)buf->virt_addr); + } + + return 0; +} + +static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size) +{ + int i; + + stat->buf_alloc_size = size; + + for (i = 0; i < STAT_MAX_BUFS; i++) { + struct ispstat_buffer *buf = &stat->buf[i]; + + WARN_ON(buf->iommu_addr); + buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size, + &buf->dma_addr, GFP_KERNEL | GFP_DMA); + + if (!buf->virt_addr || !buf->dma_addr) { + dev_info(stat->isp->dev, + "%s: Can't acquire memory for " + "DMA buffer %d\n", stat->subdev.name, i); + isp_stat_bufs_free(stat); + return -ENOMEM; + } + buf->empty = 1; + + dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." + "dma_addr=0x%08lx virt_addr=0x%08lx\n", + stat->subdev.name, i, (unsigned long)buf->dma_addr, + (unsigned long)buf->virt_addr); + } + + return 0; +} + +static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size) +{ + unsigned long flags; + + spin_lock_irqsave(&stat->isp->stat_lock, flags); + + BUG_ON(stat->locked_buf != NULL); + + /* Are the old buffers big enough? */ + if (stat->buf_alloc_size >= size) { + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); + return 0; + } + + if (stat->state != ISPSTAT_DISABLED || stat->buf_processing) { + dev_info(stat->isp->dev, + "%s: trying to allocate memory when busy\n", + stat->subdev.name); + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); + return -EBUSY; + } + + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); + + isp_stat_bufs_free(stat); + + if (IS_COHERENT_BUF(stat)) + return isp_stat_bufs_alloc_dma(stat, size); + else + return isp_stat_bufs_alloc_iommu(stat, size); +} + +static void isp_stat_queue_event(struct ispstat *stat, int err) +{ + struct video_device *vdev = &stat->subdev.devnode; + struct v4l2_event event; + struct omap3isp_stat_event_status *status = (void *)event.u.data; + + memset(&event, 0, sizeof(event)); + if (!err) { + status->frame_number = stat->frame_number; + status->config_counter = stat->config_counter; + } else { + status->buf_err = 1; + } + event.type = stat->event_type; + v4l2_event_queue(vdev, &event); +} + + +/* + * omap3isp_stat_request_statistics - Request statistics. + * @data: Pointer to return statistics data. + * + * Returns 0 if successful. + */ +int omap3isp_stat_request_statistics(struct ispstat *stat, + struct omap3isp_stat_data *data) +{ + struct ispstat_buffer *buf; + + if (stat->state != ISPSTAT_ENABLED) { + dev_dbg(stat->isp->dev, "%s: engine not enabled.\n", + stat->subdev.name); + return -EINVAL; + } + + mutex_lock(&stat->ioctl_lock); + buf = isp_stat_buf_get(stat, data); + if (IS_ERR(buf)) { + mutex_unlock(&stat->ioctl_lock); + return PTR_ERR(buf); + } + + data->ts = buf->ts; + data->config_counter = buf->config_counter; + data->frame_number = buf->frame_number; + data->buf_size = buf->buf_size; + + buf->empty = 1; + isp_stat_buf_release(stat); + mutex_unlock(&stat->ioctl_lock); + + return 0; +} + +/* + * omap3isp_stat_config - Receives new statistic engine configuration. + * @new_conf: Pointer to config structure. + * + * Returns 0 if successful, -EINVAL if new_conf pointer is NULL, -ENOMEM if + * was unable to allocate memory for the buffer, or other errors if parameters + * are invalid. + */ +int omap3isp_stat_config(struct ispstat *stat, void *new_conf) +{ + int ret; + unsigned long irqflags; + struct ispstat_generic_config *user_cfg = new_conf; + u32 buf_size = user_cfg->buf_size; + + if (!new_conf) { + dev_dbg(stat->isp->dev, "%s: configuration is NULL\n", + stat->subdev.name); + return -EINVAL; + } + + mutex_lock(&stat->ioctl_lock); + + dev_dbg(stat->isp->dev, "%s: configuring module with buffer " + "size=0x%08lx\n", stat->subdev.name, (unsigned long)buf_size); + + ret = stat->ops->validate_params(stat, new_conf); + if (ret) { + mutex_unlock(&stat->ioctl_lock); + dev_dbg(stat->isp->dev, "%s: configuration values are " + "invalid.\n", stat->subdev.name); + return ret; + } + + if (buf_size != user_cfg->buf_size) + dev_dbg(stat->isp->dev, "%s: driver has corrected buffer size " + "request to 0x%08lx\n", stat->subdev.name, + (unsigned long)user_cfg->buf_size); + + /* + * Hack: H3A modules may need a doubled buffer size to avoid access + * to a invalid memory address after a SBL overflow. + * The buffer size is always PAGE_ALIGNED. + * Hack 2: MAGIC_SIZE is added to buf_size so a magic word can be + * inserted at the end to data integrity check purpose. + * Hack 3: AF module writes one paxel data more than it should, so + * the buffer allocation must consider it to avoid invalid memory + * access. + * Hack 4: H3A need to allocate extra space for the recover state. + */ + if (IS_H3A(stat)) { + buf_size = user_cfg->buf_size * 2 + MAGIC_SIZE; + if (IS_H3A_AF(stat)) + /* + * Adding one extra paxel data size for each recover + * buffer + 2 regular ones. + */ + buf_size += AF_EXTRA_DATA * (NUM_H3A_RECOVER_BUFS + 2); + if (stat->recover_priv) { + struct ispstat_generic_config *recover_cfg = + stat->recover_priv; + buf_size += recover_cfg->buf_size * + NUM_H3A_RECOVER_BUFS; + } + buf_size = PAGE_ALIGN(buf_size); + } else { /* Histogram */ + buf_size = PAGE_ALIGN(user_cfg->buf_size + MAGIC_SIZE); + } + + ret = isp_stat_bufs_alloc(stat, buf_size); + if (ret) { + mutex_unlock(&stat->ioctl_lock); + return ret; + } + + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); + stat->ops->set_params(stat, new_conf); + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + + /* + * Returning the right future config_counter for this setup, so + * userspace can *know* when it has been applied. + */ + user_cfg->config_counter = stat->config_counter + stat->inc_config; + + /* Module has a valid configuration. */ + stat->configured = 1; + dev_dbg(stat->isp->dev, "%s: module has been successfully " + "configured.\n", stat->subdev.name); + + mutex_unlock(&stat->ioctl_lock); + + return 0; +} + +/* + * isp_stat_buf_process - Process statistic buffers. + * @buf_state: points out if buffer is ready to be processed. It's necessary + * because histogram needs to copy the data from internal memory + * before be able to process the buffer. + */ +static int isp_stat_buf_process(struct ispstat *stat, int buf_state) +{ + int ret = STAT_NO_BUF; + + if (!atomic_add_unless(&stat->buf_err, -1, 0) && + buf_state == STAT_BUF_DONE && stat->state == ISPSTAT_ENABLED) { + ret = isp_stat_buf_queue(stat); + isp_stat_buf_next(stat); + } + + return ret; +} + +int omap3isp_stat_pcr_busy(struct ispstat *stat) +{ + return stat->ops->busy(stat); +} + +int omap3isp_stat_busy(struct ispstat *stat) +{ + return omap3isp_stat_pcr_busy(stat) | stat->buf_processing | + (stat->state != ISPSTAT_DISABLED); +} + +/* + * isp_stat_pcr_enable - Disables/Enables statistic engines. + * @pcr_enable: 0/1 - Disables/Enables the engine. + * + * Must be called from ISP driver when the module is idle and synchronized + * with CCDC. + */ +static void isp_stat_pcr_enable(struct ispstat *stat, u8 pcr_enable) +{ + if ((stat->state != ISPSTAT_ENABLING && + stat->state != ISPSTAT_ENABLED) && pcr_enable) + /* Userspace has disabled the module. Aborting. */ + return; + + stat->ops->enable(stat, pcr_enable); + if (stat->state == ISPSTAT_DISABLING && !pcr_enable) + stat->state = ISPSTAT_DISABLED; + else if (stat->state == ISPSTAT_ENABLING && pcr_enable) + stat->state = ISPSTAT_ENABLED; +} + +void omap3isp_stat_suspend(struct ispstat *stat) +{ + unsigned long flags; + + spin_lock_irqsave(&stat->isp->stat_lock, flags); + + if (stat->state != ISPSTAT_DISABLED) + stat->ops->enable(stat, 0); + if (stat->state == ISPSTAT_ENABLED) + stat->state = ISPSTAT_SUSPENDED; + + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); +} + +void omap3isp_stat_resume(struct ispstat *stat) +{ + /* Module will be re-enabled with its pipeline */ + if (stat->state == ISPSTAT_SUSPENDED) + stat->state = ISPSTAT_ENABLING; +} + +static void isp_stat_try_enable(struct ispstat *stat) +{ + unsigned long irqflags; + + if (stat->priv == NULL) + /* driver wasn't initialised */ + return; + + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); + if (stat->state == ISPSTAT_ENABLING && !stat->buf_processing && + stat->buf_alloc_size) { + /* + * Userspace's requested to enable the engine but it wasn't yet. + * Let's do that now. + */ + stat->update = 1; + isp_stat_buf_next(stat); + stat->ops->setup_regs(stat, stat->priv); + isp_stat_buf_insert_magic(stat, stat->active_buf); + + /* + * H3A module has some hw issues which forces the driver to + * ignore next buffers even if it was disabled in the meantime. + * On the other hand, Histogram shouldn't ignore buffers anymore + * if it's being enabled. + */ + if (!IS_H3A(stat)) + atomic_set(&stat->buf_err, 0); + + isp_stat_pcr_enable(stat, 1); + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + dev_dbg(stat->isp->dev, "%s: module is enabled.\n", + stat->subdev.name); + } else { + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + } +} + +void omap3isp_stat_isr_frame_sync(struct ispstat *stat) +{ + isp_stat_try_enable(stat); +} + +void omap3isp_stat_sbl_overflow(struct ispstat *stat) +{ + unsigned long irqflags; + + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); + /* + * Due to a H3A hw issue which prevents the next buffer to start from + * the correct memory address, 2 buffers must be ignored. + */ + atomic_set(&stat->buf_err, 2); + + /* + * If more than one SBL overflow happen in a row, H3A module may access + * invalid memory region. + * stat->sbl_ovl_recover is set to tell to the driver to temporarily use + * a soft configuration which helps to avoid consecutive overflows. + */ + if (stat->recover_priv) + stat->sbl_ovl_recover = 1; + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); +} + +/* + * omap3isp_stat_enable - Disable/Enable statistic engine as soon as possible + * @enable: 0/1 - Disables/Enables the engine. + * + * Client should configure all the module registers before this. + * This function can be called from a userspace request. + */ +int omap3isp_stat_enable(struct ispstat *stat, u8 enable) +{ + unsigned long irqflags; + + dev_dbg(stat->isp->dev, "%s: user wants to %s module.\n", + stat->subdev.name, enable ? "enable" : "disable"); + + /* Prevent enabling while configuring */ + mutex_lock(&stat->ioctl_lock); + + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); + + if (!stat->configured && enable) { + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + mutex_unlock(&stat->ioctl_lock); + dev_dbg(stat->isp->dev, "%s: cannot enable module as it's " + "never been successfully configured so far.\n", + stat->subdev.name); + return -EINVAL; + } + + if (enable) { + if (stat->state == ISPSTAT_DISABLING) + /* Previous disabling request wasn't done yet */ + stat->state = ISPSTAT_ENABLED; + else if (stat->state == ISPSTAT_DISABLED) + /* Module is now being enabled */ + stat->state = ISPSTAT_ENABLING; + } else { + if (stat->state == ISPSTAT_ENABLING) { + /* Previous enabling request wasn't done yet */ + stat->state = ISPSTAT_DISABLED; + } else if (stat->state == ISPSTAT_ENABLED) { + /* Module is now being disabled */ + stat->state = ISPSTAT_DISABLING; + isp_stat_buf_clear(stat); + } + } + + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + mutex_unlock(&stat->ioctl_lock); + + return 0; +} + +int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ispstat *stat = v4l2_get_subdevdata(subdev); + + if (enable) { + /* + * Only set enable PCR bit if the module was previously + * enabled through ioct. + */ + isp_stat_try_enable(stat); + } else { + unsigned long flags; + /* Disable PCR bit and config enable field */ + omap3isp_stat_enable(stat, 0); + spin_lock_irqsave(&stat->isp->stat_lock, flags); + stat->ops->enable(stat, 0); + spin_unlock_irqrestore(&stat->isp->stat_lock, flags); + + /* + * If module isn't busy, a new interrupt may come or not to + * set the state to DISABLED. As Histogram needs to read its + * internal memory to clear it, let interrupt handler + * responsible of changing state to DISABLED. If the last + * interrupt is coming, it's still safe as the handler will + * ignore the second time when state is already set to DISABLED. + * It's necessary to synchronize Histogram with streamoff, once + * the module may be considered idle before last SDMA transfer + * starts if we return here. + */ + if (!omap3isp_stat_pcr_busy(stat)) + omap3isp_stat_isr(stat); + + dev_dbg(stat->isp->dev, "%s: module is being disabled\n", + stat->subdev.name); + } + + return 0; +} + +/* + * __stat_isr - Interrupt handler for statistic drivers + */ +static void __stat_isr(struct ispstat *stat, int from_dma) +{ + int ret = STAT_BUF_DONE; + int buf_processing; + unsigned long irqflags; + struct isp_pipeline *pipe; + + /* + * stat->buf_processing must be set before disable module. It's + * necessary to not inform too early the buffers aren't busy in case + * of SDMA is going to be used. + */ + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); + if (stat->state == ISPSTAT_DISABLED) { + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + return; + } + buf_processing = stat->buf_processing; + stat->buf_processing = 1; + stat->ops->enable(stat, 0); + + if (buf_processing && !from_dma) { + if (stat->state == ISPSTAT_ENABLED) { + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + dev_err(stat->isp->dev, + "%s: interrupt occurred when module was still " + "processing a buffer.\n", stat->subdev.name); + ret = STAT_NO_BUF; + goto out; + } else { + /* + * Interrupt handler was called from streamoff when + * the module wasn't busy anymore to ensure it is being + * disabled after process last buffer. If such buffer + * processing has already started, no need to do + * anything else. + */ + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + return; + } + } + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + + /* If it's busy we can't process this buffer anymore */ + if (!omap3isp_stat_pcr_busy(stat)) { + if (!from_dma && stat->ops->buf_process) + /* Module still need to copy data to buffer. */ + ret = stat->ops->buf_process(stat); + if (ret == STAT_BUF_WAITING_DMA) + /* Buffer is not ready yet */ + return; + + spin_lock_irqsave(&stat->isp->stat_lock, irqflags); + + /* + * Histogram needs to read its internal memory to clear it + * before be disabled. For that reason, common statistic layer + * can return only after call stat's buf_process() operator. + */ + if (stat->state == ISPSTAT_DISABLING) { + stat->state = ISPSTAT_DISABLED; + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + stat->buf_processing = 0; + return; + } + pipe = to_isp_pipeline(&stat->subdev.entity); + stat->frame_number = atomic_read(&pipe->frame_number); + + /* + * Before this point, 'ret' stores the buffer's status if it's + * ready to be processed. Afterwards, it holds the status if + * it was processed successfully. + */ + ret = isp_stat_buf_process(stat, ret); + + if (likely(!stat->sbl_ovl_recover)) { + stat->ops->setup_regs(stat, stat->priv); + } else { + /* + * Using recover config to increase the chance to have + * a good buffer processing and make the H3A module to + * go back to a valid state. + */ + stat->update = 1; + stat->ops->setup_regs(stat, stat->recover_priv); + stat->sbl_ovl_recover = 0; + + /* + * Set 'update' in case of the module needs to use + * regular configuration after next buffer. + */ + stat->update = 1; + } + + isp_stat_buf_insert_magic(stat, stat->active_buf); + + /* + * Hack: H3A modules may access invalid memory address or send + * corrupted data to userspace if more than 1 SBL overflow + * happens in a row without re-writing its buffer's start memory + * address in the meantime. Such situation is avoided if the + * module is not immediately re-enabled when the ISR misses the + * timing to process the buffer and to setup the registers. + * Because of that, pcr_enable(1) was moved to inside this 'if' + * block. But the next interruption will still happen as during + * pcr_enable(0) the module was busy. + */ + isp_stat_pcr_enable(stat, 1); + spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); + } else { + /* + * If a SBL overflow occurs and the H3A driver misses the timing + * to process the buffer, stat->buf_err is set and won't be + * cleared now. So the next buffer will be correctly ignored. + * It's necessary due to a hw issue which makes the next H3A + * buffer to start from the memory address where the previous + * one stopped, instead of start where it was configured to. + * Do not "stat->buf_err = 0" here. + */ + + if (stat->ops->buf_process) + /* + * Driver may need to erase current data prior to + * process a new buffer. If it misses the timing, the + * next buffer might be wrong. So should be ignored. + * It happens only for Histogram. + */ + atomic_set(&stat->buf_err, 1); + + ret = STAT_NO_BUF; + dev_dbg(stat->isp->dev, "%s: cannot process buffer, " + "device is busy.\n", stat->subdev.name); + } + +out: + stat->buf_processing = 0; + isp_stat_queue_event(stat, ret != STAT_BUF_DONE); +} + +void omap3isp_stat_isr(struct ispstat *stat) +{ + __stat_isr(stat, 0); +} + +void omap3isp_stat_dma_isr(struct ispstat *stat) +{ + __stat_isr(stat, 1); +} + +static int isp_stat_init_entities(struct ispstat *stat, const char *name, + const struct v4l2_subdev_ops *sd_ops) +{ + struct v4l2_subdev *subdev = &stat->subdev; + struct media_entity *me = &subdev->entity; + + v4l2_subdev_init(subdev, sd_ops); + snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name); + subdev->grp_id = 1 << 16; /* group ID for isp subdevs */ + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->nevents = STAT_NEVENTS; + v4l2_set_subdevdata(subdev, stat); + + stat->pad.flags = MEDIA_PAD_FL_SINK; + me->ops = NULL; + + return media_entity_init(me, 1, &stat->pad, 0); +} + +int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct ispstat *stat = v4l2_get_subdevdata(subdev); + + if (sub->type != stat->event_type) + return -EINVAL; + + return v4l2_event_subscribe(fh, sub); +} + +int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} + +void omap3isp_stat_unregister_entities(struct ispstat *stat) +{ + media_entity_cleanup(&stat->subdev.entity); + v4l2_device_unregister_subdev(&stat->subdev); +} + +int omap3isp_stat_register_entities(struct ispstat *stat, + struct v4l2_device *vdev) +{ + return v4l2_device_register_subdev(vdev, &stat->subdev); +} + +int omap3isp_stat_init(struct ispstat *stat, const char *name, + const struct v4l2_subdev_ops *sd_ops) +{ + stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL); + if (!stat->buf) + return -ENOMEM; + isp_stat_buf_clear(stat); + mutex_init(&stat->ioctl_lock); + atomic_set(&stat->buf_err, 0); + + return isp_stat_init_entities(stat, name, sd_ops); +} + +void omap3isp_stat_free(struct ispstat *stat) +{ + isp_stat_bufs_free(stat); + kfree(stat->buf); +} diff --git a/drivers/media/video/omap3isp/ispstat.h b/drivers/media/video/omap3isp/ispstat.h new file mode 100644 index 0000000..820950c --- /dev/null +++ b/drivers/media/video/omap3isp/ispstat.h @@ -0,0 +1,169 @@ +/* + * ispstat.h + * + * TI OMAP3 ISP - Statistics core + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2009 Texas Instruments, Inc + * + * Contacts: David Cohen + * Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP3_ISP_STAT_H +#define OMAP3_ISP_STAT_H + +#include +#include +#include +#include + +#include "isp.h" +#include "ispvideo.h" + +#define STAT_MAX_BUFS 5 +#define STAT_NEVENTS 8 + +#define STAT_BUF_DONE 0 /* Buffer is ready */ +#define STAT_NO_BUF 1 /* An error has occurred */ +#define STAT_BUF_WAITING_DMA 2 /* Histogram only: DMA is running */ + +struct ispstat; + +struct ispstat_buffer { + unsigned long iommu_addr; + struct iovm_struct *iovm; + void *virt_addr; + dma_addr_t dma_addr; + struct timeval ts; + u32 buf_size; + u32 frame_number; + u16 config_counter; + u8 empty; +}; + +struct ispstat_ops { + /* + * Validate new params configuration. + * new_conf->buf_size value must be changed to the exact buffer size + * necessary for the new configuration if it's smaller. + */ + int (*validate_params)(struct ispstat *stat, void *new_conf); + + /* + * Save new params configuration. + * stat->priv->buf_size value must be set to the exact buffer size for + * the new configuration. + * stat->update is set to 1 if new configuration is different than + * current one. + */ + void (*set_params)(struct ispstat *stat, void *new_conf); + + /* Apply stored configuration. */ + void (*setup_regs)(struct ispstat *stat, void *priv); + + /* Enable/Disable module. */ + void (*enable)(struct ispstat *stat, int enable); + + /* Verify is module is busy. */ + int (*busy)(struct ispstat *stat); + + /* Used for specific operations during generic buf process task. */ + int (*buf_process)(struct ispstat *stat); +}; + +enum ispstat_state_t { + ISPSTAT_DISABLED = 0, + ISPSTAT_DISABLING, + ISPSTAT_ENABLED, + ISPSTAT_ENABLING, + ISPSTAT_SUSPENDED, +}; + +struct ispstat { + struct v4l2_subdev subdev; + struct media_pad pad; /* sink pad */ + + /* Control */ + unsigned configured:1; + unsigned update:1; + unsigned buf_processing:1; + unsigned sbl_ovl_recover:1; + u8 inc_config; + atomic_t buf_err; + enum ispstat_state_t state; /* enabling/disabling state */ + struct omap_dma_channel_params dma_config; + struct isp_device *isp; + void *priv; /* pointer to priv config struct */ + void *recover_priv; /* pointer to recover priv configuration */ + struct mutex ioctl_lock; /* serialize private ioctl */ + + const struct ispstat_ops *ops; + + /* Buffer */ + u8 wait_acc_frames; + u16 config_counter; + u32 frame_number; + u32 buf_size; + u32 buf_alloc_size; + int dma_ch; + unsigned long event_type; + struct ispstat_buffer *buf; + struct ispstat_buffer *active_buf; + struct ispstat_buffer *locked_buf; +}; + +struct ispstat_generic_config { + /* + * Fields must be in the same order as in: + * - isph3a_aewb_config + * - isph3a_af_config + * - isphist_config + */ + u32 buf_size; + u16 config_counter; +}; + +int omap3isp_stat_config(struct ispstat *stat, void *new_conf); +int omap3isp_stat_request_statistics(struct ispstat *stat, + struct omap3isp_stat_data *data); +int omap3isp_stat_init(struct ispstat *stat, const char *name, + const struct v4l2_subdev_ops *sd_ops); +void omap3isp_stat_free(struct ispstat *stat); +int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); +int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); +int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable); + +int omap3isp_stat_busy(struct ispstat *stat); +int omap3isp_stat_pcr_busy(struct ispstat *stat); +void omap3isp_stat_suspend(struct ispstat *stat); +void omap3isp_stat_resume(struct ispstat *stat); +int omap3isp_stat_enable(struct ispstat *stat, u8 enable); +void omap3isp_stat_sbl_overflow(struct ispstat *stat); +void omap3isp_stat_isr(struct ispstat *stat); +void omap3isp_stat_isr_frame_sync(struct ispstat *stat); +void omap3isp_stat_dma_isr(struct ispstat *stat); +int omap3isp_stat_register_entities(struct ispstat *stat, + struct v4l2_device *vdev); +void omap3isp_stat_unregister_entities(struct ispstat *stat); + +#endif /* OMAP3_ISP_STAT_H */ -- cgit v0.10.2 From 7e8970e1d5ae9237d00909599e70f85fce1fc489 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 12 Feb 2011 18:05:06 -0300 Subject: [media] omap3isp: Kconfig and Makefile Add the OMAP3 ISP driver to the kernel build system. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index f1bc3dc..1034f4a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4556,6 +4556,12 @@ L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/mach-omap2/omap_hwmod_44xx_data.c +OMAP IMAGE SIGNAL PROCESSOR (ISP) +M: Laurent Pinchart +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/video/omap3isp/* + OMAP USB SUPPORT M: Felipe Balbi M: David Brownell diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index e2f5a69..4498b94 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -746,6 +746,19 @@ config VIDEO_NOON010PC30 ---help--- This driver supports NOON010PC30 CIF camera from Siliconfile +config VIDEO_OMAP3 + tristate "OMAP 3 Camera support (EXPERIMENTAL)" + select OMAP_IOMMU + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL + ---help--- + Driver for an OMAP 3 camera controller. + +config VIDEO_OMAP3_DEBUG + bool "OMAP 3 Camera debug messages" + depends on VIDEO_OMAP3 + ---help--- + Enable debug messages on OMAP 3 camera controller driver. + config SOC_CAMERA tristate "SoC camera support" depends on VIDEO_V4L2 && HAS_DMA && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 7ea6516..ace5d8b 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -129,6 +129,8 @@ obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o +obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ + obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o diff --git a/drivers/media/video/omap3isp/Makefile b/drivers/media/video/omap3isp/Makefile new file mode 100644 index 0000000..b1b34477 --- /dev/null +++ b/drivers/media/video/omap3isp/Makefile @@ -0,0 +1,13 @@ +# Makefile for OMAP3 ISP driver + +ifdef CONFIG_VIDEO_OMAP3_DEBUG +EXTRA_CFLAGS += -DDEBUG +endif + +omap3-isp-objs += \ + isp.o ispqueue.o ispvideo.o \ + ispcsiphy.o ispccp2.o ispcsi2.o \ + ispccdc.o isppreview.o ispresizer.o \ + ispstat.o isph3a_aewb.o isph3a_af.o isphist.o + +obj-$(CONFIG_VIDEO_OMAP3) += omap3-isp.o diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 43918a3..75cf611 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -277,6 +277,7 @@ header-y += nfsacl.h header-y += nl80211.h header-y += nubus.h header-y += nvram.h +header-y += omap3isp.h header-y += omapfb.h header-y += oom.h header-y += param.h -- cgit v0.10.2 From 4b0ec19e02fabdd09fc2f4754304894bbefb3f45 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Mar 2011 10:05:22 -0300 Subject: [media] omap3isp: Add set performance callback in isp platform data Replace the omap_pm_set_min_bus_tput() with a callback to board code. This makes the omap3isp driver compilable as a module, and lets board code manage OMAP PM details depending on board requirements. Signed-off-by: Stanimir Varbanov Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h index a8dfea4..cf5214e 100644 --- a/drivers/media/video/omap3isp/isp.h +++ b/drivers/media/video/omap3isp/isp.h @@ -199,6 +199,7 @@ struct isp_v4l2_subdevs_group { struct isp_platform_data { struct isp_v4l2_subdevs_group *subdevs; + void (*set_constraints)(struct isp_device *isp, bool enable); }; struct isp_platform_callback { diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index 517a24d..a0bb5db 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -921,18 +921,8 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe->output = far_end; } - /* Make sure the interconnect clock runs fast enough. - * - * Formula from: resource34xx.c set_opp() - * If MPU freq is above 500MHz, make sure the interconnect - * is at 100Mhz or above. - * throughput in KiB/s for 100 Mhz = 100 * 1000 * 4. - * - * We want to be fast enough then set OCP clock to be max as - * possible, in that case 185Mhz then: - * throughput in KiB/s for 185Mhz = 185 * 1000 * 4 = 740000 KiB/s - */ - omap_pm_set_min_bus_tput(video->isp->dev, OCP_INITIATOR_AGENT, 740000); + if (video->isp->pdata->set_constraints) + video->isp->pdata->set_constraints(video->isp, true); pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); /* Validate the pipeline and update its state. */ @@ -978,8 +968,8 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) error: if (ret < 0) { omap3isp_video_queue_streamoff(&vfh->queue); - omap_pm_set_min_bus_tput(video->isp->dev, - OCP_INITIATOR_AGENT, 0); + if (video->isp->pdata->set_constraints) + video->isp->pdata->set_constraints(video->isp, false); media_entity_pipeline_stop(&video->video.entity); video->queue = NULL; } @@ -1032,7 +1022,8 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) video->queue = NULL; video->streaming = 0; - omap_pm_set_min_bus_tput(video->isp->dev, OCP_INITIATOR_AGENT, 0); + if (video->isp->pdata->set_constraints) + video->isp->pdata->set_constraints(video->isp, false); media_entity_pipeline_stop(&video->video.entity); done: -- cgit v0.10.2 From 474966ee01cc877e28abed3ada5b48a963c58695 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 5 Mar 2011 17:14:33 -0300 Subject: [media] media: Pick a free ioctls range Pick an unused range of ioctls in Documentation/ioctl/ioctl-number.txt and use it for the MEDIA_IOC_* ioctls. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index ac293e9..2b8c059 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -272,6 +272,7 @@ Code Seq#(hex) Include File Comments 'z' 40-7F CAN bus card conflict! 'z' 10-4F drivers/s390/crypto/zcrypt_api.h conflict! +'|' 00-7F linux/media.h 0x80 00-1F linux/fb.h 0x89 00-06 arch/x86/include/asm/sockios.h 0x89 0B-DF linux/sockios.h diff --git a/include/linux/media.h b/include/linux/media.h index 7ed23b4..0ef8833 100644 --- a/include/linux/media.h +++ b/include/linux/media.h @@ -124,9 +124,9 @@ struct media_links_enum { __u32 reserved[4]; }; -#define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) -#define MEDIA_IOC_ENUM_ENTITIES _IOWR('M', 2, struct media_entity_desc) -#define MEDIA_IOC_ENUM_LINKS _IOWR('M', 3, struct media_links_enum) -#define MEDIA_IOC_SETUP_LINK _IOWR('M', 4, struct media_link_desc) +#define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info) +#define MEDIA_IOC_ENUM_ENTITIES _IOWR('|', 0x01, struct media_entity_desc) +#define MEDIA_IOC_ENUM_LINKS _IOWR('|', 0x02, struct media_links_enum) +#define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc) #endif /* __LINUX_MEDIA_H */ -- cgit v0.10.2 From e83dd485ed04b21215c1283042e8d4712ab1a675 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 7 Mar 2011 14:20:52 -0300 Subject: [media] omap3isp: Add documentation Add documentation on the OMAP 3 ISP driver. Document the subdevs, V4L2 events and private IOCTLs the driver implements Signed-off-by: Sakari Ailus Signed-off-by: David Cohen Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/omap3isp.txt b/Documentation/video4linux/omap3isp.txt new file mode 100644 index 0000000..69be2c7 --- /dev/null +++ b/Documentation/video4linux/omap3isp.txt @@ -0,0 +1,278 @@ +OMAP 3 Image Signal Processor (ISP) driver + +Copyright (C) 2010 Nokia Corporation +Copyright (C) 2009 Texas Instruments, Inc. + +Contacts: Laurent Pinchart + Sakari Ailus + David Cohen + + +Introduction +============ + +This file documents the Texas Instruments OMAP 3 Image Signal Processor (ISP) +driver located under drivers/media/video/omap3isp. The original driver was +written by Texas Instruments but since that it has been rewritten (twice) at +Nokia. + +The driver has been successfully used on the following versions of OMAP 3: + + 3430 + 3530 + 3630 + +The driver implements V4L2, Media controller and v4l2_subdev interfaces. +Sensor, lens and flash drivers using the v4l2_subdev interface in the kernel +are supported. + + +Split to subdevs +================ + +The OMAP 3 ISP is split into V4L2 subdevs, each of the blocks inside the ISP +having one subdev to represent it. Each of the subdevs provide a V4L2 subdev +interface to userspace. + + OMAP3 ISP CCP2 + OMAP3 ISP CSI2a + OMAP3 ISP CCDC + OMAP3 ISP preview + OMAP3 ISP resizer + OMAP3 ISP AEWB + OMAP3 ISP AF + OMAP3 ISP histogram + +Each possible link in the ISP is modelled by a link in the Media controller +interface. For an example program see [2]. + + +Controlling the OMAP 3 ISP +========================== + +In general, the settings given to the OMAP 3 ISP take effect at the beginning +of the following frame. This is done when the module becomes idle during the +vertical blanking period on the sensor. In memory-to-memory operation the pipe +is run one frame at a time. Applying the settings is done between the frames. + +All the blocks in the ISP, excluding the CSI-2 and possibly the CCP2 receiver, +insist on receiving complete frames. Sensors must thus never send the ISP +partial frames. + +Autoidle does have issues with some ISP blocks on the 3430, at least. +Autoidle is only enabled on 3630 when the omap3isp module parameter autoidle +is non-zero. + + +Events +====== + +The OMAP 3 ISP driver does support the V4L2 event interface on CCDC and +statistics (AEWB, AF and histogram) subdevs. + +The CCDC subdev produces V4L2_EVENT_OMAP3ISP_HS_VS type event on HS_VS +interrupt which is used to signal frame start. The event is triggered exactly +when the reception of the first line of the frame starts in the CCDC module. +The event can be subscribed on the CCDC subdev. + +(When using parallel interface one must pay account to correct configuration +of the VS signal polarity. This is automatically correct when using the serial +receivers.) + +Each of the statistics subdevs is able to produce events. An event is +generated whenever a statistics buffer can be dequeued by a user space +application using the VIDIOC_OMAP3ISP_STAT_REQ IOCTL. The events available +are: + + V4L2_EVENT_OMAP3ISP_AEWB + V4L2_EVENT_OMAP3ISP_AF + V4L2_EVENT_OMAP3ISP_HIST + +The type of the event data is struct omap3isp_stat_event_status for these +ioctls. If there is an error calculating the statistics, there will be an +event as usual, but no related statistics buffer. In this case +omap3isp_stat_event_status.buf_err is set to non-zero. + + +Private IOCTLs +============== + +The OMAP 3 ISP driver supports standard V4L2 IOCTLs and controls where +possible and practical. Much of the functions provided by the ISP, however, +does not fall under the standard IOCTLs --- gamma tables and configuration of +statistics collection are examples of such. + +In general, there is a private ioctl for configuring each of the blocks +containing hardware-dependent functions. + +The following private IOCTLs are supported: + + VIDIOC_OMAP3ISP_CCDC_CFG + VIDIOC_OMAP3ISP_PRV_CFG + VIDIOC_OMAP3ISP_AEWB_CFG + VIDIOC_OMAP3ISP_HIST_CFG + VIDIOC_OMAP3ISP_AF_CFG + VIDIOC_OMAP3ISP_STAT_REQ + VIDIOC_OMAP3ISP_STAT_EN + +The parameter structures used by these ioctls are described in +include/linux/omap3isp.h. The detailed functions of the ISP itself related to +a given ISP block is described in the Technical Reference Manuals (TRMs) --- +see the end of the document for those. + +While it is possible to use the ISP driver without any use of these private +IOCTLs it is not possible to obtain optimal image quality this way. The AEWB, +AF and histogram modules cannot be used without configuring them using the +appropriate private IOCTLs. + + +CCDC and preview block IOCTLs +============================= + +The VIDIOC_OMAP3ISP_CCDC_CFG and VIDIOC_OMAP3ISP_PRV_CFG IOCTLs are used to +configure, enable and disable functions in the CCDC and preview blocks, +respectively. Both IOCTLs control several functions in the blocks they +control. VIDIOC_OMAP3ISP_CCDC_CFG IOCTL accepts a pointer to struct +omap3isp_ccdc_update_config as its argument. Similarly VIDIOC_OMAP3ISP_PRV_CFG +accepts a pointer to struct omap3isp_prev_update_config. The definition of +both structures is available in [1]. + +The update field in the structures tells whether to update the configuration +for the specific function and the flag tells whether to enable or disable the +function. + +The update and flag bit masks accept the following values. Each separate +functions in the CCDC and preview blocks is associated with a flag (either +disable or enable; part of the flag field in the structure) and a pointer to +configuration data for the function. + +Valid values for the update and flag fields are listed here for +VIDIOC_OMAP3ISP_CCDC_CFG. Values may be or'ed to configure more than one +function in the same IOCTL call. + + OMAP3ISP_CCDC_ALAW + OMAP3ISP_CCDC_LPF + OMAP3ISP_CCDC_BLCLAMP + OMAP3ISP_CCDC_BCOMP + OMAP3ISP_CCDC_FPC + OMAP3ISP_CCDC_CULL + OMAP3ISP_CCDC_CONFIG_LSC + OMAP3ISP_CCDC_TBL_LSC + +The corresponding values for the VIDIOC_OMAP3ISP_PRV_CFG are here: + + OMAP3ISP_PREV_LUMAENH + OMAP3ISP_PREV_INVALAW + OMAP3ISP_PREV_HRZ_MED + OMAP3ISP_PREV_CFA + OMAP3ISP_PREV_CHROMA_SUPP + OMAP3ISP_PREV_WB + OMAP3ISP_PREV_BLKADJ + OMAP3ISP_PREV_RGB2RGB + OMAP3ISP_PREV_COLOR_CONV + OMAP3ISP_PREV_YC_LIMIT + OMAP3ISP_PREV_DEFECT_COR + OMAP3ISP_PREV_GAMMABYPASS + OMAP3ISP_PREV_DRK_FRM_CAPTURE + OMAP3ISP_PREV_DRK_FRM_SUBTRACT + OMAP3ISP_PREV_LENS_SHADING + OMAP3ISP_PREV_NF + OMAP3ISP_PREV_GAMMA + +The associated configuration pointer for the function may not be NULL when +enabling the function. When disabling a function the configuration pointer is +ignored. + + +Statistic blocks IOCTLs +======================= + +The statistics subdevs do offer more dynamic configuration options than the +other subdevs. They can be enabled, disable and reconfigured when the pipeline +is in streaming state. + +The statistics blocks always get the input image data from the CCDC (as the +histogram memory read isn't implemented). The statistics are dequeueable by +the user from the statistics subdev nodes using private IOCTLs. + +The private IOCTLs offered by the AEWB, AF and histogram subdevs are heavily +reflected by the register level interface offered by the ISP hardware. There +are aspects that are purely related to the driver implementation and these are +discussed next. + +VIDIOC_OMAP3ISP_STAT_EN +----------------------- + +This private IOCTL enables/disables a statistic module. If this request is +done before streaming, it will take effect as soon as the pipeline starts to +stream. If the pipeline is already streaming, it will take effect as soon as +the CCDC becomes idle. + +VIDIOC_OMAP3ISP_AEWB_CFG, VIDIOC_OMAP3ISP_HIST_CFG and VIDIOC_OMAP3ISP_AF_CFG +----------------------------------------------------------------------------- + +Those IOCTLs are used to configure the modules. They require user applications +to have an in-depth knowledge of the hardware. Most of the fields explanation +can be found on OMAP's TRMs. The two following fields common to all the above +configure private IOCTLs require explanation for better understanding as they +are not part of the TRM. + +omap3isp_[h3a_af/h3a_aewb/hist]_config.buf_size: + +The modules handle their buffers internally. The necessary buffer size for the +module's data output depends on the requested configuration. Although the +driver supports reconfiguration while streaming, it does not support a +reconfiguration which requires bigger buffer size than what is already +internally allocated if the module is enabled. It will return -EBUSY on this +case. In order to avoid such condition, either disable/reconfigure/enable the +module or request the necessary buffer size during the first configuration +while the module is disabled. + +The internal buffer size allocation considers the requested configuration's +minimum buffer size and the value set on buf_size field. If buf_size field is +out of [minimum, maximum] buffer size range, it's clamped to fit in there. +The driver then selects the biggest value. The corrected buf_size value is +written back to user application. + +omap3isp_[h3a_af/h3a_aewb/hist]_config.config_counter: + +As the configuration doesn't take effect synchronously to the request, the +driver must provide a way to track this information to provide more accurate +data. After a configuration is requested, the config_counter returned to user +space application will be an unique value associated to that request. When +user application receives an event for buffer availability or when a new +buffer is requested, this config_counter is used to match a buffer data and a +configuration. + +VIDIOC_OMAP3ISP_STAT_REQ +------------------------ + +Send to user space the oldest data available in the internal buffer queue and +discards such buffer afterwards. The field omap3isp_stat_data.frame_number +matches with the video buffer's field_count. + + +Technical reference manuals (TRMs) and other documentation +========================================================== + +OMAP 3430 TRM: + +Referenced 2011-03-05. + +OMAP 35xx TRM: + Referenced 2011-03-05. + +OMAP 3630 TRM: + +Referenced 2011-03-05. + +DM 3730 TRM: + Referenced 2011-03-06. + + +References +========== + +[1] include/linux/omap3isp.h + +[2] http://git.ideasonboard.org/?p=media-ctl.git;a=summary -- cgit v0.10.2 From aa40d194975e23ba7a55936c7b5dae4e964461b5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 Mar 2011 09:24:32 -0300 Subject: [media] tuner-xc2028.c: fix compile warning drivers/media/common/tuners/tuner-xc2028.c: In function 'generic_set_freq': drivers/media/common/tuners/tuner-xc2028.c:936:15: warning: comparison between 'enum tuner_mode' and 'enum v4l2_tuner_type' Argument type was enum tuner_mode, but should have been enum v4l2_tuner_type. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index efcbc3e..16fba6b 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -907,7 +907,7 @@ ret: #define DIV 15625 static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, - enum tuner_mode new_mode, + enum v4l2_tuner_type new_type, unsigned int type, v4l2_std_id std, u16 int_freq) @@ -933,7 +933,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, * that xc2028 will be in a safe state. * Maybe this might also be needed for DTV. */ - if (new_mode == V4L2_TUNER_ANALOG_TV) { + if (new_type == V4L2_TUNER_ANALOG_TV) { rc = send_seq(priv, {0x00, 0x00}); /* Analog modes require offset = 0 */ -- cgit v0.10.2 From fb661a78699ad34c385a014fe0d17226234f8496 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 Mar 2011 09:26:24 -0300 Subject: [media] stv0367.c: fix compiler warning drivers/media/dvb/frontends/stv0367.c: In function 'stv0367ter_lock_algo': drivers/media/dvb/frontends/stv0367.c:1315:2: warning: cast from pointer to integer of different size Use %p to print addresses instead of 0x%x. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c index eecdf23..f910d0d 100644 --- a/drivers/media/dvb/frontends/stv0367.c +++ b/drivers/media/dvb/frontends/stv0367.c @@ -1312,7 +1312,7 @@ stv0367_ter_signal_type stv0367ter_lock_algo(struct stv0367_state *state) tmp = stv0367_readreg(state, R367TER_SYR_STAT); tmp2 = stv0367_readreg(state, R367TER_STATUS); - dprintk("state=0x%x\n", (int)state); + dprintk("state=%p\n", state); dprintk("LOCK OK! mode=%d SYR_STAT=0x%x R367TER_STATUS=0x%x\n", mode, tmp, tmp2); -- cgit v0.10.2 From 3920d6b34d7f6d4e81773b6bbd93d53c167b048f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 Mar 2011 09:27:44 -0300 Subject: [media] altera-ci.c: fix compiler warnings drivers/media/video/cx23885/altera-ci.c: In function 'altera_hw_filt_init': drivers/media/video/cx23885/altera-ci.c:671:2: warning: cast from pointer to integer of different size drivers/media/video/cx23885/altera-ci.c: In function 'altera_ci_init': drivers/media/video/cx23885/altera-ci.c:739:2: warning: cast from pointer to integer of different size Use %p instead of 0x%x to print addresses. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/altera-ci.c b/drivers/media/video/cx23885/altera-ci.c index ad6cc68..4269e05 100644 --- a/drivers/media/video/cx23885/altera-ci.c +++ b/drivers/media/video/cx23885/altera-ci.c @@ -668,8 +668,8 @@ int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr) ci_dbg_print("%s: Create New Internal Structure!\n", __func__); } - ci_dbg_print("%s: setting hw pid filter = 0x%x for ci = %d\n", __func__, - (int)pid_filt, hw_filt_nr - 1); + ci_dbg_print("%s: setting hw pid filter = %p for ci = %d\n", __func__, + pid_filt, hw_filt_nr - 1); inter->pid_filt[hw_filt_nr - 1] = pid_filt; pid_filt->demux = config->demux; pid_filt->internal = inter; @@ -736,8 +736,8 @@ int altera_ci_init(struct altera_ci_config *config, int ci_nr) ci_dbg_print("%s: Create New Internal Structure!\n", __func__); } - ci_dbg_print("%s: setting state = 0x%x for ci = %d\n", __func__, - (int)state, ci_nr - 1); + ci_dbg_print("%s: setting state = %p for ci = %d\n", __func__, + state, ci_nr - 1); inter->state[ci_nr - 1] = state; state->internal = inter; state->nr = ci_nr - 1; -- cgit v0.10.2 From cfa746b39c2db17f2fde301e347324ac89fc5ea9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 Mar 2011 09:30:02 -0300 Subject: [media] fmdrv_common.c: fix compiler warning drivers/media/radio/wl128x/fmdrv_common.c: In function 'recv_tasklet': drivers/media/radio/wl128x/fmdrv_common.c:274:4: warning: format '%d' expects type 'int', but argument 4 has type 'long unsigned int' The result of sizeof() should be printed with %zu. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 12f4c65..64454d3 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -271,8 +271,8 @@ static void recv_tasklet(unsigned long arg) /* Process all packets in the RX queue */ while ((skb = skb_dequeue(&fmdev->rx_q))) { if (skb->len < sizeof(struct fm_event_msg_hdr)) { - fmerr("skb(%p) has only %d bytes" - "atleast need %d bytes to decode\n", skb, + fmerr("skb(%p) has only %d bytes, " + "at least need %zu bytes to decode\n", skb, skb->len, sizeof(struct fm_event_msg_hdr)); kfree_skb(skb); continue; -- cgit v0.10.2 From d6972cc8a35514a518ef9c9f608327851cd2e856 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 Mar 2011 09:31:00 -0300 Subject: [media] cx88-alsa: fix compiler warning drivers/media/video/cx88/cx88-alsa.c: In function 'cx88_audio_initdev': drivers/media/video/cx88/cx88-alsa.c:881:20: warning: 'core' may be used uninitialized in this function The compiler doesn't understand that snd_cx88_create fills in the core pointer. So just initialize it to NULL. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index a2d688e..423c1af 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -878,7 +878,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, { struct snd_card *card; snd_cx88_card_t *chip; - struct cx88_core *core; + struct cx88_core *core = NULL; int err; if (devno >= SNDRV_CARDS) -- cgit v0.10.2 From fa9448d9bdbae42bb10545f6fcfc980f38f53aa8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 6 Mar 2011 09:49:37 -0300 Subject: [media] altera-ci.h: add missing inline Functions defined in a header should be static inline. This prevents compile warnings like: 'altera_ci_tuner_reset' defined but not used (Actually appeared in the media backwards compatibility build). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/altera-ci.h b/drivers/media/video/cx23885/altera-ci.h index bf0aa2c..70e4fd6 100644 --- a/drivers/media/video/cx23885/altera-ci.h +++ b/drivers/media/video/cx23885/altera-ci.h @@ -68,7 +68,7 @@ static inline int altera_ci_irq(void *dev) return 0; } -static int altera_ci_tuner_reset(void *dev, int ci_nr) +static inline int altera_ci_tuner_reset(void *dev, int ci_nr) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return 0; -- cgit v0.10.2 From 972d724caf8b6c461b585c800d6460a12c098a5e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 11 Mar 2011 16:51:18 -0300 Subject: [media] several drivers: Fix a few gcc 4.6 warnings au0828-dvb.c:99:6: warning: variable 'ptr' set but not used [-Wunused-but-set-variable] au0828-video.c:1180:25: warning: variable 'maxheight' set but not used [-Wunused-but-set-variable] au0828-video.c:1180:15: warning: variable 'maxwidth' set but not used [-Wunused-but-set-variable] bttv-input.c:196:16: warning: variable 'current_jiffies' set but not used [-Wunused-but-set-variable] Those variables are not used at all, so just remove them. Cc: Steven Toth Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index f1edf1d..5182167 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -96,7 +96,6 @@ static struct tda18271_config hauppauge_woodbury_tunerconfig = { /*-------------------------------------------------------------------*/ static void urb_completion(struct urb *purb) { - u8 *ptr; struct au0828_dev *dev = purb->context; int ptype = usb_pipetype(purb->pipe); @@ -114,8 +113,6 @@ static void urb_completion(struct urb *purb) return; } - ptr = (u8 *)purb->transfer_buffer; - /* Feed the transport payload into the kernel demux */ dvb_dmx_swfilter_packets(&dev->dvb.demux, purb->transfer_buffer, purb->actual_length / 188); diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 9c475c6..6ad83a1 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -1177,10 +1177,6 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, int ret; int width = format->fmt.pix.width; int height = format->fmt.pix.height; - unsigned int maxwidth, maxheight; - - maxwidth = 720; - maxheight = 480; if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index e8b64bc..677d70c 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -193,12 +193,10 @@ static void bttv_rc5_timer_end(unsigned long data) { struct bttv_ir *ir = (struct bttv_ir *)data; struct timeval tv; - unsigned long current_jiffies; u32 gap; u32 rc5 = 0; /* get time */ - current_jiffies = jiffies; do_gettimeofday(&tv); /* avoid overflow with gap >1s */ -- cgit v0.10.2 From 1039752353f1308bcd594a79196f60a22d45975d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 11 Mar 2011 17:00:01 -0300 Subject: [media] cpia2: Fix some gcc 4.6 warnings when debug is disabled cpia2_core.c:529:14: warning: variable 'dir' set but not used [-Wunused-but-set-variable] cpia2_core.c:526:5: warning: variable 'block_index' set but not used [-Wunused-but-set-variable] Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c index aaffca8..ee91e295 100644 --- a/drivers/media/video/cpia2/cpia2_core.c +++ b/drivers/media/video/cpia2/cpia2_core.c @@ -519,22 +519,16 @@ int cpia2_do_command(struct camera_data *cam, * cpia2_send_command * *****************************************************************************/ + +#define DIR(cmd) ((cmd->direction == TRANSFER_WRITE) ? "Write" : "Read") +#define BINDEX(cmd) (cmd->req_mode & 0x03) + int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd) { u8 count; u8 start; - u8 block_index; u8 *buffer; int retval; - const char* dir; - - if (cmd->direction == TRANSFER_WRITE) { - dir = "Write"; - } else { - dir = "Read"; - } - - block_index = cmd->req_mode & 0x03; switch (cmd->req_mode & 0x0c) { case CAMERAACCESS_TYPE_RANDOM: @@ -542,32 +536,32 @@ int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd) start = 0; buffer = (u8 *) & cmd->buffer; if (debugs_on & DEBUG_REG) - DBG("%s Random: Register block %s\n", dir, - block_name[block_index]); + DBG("%s Random: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); break; case CAMERAACCESS_TYPE_BLOCK: count = cmd->reg_count; start = cmd->start; buffer = cmd->buffer.block_data; if (debugs_on & DEBUG_REG) - DBG("%s Block: Register block %s\n", dir, - block_name[block_index]); + DBG("%s Block: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); break; case CAMERAACCESS_TYPE_MASK: count = cmd->reg_count * sizeof(struct cpia2_reg_mask); start = 0; buffer = (u8 *) & cmd->buffer; if (debugs_on & DEBUG_REG) - DBG("%s Mask: Register block %s\n", dir, - block_name[block_index]); + DBG("%s Mask: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); break; case CAMERAACCESS_TYPE_REPEAT: /* For patch blocks only */ count = cmd->reg_count; start = cmd->start; buffer = cmd->buffer.block_data; if (debugs_on & DEBUG_REG) - DBG("%s Repeat: Register block %s\n", dir, - block_name[block_index]); + DBG("%s Repeat: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); break; default: LOG("%s: invalid request mode\n",__func__); @@ -584,10 +578,10 @@ int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd) for (i = 0; i < cmd->reg_count; i++) { if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_BLOCK) KINFO("%s Block: [0x%02X] = 0x%02X\n", - dir, start + i, buffer[i]); + DIR(cmd), start + i, buffer[i]); if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_RANDOM) KINFO("%s Random: [0x%02X] = 0x%02X\n", - dir, cmd->buffer.registers[i].index, + DIR(cmd), cmd->buffer.registers[i].index, cmd->buffer.registers[i].value); } } -- cgit v0.10.2 From d164460f897e2ae8c48ca28c763bb1233d180e7e Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sun, 2 Jan 2011 09:10:00 -0300 Subject: [media] cx23885: Altera FPGA CI interface reworked It decreases I2C traffic. Signed-off-by: Abylay Ospan Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index cf36b9b..c0e6655 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -620,29 +620,29 @@ int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; unsigned long timeout = jiffies + msecs_to_jiffies(1); - int mem = 0; + uint32_t mem = 0; - cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS); + mem = cx_read(MC417_RWD); if (read) cx_set(MC417_OEN, ALT_DATA); else { cx_clear(MC417_OEN, ALT_DATA);/* D0-D7 out */ - mem = cx_read(MC417_RWD); mem &= ~ALT_DATA; mem |= (data & ALT_DATA); - cx_write(MC417_RWD, mem); } if (flag) - cx_set(MC417_RWD, ALT_AD_RG);/* ADDR */ + mem |= ALT_AD_RG; else - cx_clear(MC417_RWD, ALT_AD_RG);/* VAL */ + mem &= ~ALT_AD_RG; - cx_clear(MC417_RWD, ALT_CS);/* ~CS */ + mem &= ~ALT_CS; if (read) - cx_clear(MC417_RWD, ALT_RD); + mem = (mem & ~ALT_RD) | ALT_WR; else - cx_clear(MC417_RWD, ALT_WR); + mem = (mem & ~ALT_WR) | ALT_RD; + + cx_write(MC417_RWD, mem); /* start RW cycle */ for (;;) { mem = cx_read(MC417_RWD); -- cgit v0.10.2 From 01a475df3d178d9ace345cc81173537970cf93e2 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sun, 2 Jan 2011 09:11:00 -0300 Subject: [media] stv0367: change default value for AGC register Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c index f910d0d..6d4d49e 100644 --- a/drivers/media/dvb/frontends/stv0367.c +++ b/drivers/media/dvb/frontends/stv0367.c @@ -286,7 +286,7 @@ static struct st_register def0367ter[STV0367TER_NBREGS] = { {R367TER_PLLMDIV, 0x01},/* for xc5000; was 0x0c */ {R367TER_PLLNDIV, 0x06},/* for xc5000; was 0x55 */ {R367TER_PLLSETUP, 0x18}, - {R367TER_DUAL_AD12, 0x04},/* for xc5000; was 0x00 */ + {R367TER_DUAL_AD12, 0x0C},/* for xc5000 AGC voltage 1.6V */ {R367TER_TSTBIST, 0x00}, {R367TER_PAD_COMP_CTRL, 0x00}, {R367TER_PAD_COMP_WR, 0x00}, @@ -599,7 +599,7 @@ static struct st_register def0367cab[STV0367CAB_NBREGS] = { {R367CAB_PLLMDIV, 0x01}, {R367CAB_PLLNDIV, 0x08}, {R367CAB_PLLSETUP, 0x18}, - {R367CAB_DUAL_AD12, 0x04}, + {R367CAB_DUAL_AD12, 0x0C}, /* for xc5000 AGC voltage 1.6V */ {R367CAB_TSTBIST, 0x00}, {R367CAB_CTRL_1, 0x00}, {R367CAB_CTRL_2, 0x03}, -- cgit v0.10.2 From 78db66e5b2b4ef48eeede17a7159cb0622c7f03d Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sun, 2 Jan 2011 09:12:00 -0300 Subject: [media] stv0367: implement uncorrected blocks counter Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c index 6d4d49e..120fed8 100644 --- a/drivers/media/dvb/frontends/stv0367.c +++ b/drivers/media/dvb/frontends/stv0367.c @@ -3367,6 +3367,24 @@ static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr) return 0; } +static int stv0367cab_read_ucblcks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct stv0367_state *state = fe->demodulator_priv; + int corrected, tscount; + + *ucblocks = (stv0367_readreg(state, R367CAB_RS_COUNTER_5) << 8) + | stv0367_readreg(state, R367CAB_RS_COUNTER_4); + corrected = (stv0367_readreg(state, R367CAB_RS_COUNTER_3) << 8) + | stv0367_readreg(state, R367CAB_RS_COUNTER_2); + tscount = (stv0367_readreg(state, R367CAB_RS_COUNTER_2) << 8) + | stv0367_readreg(state, R367CAB_RS_COUNTER_1); + + dprintk("%s: uncorrected blocks=%d corrected blocks=%d tscount=%d\n", + __func__, *ucblocks, corrected, tscount); + + return 0; +}; + static struct dvb_frontend_ops stv0367cab_ops = { .info = { .name = "ST STV0367 DVB-C", @@ -3391,7 +3409,7 @@ static struct dvb_frontend_ops stv0367cab_ops = { /* .read_ber = stv0367cab_read_ber, */ .read_signal_strength = stv0367cab_read_strength, .read_snr = stv0367cab_read_snr, -/* .read_ucblocks = stv0367cab_read_ucblcks,*/ + .read_ucblocks = stv0367cab_read_ucblcks, .get_tune_settings = stv0367_get_tune_settings, }; -- cgit v0.10.2 From 8af81ddb12cf34e2f6eab7e130f1e55e1911ab8f Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sun, 2 Jan 2011 09:14:00 -0300 Subject: [media] Fix CI code for NetUP Dual DVB-T/C CI RF card CI reset takes several seconds on some CAM, so there is no need to lock mutex all that time. Also we need not to preserve CI's reset bits in CIBUSCTRL register, they are handled automatically by FPGA. Set it to 0 explicitly in order to not reset wrong CAM. Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/altera-ci.c b/drivers/media/video/cx23885/altera-ci.c index 4269e05..678539b 100644 --- a/drivers/media/video/cx23885/altera-ci.c +++ b/drivers/media/video/cx23885/altera-ci.c @@ -283,7 +283,7 @@ int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0); store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); - store &= 0x3f; + store &= 0x0f; store |= ((state->nr << 7) | (flag << 6)); netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0); @@ -340,19 +340,25 @@ int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, - ret | (1 << (5 - state->nr)), 0); + (ret & 0xcf) | (1 << (5 - state->nr)), 0); + + mutex_unlock(&inter->fpga_mutex); for (;;) { mdelay(50); + + mutex_lock(&inter->fpga_mutex); + ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); + mutex_unlock(&inter->fpga_mutex); + if ((ret & (1 << (5 - state->nr))) == 0) break; if (time_after(jiffies, t_out)) break; } - mutex_unlock(&inter->fpga_mutex); ci_dbg_print("%s: %d msecs\n", __func__, jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out)); @@ -381,7 +387,7 @@ int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot) ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, - ret | (1 << (3 - state->nr)), 0); + (ret & 0x0f) | (1 << (3 - state->nr)), 0); mutex_unlock(&inter->fpga_mutex); -- cgit v0.10.2 From 4174ebf5ebd7a09589ff8ff3bc3246ea0a9bd356 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sun, 2 Jan 2011 09:15:00 -0300 Subject: [media] Force xc5000 firmware loading for NetUP Dual DVB-T/C CI RF card Two xc5000 tuners connected to same i2c bus. Experiments shows that situation when one tuner is not initialized while other is tuned to channel causes TS errors. Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index c0e6655..3c315f9 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -1071,12 +1071,15 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend = dvb_attach(stv0367ter_attach, &netup_stv0367_config[port->nr - 1], &i2c_bus->i2c_adap); - if (fe0->dvb.frontend != NULL) + if (fe0->dvb.frontend != NULL) { if (NULL == dvb_attach(xc5000_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, &netup_xc5000_config[port->nr - 1])) goto frontend_detach; + /* load xc5000 firmware */ + fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend); + } /* MFE frontend 2 */ fe1 = videobuf_dvb_get_frontend(&port->frontends, 2); if (fe1 == NULL) -- cgit v0.10.2 From 8ec737ffccd7fab629e28427b4d0581ce62ffedd Mon Sep 17 00:00:00 2001 From: Sungchun Kang Date: Mon, 7 Feb 2011 15:59:46 -0300 Subject: [media] s5p-fimc: fix ISR and buffer handling for fimc-capture In some cases fimc H/W did not stop although there were no output buffers available. So the capture deactivation interrupt routine is modified and the state of ST_CAPT_RUN is cleared only in the LAST-IRQ call. After LAST-IRQ is generated, H/W pointer will be skipped by 1 frame. (reference by user manual) So, S/W pointer should be increased too. Reviewed-by Jonghun Han Signed-off-by: Sungchun Kang Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 5159cc8..6b6f72e 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -153,40 +153,6 @@ static int fimc_isp_subdev_init(struct fimc_dev *fimc, unsigned int index) return ret; } -/* - * At least one buffer on the pending_buf_q queue is required. - * Locking: The caller holds fimc->slock spinlock. - */ -int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, - struct fimc_vid_buffer *fimc_vb) -{ - struct fimc_vid_cap *cap = &fimc->vid_cap; - struct fimc_ctx *ctx = cap->ctx; - int ret = 0; - - BUG_ON(!fimc || !fimc_vb); - - ret = fimc_prepare_addr(ctx, &fimc_vb->vb, &ctx->d_frame, - &fimc_vb->paddr); - if (ret) - return ret; - - if (test_bit(ST_CAPT_STREAM, &fimc->state)) { - fimc_pending_queue_add(cap, fimc_vb); - } else { - /* Setup the buffer directly for processing. */ - int buf_id = (cap->reqbufs_count == 1) ? -1 : cap->buf_index; - fimc_hw_set_output_addr(fimc, &fimc_vb->paddr, buf_id); - - fimc_vb->index = cap->buf_index; - active_queue_add(cap, fimc_vb); - - if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) - cap->buf_index = 0; - } - return ret; -} - static int fimc_stop_capture(struct fimc_dev *fimc) { unsigned long flags; @@ -211,7 +177,7 @@ static int fimc_stop_capture(struct fimc_dev *fimc) spin_lock_irqsave(&fimc->slock, flags); fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND | - 1 << ST_CAPT_STREAM); + 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM); fimc->vid_cap.active_buf_cnt = 0; @@ -239,6 +205,8 @@ static int start_streaming(struct vb2_queue *q) struct s5p_fimc_isp_info *isp_info; int ret; + fimc_hw_reset(fimc); + ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1); if (ret && ret != -ENOIOCTLCMD) return ret; @@ -273,7 +241,7 @@ static int start_streaming(struct vb2_queue *q) INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); fimc->vid_cap.active_buf_cnt = 0; fimc->vid_cap.frame_count = 0; - fimc->vid_cap.buf_index = fimc_hw_get_frame_index(fimc); + fimc->vid_cap.buf_index = 0; set_bit(ST_CAPT_PEND, &fimc->state); @@ -372,19 +340,33 @@ static void buffer_queue(struct vb2_buffer *vb) = container_of(vb, struct fimc_vid_buffer, vb); struct fimc_vid_cap *vid_cap = &fimc->vid_cap; unsigned long flags; + int min_bufs; spin_lock_irqsave(&fimc->slock, flags); - fimc_vid_cap_buf_queue(fimc, buf); + fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr); + + if (!test_bit(ST_CAPT_STREAM, &fimc->state) + && vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) { + /* Setup the buffer directly for processing. */ + int buf_id = (vid_cap->reqbufs_count == 1) ? -1 : + vid_cap->buf_index; - dbg("active_buf_cnt: %d", fimc->vid_cap.active_buf_cnt); + fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id); + buf->index = vid_cap->buf_index; + active_queue_add(vid_cap, buf); - if (vid_cap->active_buf_cnt >= vid_cap->reqbufs_count || - vid_cap->active_buf_cnt >= FIMC_MAX_OUT_BUFS) { - if (!test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) { - fimc_activate_capture(ctx); - dbg(""); - } + if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS) + vid_cap->buf_index = 0; + } else { + fimc_pending_queue_add(vid_cap, buf); } + + min_bufs = vid_cap->reqbufs_count > 1 ? 2 : 1; + + if (vid_cap->active_buf_cnt >= min_bufs && + !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) + fimc_activate_capture(ctx); + spin_unlock_irqrestore(&fimc->slock, flags); } diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index cd8a300..fdf4270 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -327,9 +327,10 @@ static int stop_streaming(struct vb2_queue *q) static void fimc_capture_handler(struct fimc_dev *fimc) { struct fimc_vid_cap *cap = &fimc->vid_cap; - struct fimc_vid_buffer *v_buf = NULL; + struct fimc_vid_buffer *v_buf; - if (!list_empty(&cap->active_buf_q)) { + if (!list_empty(&cap->active_buf_q) && + test_bit(ST_CAPT_RUN, &fimc->state)) { v_buf = active_queue_pop(cap); vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); } @@ -345,9 +346,6 @@ static void fimc_capture_handler(struct fimc_dev *fimc) fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); v_buf->index = cap->buf_index; - dbg("hw ptr: %d, sw ptr: %d", - fimc_hw_get_frame_index(fimc), cap->buf_index); - /* Move the buffer to the capture active queue */ active_queue_add(cap, v_buf); @@ -356,19 +354,25 @@ static void fimc_capture_handler(struct fimc_dev *fimc) if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) cap->buf_index = 0; + } + + if (cap->active_buf_cnt == 0) { + clear_bit(ST_CAPT_RUN, &fimc->state); - } else if (test_and_clear_bit(ST_CAPT_STREAM, &fimc->state) && - cap->active_buf_cnt <= 1) { - fimc_deactivate_capture(fimc); + if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) + cap->buf_index = 0; + } else { + set_bit(ST_CAPT_RUN, &fimc->state); } - dbg("frame: %d, active_buf_cnt= %d", + dbg("frame: %d, active_buf_cnt: %d", fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); } static irqreturn_t fimc_isr(int irq, void *priv) { struct fimc_dev *fimc = priv; + struct fimc_vid_cap *cap = &fimc->vid_cap; BUG_ON(!fimc); fimc_hw_clear_irq(fimc); @@ -396,12 +400,13 @@ static irqreturn_t fimc_isr(int irq, void *priv) } - if (test_bit(ST_CAPT_RUN, &fimc->state)) - fimc_capture_handler(fimc); + if (test_bit(ST_CAPT_PEND, &fimc->state)) { + fimc_capture_irq_handler(fimc); - if (test_and_clear_bit(ST_CAPT_PEND, &fimc->state)) { - set_bit(ST_CAPT_RUN, &fimc->state); - wake_up(&fimc->irq_queue); + if (cap->active_buf_cnt == 1) { + fimc_deactivate_capture(fimc); + clear_bit(ST_CAPT_STREAM, &fimc->state); + } } isr_unlock: -- cgit v0.10.2 From a0f8caefaf3295721ef00415c7d63ec10af253ab Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 11 Feb 2011 12:33:06 -0300 Subject: [media] s5p-fimc: Prevent oops when i2c adapter is not available Prevent invalid pointer dereference on error path. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 6b6f72e..423931c 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -98,7 +98,7 @@ static int fimc_subdev_attach(struct fimc_dev *fimc, int index) continue; sd = fimc_subdev_register(fimc, isp_info); - if (sd) { + if (!IS_ERR_OR_NULL(sd)) { vid_cap->sd = sd; vid_cap->input_index = i; -- cgit v0.10.2 From 4ecbf5d1d2bb7d1e3882de79e512c1a0a2816581 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 23 Feb 2011 08:24:33 -0300 Subject: [media] s5p-fimc: Prevent hanging on device close and fix the locking Rework the locking in m2m driver to assure proper operation on SMP systems. When job_abort or stop_streaming was called to immediately shutdown a memory-to-memory transaction video buffers scheduled for processing were never returned to vb2 and v4l2_m2m_job_finish was not called which led to hanging. Correct this and also return the unprocessed buffers to vb2 marking them as erroneous, in case the end of frame interrupt do not occur. Reported-by: Sewoon Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 423931c..8312ce4 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -252,14 +252,9 @@ static int stop_streaming(struct vb2_queue *q) { struct fimc_ctx *ctx = q->drv_priv; struct fimc_dev *fimc = ctx->fimc_dev; - unsigned long flags; - spin_lock_irqsave(&fimc->slock, flags); - if (!fimc_capture_running(fimc) && !fimc_capture_pending(fimc)) { - spin_unlock_irqrestore(&fimc->slock, flags); + if (!fimc_capture_active(fimc)) return -EINVAL; - } - spin_unlock_irqrestore(&fimc->slock, flags); return fimc_stop_capture(fimc); } @@ -773,7 +768,7 @@ static int fimc_cap_s_crop(struct file *file, void *fh, ctx->d_frame.width, ctx->d_frame.height, ctx->rotation); if (ret) { - v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range"); + v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range\n"); return ret; } diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index fdf4270..f506197 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -307,24 +307,57 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) return 0; } -static int stop_streaming(struct vb2_queue *q) +static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) { - struct fimc_ctx *ctx = q->drv_priv; + struct vb2_buffer *src_vb, *dst_vb; struct fimc_dev *fimc = ctx->fimc_dev; + if (!ctx || !ctx->m2m_ctx) + return; + + src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + + if (src_vb && dst_vb) { + v4l2_m2m_buf_done(src_vb, vb_state); + v4l2_m2m_buf_done(dst_vb, vb_state); + v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx); + } +} + +/* Complete the transaction which has been scheduled for execution. */ +static void fimc_m2m_shutdown(struct fimc_ctx *ctx) +{ + struct fimc_dev *fimc = ctx->fimc_dev; + int ret; + if (!fimc_m2m_pending(fimc)) - return 0; + return; - set_bit(ST_M2M_SHUT, &fimc->state); + fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx); - wait_event_timeout(fimc->irq_queue, - !test_bit(ST_M2M_SHUT, &fimc->state), + ret = wait_event_timeout(fimc->irq_queue, + !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), FIMC_SHUTDOWN_TIMEOUT); + /* + * In case of a timeout the buffers are not released in the interrupt + * handler so return them here with the error flag set, if there are + * any on the queue. + */ + if (ret == 0) + fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); +} + +static int stop_streaming(struct vb2_queue *q) +{ + struct fimc_ctx *ctx = q->drv_priv; + + fimc_m2m_shutdown(ctx); return 0; } -static void fimc_capture_handler(struct fimc_dev *fimc) +static void fimc_capture_irq_handler(struct fimc_dev *fimc) { struct fimc_vid_cap *cap = &fimc->vid_cap; struct fimc_vid_buffer *v_buf; @@ -373,33 +406,28 @@ static irqreturn_t fimc_isr(int irq, void *priv) { struct fimc_dev *fimc = priv; struct fimc_vid_cap *cap = &fimc->vid_cap; + struct fimc_ctx *ctx; - BUG_ON(!fimc); fimc_hw_clear_irq(fimc); - spin_lock(&fimc->slock); + if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) { + ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev); + if (ctx != NULL) { + fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); - if (test_and_clear_bit(ST_M2M_SHUT, &fimc->state)) { - wake_up(&fimc->irq_queue); - goto isr_unlock; - } else if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) { - struct vb2_buffer *src_vb, *dst_vb; - struct fimc_ctx *ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev); - - if (!ctx || !ctx->m2m_ctx) - goto isr_unlock; - - src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - if (src_vb && dst_vb) { - v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); - v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); - v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx); + spin_lock(&ctx->slock); + if (ctx->state & FIMC_CTX_SHUT) { + ctx->state &= ~FIMC_CTX_SHUT; + wake_up(&fimc->irq_queue); + } + spin_unlock(&ctx->slock); } - goto isr_unlock; + return IRQ_HANDLED; } + spin_lock(&fimc->slock); + if (test_bit(ST_CAPT_PEND, &fimc->state)) { fimc_capture_irq_handler(fimc); @@ -409,7 +437,6 @@ static irqreturn_t fimc_isr(int irq, void *priv) } } -isr_unlock: spin_unlock(&fimc->slock); return IRQ_HANDLED; } @@ -613,26 +640,26 @@ static void fimc_dma_run(void *priv) ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR); ret = fimc_prepare_config(ctx, ctx->state); - if (ret) { - err("Wrong parameters"); + if (ret) goto dma_unlock; - } + /* Reconfigure hardware if the context has changed. */ if (fimc->m2m.ctx != ctx) { ctx->state |= FIMC_PARAMS; fimc->m2m.ctx = ctx; } + spin_lock(&fimc->slock); fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr); if (ctx->state & FIMC_PARAMS) { fimc_hw_set_input_path(ctx); fimc_hw_set_in_dma(ctx); - if (fimc_set_scaler_info(ctx)) { - err("Scaler setup error"); + ret = fimc_set_scaler_info(ctx); + if (ret) { + spin_unlock(&fimc->slock); goto dma_unlock; } - fimc_hw_set_prescaler(ctx); fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); @@ -652,6 +679,7 @@ static void fimc_dma_run(void *priv) ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | FIMC_SRC_FMT | FIMC_DST_FMT); fimc_hw_activate_input_dma(fimc, true); + spin_unlock(&fimc->slock); dma_unlock: spin_unlock_irqrestore(&ctx->slock, flags); @@ -659,17 +687,7 @@ dma_unlock: static void fimc_job_abort(void *priv) { - struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; - - if (!fimc_m2m_pending(fimc)) - return; - - set_bit(ST_M2M_SHUT, &fimc->state); - - wait_event_timeout(fimc->irq_queue, - !test_bit(ST_M2M_SHUT, &fimc->state), - FIMC_SHUTDOWN_TIMEOUT); + fimc_m2m_shutdown(priv); } static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, @@ -843,7 +861,7 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - if (ctx->state & FIMC_CTX_CAP) + if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) return -EINVAL; is_output = 1; } else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { @@ -920,9 +938,7 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv, struct vb2_queue *vq; struct fimc_frame *frame; struct v4l2_pix_format_mplane *pix; - unsigned long flags; int i, ret = 0; - u32 tmp; ret = fimc_vidioc_try_fmt_mplane(file, priv, f); if (ret) @@ -963,10 +979,10 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv, frame->offs_h = 0; frame->offs_v = 0; - spin_lock_irqsave(&ctx->slock, flags); - tmp = (frame == &ctx->d_frame) ? FIMC_DST_FMT : FIMC_SRC_FMT; - ctx->state |= FIMC_PARAMS | tmp; - spin_unlock_irqrestore(&ctx->slock, flags); + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); + else + fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); @@ -1009,9 +1025,9 @@ static int fimc_m2m_streamon(struct file *file, void *priv, /* The source and target color format need to be set */ if (V4L2_TYPE_IS_OUTPUT(type)) { - if (~ctx->state & FIMC_SRC_FMT) + if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx)) return -EINVAL; - } else if (~ctx->state & FIMC_DST_FMT) { + } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) { return -EINVAL; } @@ -1038,7 +1054,7 @@ int fimc_vidioc_queryctrl(struct file *file, void *priv, return 0; } - if (ctx->state & FIMC_CTX_CAP) { + if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) { return v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd, core, queryctrl, qc); } @@ -1062,12 +1078,11 @@ int fimc_vidioc_g_ctrl(struct file *file, void *priv, ctrl->value = ctx->rotation; break; default: - if (ctx->state & FIMC_CTX_CAP) { + if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) { return v4l2_subdev_call(fimc->vid_cap.sd, core, g_ctrl, ctrl); } else { - v4l2_err(&fimc->m2m.v4l2_dev, - "Invalid control\n"); + v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n"); return -EINVAL; } } @@ -1097,11 +1112,8 @@ int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl) { struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; struct fimc_dev *fimc = ctx->fimc_dev; - unsigned long flags; int ret = 0; - spin_lock_irqsave(&ctx->slock, flags); - switch (ctrl->id) { case V4L2_CID_HFLIP: if (ctrl->value) @@ -1118,37 +1130,30 @@ int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl) break; case V4L2_CID_ROTATE: - if (!(~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT))) { + if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { ret = fimc_check_scaler_ratio(ctx->s_frame.width, - ctx->s_frame.height, - ctx->d_frame.width, - ctx->d_frame.height, - ctrl->value); - if (ret) { - v4l2_err(&fimc->m2m.v4l2_dev, - "Out of scaler range"); - spin_unlock_irqrestore(&ctx->slock, flags); - return -EINVAL; - } + ctx->s_frame.height, ctx->d_frame.width, + ctx->d_frame.height, ctrl->value); + } + + if (ret) { + v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n"); + return -EINVAL; } /* Check for the output rotator availability */ if ((ctrl->value == 90 || ctrl->value == 270) && - (ctx->in_path == FIMC_DMA && !variant->has_out_rot)) { - spin_unlock_irqrestore(&ctx->slock, flags); + (ctx->in_path == FIMC_DMA && !variant->has_out_rot)) return -EINVAL; - } else { - ctx->rotation = ctrl->value; - } + ctx->rotation = ctrl->value; break; default: - spin_unlock_irqrestore(&ctx->slock, flags); v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n"); return -EINVAL; } - ctx->state |= FIMC_PARAMS; - spin_unlock_irqrestore(&ctx->slock, flags); + + fimc_ctx_state_lock_set(FIMC_PARAMS, ctx); return 0; } @@ -1208,6 +1213,7 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_frame *f; u32 min_size, halign, depth = 0; + bool is_capture_ctx; int i; if (cr->c.top < 0 || cr->c.left < 0) { @@ -1216,10 +1222,12 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) return -EINVAL; } + is_capture_ctx = fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx); + if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - f = (ctx->state & FIMC_CTX_CAP) ? &ctx->s_frame : &ctx->d_frame; + f = is_capture_ctx ? &ctx->s_frame : &ctx->d_frame; else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && - ctx->state & FIMC_CTX_M2M) + !is_capture_ctx) f = &ctx->s_frame; else return -EINVAL; @@ -1227,15 +1235,15 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) min_size = (f == &ctx->s_frame) ? fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; - if (ctx->state & FIMC_CTX_M2M) { + /* Get pixel alignment constraints. */ + if (is_capture_ctx) { + min_size = 16; + halign = 4; + } else { if (fimc->id == 1 && fimc->variant->pix_hoff) halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; else halign = ffs(min_size) - 1; - /* there are more strict aligment requirements at camera interface */ - } else { - min_size = 16; - halign = 4; } for (i = 0; i < f->fmt->colplanes; i++) @@ -1253,8 +1261,7 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) cr->c.top = f->o_height - cr->c.height; cr->c.left = round_down(cr->c.left, min_size); - cr->c.top = round_down(cr->c.top, - ctx->state & FIMC_CTX_M2M ? 8 : 16); + cr->c.top = round_down(cr->c.top, is_capture_ctx ? 16 : 8); dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", cr->c.left, cr->c.top, cr->c.width, cr->c.height, @@ -1263,12 +1270,10 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) return 0; } - static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) { struct fimc_ctx *ctx = file->private_data; struct fimc_dev *fimc = ctx->fimc_dev; - unsigned long flags; struct fimc_frame *f; int ret; @@ -1279,9 +1284,8 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? &ctx->s_frame : &ctx->d_frame; - spin_lock_irqsave(&ctx->slock, flags); /* Check to see if scaling ratio is within supported range */ - if (!(~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT))) { + if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height, ctx->d_frame.width, @@ -1293,22 +1297,19 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) cr->c.width, cr->c.height, ctx->rotation); } - if (ret) { - v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range"); - spin_unlock_irqrestore(&ctx->slock, flags); + v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n"); return -EINVAL; } } - ctx->state |= FIMC_PARAMS; - f->offs_h = cr->c.left; f->offs_v = cr->c.top; f->width = cr->c.width; f->height = cr->c.height; - spin_unlock_irqrestore(&ctx->slock, flags); + fimc_ctx_state_lock_set(FIMC_PARAMS, ctx); + return 0; } diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 4829a25..41b1352 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -14,6 +14,7 @@ /*#define DEBUG*/ #include +#include #include #include #include @@ -57,7 +58,6 @@ enum fimc_dev_flags { ST_IDLE, ST_OUTDMA_RUN, ST_M2M_PEND, - ST_M2M_SHUT, /* for capture node */ ST_CAPT_PEND, ST_CAPT_RUN, @@ -71,13 +71,6 @@ enum fimc_dev_flags { #define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state) #define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state) -#define fimc_capture_active(dev) \ - (test_bit(ST_CAPT_RUN, &(dev)->state) || \ - test_bit(ST_CAPT_PEND, &(dev)->state)) - -#define fimc_capture_streaming(dev) \ - test_bit(ST_CAPT_STREAM, &(dev)->state) - enum fimc_datapath { FIMC_CAMERA, FIMC_DMA, @@ -119,6 +112,7 @@ enum fimc_color_fmt { #define FIMC_DST_FMT (1 << 4) #define FIMC_CTX_M2M (1 << 5) #define FIMC_CTX_CAP (1 << 6) +#define FIMC_CTX_SHUT (1 << 7) /* Image conversion flags */ #define FIMC_IN_DMA_ACCESS_TILED (1 << 0) @@ -476,6 +470,38 @@ struct fimc_ctx { struct v4l2_m2m_ctx *m2m_ctx; }; +static inline bool fimc_capture_active(struct fimc_dev *fimc) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&fimc->slock, flags); + ret = !!(fimc->state & (1 << ST_CAPT_RUN) || + fimc->state & (1 << ST_CAPT_PEND)); + spin_unlock_irqrestore(&fimc->slock, flags); + return ret; +} + +static inline void fimc_ctx_state_lock_set(u32 state, struct fimc_ctx *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->slock, flags); + ctx->state |= state; + spin_unlock_irqrestore(&ctx->slock, flags); +} + +static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&ctx->slock, flags); + ret = (ctx->state & mask) == mask; + spin_unlock_irqrestore(&ctx->slock, flags); + return ret; +} + static inline int tiled_fmt(struct fimc_fmt *fmt) { return fmt->fourcc == V4L2_PIX_FMT_NV12MT; @@ -535,7 +561,7 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, struct fimc_frame *frame; if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) { - if (ctx->state & FIMC_CTX_M2M) + if (fimc_ctx_state_is_set(FIMC_CTX_M2M, ctx)) frame = &ctx->s_frame; else return ERR_PTR(-EINVAL); -- cgit v0.10.2 From 117182d18399a956719be94d3e6e17aad9210cda Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 28 Feb 2011 11:12:19 -0300 Subject: [media] s5p-fimc: Allow defining number of sensors at runtime Add num_clients field to struct s5p_fimc_isp_info to define exactly size of clients array which simplifies a bit the sensors management. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 8312ce4..95f8b4e1 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -91,10 +91,10 @@ static int fimc_subdev_attach(struct fimc_dev *fimc, int index) struct v4l2_subdev *sd; int i; - for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i) { - isp_info = pdata->isp_info[i]; + for (i = 0; i < pdata->num_clients; ++i) { + isp_info = &pdata->isp_info[i]; - if (!isp_info || (index >= 0 && i != index)) + if (index >= 0 && i != index) continue; sd = fimc_subdev_register(fimc, isp_info); @@ -116,14 +116,13 @@ static int fimc_subdev_attach(struct fimc_dev *fimc, int index) static int fimc_isp_subdev_init(struct fimc_dev *fimc, unsigned int index) { struct s5p_fimc_isp_info *isp_info; + struct s5p_platform_fimc *pdata = fimc->pdata; int ret; - if (index >= FIMC_MAX_CAMIF_CLIENTS) + if (index >= pdata->num_clients) return -EINVAL; - isp_info = fimc->pdata->isp_info[index]; - if (!isp_info) - return -EINVAL; + isp_info = &pdata->isp_info[index]; if (isp_info->clk_frequency) clk_set_rate(fimc->clock[CLK_CAM], isp_info->clk_frequency); @@ -215,7 +214,7 @@ static int start_streaming(struct vb2_queue *q) if (ret) return ret; - isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index]; + isp_info = &fimc->pdata->isp_info[fimc->vid_cap.input_index]; fimc_hw_set_camera_type(fimc, isp_info); fimc_hw_set_camera_source(fimc, isp_info); fimc_hw_set_camera_offset(fimc, &ctx->s_frame); @@ -567,12 +566,10 @@ static int fimc_cap_enum_input(struct file *file, void *priv, struct s5p_platform_fimc *pldata = ctx->fimc_dev->pdata; struct s5p_fimc_isp_info *isp_info; - if (i->index >= FIMC_MAX_CAMIF_CLIENTS) + if (i->index >= pldata->num_clients) return -EINVAL; - isp_info = pldata->isp_info[i->index]; - if (isp_info == NULL) - return -EINVAL; + isp_info = &pldata->isp_info[i->index]; i->type = V4L2_INPUT_TYPE_CAMERA; strncpy(i->name, isp_info->board_info->type, 32); @@ -589,7 +586,7 @@ static int fimc_cap_s_input(struct file *file, void *priv, if (fimc_capture_active(ctx->fimc_dev)) return -EBUSY; - if (i >= FIMC_MAX_CAMIF_CLIENTS || !pdata->isp_info[i]) + if (i >= pdata->num_clients) return -EINVAL; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index f506197..b29937e 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1577,6 +1577,7 @@ static int fimc_probe(struct platform_device *pdev) struct fimc_dev *fimc; struct resource *res; struct samsung_fimc_driverdata *drv_data; + struct s5p_platform_fimc *pdata; int ret = 0; int cap_input_index = -1; @@ -1598,7 +1599,8 @@ static int fimc_probe(struct platform_device *pdev) fimc->id = pdev->id; fimc->variant = drv_data->variant[fimc->id]; fimc->pdev = pdev; - fimc->pdata = pdev->dev.platform_data; + pdata = pdev->dev.platform_data; + fimc->pdata = pdata; fimc->state = ST_IDLE; init_waitqueue_head(&fimc->irq_queue); @@ -1629,19 +1631,11 @@ static int fimc_probe(struct platform_device *pdev) } fimc->num_clocks = MAX_FIMC_CLOCKS - 1; - /* - * Check if vide capture node needs to be registered for this device - * instance. - */ - if (fimc->pdata) { - int i; - for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i) - if (fimc->pdata->isp_info[i]) - break; - if (i < FIMC_MAX_CAMIF_CLIENTS) { - cap_input_index = i; - fimc->num_clocks++; - } + + /* Check if a video capture node needs to be registered. */ + if (pdata && pdata->num_clients > 0) { + cap_input_index = 0; + fimc->num_clocks++; } ret = fimc_clk_get(fimc); diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index 0d457ca..82496d3 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -46,15 +46,14 @@ struct s5p_fimc_isp_info { u16 flags; }; - -#define FIMC_MAX_CAMIF_CLIENTS 2 - /** * struct s5p_platform_fimc - camera host interface platform data * * @isp_info: properties of camera sensor required for host interface setup + * @num_clients: the number of attached image sensors */ struct s5p_platform_fimc { - struct s5p_fimc_isp_info *isp_info[FIMC_MAX_CAMIF_CLIENTS]; + struct s5p_fimc_isp_info *isp_info; + int num_clients; }; #endif /* S5P_FIMC_H_ */ -- cgit v0.10.2 From e0eec9af8bf599642f2730ed207296a92e6204aa Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 21 Feb 2011 12:09:01 -0300 Subject: [media] s5p-fimc: Add a platform data entry for MIPI-CSI data alignment Allow the MIPI-CSI data alignment to be defined in the board setup as it may be different across various camera sensors. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 10684ae..4d929a3 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -665,10 +665,12 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, vid_cap->fmt.code); return -EINVAL; } - writel(tmp | (0x1 << 8), fimc->regs + S5P_CSIIMGFMT); + tmp |= (cam->csi_data_align == 32) << 8; + + writel(tmp, fimc->regs + S5P_CSIIMGFMT); } else if (cam->bus_type == FIMC_ITU_601 || - cam->bus_type == FIMC_ITU_656) { + cam->bus_type == FIMC_ITU_656) { if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ cfg |= S5P_CIGCTRL_SELCAM_ITU_A; } else if (cam->bus_type == FIMC_LCD_WB) { diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index 82496d3..9fdff8a4 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -33,6 +33,7 @@ struct i2c_board_info; * @board_info: pointer to I2C subdevice's board info * @clk_frequency: frequency of the clock the host interface provides to sensor * @bus_type: determines bus type, MIPI, ITU-R BT.601 etc. + * @csi_data_align: MIPI-CSI interface data alignment in bits * @i2c_bus_num: i2c control bus id the sensor is attached to * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) * @flags: flags defining bus signals polarity inversion (High by default) @@ -41,6 +42,7 @@ struct s5p_fimc_isp_info { struct i2c_board_info *board_info; unsigned long clk_frequency; enum cam_bus_type bus_type; + u16 csi_data_align; u16 i2c_bus_num; u16 mux_id; u16 flags; -- cgit v0.10.2 From 1e00469599b5841f5b02c436b9958073a0ad576c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 23 Feb 2011 08:17:57 -0300 Subject: [media] s5p-fimc: Use dynamic debug Use pr_debug instead of printk so it is possible to control debug traces at runtime. E.g. to enable debug trace in file fimc-core.c use command: echo -n 'file fimc-core.c +p' > /sys/kernel/debug/dynamic_debug/control or echo -n 'file fimc-core.c -p' > /sys/kernel/debug/dynamic_debug/control to disable. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 41b1352..3beb1e5 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -29,12 +29,8 @@ #define err(fmt, args...) \ printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args) -#ifdef DEBUG #define dbg(fmt, args...) \ - printk(KERN_DEBUG "%s:%d: " fmt "\n", __func__, __LINE__, ##args) -#else -#define dbg(fmt, args...) -#endif + pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args) /* Time to wait for next frame VSYNC interrupt while stopping operation. */ #define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) -- cgit v0.10.2 From 91707b8b27a785c42949ba55b65f084027962afa Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 15 Jan 2011 01:17:42 -0300 Subject: [media] s5p-fimc: Fix G_FMT ioctl handler Use pix_mp member of struct v4l2_format to return a format description rather than pix. Also fill in the plane_fmt array. This is a missing bit of conversion to the multiplanar API. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index b29937e..6c919b3 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -804,15 +804,33 @@ int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv, { struct fimc_ctx *ctx = priv; struct fimc_frame *frame; + struct v4l2_pix_format_mplane *pixm; + int i; frame = ctx_get_frame(ctx, f->type); if (IS_ERR(frame)) return PTR_ERR(frame); - f->fmt.pix.width = frame->width; - f->fmt.pix.height = frame->height; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = frame->fmt->fourcc; + pixm = &f->fmt.pix_mp; + + pixm->width = frame->width; + pixm->height = frame->height; + pixm->field = V4L2_FIELD_NONE; + pixm->pixelformat = frame->fmt->fourcc; + pixm->colorspace = V4L2_COLORSPACE_JPEG; + pixm->num_planes = frame->fmt->memplanes; + + for (i = 0; i < pixm->num_planes; ++i) { + int bpl = frame->o_width; + + if (frame->fmt->colplanes == 1) /* packed formats */ + bpl = (bpl * frame->fmt->depth[0]) / 8; + + pixm->plane_fmt[i].bytesperline = bpl; + + pixm->plane_fmt[i].sizeimage = (frame->o_width * + frame->o_height * frame->fmt->depth[i]) / 8; + } return 0; } @@ -907,6 +925,7 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); pix->num_planes = fmt->memplanes; + pix->colorspace = V4L2_COLORSPACE_JPEG; for (i = 0; i < pix->num_planes; ++i) { int bpl = pix->plane_fmt[i].bytesperline; -- cgit v0.10.2 From bd08a0cd5f546916a9454ae2c35756ed77957458 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Fri, 11 Mar 2011 06:16:22 -0300 Subject: [media] v4l: Documentation for the NV12MT format Added documentation for V4L2_PIX_FMT_NV12MT format. This is a YUV 4:2:0 format with macro block size of 64x32 and specific order of macro blocks in the memory. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 40158ee..5d259c6 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -272,6 +272,7 @@ + diff --git a/Documentation/DocBook/v4l/nv12mt.gif b/Documentation/DocBook/v4l/nv12mt.gif new file mode 100644 index 0000000..ef2d4cf Binary files /dev/null and b/Documentation/DocBook/v4l/nv12mt.gif differ diff --git a/Documentation/DocBook/v4l/nv12mt_example.gif b/Documentation/DocBook/v4l/nv12mt_example.gif new file mode 100644 index 0000000..df81d68 Binary files /dev/null and b/Documentation/DocBook/v4l/nv12mt_example.gif differ diff --git a/Documentation/DocBook/v4l/pixfmt-nv12mt.xml b/Documentation/DocBook/v4l/pixfmt-nv12mt.xml new file mode 100644 index 0000000..5cb5bec --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-nv12mt.xml @@ -0,0 +1,74 @@ + + + V4L2_PIX_FMT_NV12MT ('TM12') + &manvol; + + + V4L2_PIX_FMT_NV12MT + + Formats with ½ horizontal and vertical +chroma resolution. This format has two planes - one for luminance and one for +chrominance. Chroma samples are interleaved. The difference to +V4L2_PIX_FMT_NV12 is the memory layout. Pixels are +grouped in macroblocks of 64x32 size. The order of macroblocks in memory is +also not standard. + + + + Description + + This is the two-plane versions of the YUV 4:2:0 format where data +is grouped into 64x32 macroblocks. The three components are separated into two +sub-images or planes. The Y plane has one byte per pixel and pixels are grouped +into 64x32 macroblocks. The CbCr plane has the same width, in bytes, as the Y +plane (and the image), but is half as tall in pixels. The chroma plane is also +grouped into 64x32 macroblocks. + Width of the buffer has to be aligned to the multiple of 128, and +height alignment is 32. Every four adjactent buffers - two horizontally and two +vertically are grouped together and are located in memory in Z or flipped Z +order. + Layout of macroblocks in memory is presented in the following +figure. +
+ <constant>V4L2_PIX_FMT_NV12MT</constant> macroblock Z shape +memory layout + + + + + +
+ The requirement that width is multiple of 128 is implemented because, +the Z shape cannot be cut in half horizontally. In case the vertical resolution +of macroblocks is odd then the last row of macroblocks is arranged in a linear +order.
+ In case of chroma the layout is identical. Cb and Cr samples are +interleaved. Height of the buffer is aligned to 32. + + + Memory layout of macroblocks in <constant>V4L2_PIX_FMT_NV12 +</constant> format pixel image - extreme case + +
+ Example <constant>V4L2_PIX_FMT_NV12MT</constant> memory +layout of macroblocks + + + + + +
+ Memory layout of macroblocks of V4L2_PIX_FMT_NV12MT + format in most extreme case. +
+
+
+
+ + diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index f8436dc..c6fdcbb 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -709,6 +709,7 @@ information. &sub-yuv411p; &sub-nv12; &sub-nv12m; + &sub-nv12mt; &sub-nv16; -- cgit v0.10.2 From 29e3fbd8edb31549fa8302c185bd0b915a43d8b8 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 9 Mar 2011 14:03:24 -0300 Subject: [media] v4l2: vb2: fix queue reallocation and REQBUFS(0) case This patch fixes 2 minor bugs in videobuf2 core: 1. Queue should be reallocated if one change the memory access method without changing the number of buffers. 2. In case of REQBUFS(0), the request should not be passed to the driver. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index cc7ab0a..c5f99c7 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -488,7 +488,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return -EINVAL; } - if (req->count == 0 || q->num_buffers != 0) { + if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) { /* * We already have buffers allocated, so first check if they * are not in use and can be freed. @@ -501,6 +501,13 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) ret = __vb2_queue_free(q); if (ret != 0) return ret; + + /* + * In case of REQBUFS(0) return immediately without calling + * driver's queue_setup() callback and allocating resources. + */ + if (req->count == 0) + return 0; } /* -- cgit v0.10.2 From a9bb36aa0d6547aa0ab3d5cfde08e8651bd460bc Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 28 Jan 2011 09:42:51 -0300 Subject: [media] v4l2: vb2-dma-sg: fix memory leak This patch fixes two minor memory leaks in videobuf2-dma-sg module. They might happen only in case some other operations (like memory allocation) failed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c index 20b5c5d..d5311ff 100644 --- a/drivers/media/video/videobuf2-dma-sg.c +++ b/drivers/media/video/videobuf2-dma-sg.c @@ -88,6 +88,7 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size) fail_pages_alloc: while (--i >= 0) __free_page(buf->pages[i]); + kfree(buf->pages); fail_pages_array_alloc: vfree(buf->sg_desc.sglist); @@ -176,6 +177,7 @@ userptr_fail_get_user_pages: num_pages_from_user, buf->sg_desc.num_pages); while (--num_pages_from_user >= 0) put_page(buf->pages[num_pages_from_user]); + kfree(buf->pages); userptr_fail_pages_array_alloc: vfree(buf->sg_desc.sglist); -- cgit v0.10.2 From 31901a078af29c33c736dcbf815656920e904632 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 11 Mar 2011 06:07:27 -0300 Subject: [media] v4l2: vb2: one more fix for REQBUFS() Return immediately if the target number of buffers is the same as the current one and memory access type doesn't change. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index c5f99c7..2baf7db 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -488,6 +488,13 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return -EINVAL; } + /* + * If the same number of buffers and memory access method is requested + * then return immediately. + */ + if (q->memory == req->memory && req->count == q->num_buffers) + return 0; + if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) { /* * We already have buffers allocated, so first check if they -- cgit v0.10.2 From 72f1fc33a76a115e8c67f7b052bd7f6e129cda50 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 10 Mar 2011 09:10:49 -0300 Subject: [media] v4l2: vb2: simplify __vb2_queue_free function __vb2_queue_free function doesn't really return anything useful. This patch removes support for the return value to simplify the code. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 2baf7db..3e9b67b 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -227,7 +227,7 @@ static void __vb2_free_mem(struct vb2_queue *q) * and return the queue to an uninitialized state. Might be called even if the * queue has already been freed. */ -static int __vb2_queue_free(struct vb2_queue *q) +static void __vb2_queue_free(struct vb2_queue *q) { unsigned int buffer; @@ -251,8 +251,6 @@ static int __vb2_queue_free(struct vb2_queue *q) q->num_buffers = 0; q->memory = 0; - - return 0; } /** @@ -505,9 +503,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return -EBUSY; } - ret = __vb2_queue_free(q); - if (ret != 0) - return ret; + __vb2_queue_free(q); /* * In case of REQBUFS(0) return immediately without calling -- cgit v0.10.2 From 941f896071c5c9120c8473c77246beaf36155247 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 2 Mar 2011 09:12:13 -0300 Subject: [media] v4l2: vb2-dma-sg: fix potential security hole Memory allocated by alloc_page() function might contain some potentially important data from other system processes. The patch adds a flag to zero the allocated page before giving it to videobuf2 (and then to userspace). Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c index d5311ff..b2d9485 100644 --- a/drivers/media/video/videobuf2-dma-sg.c +++ b/drivers/media/video/videobuf2-dma-sg.c @@ -62,7 +62,7 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size) goto fail_pages_array_alloc; for (i = 0; i < buf->sg_desc.num_pages; ++i) { - buf->pages[i] = alloc_page(GFP_KERNEL); + buf->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); if (NULL == buf->pages[i]) goto fail_pages_alloc; sg_set_page(&buf->sg_desc.sglist[i], -- cgit v0.10.2 From 665bf368fdc1e7afc9616eda128fe83d6053d5bc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Mar 2011 16:22:21 -0300 Subject: [media] V4L doc fixes The xmlto validation produced a number of errors that are now fixed. Sadly, the DocBook/Makefile still adds --skip-validation to xmlto, so these errors are missed during a normal compile. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/dev-subdev.xml b/Documentation/DocBook/v4l/dev-subdev.xml index e9eb8af..21caff6 100644 --- a/Documentation/DocBook/v4l/dev-subdev.xml +++ b/Documentation/DocBook/v4l/dev-subdev.xml @@ -23,9 +23,9 @@ driver and the V4L2 device driver support this, sub-devices will feature a character device node on which ioctls can be called to - query, read and write sub-devices controls - subscribe and unsubscribe to events and retrieve them - negotiate image formats on individual pads + query, read and write sub-devices controls + subscribe and unsubscribe to events and retrieve them + negotiate image formats on individual pads @@ -67,14 +67,14 @@
Pad-level Formats - Pad-level formats are only applicable to very complex device that + Pad-level formats are only applicable to very complex device that need to expose low-level format configuration to user space. Generic V4L2 applications do not need to use the API described in - this section. + this section. - For the purpose of this section, the term + For the purpose of this section, the term format means the combination of media bus data - format, frame width and frame height. + format, frame width and frame height. Image formats are typically negotiated on video capture and output devices using the cropping and scaling ioctls. @@ -84,8 +84,8 @@ For complex devices, such as often found in embedded systems, identical image sizes at the output of a pipeline can be achieved using - different hardware configurations. One such exemple is shown on - , where + different hardware configurations. One such example is shown on + , where image scaling can be performed on both the video sensor and the host image processing hardware. @@ -168,13 +168,13 @@ modify formats as required by the device. However, they should comply with the following rules when possible: - Formats should be propagated from sink pads to source pads. + Formats should be propagated from sink pads to source pads. Modifying a format on a source pad should not modify the format on any - sink pad. - Sub-devices that scale frames using variable scaling factors + sink pad. + Sub-devices that scale frames using variable scaling factors should reset the scale factors to default values when sink pads formats are modified. If the 1:1 scaling ratio is supported, this means that - source pads formats should be reset to the sink pads formats. + source pads formats should be reset to the sink pads formats. @@ -185,9 +185,9 @@ guaranteed to be compatible. Drivers are free to accept different formats matching device requirements as being compatible. - + shows a sample configuration sequence for the pipeline described in - (table + (table columns list entity names and pad numbers). @@ -248,19 +248,19 @@ - Initial state. The sensor output is set to its native 3MP + Initial state. The sensor output is set to its native 3MP resolution. Resolutions on the host frontend and scaler input and output - pads are undefined. - The application configures the frontend input pad resolution to + pads are undefined. + The application configures the frontend input pad resolution to 2048x1536. The driver propagates the format to the frontend output pad. Note that the propagated output format can be different, as in this case, than the input format, as the hardware might need to crop pixels (for - instance when converting a Bayer filter pattern to RGB or YUV). - The application configures the scaler input pad resolution to + instance when converting a Bayer filter pattern to RGB or YUV). + The application configures the scaler input pad resolution to 2046x1534 to match the frontend output resolution. The driver propagates - the format to the scaler output pad. - The application configures the scaler output pad resolution to - 1280x960. + the format to the scaler output pad. + The application configures the scaler output pad resolution to + 1280x960. diff --git a/Documentation/DocBook/v4l/media-ioc-enum-entities.xml b/Documentation/DocBook/v4l/media-ioc-enum-entities.xml index 13d0cc4..576b68b 100644 --- a/Documentation/DocBook/v4l/media-ioc-enum-entities.xml +++ b/Documentation/DocBook/v4l/media-ioc-enum-entities.xml @@ -63,9 +63,9 @@ group_id value are considered as logically grouped. Groups are used to report - ALSA, VBI and video nodes that carry the same media - stream - lens and flash controllers associated with a sensor + ALSA, VBI and video nodes that carry the same media + stream + lens and flash controllers associated with a sensor diff --git a/Documentation/DocBook/v4l/subdev-formats.xml b/Documentation/DocBook/v4l/subdev-formats.xml index b5376e2..7041127 100644 --- a/Documentation/DocBook/v4l/subdev-formats.xml +++ b/Documentation/DocBook/v4l/subdev-formats.xml @@ -63,22 +63,22 @@ Those formats transfer pixel data as red, green and blue components. The format code is made of the following information. - The red, green and blue components order code, as encoded in a - pixel sample. Possible values are RGB and BGR. - The number of bits per component, for each component. The values - can be different for all components. Common values are 555 and 565. + The red, green and blue components order code, as encoded in a + pixel sample. Possible values are RGB and BGR. + The number of bits per component, for each component. The values + can be different for all components. Common values are 555 and 565. - The number of bus samples per pixel. Pixels that are wider than + The number of bus samples per pixel. Pixels that are wider than the bus width must be transferred in multiple samples. Common values are - 1 and 2. - The bus width. - For formats where the total number of bits per pixel is smaller + 1 and 2. + The bus width. + For formats where the total number of bits per pixel is smaller than the number of bus samples per pixel times the bus width, a padding value stating if the bytes are padded in their most high order bits - (PADHI) or low order bits (PADLO). - For formats where the number of bus samples per pixel is larger + (PADHI) or low order bits (PADLO). + For formats where the number of bus samples per pixel is larger than 1, an endianness value stating if the pixel is transferred MSB first - (BE) or LSB first (LE). + (BE) or LSB first (LE). @@ -347,26 +347,26 @@ Those formats transfer pixel data as red, green and blue components. The format code is made of the following information. - The red, green and blue components order code, as encoded in a + The red, green and blue components order code, as encoded in a pixel sample. The possible values are shown in . - The number of bits per pixel component. All components are - transferred on the same number of bits. Common values are 8, 10 and 12. + linkend="bayer-patterns" />. + The number of bits per pixel component. All components are + transferred on the same number of bits. Common values are 8, 10 and 12. - If the pixel components are DPCM-compressed, a mention of the - DPCM compression and the number of bits per compressed pixel component. + If the pixel components are DPCM-compressed, a mention of the + DPCM compression and the number of bits per compressed pixel component. - The number of bus samples per pixel. Pixels that are wider than + The number of bus samples per pixel. Pixels that are wider than the bus width must be transferred in multiple samples. Common values are - 1 and 2. - The bus width. - For formats where the total number of bits per pixel is smaller + 1 and 2. + The bus width. + For formats where the total number of bits per pixel is smaller than the number of bus samples per pixel times the bus width, a padding value stating if the bytes are padded in their most high order bits - (PADHI) or low order bits (PADLO). - For formats where the number of bus samples per pixel is larger + (PADHI) or low order bits (PADLO). + For formats where the number of bus samples per pixel is larger than 1, an endianness value stating if the pixel is transferred MSB first - (BE) or LSB first (LE). + (BE) or LSB first (LE). @@ -824,19 +824,19 @@ Those data formats transfer pixel data as (possibly downsampled) Y, U and V components. The format code is made of the following information. - The Y, U and V components order code, as transferred on the - bus. Possible values are YUYV, UYVY, YVYU and VYUY. - The number of bits per pixel component. All components are - transferred on the same number of bits. Common values are 8, 10 and 12. + The Y, U and V components order code, as transferred on the + bus. Possible values are YUYV, UYVY, YVYU and VYUY. + The number of bits per pixel component. All components are + transferred on the same number of bits. Common values are 8, 10 and 12. - The number of bus samples per pixel. Pixels that are wider than + The number of bus samples per pixel. Pixels that are wider than the bus width must be transferred in multiple samples. Common values are - 1, 1.5 (encoded as 1_5) and 2. - The bus width. When the bus width is larger than the number of + 1, 1.5 (encoded as 1_5) and 2. + The bus width. When the bus width is larger than the number of bits per pixel component, several components are packed in a single bus sample. The components are ordered as specified by the order code, with components on the left of the code transferred in the high order bits. - Common values are 8 and 16. + Common values are 8 and 16. -- cgit v0.10.2 From 6e5b55fbdab3845bfb328f986f58e85d43cee59e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Mar 2011 16:25:23 -0300 Subject: [media] V4L DocBook: update V4L2 version Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml index 3a6e3b5..ae7a069 100644 --- a/Documentation/DocBook/v4l/v4l2.xml +++ b/Documentation/DocBook/v4l/v4l2.xml @@ -410,7 +410,7 @@ and discussions on the V4L mailing list. Video for Linux Two API Specification - Revision 2.6.38 + Revision 2.6.39 &sub-common; -- cgit v0.10.2 From 34fd68bc03c8d656f991bd4f1646ec773179521b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Mar 2011 18:18:54 -0300 Subject: [media] Fix 'ID nv12mt already defined' error DocBook validation fix. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/pixfmt-nv12mt.xml b/Documentation/DocBook/v4l/pixfmt-nv12mt.xml index 5cb5bec..7a2855a 100644 --- a/Documentation/DocBook/v4l/pixfmt-nv12mt.xml +++ b/Documentation/DocBook/v4l/pixfmt-nv12mt.xml @@ -49,7 +49,7 @@ interleaved. Height of the buffer is aligned to 32. Memory layout of macroblocks in <constant>V4L2_PIX_FMT_NV12 </constant> format pixel image - extreme case -
+
Example <constant>V4L2_PIX_FMT_NV12MT</constant> memory layout of macroblocks -- cgit v0.10.2 From 43823c02245a445b5669d43b1ec07b6d8ae710f5 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 13 Mar 2011 20:37:11 -0300 Subject: [media] pvrusb2: Implement support for Terratec Grabster AV400 Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index 2222da8..c514d0b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -99,9 +99,27 @@ static const struct routing_scheme routing_defgv = { .cnt = ARRAY_SIZE(routing_schemegv), }; +/* Specific to grabster av400 device */ +static const struct routing_scheme_item routing_schemeav400[] = { + [PVR2_CVAL_INPUT_COMPOSITE] = { + .vid = CX25840_COMPOSITE1, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_SVIDEO] = { + .vid = (CX25840_SVIDEO_LUMA2|CX25840_SVIDEO_CHROMA4), + .aud = CX25840_AUDIO_SERIAL, + }, +}; + +static const struct routing_scheme routing_defav400 = { + .def = routing_schemeav400, + .cnt = ARRAY_SIZE(routing_schemeav400), +}; + static const struct routing_scheme *routing_schemes[] = { [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, [PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv, + [PVR2_ROUTING_SCHEME_AV400] = &routing_defav400, }; void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 3092abf..e799331 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -157,6 +157,28 @@ static const struct pvr2_device_desc pvr2_device_gotview_2d = { /*------------------------------------------------------------------------*/ +/* Terratec Grabster AV400 */ + +static const struct pvr2_device_client_desc pvr2_cli_av400[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, +}; + +static const struct pvr2_device_desc pvr2_device_av400 = { + .description = "Terratec Grabster AV400", + .shortname = "av400", + .flag_is_experimental = 1, + .client_table.lst = pvr2_cli_av400, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_av400), + .flag_has_cx25840 = !0, + .flag_has_analogtuner = 0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_AV400, +}; + + + +/*------------------------------------------------------------------------*/ /* OnAir Creator */ #ifdef CONFIG_VIDEO_PVRUSB2_DVB @@ -517,6 +539,8 @@ struct usb_device_id pvr2_device_table[] = { .driver_info = (kernel_ulong_t)&pvr2_device_750xx}, { USB_DEVICE(0x2040, 0x7501), .driver_info = (kernel_ulong_t)&pvr2_device_751xx}, + { USB_DEVICE(0x0ccd, 0x0039), + .driver_info = (kernel_ulong_t)&pvr2_device_av400}, { } }; -- cgit v0.10.2 From 228ee5928b5d4192a23cda3fdf755a956516c13e Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 13 Mar 2011 22:18:23 -0300 Subject: [media] pvrusb2: Remove dead code This was caught via a compiler warning. Amazingly enough this bit of benign dreck dates all the way back to 2008. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 58617fc..3876114 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -795,12 +795,10 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_CROP: { struct v4l2_crop *crop = (struct v4l2_crop *)arg; - struct v4l2_cropcap cap; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ret = -EINVAL; break; } - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), crop->c.left); -- cgit v0.10.2 From c05df8b32ab4b8103f8c20cbd3ab7191be613b68 Mon Sep 17 00:00:00 2001 From: Xiaochen Wang Date: Sun, 13 Mar 2011 22:32:53 -0300 Subject: [media] pvrusb2: check kmalloc return value allows the driver to proceed and initialize the below two values even if the kmalloc() fails. hdw->std_info_enum.def.type_enum.value_names hdw->std_info_enum.def.type_enum.count Signed-off-by: Xiaochen Wang Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index d33dd61..9d0dd08 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2850,15 +2850,23 @@ static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw) PVR2_TRACE_ERROR_LEGS, "WARNING: Failed to identify any viable standards"); } + + /* Set up the dynamic control for this standard */ hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL); - hdw->std_enum_names[0] = "none"; - for (idx = 0; idx < std_cnt; idx++) { - hdw->std_enum_names[idx+1] = - newstd[idx].name; - } - // Set up the dynamic control for this standard - hdw->std_info_enum.def.type_enum.value_names = hdw->std_enum_names; - hdw->std_info_enum.def.type_enum.count = std_cnt+1; + if (hdw->std_enum_names) { + hdw->std_enum_names[0] = "none"; + for (idx = 0; idx < std_cnt; idx++) + hdw->std_enum_names[idx+1] = newstd[idx].name; + hdw->std_info_enum.def.type_enum.value_names = + hdw->std_enum_names; + hdw->std_info_enum.def.type_enum.count = std_cnt+1; + } else { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "WARNING: Failed to alloc memory for names"); + hdw->std_info_enum.def.type_enum.value_names = NULL; + hdw->std_info_enum.def.type_enum.count = 0; + } hdw->std_defs = newstd; hdw->std_enum_cnt = std_cnt+1; hdw->std_enum_cur = 0; -- cgit v0.10.2 From c2f644aeeba3a5178767e757e93151959f037db1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 13 Mar 2011 12:26:14 -0300 Subject: [media] gspca_cpia1: Add support for button Not only the qx3 microscope has a button, but some cameras too. Tested with the Trust sp@cecam 100 (and with a creative and ezcam without button). Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c index 2a4a428..9ddbac6 100644 --- a/drivers/media/video/gspca/cpia1.c +++ b/drivers/media/video/gspca/cpia1.c @@ -28,6 +28,7 @@ #define MODULE_NAME "cpia1" +#include #include "gspca.h" MODULE_AUTHOR("Hans de Goede "); @@ -653,10 +654,15 @@ static int do_command(struct gspca_dev *gspca_dev, u16 command, break; case CPIA_COMMAND_ReadMCPorts: - if (!sd->params.qx3.qx3_detected) - break; /* test button press */ - sd->params.qx3.button = ((gspca_dev->usb_buf[1] & 0x02) == 0); + a = ((gspca_dev->usb_buf[1] & 0x02) == 0); + if (a != sd->params.qx3.button) { +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + input_report_key(gspca_dev->input_dev, KEY_CAMERA, a); + input_sync(gspca_dev->input_dev); +#endif + sd->params.qx3.button = a; + } if (sd->params.qx3.button) { /* button pressed - unlock the latch */ do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, @@ -1738,6 +1744,8 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + command_pause(gspca_dev); /* save camera state for later open (developers guide ch 3.5.3) */ @@ -1748,6 +1756,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* Update the camera status */ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + /* If the last button state is pressed, release it now! */ + if (sd->params.qx3.button) { + /* The camera latch will hold the pressed state until we reset + the latch, so we do not reset sd->params.qx3.button now, to + avoid a false keypress being reported the next sd_start */ + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + } +#endif } /* this function is called at probe and resume time */ @@ -1852,8 +1871,7 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev) /* Update our knowledge of the camera state */ do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - if (sd->params.qx3.qx3_detected) - do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); + do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) @@ -2085,6 +2103,9 @@ static const struct sd_desc sd_desc = { .dq_callback = sd_dq_callback, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + .other_input = 1, +#endif }; /* -- module initialisation -- */ -- cgit v0.10.2 From 170960fa9a4688299350ea356e4e5a3e76d15711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Sat, 26 Feb 2011 08:04:03 -0300 Subject: [media] gspca - zc3xx: Remove double definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/zc3xx-reg.h b/drivers/media/video/gspca/zc3xx-reg.h index bfb559c..a1bd94e 100644 --- a/drivers/media/video/gspca/zc3xx-reg.h +++ b/drivers/media/video/gspca/zc3xx-reg.h @@ -160,8 +160,6 @@ #define ZC3XX_R1A6_YMEANAFTERAE 0x01a6 #define ZC3XX_R1A7_CALCGLOBALMEAN 0x01a7 -#define ZC3XX_R1A2_BLUEMEANAFTERAGC 0x01a2 - /* Matrixes */ /* Color matrix is like : -- cgit v0.10.2 From 4e5144eef436720b08f7bdb1beba4960af23c737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Sat, 26 Feb 2011 13:56:20 -0300 Subject: [media] gspca - zc3xx: Cleanup source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - change some comments and copyright - change an erroneous register name - change hdcs2020b to hdcs2020 Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index b201491..6dcb1cf 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -1,7 +1,7 @@ /* * Z-Star/Vimicro zc301/zc302p/vc30x library * - * Copyright (C) 2009-2010 Jean-Francois Moine + * Copyright (C) 2009-2011 Jean-Francois Moine * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr * * This program is free software; you can redistribute it and/or modify @@ -73,7 +73,7 @@ enum sensors { SENSOR_CS2102K, SENSOR_GC0303, SENSOR_GC0305, - SENSOR_HDCS2020b, + SENSOR_HDCS2020, SENSOR_HV7131B, SENSOR_HV7131R, SENSOR_ICM105A, @@ -1498,7 +1498,7 @@ static const struct usb_action gc0305_NoFliker[] = { {} }; -static const struct usb_action hdcs2020b_InitialScale[] = { +static const struct usb_action hdcs2020_InitialScale[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */ @@ -1630,7 +1630,7 @@ static const struct usb_action hdcs2020b_InitialScale[] = { {0xa0, 0x40, ZC3XX_R118_BGAIN}, {} }; -static const struct usb_action hdcs2020b_Initial[] = { +static const struct usb_action hdcs2020_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -1758,7 +1758,7 @@ static const struct usb_action hdcs2020b_Initial[] = { {0xa0, 0x40, ZC3XX_R118_BGAIN}, {} }; -static const struct usb_action hdcs2020b_50HZ[] = { +static const struct usb_action hdcs2020_50HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ @@ -1779,7 +1779,7 @@ static const struct usb_action hdcs2020b_50HZ[] = { {0xa0, 0x2f, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2f,cc */ {} }; -static const struct usb_action hdcs2020b_60HZ[] = { +static const struct usb_action hdcs2020_60HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ @@ -1800,7 +1800,7 @@ static const struct usb_action hdcs2020b_60HZ[] = { {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */ {} }; -static const struct usb_action hdcs2020b_NoFliker[] = { +static const struct usb_action hdcs2020_NoFliker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ @@ -2126,7 +2126,6 @@ static const struct usb_action hv7131r_Initial[] = { {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, @@ -2878,7 +2877,7 @@ static const struct usb_action mc501cb_Initial[] = { {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */ {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */ {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */ - {0xaa, 0xa0, ZC3XX_R01A_LASTFRAMESTATE}, /* 00,a0,1a,aa */ + {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */ {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */ {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */ {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */ @@ -2998,7 +2997,7 @@ static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */ {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */ {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */ {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */ - {0xaa, 0xa0, ZC3XX_R01A_LASTFRAMESTATE}, /* 00,a0,1a,aa */ + {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */ {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */ {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */ {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */ @@ -3310,7 +3309,7 @@ static const struct usb_action ov7620_50HZ[] = { {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */ {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ /* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc - if mode0 (640x480) */ + * if mode0 (640x480) */ {} }; static const struct usb_action ov7620_60HZ[] = { @@ -5828,7 +5827,7 @@ static void setmatrix(struct gspca_dev *gspca_dev) [SENSOR_CS2102K] = NULL, [SENSOR_GC0303] = gc0303_matrix, [SENSOR_GC0305] = gc0305_matrix, - [SENSOR_HDCS2020b] = NULL, + [SENSOR_HDCS2020] = NULL, [SENSOR_HV7131B] = NULL, [SENSOR_HV7131R] = po2030_matrix, [SENSOR_ICM105A] = po2030_matrix, @@ -5990,10 +5989,10 @@ static void setlightfreq(struct gspca_dev *gspca_dev) {gc0305_NoFliker, gc0305_NoFliker, gc0305_50HZ, gc0305_50HZ, gc0305_60HZ, gc0305_60HZ}, - [SENSOR_HDCS2020b] = - {hdcs2020b_NoFliker, hdcs2020b_NoFliker, - hdcs2020b_50HZ, hdcs2020b_50HZ, - hdcs2020b_60HZ, hdcs2020b_60HZ}, + [SENSOR_HDCS2020] = + {hdcs2020_NoFliker, hdcs2020_NoFliker, + hdcs2020_50HZ, hdcs2020_50HZ, + hdcs2020_60HZ, hdcs2020_60HZ}, [SENSOR_HV7131B] = {hv7131b_NoFliker, hv7131b_NoFlikerScale, hv7131b_50HZ, hv7131b_50HZScale, @@ -6091,7 +6090,7 @@ static void setautogain(struct gspca_dev *gspca_dev) static void send_unknown(struct gspca_dev *gspca_dev, int sensor) { - reg_w(gspca_dev, 0x01, 0x0000); /* led off */ + reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */ switch (sensor) { case SENSOR_PAS106: reg_w(gspca_dev, 0x03, 0x003a); @@ -6310,6 +6309,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) return 0x0a; /* PB0330 */ } + /* probe gc0303 / gc0305 */ reg_w(gspca_dev, 0x01, 0x0000); reg_w(gspca_dev, 0x01, 0x0001); reg_w(gspca_dev, 0x98, 0x008b); @@ -6433,7 +6433,7 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_CS2102K] = 5, [SENSOR_GC0303] = 3, [SENSOR_GC0305] = 4, - [SENSOR_HDCS2020b] = 4, + [SENSOR_HDCS2020] = 4, [SENSOR_HV7131B] = 4, [SENSOR_HV7131R] = 4, [SENSOR_ICM105A] = 4, @@ -6454,7 +6454,7 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_CS2102K] = 1, [SENSOR_GC0303] = 1, [SENSOR_GC0305] = 1, - [SENSOR_HDCS2020b] = 1, + [SENSOR_HDCS2020] = 1, [SENSOR_HV7131B] = 1, [SENSOR_HV7131R] = 1, [SENSOR_ICM105A] = 1, @@ -6517,8 +6517,8 @@ static int sd_init(struct gspca_dev *gspca_dev) sd->sensor = SENSOR_CS2102; break; case 0x08: - PDEBUG(D_PROBE, "Find Sensor HDCS2020(b)"); - sd->sensor = SENSOR_HDCS2020b; + PDEBUG(D_PROBE, "Find Sensor HDCS2020"); + sd->sensor = SENSOR_HDCS2020; break; case 0x0a: PDEBUG(D_PROBE, @@ -6648,8 +6648,8 @@ static int sd_start(struct gspca_dev *gspca_dev) {gc0303_Initial, gc0303_InitialScale}, [SENSOR_GC0305] = {gc0305_Initial, gc0305_InitialScale}, - [SENSOR_HDCS2020b] = - {hdcs2020b_Initial, hdcs2020b_InitialScale}, + [SENSOR_HDCS2020] = + {hdcs2020_Initial, hdcs2020_InitialScale}, [SENSOR_HV7131B] = {hv7131b_Initial, hv7131b_InitialScale}, [SENSOR_HV7131R] = @@ -6743,7 +6743,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* set the gamma tables when not set */ switch (sd->sensor) { case SENSOR_CS2102K: /* gamma set in xxx_Initial */ - case SENSOR_HDCS2020b: + case SENSOR_HDCS2020: case SENSOR_OV7630C: break; default: -- cgit v0.10.2 From fa85bb6f9b101d92940f54c7ab1b8557aaa35a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Sun, 13 Mar 2011 14:38:46 -0300 Subject: [media] gspca: New file autogain_functions.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This file contains functions for the auto gain. It must be used with the new control mechanism. Acked-by: Hans de Goede Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/autogain_functions.h b/drivers/media/video/gspca/autogain_functions.h new file mode 100644 index 0000000..46777ee --- /dev/null +++ b/drivers/media/video/gspca/autogain_functions.h @@ -0,0 +1,179 @@ +/* + * Functions for auto gain. + * + * Copyright (C) 2010-2011 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* auto gain and exposure algorithm based on the knee algorithm described here: + http://ytse.tricolour.net/docs/LowLightOptimization.html + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +static inline int auto_gain_n_exposure( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone, + int gain_knee, + int exposure_knee) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, steps, gain, orig_gain, exposure, orig_exposure; + int retval = 0; + + orig_gain = gain = sd->ctrls[GAIN].val; + orig_exposure = exposure = sd->ctrls[EXPOSURE].val; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = abs(desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + for (i = 0; i < steps; i++) { + if (avg_lum > desired_avg_lum) { + if (gain > gain_knee) + gain--; + else if (exposure > exposure_knee) + exposure--; + else if (gain > sd->ctrls[GAIN].def) + gain--; + else if (exposure > sd->ctrls[EXPOSURE].min) + exposure--; + else if (gain > sd->ctrls[GAIN].min) + gain--; + else + break; + } else { + if (gain < sd->ctrls[GAIN].def) + gain++; + else if (exposure < exposure_knee) + exposure++; + else if (gain < gain_knee) + gain++; + else if (exposure < sd->ctrls[EXPOSURE].max) + exposure++; + else if (gain < sd->ctrls[GAIN].max) + gain++; + else + break; + } + } + + if (gain != orig_gain) { + sd->ctrls[GAIN].val = gain; + setgain(gspca_dev); + retval = 1; + } + if (exposure != orig_exposure) { + sd->ctrls[EXPOSURE].val = exposure; + setexposure(gspca_dev); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} + +/* Autogain + exposure algorithm for cameras with a coarse exposure control + (usually this means we can only control the clockdiv to change exposure) + As changing the clockdiv so that the fps drops from 30 to 15 fps for + example, will lead to a huge exposure change (it effectively doubles), + this algorithm normally tries to only adjust the gain (between 40 and + 80 %) and if that does not help, only then changes exposure. This leads + to a much more stable image then using the knee algorithm which at + certain points of the knee graph will only try to adjust exposure, + which leads to oscilating as one exposure step is huge. + + Note this assumes that the sd struct for the cam in question has + exp_too_high_cnt and exp_too_high_cnt int members for use by this function. + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +static inline int coarse_grained_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone) +{ + struct sd *sd = (struct sd *) gspca_dev; + int steps, gain, orig_gain, exposure, orig_exposure; + int gain_low, gain_high; + int retval = 0; + + orig_gain = gain = sd->ctrls[GAIN].val; + orig_exposure = exposure = sd->ctrls[EXPOSURE].val; + + gain_low = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 2; + gain_low += sd->ctrls[GAIN].min; + gain_high = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 4; + gain_high += sd->ctrls[GAIN].min; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = (desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + if ((gain + steps) > gain_high && + exposure < sd->ctrls[EXPOSURE].max) { + gain = gain_high; + sd->exp_too_low_cnt++; + sd->exp_too_high_cnt = 0; + } else if ((gain + steps) < gain_low && + exposure > sd->ctrls[EXPOSURE].min) { + gain = gain_low; + sd->exp_too_high_cnt++; + sd->exp_too_low_cnt = 0; + } else { + gain += steps; + if (gain > sd->ctrls[GAIN].max) + gain = sd->ctrls[GAIN].max; + else if (gain < sd->ctrls[GAIN].min) + gain = sd->ctrls[GAIN].min; + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; + } + + if (sd->exp_too_high_cnt > 3) { + exposure--; + sd->exp_too_high_cnt = 0; + } else if (sd->exp_too_low_cnt > 3) { + exposure++; + sd->exp_too_low_cnt = 0; + } + + if (gain != orig_gain) { + sd->ctrls[GAIN].val = gain; + setgain(gspca_dev); + retval = 1; + } + if (exposure != orig_exposure) { + sd->ctrls[EXPOSURE].val = exposure; + setexposure(gspca_dev); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} -- cgit v0.10.2 From f51a8caafedfc643b3dfa2ffdfb02da31a13db3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Sun, 13 Mar 2011 15:04:11 -0300 Subject: [media] gspca - sonixb: Use the new control mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Hans de Goede Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index c6cd68d..13cca7a 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -56,9 +56,22 @@ MODULE_AUTHOR("Michel Xhaard "); MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); MODULE_LICENSE("GPL"); +/* controls */ +enum e_ctrl { + BRIGHTNESS, + GAIN, + EXPOSURE, + AUTOGAIN, + FREQ, + NCTRLS /* number of controls */ +}; + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct gspca_ctrl ctrls[NCTRLS]; + atomic_t avg_lum; int prev_avg_lum; int exp_too_low_cnt; @@ -66,13 +79,8 @@ struct sd { int header_read; u8 header[12]; /* Header without sof marker */ - unsigned short exposure; - unsigned char gain; - unsigned char brightness; - unsigned char autogain; unsigned char autogain_ignore_frames; unsigned char frames_to_drop; - unsigned char freq; /* light freq filter setting */ __u8 bridge; /* Type of bridge */ #define BRIDGE_101 0 @@ -113,10 +121,9 @@ struct sensor_data { #define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */ /* ctrl_dis helper macros */ -#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << COARSE_EXPOSURE_IDX) | \ - (1 << AUTOGAIN_IDX)) -#define NO_FREQ (1 << FREQ_IDX) -#define NO_BRIGHTNESS (1 << BRIGHTNESS_IDX) +#define NO_EXPO ((1 << EXPOSURE) | (1 << AUTOGAIN)) +#define NO_FREQ (1 << FREQ) +#define NO_BRIGHTNESS (1 << BRIGHTNESS) #define COMP 0xc7 /* 0x87 //0x07 */ #define COMP1 0xc9 /* 0x89 //0x09 */ @@ -141,20 +148,14 @@ struct sensor_data { #define AUTOGAIN_IGNORE_FRAMES 1 /* 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_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 setbrightness(struct gspca_dev *gspca_dev); +static void setgain(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 int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static void setfreq(struct gspca_dev *gspca_dev); -static const struct ctrl sd_ctrls[] = { -#define BRIGHTNESS_IDX 0 - { +static const struct ctrl sd_ctrls[NCTRLS] = { +[BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -162,14 +163,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 255, .step = 1, -#define BRIGHTNESS_DEF 127 - .default_value = BRIGHTNESS_DEF, + .default_value = 127, }, - .set = sd_setbrightness, - .get = sd_getbrightness, + .set_control = setbrightness }, -#define GAIN_IDX 1 - { +[GAIN] = { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, @@ -177,48 +175,31 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 255, .step = 1, -#define GAIN_DEF 127 #define GAIN_KNEE 230 - .default_value = GAIN_DEF, + .default_value = 127, }, - .set = sd_setgain, - .get = sd_getgain, + .set_control = setgain }, -#define EXPOSURE_IDX 2 - { +[EXPOSURE] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Exposure", -#define EXPOSURE_DEF 66 /* 33 ms / 30 fps (except on PASXXX) */ -#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PASXXX) */ .minimum = 0, .maximum = 1023, .step = 1, - .default_value = EXPOSURE_DEF, + .default_value = 66, + /* 33 ms / 30 fps (except on PASXXX) */ +#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PASXXX) */ .flags = 0, }, - .set = sd_setexposure, - .get = sd_getexposure, + .set_control = setexposure }, -#define COARSE_EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", +/* for coarse exposure */ +#define COARSE_EXPOSURE_MIN 2 +#define COARSE_EXPOSURE_MAX 15 #define COARSE_EXPOSURE_DEF 2 /* 30 fps */ - .minimum = 2, - .maximum = 15, - .step = 1, - .default_value = COARSE_EXPOSURE_DEF, - .flags = 0, - }, - .set = sd_setexposure, - .get = sd_getexposure, - }, -#define AUTOGAIN_IDX 4 - { +[AUTOGAIN] = { { .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -231,10 +212,8 @@ static const struct ctrl sd_ctrls[] = { .flags = 0, }, .set = sd_setautogain, - .get = sd_getautogain, }, -#define FREQ_IDX 5 - { +[FREQ] = { { .id = V4L2_CID_POWER_LINE_FREQUENCY, .type = V4L2_CTRL_TYPE_MENU, @@ -245,8 +224,7 @@ static const struct ctrl sd_ctrls[] = { #define FREQ_DEF 0 .default_value = FREQ_DEF, }, - .set = sd_setfreq, - .get = sd_getfreq, + .set_control = setfreq }, }; @@ -646,7 +624,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) /* change reg 0x06 */ i2cOV[1] = sensor_data[sd->sensor].sensor_addr; - i2cOV[3] = sd->brightness; + i2cOV[3] = sd->ctrls[BRIGHTNESS].val; if (i2c_w(gspca_dev, i2cOV) < 0) goto err; break; @@ -664,13 +642,13 @@ static void setbrightness(struct gspca_dev *gspca_dev) i2cpdoit[2] = 0x13; } - if (sd->brightness < 127) { + if (sd->ctrls[BRIGHTNESS].val < 127) { /* change reg 0x0b, signreg */ i2cpbright[3] = 0x01; /* set reg 0x0c, offset */ - i2cpbright[4] = 127 - sd->brightness; + i2cpbright[4] = 127 - sd->ctrls[BRIGHTNESS].val; } else - i2cpbright[4] = sd->brightness - 127; + i2cpbright[4] = sd->ctrls[BRIGHTNESS].val - 127; if (i2c_w(gspca_dev, i2cpbright) < 0) goto err; @@ -687,16 +665,16 @@ err: static void setsensorgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - unsigned char gain = sd->gain; + u8 gain = sd->ctrls[GAIN].val; switch (sd->sensor) { case SENSOR_HV7131D: { __u8 i2c[] = {0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17}; - i2c[3] = 0x3f - (sd->gain / 4); - i2c[4] = 0x3f - (sd->gain / 4); - i2c[5] = 0x3f - (sd->gain / 4); + i2c[3] = 0x3f - (gain / 4); + i2c[4] = 0x3f - (gain / 4); + i2c[5] = 0x3f - (gain / 4); if (i2c_w(gspca_dev, i2c) < 0) goto err; @@ -759,11 +737,11 @@ static void setsensorgain(struct gspca_dev *gspca_dev) i2cpdoit[2] = 0x13; } - i2cpgain[3] = sd->gain >> 3; - i2cpcolorgain[3] = sd->gain >> 4; - i2cpcolorgain[4] = sd->gain >> 4; - i2cpcolorgain[5] = sd->gain >> 4; - i2cpcolorgain[6] = sd->gain >> 4; + i2cpgain[3] = gain >> 3; + i2cpcolorgain[3] = gain >> 4; + i2cpcolorgain[4] = gain >> 4; + i2cpcolorgain[5] = gain >> 4; + i2cpcolorgain[6] = gain >> 4; if (i2c_w(gspca_dev, i2cpgain) < 0) goto err; @@ -792,13 +770,13 @@ static void setgain(struct gspca_dev *gspca_dev) } if (sd->bridge == BRIDGE_103) { - gain = sd->gain >> 1; + gain = sd->ctrls[GAIN].val >> 1; buf[0] = gain; /* Red */ buf[1] = gain; /* Green */ buf[2] = gain; /* Blue */ reg_w(gspca_dev, 0x05, buf, 3); } else { - gain = sd->gain >> 4; + gain = sd->ctrls[GAIN].val >> 4; buf[0] = gain << 4 | gain; /* Red and blue */ buf[1] = gain; /* Green */ reg_w(gspca_dev, 0x10, buf, 2); @@ -820,7 +798,8 @@ static void setexposure(struct gspca_dev *gspca_dev) where the framerate starts dropping 2) At 6138 the framerate has already dropped to 2 fps, going any lower makes little sense */ - __u16 reg = sd->exposure * 6; + u16 reg = sd->ctrls[EXPOSURE].val * 6; + i2c[3] = reg >> 8; i2c[4] = reg & 0xff; if (i2c_w(gspca_dev, i2c) != 0) @@ -832,7 +811,8 @@ static void setexposure(struct gspca_dev *gspca_dev) /* register 19's high nibble contains the sn9c10x clock divider The high nibble configures the no fps according to the formula: 60 / high_nibble. With a maximum of 30 fps */ - __u8 reg = sd->exposure; + u8 reg = sd->ctrls[EXPOSURE].val; + reg = (reg << 4) | 0x0b; reg_w(gspca_dev, 0x19, ®, 1); break; @@ -868,7 +848,7 @@ static void setexposure(struct gspca_dev *gspca_dev) } else reg10_max = 0x41; - reg11 = (15 * sd->exposure + 999) / 1000; + reg11 = (15 * sd->ctrls[EXPOSURE].val + 999) / 1000; if (reg11 < 1) reg11 = 1; else if (reg11 > 16) @@ -881,14 +861,16 @@ static void setexposure(struct gspca_dev *gspca_dev) reg11 = 4; /* frame exposure time in ms = 1000 * reg11 / 30 -> - reg10 = (sd->exposure / 2) * reg10_max / (1000 * reg11 / 30) */ - reg10 = (sd->exposure * 15 * reg10_max) / (1000 * reg11); + reg10 = (sd->ctrls[EXPOSURE].val / 2) * reg10_max + / (1000 * reg11 / 30) */ + reg10 = (sd->ctrls[EXPOSURE].val * 15 * reg10_max) + / (1000 * reg11); /* Don't allow this to get below 10 when using autogain, the steps become very large (relatively) when below 10 causing the image to oscilate from much too dark, to much too bright and back again. */ - if (sd->autogain && reg10 < 10) + if (sd->ctrls[AUTOGAIN].val && reg10 < 10) reg10 = 10; else if (reg10 > reg10_max) reg10 = reg10_max; @@ -927,15 +909,16 @@ static void setexposure(struct gspca_dev *gspca_dev) frame exposure times (like we are doing with the ov chips), as that sometimes leads to jumps in the exposure control, which are bad for auto exposure. */ - if (sd->exposure < 200) { - i2cpexpo[3] = 255 - (sd->exposure * 255) / 200; + if (sd->ctrls[EXPOSURE].val < 200) { + i2cpexpo[3] = 255 - (sd->ctrls[EXPOSURE].val * 255) + / 200; framerate_ctrl = 500; } else { /* The PAS202's exposure control goes from 0 - 4095, but anything below 500 causes vsync issues, so scale our 200-1023 to 500-4095 */ - framerate_ctrl = (sd->exposure - 200) * 1000 / 229 + - 500; + framerate_ctrl = (sd->ctrls[EXPOSURE].val - 200) + * 1000 / 229 + 500; } i2cpframerate[3] = framerate_ctrl >> 6; @@ -959,15 +942,15 @@ static void setexposure(struct gspca_dev *gspca_dev) /* For values below 150 use partial frame exposure, above that use framerate ctrl */ - if (sd->exposure < 150) { - i2cpexpo[3] = 150 - sd->exposure; + if (sd->ctrls[EXPOSURE].val < 150) { + i2cpexpo[3] = 150 - sd->ctrls[EXPOSURE].val; framerate_ctrl = 300; } else { /* The PAS106's exposure control goes from 0 - 4095, but anything below 300 causes vsync issues, so scale our 150-1023 to 300-4095 */ - framerate_ctrl = (sd->exposure - 150) * 1000 / 230 + - 300; + framerate_ctrl = (sd->ctrls[EXPOSURE].val - 150) + * 1000 / 230 + 300; } i2cpframerate[3] = framerate_ctrl >> 4; @@ -998,7 +981,7 @@ static void setfreq(struct gspca_dev *gspca_dev) 0x2b register, see ov6630 datasheet. 0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */ __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}; - switch (sd->freq) { + switch (sd->ctrls[FREQ].val) { default: /* case 0: * no filter*/ /* case 2: * 60 hz */ @@ -1017,7 +1000,7 @@ static void setfreq(struct gspca_dev *gspca_dev) } } -#include "coarse_expo_autogain.h" +#include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) { @@ -1025,7 +1008,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int avg_lum = atomic_read(&sd->avg_lum); - if (avg_lum == -1 || !sd->autogain) + if (avg_lum == -1 || !sd->ctrls[AUTOGAIN].val) return; if (sd->autogain_ignore_frames > 0) { @@ -1045,17 +1028,20 @@ static void do_autogain(struct gspca_dev *gspca_dev) } if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) - result = gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, - sd->brightness * desired_avg_lum / 127, + result = coarse_grained_expo_autogain(gspca_dev, avg_lum, + sd->ctrls[BRIGHTNESS].val + * desired_avg_lum / 127, deadzone); else - result = gspca_auto_gain_n_exposure(gspca_dev, avg_lum, - sd->brightness * desired_avg_lum / 127, + result = auto_gain_n_exposure(gspca_dev, avg_lum, + sd->ctrls[BRIGHTNESS].val + * desired_avg_lum / 127, deadzone, GAIN_KNEE, EXPOSURE_KNEE); if (result) { PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d", - (int)sd->gain, (int)sd->exposure); + (int) sd->ctrls[GAIN].val, + (int) sd->ctrls[EXPOSURE].val); sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; } } @@ -1074,9 +1060,11 @@ static int sd_config(struct gspca_dev *gspca_dev, /* copy the webcam info from the device id */ sd->sensor = id->driver_info >> 8; sd->bridge = id->driver_info & 0xff; + gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis; cam = &gspca_dev->cam; + cam->ctrls = sd->ctrls; if (!(sensor_data[sd->sensor].flags & F_SIF)) { cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); @@ -1086,20 +1074,11 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam->npkt = 36; /* 36 packets per ISOC message */ - sd->brightness = BRIGHTNESS_DEF; - sd->gain = GAIN_DEF; if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) { - sd->exposure = COARSE_EXPOSURE_DEF; - gspca_dev->ctrl_dis |= (1 << EXPOSURE_IDX); - } else { - sd->exposure = EXPOSURE_DEF; - gspca_dev->ctrl_dis |= (1 << COARSE_EXPOSURE_IDX); + sd->ctrls[EXPOSURE].min = COARSE_EXPOSURE_MIN; + sd->ctrls[EXPOSURE].max = COARSE_EXPOSURE_MAX; + sd->ctrls[EXPOSURE].def = COARSE_EXPOSURE_DEF; } - if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) - sd->autogain = 0; /* Disable do_autogain callback */ - else - sd->autogain = AUTOGAIN_DEF; - sd->freq = FREQ_DEF; return 0; } @@ -1398,65 +1377,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightness(gspca_dev); - return 0; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; -} - -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - setgain(gspca_dev); - return 0; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->gain; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - setexposure(gspca_dev); - return 0; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->exposure; - return 0; -} - static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->autogain = val; + sd->ctrls[AUTOGAIN].val = val; sd->exp_too_high_cnt = 0; sd->exp_too_low_cnt = 0; @@ -1464,9 +1389,10 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) 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 && !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) { - sd->exposure = EXPOSURE_DEF; - sd->gain = GAIN_DEF; + if (sd->ctrls[AUTOGAIN].val + && !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) { + sd->ctrls[EXPOSURE].val = sd->ctrls[EXPOSURE].def; + sd->ctrls[GAIN].val = sd->ctrls[GAIN].def; if (gspca_dev->streaming) { sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; setexposure(gspca_dev); @@ -1477,32 +1403,6 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) return 0; } -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->autogain; - return 0; -} - -static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->freq = val; - if (gspca_dev->streaming) - setfreq(gspca_dev); - return 0; -} - -static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->freq; - return 0; -} - static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { -- cgit v0.10.2 From f0baad866b3c71d448747b06b1cf6b3493d2f7ce Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 13 Mar 2011 16:05:55 -0300 Subject: [media] gspca - sonixb: Update inactive flags to reflect autogain setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans de Goede Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 13cca7a..c6c65f2 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -209,7 +209,7 @@ static const struct ctrl sd_ctrls[NCTRLS] = { .step = 1, #define AUTOGAIN_DEF 1 .default_value = AUTOGAIN_DEF, - .flags = 0, + .flags = V4L2_CTRL_FLAG_UPDATE }, .set = sd_setautogain, }, @@ -1008,7 +1008,8 @@ static void do_autogain(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int avg_lum = atomic_read(&sd->avg_lum); - if (avg_lum == -1 || !sd->ctrls[AUTOGAIN].val) + if ((gspca_dev->ctrl_dis & (1 << AUTOGAIN)) || + avg_lum == -1 || !sd->ctrls[AUTOGAIN].val) return; if (sd->autogain_ignore_frames > 0) { @@ -1062,6 +1063,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->bridge = id->driver_info & 0xff; gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis; +#if AUTOGAIN_DEF + if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN))) + gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE); +#endif cam = &gspca_dev->cam; cam->ctrls = sd->ctrls; @@ -1400,6 +1405,11 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) } } + if (sd->ctrls[AUTOGAIN].val) + gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE); + else + gspca_dev->ctrl_inac = 0; + return 0; } -- cgit v0.10.2 From 75b79ffcc37c6bd05fa895f85d2d6426a9e4c3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Sun, 13 Mar 2011 16:07:25 -0300 Subject: [media] gspca - sonixb: Clenup source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update copyright and module author - set the sensor table as constant Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index c6c65f2..5a08738 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1,9 +1,9 @@ /* * sonix sn9c102 (bayer) library - * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr - * Add Pas106 Stefano Mozzi (C) 2004 * - * V4L2 by Jean-Francois Moine + * Copyright (C) 2009-2011 Jean-François Moine + * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr + * Add Pas106 Stefano Mozzi (C) 2004 * * 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 @@ -52,7 +52,7 @@ all: #include #include "gspca.h" -MODULE_AUTHOR("Michel Xhaard "); +MODULE_AUTHOR("Jean-François Moine "); MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -531,7 +531,7 @@ static const __u8 tas5130_sensor_init[][8] = { {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}, }; -static struct sensor_data sensor_data[] = { +static const struct sensor_data sensor_data[] = { SENS(initHv7131d, hv7131d_sensor_init, F_GAIN, NO_BRIGHTNESS|NO_FREQ, 0), SENS(initHv7131r, hv7131r_sensor_init, 0, NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0), SENS(initOv6650, ov6650_sensor_init, F_GAIN|F_SIF, 0, 0x60), -- cgit v0.10.2 From 7d47b789ceb456d90a3ace522dbcb2a94d2693ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Sun, 13 Mar 2011 16:17:08 -0300 Subject: [media] gspca - jeilinj / stv06xx: Fix some warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index 06b777f..36dae38 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -183,7 +183,6 @@ static void jlj_dostream(struct work_struct *work) struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; int blocks_left; /* 0x200-sized blocks remaining in current frame. */ - int size_in_blocks; int act_len; int packet_type; int ret; @@ -209,7 +208,6 @@ static void jlj_dostream(struct work_struct *work) act_len, JEILINJ_MAX_TRANSFER); if (ret < 0 || act_len < FRAME_HEADER_LEN) goto quit_stream; - size_in_blocks = buffer[0x0a]; blocks_left = buffer[0x0a] - 1; PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 7e06614..abf1658 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -525,11 +525,9 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; PDEBUG(D_PROBE, "Configuring camera"); - cam = &gspca_dev->cam; sd->desc = sd_desc; sd->bridge = id->driver_info; gspca_dev->sd_desc = &sd->desc; -- cgit v0.10.2 From 58c92d37e7d34433cfbc3989bec20e48fba0799e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Sun, 13 Mar 2011 16:36:49 -0300 Subject: [media] gspca - ov519: Add exposure and autogain controls for ov2610/2610ae MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 42670ec..fd1b608 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -1,7 +1,7 @@ /** * OV519 driver * - * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + * Copyright (C) 2008-2011 Jean-François Moine * Copyright (C) 2009 Hans de Goede * * This module is adapted from the ov51x-jpeg package, which itself @@ -61,10 +61,12 @@ static int i2c_detect_tries = 10; enum e_ctrl { BRIGHTNESS, CONTRAST, + EXPOSURE, COLORS, HFLIP, VFLIP, AUTOBRIGHT, + AUTOGAIN, FREQ, NCTRL /* number of controls */ }; @@ -142,9 +144,11 @@ enum sensors { /* V4L2 controls supported by the driver */ static void setbrightness(struct gspca_dev *gspca_dev); static void setcontrast(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); static void setcolors(struct gspca_dev *gspca_dev); static void sethvflip(struct gspca_dev *gspca_dev); static void setautobright(struct gspca_dev *gspca_dev); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static void setfreq(struct gspca_dev *gspca_dev); static void setfreq_i(struct sd *sd); @@ -173,6 +177,18 @@ static const struct ctrl sd_ctrls[] = { }, .set_control = setcontrast, }, +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set_control = setexposure, + }, [COLORS] = { { .id = V4L2_CID_SATURATION, @@ -222,6 +238,19 @@ static const struct ctrl sd_ctrls[] = { }, .set_control = setautobright, }, +[AUTOGAIN] = { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = V4L2_CTRL_FLAG_UPDATE + }, + .set = sd_setautogain, + }, [FREQ] = { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -238,50 +267,78 @@ static const struct ctrl sd_ctrls[] = { /* table of the disabled controls */ static const unsigned ctrl_dis[] = { -[SEN_OV2610] = (1 << NCTRL) - 1, /* no control */ +[SEN_OV2610] = ((1 << NCTRL) - 1) /* no control */ + ^ ((1 << EXPOSURE) /* but exposure */ + | (1 << AUTOGAIN)), /* and autogain */ -[SEN_OV2610AE] = (1 << NCTRL) - 1, /* no control */ +[SEN_OV2610AE] = ((1 << NCTRL) - 1) /* no control */ + ^ ((1 << EXPOSURE) /* but exposure */ + | (1 << AUTOGAIN)), /* and autogain */ [SEN_OV3610] = (1 << NCTRL) - 1, /* no control */ [SEN_OV6620] = (1 << HFLIP) | - (1 << VFLIP), + (1 << VFLIP) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV6630] = (1 << HFLIP) | - (1 << VFLIP), + (1 << VFLIP) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV66308AF] = (1 << HFLIP) | - (1 << VFLIP), + (1 << VFLIP) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV7610] = (1 << HFLIP) | - (1 << VFLIP), + (1 << VFLIP) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV7620] = (1 << HFLIP) | - (1 << VFLIP), + (1 << VFLIP) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV7620AE] = (1 << HFLIP) | - (1 << VFLIP), + (1 << VFLIP) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV7640] = (1 << HFLIP) | (1 << VFLIP) | (1 << AUTOBRIGHT) | - (1 << CONTRAST), + (1 << CONTRAST) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV7648] = (1 << HFLIP) | (1 << VFLIP) | (1 << AUTOBRIGHT) | - (1 << CONTRAST), + (1 << CONTRAST) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), -[SEN_OV7660] = (1 << AUTOBRIGHT), +[SEN_OV7660] = (1 << AUTOBRIGHT) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV7670] = (1 << COLORS) | - (1 << AUTOBRIGHT), + (1 << AUTOBRIGHT) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV76BE] = (1 << HFLIP) | - (1 << VFLIP), + (1 << VFLIP) | + (1 << EXPOSURE) | + (1 << AUTOGAIN), [SEN_OV8610] = (1 << HFLIP) | (1 << VFLIP) | + (1 << EXPOSURE) | + (1 << AUTOGAIN) | (1 << FREQ), }; @@ -3201,6 +3258,13 @@ static void ov519_set_fr(struct sd *sd) ov518_i2c_w(sd, OV7670_R11_CLKRC, clock); } +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + i2c_w_mask(sd, 0x13, sd->ctrls[AUTOGAIN].val ? 0x05 : 0x00, 0x05); +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -4189,12 +4253,16 @@ static int sd_start(struct gspca_dev *gspca_dev) setcontrast(gspca_dev); if (!(sd->gspca_dev.ctrl_dis & (1 << BRIGHTNESS))) setbrightness(gspca_dev); + if (!(sd->gspca_dev.ctrl_dis & (1 << EXPOSURE))) + setexposure(gspca_dev); if (!(sd->gspca_dev.ctrl_dis & (1 << COLORS))) setcolors(gspca_dev); if (!(sd->gspca_dev.ctrl_dis & ((1 << HFLIP) | (1 << VFLIP)))) sethvflip(gspca_dev); if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOBRIGHT))) setautobright(gspca_dev); + if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOGAIN))) + setautogain(gspca_dev); if (!(sd->gspca_dev.ctrl_dis & (1 << FREQ))) setfreq_i(sd); @@ -4608,6 +4676,14 @@ static void setcontrast(struct gspca_dev *gspca_dev) } } +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->ctrls[AUTOGAIN].val) + i2c_w(sd, 0x10, sd->ctrls[EXPOSURE].val); +} + static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -4666,6 +4742,22 @@ static void setautobright(struct gspca_dev *gspca_dev) i2c_w_mask(sd, 0x2d, sd->ctrls[AUTOBRIGHT].val ? 0x10 : 0x00, 0x10); } +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->ctrls[AUTOGAIN].val = val; + if (val) { + gspca_dev->ctrl_inac |= (1 << EXPOSURE); + } else { + gspca_dev->ctrl_inac &= ~(1 << EXPOSURE); + sd->ctrls[EXPOSURE].val = i2c_r(sd, 0x10); + } + if (gspca_dev->streaming) + setautogain(gspca_dev); + return gspca_dev->usb_err; +} + static void setfreq_i(struct sd *sd) { if (sd->sensor == SEN_OV7660 -- cgit v0.10.2 From 59929cefa5f573178df3c4abfb36d55f7b33f214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Mon, 14 Mar 2011 05:33:01 -0300 Subject: [media] gspca - main: Cleanup source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - change copyright and erroneous comment - small code optimization - have constant the device template Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index f21f2a2..6288174 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1,7 +1,7 @@ /* * Main USB camera driver * - * Copyright (C) 2008-2010 Jean-François Moine + * Copyright (C) 2008-2011 Jean-François Moine * * Camera button input handling by Márton Németh * Copyright (C) 2009-2010 Márton Németh @@ -414,7 +414,6 @@ resubmit: * - 0 or many INTER_PACKETs * - one LAST_PACKET * DISCARD_PACKET invalidates the whole frame. - * On LAST_PACKET, a new frame is returned. */ void gspca_frame_add(struct gspca_dev *gspca_dev, enum gspca_packet_type packet_type, @@ -1525,10 +1524,12 @@ static int vidioc_reqbufs(struct file *file, void *priv, gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); + + /* Don't restart the stream when switching from read + * to mmap mode */ + if (gspca_dev->memory == GSPCA_MEMORY_READ) + streaming = 0; } - /* Don't restart the stream when switching from read to mmap mode */ - if (gspca_dev->memory == GSPCA_MEMORY_READ) - streaming = 0; /* free the previous allocated buffers, if any */ if (gspca_dev->nframes != 0) @@ -2152,7 +2153,7 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_g_chip_ident = vidioc_g_chip_ident, }; -static struct video_device gspca_template = { +static const struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, -- cgit v0.10.2 From ce5b2acce60405b938d1f1f994024cde4e2cdd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Mon, 14 Mar 2011 08:49:28 -0300 Subject: [media] gspca - nw80x: New subdriver for Divio based webcams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [mchehab@redhat.com: Fix a few CodingStyle issues] Tested-by: Kjell Claesson Tested-by: Hans de Goede Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index dc72fff..5c542e6 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -103,6 +103,7 @@ spca561 046d:092d Logitech QC Elch2 spca561 046d:092e Logitech QC Elch2 spca561 046d:092f Logitech QuickCam Express Plus sunplus 046d:0960 Logitech ClickSmart 420 +nw80x 046d:d001 Logitech QuickCam Pro (dark focus ring) sunplus 0471:0322 Philips DMVC1300K zc3xx 0471:0325 Philips SPC 200 NC zc3xx 0471:0326 Philips SPC 300 NC @@ -150,10 +151,12 @@ sunplus 04fc:5330 Digitrex 2110 sunplus 04fc:5360 Sunplus Generic spca500 04fc:7333 PalmPixDC85 sunplus 04fc:ffff Pure DigitalDakota +nw80x 0502:d001 DVC V6 spca501 0506:00df 3Com HomeConnect Lite sunplus 052b:1507 Megapixel 5 Pretec DC-1007 sunplus 052b:1513 Megapix V4 sunplus 052b:1803 MegaImage VI +nw80x 052b:d001 EZCam Pro p35u tv8532 0545:808b Veo Stingray tv8532 0545:8333 Veo Stingray sunplus 0546:3155 Polaroid PDC3070 @@ -177,6 +180,7 @@ sunplus 055f:c530 Mustek Gsmart LCD 3 sunplus 055f:c540 Gsmart D30 sunplus 055f:c630 Mustek MDC4000 sunplus 055f:c650 Mustek MDC5500Z +nw80x 055f:d001 Mustek Wcam 300 mini zc3xx 055f:d003 Mustek WCam300A zc3xx 055f:d004 Mustek WCam300 AN conex 0572:0041 Creative Notebook cx11646 @@ -195,8 +199,12 @@ gl860 05e3:0503 Genesys Logic PC Camera gl860 05e3:f191 Genesys Logic PC Camera spca561 060b:a001 Maxell Compact Pc PM3 zc3xx 0698:2003 CTX M730V built in +nw80x 06a5:0000 Typhoon Webcam 100 USB +nw80x 06a5:d001 Divio based webcams +nw80x 06a5:d800 Divio Chicony TwinkleCam, Trust SpaceCam spca500 06bd:0404 Agfa CL20 spca500 06be:0800 Optimedia +nw80x 06be:d001 EZCam Pro p35u sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom spca506 06e1:a190 ADS Instant VCD ov534 06f8:3002 Hercules Blog Webcam @@ -204,6 +212,7 @@ ov534_9 06f8:3003 Hercules Dualpix HD Weblog sonixj 06f8:3004 Hercules Classic Silver sonixj 06f8:3008 Hercules Deluxe Optical Glass pac7302 06f8:3009 Hercules Classic Link +nw80x 0728:d001 AVerMedia Camguard spca508 0733:0110 ViewQuest VQ110 spca501 0733:0401 Intel Create and Share spca501 0733:0402 ViewQuest M318B diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index a20f6ae..eb04e8b 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -104,6 +104,15 @@ config USB_GSPCA_MR97310A To compile this driver as a module, choose M here: the module will be called gspca_mr97310a. +config USB_GSPCA_NW80X + tristate "Divio based (NW80x) USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the NW80x chips. + + To compile this driver as a module, choose M here: the + module will be called gspca_nw80x. + config USB_GSPCA_OV519 tristate "OV51x / OVFX2 / W996xCF USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index a0dbcfb..855fbc8 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o obj-$(CONFIG_USB_GSPCA_KONICA) += gspca_konica.o obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o +obj-$(CONFIG_USB_GSPCA_NW80X) += gspca_nw80x.o obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o obj-$(CONFIG_USB_GSPCA_OV534_9) += gspca_ov534_9.o @@ -48,6 +49,7 @@ gspca_jeilinj-objs := jeilinj.o gspca_konica-objs := konica.o gspca_mars-objs := mars.o gspca_mr97310a-objs := mr97310a.o +gspca_nw80x-objs := nw80x.o gspca_ov519-objs := ov519.o gspca_ov534-objs := ov534.o gspca_ov534_9-objs := ov534_9.o diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c new file mode 100644 index 0000000..1c9d3c2 --- /dev/null +++ b/drivers/media/video/gspca/nw80x.c @@ -0,0 +1,2443 @@ +/* + * DivIO nw80x subdriver + * + * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr) + * Copyright (C) 2003 Sylvain Munaut + * Kjell Claesson + * + * 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 + * 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 + */ + +#define MODULE_NAME "nw80x" + +#include "gspca.h" + +MODULE_AUTHOR("Jean-Francois Moine "); +MODULE_DESCRIPTION("NW80x USB Camera Driver"); +MODULE_LICENSE("GPL"); + +static int webcam; + +/* controls */ +enum e_ctrl { + GAIN, + EXPOSURE, + AUTOGAIN, + NCTRLS /* number of controls */ +}; + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct gspca_ctrl ctrls[NCTRLS]; + + u32 ae_res; + s8 ag_cnt; +#define AG_CNT_START 13 + + u8 bridge; + u8 webcam; +}; + +enum bridges { + BRIDGE_NW800, /* et31x110 */ + BRIDGE_NW801, + BRIDGE_NW802, +}; +enum webcams { + Generic800, + SpaceCam, /* Trust 120 SpaceCam */ + SpaceCam2, /* other Trust 120 SpaceCam */ + Cvideopro, /* Conceptronic Video Pro */ + Dlink350c, + DS3303u, + Kr651us, + Kritter, + Mustek300, + Proscope, + Twinkle, + DsbC110, + DvcV6, + P35u, + Generic802, + NWEBCAMS /* number of webcams */ +}; + +/* + - webcams: + - Typhoon Webcam 100 USB (06a5:0000) + nw800 + - Trust SpaceCam120 or SpaceCam100 PORTABLE (06a5:d800) + nw801 SpaceCam.init + or trust_space.init if no LED (?) + - Divio Chicony TwinkleCam (06a5:d800) ? + nw800 Twinkle.init + - Plustek Opticam 500U or ProLink DS3303u + nw801 DS3303u.init + - Logitech QuickCam Pro (dark focus ring) (046d:d001) + nw801 + - EZCam Pro p35u (052b:d001, 06a5:d001 and 06be:d001) + nw801 - sensor Sharp IR3Y38M + - AVerMedia Camguard (0728:d001) + nw801 + - Panasonic GP-KR651US (06a5:d001) + nw802 kr651us.init + - iRez Kritter cam + nw802 kritter.init + - D-link dru-350c cam + nw802 d-link-350c.init + - The Scope USB Microscope M2 (ProScope) + = Divio ProLink DS3303u WebCam (06a5:d001) + = Scalar USB Microscope M2 (Proscope) + nw802 proscope.init + - Conceptronic Video Pro 'CVIDEOPRO USB Webcam CCD' (06a5:d001) + nw802 cvideopro.init + - Mustek Wcam 300 mini + nw802 mustek_300_mini.init + - D-Link NetQam Pro 250plus (06a5:d001) + - Showcam NGS webcam (065a:d800) + - sceptre svc300 + - DSB-C110 (06a5:d800) + et31x110 + - DVC V6 (0502:d001) + nw802 + - registers + nw800/et31x110 nw801 nw802 + 0000..009e 0000..00a1 0000..009e + 0200..0211 id id + 0300..0302 id id + 0400..0406 (inex) 0400..0406 + 0500..0505 0500..0506 (inex) + 0600..061a 0600..0601 0600..0601 + 0800..0814 id id + 1000..109c 1000..10a1 1000..109a + + 080c: luma (nw800/nw802) + 080d: luma (nw801) + 1004: LUT (?) + 100b: R gain (0..63) + 100c: B gain + 100d: G gain + 100e: Y gain + 100f: U gain + 1010: V gain + 1019: clock (nw801 - bit 0x08: indoor/outdoor) + 101b: shutter 1 (0..255) + 101c: shutter 2 + 1026: BP = gain (nw801) + 1041, 1052, 1063, 1074: LUT base (nw802) + 1048, 1059, 106a, 107b: LUT base (nw801) + - resolutions + nw800 352x288 + nw801/nw802 320x240 - 640x480 +*/ + +static const struct v4l2_pix_format sif_mode[] = { + {352, 288, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 4 / 8, + .colorspace = V4L2_COLORSPACE_JPEG} +}; +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +/* + * The sequences below contain: + * - 1st and 2nd bytes: either + * - register number (BE) + * - I2C0 + i2c address + * - 3rd byte: data length (=0 for end of sequence) + * - n bytes: data + */ +#define I2C0 0xff +static const u8 nw800_init[] = { + 0x05, 0x00, 0x01, 0x55, + 0x10, 0x9b, 0x01, 0xaa, + 0x05, 0x02, 0x01, 0x02, + 0x06, 0x00, 0x02, 0x04, 0xd9, + 0x05, 0x05, 0x01, 0x00, + 0x05, 0x05, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x04, + + 0x04, 0x06, 0x01, 0xc0, + 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24, + 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08, + 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0, + 0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + + 0x04, 0x04, 0x01, 0xff, + 0x04, 0x06, 0x01, 0xc4, + + 0x04, 0x06, 0x01, 0xc0, + 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24, + 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08, + 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0, + 0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + + 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, + 0x40, + 0x00, 0x80, 0x01, 0xa0, + 0x10, 0x1a, 0x01, 0x00, + 0x00, 0x91, 0x02, 0x6c, 0x01, + 0x00, 0x03, 0x02, 0xc8, 0x01, + 0x10, 0x1a, 0x01, 0x00, + 0x10, 0x00, 0x01, 0x83, + 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, + 0x20, 0x01, 0x60, 0x01, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x10, 0x1b, 0x02, 0x69, 0x00, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x05, 0x02, 0x01, 0x02, + 0x06, 0x00, 0x02, 0x04, 0xd9, + 0x05, 0x05, 0x01, 0x20, + 0x05, 0x05, 0x01, 0x21, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83, + 0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0, + 0xea, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x13, 0x13, + 0x10, 0x03, 0x01, 0x14, + 0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83, + 0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0, + 0xea, + 0x10, 0x0b, 0x01, 0x14, + 0x10, 0x0d, 0x01, 0x20, + 0x10, 0x0c, 0x01, 0x34, + 0x04, 0x06, 0x01, 0xc3, + 0x04, 0x04, 0x01, 0x00, + 0x05, 0x02, 0x01, 0x02, + 0x06, 0x00, 0x02, 0x00, 0x48, + 0x05, 0x05, 0x01, 0x20, + 0x05, 0x05, 0x01, 0x21, + 0, 0, 0 +}; + +/* 06a5:d001 - nw801 - p35u */ +static const u8 nw801_init_1[] = { + 0x05, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x0e, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e, + 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x69, 0xa8, 0x1f, 0x00, + 0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00, + 0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00, + 0x36, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x22, 0x02, 0x80, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x15, 0x08, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x35, 0xfd, 0x07, 0x3d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x14, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7, + 0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80, + 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, 0xa4, + 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, 0xcf, + 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, + 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, + 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x82, 0x02, + 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01, + 0xf0, 0x00, + 0, 0, 0, +}; +static const u8 nw801_init_qvga[] = { + 0x02, 0x00, 0x10, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x18, 0x0b, 0x06, 0xa2, 0x86, 0x78, + 0x02, 0x0f, 0x01, 0x6b, + 0x10, 0x1a, 0x01, 0x15, + 0x00, 0x00, 0x01, 0x1e, + 0x10, 0x00, 0x01, 0x2f, + 0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00, + /* AE window */ + 0, 0, 0, +}; +static const u8 nw801_init_vga[] = { + 0x02, 0x00, 0x10, 0x78, 0xa0, 0x97, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xf0, + 0x02, 0x0f, 0x01, 0xd5, + 0x10, 0x1a, 0x01, 0x15, + 0x00, 0x00, 0x01, 0x0e, + 0x10, 0x00, 0x01, 0x22, + 0x10, 0x8c, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, + 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01, + 0, 0, 0, +}; +static const u8 nw801_init_2[] = { + 0x10, 0x04, 0x01, 0x1a, + 0x10, 0x19, 0x01, 0x09, /* clock */ + 0x10, 0x24, 0x06, 0xc0, 0x00, 0x3f, 0x02, 0x00, 0x01, + /* .. gain .. */ + 0x00, 0x03, 0x02, 0x92, 0x03, + 0x00, 0x1d, 0x04, 0xf2, 0x00, 0x24, 0x07, + 0x00, 0x7b, 0x01, 0xcf, + 0x10, 0x94, 0x01, 0x07, + 0x05, 0x05, 0x01, 0x01, + 0x05, 0x04, 0x01, 0x01, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8, + 0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0, + 0xf0, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x0c, 0x0c, + 0x10, 0x03, 0x01, 0x08, + 0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8, + 0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0, + 0xf0, + 0x10, 0x0b, 0x01, 0x0b, + 0x10, 0x0d, 0x01, 0x0b, + 0x10, 0x0c, 0x01, 0x1f, + 0x05, 0x06, 0x01, 0x03, + 0, 0, 0 +}; + +/* nw802 (sharp IR3Y38M?) */ +static const u8 nw802_init[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x4d, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x08, 0x00, 0x18, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0xff, 0x01, 0xc0, 0x00, 0x14, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x00, + 0x10, 0x00, 0x01, 0xad, + 0x00, 0x00, 0x01, 0x08, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00, + 0x10, 0x1d, 0x08, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, + 0x10, 0x0e, 0x01, 0x27, + 0x10, 0x41, 0x11, 0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b, + 0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2, + 0xd8, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x14, 0x14, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, 0xf1, + 0xff, +/* 0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b, + * 0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2, + * 0xd8, */ + 0x10, 0x0b, 0x01, 0x10, + 0x10, 0x0d, 0x01, 0x11, + 0x10, 0x0c, 0x01, 0x1c, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; +/* et31x110 - Trust 120 SpaceCam */ +static const u8 spacecam_init[] = { +/*fixme: at connection time*/ + 0x04, 0x05, 0x01, 0x01, + 0x04, 0x04, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x04, + 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x05, 0x01, 0x00, +/*fixme: add 300ms delay?*/ +/*fixme: at capture start time*/ + 0x04, 0x06, 0x01, 0x44, + 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24, + 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08, + 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x7c, 0x00, 0x80, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + 0x04, 0x06, 0x01, 0xc0, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, + 0x40, + 0x00, 0x80, 0x01, 0xa0, + 0x10, 0x1a, 0x01, 0x00, + 0x00, 0x91, 0x02, 0x32, 0x01, + 0x00, 0x03, 0x02, 0x08, 0x02, + 0x10, 0x00, 0x01, 0x83, + 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, + 0x20, 0x01, 0x60, 0x01, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x13, 0x13, + 0x10, 0x03, 0x01, 0x06, + 0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, + 0x10, 0x0b, 0x01, 0x08, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1f, + 0x04, 0x06, 0x01, 0xc3, + 0x04, 0x05, 0x01, 0x40, + 0x04, 0x04, 0x01, 0x40, + 0, 0, 0 +}; +/* et31x110 - other Trust SpaceCam120 */ +static const u8 spacecam2_init[] = { + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x04, + 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x05, 0x01, 0x00, + 0x04, 0x06, 0x01, 0x44, + 0x04, 0x06, 0x01, 0x00, + 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc, + 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03, + 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, + 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x04, 0x04, 0x01, 0x40, + 0x04, 0x04, 0x01, 0x00, +#if 1 + I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x05, + I2C0, 0x40, 0x02, 0x11, 0x06, + I2C0, 0x40, 0x02, 0x14, 0x00, + I2C0, 0x40, 0x02, 0x13, 0x01, +#else + 0x06, 0x00, 0x0b, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x05, + 0x06, 0x00, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x05, + 0x05, 0x02, 0x02, 0x0c, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x06, + 0x06, 0x00, 0x02, 0x11, 0x06, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x02, 0x14, 0x00, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x13, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, + 0x40, +#if 1 + I2C0, 0x40, 0x02, 0x02, 0x0c, + I2C0, 0x40, 0x02, 0x0f, 0x00, + I2C0, 0x40, 0x02, 0x13, 0x01, +#else + 0x06, 0x00, 0x01, 0x0c, + 0x06, 0x00, 0x02, 0x02, 0x0c, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x02, 0x0f, 0x00, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x13, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x10, 0x00, 0x01, 0x01, + 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, + 0x20, 0x01, 0x60, 0x01, +#if 1 + I2C0, 0x40, 0x02, 0x05, 0x0f, + I2C0, 0x40, 0x02, 0x13, 0x01, + I2C0, 0x40, 0x07, 0x09, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00, + I2C0, 0x40, 0x03, 0x12, 0x04, 0x01, +#else + 0x06, 0x00, 0x01, 0x0f, + 0x06, 0x00, 0x02, 0x05, 0x0f, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x13, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x06, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00, + 0x06, 0x00, 0x07, 0x09, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00, + 0x05, 0x02, 0x02, 0x07, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x04, 0x01, + 0x06, 0x00, 0x03, 0x12, 0x04, 0x01, + 0x05, 0x02, 0x02, 0x03, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x13, 0x13, + 0x10, 0x03, 0x01, 0x06, + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x0b, 0x01, 0x11, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x14, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw802 - Conceptronic Video Pro */ +static const u8 cvideopro_init[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4, + 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, + 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, + 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30, + 0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, + 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, + 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0, + 0x40, + 0x10, 0x1a, 0x01, 0x03, + 0x10, 0x00, 0x01, 0xac, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x3b, 0x01, + 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, + 0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x1d, 0x02, 0x40, 0x06, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0, + 0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc, + 0xdc, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x12, 0x12, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0, + 0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc, + 0xdc, + 0x10, 0x0b, 0x01, 0x09, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x2f, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw802 - D-link dru-350c cam */ +static const u8 dlink_init[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d, +/* 0xf9, 0x02, = nb pixels per line */ + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, +/* 0x19, v index of clamping pulse */ + 0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, +/* 0x00, 0x03, 0x02, 0x92, 0x03, = nb of pixels per line */ + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x00, + 0x10, 0x00, 0x01, 0xad, + 0x00, 0x00, 0x01, 0x08, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00, + 0x10, 0x1d, 0x08, 0x40, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x0e, 0x01, 0x20, + 0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f, + 0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf, + 0xea, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x11, 0x11, + 0x10, 0x03, 0x01, 0x10, + 0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f, + 0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf, + 0xea, + 0x10, 0x0b, 0x01, 0x19, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1e, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw801 - Plustek Opticam 500U or ProLink DS3303u (Hitachi HD49322BF) */ +static const u8 ds330_init[] = { + 0x05, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x16, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e, + 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa9, 0xa8, 0x1f, 0x00, + 0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00, + 0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00, + 0x36, 0x00, + 0x02, 0x00, 0x12, 0x03, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0x50, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x2f, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a, + 0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4, + 0x00, 0x01, 0x15, 0xfd, 0x07, 0x3d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x8c, 0x04, 0x01, 0x20, + 0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7, + 0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80, + 0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f, 0x88, + 0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4, 0xcb, + 0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, + 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, + 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f, 0x01, + 0x00, 0x00, 0xef, 0x00, 0x02, 0x0a, 0x82, 0x02, + 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01, + 0xf0, 0x00, + + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x3f, 0x00, 0xf2, 0x8f, 0x81, + 0x40, + 0x10, 0x1a, 0x01, 0x15, + 0x10, 0x00, 0x01, 0x2f, + 0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, + 0x10, 0x26, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x24, 0x02, 0x40, 0x06, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6, + 0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x16, 0x16, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6, + 0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, + 0x10, 0x0b, 0x01, 0x26, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1c, + 0x05, 0x06, 0x01, 0x03, + 0x05, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* 06a5:d001 - nw802 - Panasonic GP-KR651US (Philips TDA8786) */ +static const u8 kr651_init_1[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x48, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4, + 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, + 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, + 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30, + 0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, + 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, + 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0, 0, 0 +}; +static const u8 kr651_init_qvga[] = { + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x03, + 0x10, 0x00, 0x01, 0xac, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00, + 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, + 0x10, 0x1d, 0x02, 0x28, 0x01, + 0, 0, 0 +}; +static const u8 kr651_init_vga[] = { + 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x30, 0x03, 0x01, 0x82, 0x82, 0x98, + 0x80, + 0x10, 0x1a, 0x01, 0x03, + 0x10, 0x00, 0x01, 0xa0, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, + + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01, + + 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, + 0x10, 0x1d, 0x02, 0x68, 0x00, +}; +static const u8 kr651_init_2[] = { + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8, + 0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdc, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x0c, 0x0c, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8, + 0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdc, + 0x10, 0x0b, 0x01, 0x10, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x2d, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw802 - iRez Kritter cam */ +static const u8 kritter_init[] = { + 0x04, 0x06, 0x01, 0x06, + 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0x94, 0x03, 0x18, 0x00, 0x48, + 0x0f, 0x1e, 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x1b, 0x00, 0x0a, 0x01, 0x28, + 0x07, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, + 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, + 0x00, 0x5d, 0x00, 0x0e, 0x00, 0x7e, 0x00, 0x30, + 0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, + 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0b, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, + 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0, + 0x40, + 0x10, 0x1a, 0x01, 0x03, + 0x10, 0x00, 0x01, 0xaf, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x3b, 0x01, + 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, + 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, + 0x10, 0x1d, 0x02, 0x00, 0x00, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86, + 0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4, + 0xcb, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x0d, 0x0d, + 0x10, 0x03, 0x01, 0x02, + 0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86, + 0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4, + 0xcb, + 0x10, 0x0b, 0x01, 0x17, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1e, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw802 - Mustek Wcam 300 mini */ +static const u8 mustek_init[] = { + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, + 0x00, 0x10, 0x06, 0xfc, 0x05, 0x0c, 0x06, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x00, + 0x10, 0x00, 0x01, 0xad, + 0x00, 0x00, 0x01, 0x08, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1d, 0x08, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x10, 0x0e, 0x01, 0x0f, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e, + 0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9, + 0xff, + 0x10, 0x0f, 0x02, 0x11, 0x11, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e, + 0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9, + 0xff, + 0x10, 0x0b, 0x01, 0x1c, + 0x10, 0x0d, 0x01, 0x1a, + 0x10, 0x0c, 0x01, 0x34, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x40, + 0x04, 0x06, 0x01, 0x03, + 0, 0, 0 +}; + +/* nw802 - Scope USB Microscope M2 (ProScope) (Hitachi HD49322BF) */ +static const u8 proscope_init_1[] = { + 0x04, 0x05, 0x01, 0x21, + 0x04, 0x04, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x04, + 0x00, 0x00, 0x40, 0x10, 0x01, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x04, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x08, 0x00, 0x17, 0x00, 0xce, 0x00, 0xf4, + 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0xce, 0x00, 0xf8, 0x03, 0x3e, 0x00, 0x86, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0xb6, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xf6, 0x03, 0x34, 0x04, 0xf6, 0x03, 0x34, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xe8, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x1f, 0x0f, 0x08, 0x20, 0xa8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xad, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a, + 0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x8c, 0x04, 0x01, + 0x20, 0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f, + 0x88, 0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4, + 0xcb, 0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f, + 0x01, 0x00, 0x00, 0xef, 0x00, 0x09, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0, 0, 0 +}; +static const u8 proscope_init_qvga[] = { + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, + 0x40, + 0x10, 0x1a, 0x01, 0x06, + 0x00, 0x03, 0x02, 0xf9, 0x02, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x0e, 0x01, 0x10, + 0, 0, 0 +}; +static const u8 proscope_init_vga[] = { + 0x00, 0x03, 0x02, 0xf9, 0x02, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, + 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x16, 0x00, 0x00, 0x82, 0x84, 0x00, + 0x80, + 0x10, 0x1a, 0x01, 0x06, + 0x10, 0x00, 0x01, 0xa1, + 0x10, 0x1b, 0x02, 0x00, 0x00, + 0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x11, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, + 0x10, 0x0e, 0x01, 0x10, + 0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae, + 0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0, 0, 0 +}; +static const u8 proscope_init_2[] = { + 0x10, 0x0f, 0x02, 0x0c, 0x0c, + 0x10, 0x03, 0x01, 0x0c, + 0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae, + 0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2, + 0xf9, + 0x10, 0x0b, 0x01, 0x0b, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1b, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x05, 0x01, 0x21, + 0x04, 0x04, 0x01, 0x00, + 0, 0, 0 +}; + +/* nw800 - Divio Chicony TwinkleCam */ +static const u8 twinkle_init[] = { + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x04, + 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x05, 0x01, 0x00, + + 0x04, 0x06, 0x01, 0x44, + 0x04, 0x06, 0x01, 0x00, + 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc, + 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03, + 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, + 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x08, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x10, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x00, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + + 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, +/*320 3f ef, 0x00 */ + 0x04, 0x04, 0x01, 0x10, + 0x04, 0x04, 0x01, 0x00, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x01, +#if 1 + I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, + I2C0, 0x40, 0x02, 0x11, 0x06, + I2C0, 0x40, 0x02, 0x14, 0x00, + I2C0, 0x40, 0x02, 0x13, 0x01, + I2C0, 0x40, 0x02, 0x07, 0x01, +#else + 0x06, 0x00, 0x0b, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, + 0x06, 0x00, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, + 0x05, 0x02, 0x02, 0x0c, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x06, + 0x06, 0x00, 0x02, 0x11, 0x06, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x02, 0x14, 0x00, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x13, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x07, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, +/* 320 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, */ + 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, +/* 320 0x00, 0x78, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x5c, */ + 0x40, +#if 1 + I2C0, 0x40, 0x02, 0x02, 0x0c, + I2C0, 0x40, 0x02, 0x13, 0x01, +#else + 0x06, 0x00, 0x01, 0x0c, + 0x06, 0x00, 0x02, 0x02, 0x0c, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x13, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x10, 0x00, 0x01, 0x01, + 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, + 0x20, 0x01, 0x60, 0x01, +/* 320 0xf0, 0x00, 0x40, 0x01, */ +#if 1 + I2C0, 0x40, 0x02, 0x05, 0x0f, + I2C0, 0x40, 0x02, 0x13, 0x01, + I2C0, 0x40, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17, + I2C0, 0x40, 0x03, 0x12, 0x00, 0x01, +#else + 0x06, 0x00, 0x01, 0x0f, + 0x06, 0x00, 0x02, 0x05, 0x0f, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x13, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x07, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17, + 0x06, 0x00, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17, + 0x05, 0x02, 0x02, 0x08, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x00, 0x01, + 0x06, 0x00, 0x03, 0x12, 0x00, 0x01, + 0x05, 0x02, 0x02, 0x03, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, +/* 320 3f ef */ +#if 1 + I2C0, 0x40, 0x02, 0x12, 0x00, + I2C0, 0x40, 0x02, 0x0e, 0x00, + I2C0, 0x40, 0x02, 0x11, 0x06, +#else + 0x06, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x02, 0x12, 0x00, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x02, 0x0e, 0x00, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x06, + 0x06, 0x00, 0x02, 0x11, 0x06, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x0c, 0x0c, + 0x10, 0x03, 0x01, 0x06, + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x0b, 0x01, 0x19, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x0d, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x41, + 0, 0, 0 +}; + +/* et31x110 DSB-C110 */ +static const u8 dsbc110_init[] = { + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x04, + 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x05, 0x01, 0x00, + + 0x04, 0x06, 0x01, 0x44, + 0x04, 0x06, 0x01, 0x00, + 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, + 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc, + 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, + 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, + 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, + 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, + 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, + 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, + 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03, + 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, + 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, + 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x08, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x10, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x00, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, + 0x01, 0x60, 0x01, 0x00, 0x00, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x04, 0x04, 0x01, 0x10, + 0x04, 0x04, 0x01, 0x00, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x01, +#if 1 + I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, + I2C0, 0x40, 0x02, 0x11, 0x06, + I2C0, 0x40, 0x02, 0x14, 0x00, + I2C0, 0x40, 0x02, 0x13, 0x01, + I2C0, 0x40, 0x02, 0x07, 0x01, +#else + 0x06, 0x00, 0x0b, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, + 0x06, 0x00, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, + 0x05, 0x02, 0x02, 0x0c, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x06, + 0x06, 0x00, 0x02, 0x11, 0x06, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x02, 0x14, 0x00, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x13, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x07, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xe4, + 0x40, +#if 1 + I2C0, 0x40, 0x02, 0x02, 0x0c, + I2C0, 0x40, 0x02, 0x13, 0x01, +#else + 0x06, 0x00, 0x01, 0x1c, + 0x06, 0x00, 0x02, 0x02, 0x1c, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x13, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x10, 0x00, 0x01, 0x01, + 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, + 0xf0, 0x00, 0x40, 0x01, +#if 1 + I2C0, 0x40, 0x02, 0x05, 0x23, + I2C0, 0x40, 0x02, 0x13, 0x01, + I2C0, 0x40, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x00, + I2C0, 0x40, 0x03, 0x12, 0x00, 0x01, +#else + 0x06, 0x00, 0x01, 0x24, + 0x06, 0x00, 0x02, 0x05, 0x24, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x13, 0x01, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x07, 0x06, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x00, + 0x06, 0x00, 0x08, 0x08, 0x06, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x00, + 0x05, 0x02, 0x02, 0x08, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x02, 0x00, 0x01, + 0x06, 0x00, 0x03, 0x12, 0x00, 0x01, + 0x05, 0x02, 0x02, 0x03, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, +#if 1 + I2C0, 0x40, 0x02, 0x12, 0x00, + I2C0, 0x40, 0x02, 0x0e, 0x00, + I2C0, 0x40, 0x02, 0x11, 0x06, +#else + 0x06, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x02, 0x12, 0x00, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x02, 0x0e, 0x00, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, + 0x06, 0x00, 0x01, 0x06, + 0x06, 0x00, 0x02, 0x11, 0x06, + 0x05, 0x02, 0x02, 0x02, 0x40, + 0x05, 0x05, 0x01, 0x01, +#endif + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x0c, 0x0c, + 0x10, 0x03, 0x01, 0x06, + 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, + 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, + 0xf9, + 0x10, 0x0b, 0x01, 0x14, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1d, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x41, +}; + +/* nw802 dvc-v6 */ +static const u8 dvcv6_init[] = { + 0x04, 0x06, 0x01, 0x06, + 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c, + 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, + 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4, + 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54, + 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, + 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30, + 0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f, + 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, + 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94, + 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00, + 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, + 0x40, 0x20, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x06, 0x00, 0x02, 0x09, 0x99, + 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c, + 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, + 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, + 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, + 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, + 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, + 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, + 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, + 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, + 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, + 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, + 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82, + 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, + 0x01, 0xf0, 0x00, + 0x00, 0x03, 0x02, 0x94, 0x03, + 0x00, 0x1d, 0x04, 0x0a, 0x01, 0x28, 0x07, + 0x00, 0x7b, 0x02, 0xe0, 0x00, + 0x10, 0x8d, 0x01, 0x00, + 0x00, 0x09, 0x04, 0x1e, 0x00, 0x0c, 0x02, + 0x00, 0x91, 0x02, 0x0b, 0x02, + 0x10, 0x00, 0x01, 0xaf, + 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8f, 0x3c, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0, + 0x40, + 0x10, 0x1a, 0x01, 0x02, + 0x10, 0x00, 0x01, 0xaf, + 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, + 0x10, 0x1b, 0x02, 0x07, 0x01, + 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00, + 0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00, + 0x10, 0x1d, 0x02, 0x40, 0x06, + 0x10, 0x0e, 0x01, 0x08, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa, + 0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc, + 0xdc, + 0x10, 0x03, 0x01, 0x00, + 0x10, 0x0f, 0x02, 0x12, 0x12, + 0x10, 0x03, 0x01, 0x11, + 0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa, + 0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc, + 0xdc, + 0x10, 0x0b, 0x01, 0x16, + 0x10, 0x0d, 0x01, 0x10, + 0x10, 0x0c, 0x01, 0x1a, + 0x04, 0x06, 0x01, 0x03, + 0x04, 0x04, 0x01, 0x00, +}; + +static const u8 *webcam_init[] = { + [Generic800] = nw800_init, + [SpaceCam] = spacecam_init, + [SpaceCam2] = spacecam2_init, + [Cvideopro] = cvideopro_init, + [Dlink350c] = dlink_init, + [DS3303u] = ds330_init, + [Kr651us] = kr651_init_1, + [Kritter] = kritter_init, + [Mustek300] = mustek_init, + [Proscope] = proscope_init_1, + [Twinkle] = twinkle_init, + [DsbC110] = dsbc110_init, + [DvcV6] = dvcv6_init, + [P35u] = nw801_init_1, + [Generic802] = nw802_init, +}; + +/* -- write a register -- */ +static void reg_w(struct gspca_dev *gspca_dev, + u16 index, + const u8 *data, + int len) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + if (len == 1) + PDEBUG(D_USBO, "SET 00 0000 %04x %02x", index, *data); + else + PDEBUG(D_USBO, "SET 00 0000 %04x %02x %02x ...", + index, *data, data[1]); + memcpy(gspca_dev->usb_buf, data, len); + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x00, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, /* value */ + index, + gspca_dev->usb_buf, + len, + 500); + if (ret < 0) { + err("reg_w err %d", ret); + gspca_dev->usb_err = ret; + } +} + +/* -- read registers in usb_buf -- */ +static void reg_r(struct gspca_dev *gspca_dev, + u16 index, + int len) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, index, + gspca_dev->usb_buf, len, 500); + if (ret < 0) { + err("reg_r err %d", ret); + gspca_dev->usb_err = ret; + return; + } + if (len == 1) + PDEBUG(D_USBI, "GET 00 0000 %04x %02x", + index, gspca_dev->usb_buf[0]); + else + PDEBUG(D_USBI, "GET 00 0000 %04x %02x %02x ..", + index, gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1]); +} + +static void i2c_w(struct gspca_dev *gspca_dev, + u8 i2c_addr, + const u8 *data, + int len) +{ + u8 val[2]; + int i; + + reg_w(gspca_dev, 0x0600, data + 1, len - 1); + reg_w(gspca_dev, 0x0600, data, len); + val[0] = len; + val[1] = i2c_addr; + reg_w(gspca_dev, 0x0502, val, 2); + val[0] = 0x01; + reg_w(gspca_dev, 0x0501, val, 1); + for (i = 5; --i >= 0; ) { + msleep(4); + reg_r(gspca_dev, 0x0505, 1); + if (gspca_dev->usb_err < 0) + return; + if (gspca_dev->usb_buf[0] == 0) + return; + } + gspca_dev->usb_err = -ETIME; +} + +static void reg_w_buf(struct gspca_dev *gspca_dev, + const u8 *cmd) +{ + u16 reg; + int len; + + for (;;) { + reg = *cmd++ << 8; + reg += *cmd++; + len = *cmd++; + if (len == 0) + break; + if (cmd[-3] != I2C0) + reg_w(gspca_dev, reg, cmd, len); + else + i2c_w(gspca_dev, reg & 0xff, cmd, len); + cmd += len; + } +} + +static int swap_6bits(int v) +{ + int r, i; + + r = 0; + for (i = 0; i < 6; i++) { + r <<= 1; + if (v & 1) + r++; + v >>= 1; + } + return r; +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val, v[2]; + + val = sd->ctrls[GAIN].val >> 1; /* 0 - 63 -> 0 - 31 */ + reg_w(gspca_dev, 0x100e, &val, 1); /* AE Y gain */ + + switch (sd->webcam) { + case P35u: + /* Note the control goes from 0-255 not 0-127, but anything + above 127 just means amplifying noise */ + val = sd->ctrls[GAIN].val << 1; /* 0 - 63 -> 0 - 127 */ + reg_w(gspca_dev, 0x1026, &val, 1); + break; + case Kr651us: + /* 0 - 63 -> 0 - 0x37 */ + val = (sd->ctrls[GAIN].val * 0x37) / 63; + val = swap_6bits(val); + v[0] = val << 3; + v[1] = val >> 5; + reg_w(gspca_dev, 0x101d, v, 2); /* SIF reg0/1 (AGC) */ + break; + } +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 v[2]; + + switch (sd->webcam) { + case P35u: + v[0] = (sd->ctrls[EXPOSURE].val << 3) | 0x01; + reg_w(gspca_dev, 0x1019, v, 1); + break; + case Kr651us: + v[0] = sd->ctrls[EXPOSURE].val; + v[1] = sd->ctrls[EXPOSURE].val >> 8; + reg_w(gspca_dev, 0x101b, v, 2); + break; + } +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int w, h; + + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN)) + return; + if (!sd->ctrls[AUTOGAIN].val) { + sd->ag_cnt = -1; + return; + } + sd->ag_cnt = AG_CNT_START; + + reg_r(gspca_dev, 0x1004, 1); + if (gspca_dev->usb_buf[0] & 0x04) { /* if AE_FULL_FRM */ + sd->ae_res = gspca_dev->width * gspca_dev->height; + } else { /* get the AE window size */ + reg_r(gspca_dev, 0x1011, 8); + w = (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0] + - (gspca_dev->usb_buf[3] << 8) - gspca_dev->usb_buf[2]; + h = (gspca_dev->usb_buf[5] << 8) + gspca_dev->usb_buf[4] + - (gspca_dev->usb_buf[7] << 8) - gspca_dev->usb_buf[6]; + sd->ae_res = h * w; + if (sd->ae_res == 0) + sd->ae_res = gspca_dev->width * gspca_dev->height; + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if ((unsigned) webcam >= NWEBCAMS) + webcam = 0; + sd->webcam = webcam; + gspca_dev->cam.reverse_alts = 1; + gspca_dev->cam.ctrls = sd->ctrls; + sd->ag_cnt = -1; + return 0; +} + +static int nw802_test_reg(struct gspca_dev *gspca_dev, + u16 index, + u8 value) +{ + /* write the value */ + reg_w(gspca_dev, index, &value, 1); + + /* read it */ + reg_r(gspca_dev, index, 1); + + return gspca_dev->usb_buf[0] == value; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* + * Autodetect sequence inspired from some log. + * We try to detect what registers exist or not. + * If 0x0500 does not exist => NW802 + * If it does, test 0x109b. If it doesn't exist, + * then it's a NW801. Else, a NW800 + */ + if (!nw802_test_reg(gspca_dev, 0x0500, 0x55)) { + sd->bridge = BRIDGE_NW802; + if (sd->webcam == Generic800) + sd->webcam = Generic802; + } else if (!nw802_test_reg(gspca_dev, 0x109b, 0xaa)) { + sd->bridge = BRIDGE_NW801; + if (sd->webcam == Generic800) + sd->webcam = P35u; + } + PDEBUG(D_PROBE, "Bridge nw80%d", sd->bridge); + + if (sd->bridge == BRIDGE_NW800) { + gspca_dev->cam.cam_mode = sif_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(sif_mode); + } else { + gspca_dev->cam.cam_mode = vga_mode; + switch (sd->webcam) { + case Generic802: + case Kr651us: + case Proscope: + case P35u: + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + break; + default: + gspca_dev->cam.nmodes = 1; /* qvga only */ + break; + } + } + switch (sd->webcam) { + case P35u: +/* sd->ctrls[EXPOSURE].min = 0; + * sd->ctrls[EXPOSURE].max = 9; + * sd->ctrls[EXPOSURE].def = 1; */ + break; + case Kr651us: +/* sd->ctrls[EXPOSURE].min = 0; */ + sd->ctrls[EXPOSURE].max = 315; + sd->ctrls[EXPOSURE].def = 150; + break; + default: + gspca_dev->ctrl_dis = ~(1 << GAIN); + break; + } + + return gspca_dev->usb_err; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + const u8 *cmd; + + cmd = webcam_init[sd->webcam]; + reg_w_buf(gspca_dev, cmd); + switch (sd->webcam) { + case P35u: + if (gspca_dev->width == 320) + reg_w_buf(gspca_dev, nw801_init_qvga); + else + reg_w_buf(gspca_dev, nw801_init_vga); + reg_w_buf(gspca_dev, nw801_init_2); + break; + case Kr651us: + if (gspca_dev->width == 320) + reg_w_buf(gspca_dev, kr651_init_qvga); + else + reg_w_buf(gspca_dev, kr651_init_vga); + reg_w_buf(gspca_dev, kr651_init_2); + break; + case Proscope: + if (gspca_dev->width == 320) + reg_w_buf(gspca_dev, proscope_init_qvga); + else + reg_w_buf(gspca_dev, proscope_init_vga); + reg_w_buf(gspca_dev, proscope_init_2); + break; + } + + setgain(gspca_dev); + setexposure(gspca_dev); + setautogain(gspca_dev); + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 value; + + /* 'go' off */ + if (sd->bridge != BRIDGE_NW801) { + value = 0x02; + reg_w(gspca_dev, 0x0406, &value, 1); + } + + /* LED off */ + switch (sd->webcam) { + case Cvideopro: + case Kr651us: + case DvcV6: + case Kritter: + value = 0xff; + break; + case Dlink350c: + value = 0x21; + break; + case SpaceCam: + case SpaceCam2: + case Proscope: + case Twinkle: + value = 0x01; + break; + default: + return; + } + reg_w(gspca_dev, 0x0404, &value, 1); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* + * frame header = '00 00 hh ww ss xx ff ff' + * with: + * - 'hh': height / 4 + * - 'ww': width / 4 + * - 'ss': frame sequence number c0..dd + */ + if (data[0] == 0x00 && data[1] == 0x00 + && data[6] == 0xff && data[7] == 0xff) { + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, data + 8, len - 8); + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + } +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->ctrls[AUTOGAIN].val = val; + if (val) + gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE); + else + gspca_dev->ctrl_inac = 0; + if (gspca_dev->streaming) + setautogain(gspca_dev); + return gspca_dev->usb_err; +} + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int luma; + int gain, shutter; + + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt >= 0) + return; + sd->ag_cnt = AG_CNT_START; + + /* get the average luma */ + reg_r(gspca_dev, sd->bridge == BRIDGE_NW801 ? 0x080d : 0x080c, 4); + luma = (gspca_dev->usb_buf[3] << 24) + (gspca_dev->usb_buf[2] << 16) + + (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; + luma /= sd->ae_res; + + if (sd->webcam == P35u) { + u8 clock; + + if (luma > 92 && luma < 108) + return; /* fine */ + clock = sd->ctrls[EXPOSURE].val; + gain = sd->ctrls[GAIN].val; + if (luma < 100) { + if (luma < 70 && clock > 0) + clock--; + if (gain > 98 && clock > 0) + clock--; + if (gain <= 50) + gain += 3; + } else { + if (luma > 150 && clock < 9) + clock++; + if (gain < 12 && clock < 9) + clock++; + if (gain >= 5) + gain -= 3; + } + if (gain != sd->ctrls[GAIN].val) { + sd->ctrls[GAIN].val = gain; + setgain(gspca_dev); + } + if (clock != sd->ctrls[EXPOSURE].val) { + sd->ctrls[EXPOSURE].val = clock; + setexposure(gspca_dev); + } + return; + } + + /* kr651us */ + if (luma > 95 && luma < 105) + return; /* fine */ + gain = sd->ctrls[GAIN].val; + shutter = sd->ctrls[EXPOSURE].val; + if (luma < 100) { + if (shutter > 0) { + if (luma < 85 && shutter > 50) + shutter -= 50; + else + shutter--; + } else if (gain < 63) { + if (luma < 85 && gain < 53) + gain += 10; + else + gain++; + } + } else { + if (gain > 0) { + if (luma > 115 && gain > 10) + gain -= 10; + else + gain--; + } else if (shutter < 316) { /* max 0x13b */ + if (luma > 115 && shutter < 266) + shutter += 50; + else + shutter++; + } + } + if (gain != sd->ctrls[GAIN].val) { + sd->ctrls[GAIN].val = gain; + setgain(gspca_dev); + } + if (shutter != sd->ctrls[EXPOSURE].val) { + sd->ctrls[EXPOSURE].val = shutter; + setexposure(gspca_dev); + } +} + +/* V4L2 controls supported by the driver */ +static const struct ctrl sd_ctrls[NCTRLS] = { +[GAIN] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 16 + }, + .set_control = setgain + }, +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 9, + .step = 1, + .default_value = 1 + }, + .set_control = setexposure + }, +[AUTOGAIN] = { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = V4L2_CTRL_FLAG_UPDATE + }, + .set = sd_setautogain + }, +}; + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0xd001)}, + {USB_DEVICE(0x0502, 0xd001)}, + {USB_DEVICE(0x052b, 0xd001)}, + {USB_DEVICE(0x055f, 0xd001)}, + {USB_DEVICE(0x06a5, 0x0000)}, + {USB_DEVICE(0x06a5, 0xd001)}, + {USB_DEVICE(0x06a5, 0xd800)}, + {USB_DEVICE(0x06be, 0xd001)}, + {USB_DEVICE(0x0728, 0xd001)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + return usb_register(&sd_driver); +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +module_param(webcam, int, 0644); +MODULE_PARM_DESC(webcam, + "Webcam type\n" + "0: generic\n" + "1: Trust 120 SpaceCam\n" + "2: other Trust 120 SpaceCam\n" + "3: Conceptronic Video Pro\n" + "4: D-link dru-350c\n" + "5: Plustek Opticam 500U\n" + "6: Panasonic GP-KR651US\n" + "7: iRez Kritter\n" + "8: Mustek Wcam 300 mini\n" + "9: Scalar USB Microscope M2 (Proscope)\n" + "10: Divio Chicony TwinkleCam\n" + "11: DSB-C110\n" + "12: DVC-V6\n"); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 8c80fd3..aa6c393 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -396,6 +396,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */ #define V4L2_PIX_FMT_CIT_YYVYUY v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */ #define V4L2_PIX_FMT_KONICA420 v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */ +#define V4L2_PIX_FMT_JPGL v4l2_fourcc('J', 'P', 'G', 'L') /* JPEG-Lite */ /* * F O R M A T E N U M E R A T I O N -- cgit v0.10.2 From c1e13971b19324305b35fafc080b527465214d3c Mon Sep 17 00:00:00 2001 From: Ian Armstrong Date: Fri, 18 Mar 2011 19:23:05 -0300 Subject: [media] af9015: enhance RC Patch from Ian Armstrong. I've encountered a couple of problems with the current af9015 driver as supplied with the 2.6.37 kernel, that the attached patch appears to fix. (I've generated this patch against the current v4l-dvb git). Some key-presses are lost. A key-press is only generated upon 'valid' data (buf[14] == (u8) ~buf[15]), but the buffer is wiped before this check. Sometimes the 15th byte has not been set at the time of read, so the data isn't valid & ignored. On the next poll the 15th byte has been set, but the rest of the data was wiped previously, so the data is still invalid & the key is lost. Weird repeat error, where an old key press is sometimes repeated in error. ie. button sequence '1 (pause) 2 (pause) 3 (pause) 4' generates output like '1 (pause) 2 (pause) 23 (pause) 4'. The current driver zeroes the data for the key pushed, but sometimes this data is already zero but with other bytes set suggesting a repeat code. This results in the last key being incorrectly repeated. This patch attempts to reduce the risk of a missed key-press & also stop the random repeat of an old key-press when a new key is pressed. Signed-off-by: Ian Armstrong Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 8671ca3..6457d04 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -479,6 +479,9 @@ static int af9015_init_endpoint(struct dvb_usb_device *d) ret = af9015_set_reg_bit(d, 0xd50b, 0); else ret = af9015_clear_reg_bit(d, 0xd50b, 0); + if (ret) + goto error; + ret = af9015_write_reg(d, 0x98e9, 0xff); error: if (ret) err("endpoint init failed:%d", ret); @@ -1016,22 +1019,35 @@ static int af9015_rc_query(struct dvb_usb_device *d) { struct af9015_state *priv = d->priv; int ret; - u8 buf[16]; + u8 buf[17]; /* read registers needed to detect remote controller code */ ret = af9015_read_regs(d, 0x98d9, buf, sizeof(buf)); if (ret) goto error; - if (buf[14] || buf[15]) { + /* If any of these are non-zero, assume invalid data */ + if (buf[1] || buf[2] || buf[3]) + return ret; + + /* Check for repeat of previous code */ + if ((priv->rc_repeat != buf[6] || buf[0]) && + !memcmp(&buf[12], priv->rc_last, 4)) { + deb_rc("%s: key repeated\n", __func__); + rc_keydown(d->rc_dev, priv->rc_keycode, 0); + priv->rc_repeat = buf[6]; + return ret; + } + + /* Only process key if canary killed */ + if (buf[16] != 0xff && buf[0] != 0x01) { + /* Reset the canary */ + af9015_write_reg(d, 0x98e9, 0xff); deb_rc("%s: key pressed %02x %02x %02x %02x\n", __func__, buf[12], buf[13], buf[14], buf[15]); - /* clean IR code from mem */ - ret = af9015_write_regs(d, 0x98e5, "\x00\x00\x00\x00", 4); - if (ret) - goto error; - + /* Remember this key */ + memcpy(priv->rc_last, &buf[12], 4); if (buf[14] == (u8) ~buf[15]) { if (buf[12] == (u8) ~buf[13]) { /* NEC */ @@ -1041,15 +1057,16 @@ static int af9015_rc_query(struct dvb_usb_device *d) priv->rc_keycode = buf[12] << 16 | buf[13] << 8 | buf[14]; } - rc_keydown(d->rc_dev, priv->rc_keycode, 0); } else { - priv->rc_keycode = 0; /* clear just for sure */ + priv->rc_keycode = buf[12] << 24 | buf[13] << 16 | + buf[14] << 8 | buf[15]; } - } else if (priv->rc_repeat != buf[6] || buf[0]) { - deb_rc("%s: key repeated\n", __func__); rc_keydown(d->rc_dev, priv->rc_keycode, 0); } else { deb_rc("%s: no key press\n", __func__); + /* Invalidate last keypress */ + /* Not really needed, but helps with debug */ + priv->rc_last[2] = priv->rc_last[3]; } priv->rc_repeat = buf[6]; diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index f20cfa6..beb3004 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -102,6 +102,7 @@ struct af9015_state { struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */ u8 rc_repeat; u32 rc_keycode; + u8 rc_last[4]; }; struct af9015_config { -- cgit v0.10.2 From 1e8750c2ac2bcbf2b084d3fa77cbb5eef33b3e7f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 Mar 2011 19:36:42 -0300 Subject: [media] af9015: small RC change Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 6457d04..da2f87e 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -479,9 +479,7 @@ static int af9015_init_endpoint(struct dvb_usb_device *d) ret = af9015_set_reg_bit(d, 0xd50b, 0); else ret = af9015_clear_reg_bit(d, 0xd50b, 0); - if (ret) - goto error; - ret = af9015_write_reg(d, 0x98e9, 0xff); + error: if (ret) err("endpoint init failed:%d", ret); @@ -614,6 +612,11 @@ static int af9015_init(struct dvb_usb_device *d) int ret; deb_info("%s:\n", __func__); + /* init RC canary */ + ret = af9015_write_reg(d, 0x98e9, 0xff); + if (ret) + goto error; + ret = af9015_init_endpoint(d); if (ret) goto error; @@ -1041,11 +1044,14 @@ static int af9015_rc_query(struct dvb_usb_device *d) /* Only process key if canary killed */ if (buf[16] != 0xff && buf[0] != 0x01) { - /* Reset the canary */ - af9015_write_reg(d, 0x98e9, 0xff); deb_rc("%s: key pressed %02x %02x %02x %02x\n", __func__, buf[12], buf[13], buf[14], buf[15]); + /* Reset the canary */ + ret = af9015_write_reg(d, 0x98e9, 0xff); + if (ret) + goto error; + /* Remember this key */ memcpy(priv->rc_last, &buf[12], 4); if (buf[14] == (u8) ~buf[15]) { @@ -1058,6 +1064,7 @@ static int af9015_rc_query(struct dvb_usb_device *d) buf[13] << 8 | buf[14]; } } else { + /* 32 bit NEC */ priv->rc_keycode = buf[12] << 24 | buf[13] << 16 | buf[14] << 8 | buf[15]; } -- cgit v0.10.2 From 638b53f80ceb5a57b5c9b6c4a078f605a8cc2d71 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 19 Mar 2011 14:11:55 -0300 Subject: [media] add TerraTec remote Remote used for TerraTec Cinergy T Stick RC. Keytable from Martin Groszhauser Signed-off-by: Antti Palosaari Cc: Martin Groszhauser Cc: TerraTux Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index f0c8055..cb72121 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-technisat-usb2.o \ rc-terratec-cinergy-xs.o \ rc-terratec-slim.o \ + rc-terratec-slim-2.o \ rc-tevii-nec.o \ rc-total-media-in-hand.o \ rc-trekstor.o \ diff --git a/drivers/media/rc/keymaps/rc-terratec-slim-2.c b/drivers/media/rc/keymaps/rc-terratec-slim-2.c new file mode 100644 index 0000000..4409391 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-terratec-slim-2.c @@ -0,0 +1,72 @@ +/* + * TerraTec remote controller keytable + * + * Copyright (C) 2011 Martin Groszhauser + * Copyright (C) 2011 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +/* + * TerraTec slim remote, 6 rows, 3 columns. + * Keytable from Martin Groszhauser + */ +static struct rc_map_table terratec_slim_2[] = { + { 0x8001, KEY_MUTE }, /* MUTE */ + { 0x8002, KEY_VOLUMEDOWN }, + { 0x8003, KEY_CHANNELDOWN }, + { 0x8004, KEY_1 }, + { 0x8005, KEY_2 }, + { 0x8006, KEY_3 }, + { 0x8007, KEY_4 }, + { 0x8008, KEY_5 }, + { 0x8009, KEY_6 }, + { 0x800a, KEY_7 }, + { 0x800c, KEY_ZOOM }, /* [fullscreen] */ + { 0x800d, KEY_0 }, + { 0x800e, KEY_AGAIN }, /* [two arrows forming a circle] */ + { 0x8012, KEY_POWER2 }, /* [red power button] */ + { 0x801a, KEY_VOLUMEUP }, + { 0x801b, KEY_8 }, + { 0x801e, KEY_CHANNELUP }, + { 0x801f, KEY_9 }, +}; + +static struct rc_map_list terratec_slim_2_map = { + .map = { + .scan = terratec_slim_2, + .size = ARRAY_SIZE(terratec_slim_2), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_TERRATEC_SLIM_2, + } +}; + +static int __init init_rc_map_terratec_slim_2(void) +{ + return rc_map_register(&terratec_slim_2_map); +} + +static void __exit exit_rc_map_terratec_slim_2(void) +{ + rc_map_unregister(&terratec_slim_2_map); +} + +module_init(init_rc_map_terratec_slim_2) +module_exit(exit_rc_map_terratec_slim_2) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari "); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 4617117..d843afc 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -134,6 +134,7 @@ void rc_map_init(void); #define RC_MAP_TECHNISAT_USB2 "rc-technisat-usb2" #define RC_MAP_TERRATEC_CINERGY_XS "rc-terratec-cinergy-xs" #define RC_MAP_TERRATEC_SLIM "rc-terratec-slim" +#define RC_MAP_TERRATEC_SLIM_2 "rc-terratec-slim-2" #define RC_MAP_TEVII_NEC "rc-tevii-nec" #define RC_MAP_TOTAL_MEDIA_IN_HAND "rc-total-media-in-hand" #define RC_MAP_TREKSTOR "rc-trekstor" -- cgit v0.10.2 From 04599c2b1ff1eece19b7abaacd97266b8a6b79f2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 19 Mar 2011 14:25:36 -0300 Subject: [media] af9015: map remote for TerraTec Cinergy T Stick RC Map rc-terratec-slim-2 for TerraTec Cinergy T Stick RC device. Signed-off-by: Antti Palosaari Cc: Martin Groszhauser Cc: TerraTux Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index da2f87e..0bbc91f 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -744,6 +744,8 @@ static const struct af9015_rc_setup af9015_rc_setup_hashes[] = { }; static const struct af9015_rc_setup af9015_rc_setup_usbids[] = { + { (USB_VID_TERRATEC << 16) + USB_PID_TERRATEC_CINERGY_T_STICK_RC, + RC_MAP_TERRATEC_SLIM_2 }, { (USB_VID_TERRATEC << 16) + USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC, RC_MAP_TERRATEC_SLIM }, { (USB_VID_VISIONPLUS << 16) + USB_PID_AZUREWAVE_AD_TU700, -- cgit v0.10.2 From 582e565caeaccf145e2256b84d4fde827fede72e Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 19 Mar 2011 16:51:43 -0300 Subject: [media] af9015: reimplement firmware download Split FW download packages smarter way and bug free. Implementation is based of Andrea Merello's example he provided for tda18218 driver. Count remaining FW bytes down in loop instead of division and modulo combination used earlier. Thanks-to: Andrea Merello Signed-off-by: Antti Palosaari Cc: Andrea Merello Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 0bbc91f..100ebc3 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -665,9 +665,8 @@ error: static int af9015_download_firmware(struct usb_device *udev, const struct firmware *fw) { - int i, len, packets, remainder, ret; + int i, len, remaining, ret; struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL}; - u16 addr = 0x5100; /* firmware start address */ u16 checksum = 0; deb_info("%s:\n", __func__); @@ -679,24 +678,20 @@ static int af9015_download_firmware(struct usb_device *udev, af9015_config.firmware_size = fw->size; af9015_config.firmware_checksum = checksum; - #define FW_PACKET_MAX_DATA 55 - - packets = fw->size / FW_PACKET_MAX_DATA; - remainder = fw->size % FW_PACKET_MAX_DATA; - len = FW_PACKET_MAX_DATA; - for (i = 0; i <= packets; i++) { - if (i == packets) /* set size of the last packet */ - len = remainder; + #define FW_ADDR 0x5100 /* firmware start address */ + #define LEN_MAX 55 /* max packet size */ + for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) { + len = remaining; + if (len > LEN_MAX) + len = LEN_MAX; req.data_len = len; - req.data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA); - req.addr = addr; - addr += FW_PACKET_MAX_DATA; + req.data = (u8 *) &fw->data[fw->size - remaining]; + req.addr = FW_ADDR + fw->size - remaining; ret = af9015_rw_udev(udev, &req); if (ret) { - err("firmware download failed at packet %d with " \ - "code %d", i, ret); + err("firmware download failed:%d", ret); goto error; } } -- cgit v0.10.2 From 109a29900a36b2b1e7b7f83a561c6af7e343461f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 19 Mar 2011 17:18:18 -0300 Subject: [media] af9013: download FW earlier in attach() Quick test shows that FW must be running before other checks so make it happen earlier. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index ba25fa0..6f50f06 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -1466,20 +1466,6 @@ struct dvb_frontend *af9013_attach(const struct af9013_config *config, state->i2c = i2c; memcpy(&state->config, config, sizeof(struct af9013_config)); - /* chip version */ - ret = af9013_read_reg_bits(state, 0xd733, 4, 4, &buf[2]); - if (ret) - goto error; - - /* ROM version */ - for (i = 0; i < 2; i++) { - ret = af9013_read_reg(state, 0x116b + i, &buf[i]); - if (ret) - goto error; - } - deb_info("%s: chip version:%d ROM version:%d.%d\n", __func__, - buf[2], buf[0], buf[1]); - /* download firmware */ if (state->config.output_mode != AF9013_OUTPUT_MODE_USB) { ret = af9013_download_firmware(state); @@ -1495,6 +1481,20 @@ struct dvb_frontend *af9013_attach(const struct af9013_config *config, } info("firmware version:%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]); + /* chip version */ + ret = af9013_read_reg_bits(state, 0xd733, 4, 4, &buf[2]); + if (ret) + goto error; + + /* ROM version */ + for (i = 0; i < 2; i++) { + ret = af9013_read_reg(state, 0x116b + i, &buf[i]); + if (ret) + goto error; + } + deb_info("%s: chip version:%d ROM version:%d.%d\n", __func__, + buf[2], buf[0], buf[1]); + /* settings for mp2if */ if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) { /* AF9015 split PSB to 1.5k + 0.5k */ -- cgit v0.10.2 From 6ed9d56086acb3bb4ec003ff6d2f54313774d72b Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 19 Mar 2011 18:02:22 -0300 Subject: [media] af9013: reimplement firmware download Split FW download packages smarter way and bug free. Implementation is based of Andrea Merello's example he provided for tda18218 driver. Count remaining FW bytes down in loop instead of division and modulo combination used earlier. Thanks to: Andrea Merello Signed-off-by: Antti Palosaari Cc: Andrea Merello Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index 6f50f06..345311c 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -1323,13 +1323,11 @@ static struct dvb_frontend_ops af9013_ops; static int af9013_download_firmware(struct af9013_state *state) { - int i, len, packets, remainder, ret; + int i, len, remaining, ret; const struct firmware *fw; - u16 addr = 0x5100; /* firmware start address */ u16 checksum = 0; u8 val; u8 fw_params[4]; - u8 *data; u8 *fw_file = AF9013_DEFAULT_FIRMWARE; msleep(100); @@ -1373,21 +1371,18 @@ static int af9013_download_firmware(struct af9013_state *state) if (ret) goto error_release; - #define FW_PACKET_MAX_DATA 16 - - packets = fw->size / FW_PACKET_MAX_DATA; - remainder = fw->size % FW_PACKET_MAX_DATA; - len = FW_PACKET_MAX_DATA; - for (i = 0; i <= packets; i++) { - if (i == packets) /* set size of the last packet */ - len = remainder; - - data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA); - ret = af9013_write_ofsm_regs(state, addr, data, len); - addr += FW_PACKET_MAX_DATA; + #define FW_ADDR 0x5100 /* firmware start address */ + #define LEN_MAX 16 /* max packet size */ + for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) { + len = remaining; + if (len > LEN_MAX) + len = LEN_MAX; + ret = af9013_write_ofsm_regs(state, + FW_ADDR + fw->size - remaining, + (u8 *) &fw->data[fw->size - remaining], len); if (ret) { - err("firmware download failed at %d with %d", i, ret); + err("firmware download failed:%d", ret); goto error_release; } } -- cgit v0.10.2 From f0e3b646ccce66c9e878966227ce8415d3c11c2a Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Sun, 13 Mar 2011 15:20:22 -0300 Subject: [media] Make 2.6.39 not 2.6.38 the version when Multi-planar API was added Multi-planar API was added to 2.6.39 version of Video for Linux 2 API. Signed-off-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/compat.xml b/Documentation/DocBook/v4l/compat.xml index 4d74bf2..9f7cd4f 100644 --- a/Documentation/DocBook/v4l/compat.xml +++ b/Documentation/DocBook/v4l/compat.xml @@ -2354,9 +2354,12 @@ that used it. It was originally scheduled for removal in 2.6.35.
- V4L2 in Linux 2.6.38 + V4L2 in Linux 2.6.39 + The old VIDIOC_*_OLD symbols and V4L1 support were removed. + + Multi-planar API added. Does not affect the compatibility of current drivers and applications. See multi-planar API @@ -2364,14 +2367,6 @@ that used it. It was originally scheduled for removal in 2.6.35.
-
- V4L2 in Linux 2.6.39 - - - The old VIDIOC_*_OLD symbols and V4L1 support were removed. - - -
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml index ae7a069..a7fd76d 100644 --- a/Documentation/DocBook/v4l/v4l2.xml +++ b/Documentation/DocBook/v4l/v4l2.xml @@ -130,17 +130,8 @@ applications. --> 2.6.39 2011-03-01 - mcc - Removed VIDIOC_*_OLD from videodev2.h header and update it to reflect latest changes. - - - - - 2.6.38 - 2011-01-16 - po - Added the multi-planar API. - + mcc, po + Removed VIDIOC_*_OLD from videodev2.h header and update it to reflect latest changes. Added the multi-planar API. -- cgit v0.10.2 From 950720840f392075b19d902e5ca7806a17a562cb Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Sun, 13 Mar 2011 15:23:32 -0300 Subject: [media] Update Pawel Osciak's e-mail address Signed-off-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index e1f96ea..b03d74e 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -8,7 +8,7 @@ * operation (via the mem2mem framework). * * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. - * Pawel Osciak, + * Pawel Osciak, * Marek Szyprowski, * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ #define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev" MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); -MODULE_AUTHOR("Pawel Osciak, "); +MODULE_AUTHOR("Pawel Osciak, "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c index a78e5c9..3b15bf5 100644 --- a/drivers/media/video/v4l2-mem2mem.c +++ b/drivers/media/video/v4l2-mem2mem.c @@ -5,7 +5,7 @@ * source and destination. * * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. - * Pawel Osciak, + * Pawel Osciak, * Marek Szyprowski, * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,7 @@ #include MODULE_DESCRIPTION("Mem to mem device framework for videobuf"); -MODULE_AUTHOR("Pawel Osciak, "); +MODULE_AUTHOR("Pawel Osciak, "); MODULE_LICENSE("GPL"); static bool debug; diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 3e9b67b..ce03225 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Pawel Osciak + * Author: Pawel Osciak * Marek Szyprowski * * This program is free software; you can redistribute it and/or modify @@ -1810,5 +1810,5 @@ size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count, EXPORT_SYMBOL_GPL(vb2_write); MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2"); -MODULE_AUTHOR("Pawel Osciak, Marek Szyprowski"); +MODULE_AUTHOR("Pawel Osciak , Marek Szyprowski"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index bb6a560..90495b7 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Pawel Osciak + * Author: Pawel Osciak * * 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 @@ -181,5 +181,5 @@ void vb2_dma_contig_cleanup_ctx(void *alloc_ctx) EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx); MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2"); -MODULE_AUTHOR("Pawel Osciak"); +MODULE_AUTHOR("Pawel Osciak "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c index a3eb656..5370a3a 100644 --- a/drivers/media/video/videobuf2-memops.c +++ b/drivers/media/video/videobuf2-memops.c @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Pawel Osciak + * Author: Pawel Osciak * Marek Szyprowski * * This program is free software; you can redistribute it and/or modify @@ -231,5 +231,5 @@ const struct vm_operations_struct vb2_common_vm_ops = { EXPORT_SYMBOL_GPL(vb2_common_vm_ops); MODULE_DESCRIPTION("common memory handling routines for videobuf2"); -MODULE_AUTHOR("Pawel Osciak"); +MODULE_AUTHOR("Pawel Osciak "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c index b5e6936..a3a8842 100644 --- a/drivers/media/video/videobuf2-vmalloc.c +++ b/drivers/media/video/videobuf2-vmalloc.c @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Pawel Osciak + * Author: Pawel Osciak * * 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 @@ -128,5 +128,5 @@ const struct vb2_mem_ops vb2_vmalloc_memops = { EXPORT_SYMBOL_GPL(vb2_vmalloc_memops); MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2"); -MODULE_AUTHOR("Pawel Osciak"); +MODULE_AUTHOR("Pawel Osciak "); MODULE_LICENSE("GPL"); diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index bf5eaaf..16ac473 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -5,7 +5,7 @@ * and destination. * * Copyright (c) 2009 Samsung Electronics Co., Ltd. - * Pawel Osciak, + * Pawel Osciak, * Marek Szyprowski, * * This program is free software; you can redistribute it and/or modify diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 597efe6..f87472a 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Pawel Osciak + * Author: Pawel Osciak * * 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 diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h index fb7ca84..1d6188d 100644 --- a/include/media/videobuf2-dma-contig.h +++ b/include/media/videobuf2-dma-contig.h @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Pawel Osciak + * Author: Pawel Osciak * * 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 diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index fee1703..84e1f6c 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Pawel Osciak + * Author: Pawel Osciak * Marek Szyprowski * * This program is free software; you can redistribute it and/or modify diff --git a/include/media/videobuf2-vmalloc.h b/include/media/videobuf2-vmalloc.h index a76b8af..93a76b4 100644 --- a/include/media/videobuf2-vmalloc.h +++ b/include/media/videobuf2-vmalloc.h @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Pawel Osciak + * Author: Pawel Osciak * * 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 -- cgit v0.10.2 From 4ffabdb35ad59fdb8cb6ad3ff4733694ff0dfc35 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Sun, 20 Mar 2011 18:17:34 -0300 Subject: [media] vb2: vb2_poll() fix return values for file I/O mode poll() should be returning poll-specific error values, not E* errors. Signed-off-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index ce03225..8c6f04b 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -1364,18 +1364,18 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) struct vb2_buffer *vb = NULL; /* - * Start file io emulator if streaming api has not been used yet. + * 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 ret; + return POLLERR; } if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) { ret = __vb2_init_fileio(q, 0); if (ret) - return ret; + return POLLERR; /* * Write to OUTPUT queue can be done immediately. */ -- cgit v0.10.2 From 5db2c3ba4de8489a7a064bac463bb8af2c7a1ae4 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Sun, 20 Mar 2011 19:26:41 -0300 Subject: [media] vb2: Handle return value from start_streaming callback Fix vb2 not handling return value from start_streaming() callback. Signed-off-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 8c6f04b..6698c77 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -1111,6 +1111,7 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf); int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { struct vb2_buffer *vb; + int ret; if (q->fileio) { dprintk(1, "streamon: file io in progress\n"); @@ -1138,12 +1139,16 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) } } - q->streaming = 1; - /* * Let driver notice that streaming state has been enabled. */ - call_qop(q, start_streaming, q); + ret = call_qop(q, start_streaming, q); + if (ret) { + dprintk(1, "streamon: driver refused to start streaming\n"); + return ret; + } + + q->streaming = 1; /* * If any buffers were queued before streamon, -- cgit v0.10.2 From 38a66824d96de8aeeb915e6f46f0d3fe55828eb1 Mon Sep 17 00:00:00 2001 From: Stephan Lachowsky Date: Thu, 27 Jan 2011 23:04:33 -0300 Subject: [media] uvcvideo: Fix uvc_fixup_video_ctrl() format search The scheme used to index format in uvc_fixup_video_ctrl() is not robust: format index is based on descriptor ordering, which does not necessarily match bFormatIndex ordering. Searching for first matching format will prevent uvc_fixup_video_ctrl() from using the wrong format/frame to make adjustments. Signed-off-by: Stephan Lachowsky Cc: stable@kernel.org Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 5673d67..545c029 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -89,15 +89,19 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl) { - struct uvc_format *format; + struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; unsigned int i; - if (ctrl->bFormatIndex <= 0 || - ctrl->bFormatIndex > stream->nformats) - return; + for (i = 0; i < stream->nformats; ++i) { + if (stream->format[i].index == ctrl->bFormatIndex) { + format = &stream->format[i]; + break; + } + } - format = &stream->format[ctrl->bFormatIndex - 1]; + if (format == NULL) + return; for (i = 0; i < format->nframes; ++i) { if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) { -- cgit v0.10.2 From 4093a5c4a3f59cba1a085bbf87b6ffdddc5a443d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 23 Feb 2011 11:19:17 -0300 Subject: [media] uvcvideo: Fix descriptor parsing for video output devices Commit 4057ac6ca9a77c4275b34b5925ab5c99557913b1 V4L/DVB (13505): uvcvideo: Refactor chain scan broke output terminals parsing. Fix it. Signed-off-by: Laurent Pinchart Cc: stable@kernel.org Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index a1e9dfb..6459b8c 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -1264,6 +1264,14 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, break; + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" OT %d", entity->id); + + break; + case UVC_TT_STREAMING: if (UVC_ENTITY_IS_ITERM(entity)) { if (uvc_trace_param & UVC_TRACE_PROBE) -- cgit v0.10.2 From 35d9f510b67b10338161aba6229d4f55b4000f5b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Feb 2011 06:37:02 -0300 Subject: [media] V4L: videobuf, don't use dma addr as physical mem->dma_handle is a dma address obtained by dma_alloc_coherent which needn't be a physical address in presence of IOMMU, as a hardware IOMMU can (and most likely) will return a bus address where physical != bus address. So ensure we are remapping (remap_pfn_range) the right page in __videobuf_mmap_mapper by using virt_to_phys(mem->vaddr) and not mem->dma_handle. While at it, use PFN_DOWN instead of explicit shift. [mchehab@redhat.com: Fix compilation breakage due to the lack of a comma] Signed-off-by: Jiri Slaby Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index c969111..c4742fc 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -300,7 +300,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); retval = remap_pfn_range(vma, vma->vm_start, - mem->dma_handle >> PAGE_SHIFT, + PFN_DOWN(virt_to_phys(mem->vaddr)), size, vma->vm_page_prot); if (retval) { dev_err(q->dev, "mmap: remap failed with error %d. ", retval); -- cgit v0.10.2 From 7e270941c8f0a11224e383d3d38946451f66fbbf Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Thu, 17 Feb 2011 17:33:56 -0300 Subject: [media] Zarlink zl10036 DVB-S: Fix mem leak in zl10036_attach On Thu, 17 Feb 2011, Matthias Schwarzott wrote: > On Sunday 06 February 2011, Jesper Juhl wrote: > > If the memory allocation to 'state' succeeds but we jump to the 'error' > > label before 'state' is assigned to fe->tuner_priv, then the call to > > 'zl10036_release(fe)' at the 'error:' label will not free 'state', but > > only what was previously assigned to 'tuner_priv', thus leaking the memory > > allocated to 'state'. > > There are may ways to fix this, including assigning the allocated memory > > directly to 'fe->tuner_priv', but I did not go for that since the > > additional pointer derefs are more expensive than the local variable, so I > > just added a 'kfree(state)' call. I guess the call to 'zl10036_release' > > might not even be needed in this case, but I wasn't sure, so I left it in. > > > Yeah, that call to zl10036_release can be completely eleminated. > Another thing is: jumping to the error label only makes sense when memory was > already allocated. So the jump in line 471 can be replaced by "return NULL", > as the other error handling before allocation: > if (NULL == config) { > printk(KERN_ERR "%s: no config specified", __func__); > goto error; > } > > I suggest to improve the patch to clean the code up when changing that. > > But I am fine with commiting this patch also if you do not want to change it. > Thank you for your feedback. It makes a lot of sense. Changing it is not a problem :) How about the updated patch below? If the memory allocation to 'state' succeeds but we jump to the 'error' label before 'state' is assigned to fe->tuner_priv, then the call to 'zl10036_release(fe)' at the 'error:' label will not free 'state', but only what was previously assigned to 'tuner_priv', thus leaking the memory allocated to 'state'. This patch fixes the leak and also does not jump to 'error:' before mem has been allocated but instead just returns. Also some small style cleanups. Signed-off-by: Jesper Juhl Signed-off-by: Matthias Schwarzott Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/zl10036.c b/drivers/media/dvb/frontends/zl10036.c index 4627f49..81aa984 100644 --- a/drivers/media/dvb/frontends/zl10036.c +++ b/drivers/media/dvb/frontends/zl10036.c @@ -463,16 +463,16 @@ struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, const struct zl10036_config *config, struct i2c_adapter *i2c) { - struct zl10036_state *state = NULL; + struct zl10036_state *state; int ret; - if (NULL == config) { + if (!config) { printk(KERN_ERR "%s: no config specified", __func__); - goto error; + return NULL; } state = kzalloc(sizeof(struct zl10036_state), GFP_KERNEL); - if (NULL == state) + if (!state) return NULL; state->config = config; @@ -507,7 +507,7 @@ struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, return fe; error: - zl10036_release(fe); + kfree(state); return NULL; } EXPORT_SYMBOL(zl10036_attach); -- cgit v0.10.2 From 868e90d55c0eb6f3c5949c2a747714072c3e080a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 24 Jan 2011 11:23:15 -0300 Subject: [media] radio-wl1273: remove unused wl1273_device->work wl1273_device->work is unused. Remove it along with the spurious flush_scheduled_work() call in wl1273_fm_module_exit(). Signed-off-by: Tejun Heo Cc: Matti Aaltonen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 9e177dc..46cacf8 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -67,7 +67,6 @@ struct wl1273_device { /* RDS */ unsigned int rds_on; - struct delayed_work work; wait_queue_head_t read_queue; struct mutex lock; /* for serializing fm radio operations */ @@ -967,9 +966,6 @@ static int wl1273_fm_rds_off(struct wl1273_device *radio) if (r) goto out; - /* stop rds reception */ - cancel_delayed_work(&radio->work); - /* Service pending read */ wake_up_interruptible(&radio->read_queue); @@ -2171,7 +2167,6 @@ module_init(wl1273_fm_module_init); static void __exit wl1273_fm_module_exit(void) { - flush_scheduled_work(); platform_driver_unregister(&wl1273_fm_radio_driver); pr_info(DRIVER_DESC ", Exiting.\n"); } -- cgit v0.10.2 From a07500ef690fcbec76e879ee2093d7ca69883825 Mon Sep 17 00:00:00 2001 From: Alexander Strakh Date: Tue, 25 Jan 2011 18:00:13 -0300 Subject: [media] drivers/media/video/tlg2300/pd-video.c: Remove second mutex_unlock in pd_vidioc_s_fmt Error path in file drivers/media/video/tlg2300/pd-video.c: 1. First mutex_unlock on &pd->lock in line 767 (in function that called from line 805) 2. Second in line 806 805 pd_vidioc_s_fmt(pd, &f->fmt.pix); 806 mutex_unlock(&pd->lock); Found by Linux Device Drivers Verification Project Signed-off-by: Alexander Strakh Acked-by: Huang Shijie Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index df33a1d..a794ae6 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -764,10 +764,8 @@ static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix) } ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, vid_resol, &cmd_status); - if (ret || cmd_status) { - mutex_unlock(&pd->lock); + if (ret || cmd_status) return -EBUSY; - } pix_def->pixelformat = pix->pixelformat; /* save it */ pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576; -- cgit v0.10.2 From 9d4540c9395f5f01cdd0cf60d230ec8bee165bb8 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 12 Feb 2011 22:35:08 -0300 Subject: [media] v180 - DM04/QQBOX added support for BS2F7HZ0194 versions Old versions of these boxes have the BS2F7HZ0194 tuner module on both the LME2510 and LME2510C. Firmware dvb-usb-lme2510-s0194.fw and/or dvb-usb-lme2510c-s0194.fw files are required. See Documentation/dvb/lmedm04.txt Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index 46ccd01..fa64b6c 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -2,7 +2,9 @@ * * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 * LME2510C + LG TDQY-P001F + * LME2510C + BS2F7HZ0194 * LME2510 + LG TDQY-P001F + * LME2510 + BS2F7HZ0194 * * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) @@ -12,20 +14,22 @@ * * MVB0001F (LME2510C+LGTDQT-P001F) * + * MV0194 (LME2510+SHARP:BS2F7HZ0194) + * SHARP:BS2F7HZ0194 = (STV0299+IX2410) + * + * MVB0194 (LME2510C+SHARP0194) + * * For firmware see Documentation/dvb/lmedm04.txt * * I2C addresses: * 0xd0 - STV0288 - Demodulator * 0xc0 - Sharp IX2505V - Tuner - * --or-- + * -- * 0x1c - TDA10086 - Demodulator * 0xc0 - TDA8263 - Tuner - * - * ***Please Note*** - * There are other variants of the DM04 - * ***NOT SUPPORTED*** - * MV0194 (LME2510+SHARP0194) - * MVB0194 (LME2510C+SHARP0194) + * -- + * 0xd0 - STV0299 - Demodulator + * 0xc0 - IX2410 - Tuner * * * VID = 3344 PID LME2510=1122 LME2510C=1120 @@ -55,6 +59,9 @@ * * QQbox suffers from noise on LNB voltage. * + * LME2510: SHARP:BS2F7HZ0194(MV0194) cannot cold reset and share system + * with other tuners. After a cold reset streaming will not start. + * * PID functions have been removed from this driver version due to * problems with different firmware and application versions. */ @@ -69,6 +76,9 @@ #include "tda10086.h" #include "stv0288.h" #include "ix2505v.h" +#include "stv0299.h" +#include "dvb-pll.h" +#include "z0194a.h" @@ -96,8 +106,11 @@ MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG"); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define TUNER_DEFAULT 0x0 #define TUNER_LG 0x1 #define TUNER_S7395 0x2 +#define TUNER_S0194 0x3 struct lme2510_state { u8 id; @@ -249,6 +262,7 @@ static void lme2510_int_response(struct urb *lme_urb) st->time_key = ibuf[7]; break; case TUNER_S7395: + case TUNER_S0194: /* Tweak for earlier firmware*/ if (ibuf[1] == 0x03) { if (ibuf[2] > 1) @@ -364,6 +378,18 @@ static int lme2510_msg(struct dvb_usb_device *d, msleep(5); } break; + case TUNER_S0194: + if (wbuf[2] == 0xd0) { + if (wbuf[3] == 0x1b) { + st->signal_lock = rbuf[1]; + if ((st->stream_on & 1) && + (st->signal_lock & 0x8)) { + lme2510_stream_restart(d); + st->i2c_talk_onoff = 0; + } + } + } + break; default: break; } @@ -423,6 +449,34 @@ static int lme2510_msg(struct dvb_usb_device *d, break; } break; + case TUNER_S0194: + switch (wbuf[3]) { + case 0x18: + rbuf[0] = 0x55; + rbuf[1] = (st->signal_level & 0x80) + ? 0 : (st->signal_level * 2); + break; + case 0x24: + rbuf[0] = 0x55; + rbuf[1] = st->signal_sn; + break; + case 0x1b: + rbuf[0] = 0x55; + rbuf[1] = st->signal_lock; + break; + case 0x19: + case 0x25: + case 0x1e: + case 0x1d: + rbuf[0] = 0x55; + rbuf[1] = 0x00; + break; + default: + lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 1; + break; + } + break; default: break; } @@ -517,17 +571,14 @@ static int lme2510_identify_state(struct usb_device *udev, struct dvb_usb_device_description **desc, int *cold) { - if (lme2510_return_status(udev) == 0x44) - *cold = 1; - else - *cold = 0; + *cold = 0; return 0; } 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_CLEAR_PID; static u8 rbuf[1]; int ret = 0, rlen = sizeof(rbuf); @@ -658,9 +709,6 @@ static int lme2510_download_firmware(struct usb_device *dev, return (ret < 0) ? -ENODEV : 0; } -/* Default firmware for LME2510C */ -char lme_firmware[50] = "dvb-usb-lme2510c-s7395.fw"; - static void lme_coldreset(struct usb_device *dev) { int ret = 0, len_in; @@ -678,49 +726,83 @@ static void lme_coldreset(struct usb_device *dev) static int lme_firmware_switch(struct usb_device *udev, int cold) { const struct firmware *fw = NULL; - char lme2510c_s7395[] = "dvb-usb-lme2510c-s7395.fw"; - char lme2510c_lg[] = "dvb-usb-lme2510c-lg.fw"; - char *firm_msg[] = {"Loading", "Switching to"}; - int ret; + 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_lg[] = "dvb-usb-lme2510-lg.fw"; + const char fw_s0194[] = "dvb-usb-lme2510-s0194.fw"; + const char *fw_lme; + int ret, cold_fw; cold = (cold > 0) ? (cold & 1) : 0; - if (udev->descriptor.idProduct == 0x1122) - return 0; + cold_fw = !cold; - switch (dvb_usb_lme2510_firmware) { - case 0: - default: - memcpy(&lme_firmware, lme2510c_s7395, sizeof(lme2510c_s7395)); - ret = request_firmware(&fw, lme_firmware, &udev->dev); - if (ret == 0) { - info("FRM %s S7395 Firmware", firm_msg[cold]); + if (udev->descriptor.idProduct == 0x1122) { + switch (dvb_usb_lme2510_firmware) { + default: + dvb_usb_lme2510_firmware = TUNER_S0194; + case TUNER_S0194: + fw_lme = fw_s0194; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) { + cold = 0;/*lme2510-s0194 cannot cold reset*/ + break; + } + dvb_usb_lme2510_firmware = TUNER_LG; + case TUNER_LG: + fw_lme = fw_lg; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) + break; + info("FRM No Firmware Found - please install"); + dvb_usb_lme2510_firmware = TUNER_DEFAULT; + cold = 0; + cold_fw = 0; break; } - if (cold == 0) - dvb_usb_lme2510_firmware = 1; - else + } else { + switch (dvb_usb_lme2510_firmware) { + default: + dvb_usb_lme2510_firmware = TUNER_S7395; + case TUNER_S7395: + fw_lme = fw_c_s7395; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) + break; + dvb_usb_lme2510_firmware = TUNER_LG; + case TUNER_LG: + fw_lme = fw_c_lg; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) + break; + dvb_usb_lme2510_firmware = TUNER_S0194; + case TUNER_S0194: + fw_lme = fw_c_s0194; + ret = request_firmware(&fw, fw_lme, &udev->dev); + if (ret == 0) + break; + info("FRM No Firmware Found - please install"); + dvb_usb_lme2510_firmware = TUNER_DEFAULT; cold = 0; - case 1: - memcpy(&lme_firmware, lme2510c_lg, sizeof(lme2510c_lg)); - ret = request_firmware(&fw, lme_firmware, &udev->dev); - if (ret == 0) { - info("FRM %s LG Firmware", firm_msg[cold]); + cold_fw = 0; break; } - info("FRM No Firmware Found - please install"); - dvb_usb_lme2510_firmware = 0; - cold = 0; - break; } - release_firmware(fw); + if (cold_fw) { + info("FRM Loading %s file", fw_lme); + ret = lme2510_download_firmware(udev, fw); + } if (cold) { + info("FRM Changing to %s firmware", fw_lme); lme_coldreset(udev); return -ENODEV; } + release_firmware(fw); + return ret; } @@ -758,6 +840,18 @@ static struct ix2505v_config lme_tuner = { .tuner_chargepump = 0x3, }; +static struct stv0299_config sharp_z0194_config = { + .demod_address = 0xd0, + .inittab = sharp_z0194a_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = sharp_z0194a_set_symbol_rate, +}; + static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { @@ -793,7 +887,8 @@ 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"}; + char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395", + " SHARP:BS2F7HZ0194"}; char *name = adap->fe->ops.info.name; strlcpy(name, desc, 128); @@ -820,26 +915,40 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) st->i2c_tuner_gate_r = 4; st->i2c_tuner_addr = 0xc0; st->tuner_config = TUNER_LG; - if (dvb_usb_lme2510_firmware != 1) { - dvb_usb_lme2510_firmware = 1; + if (dvb_usb_lme2510_firmware != TUNER_LG) { + dvb_usb_lme2510_firmware = TUNER_LG; ret = lme_firmware_switch(adap->dev->udev, 1); - } else /*stops LG/Sharp multi tuner problems*/ - dvb_usb_lme2510_firmware = 0; + } + goto end; + } + + st->i2c_gate = 4; + adap->fe = dvb_attach(stv0299_attach, &sharp_z0194_config, + &adap->dev->i2c_adap); + if (adap->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); + } goto end; } st->i2c_gate = 5; adap->fe = dvb_attach(stv0288_attach, &lme_config, &adap->dev->i2c_adap); - if (adap->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 != 0) { - dvb_usb_lme2510_firmware = 0; + if (dvb_usb_lme2510_firmware != TUNER_S7395) { + dvb_usb_lme2510_firmware = TUNER_S7395; ret = lme_firmware_switch(adap->dev->udev, 1); } } else { @@ -847,6 +956,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } + end: if (ret) { kfree(adap->fe); adap->fe = NULL; @@ -855,14 +965,13 @@ end: if (ret) { adap->fe->ops.set_voltage = dm04_lme2510_set_voltage; ret = lme_name(adap); - return ret; } static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) { struct lme2510_state *st = adap->dev->priv; - char *tun_msg[] = {"", "TDA8263", "IX2505V"}; + char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA"}; int ret = 0; switch (st->tuner_config) { @@ -876,6 +985,11 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) &adap->dev->i2c_adap)) ret = st->tuner_config; break; + case TUNER_S0194: + if (dvb_attach(dvb_pll_attach , adap->fe, 0xc0, + &adap->dev->i2c_adap, DVB_PLL_OPERA1)) + ret = st->tuner_config; + break; default: break; } @@ -936,7 +1050,10 @@ static int lme2510_probe(struct usb_interface *intf, return -ENODEV; } - lme_firmware_switch(udev, 0); + if (lme2510_return_status(udev) == 0x44) { + lme_firmware_switch(udev, 0); + return -ENODEV; + } if (0 == dvb_usb_device_init(intf, &lme2510_properties, THIS_MODULE, NULL, adapter_nr)) { @@ -964,10 +1081,6 @@ MODULE_DEVICE_TABLE(usb, lme2510_table); static struct dvb_usb_device_properties lme2510_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, - .usb_ctrl = DEVICE_SPECIFIC, - .download_firmware = lme2510_download_firmware, - .firmware = "dvb-usb-lme2510-lg.fw", - .size_of_priv = sizeof(struct lme2510_state), .num_adapters = 1, .adapter = { @@ -1004,9 +1117,6 @@ static struct dvb_usb_device_properties lme2510_properties = { static struct dvb_usb_device_properties lme2510c_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, - .usb_ctrl = DEVICE_SPECIFIC, - .download_firmware = lme2510_download_firmware, - .firmware = (const char *)&lme_firmware, .size_of_priv = sizeof(struct lme2510_state), .num_adapters = 1, .adapter = { @@ -1109,5 +1219,5 @@ module_exit(lme2510_module_exit); MODULE_AUTHOR("Malcolm Priestley "); MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); -MODULE_VERSION("1.75"); +MODULE_VERSION("1.80"); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 3286dac1ca34b85b069c409414a3e51a9217b159 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 12 Feb 2011 22:38:47 -0300 Subject: [media] DM04 LME2510(C) Sharp BS2F7HZ0194 Firmware Information DM04 LME2510(C) Sharp BS2F7HZ0194 Firmware Information Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/dvb/lmedm04.txt b/Documentation/dvb/lmedm04.txt index 6418865..10b5f04 100644 --- a/Documentation/dvb/lmedm04.txt +++ b/Documentation/dvb/lmedm04.txt @@ -4,7 +4,7 @@ following file(s) to this directory. for DM04+/QQBOX LME2510C (Sharp 7395 Tuner) ------------------------------------------- -The Sharp 7395 driver can be found in windows/system32/driver +The Sharp 7395 driver can be found in windows/system32/drivers US2A0D.sys (dated 17 Mar 2009) @@ -44,7 +44,7 @@ and run Other LG firmware can be extracted manually from US280D.sys -only found in windows/system32/driver. +only found in windows/system32/drivers dd if=US280D.sys ibs=1 skip=42360 count=3924 of=dvb-usb-lme2510-lg.fw @@ -55,4 +55,16 @@ dd if=US280D.sys ibs=1 skip=35200 count=3850 of=dvb-usb-lme2510c-lg.fw --------------------------------------------------------------------- +The Sharp 0194 tuner driver can be found in windows/system32/drivers + +US290D.sys (dated 09 Apr 2009) + +For LME2510 +dd if=US290D.sys ibs=1 skip=36856 count=3976 of=dvb-usb-lme2510-s0194.fw + + +For LME2510C +dd if=US290D.sys ibs=1 skip=33152 count=3697 of=dvb-usb-lme2510c-s0194.fw + + Copy the firmware file(s) to /lib/firmware -- cgit v0.10.2 From f8931f56f51a795e030318b95e4a0f8ac453e35e Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 23 Jan 2011 18:22:45 -0300 Subject: [media] DM04/QQBOX Update V1.76 - use 32 bit remote decoding Use 32 bit decoding to add support for more than one variant of remote control. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index fa64b6c..cd26e7c 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -204,7 +204,7 @@ static int lme2510_stream_restart(struct dvb_usb_device *d) rbuff, sizeof(rbuff)); return ret; } -static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u16 keypress) +static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u32 keypress) { struct dvb_usb_device *d = adap->dev; @@ -250,7 +250,8 @@ static void lme2510_int_response(struct urb *lme_urb) case 0xaa: debug_data_snipet(1, "INT Remote data snipet in", ibuf); lme2510_remote_keypress(adap, - (u16)(ibuf[4]<<8)+ibuf[5]); + (u32)(ibuf[2] << 24) + (ibuf[3] << 16) + + (ibuf[4] << 8) + ibuf[5]); break; case 0xbb: switch (st->tuner_config) { -- cgit v0.10.2 From 4a1cba5151ccddf4a7071fe4950fb699e8b23bf9 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 23 Jan 2011 18:26:03 -0300 Subject: [media] Change to 32 bit and add other remote controls for lme2510 These bubble button remote controls appear to be generic from China. These are the three variants known to be supplied with DM04/QQBOX DVB-S They could well be supplied with other devices from the region. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/keymaps/rc-lme2510.c b/drivers/media/rc/keymaps/rc-lme2510.c index 875cd81..3c19139 100644 --- a/drivers/media/rc/keymaps/rc-lme2510.c +++ b/drivers/media/rc/keymaps/rc-lme2510.c @@ -13,33 +13,75 @@ static struct rc_map_table lme2510_rc[] = { - { 0xba45, KEY_0 }, - { 0xa05f, KEY_1 }, - { 0xaf50, KEY_2 }, - { 0xa25d, KEY_3 }, - { 0xbe41, KEY_4 }, - { 0xf50a, KEY_5 }, - { 0xbd42, KEY_6 }, - { 0xb847, KEY_7 }, - { 0xb649, KEY_8 }, - { 0xfa05, KEY_9 }, - { 0xbc43, KEY_POWER }, - { 0xb946, KEY_SUBTITLE }, - { 0xf906, KEY_PAUSE }, - { 0xfc03, KEY_MEDIA_REPEAT}, - { 0xfd02, KEY_PAUSE }, - { 0xa15e, KEY_VOLUMEUP }, - { 0xa35c, KEY_VOLUMEDOWN }, - { 0xf609, KEY_CHANNELUP }, - { 0xe51a, KEY_CHANNELDOWN }, - { 0xe11e, KEY_PLAY }, - { 0xe41b, KEY_ZOOM }, - { 0xa659, KEY_MUTE }, - { 0xa55a, KEY_TV }, - { 0xe718, KEY_RECORD }, - { 0xf807, KEY_EPG }, - { 0xfe01, KEY_STOP }, - + /* Type 1 - 26 buttons */ + { 0xef12ba45, KEY_0 }, + { 0xef12a05f, KEY_1 }, + { 0xef12af50, KEY_2 }, + { 0xef12a25d, KEY_3 }, + { 0xef12be41, KEY_4 }, + { 0xef12f50a, KEY_5 }, + { 0xef12bd42, KEY_6 }, + { 0xef12b847, KEY_7 }, + { 0xef12b649, KEY_8 }, + { 0xef12fa05, KEY_9 }, + { 0xef12bc43, KEY_POWER }, + { 0xef12b946, KEY_SUBTITLE }, + { 0xef12f906, KEY_PAUSE }, + { 0xef12fc03, KEY_MEDIA_REPEAT}, + { 0xef12fd02, KEY_PAUSE }, + { 0xef12a15e, KEY_VOLUMEUP }, + { 0xef12a35c, KEY_VOLUMEDOWN }, + { 0xef12f609, KEY_CHANNELUP }, + { 0xef12e51a, KEY_CHANNELDOWN }, + { 0xef12e11e, KEY_PLAY }, + { 0xef12e41b, KEY_ZOOM }, + { 0xef12a659, KEY_MUTE }, + { 0xef12a55a, KEY_TV }, + { 0xef12e718, KEY_RECORD }, + { 0xef12f807, KEY_EPG }, + { 0xef12fe01, KEY_STOP }, + /* Type 2 - 20 buttons */ + { 0xff40ea15, KEY_0 }, + { 0xff40f708, KEY_1 }, + { 0xff40f609, KEY_2 }, + { 0xff40f50a, KEY_3 }, + { 0xff40f30c, KEY_4 }, + { 0xff40f20d, KEY_5 }, + { 0xff40f10e, KEY_6 }, + { 0xff40ef10, KEY_7 }, + { 0xff40ee11, KEY_8 }, + { 0xff40ed12, KEY_9 }, + { 0xff40ff00, KEY_POWER }, + { 0xff40fb04, KEY_MEDIA_REPEAT}, /* Recall */ + { 0xff40e51a, KEY_PAUSE }, /* Timeshift */ + { 0xff40fd02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0xff40f906, KEY_VOLUMEDOWN }, /* Volumne defined as right hand*/ + { 0xff40fe01, KEY_CHANNELUP }, + { 0xff40fa05, KEY_CHANNELDOWN }, + { 0xff40eb14, KEY_ZOOM }, + { 0xff40e718, KEY_RECORD }, + { 0xff40e916, KEY_STOP }, + /* Type 3 - 20 buttons */ + { 0xff00e31c, KEY_0 }, + { 0xff00f807, KEY_1 }, + { 0xff00ea15, KEY_2 }, + { 0xff00f609, KEY_3 }, + { 0xff00e916, KEY_4 }, + { 0xff00e619, KEY_5 }, + { 0xff00f20d, KEY_6 }, + { 0xff00f30c, KEY_7 }, + { 0xff00e718, KEY_8 }, + { 0xff00a15e, KEY_9 }, + { 0xff00ba45, KEY_POWER }, + { 0xff00bb44, KEY_MEDIA_REPEAT}, /* Recall */ + { 0xff00b54a, KEY_PAUSE }, /* Timeshift */ + { 0xff00b847, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0xff00bc43, KEY_VOLUMEDOWN }, /* Volumne defined as right hand*/ + { 0xff00b946, KEY_CHANNELUP }, + { 0xff00bf40, KEY_CHANNELDOWN }, + { 0xff00f708, KEY_ZOOM }, + { 0xff00bd42, KEY_RECORD }, + { 0xff00a55a, KEY_STOP }, }; static struct rc_map_list lme2510_map = { -- cgit v0.10.2 From c6384c886dfed806f07ae2f8ba4d3740b57cde9c Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 3 Mar 2011 16:03:31 -0300 Subject: [media] via-camera: Fix OLPC serial check The code that checks the OLPC serial port is never built at the moment, because CONFIG_OLPC_XO_1_5 doesn't exist and probably won't be added. Fix it so that it gets compiled in, only executes on OLPC laptops, and move the check into the probe routine. The compiler is smart enough to eliminate this code when CONFIG_OLPC=n (due to machine_is_olpc() always returning false). Signed-off-by: Daniel Drake Acked-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c index 3f0146f..8c780c2 100644 --- a/drivers/media/video/via-camera.c +++ b/drivers/media/video/via-camera.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "via-camera.h" @@ -38,14 +39,12 @@ MODULE_PARM_DESC(flip_image, "If set, the sensor will be instructed to flip the image " "vertically."); -#ifdef CONFIG_OLPC_XO_1_5 static int override_serial; module_param(override_serial, bool, 0444); MODULE_PARM_DESC(override_serial, "The camera driver will normally refuse to load if " "the XO 1.5 serial port is enabled. Set this option " - "to force the issue."); -#endif + "to force-enable the camera."); /* * Basic window sizes. @@ -1317,6 +1316,37 @@ static struct video_device viacam_v4l_template = { .release = video_device_release_empty, /* Check this */ }; +/* + * The OLPC folks put the serial port on the same pin as + * the camera. They also get grumpy if we break the + * serial port and keep them from using it. So we have + * to check the serial enable bit and not step on it. + */ +#define VIACAM_SERIAL_DEVFN 0x88 +#define VIACAM_SERIAL_CREG 0x46 +#define VIACAM_SERIAL_BIT 0x40 + +static __devinit bool viacam_serial_is_enabled(void) +{ + struct pci_bus *pbus = pci_find_bus(0, 0); + u8 cbyte; + + pci_bus_read_config_byte(pbus, VIACAM_SERIAL_DEVFN, + VIACAM_SERIAL_CREG, &cbyte); + if ((cbyte & VIACAM_SERIAL_BIT) == 0) + return false; /* Not enabled */ + if (override_serial == 0) { + printk(KERN_NOTICE "Via camera: serial port is enabled, " \ + "refusing to load.\n"); + printk(KERN_NOTICE "Specify override_serial=1 to force " \ + "module loading.\n"); + return true; + } + printk(KERN_NOTICE "Via camera: overriding serial port\n"); + pci_bus_write_config_byte(pbus, VIACAM_SERIAL_DEVFN, + VIACAM_SERIAL_CREG, cbyte & ~VIACAM_SERIAL_BIT); + return false; +} static __devinit int viacam_probe(struct platform_device *pdev) { @@ -1348,6 +1378,10 @@ static __devinit int viacam_probe(struct platform_device *pdev) printk(KERN_ERR "viacam: No I/O memory, so no pictures\n"); return -ENOMEM; } + + if (machine_is_olpc() && viacam_serial_is_enabled()) + return -EBUSY; + /* * Basic structure initialization. */ @@ -1459,7 +1493,6 @@ static __devexit int viacam_remove(struct platform_device *pdev) return 0; } - static struct platform_driver viacam_driver = { .driver = { .name = "viafb-camera", @@ -1468,50 +1501,8 @@ static struct platform_driver viacam_driver = { .remove = viacam_remove, }; - -#ifdef CONFIG_OLPC_XO_1_5 -/* - * The OLPC folks put the serial port on the same pin as - * the camera. They also get grumpy if we break the - * serial port and keep them from using it. So we have - * to check the serial enable bit and not step on it. - */ -#define VIACAM_SERIAL_DEVFN 0x88 -#define VIACAM_SERIAL_CREG 0x46 -#define VIACAM_SERIAL_BIT 0x40 - -static __devinit int viacam_check_serial_port(void) -{ - struct pci_bus *pbus = pci_find_bus(0, 0); - u8 cbyte; - - pci_bus_read_config_byte(pbus, VIACAM_SERIAL_DEVFN, - VIACAM_SERIAL_CREG, &cbyte); - if ((cbyte & VIACAM_SERIAL_BIT) == 0) - return 0; /* Not enabled */ - if (override_serial == 0) { - printk(KERN_NOTICE "Via camera: serial port is enabled, " \ - "refusing to load.\n"); - printk(KERN_NOTICE "Specify override_serial=1 to force " \ - "module loading.\n"); - return -EBUSY; - } - printk(KERN_NOTICE "Via camera: overriding serial port\n"); - pci_bus_write_config_byte(pbus, VIACAM_SERIAL_DEVFN, - VIACAM_SERIAL_CREG, cbyte & ~VIACAM_SERIAL_BIT); - return 0; -} -#endif - - - - static int viacam_init(void) { -#ifdef CONFIG_OLPC_XO_1_5 - if (viacam_check_serial_port()) - return -EBUSY; -#endif return platform_driver_register(&viacam_driver); } module_init(viacam_init); -- cgit v0.10.2 From bf512b2d9c39af31507f485b15b9b40e2a5c499c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 6 Mar 2011 10:40:11 -0300 Subject: [media] stv0367: signedness bug in stv0367_get_tuner_freq() We use err to store negative error codes so it should be signed. And if we return an error from stv0367_get_tuner_freq() that needs to be handled properly as well. (param->frequency is a u32). Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c index 120fed8..0ddcf7e 100644 --- a/drivers/media/dvb/frontends/stv0367.c +++ b/drivers/media/dvb/frontends/stv0367.c @@ -913,7 +913,7 @@ static u32 stv0367_get_tuner_freq(struct dvb_frontend *fe) struct dvb_frontend_ops *frontend_ops = NULL; struct dvb_tuner_ops *tuner_ops = NULL; u32 freq = 0; - u32 err = 0; + int err = 0; dprintk("%s:\n", __func__); @@ -1940,7 +1940,7 @@ static int stv0367ter_get_frontend(struct dvb_frontend *fe, int constell = 0,/* snr = 0,*/ Data = 0; param->frequency = stv0367_get_tuner_freq(fe); - if (param->frequency < 0) + if ((int)param->frequency < 0) param->frequency = c->frequency; constell = stv0367_readbits(state, F367TER_TPS_CONST); -- cgit v0.10.2 From bc4b18c6f536de3733e247b07adcc9ea3f5684dd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 6 Mar 2011 10:41:23 -0300 Subject: [media] stv0367: typo in function parameter The CellsCoeffs arrays are [3][6][5] not [2][6][5]. Signed-off-by: Dan Carpenter Cc: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c index 0ddcf7e..4e0e6a8 100644 --- a/drivers/media/dvb/frontends/stv0367.c +++ b/drivers/media/dvb/frontends/stv0367.c @@ -1046,7 +1046,7 @@ static u32 stv0367ter_get_mclk(struct stv0367_state *state, u32 ExtClk_Hz) } static int stv0367ter_filt_coeff_init(struct stv0367_state *state, - u16 CellsCoeffs[2][6][5], u32 DemodXtal) + u16 CellsCoeffs[3][6][5], u32 DemodXtal) { int i, j, k, freq; -- cgit v0.10.2 From 8c1476ffc0b820f6ca8cc0b3f50c8a0f57e8d82d Mon Sep 17 00:00:00 2001 From: Vadim Solomin Date: Sun, 6 Mar 2011 13:00:38 -0300 Subject: [media] saa7134-input: key up events not sent after suspend/resume On my AverMedia AverTV Studio 507, key up events are no longer sent after a suspend-to-disk/resume cycle, resulting in "stuck" keys. Apparently, for key up events to be generated, a certain GPIO pin must be set. Currently it's set in saa7134_input_init1(), but that function is not called on device resume. I suggest that code be moved to __saa7134_ir_start(), which is called both on init and resume. Signed-off-by: Vadim Solomin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index c9eff03..790d667 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -414,6 +414,41 @@ static int __saa7134_ir_start(void *priv) if (ir->running) return 0; + /* Moved here from saa7134_input_init1() because the latter + * is not called on device resume */ + switch (dev->board) { + case SAA7134_BOARD_MD2819: + case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: + case SAA7134_BOARD_AVERMEDIA_305: + case SAA7134_BOARD_AVERMEDIA_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_505: + case SAA7134_BOARD_AVERMEDIA_STUDIO_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM: + case SAA7134_BOARD_AVERMEDIA_M102: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE0, 0x4); + saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); + break; + case SAA7134_BOARD_AVERMEDIA_777: + case SAA7134_BOARD_AVERMEDIA_A16AR: + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE1, 0x1); + saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE1, 0x1); + saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + break; + case SAA7134_BOARD_GOTVIEW_7135: + saa_setb(SAA7134_GPIO_GPMODE1, 0x80); + break; + } + ir->running = true; ir->active = false; @@ -548,9 +583,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x0007C8; mask_keydown = 0x000010; polling = 50; // ms - /* Set GPIO pin2 to high to enable the IR controller */ - saa_setb(SAA7134_GPIO_GPMODE0, 0x4); - saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); + /* GPIO stuff moved to __saa7134_ir_start() */ break; case SAA7134_BOARD_AVERMEDIA_M135A: ir_codes = RC_MAP_AVERMEDIA_M135A; @@ -572,18 +605,14 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x02F200; mask_keydown = 0x000400; polling = 50; // ms - /* Without this we won't receive key up events */ - saa_setb(SAA7134_GPIO_GPMODE1, 0x1); - saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + /* GPIO stuff moved to __saa7134_ir_start() */ break; case SAA7134_BOARD_AVERMEDIA_A16D: ir_codes = RC_MAP_AVERMEDIA_A16D; mask_keycode = 0x02F200; mask_keydown = 0x000400; polling = 50; /* ms */ - /* Without this we won't receive key up events */ - saa_setb(SAA7134_GPIO_GPMODE1, 0x1); - saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + /* GPIO stuff moved to __saa7134_ir_start() */ break; case SAA7134_BOARD_KWORLD_TERMINATOR: ir_codes = RC_MAP_PIXELVIEW; @@ -635,7 +664,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x0003CC; mask_keydown = 0x000010; polling = 5; /* ms */ - saa_setb(SAA7134_GPIO_GPMODE1, 0x80); + /* GPIO stuff moved to __saa7134_ir_start() */ break; case SAA7134_BOARD_VIDEOMATE_TV_PVR: case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: -- cgit v0.10.2 From 022654930891c7ddfdb1ea34d6c4af9d1096bf91 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 29 Dec 2010 10:05:02 -0300 Subject: [media] v4l2_prio: move from v4l2-common to v4l2-dev We are going to move priority handling into the v4l2 core. As a consequence the v4l2_prio helper functions need to be moved into the core videodev module as well to prevent circular dependencies. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 940b5db..06b9f9f 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -80,69 +80,6 @@ MODULE_LICENSE("GPL"); * Video Standard Operations (contributed by Michael Schimek) */ - -/* ----------------------------------------------------------------- */ -/* priority handling */ - -#define V4L2_PRIO_VALID(val) (val == V4L2_PRIORITY_BACKGROUND || \ - val == V4L2_PRIORITY_INTERACTIVE || \ - val == V4L2_PRIORITY_RECORD) - -void v4l2_prio_init(struct v4l2_prio_state *global) -{ - memset(global, 0, sizeof(*global)); -} -EXPORT_SYMBOL(v4l2_prio_init); - -int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, - enum v4l2_priority new) -{ - if (!V4L2_PRIO_VALID(new)) - return -EINVAL; - if (*local == new) - return 0; - - atomic_inc(&global->prios[new]); - if (V4L2_PRIO_VALID(*local)) - atomic_dec(&global->prios[*local]); - *local = new; - return 0; -} -EXPORT_SYMBOL(v4l2_prio_change); - -void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local) -{ - v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT); -} -EXPORT_SYMBOL(v4l2_prio_open); - -void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local) -{ - if (V4L2_PRIO_VALID(local)) - atomic_dec(&global->prios[local]); -} -EXPORT_SYMBOL(v4l2_prio_close); - -enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global) -{ - if (atomic_read(&global->prios[V4L2_PRIORITY_RECORD]) > 0) - return V4L2_PRIORITY_RECORD; - if (atomic_read(&global->prios[V4L2_PRIORITY_INTERACTIVE]) > 0) - return V4L2_PRIORITY_INTERACTIVE; - if (atomic_read(&global->prios[V4L2_PRIORITY_BACKGROUND]) > 0) - return V4L2_PRIORITY_BACKGROUND; - return V4L2_PRIORITY_UNSET; -} -EXPORT_SYMBOL(v4l2_prio_max); - -int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local) -{ - return (local < v4l2_prio_max(global)) ? -EBUSY : 0; -} -EXPORT_SYMBOL(v4l2_prio_check); - -/* ----------------------------------------------------------------- */ - /* Helper functions for control handling */ /* Check for correctness of the ctrl's value based on the data from diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index e405b80..65d546f 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -182,6 +182,70 @@ struct video_device *video_devdata(struct file *file) } EXPORT_SYMBOL(video_devdata); + +/* Priority handling */ + +static inline bool prio_is_valid(enum v4l2_priority prio) +{ + return prio == V4L2_PRIORITY_BACKGROUND || + prio == V4L2_PRIORITY_INTERACTIVE || + prio == V4L2_PRIORITY_RECORD; +} + +void v4l2_prio_init(struct v4l2_prio_state *global) +{ + memset(global, 0, sizeof(*global)); +} +EXPORT_SYMBOL(v4l2_prio_init); + +int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, + enum v4l2_priority new) +{ + if (!prio_is_valid(new)) + return -EINVAL; + if (*local == new) + return 0; + + atomic_inc(&global->prios[new]); + if (prio_is_valid(*local)) + atomic_dec(&global->prios[*local]); + *local = new; + return 0; +} +EXPORT_SYMBOL(v4l2_prio_change); + +void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local) +{ + v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT); +} +EXPORT_SYMBOL(v4l2_prio_open); + +void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local) +{ + if (prio_is_valid(local)) + atomic_dec(&global->prios[local]); +} +EXPORT_SYMBOL(v4l2_prio_close); + +enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global) +{ + if (atomic_read(&global->prios[V4L2_PRIORITY_RECORD]) > 0) + return V4L2_PRIORITY_RECORD; + if (atomic_read(&global->prios[V4L2_PRIORITY_INTERACTIVE]) > 0) + return V4L2_PRIORITY_INTERACTIVE; + if (atomic_read(&global->prios[V4L2_PRIORITY_BACKGROUND]) > 0) + return V4L2_PRIORITY_BACKGROUND; + return V4L2_PRIORITY_UNSET; +} +EXPORT_SYMBOL(v4l2_prio_max); + +int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local) +{ + return (local < v4l2_prio_max(global)) ? -EBUSY : 0; +} +EXPORT_SYMBOL(v4l2_prio_check); + + static ssize_t v4l2_read(struct file *filp, char __user *buf, size_t sz, loff_t *off) { diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index a659319..a298ec4 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -80,21 +80,6 @@ /* ------------------------------------------------------------------------- */ -/* Priority helper functions */ - -struct v4l2_prio_state { - atomic_t prios[4]; -}; -void v4l2_prio_init(struct v4l2_prio_state *global); -int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, - enum v4l2_priority new); -void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local); -void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local); -enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global); -int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local); - -/* ------------------------------------------------------------------------- */ - /* Control helper functions */ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 51b2c51..9193703 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -37,6 +37,21 @@ struct v4l2_ctrl_handler; #define V4L2_FL_REGISTERED (0) #define V4L2_FL_USES_V4L2_FH (1) +/* Priority helper functions */ + +struct v4l2_prio_state { + atomic_t prios[4]; +}; + +void v4l2_prio_init(struct v4l2_prio_state *global); +int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, + enum v4l2_priority new); +void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local); +void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local); +enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global); +int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local); + + struct v4l2_file_operations { struct module *owner; ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); -- cgit v0.10.2 From 0f62fd6a2fa12d6a63cbb18f9e30b05345f636f6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 24 Feb 2011 10:42:24 -0300 Subject: [media] v4l2: add v4l2_prio_state to v4l2_device and video_device Integrate the v4l2_prio_state into the core, ready for use. One struct v4l2_prio_state is added to v4l2_device and a pointer to a prio state is added to video_device. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 65d546f..6b1ef85 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -573,6 +573,12 @@ int __video_register_device(struct video_device *vdev, int type, int nr, vdev->parent = vdev->v4l2_dev->dev; if (vdev->ctrl_handler == NULL) vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; + /* If the prio state pointer is NULL, and if the driver doesn't + handle priorities itself, then use the v4l2_device prio + state. */ + if (vdev->prio == NULL && vdev->ioctl_ops && + vdev->ioctl_ops->vidioc_s_priority == NULL) + vdev->prio = &vdev->v4l2_dev->prio; } /* Part 2: find a free minor, device node number and device index. */ diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index a1afda3..fd7a445 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -36,6 +36,7 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) INIT_LIST_HEAD(&v4l2_dev->subdevs); spin_lock_init(&v4l2_dev->lock); mutex_init(&v4l2_dev->ioctl_lock); + v4l2_prio_init(&v4l2_dev->prio); v4l2_dev->dev = dev; if (dev == NULL) { /* If dev == NULL, then name must be filled in by the caller */ diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 9193703..3700127 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -89,6 +89,9 @@ struct video_device /* Control handler associated with this device node. May be NULL. */ struct v4l2_ctrl_handler *ctrl_handler; + /* Priority state. If NULL, then v4l2_dev->prio will be used. */ + struct v4l2_prio_state *prio; + /* device info */ char name[32]; int vfl_type; diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 0c2bd30..3b72328 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -23,6 +23,7 @@ #include #include +#include /* Each instance of a V4L2 device should create the v4l2_device struct, either stand-alone or embedded in a larger struct. @@ -55,6 +56,8 @@ struct v4l2_device { unsigned int notification, void *arg); /* The control handler. May be NULL. */ struct v4l2_ctrl_handler *ctrl_handler; + /* Device's priority state */ + struct v4l2_prio_state prio; /* BKL replacement mutex. Temporary solution only. */ struct mutex ioctl_lock; }; -- cgit v0.10.2 From fc5602be7ca5b55174c5d6595089718779b28dfa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 29 Dec 2010 13:36:50 -0300 Subject: [media] v4l2-fh: implement v4l2_priority support Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c index d78f184..78a1608 100644 --- a/drivers/media/video/v4l2-fh.c +++ b/drivers/media/video/v4l2-fh.c @@ -33,6 +33,8 @@ int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) fh->vdev = vdev; INIT_LIST_HEAD(&fh->list); set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags); + fh->prio = V4L2_PRIORITY_UNSET; + BUG_ON(vdev->prio == NULL); /* * fh->events only needs to be initialized if the driver @@ -51,6 +53,7 @@ void v4l2_fh_add(struct v4l2_fh *fh) { unsigned long flags; + v4l2_prio_open(fh->vdev->prio, &fh->prio); spin_lock_irqsave(&fh->vdev->fh_lock, flags); list_add(&fh->list, &fh->vdev->fh_list); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); @@ -64,6 +67,7 @@ void v4l2_fh_del(struct v4l2_fh *fh) spin_lock_irqsave(&fh->vdev->fh_lock, flags); list_del_init(&fh->list); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + v4l2_prio_close(fh->vdev->prio, fh->prio); } EXPORT_SYMBOL_GPL(v4l2_fh_del); diff --git a/include/media/v4l2-fh.h b/include/media/v4l2-fh.h index 1d72dde..5fc5ba9 100644 --- a/include/media/v4l2-fh.h +++ b/include/media/v4l2-fh.h @@ -35,6 +35,7 @@ struct v4l2_fh { struct list_head list; struct video_device *vdev; struct v4l2_events *events; /* events, pending and subscribed */ + enum v4l2_priority prio; }; /* -- cgit v0.10.2 From 73cb42068cff419e72456940c713ceb5efa68c2a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Jan 2011 09:36:04 -0300 Subject: [media] v4l2-fh: add v4l2_fh_open and v4l2_fh_release helper functions Add two new functions: v4l2_fh_open allocates and initializes a struct v4l2_fh based on a struct file pointer and v4l2_fh_release releases and frees a struct v4l2_fh. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c index 78a1608..27242e5 100644 --- a/drivers/media/video/v4l2-fh.c +++ b/drivers/media/video/v4l2-fh.c @@ -23,6 +23,7 @@ */ #include +#include #include #include #include @@ -60,6 +61,20 @@ void v4l2_fh_add(struct v4l2_fh *fh) } EXPORT_SYMBOL_GPL(v4l2_fh_add); +int v4l2_fh_open(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL); + + filp->private_data = fh; + if (fh == NULL) + return -ENOMEM; + v4l2_fh_init(fh, vdev); + v4l2_fh_add(fh); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_fh_open); + void v4l2_fh_del(struct v4l2_fh *fh) { unsigned long flags; @@ -81,3 +96,16 @@ void v4l2_fh_exit(struct v4l2_fh *fh) v4l2_event_free(fh); } EXPORT_SYMBOL_GPL(v4l2_fh_exit); + +int v4l2_fh_release(struct file *filp) +{ + struct v4l2_fh *fh = filp->private_data; + + if (fh) { + v4l2_fh_del(fh); + v4l2_fh_exit(fh); + kfree(fh); + } + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_fh_release); diff --git a/include/media/v4l2-fh.h b/include/media/v4l2-fh.h index 5fc5ba9..5657881 100644 --- a/include/media/v4l2-fh.h +++ b/include/media/v4l2-fh.h @@ -51,8 +51,16 @@ int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev); */ void v4l2_fh_add(struct v4l2_fh *fh); /* + * Can be used as the open() op of v4l2_file_operations. + * It allocates a v4l2_fh and inits and adds it to the video_device associated + * with the file pointer. + */ +int v4l2_fh_open(struct file *filp); +/* * Remove file handle from the list of file handles. Must be called in * v4l2_file_operations->release() handler if the driver uses v4l2_fh. + * On error filp->private_data will be NULL, otherwise it will point to + * the v4l2_fh struct. */ void v4l2_fh_del(struct v4l2_fh *fh); /* @@ -62,5 +70,12 @@ void v4l2_fh_del(struct v4l2_fh *fh); * driver uses v4l2_fh. */ void v4l2_fh_exit(struct v4l2_fh *fh); +/* + * Can be used as the release() op of v4l2_file_operations. + * It deletes and exits the v4l2_fh associated with the file pointer and + * frees it. It will do nothing if filp->private_data (the pointer to the + * v4l2_fh struct) is NULL. This function always returns 0. + */ +int v4l2_fh_release(struct file *filp); #endif /* V4L2_EVENT_H */ -- cgit v0.10.2 From dfddb2441f39e8c0254504516be35b854addf6fa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Jan 2011 09:38:02 -0300 Subject: [media] v4l2-fh: add v4l2_fh_is_singular Several drivers need to do something when the first filehandle is opened or the last filehandle is closed. Most implement some use count mechanism, but if they use v4l2_fh, then you can also just check if this is the only filehandle for the device node. A simple helper function can do this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c index 27242e5..543b3fe 100644 --- a/drivers/media/video/v4l2-fh.c +++ b/drivers/media/video/v4l2-fh.c @@ -109,3 +109,17 @@ int v4l2_fh_release(struct file *filp) return 0; } EXPORT_SYMBOL_GPL(v4l2_fh_release); + +int v4l2_fh_is_singular(struct v4l2_fh *fh) +{ + unsigned long flags; + int is_singular; + + if (fh == NULL || fh->vdev == NULL) + return 0; + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + is_singular = list_is_singular(&fh->list); + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + return is_singular; +} +EXPORT_SYMBOL_GPL(v4l2_fh_is_singular); diff --git a/include/media/v4l2-fh.h b/include/media/v4l2-fh.h index 5657881..0206aa5 100644 --- a/include/media/v4l2-fh.h +++ b/include/media/v4l2-fh.h @@ -77,5 +77,18 @@ void v4l2_fh_exit(struct v4l2_fh *fh); * v4l2_fh struct) is NULL. This function always returns 0. */ int v4l2_fh_release(struct file *filp); +/* + * Returns 1 if this filehandle is the only filehandle opened for the + * associated video_device. If fh is NULL, then it returns 0. + */ +int v4l2_fh_is_singular(struct v4l2_fh *fh); +/* + * Helper function with struct file as argument. If filp->private_data is + * NULL, then it will return 0. + */ +static inline int v4l2_fh_is_singular_file(struct file *filp) +{ + return v4l2_fh_is_singular(filp->private_data); +} #endif /* V4L2_EVENT_H */ -- cgit v0.10.2 From 99cd47bc733436da282016e629eef6baa0f6047c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Mar 2011 19:00:56 -0300 Subject: [media] v4l2-ioctl: add priority handling support Drivers that use v4l2_fh can now use the core framework support of g/s_priority. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c index 726d367..444b4cf 100644 --- a/drivers/media/radio/radio-si4713.c +++ b/drivers/media/radio/radio-si4713.c @@ -224,7 +224,8 @@ static int radio_si4713_s_frequency(struct file *file, void *p, s_frequency, vf); } -static long radio_si4713_default(struct file *file, void *p, int cmd, void *arg) +static long radio_si4713_default(struct file *file, void *p, + bool valid_prio, int cmd, void *arg) { return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core, ioctl, cmd, arg); diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 363fe09..5111bbc 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -395,7 +395,8 @@ static int sync(struct camera_data *cam, int frame_nr) * *****************************************************************************/ -static long cpia2_default(struct file *file, void *fh, int cmd, void *arg) +static long cpia2_default(struct file *file, void *fh, bool valid_prio, + int cmd, void *arg) { struct camera_data *cam = video_drvdata(file); __u32 gpio_val; diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 36b018c..08ed75d 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -1057,7 +1057,8 @@ static int cx18_log_status(struct file *file, void *fh) return 0; } -static long cx18_default(struct file *file, void *fh, int cmd, void *arg) +static long cx18_default(struct file *file, void *fh, bool valid_prio, + int cmd, void *arg) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 353eada..71e961e 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -1719,7 +1719,7 @@ unlock_out: static long vpfe_param_handler(struct file *file, void *priv, - int cmd, void *param) + bool valid_prio, int cmd, void *param) { struct vpfe_device *vpfe_dev = video_drvdata(file); int ret = 0; diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index b686da5..d34a326 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1795,7 +1795,8 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) return 0; } -static long ivtv_default(struct file *file, void *fh, int cmd, void *arg) +static long ivtv_default(struct file *file, void *fh, bool valid_prio, + int cmd, void *arg) { struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 48d2c24..b09a3c8 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1547,7 +1547,8 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) return 0; } -static long vidioc_default(struct file *file, void *fh, int cmd, void *arg) +static long vidioc_default(struct file *file, void *fh, bool valid_prio, + int cmd, void *arg) { switch (cmd) { case MEYEIOC_G_PARAMS: diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index e8846a0..0b38500 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -643,7 +643,8 @@ static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_regist } #endif -static long vidioc_default(struct file *file, void *fh, int cmd, void *arg) +static long vidioc_default(struct file *file, void *fh, bool valid_prio, + int cmd, void *arg) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 68a5313..aa87e46 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -860,7 +860,8 @@ static int pwc_enum_frameintervals(struct file *file, void *fh, return 0; } -static long pwc_default(struct file *file, void *fh, int cmd, void *arg) +static long pwc_default(struct file *file, void *fh, bool valid_prio, + int cmd, void *arg) { struct pwc_device *pdev = video_drvdata(file); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index db6ec38..3e6b6fa 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #define dbgarg(cmd, fmt, arg...) \ @@ -538,6 +539,7 @@ static long __video_do_ioctl(struct file *file, struct video_device *vfd = video_devdata(file); const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; void *fh = file->private_data; + struct v4l2_fh *vfh = NULL; struct v4l2_format f_copy; long ret = -EINVAL; @@ -553,6 +555,43 @@ static long __video_do_ioctl(struct file *file, printk(KERN_CONT "\n"); } + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) + vfh = file->private_data; + + if (vfh && !ops->vidioc_s_priority) { + switch (cmd) { + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + case VIDIOC_S_FMT: + case VIDIOC_S_CROP: + case VIDIOC_S_AUDIO: + case VIDIOC_S_AUDOUT: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_S_FBUF: + case VIDIOC_S_PRIORITY: + case VIDIOC_S_DV_PRESET: + case VIDIOC_S_DV_TIMINGS: + case VIDIOC_S_JPEGCOMP: + case VIDIOC_S_MODULATOR: + case VIDIOC_S_PARM: + case VIDIOC_S_HW_FREQ_SEEK: + case VIDIOC_ENCODER_CMD: + case VIDIOC_OVERLAY: + case VIDIOC_REQBUFS: + case VIDIOC_STREAMON: + case VIDIOC_STREAMOFF: + ret = v4l2_prio_check(vfd->prio, vfh->prio); + if (ret) + goto exit_prio; + ret = -EINVAL; + break; + } + } + switch (cmd) { /* --- capabilities ------------------------------------------ */ @@ -579,9 +618,12 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_priority *p = arg; - if (!ops->vidioc_g_priority) - break; - ret = ops->vidioc_g_priority(file, fh, p); + if (ops->vidioc_g_priority) { + ret = ops->vidioc_g_priority(file, fh, p); + } else if (vfh) { + *p = v4l2_prio_max(&vfd->v4l2_dev->prio); + ret = 0; + } if (!ret) dbgarg(cmd, "priority is %d\n", *p); break; @@ -590,10 +632,13 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_priority *p = arg; - if (!ops->vidioc_s_priority) - break; + if (!ops->vidioc_s_priority && vfh == NULL) + break; dbgarg(cmd, "setting priority to %d\n", *p); - ret = ops->vidioc_s_priority(file, fh, *p); + if (ops->vidioc_s_priority) + ret = ops->vidioc_s_priority(file, fh, *p); + else + ret = v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p); break; } @@ -2138,13 +2183,18 @@ static long __video_do_ioctl(struct file *file, } default: { + bool valid_prio = true; + if (!ops->vidioc_default) break; - ret = ops->vidioc_default(file, fh, cmd, arg); + if (vfh && !ops->vidioc_s_priority) + valid_prio = v4l2_prio_check(vfd->prio, vfh->prio) >= 0; + ret = ops->vidioc_default(file, fh, valid_prio, cmd, arg); break; } } /* switch */ +exit_prio: if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { if (ret < 0) { v4l_print_ioctl(vfd->name, cmd); diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 1572c7f..dd9f1e7 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -270,7 +270,7 @@ struct v4l2_ioctl_ops { /* For other private ioctls */ long (*vidioc_default) (struct file *file, void *fh, - int cmd, void *arg); + bool valid_prio, int cmd, void *arg); }; -- cgit v0.10.2 From cc0a2d411f158c61af34d056191c7b4669913ddc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Mar 2011 19:03:41 -0300 Subject: [media] ivtv: convert to core priority handling Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 04bacdb..84bdf0f 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -383,7 +383,6 @@ struct ivtv_open_id { u32 open_id; /* unique ID for this file descriptor */ int type; /* stream type */ int yuv_frames; /* 1: started OUT_UDMA_YUV output mode */ - enum v4l2_priority prio; /* priority */ struct ivtv *itv; }; @@ -710,7 +709,6 @@ struct ivtv { /* Miscellaneous */ u32 open_id; /* incremented each time an open occurs, is >= 1 */ - struct v4l2_prio_state prio; /* priority state */ int search_pack_header; /* 1 if ivtv_copy_buf_to_user() is scanning for a pack header (0xba) */ int speed; /* current playback speed setting */ u8 speed_mute_audio; /* 1 if audio should be muted when fast forward */ diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index c57a585..4463bf4 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -856,7 +856,6 @@ int ivtv_v4l2_close(struct file *filp) IVTV_DEBUG_FILE("close %s\n", s->name); - v4l2_prio_close(&itv->prio, id->prio); v4l2_fh_del(fh); v4l2_fh_exit(fh); @@ -973,7 +972,6 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) } item->itv = itv; item->type = s->type; - v4l2_prio_open(&itv->prio, &item->prio); item->open_id = itv->open_id++; filp->private_data = &item->fh; diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index d34a326..b443c03 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -750,23 +750,6 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register } #endif -static int ivtv_g_priority(struct file *file, void *fh, enum v4l2_priority *p) -{ - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - - *p = v4l2_prio_max(&itv->prio); - - return 0; -} - -static int ivtv_s_priority(struct file *file, void *fh, enum v4l2_priority prio) -{ - struct ivtv_open_id *id = fh; - struct ivtv *itv = id->itv; - - return v4l2_prio_change(&itv->prio, &id->prio, prio); -} - static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap) { struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; @@ -1800,6 +1783,21 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, { struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + if (!valid_prio) { + switch (cmd) { + case VIDEO_PLAY: + case VIDEO_STOP: + case VIDEO_FREEZE: + case VIDEO_CONTINUE: + case VIDEO_COMMAND: + case VIDEO_SELECT_SOURCE: + case AUDIO_SET_MUTE: + case AUDIO_CHANNEL_SELECT: + case AUDIO_BILINGUAL_CHANNEL_SELECT: + return -EBUSY; + } + } + switch (cmd) { case VIDIOC_INT_RESET: { u32 val = *(u32 *)arg; @@ -1837,30 +1835,8 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, unsigned int cmd, unsigned long arg) { struct video_device *vfd = video_devdata(filp); - struct ivtv_open_id *id = fh2id(filp->private_data); long ret; - /* check priority */ - switch (cmd) { - case VIDIOC_S_CTRL: - case VIDIOC_S_STD: - case VIDIOC_S_INPUT: - case VIDIOC_S_OUTPUT: - case VIDIOC_S_TUNER: - case VIDIOC_S_FREQUENCY: - case VIDIOC_S_FMT: - case VIDIOC_S_CROP: - case VIDIOC_S_AUDIO: - case VIDIOC_S_AUDOUT: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_S_FBUF: - case VIDIOC_S_PRIORITY: - case VIDIOC_OVERLAY: - ret = v4l2_prio_check(&itv->prio, id->prio); - if (ret) - return ret; - } - if (ivtv_debug & IVTV_DBGFLG_IOCTL) vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; ret = video_ioctl2(filp, cmd, arg); @@ -1885,8 +1861,6 @@ long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { .vidioc_querycap = ivtv_querycap, - .vidioc_g_priority = ivtv_g_priority, - .vidioc_s_priority = ivtv_s_priority, .vidioc_s_audio = ivtv_s_audio, .vidioc_g_audio = ivtv_g_audio, .vidioc_enumaudio = ivtv_enumaudio, -- cgit v0.10.2 From 6e29ad50b4d688b1d18e2d255e31676c7ee46d3d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 24 Feb 2011 10:58:13 -0300 Subject: [media] v4l2-framework.txt: improve v4l2_fh/priority documentation Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index f2df31b..1c8d449 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -549,6 +549,10 @@ You should also set these fields: Otherwise you give it a pointer to a struct mutex_lock and before any of the v4l2_file_operations is called this lock will be taken by the core and released afterwards. +- prio: keeps track of the priorities. Used to implement VIDIOC_G/S_PRIORITY. + If left to NULL, then it will use the struct v4l2_prio_state in v4l2_device. + If you want to have a separate priority state per (group of) device node(s), + then you can point it to your own struct v4l2_prio_state. - parent: you only set this if v4l2_device was registered with NULL as the parent device struct. This only happens in cases where one hardware device has multiple PCI devices that all share the same v4l2_device core. @@ -559,8 +563,10 @@ You should also set these fields: PCI device it is setup without a parent device. But when the struct video_device is setup you do know which parent PCI device to use. -If you use v4l2_ioctl_ops, then you should set either .unlocked_ioctl or -.ioctl to video_ioctl2 in your v4l2_file_operations struct. +If you use v4l2_ioctl_ops, then you should set .unlocked_ioctl to video_ioctl2 +in your v4l2_file_operations struct. + +Do not use .ioctl! This is deprecated and will go away in the future. The v4l2_file_operations struct is a subset of file_operations. The main difference is that the inode argument is omitted since it is never used. @@ -753,39 +759,24 @@ struct v4l2_fh -------------- struct v4l2_fh provides a way to easily keep file handle specific data -that is used by the V4L2 framework. Using v4l2_fh is optional for -drivers. +that is used by the V4L2 framework. New drivers must use struct v4l2_fh +since it is also used to implement priority handling (VIDIOC_G/S_PRIORITY). The users of v4l2_fh (in the V4L2 framework, not the driver) know whether a driver uses v4l2_fh as its file->private_data pointer by -testing the V4L2_FL_USES_V4L2_FH bit in video_device->flags. - -Useful functions: - -- v4l2_fh_init() - - Initialise the file handle. This *MUST* be performed in the driver's - v4l2_file_operations->open() handler. - -- v4l2_fh_add() +testing the V4L2_FL_USES_V4L2_FH bit in video_device->flags. This bit is +set whenever v4l2_fh_init() is called. - Add a v4l2_fh to video_device file handle list. May be called after - initialising the file handle. - -- v4l2_fh_del() - - Unassociate the file handle from video_device(). The file handle - exit function may now be called. +struct v4l2_fh is allocated as a part of the driver's own file handle +structure and file->private_data is set to it in the driver's open +function by the driver. -- v4l2_fh_exit() +In many cases the struct v4l2_fh will be embedded in a larger structure. +In that case you should call v4l2_fh_init+v4l2_fh_add in open() and +v4l2_fh_del+v4l2_fh_exit in release(). - Uninitialise the file handle. After uninitialisation the v4l2_fh - memory can be freed. - -struct v4l2_fh is allocated as a part of the driver's own file handle -structure and is set to file->private_data in the driver's open -function by the driver. Drivers can extract their own file handle -structure by using the container_of macro. Example: +Drivers can extract their own file handle structure by using the container_of +macro. Example: struct my_fh { int blah; @@ -802,15 +793,21 @@ int my_open(struct file *file) ... + my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL); + + ... + ret = v4l2_fh_init(&my_fh->fh, vfd); - if (ret) + if (ret) { + kfree(my_fh); return ret; + } - v4l2_fh_add(&my_fh->fh); + ... file->private_data = &my_fh->fh; - - ... + v4l2_fh_add(&my_fh->fh); + return 0; } int my_release(struct file *file) @@ -819,8 +816,65 @@ int my_release(struct file *file) struct my_fh *my_fh = container_of(fh, struct my_fh, fh); ... + v4l2_fh_del(&my_fh->fh); + v4l2_fh_exit(&my_fh->fh); + kfree(my_fh); + return 0; } +Below is a short description of the v4l2_fh functions used: + +int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) + + Initialise the file handle. This *MUST* be performed in the driver's + v4l2_file_operations->open() handler. + +void v4l2_fh_add(struct v4l2_fh *fh) + + Add a v4l2_fh to video_device file handle list. Must be called once the + file handle is completely initialized. + +void v4l2_fh_del(struct v4l2_fh *fh) + + Unassociate the file handle from video_device(). The file handle + exit function may now be called. + +void v4l2_fh_exit(struct v4l2_fh *fh) + + Uninitialise the file handle. After uninitialisation the v4l2_fh + memory can be freed. + + +If struct v4l2_fh is not embedded, then you can use these helper functions: + +int v4l2_fh_open(struct file *filp) + + This allocates a struct v4l2_fh, initializes it and adds it to the struct + video_device associated with the file struct. + +int v4l2_fh_release(struct file *filp) + + This deletes it from the struct video_device associated with the file + struct, uninitialised the v4l2_fh and frees it. + +These two functions can be plugged into the v4l2_file_operation's open() and +release() ops. + + +Several drivers need to do something when the first file handle is opened and +when the last file handle closes. Two helper functions were added to check +whether the v4l2_fh struct is the only open filehandle of the associated +device node: + +int v4l2_fh_is_singular(struct v4l2_fh *fh) + + Returns 1 if the file handle is the only open file handle, else 0. + +int v4l2_fh_is_singular_file(struct file *filp) + + Same, but it calls v4l2_fh_is_singular with filp->private_data. + + V4L2 events ----------- -- cgit v0.10.2 From 0b5f265a88d89cbbf8abc42ca3311cb3219162ab Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 12 Mar 2011 06:35:33 -0300 Subject: [media] cx18: use v4l2_fh as preparation for adding core priority support Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index de24577..30a8574 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include "cx18-mailbox.h" @@ -405,12 +406,23 @@ struct cx18_stream { }; struct cx18_open_id { + struct v4l2_fh fh; u32 open_id; int type; enum v4l2_priority prio; struct cx18 *cx; }; +static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh) +{ + return container_of(fh, struct cx18_open_id, fh); +} + +static inline struct cx18_open_id *file2id(struct file *file) +{ + return fh2id(file->private_data); +} + /* forward declaration of struct defined in cx18-cards.h */ struct cx18_card; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 98ef33e..1172d68 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -585,7 +585,7 @@ start_failed: ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) { - struct cx18_open_id *id = filp->private_data; + struct cx18_open_id *id = file2id(filp); struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[id->type]; int rc; @@ -602,7 +602,7 @@ ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) { - struct cx18_open_id *id = filp->private_data; + struct cx18_open_id *id = file2id(filp); struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[id->type]; int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags); @@ -676,13 +676,16 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end) int cx18_v4l2_close(struct file *filp) { - struct cx18_open_id *id = filp->private_data; + struct v4l2_fh *fh = filp->private_data; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[id->type]; CX18_DEBUG_IOCTL("close() of %s\n", s->name); v4l2_prio_close(&cx->prio, id->prio); + v4l2_fh_del(fh); + v4l2_fh_exit(fh); /* Easy case first: this stream was never claimed by us */ if (s->id != id->open_id) { @@ -728,22 +731,25 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) CX18_DEBUG_FILE("open %s\n", s->name); /* Allocate memory */ - item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL); + item = kzalloc(sizeof(struct cx18_open_id), GFP_KERNEL); if (NULL == item) { CX18_DEBUG_WARN("nomem on v4l2 open\n"); return -ENOMEM; } + v4l2_fh_init(&item->fh, s->video_dev); + item->cx = cx; item->type = s->type; v4l2_prio_open(&cx->prio, &item->prio); item->open_id = cx->open_id++; - filp->private_data = item; + filp->private_data = &item->fh; if (item->type == CX18_ENC_STREAM_TYPE_RAD) { /* Try to claim this stream */ if (cx18_claim_stream(item, item->type)) { /* No, it's already in use */ + v4l2_fh_exit(&item->fh); kfree(item); return -EBUSY; } @@ -753,6 +759,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) /* switching to radio while capture is in progress is not polite */ cx18_release_stream(s); + v4l2_fh_exit(&item->fh); kfree(item); return -EBUSY; } @@ -769,6 +776,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) /* Done! Unmute and continue. */ cx18_unmute(cx); } + v4l2_fh_add(&item->fh); return 0; } diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 08ed75d..6624ec2 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -148,7 +148,7 @@ u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) static int cx18_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; @@ -173,7 +173,7 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; vbifmt->sampling_rate = 27000000; @@ -192,7 +192,7 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; /* sane, V4L2 spec compliant, defaults */ @@ -221,7 +221,7 @@ static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, static int cx18_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; int w = fmt->fmt.pix.width; int h = fmt->fmt.pix.height; @@ -252,7 +252,7 @@ static int cx18_try_fmt_vbi_cap(struct file *file, void *fh, static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; @@ -271,7 +271,7 @@ static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh, static int cx18_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; struct v4l2_mbus_framefmt mbus_fmt; int ret; @@ -303,7 +303,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; int ret; @@ -337,7 +337,7 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; int ret; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; @@ -372,7 +372,7 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, static int cx18_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; int err = 0; chip->ident = V4L2_IDENT_NONE; @@ -442,7 +442,7 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) static int cx18_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; if (v4l2_chip_match_host(®->match)) return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); @@ -454,7 +454,7 @@ static int cx18_g_register(struct file *file, void *fh, static int cx18_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; if (v4l2_chip_match_host(®->match)) return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); @@ -466,7 +466,7 @@ static int cx18_s_register(struct file *file, void *fh, static int cx18_g_priority(struct file *file, void *fh, enum v4l2_priority *p) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = file2id(file)->cx; *p = v4l2_prio_max(&cx->prio); return 0; @@ -474,7 +474,7 @@ static int cx18_g_priority(struct file *file, void *fh, enum v4l2_priority *p) static int cx18_s_priority(struct file *file, void *fh, enum v4l2_priority prio) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = file2id(file); struct cx18 *cx = id->cx; return v4l2_prio_change(&cx->prio, &id->prio, prio); @@ -483,7 +483,7 @@ static int cx18_s_priority(struct file *file, void *fh, enum v4l2_priority prio) static int cx18_querycap(struct file *file, void *fh, struct v4l2_capability *vcap) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); @@ -496,14 +496,14 @@ static int cx18_querycap(struct file *file, void *fh, static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; return cx18_get_audio_input(cx, vin->index, vin); } static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; vin->index = cx->audio_input; return cx18_get_audio_input(cx, vin->index, vin); @@ -511,7 +511,7 @@ static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; if (vout->index >= cx->nof_audio_inputs) return -EINVAL; @@ -522,7 +522,7 @@ static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; /* set it to defaults from our table */ return cx18_get_input(cx, vin->index, vin); @@ -531,7 +531,7 @@ static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin) static int cx18_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -546,7 +546,7 @@ static int cx18_cropcap(struct file *file, void *fh, static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; int ret; @@ -562,7 +562,7 @@ static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -590,7 +590,7 @@ static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, static int cx18_g_input(struct file *file, void *fh, unsigned int *i) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; *i = cx->active_input; return 0; @@ -598,7 +598,7 @@ static int cx18_g_input(struct file *file, void *fh, unsigned int *i) int cx18_s_input(struct file *file, void *fh, unsigned int inp) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; int ret; @@ -633,7 +633,7 @@ int cx18_s_input(struct file *file, void *fh, unsigned int inp) static int cx18_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; if (vf->tuner != 0) return -EINVAL; @@ -644,7 +644,7 @@ static int cx18_g_frequency(struct file *file, void *fh, int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; int ret; @@ -664,7 +664,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; *std = cx->std; return 0; @@ -672,7 +672,7 @@ static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std) int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; int ret; @@ -713,7 +713,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; int ret; @@ -730,7 +730,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; if (vt->index != 0) return -EINVAL; @@ -751,7 +751,7 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) static int cx18_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; int f, l; @@ -872,7 +872,7 @@ static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl, static int cx18_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; s32 tmp; struct cx18_mdl *mdl; @@ -919,7 +919,7 @@ static int cx18_g_enc_index(struct file *file, void *fh, static int cx18_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc) { - struct cx18_open_id *id = fh; + struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; u32 h; @@ -980,7 +980,7 @@ static int cx18_encoder_cmd(struct file *file, void *fh, static int cx18_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; switch (enc->cmd) { case V4L2_ENC_CMD_START: @@ -1012,7 +1012,7 @@ static int cx18_try_encoder_cmd(struct file *file, void *fh, static int cx18_log_status(struct file *file, void *fh) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; struct v4l2_input vidin; struct v4l2_audio audin; int i; @@ -1060,7 +1060,7 @@ static int cx18_log_status(struct file *file, void *fh) static long cx18_default(struct file *file, void *fh, bool valid_prio, int cmd, void *arg) { - struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18 *cx = fh2id(fh)->cx; switch (cmd) { case VIDIOC_INT_RESET: { @@ -1082,7 +1082,7 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct video_device *vfd = video_devdata(filp); - struct cx18_open_id *id = filp->private_data; + struct cx18_open_id *id = file2id(filp); struct cx18 *cx = id->cx; long res; -- cgit v0.10.2 From a64bb4b7111e063154f97ff64dab03144cdfb5d9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 12 Mar 2011 06:36:08 -0300 Subject: [media] cx18: use core priority handling VIDIOC_S/G_PRIORITY handling is now done by the v4l2 core framework. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 30a8574..b86a740 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -409,7 +409,6 @@ struct cx18_open_id { struct v4l2_fh fh; u32 open_id; int type; - enum v4l2_priority prio; struct cx18 *cx; }; @@ -605,7 +604,6 @@ struct cx18 { uninitialized value in the stream->id. */ u32 base_addr; - struct v4l2_prio_state prio; u8 card_rev; void __iomem *enc_mem, *reg_mem; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 1172d68..e9802d9 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -683,7 +683,6 @@ int cx18_v4l2_close(struct file *filp) CX18_DEBUG_IOCTL("close() of %s\n", s->name); - v4l2_prio_close(&cx->prio, id->prio); v4l2_fh_del(fh); v4l2_fh_exit(fh); @@ -740,7 +739,6 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) item->cx = cx; item->type = s->type; - v4l2_prio_open(&cx->prio, &item->prio); item->open_id = cx->open_id++; filp->private_data = &item->fh; diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 6624ec2..86c30b9 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -277,10 +277,6 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, int ret; int w, h; - ret = v4l2_prio_check(&cx->prio, id->prio); - if (ret) - return ret; - ret = cx18_try_fmt_vid_cap(file, fh, fmt); if (ret) return ret; @@ -307,10 +303,6 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, id->prio); - if (ret) - return ret; - /* * Changing the Encoder's Raw VBI parameters won't have any effect * if any analog capture is ongoing @@ -342,10 +334,6 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, int ret; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; - ret = v4l2_prio_check(&cx->prio, id->prio); - if (ret) - return ret; - cx18_try_fmt_sliced_vbi_cap(file, fh, fmt); /* @@ -464,22 +452,6 @@ static int cx18_s_register(struct file *file, void *fh, } #endif -static int cx18_g_priority(struct file *file, void *fh, enum v4l2_priority *p) -{ - struct cx18 *cx = file2id(file)->cx; - - *p = v4l2_prio_max(&cx->prio); - return 0; -} - -static int cx18_s_priority(struct file *file, void *fh, enum v4l2_priority prio) -{ - struct cx18_open_id *id = file2id(file); - struct cx18 *cx = id->cx; - - return v4l2_prio_change(&cx->prio, &id->prio, prio); -} - static int cx18_querycap(struct file *file, void *fh, struct v4l2_capability *vcap) { @@ -548,11 +520,6 @@ static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; - int ret; - - ret = v4l2_prio_check(&cx->prio, id->prio); - if (ret) - return ret; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -600,11 +567,6 @@ int cx18_s_input(struct file *file, void *fh, unsigned int inp) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; - int ret; - - ret = v4l2_prio_check(&cx->prio, id->prio); - if (ret) - return ret; if (inp >= cx->nof_inputs) return -EINVAL; @@ -646,11 +608,6 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; - int ret; - - ret = v4l2_prio_check(&cx->prio, id->prio); - if (ret) - return ret; if (vf->tuner != 0) return -EINVAL; @@ -674,11 +631,6 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; - int ret; - - ret = v4l2_prio_check(&cx->prio, id->prio); - if (ret) - return ret; if ((*std & V4L2_STD_ALL) == 0) return -EINVAL; @@ -715,11 +667,6 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; - int ret; - - ret = v4l2_prio_check(&cx->prio, id->prio); - if (ret) - return ret; if (vt->index != 0) return -EINVAL; @@ -1088,8 +1035,6 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, mutex_lock(&cx->serialize_lock); - /* FIXME - consolidate v4l2_prio_check()'s here */ - if (cx18_debug & CX18_DBGFLG_IOCTL) vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; res = video_ioctl2(filp, cmd, arg); @@ -1100,8 +1045,6 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, static const struct v4l2_ioctl_ops cx18_ioctl_ops = { .vidioc_querycap = cx18_querycap, - .vidioc_g_priority = cx18_g_priority, - .vidioc_s_priority = cx18_s_priority, .vidioc_s_audio = cx18_s_audio, .vidioc_g_audio = cx18_g_audio, .vidioc_enumaudio = cx18_enumaudio, -- cgit v0.10.2 From bedf8bcf6b4f90a6e31add3721a2e71877289381 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 12 Mar 2011 06:37:19 -0300 Subject: [media] v4l2-device: add kref and a release function The video_device struct has proper ref counting and its release function will be called when the last user releases it. But no such support was available for struct v4l2_device. This made it hard to determine when a USB driver can release the device if it has multiple device nodes. With one device node it is easy of course, since when the device node is released, the whole device can be released. This patch adds refcounting to v4l2_device. When registering device nodes the v4l2_device refcount will be increased, when releasing device nodes it will be decreased. The (optional) release function will be called when the last device node was released. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 6b1ef85..1898099 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -143,6 +143,7 @@ static inline void video_put(struct video_device *vdev) static void v4l2_device_release(struct device *cd) { struct video_device *vdev = to_video_device(cd); + struct v4l2_device *v4l2_dev = vdev->v4l2_dev; mutex_lock(&videodev_lock); if (video_device[vdev->minor] != vdev) { @@ -169,6 +170,10 @@ static void v4l2_device_release(struct device *cd) /* Release video_device and perform other cleanups as needed. */ vdev->release(vdev); + + /* Decrease v4l2_device refcount */ + if (v4l2_dev) + v4l2_device_put(v4l2_dev); } static struct class video_class = { @@ -676,6 +681,11 @@ int __video_register_device(struct video_device *vdev, int type, int nr, if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__, name_base, nr, video_device_node_name(vdev)); + + /* Increase v4l2_device refcount */ + if (vdev->v4l2_dev) + v4l2_device_get(vdev->v4l2_dev); + #if defined(CONFIG_MEDIA_CONTROLLER) /* Part 5: Register the entity. */ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) { diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index fd7a445..5aeaf87 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -37,6 +37,7 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) spin_lock_init(&v4l2_dev->lock); mutex_init(&v4l2_dev->ioctl_lock); v4l2_prio_init(&v4l2_dev->prio); + kref_init(&v4l2_dev->ref); v4l2_dev->dev = dev; if (dev == NULL) { /* If dev == NULL, then name must be filled in by the caller */ @@ -54,6 +55,21 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) } EXPORT_SYMBOL_GPL(v4l2_device_register); +static void v4l2_device_release(struct kref *ref) +{ + struct v4l2_device *v4l2_dev = + container_of(ref, struct v4l2_device, ref); + + if (v4l2_dev->release) + v4l2_dev->release(v4l2_dev); +} + +int v4l2_device_put(struct v4l2_device *v4l2_dev) +{ + return kref_put(&v4l2_dev->ref, v4l2_device_release); +} +EXPORT_SYMBOL_GPL(v4l2_device_put); + int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, atomic_t *instance) { diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 3b72328..bd102cf 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -60,8 +60,19 @@ struct v4l2_device { struct v4l2_prio_state prio; /* BKL replacement mutex. Temporary solution only. */ struct mutex ioctl_lock; + /* Keep track of the references to this struct. */ + struct kref ref; + /* Release function that is called when the ref count goes to 0. */ + void (*release)(struct v4l2_device *v4l2_dev); }; +static inline void v4l2_device_get(struct v4l2_device *v4l2_dev) +{ + kref_get(&v4l2_dev->ref); +} + +int v4l2_device_put(struct v4l2_device *v4l2_dev); + /* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev. dev may be NULL in rare cases (ISA devices). In that case you must fill in the v4l2_dev->name field before calling this function. */ -- cgit v0.10.2 From 2335e2b817186cf79a07311f14560ef8b30f6a9a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 24 Feb 2011 06:28:46 -0300 Subject: [media] v4l2-framework.txt: document new v4l2_device release() callback Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 1c8d449..7d09114 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -181,6 +181,21 @@ static int __devinit drv_probe(struct pci_dev *pdev, state->instance = atomic_inc_return(&drv_instance) - 1; } +If you have multiple device nodes then it can be difficult to know when it is +safe to unregister v4l2_device. For this purpose v4l2_device has refcounting +support. The refcount is increased whenever video_register_device is called and +it is decreased whenever that device node is released. When the refcount reaches +zero, then the v4l2_device release() callback is called. You can do your final +cleanup there. + +If other device nodes (e.g. ALSA) are created, then you can increase and +decrease the refcount manually as well by calling: + +void v4l2_device_get(struct v4l2_device *v4l2_dev); + +or: + +int v4l2_device_put(struct v4l2_device *v4l2_dev); struct v4l2_subdev ------------------ -- cgit v0.10.2 From e64d07c92daa5c7973c1d4433f939a4e0fa3beb9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 19 Dec 2010 18:54:08 -0300 Subject: [media] dsbr100: convert to unlocked_ioctl Use core-assisted locking so .ioctl can be replaced by .unlocked_ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index ed9cd7a..bf2dd6f 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -148,7 +148,7 @@ struct dsbr100_device { struct v4l2_device v4l2_dev; u8 *transfer_buffer; - struct mutex lock; /* buffer locking */ + struct mutex v4l2_lock; int curfreq; int stereo; int removed; @@ -182,8 +182,6 @@ static int dsbr100_start(struct dsbr100_device *radio) int retval; int request; - mutex_lock(&radio->lock); - retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), USB_REQ_GET_STATUS, @@ -207,11 +205,9 @@ static int dsbr100_start(struct dsbr100_device *radio) } radio->status = STARTED; - mutex_unlock(&radio->lock); return (radio->transfer_buffer)[0]; usb_control_msg_failed: - mutex_unlock(&radio->lock); dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", __func__, retval, request); @@ -225,8 +221,6 @@ static int dsbr100_stop(struct dsbr100_device *radio) int retval; int request; - mutex_lock(&radio->lock); - retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), USB_REQ_GET_STATUS, @@ -250,11 +244,9 @@ static int dsbr100_stop(struct dsbr100_device *radio) } radio->status = STOPPED; - mutex_unlock(&radio->lock); return (radio->transfer_buffer)[0]; usb_control_msg_failed: - mutex_unlock(&radio->lock); dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", __func__, retval, request); @@ -269,8 +261,6 @@ static int dsbr100_setfreq(struct dsbr100_device *radio) int request; int freq = (radio->curfreq / 16 * 80) / 1000 + 856; - mutex_lock(&radio->lock); - retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), DSB100_TUNE, @@ -306,12 +296,10 @@ static int dsbr100_setfreq(struct dsbr100_device *radio) } radio->stereo = !((radio->transfer_buffer)[0] & 0x01); - mutex_unlock(&radio->lock); return (radio->transfer_buffer)[0]; usb_control_msg_failed: radio->stereo = -1; - mutex_unlock(&radio->lock); dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", __func__, retval, request); @@ -324,8 +312,6 @@ static void dsbr100_getstat(struct dsbr100_device *radio) { int retval; - mutex_lock(&radio->lock); - retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), USB_REQ_GET_STATUS, @@ -340,33 +326,8 @@ static void dsbr100_getstat(struct dsbr100_device *radio) } else { radio->stereo = !(radio->transfer_buffer[0] & 0x01); } - - mutex_unlock(&radio->lock); -} - -/* USB subsystem interface begins here */ - -/* - * Handle unplugging of the device. - * We call video_unregister_device in any case. - * The last function called in this procedure is - * usb_dsbr100_video_device_release - */ -static void usb_dsbr100_disconnect(struct usb_interface *intf) -{ - struct dsbr100_device *radio = usb_get_intfdata(intf); - - usb_set_intfdata (intf, NULL); - - mutex_lock(&radio->lock); - radio->removed = 1; - mutex_unlock(&radio->lock); - - video_unregister_device(&radio->videodev); - v4l2_device_disconnect(&radio->v4l2_dev); } - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { @@ -432,9 +393,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (radio->removed) return -EIO; - mutex_lock(&radio->lock); radio->curfreq = f->frequency; - mutex_unlock(&radio->lock); retval = dsbr100_setfreq(radio); if (retval < 0) @@ -548,12 +507,36 @@ static int vidioc_s_audio(struct file *file, void *priv, return 0; } +/* USB subsystem interface begins here */ + +/* + * Handle unplugging of the device. + * We call video_unregister_device in any case. + * The last function called in this procedure is + * usb_dsbr100_video_device_release + */ +static void usb_dsbr100_disconnect(struct usb_interface *intf) +{ + struct dsbr100_device *radio = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + mutex_lock(&radio->v4l2_lock); + radio->removed = 1; + mutex_unlock(&radio->v4l2_lock); + + video_unregister_device(&radio->videodev); + v4l2_device_disconnect(&radio->v4l2_dev); +} + + /* Suspend device - stop device. */ static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) { struct dsbr100_device *radio = usb_get_intfdata(intf); int retval; + mutex_lock(&radio->v4l2_lock); if (radio->status == STARTED) { retval = dsbr100_stop(radio); if (retval < 0) @@ -564,11 +547,9 @@ static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) * we set status equal to STARTED. * On resume we will check status and run radio if needed. */ - - mutex_lock(&radio->lock); radio->status = STARTED; - mutex_unlock(&radio->lock); } + mutex_unlock(&radio->v4l2_lock); dev_info(&intf->dev, "going into suspend..\n"); @@ -581,11 +562,13 @@ static int usb_dsbr100_resume(struct usb_interface *intf) struct dsbr100_device *radio = usb_get_intfdata(intf); int retval; + mutex_lock(&radio->v4l2_lock); if (radio->status == STARTED) { retval = dsbr100_start(radio); if (retval < 0) dev_warn(&intf->dev, "dsbr100_start failed\n"); } + mutex_unlock(&radio->v4l2_lock); dev_info(&intf->dev, "coming out of suspend..\n"); @@ -605,7 +588,7 @@ static void usb_dsbr100_video_device_release(struct video_device *videodev) /* File system interface */ static const struct v4l2_file_operations usb_dsbr100_fops = { .owner = THIS_MODULE, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { @@ -653,13 +636,13 @@ static int usb_dsbr100_probe(struct usb_interface *intf, return retval; } + mutex_init(&radio->v4l2_lock); strlcpy(radio->videodev.name, v4l2_dev->name, sizeof(radio->videodev.name)); radio->videodev.v4l2_dev = v4l2_dev; radio->videodev.fops = &usb_dsbr100_fops; radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops; radio->videodev.release = usb_dsbr100_video_device_release; - - mutex_init(&radio->lock); + radio->videodev.lock = &radio->v4l2_lock; radio->removed = 0; radio->usbdev = interface_to_usbdev(intf); -- cgit v0.10.2 From 13099294973b14f07915d0342af2be8fa0af589b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 17 Jan 2011 20:25:11 -0300 Subject: [media] dsbr100: ensure correct disconnect sequence Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index bf2dd6f..3d8cc42 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -129,7 +129,7 @@ devices, that would be 76 and 91. */ #define STARTED 0 #define STOPPED 1 -#define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev) +#define v4l2_dev_to_radio(d) container_of(d, struct dsbr100_device, v4l2_dev) static int usb_dsbr100_probe(struct usb_interface *intf, const struct usb_device_id *id); @@ -151,7 +151,6 @@ struct dsbr100_device { struct mutex v4l2_lock; int curfreq; int stereo; - int removed; int status; }; @@ -346,10 +345,6 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct dsbr100_device *radio = video_drvdata(file); - /* safety check */ - if (radio->removed) - return -EIO; - if (v->index > 0) return -EINVAL; @@ -371,16 +366,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct dsbr100_device *radio = video_drvdata(file); - - /* safety check */ - if (radio->removed) - return -EIO; - - if (v->index > 0) - return -EINVAL; - - return 0; + return v->index ? -EINVAL : 0; } static int vidioc_s_frequency(struct file *file, void *priv, @@ -389,10 +375,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, struct dsbr100_device *radio = video_drvdata(file); int retval; - /* safety check */ - if (radio->removed) - return -EIO; - radio->curfreq = f->frequency; retval = dsbr100_setfreq(radio); @@ -406,10 +388,6 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct dsbr100_device *radio = video_drvdata(file); - /* safety check */ - if (radio->removed) - return -EIO; - f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; return 0; @@ -431,10 +409,6 @@ static int vidioc_g_ctrl(struct file *file, void *priv, { struct dsbr100_device *radio = video_drvdata(file); - /* safety check */ - if (radio->removed) - return -EIO; - switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value = radio->status; @@ -449,10 +423,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv, struct dsbr100_device *radio = video_drvdata(file); int retval; - /* safety check */ - if (radio->removed) - return -EIO; - switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: if (ctrl->value) { @@ -494,17 +464,13 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) { - if (i != 0) - return -EINVAL; - return 0; + return i ? -EINVAL : 0; } static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) { - if (a->index != 0) - return -EINVAL; - return 0; + return a->index ? -EINVAL : 0; } /* USB subsystem interface begins here */ @@ -519,14 +485,13 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) { struct dsbr100_device *radio = usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); - + v4l2_device_get(&radio->v4l2_dev); mutex_lock(&radio->v4l2_lock); - radio->removed = 1; - mutex_unlock(&radio->v4l2_lock); - + usb_set_intfdata(intf, NULL); video_unregister_device(&radio->videodev); v4l2_device_disconnect(&radio->v4l2_dev); + mutex_unlock(&radio->v4l2_lock); + v4l2_device_put(&radio->v4l2_dev); } @@ -576,9 +541,9 @@ static int usb_dsbr100_resume(struct usb_interface *intf) } /* free data structures */ -static void usb_dsbr100_video_device_release(struct video_device *videodev) +static void usb_dsbr100_release(struct v4l2_device *v4l2_dev) { - struct dsbr100_device *radio = videodev_to_radio(videodev); + struct dsbr100_device *radio = v4l2_dev_to_radio(v4l2_dev); v4l2_device_unregister(&radio->v4l2_dev); kfree(radio->transfer_buffer); @@ -627,6 +592,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf, } v4l2_dev = &radio->v4l2_dev; + v4l2_dev->release = usb_dsbr100_release; retval = v4l2_device_register(&intf->dev, v4l2_dev); if (retval < 0) { @@ -641,10 +607,9 @@ static int usb_dsbr100_probe(struct usb_interface *intf, radio->videodev.v4l2_dev = v4l2_dev; radio->videodev.fops = &usb_dsbr100_fops; radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops; - radio->videodev.release = usb_dsbr100_video_device_release; + radio->videodev.release = video_device_release_empty; radio->videodev.lock = &radio->v4l2_lock; - radio->removed = 0; radio->usbdev = interface_to_usbdev(intf); radio->curfreq = FREQ_MIN * FREQ_MUL; radio->status = STOPPED; -- cgit v0.10.2 From 2e4784d0d3a6167e905a49d0dafa40b022fa1a55 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Mar 2011 20:01:54 -0300 Subject: [media] vivi: convert to core priority handling Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index bd104d0..42500bf 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #define VIVI_MODULE_NAME "vivi" @@ -199,7 +200,6 @@ struct vivi_dev { enum v4l2_field field; unsigned int field_count; - unsigned int open_count; u8 bars[9][3]; u8 line[MAX_WIDTH * 4]; }; @@ -996,15 +996,6 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl) File operations for the device ------------------------------------------------------------------*/ -static int vivi_open(struct file *file) -{ - struct vivi_dev *dev = video_drvdata(file); - - dprintk(dev, 1, "%s, %p\n", __func__, file); - dev->open_count++; - return 0; -} - static ssize_t vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -1033,9 +1024,9 @@ static int vivi_close(struct file *file) dprintk(dev, 1, "close called (dev=%s), file %p\n", video_device_node_name(vdev), file); - if (--dev->open_count == 0) + if (v4l2_fh_is_singular_file(file)) vb2_queue_release(&dev->vb_vidq); - return 0; + return v4l2_fh_release(file); } static int vivi_mmap(struct file *file, struct vm_area_struct *vma) @@ -1128,7 +1119,7 @@ static const struct v4l2_ctrl_config vivi_ctrl_string = { static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, - .open = vivi_open, + .open = v4l2_fh_open, .release = vivi_close, .read = vivi_read, .poll = vivi_poll, -- cgit v0.10.2 From 6aff72f6d511bcadbc71358ed17bc87edbf90716 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 12 Mar 2011 06:39:44 -0300 Subject: [media] ivtv: add missing v4l2_fh_exit Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 4463bf4..a7f54b0 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -980,6 +980,7 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) /* Try to claim this stream */ if (ivtv_claim_stream(item, item->type)) { /* No, it's already in use */ + v4l2_fh_exit(&item->fh); kfree(item); return -EBUSY; } -- cgit v0.10.2 From 2f82441a8644287e5b647329cc30b590f6fd3714 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 12 Mar 2011 06:43:28 -0300 Subject: [media] ivtv: replace ugly casts with a proper container_of ivtv-ioctl cast the 'void *fh' directly to 'ivtv_open_id *'. This should be done properly with a contained_of since the 'void *fh' is really a 'struct v4l2_fh *'. It worked because the v4l2_fh field is also the first field in the ivtv_open_id struct, but it is not clean code. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index b443c03..1689783 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -313,7 +313,7 @@ static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; vbifmt->reserved[0] = 0; @@ -334,7 +334,7 @@ static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_fo static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; @@ -358,7 +358,7 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; vbifmt->sampling_rate = 27000000; @@ -377,7 +377,7 @@ static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; vbifmt->reserved[0] = 0; @@ -398,7 +398,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; @@ -439,7 +439,7 @@ static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; struct v4l2_window *winfmt = &fmt->fmt.win; if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) @@ -463,7 +463,7 @@ static int ivtv_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_ static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; int w = fmt->fmt.pix.width; int h = fmt->fmt.pix.height; @@ -492,7 +492,7 @@ static int ivtv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; if (id->type == IVTV_DEC_STREAM_TYPE_VBI) @@ -512,7 +512,7 @@ static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); s32 w = fmt->fmt.pix.width; s32 h = fmt->fmt.pix.height; int field = fmt->fmt.pix.field; @@ -546,7 +546,7 @@ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; u32 chromakey = fmt->fmt.win.chromakey; u8 global_alpha = fmt->fmt.win.global_alpha; @@ -565,7 +565,7 @@ static int ivtv_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_fo static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct v4l2_mbus_framefmt mbus_fmt; int ret = ivtv_try_fmt_vid_cap(file, fh, fmt); @@ -594,7 +594,7 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (!ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0) return -EBUSY; @@ -607,7 +607,7 @@ static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; int ret = ivtv_try_fmt_sliced_vbi_cap(file, fh, fmt); @@ -625,7 +625,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo static int ivtv_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct yuv_playback_info *yi = &itv->yuv_info; int ret = ivtv_try_fmt_vid_out(file, fh, fmt); @@ -670,7 +670,7 @@ static int ivtv_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; int ret = ivtv_try_fmt_vid_out_overlay(file, fh, fmt); if (ret == 0) { @@ -683,7 +683,7 @@ static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_f static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; chip->ident = V4L2_IDENT_NONE; chip->revision = 0; @@ -727,7 +727,7 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) static int ivtv_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (v4l2_chip_match_host(®->match)) return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg); @@ -739,7 +739,7 @@ static int ivtv_g_register(struct file *file, void *fh, struct v4l2_dbg_register static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (v4l2_chip_match_host(®->match)) return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg); @@ -752,7 +752,7 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver)); strlcpy(vcap->card, itv->card_name, sizeof(vcap->card)); @@ -764,14 +764,14 @@ static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vc static int ivtv_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; return ivtv_get_audio_input(itv, vin->index, vin); } static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; vin->index = itv->audio_input; return ivtv_get_audio_input(itv, vin->index, vin); @@ -779,7 +779,7 @@ static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) static int ivtv_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (vout->index >= itv->nof_audio_inputs) return -EINVAL; @@ -792,7 +792,7 @@ static int ivtv_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) static int ivtv_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vin) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; /* set it to defaults from our table */ return ivtv_get_audio_output(itv, vin->index, vin); @@ -800,7 +800,7 @@ static int ivtv_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vi static int ivtv_g_audout(struct file *file, void *fh, struct v4l2_audioout *vin) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; vin->index = 0; return ivtv_get_audio_output(itv, vin->index, vin); @@ -808,14 +808,14 @@ static int ivtv_g_audout(struct file *file, void *fh, struct v4l2_audioout *vin) static int ivtv_s_audout(struct file *file, void *fh, struct v4l2_audioout *vout) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; return ivtv_get_audio_output(itv, vout->index, vout); } static int ivtv_enum_input(struct file *file, void *fh, struct v4l2_input *vin) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; /* set it to defaults from our table */ return ivtv_get_input(itv, vin->index, vin); @@ -823,14 +823,14 @@ static int ivtv_enum_input(struct file *file, void *fh, struct v4l2_input *vin) static int ivtv_enum_output(struct file *file, void *fh, struct v4l2_output *vout) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; return ivtv_get_output(itv, vout->index, vout); } static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct yuv_playback_info *yi = &itv->yuv_info; int streamtype; @@ -867,7 +867,7 @@ static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropca static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct yuv_playback_info *yi = &itv->yuv_info; int streamtype; @@ -893,7 +893,7 @@ static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) static int ivtv_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct yuv_playback_info *yi = &itv->yuv_info; int streamtype; @@ -935,7 +935,7 @@ static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdes static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; static struct v4l2_fmtdesc formats[] = { { 0, 0, 0, @@ -963,7 +963,7 @@ static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdes static int ivtv_g_input(struct file *file, void *fh, unsigned int *i) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; *i = itv->active_input; @@ -972,7 +972,7 @@ static int ivtv_g_input(struct file *file, void *fh, unsigned int *i) int ivtv_s_input(struct file *file, void *fh, unsigned int inp) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (inp < 0 || inp >= itv->nof_inputs) return -EINVAL; @@ -1006,7 +1006,7 @@ int ivtv_s_input(struct file *file, void *fh, unsigned int inp) static int ivtv_g_output(struct file *file, void *fh, unsigned int *i) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) return -EINVAL; @@ -1018,7 +1018,7 @@ static int ivtv_g_output(struct file *file, void *fh, unsigned int *i) static int ivtv_s_output(struct file *file, void *fh, unsigned int outp) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (outp >= itv->card->nof_outputs) return -EINVAL; @@ -1040,7 +1040,7 @@ static int ivtv_s_output(struct file *file, void *fh, unsigned int outp) static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (vf->tuner != 0) return -EINVAL; @@ -1051,7 +1051,7 @@ static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency * int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (vf->tuner != 0) return -EINVAL; @@ -1065,7 +1065,7 @@ int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; *std = itv->std; return 0; @@ -1074,7 +1074,7 @@ static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std) int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) { DEFINE_WAIT(wait); - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; struct yuv_playback_info *yi = &itv->yuv_info; int f; @@ -1153,7 +1153,7 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; if (vt->index != 0) @@ -1166,7 +1166,7 @@ static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (vt->index != 0) return -EINVAL; @@ -1186,7 +1186,7 @@ static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; int f, l; @@ -1216,7 +1216,7 @@ static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; struct v4l2_enc_idx_entry *e = idx->entry; int entries; int i; @@ -1239,7 +1239,7 @@ static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *id static int ivtv_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; @@ -1291,7 +1291,7 @@ static int ivtv_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd static int ivtv_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; switch (enc->cmd) { case V4L2_ENC_CMD_START: @@ -1321,7 +1321,7 @@ static int ivtv_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; u32 data[CX2341X_MBOX_MAX_DATA]; struct yuv_playback_info *yi = &itv->yuv_info; @@ -1408,7 +1408,7 @@ static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) static int ivtv_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct yuv_playback_info *yi = &itv->yuv_info; @@ -1428,7 +1428,7 @@ static int ivtv_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) static int ivtv_overlay(struct file *file, void *fh, unsigned int on) { - struct ivtv_open_id *id = fh; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) @@ -1453,7 +1453,7 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti static int ivtv_log_status(struct file *file, void *fh) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; u32 data[CX2341X_MBOX_MAX_DATA]; int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT; @@ -1781,7 +1781,7 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) static long ivtv_default(struct file *file, void *fh, bool valid_prio, int cmd, void *arg) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = fh2id(fh)->itv; if (!valid_prio) { switch (cmd) { -- cgit v0.10.2 From b1a873a37b6551a214ad37d1eee7654a9d65fd6e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Mar 2011 10:14:07 -0300 Subject: [media] v4l2: use new flag to enable core priority handling Rather than guess which driver supports core priority handling, require drivers that do to explicitly set the V4L2_FL_USE_FH_PRIO flag in video_device. Updated the core prio handling accordingly and set the flag in the three drivers that do. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 7d09114..3b15608 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -577,6 +577,10 @@ You should also set these fields: (cx8802). Since the v4l2_device cannot be associated with a particular PCI device it is setup without a parent device. But when the struct video_device is setup you do know which parent PCI device to use. +- flags: optional. Set to V4L2_FL_USE_FH_PRIO if you want to let the framework + handle the VIDIOC_G/S_PRIORITY ioctls. This requires that you use struct + v4l2_fh. Eventually this flag will disappear once all drivers use the core + priority handling. But for now it has to be set explicitly. If you use v4l2_ioctl_ops, then you should set .unlocked_ioctl to video_ioctl2 in your v4l2_file_operations struct. @@ -775,7 +779,8 @@ struct v4l2_fh struct v4l2_fh provides a way to easily keep file handle specific data that is used by the V4L2 framework. New drivers must use struct v4l2_fh -since it is also used to implement priority handling (VIDIOC_G/S_PRIORITY). +since it is also used to implement priority handling (VIDIOC_G/S_PRIORITY) +if the video_device flag V4L2_FL_USE_FH_PRIO is also set. The users of v4l2_fh (in the V4L2 framework, not the driver) know whether a driver uses v4l2_fh as its file->private_data pointer by diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 2d24856..c6e2ca3 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -207,6 +207,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type) s->video_dev->fops = &cx18_v4l2_enc_fops; s->video_dev->release = video_device_release; s->video_dev->tvnorms = V4L2_STD_ALL; + set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags); cx18_set_funcs(s->video_dev); return 0; } diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 512607e..9426833 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -214,6 +214,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->fops = ivtv_stream_info[type].fops; s->vdev->release = video_device_release; s->vdev->tvnorms = V4L2_STD_ALL; + set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags); ivtv_set_funcs(s->vdev); return 0; } diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 1898099..498e674 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -578,11 +578,9 @@ int __video_register_device(struct video_device *vdev, int type, int nr, vdev->parent = vdev->v4l2_dev->dev; if (vdev->ctrl_handler == NULL) vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; - /* If the prio state pointer is NULL, and if the driver doesn't - handle priorities itself, then use the v4l2_device prio - state. */ - if (vdev->prio == NULL && vdev->ioctl_ops && - vdev->ioctl_ops->vidioc_s_priority == NULL) + /* If the prio state pointer is NULL, then use the v4l2_device + prio state. */ + if (vdev->prio == NULL) vdev->prio = &vdev->v4l2_dev->prio; } diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c index 543b3fe..717f71e 100644 --- a/drivers/media/video/v4l2-fh.c +++ b/drivers/media/video/v4l2-fh.c @@ -35,7 +35,6 @@ int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) INIT_LIST_HEAD(&fh->list); set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags); fh->prio = V4L2_PRIORITY_UNSET; - BUG_ON(vdev->prio == NULL); /* * fh->events only needs to be initialized if the driver @@ -54,7 +53,8 @@ void v4l2_fh_add(struct v4l2_fh *fh) { unsigned long flags; - v4l2_prio_open(fh->vdev->prio, &fh->prio); + if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags)) + v4l2_prio_open(fh->vdev->prio, &fh->prio); spin_lock_irqsave(&fh->vdev->fh_lock, flags); list_add(&fh->list, &fh->vdev->fh_list); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); @@ -82,7 +82,8 @@ void v4l2_fh_del(struct v4l2_fh *fh) spin_lock_irqsave(&fh->vdev->fh_lock, flags); list_del_init(&fh->list); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - v4l2_prio_close(fh->vdev->prio, fh->prio); + if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags)) + v4l2_prio_close(fh->vdev->prio, fh->prio); } EXPORT_SYMBOL_GPL(v4l2_fh_del); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 3e6b6fa..a01ed39 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -541,6 +541,7 @@ static long __video_do_ioctl(struct file *file, void *fh = file->private_data; struct v4l2_fh *vfh = NULL; struct v4l2_format f_copy; + int use_fh_prio = 0; long ret = -EINVAL; if (ops == NULL) { @@ -555,10 +556,12 @@ static long __video_do_ioctl(struct file *file, printk(KERN_CONT "\n"); } - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { vfh = file->private_data; + use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + } - if (vfh && !ops->vidioc_s_priority) { + if (use_fh_prio) { switch (cmd) { case VIDIOC_S_CTRL: case VIDIOC_S_STD: @@ -620,7 +623,7 @@ static long __video_do_ioctl(struct file *file, if (ops->vidioc_g_priority) { ret = ops->vidioc_g_priority(file, fh, p); - } else if (vfh) { + } else if (use_fh_prio) { *p = v4l2_prio_max(&vfd->v4l2_dev->prio); ret = 0; } @@ -632,7 +635,7 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_priority *p = arg; - if (!ops->vidioc_s_priority && vfh == NULL) + if (!ops->vidioc_s_priority && !use_fh_prio) break; dbgarg(cmd, "setting priority to %d\n", *p); if (ops->vidioc_s_priority) @@ -2187,7 +2190,7 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_default) break; - if (vfh && !ops->vidioc_s_priority) + if (use_fh_prio) valid_prio = v4l2_prio_check(vfd->prio, vfh->prio) >= 0; ret = ops->vidioc_default(file, fh, valid_prio, cmd, arg); break; diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 42500bf..2238a61 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -1254,6 +1254,7 @@ static int __init vivi_create_instance(int inst) *vfd = vivi_template; vfd->debug = debug; vfd->v4l2_dev = &dev->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); /* * Provide a mutex to v4l2 core. It will be used to protect diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 3700127..8266d5a 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -35,7 +35,10 @@ struct v4l2_ctrl_handler; Drivers can clear this flag if they want to block all future device access. It is cleared by video_unregister_device. */ #define V4L2_FL_REGISTERED (0) +/* file->private_data points to struct v4l2_fh */ #define V4L2_FL_USES_V4L2_FH (1) +/* Use the prio field of v4l2_fh for core priority checking */ +#define V4L2_FL_USE_FH_PRIO (2) /* Priority helper functions */ -- cgit v0.10.2 From c3ee142b44c868fb5e0eefe1b9c2df8115f6c69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Tue, 22 Mar 2011 05:16:11 -0300 Subject: [media] gspca - nw80x: Cleanup source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index 1c9d3c2..8317488 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -53,7 +53,7 @@ struct sd { }; enum bridges { - BRIDGE_NW800, /* et31x110 */ + BRIDGE_NW800, /* and et31x110 */ BRIDGE_NW801, BRIDGE_NW802, }; @@ -145,7 +145,7 @@ enum webcams { nw801/nw802 320x240 - 640x480 */ -static const struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format cif_mode[] = { {352, 288, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, .bytesperline = 352, .sizeimage = 352 * 288 * 4 / 8, @@ -171,6 +171,7 @@ static const struct v4l2_pix_format vga_mode[] = { * - n bytes: data */ #define I2C0 0xff + static const u8 nw800_init[] = { 0x05, 0x00, 0x01, 0x55, 0x10, 0x9b, 0x01, 0xaa, @@ -333,8 +334,9 @@ static const u8 nw800_init[] = { 0, 0, 0 }; -/* 06a5:d001 - nw801 - p35u */ -static const u8 nw801_init_1[] = { +/* 06a5:d001 - nw801 - Panasonic + * P35u */ +static const u8 nw801_start_1[] = { 0x05, 0x06, 0x01, 0x04, 0x00, 0x00, 0x40, 0x0e, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e, 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, @@ -389,7 +391,7 @@ static const u8 nw801_init_1[] = { 0xf0, 0x00, 0, 0, 0, }; -static const u8 nw801_init_qvga[] = { +static const u8 nw801_start_qvga[] = { 0x02, 0x00, 0x10, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, 0x0b, 0x06, 0xa2, 0x86, 0x78, 0x02, 0x0f, 0x01, 0x6b, @@ -401,7 +403,7 @@ static const u8 nw801_init_qvga[] = { /* AE window */ 0, 0, 0, }; -static const u8 nw801_init_vga[] = { +static const u8 nw801_start_vga[] = { 0x02, 0x00, 0x10, 0x78, 0xa0, 0x97, 0x78, 0xa0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xf0, 0x02, 0x0f, 0x01, 0xd5, @@ -412,7 +414,7 @@ static const u8 nw801_init_vga[] = { 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01, 0, 0, 0, }; -static const u8 nw801_init_2[] = { +static const u8 nw801_start_2[] = { 0x10, 0x04, 0x01, 0x1a, 0x10, 0x19, 0x01, 0x09, /* clock */ 0x10, 0x24, 0x06, 0xc0, 0x00, 0x3f, 0x02, 0x00, 0x01, @@ -441,7 +443,7 @@ static const u8 nw801_init_2[] = { }; /* nw802 (sharp IR3Y38M?) */ -static const u8 nw802_init[] = { +static const u8 nw802_start[] = { 0x04, 0x06, 0x01, 0x04, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x4d, 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, @@ -524,7 +526,6 @@ static const u8 nw802_init[] = { }; /* et31x110 - Trust 120 SpaceCam */ static const u8 spacecam_init[] = { -/*fixme: at connection time*/ 0x04, 0x05, 0x01, 0x01, 0x04, 0x04, 0x01, 0x01, 0x04, 0x06, 0x01, 0x04, @@ -617,13 +618,8 @@ static const u8 spacecam_init[] = { 0x04, 0x04, 0x01, 0x40, 0, 0, 0 }; -/* et31x110 - other Trust SpaceCam120 */ -static const u8 spacecam2_init[] = { - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x01, - 0x04, 0x06, 0x01, 0x04, - 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, - 0x05, 0x05, 0x01, 0x00, +/* et31x110 - pas106 - other Trust SpaceCam120 */ +static const u8 spacecam2_start[] = { 0x04, 0x06, 0x01, 0x44, 0x04, 0x06, 0x01, 0x00, 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, @@ -682,79 +678,25 @@ static const u8 spacecam2_init[] = { 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, 0x04, 0x04, 0x01, 0x40, 0x04, 0x04, 0x01, 0x00, -#if 1 I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x05, I2C0, 0x40, 0x02, 0x11, 0x06, I2C0, 0x40, 0x02, 0x14, 0x00, - I2C0, 0x40, 0x02, 0x13, 0x01, -#else - 0x06, 0x00, 0x0b, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x05, 0x05, - 0x06, 0x00, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x05, 0x05, - 0x05, 0x02, 0x02, 0x0c, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x06, - 0x06, 0x00, 0x02, 0x11, 0x06, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x00, - 0x06, 0x00, 0x02, 0x14, 0x00, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x13, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif + I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, 0x40, -#if 1 - I2C0, 0x40, 0x02, 0x02, 0x0c, + I2C0, 0x40, 0x02, 0x02, 0x0c, /* pixel clock */ I2C0, 0x40, 0x02, 0x0f, 0x00, - I2C0, 0x40, 0x02, 0x13, 0x01, -#else - 0x06, 0x00, 0x01, 0x0c, - 0x06, 0x00, 0x02, 0x02, 0x0c, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x00, - 0x06, 0x00, 0x02, 0x0f, 0x00, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x13, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif + I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ 0x10, 0x00, 0x01, 0x01, 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, 0x01, 0x60, 0x01, -#if 1 - I2C0, 0x40, 0x02, 0x05, 0x0f, - I2C0, 0x40, 0x02, 0x13, 0x01, + I2C0, 0x40, 0x02, 0x05, 0x0f, /* exposure */ + I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ I2C0, 0x40, 0x07, 0x09, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00, + /* gains */ I2C0, 0x40, 0x03, 0x12, 0x04, 0x01, -#else - 0x06, 0x00, 0x01, 0x0f, - 0x06, 0x00, 0x02, 0x05, 0x0f, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x13, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x06, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00, - 0x06, 0x00, 0x07, 0x09, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00, - 0x05, 0x02, 0x02, 0x07, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x04, 0x01, - 0x06, 0x00, 0x03, 0x12, 0x04, 0x01, - 0x05, 0x02, 0x02, 0x03, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, 0x10, 0x0e, 0x01, 0x08, 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, @@ -776,7 +718,7 @@ static const u8 spacecam2_init[] = { }; /* nw802 - Conceptronic Video Pro */ -static const u8 cvideopro_init[] = { +static const u8 cvideopro_start[] = { 0x04, 0x06, 0x01, 0x04, 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c, 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, @@ -856,10 +798,9 @@ static const u8 cvideopro_init[] = { }; /* nw802 - D-link dru-350c cam */ -static const u8 dlink_init[] = { +static const u8 dlink_start[] = { 0x04, 0x06, 0x01, 0x04, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d, -/* 0xf9, 0x02, = nb pixels per line */ 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4, @@ -878,7 +819,6 @@ static const u8 dlink_init[] = { 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11, 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94, -/* 0x19, v index of clamping pulse */ 0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00, 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, @@ -909,7 +849,6 @@ static const u8 dlink_init[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x82, 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01, 0xf0, 0x00, -/* 0x00, 0x03, 0x02, 0x92, 0x03, = nb of pixels per line */ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, 0x40, @@ -938,8 +877,10 @@ static const u8 dlink_init[] = { 0, 0, 0 }; -/* nw801 - Plustek Opticam 500U or ProLink DS3303u (Hitachi HD49322BF) */ -static const u8 ds330_init[] = { +/* 06a5:d001 - nw801 - Sony + * Plustek Opticam 500U or ProLink DS3303u (Hitachi HD49322BF) */ +/*fixme: 320x240 only*/ +static const u8 ds3303_start[] = { 0x05, 0x06, 0x01, 0x04, 0x00, 0x00, 0x40, 0x16, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e, 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, @@ -1021,8 +962,9 @@ static const u8 ds330_init[] = { 0, 0, 0 }; -/* 06a5:d001 - nw802 - Panasonic GP-KR651US (Philips TDA8786) */ -static const u8 kr651_init_1[] = { +/* 06a5:d001 - nw802 - Panasonic + * GP-KR651US (Philips TDA8786) */ +static const u8 kr651_start_1[] = { 0x04, 0x06, 0x01, 0x04, 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x48, 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, @@ -1075,7 +1017,7 @@ static const u8 kr651_init_1[] = { 0x01, 0xf0, 0x00, 0, 0, 0 }; -static const u8 kr651_init_qvga[] = { +static const u8 kr651_start_qvga[] = { 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, 0x40, @@ -1088,21 +1030,19 @@ static const u8 kr651_init_qvga[] = { 0x10, 0x1d, 0x02, 0x28, 0x01, 0, 0, 0 }; -static const u8 kr651_init_vga[] = { +static const u8 kr651_start_vga[] = { 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x30, 0x03, 0x01, 0x82, 0x82, 0x98, 0x80, 0x10, 0x1a, 0x01, 0x03, 0x10, 0x00, 0x01, 0xa0, 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, - 0x10, 0x1b, 0x02, 0x00, 0x00, 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01, - 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00, 0x10, 0x1d, 0x02, 0x68, 0x00, }; -static const u8 kr651_init_2[] = { +static const u8 kr651_start_2[] = { 0x10, 0x0e, 0x01, 0x08, 0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8, 0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, @@ -1122,7 +1062,7 @@ static const u8 kr651_init_2[] = { }; /* nw802 - iRez Kritter cam */ -static const u8 kritter_init[] = { +static const u8 kritter_start[] = { 0x04, 0x06, 0x01, 0x06, 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0x94, 0x03, 0x18, 0x00, 0x48, 0x0f, 0x1e, 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19, @@ -1202,7 +1142,7 @@ static const u8 kritter_init[] = { }; /* nw802 - Mustek Wcam 300 mini */ -static const u8 mustek_init[] = { +static const u8 mustek_start[] = { 0x04, 0x06, 0x01, 0x04, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d, 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, @@ -1337,7 +1277,7 @@ static const u8 proscope_init_1[] = { 0x01, 0xf0, 0x00, 0, 0, 0 }; -static const u8 proscope_init_qvga[] = { +static const u8 proscope_start_qvga[] = { 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78, 0x40, @@ -1350,7 +1290,7 @@ static const u8 proscope_init_qvga[] = { 0x10, 0x0e, 0x01, 0x10, 0, 0, 0 }; -static const u8 proscope_init_vga[] = { +static const u8 proscope_start_vga[] = { 0x00, 0x03, 0x02, 0xf9, 0x02, 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01, 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00, @@ -1368,7 +1308,7 @@ static const u8 proscope_init_vga[] = { 0x10, 0x03, 0x01, 0x00, 0, 0, 0 }; -static const u8 proscope_init_2[] = { +static const u8 proscope_start_2[] = { 0x10, 0x0f, 0x02, 0x0c, 0x0c, 0x10, 0x03, 0x01, 0x0c, 0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae, @@ -1383,14 +1323,8 @@ static const u8 proscope_init_2[] = { 0, 0, 0 }; -/* nw800 - Divio Chicony TwinkleCam */ -static const u8 twinkle_init[] = { - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x01, - 0x04, 0x06, 0x01, 0x04, - 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, - 0x05, 0x05, 0x01, 0x00, - +/* nw800 - hv7121b? (seems pas106) - Divio Chicony TwinkleCam */ +static const u8 twinkle_start[] = { 0x04, 0x06, 0x01, 0x44, 0x04, 0x06, 0x01, 0x00, 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, @@ -1448,107 +1382,32 @@ static const u8 twinkle_init[] = { 0x01, 0x60, 0x01, 0x00, 0x00, 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, -/*320 3f ef, 0x00 */ 0x04, 0x04, 0x01, 0x10, 0x04, 0x04, 0x01, 0x00, 0x04, 0x05, 0x01, 0x61, 0x04, 0x04, 0x01, 0x01, -#if 1 I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, I2C0, 0x40, 0x02, 0x11, 0x06, I2C0, 0x40, 0x02, 0x14, 0x00, - I2C0, 0x40, 0x02, 0x13, 0x01, + I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */ I2C0, 0x40, 0x02, 0x07, 0x01, -#else - 0x06, 0x00, 0x0b, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0a, - 0x06, 0x00, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, - 0x05, 0x02, 0x02, 0x0c, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x06, - 0x06, 0x00, 0x02, 0x11, 0x06, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x00, - 0x06, 0x00, 0x02, 0x14, 0x00, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x13, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x07, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00, -/* 320 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00, */ 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65, -/* 320 0x00, 0x78, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x5c, */ 0x40, -#if 1 I2C0, 0x40, 0x02, 0x02, 0x0c, I2C0, 0x40, 0x02, 0x13, 0x01, -#else - 0x06, 0x00, 0x01, 0x0c, - 0x06, 0x00, 0x02, 0x02, 0x0c, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x13, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif 0x10, 0x00, 0x01, 0x01, 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, 0x01, 0x60, 0x01, -/* 320 0xf0, 0x00, 0x40, 0x01, */ -#if 1 I2C0, 0x40, 0x02, 0x05, 0x0f, I2C0, 0x40, 0x02, 0x13, 0x01, I2C0, 0x40, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17, I2C0, 0x40, 0x03, 0x12, 0x00, 0x01, -#else - 0x06, 0x00, 0x01, 0x0f, - 0x06, 0x00, 0x02, 0x05, 0x0f, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x13, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x07, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17, - 0x06, 0x00, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17, - 0x05, 0x02, 0x02, 0x08, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x00, 0x01, - 0x06, 0x00, 0x03, 0x12, 0x00, 0x01, - 0x05, 0x02, 0x02, 0x03, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01, -/* 320 3f ef */ -#if 1 I2C0, 0x40, 0x02, 0x12, 0x00, I2C0, 0x40, 0x02, 0x0e, 0x00, I2C0, 0x40, 0x02, 0x11, 0x06, -#else - 0x06, 0x00, 0x01, 0x00, - 0x06, 0x00, 0x02, 0x12, 0x00, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x00, - 0x06, 0x00, 0x02, 0x0e, 0x00, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x06, - 0x06, 0x00, 0x02, 0x11, 0x06, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, 0xf9, @@ -1745,7 +1604,7 @@ static const u8 dsbc110_init[] = { }; /* nw802 dvc-v6 */ -static const u8 dvcv6_init[] = { +static const u8 dvcv6_start[] = { 0x04, 0x06, 0x01, 0x06, 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c, 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, @@ -1830,22 +1689,22 @@ static const u8 dvcv6_init[] = { 0x04, 0x04, 0x01, 0x00, }; -static const u8 *webcam_init[] = { +static const u8 *webcam_start[] = { [Generic800] = nw800_init, [SpaceCam] = spacecam_init, - [SpaceCam2] = spacecam2_init, - [Cvideopro] = cvideopro_init, - [Dlink350c] = dlink_init, - [DS3303u] = ds330_init, - [Kr651us] = kr651_init_1, - [Kritter] = kritter_init, - [Mustek300] = mustek_init, + [SpaceCam2] = spacecam2_start, + [Cvideopro] = cvideopro_start, + [Dlink350c] = dlink_start, + [DS3303u] = ds3303_start, + [Kr651us] = kr651_start_1, + [Kritter] = kritter_start, + [Mustek300] = mustek_start, [Proscope] = proscope_init_1, - [Twinkle] = twinkle_init, + [Twinkle] = twinkle_start, [DsbC110] = dsbc110_init, - [DvcV6] = dvcv6_init, - [P35u] = nw801_init_1, - [Generic802] = nw802_init, + [DvcV6] = dvcv6_start, + [P35u] = nw801_start_1, + [Generic802] = nw802_start, }; /* -- write a register -- */ @@ -1949,7 +1808,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, if (cmd[-3] != I2C0) reg_w(gspca_dev, reg, cmd, len); else - i2c_w(gspca_dev, reg & 0xff, cmd, len); + i2c_w(gspca_dev, reg, cmd, len); cmd += len; } } @@ -2092,8 +1951,8 @@ static int sd_init(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "Bridge nw80%d", sd->bridge); if (sd->bridge == BRIDGE_NW800) { - gspca_dev->cam.cam_mode = sif_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(sif_mode); + gspca_dev->cam.cam_mode = cif_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(cif_mode); } else { gspca_dev->cam.cam_mode = vga_mode; switch (sd->webcam) { @@ -2110,12 +1969,10 @@ static int sd_init(struct gspca_dev *gspca_dev) } switch (sd->webcam) { case P35u: -/* sd->ctrls[EXPOSURE].min = 0; - * sd->ctrls[EXPOSURE].max = 9; +/* sd->ctrls[EXPOSURE].max = 9; * sd->ctrls[EXPOSURE].def = 1; */ break; case Kr651us: -/* sd->ctrls[EXPOSURE].min = 0; */ sd->ctrls[EXPOSURE].max = 315; sd->ctrls[EXPOSURE].def = 150; break; @@ -2133,29 +1990,29 @@ static int sd_start(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; const u8 *cmd; - cmd = webcam_init[sd->webcam]; + cmd = webcam_start[sd->webcam]; reg_w_buf(gspca_dev, cmd); switch (sd->webcam) { case P35u: if (gspca_dev->width == 320) - reg_w_buf(gspca_dev, nw801_init_qvga); + reg_w_buf(gspca_dev, nw801_start_qvga); else - reg_w_buf(gspca_dev, nw801_init_vga); - reg_w_buf(gspca_dev, nw801_init_2); + reg_w_buf(gspca_dev, nw801_start_vga); + reg_w_buf(gspca_dev, nw801_start_2); break; case Kr651us: if (gspca_dev->width == 320) - reg_w_buf(gspca_dev, kr651_init_qvga); + reg_w_buf(gspca_dev, kr651_start_qvga); else - reg_w_buf(gspca_dev, kr651_init_vga); - reg_w_buf(gspca_dev, kr651_init_2); + reg_w_buf(gspca_dev, kr651_start_vga); + reg_w_buf(gspca_dev, kr651_start_2); break; case Proscope: if (gspca_dev->width == 320) - reg_w_buf(gspca_dev, proscope_init_qvga); + reg_w_buf(gspca_dev, proscope_start_qvga); else - reg_w_buf(gspca_dev, proscope_init_vga); - reg_w_buf(gspca_dev, proscope_init_2); + reg_w_buf(gspca_dev, proscope_start_vga); + reg_w_buf(gspca_dev, proscope_start_2); break; } -- cgit v0.10.2 From 94751d4b208281f8447a9baca39963b66dd6983f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Tue, 22 Mar 2011 05:37:18 -0300 Subject: [media] gspca - nw80x: The webcam dsb-c110 is the same as the twinkle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index 8317488..e5928b1 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -69,7 +69,6 @@ enum webcams { Mustek300, Proscope, Twinkle, - DsbC110, DvcV6, P35u, Generic802, @@ -1426,183 +1425,6 @@ static const u8 twinkle_start[] = { 0, 0, 0 }; -/* et31x110 DSB-C110 */ -static const u8 dsbc110_init[] = { - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x01, - 0x04, 0x06, 0x01, 0x04, - 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, - 0x05, 0x05, 0x01, 0x00, - - 0x04, 0x06, 0x01, 0x44, - 0x04, 0x06, 0x01, 0x00, - 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f, - 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc, - 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86, - 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e, - 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78, - 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46, - 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0, - 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e, - 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03, - 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04, - 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00, - 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0, - 0x40, 0x20, - 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, - 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x08, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, - 0x03, 0x00, 0x00, 0x10, 0x00, 0x20, 0x10, 0x06, - 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x00, 0x80, - 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, - 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, - 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, - 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, - 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, - 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, - 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, - 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, - 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, - 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20, - 0x01, 0x60, 0x01, 0x00, 0x00, - 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, - 0x04, 0x04, 0x01, 0x10, - 0x04, 0x04, 0x01, 0x00, - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x01, -#if 1 - I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, - I2C0, 0x40, 0x02, 0x11, 0x06, - I2C0, 0x40, 0x02, 0x14, 0x00, - I2C0, 0x40, 0x02, 0x13, 0x01, - I2C0, 0x40, 0x02, 0x07, 0x01, -#else - 0x06, 0x00, 0x0b, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0a, - 0x06, 0x00, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, - 0x05, 0x02, 0x02, 0x0c, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x06, - 0x06, 0x00, 0x02, 0x11, 0x06, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x00, - 0x06, 0x00, 0x02, 0x14, 0x00, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x13, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x07, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif - 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xe4, - 0x40, -#if 1 - I2C0, 0x40, 0x02, 0x02, 0x0c, - I2C0, 0x40, 0x02, 0x13, 0x01, -#else - 0x06, 0x00, 0x01, 0x1c, - 0x06, 0x00, 0x02, 0x02, 0x1c, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x13, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif - 0x10, 0x00, 0x01, 0x01, - 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, - 0xf0, 0x00, 0x40, 0x01, -#if 1 - I2C0, 0x40, 0x02, 0x05, 0x23, - I2C0, 0x40, 0x02, 0x13, 0x01, - I2C0, 0x40, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x00, - I2C0, 0x40, 0x03, 0x12, 0x00, 0x01, -#else - 0x06, 0x00, 0x01, 0x24, - 0x06, 0x00, 0x02, 0x05, 0x24, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x13, 0x01, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x07, 0x06, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x00, - 0x06, 0x00, 0x08, 0x08, 0x06, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x00, - 0x05, 0x02, 0x02, 0x08, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x02, 0x00, 0x01, - 0x06, 0x00, 0x03, 0x12, 0x00, 0x01, - 0x05, 0x02, 0x02, 0x03, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif - 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00, -#if 1 - I2C0, 0x40, 0x02, 0x12, 0x00, - I2C0, 0x40, 0x02, 0x0e, 0x00, - I2C0, 0x40, 0x02, 0x11, 0x06, -#else - 0x06, 0x00, 0x01, 0x00, - 0x06, 0x00, 0x02, 0x12, 0x00, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x00, - 0x06, 0x00, 0x02, 0x0e, 0x00, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, - 0x06, 0x00, 0x01, 0x06, - 0x06, 0x00, 0x02, 0x11, 0x06, - 0x05, 0x02, 0x02, 0x02, 0x40, - 0x05, 0x05, 0x01, 0x01, -#endif - 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, - 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, - 0xf9, - 0x10, 0x03, 0x01, 0x00, - 0x10, 0x0f, 0x02, 0x0c, 0x0c, - 0x10, 0x03, 0x01, 0x06, - 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7, - 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7, - 0xf9, - 0x10, 0x0b, 0x01, 0x14, - 0x10, 0x0d, 0x01, 0x10, - 0x10, 0x0c, 0x01, 0x1d, - 0x04, 0x06, 0x01, 0x03, - 0x04, 0x05, 0x01, 0x61, - 0x04, 0x04, 0x01, 0x41, -}; - /* nw802 dvc-v6 */ static const u8 dvcv6_start[] = { 0x04, 0x06, 0x01, 0x06, @@ -1701,7 +1523,6 @@ static const u8 *webcam_start[] = { [Mustek300] = mustek_start, [Proscope] = proscope_init_1, [Twinkle] = twinkle_start, - [DsbC110] = dsbc110_init, [DvcV6] = dvcv6_start, [P35u] = nw801_start_1, [Generic802] = nw802_start, @@ -2296,5 +2117,4 @@ MODULE_PARM_DESC(webcam, "8: Mustek Wcam 300 mini\n" "9: Scalar USB Microscope M2 (Proscope)\n" "10: Divio Chicony TwinkleCam\n" - "11: DSB-C110\n" - "12: DVC-V6\n"); + "11: DVC-V6\n"); -- cgit v0.10.2 From bad03ff53ae3d336b5ecc5356b4739d9a693fc3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Tue, 22 Mar 2011 05:46:24 -0300 Subject: [media] gspca - nw80x: Do some initialization at probe time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index e5928b1..f4c6f40 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -172,14 +172,14 @@ static const struct v4l2_pix_format vga_mode[] = { #define I2C0 0xff static const u8 nw800_init[] = { - 0x05, 0x00, 0x01, 0x55, - 0x10, 0x9b, 0x01, 0xaa, - 0x05, 0x02, 0x01, 0x02, - 0x06, 0x00, 0x02, 0x04, 0xd9, - 0x05, 0x05, 0x01, 0x00, - 0x05, 0x05, 0x01, 0x01, + 0x04, 0x05, 0x01, 0x61, + 0x04, 0x04, 0x01, 0x01, 0x04, 0x06, 0x01, 0x04, - + 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x05, 0x01, 0x00, + 0, 0, 0 +}; +static const u8 nw800_start[] = { 0x04, 0x06, 0x01, 0xc0, 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, @@ -530,8 +530,9 @@ static const u8 spacecam_init[] = { 0x04, 0x06, 0x01, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, 0x05, 0x05, 0x01, 0x00, -/*fixme: add 300ms delay?*/ -/*fixme: at capture start time*/ + 0, 0, 0 +}; +static const u8 spacecam_start[] = { 0x04, 0x06, 0x01, 0x44, 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f, 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19, @@ -1221,9 +1222,12 @@ static const u8 mustek_start[] = { }; /* nw802 - Scope USB Microscope M2 (ProScope) (Hitachi HD49322BF) */ -static const u8 proscope_init_1[] = { +static const u8 proscope_init[] = { 0x04, 0x05, 0x01, 0x21, 0x04, 0x04, 0x01, 0x01, + 0, 0, 0 +}; +static const u8 proscope_start_1[] = { 0x04, 0x06, 0x01, 0x04, 0x00, 0x00, 0x40, 0x10, 0x01, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x04, 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19, @@ -1512,8 +1516,8 @@ static const u8 dvcv6_start[] = { }; static const u8 *webcam_start[] = { - [Generic800] = nw800_init, - [SpaceCam] = spacecam_init, + [Generic800] = nw800_start, + [SpaceCam] = spacecam_start, [SpaceCam2] = spacecam2_start, [Cvideopro] = cvideopro_start, [Dlink350c] = dlink_start, @@ -1521,7 +1525,7 @@ static const u8 *webcam_start[] = { [Kr651us] = kr651_start_1, [Kritter] = kritter_start, [Mustek300] = mustek_start, - [Proscope] = proscope_init_1, + [Proscope] = proscope_start_1, [Twinkle] = twinkle_start, [DvcV6] = dvcv6_start, [P35u] = nw801_start_1, @@ -1720,21 +1724,6 @@ static void setautogain(struct gspca_dev *gspca_dev) } } -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if ((unsigned) webcam >= NWEBCAMS) - webcam = 0; - sd->webcam = webcam; - gspca_dev->cam.reverse_alts = 1; - gspca_dev->cam.ctrls = sd->ctrls; - sd->ag_cnt = -1; - return 0; -} - static int nw802_test_reg(struct gspca_dev *gspca_dev, u16 index, u8 value) @@ -1748,11 +1737,19 @@ static int nw802_test_reg(struct gspca_dev *gspca_dev, return gspca_dev->usb_buf[0] == value; } -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; + if ((unsigned) webcam >= NWEBCAMS) + webcam = 0; + sd->webcam = webcam; + gspca_dev->cam.reverse_alts = 1; + gspca_dev->cam.ctrls = sd->ctrls; + sd->ag_cnt = -1; + /* * Autodetect sequence inspired from some log. * We try to detect what registers exist or not. @@ -1801,7 +1798,35 @@ static int sd_init(struct gspca_dev *gspca_dev) gspca_dev->ctrl_dis = ~(1 << GAIN); break; } + return gspca_dev->usb_err; +} +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + case BRIDGE_NW800: + switch (sd->webcam) { + case SpaceCam: + reg_w_buf(gspca_dev, spacecam_init); + break; + default: + reg_w_buf(gspca_dev, nw800_init); + break; + } + break; + default: + switch (sd->webcam) { + case Mustek300: + case P35u: + case Proscope: + reg_w_buf(gspca_dev, proscope_init); + break; + } + break; + } return gspca_dev->usb_err; } -- cgit v0.10.2 From f12b44ff2d5ba8eb41210438589a64490ddfedec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Tue, 22 Mar 2011 05:53:21 -0300 Subject: [media] gspca - nw80x: Fix the gain, exposure and autogain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The autogain now uses common functions. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index f4c6f40..d2d356e 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -38,6 +38,8 @@ enum e_ctrl { NCTRLS /* number of controls */ }; +#define AUTOGAIN_DEF 1 + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -47,6 +49,8 @@ struct sd { u32 ae_res; s8 ag_cnt; #define AG_CNT_START 13 + u8 exp_too_low_cnt; + u8 exp_too_high_cnt; u8 bridge; u8 webcam; @@ -1638,12 +1642,12 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, } } -static int swap_6bits(int v) +static int swap_bits(int v) { int r, i; r = 0; - for (i = 0; i < 6; i++) { + for (i = 0; i < 8; i++) { r <<= 1; if (v & 1) r++; @@ -1657,20 +1661,17 @@ static void setgain(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val, v[2]; - val = sd->ctrls[GAIN].val >> 1; /* 0 - 63 -> 0 - 31 */ - reg_w(gspca_dev, 0x100e, &val, 1); /* AE Y gain */ - + val = sd->ctrls[GAIN].val; switch (sd->webcam) { case P35u: /* Note the control goes from 0-255 not 0-127, but anything above 127 just means amplifying noise */ - val = sd->ctrls[GAIN].val << 1; /* 0 - 63 -> 0 - 127 */ + val >>= 1; /* 0 - 255 -> 0 - 127 */ reg_w(gspca_dev, 0x1026, &val, 1); break; case Kr651us: - /* 0 - 63 -> 0 - 0x37 */ - val = (sd->ctrls[GAIN].val * 0x37) / 63; - val = swap_6bits(val); + /* 0 - 253 */ + val = swap_bits(val); v[0] = val << 3; v[1] = val >> 5; reg_w(gspca_dev, 0x101d, v, 2); /* SIF reg0/1 (AGC) */ @@ -1681,16 +1682,18 @@ static void setgain(struct gspca_dev *gspca_dev) static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + s16 val; u8 v[2]; + val = sd->ctrls[EXPOSURE].val; switch (sd->webcam) { case P35u: - v[0] = (sd->ctrls[EXPOSURE].val << 3) | 0x01; + v[0] = ((9 - val) << 3) | 0x01; reg_w(gspca_dev, 0x1019, v, 1); break; case Kr651us: - v[0] = sd->ctrls[EXPOSURE].val; - v[1] = sd->ctrls[EXPOSURE].val >> 8; + v[0] = val; + v[1] = val >> 8; reg_w(gspca_dev, 0x101b, v, 2); break; } @@ -1788,16 +1791,30 @@ static int sd_config(struct gspca_dev *gspca_dev, switch (sd->webcam) { case P35u: /* sd->ctrls[EXPOSURE].max = 9; - * sd->ctrls[EXPOSURE].def = 1; */ + * sd->ctrls[EXPOSURE].def = 9; */ + /* coarse expo auto gain function gain minimum, to avoid + * a large settings jump the first auto adjustment */ + sd->ctrls[GAIN].def = 255 / 5 * 2; break; + case Cvideopro: + case DvcV6: + case Kritter: + gspca_dev->ctrl_dis = (1 << GAIN) | (1 << AUTOGAIN); + /* fall thru */ case Kr651us: sd->ctrls[EXPOSURE].max = 315; sd->ctrls[EXPOSURE].def = 150; break; default: - gspca_dev->ctrl_dis = ~(1 << GAIN); + gspca_dev->ctrl_dis = (1 << GAIN) | (1 << EXPOSURE) + | (1 << AUTOGAIN); break; } + +#if AUTOGAIN_DEF + if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN))) + gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE); +#endif return gspca_dev->usb_err; } @@ -1865,6 +1882,8 @@ static int sd_start(struct gspca_dev *gspca_dev) setgain(gspca_dev); setexposure(gspca_dev); setautogain(gspca_dev); + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; return gspca_dev->usb_err; } @@ -1936,11 +1955,12 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) return gspca_dev->usb_err; } +#include "autogain_functions.h" + static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int luma; - int gain, shutter; if (sd->ag_cnt < 0) return; @@ -1954,76 +1974,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) + (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; luma /= sd->ae_res; - if (sd->webcam == P35u) { - u8 clock; - - if (luma > 92 && luma < 108) - return; /* fine */ - clock = sd->ctrls[EXPOSURE].val; - gain = sd->ctrls[GAIN].val; - if (luma < 100) { - if (luma < 70 && clock > 0) - clock--; - if (gain > 98 && clock > 0) - clock--; - if (gain <= 50) - gain += 3; - } else { - if (luma > 150 && clock < 9) - clock++; - if (gain < 12 && clock < 9) - clock++; - if (gain >= 5) - gain -= 3; - } - if (gain != sd->ctrls[GAIN].val) { - sd->ctrls[GAIN].val = gain; - setgain(gspca_dev); - } - if (clock != sd->ctrls[EXPOSURE].val) { - sd->ctrls[EXPOSURE].val = clock; - setexposure(gspca_dev); - } - return; - } - - /* kr651us */ - if (luma > 95 && luma < 105) - return; /* fine */ - gain = sd->ctrls[GAIN].val; - shutter = sd->ctrls[EXPOSURE].val; - if (luma < 100) { - if (shutter > 0) { - if (luma < 85 && shutter > 50) - shutter -= 50; - else - shutter--; - } else if (gain < 63) { - if (luma < 85 && gain < 53) - gain += 10; - else - gain++; - } - } else { - if (gain > 0) { - if (luma > 115 && gain > 10) - gain -= 10; - else - gain--; - } else if (shutter < 316) { /* max 0x13b */ - if (luma > 115 && shutter < 266) - shutter += 50; - else - shutter++; - } - } - if (gain != sd->ctrls[GAIN].val) { - sd->ctrls[GAIN].val = gain; - setgain(gspca_dev); - } - if (shutter != sd->ctrls[EXPOSURE].val) { - sd->ctrls[EXPOSURE].val = shutter; - setexposure(gspca_dev); + switch (sd->webcam) { + case P35u: + coarse_grained_expo_autogain(gspca_dev, luma, 100, 5); + break; + default: + auto_gain_n_exposure(gspca_dev, luma, 100, 5, 230, 0); + break; } } @@ -2035,9 +1992,9 @@ static const struct ctrl sd_ctrls[NCTRLS] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", .minimum = 0, - .maximum = 63, + .maximum = 253, .step = 1, - .default_value = 16 + .default_value = 128 }, .set_control = setgain }, @@ -2049,7 +2006,7 @@ static const struct ctrl sd_ctrls[NCTRLS] = { .minimum = 0, .maximum = 9, .step = 1, - .default_value = 1 + .default_value = 9 }, .set_control = setexposure }, @@ -2061,7 +2018,7 @@ static const struct ctrl sd_ctrls[NCTRLS] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 1, + .default_value = AUTOGAIN_DEF, .flags = V4L2_CTRL_FLAG_UPDATE }, .set = sd_setautogain -- cgit v0.10.2 From ee32ffbf8e446e9b8c247ae9c9ba032979c3eafc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Tue, 22 Mar 2011 05:58:19 -0300 Subject: [media] gspca - nw80x: Check the bridge from the webcam type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index d2d356e..f2adf6f 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -79,75 +79,80 @@ enum webcams { NWEBCAMS /* number of webcams */ }; +static const u8 webcam_chip[NWEBCAMS] = { + [Generic800] = BRIDGE_NW800, /* 06a5:0000 + * Typhoon Webcam 100 USB */ + + [SpaceCam] = BRIDGE_NW800, /* 06a5:d800 + * Trust SpaceCam120 or SpaceCam100 PORTABLE */ + + [SpaceCam2] = BRIDGE_NW800, /* 06a5:d800 - pas106 + * other Trust SpaceCam120 or SpaceCam100 PORTABLE */ + + [Cvideopro] = BRIDGE_NW802, /* 06a5:d001 + * Conceptronic Video Pro 'CVIDEOPRO USB Webcam CCD' */ + + [Dlink350c] = BRIDGE_NW802, /* 06a5:d001 + * D-Link NetQam Pro 250plus */ + + [DS3303u] = BRIDGE_NW801, /* 06a5:d001 + * Plustek Opticam 500U or ProLink DS3303u */ + + [Kr651us] = BRIDGE_NW802, /* 06a5:d001 + * Panasonic GP-KR651US */ + + [Kritter] = BRIDGE_NW802, /* 06a5:d001 + * iRez Kritter cam */ + + [Mustek300] = BRIDGE_NW802, /* 055f:d001 + * Mustek Wcam 300 mini */ + + [Proscope] = BRIDGE_NW802, /* 06a5:d001 + * Scalar USB Microscope (ProScope) */ + + [Twinkle] = BRIDGE_NW800, /* 06a5:d800 - hv7121b? (seems pas106) + * Divio Chicony TwinkleCam + * DSB-C110 */ + + [DvcV6] = BRIDGE_NW802, /* 0502:d001 + * DVC V6 */ + + [P35u] = BRIDGE_NW801, /* 052b:d001, 06a5:d001 and 06be:d001 + * EZCam Pro p35u */ + + [Generic802] = BRIDGE_NW802, +}; +/* + * other webcams: + * - nw801 046d:d001 + * Logitech QuickCam Pro (dark focus ring) + * - nw801 0728:d001 + * AVerMedia Camguard + * - nw??? 06a5:d001 + * D-Link NetQam Pro 250plus + * - nw800 065a:d800 + * Showcam NGS webcam + * - nw??? ????:???? + * Sceptre svc300 + */ + /* - - webcams: - - Typhoon Webcam 100 USB (06a5:0000) - nw800 - - Trust SpaceCam120 or SpaceCam100 PORTABLE (06a5:d800) - nw801 SpaceCam.init - or trust_space.init if no LED (?) - - Divio Chicony TwinkleCam (06a5:d800) ? - nw800 Twinkle.init - - Plustek Opticam 500U or ProLink DS3303u - nw801 DS3303u.init - - Logitech QuickCam Pro (dark focus ring) (046d:d001) - nw801 - - EZCam Pro p35u (052b:d001, 06a5:d001 and 06be:d001) - nw801 - sensor Sharp IR3Y38M - - AVerMedia Camguard (0728:d001) - nw801 - - Panasonic GP-KR651US (06a5:d001) - nw802 kr651us.init - - iRez Kritter cam - nw802 kritter.init - - D-link dru-350c cam - nw802 d-link-350c.init - - The Scope USB Microscope M2 (ProScope) - = Divio ProLink DS3303u WebCam (06a5:d001) - = Scalar USB Microscope M2 (Proscope) - nw802 proscope.init - - Conceptronic Video Pro 'CVIDEOPRO USB Webcam CCD' (06a5:d001) - nw802 cvideopro.init - - Mustek Wcam 300 mini - nw802 mustek_300_mini.init - - D-Link NetQam Pro 250plus (06a5:d001) - - Showcam NGS webcam (065a:d800) - - sceptre svc300 - - DSB-C110 (06a5:d800) - et31x110 - - DVC V6 (0502:d001) - nw802 - - registers - nw800/et31x110 nw801 nw802 - 0000..009e 0000..00a1 0000..009e - 0200..0211 id id - 0300..0302 id id - 0400..0406 (inex) 0400..0406 - 0500..0505 0500..0506 (inex) - 0600..061a 0600..0601 0600..0601 - 0800..0814 id id - 1000..109c 1000..10a1 1000..109a - - 080c: luma (nw800/nw802) - 080d: luma (nw801) - 1004: LUT (?) - 100b: R gain (0..63) - 100c: B gain - 100d: G gain - 100e: Y gain - 100f: U gain - 1010: V gain - 1019: clock (nw801 - bit 0x08: indoor/outdoor) - 101b: shutter 1 (0..255) - 101c: shutter 2 - 1026: BP = gain (nw801) - 1041, 1052, 1063, 1074: LUT base (nw802) - 1048, 1059, 106a, 107b: LUT base (nw801) - - resolutions - nw800 352x288 - nw801/nw802 320x240 - 640x480 -*/ + * registers + * nw800/et31x110 nw801 nw802 + * 0000..009e 0000..00a1 0000..009e + * 0200..0211 id id + * 0300..0302 id id + * 0400..0406 (inex) 0400..0406 + * 0500..0505 0500..0506 (inex) + * 0600..061a 0600..0601 0600..0601 + * 0800..0814 id id + * 1000..109c 1000..10a1 1000..109a + */ +/* resolutions + * nw800: 320x240, 352x288 + * nw801/802: 320x240, 640x480 + */ static const struct v4l2_pix_format cif_mode[] = { {352, 288, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, .bytesperline = 352, @@ -1769,7 +1774,12 @@ static int sd_config(struct gspca_dev *gspca_dev, if (sd->webcam == Generic800) sd->webcam = P35u; } - PDEBUG(D_PROBE, "Bridge nw80%d", sd->bridge); + if (webcam_chip[sd->webcam] != sd->bridge) { + err("Bad webcam type %d for NW80%d", sd->webcam, sd->bridge); + gspca_dev->usb_err = -ENODEV; + return gspca_dev->usb_err; + } + PDEBUG(D_PROBE, "Bridge nw80%d - type: %d", sd->bridge, sd->webcam); if (sd->bridge == BRIDGE_NW800) { gspca_dev->cam.cam_mode = cif_mode; -- cgit v0.10.2 From 152507144a59d74423a25e1ba80f4f7f17449417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Tue, 22 Mar 2011 06:04:09 -0300 Subject: [media] gspca - nw80x: Fix some image resolutions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No 352x288 for Ds3303 and no 320x240 for Generic802. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index f2adf6f..cfab897 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -154,6 +154,10 @@ static const u8 webcam_chip[NWEBCAMS] = { * nw801/802: 320x240, 640x480 */ static const struct v4l2_pix_format cif_mode[] = { + {320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8, + .colorspace = V4L2_COLORSPACE_JPEG}, {352, 288, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE, .bytesperline = 352, .sizeimage = 352 * 288 * 4 / 8, @@ -1782,12 +1786,18 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Bridge nw80%d - type: %d", sd->bridge, sd->webcam); if (sd->bridge == BRIDGE_NW800) { - gspca_dev->cam.cam_mode = cif_mode; - gspca_dev->cam.nmodes = ARRAY_SIZE(cif_mode); + switch (sd->webcam) { + case DS3303u: + gspca_dev->cam.cam_mode = cif_mode; /* qvga */ + break; + default: + gspca_dev->cam.cam_mode = &cif_mode[1]; /* cif */ + break; + } + gspca_dev->cam.nmodes = 1; } else { gspca_dev->cam.cam_mode = vga_mode; switch (sd->webcam) { - case Generic802: case Kr651us: case Proscope: case P35u: -- cgit v0.10.2 From 088fc390bd2d6af547043b4579bf52d1c163c702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Tue, 22 Mar 2011 06:07:40 -0300 Subject: [media] gspca - nw80x: Get the sensor ID when bridge et31x110 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index cfab897..b6d7c8d 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -1768,6 +1768,8 @@ static int sd_config(struct gspca_dev *gspca_dev, * If 0x0500 does not exist => NW802 * If it does, test 0x109b. If it doesn't exist, * then it's a NW801. Else, a NW800 + * If a et31x110 (nw800 and 06a5:d800) + * get the sensor ID */ if (!nw802_test_reg(gspca_dev, 0x0500, 0x55)) { sd->bridge = BRIDGE_NW802; @@ -1777,6 +1779,24 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->bridge = BRIDGE_NW801; if (sd->webcam == Generic800) sd->webcam = P35u; + } else if (id->idVendor == 0x06a5 && id->idProduct == 0xd800) { + reg_r(gspca_dev, 0x0403, 1); /* GPIO */ + PDEBUG(D_PROBE, "et31x110 sensor type %02x", + gspca_dev->usb_buf[0]); + switch (gspca_dev->usb_buf[0] >> 1) { + case 0x00: /* ?? */ + if (sd->webcam == Generic800) + sd->webcam = SpaceCam; + break; + case 0x01: /* Hynix? */ + if (sd->webcam == Generic800) + sd->webcam = Twinkle; + break; + case 0x0a: /* Pixart */ + if (sd->webcam == Generic800) + sd->webcam = SpaceCam2; + break; + } } if (webcam_chip[sd->webcam] != sd->bridge) { err("Bad webcam type %d for NW80%d", sd->webcam, sd->bridge); -- cgit v0.10.2 From 8e3c08bb41145bd8507397f63937759500c12a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Tue, 22 Mar 2011 06:09:49 -0300 Subject: [media] gspca - nw80x: Fix exposure for some webcams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index b6d7c8d..8e754fd 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -1700,6 +1700,9 @@ static void setexposure(struct gspca_dev *gspca_dev) v[0] = ((9 - val) << 3) | 0x01; reg_w(gspca_dev, 0x1019, v, 1); break; + case Cvideopro: + case DvcV6: + case Kritter: case Kr651us: v[0] = val; v[1] = val >> 8; -- cgit v0.10.2 From 7106225a9e7d123ff87a6848972b6d28c8903940 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Mon, 21 Mar 2011 16:58:56 -0300 Subject: [media] gspca - main: Add endpoint direction test in alt_xfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes a bug in gspca, more precisely in alt_xfer(). This function looks for an input transfer endpoint in an alternate setting. By default it returns the first endpoint corresponding to the transfer type indicated in parameter. But with some USB devices, the first endpoint corresponding to the transfer type is not always an INPUT endpoint but an OUTPUT one. This patch adds the endpoint direction test to be sure to return an INPUT endpoint Signed-off-by: Patrice CHOTARD Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 6288174..9c6a643 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -630,7 +630,8 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, ep = &alt->endpoint[i]; attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; if (attr == xfer - && ep->desc.wMaxPacketSize != 0) + && ep->desc.wMaxPacketSize != 0 + && usb_endpoint_dir_in(&ep->desc)) return ep; } return NULL; -- cgit v0.10.2 From 590f21680616a78f7d6972b92ceb540e306452c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Tue, 22 Mar 2011 06:47:56 -0300 Subject: [media] gspca - zc3xx: Add exposure control for sensor hv7131r MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 6dcb1cf..fa164e8 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -39,6 +39,7 @@ static int force_sensor = -1; enum e_ctrl { BRIGHTNESS, CONTRAST, + EXPOSURE, GAMMA, AUTOGAIN, LIGHTFREQ, @@ -46,6 +47,8 @@ enum e_ctrl { NCTRLS /* number of controls */ }; +#define AUTOGAIN_DEF 1 + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -92,7 +95,8 @@ enum sensors { /* V4L2 controls supported by the driver */ static void setcontrast(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 setlightfreq(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); @@ -121,6 +125,18 @@ static const struct ctrl sd_ctrls[NCTRLS] = { }, .set_control = setcontrast }, +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0x30d, + .maximum = 0x493e, + .step = 1, + .default_value = 0x927 + }, + .set_control = setexposure + }, [GAMMA] = { { .id = V4L2_CID_GAMMA, @@ -141,9 +157,10 @@ static const struct ctrl sd_ctrls[NCTRLS] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 1, + .default_value = AUTOGAIN_DEF, + .flags = V4L2_CTRL_FLAG_UPDATE }, - .set_control = setautogain + .set = sd_setautogain }, [LIGHTFREQ] = { { @@ -5926,6 +5943,26 @@ static void setcontrast(struct gspca_dev *gspca_dev) reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */ } +static void getexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9) + | (i2c_read(gspca_dev, 0x26) << 1) + | (i2c_read(gspca_dev, 0x27) >> 7); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; + + val = sd->ctrls[EXPOSURE].val; + i2c_write(gspca_dev, 0x25, val >> 9, 0x00); + i2c_write(gspca_dev, 0x26, val >> 1, 0x00); + i2c_write(gspca_dev, 0x27, val << 7, 0x00); +} + static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -6623,10 +6660,19 @@ static int sd_init(struct gspca_dev *gspca_dev) sd->ctrls[GAMMA].def = gamma[sd->sensor]; switch (sd->sensor) { + case SENSOR_HV7131R: + break; case SENSOR_OV7630C: - gspca_dev->ctrl_dis = (1 << LIGHTFREQ); + gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE); + break; + default: + gspca_dev->ctrl_dis = (1 << EXPOSURE); break; } +#if AUTOGAIN_DEF + if (sd->ctrls[AUTOGAIN].val) + gspca_dev->ctrl_inac = (1 << EXPOSURE); +#endif /* switch off the led */ reg_w(gspca_dev, 0x01, 0x0000); @@ -6772,9 +6818,8 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x40, 0x0117); break; case SENSOR_HV7131R: - i2c_write(gspca_dev, 0x25, 0x04, 0x00); /* exposure */ - i2c_write(gspca_dev, 0x26, 0x93, 0x00); - i2c_write(gspca_dev, 0x27, 0xe0, 0x00); + if (!sd->ctrls[AUTOGAIN].val) + setexposure(gspca_dev); reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN); break; case SENSOR_GC0305: @@ -6852,6 +6897,23 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->ctrls[AUTOGAIN].val = val; + if (val) { + gspca_dev->ctrl_inac |= (1 << EXPOSURE); + } else { + gspca_dev->ctrl_inac &= ~(1 << EXPOSURE); + if (gspca_dev->streaming) + getexposure(gspca_dev); + } + if (gspca_dev->streaming) + setautogain(gspca_dev); + return gspca_dev->usb_err; +} + static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { -- cgit v0.10.2 From b50b3a1acd22a07e354a154e5d00a9d338446b77 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 6 Mar 2011 13:37:00 -0300 Subject: [media] STV0288 added full frontend status status now returns FE_HAS_CARRIER FE_HAS_SIGNAL FE_HAS_VITERBI Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c index 63db8fd..e3fe17f 100644 --- a/drivers/media/dvb/frontends/stv0288.c +++ b/drivers/media/dvb/frontends/stv0288.c @@ -367,8 +367,11 @@ static int stv0288_read_status(struct dvb_frontend *fe, fe_status_t *status) dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync); *status = 0; - - if ((sync & 0x08) == 0x08) { + if (sync & 0x80) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + if (sync & 0x10) + *status |= FE_HAS_VITERBI; + if (sync & 0x08) { *status |= FE_HAS_LOCK; dprintk("stv0288 has locked\n"); } -- cgit v0.10.2 From 3e507c55918b51fed30ae5e9a3667c6a5f2e8755 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 10 Mar 2011 19:41:29 -0300 Subject: [media] saa7134: Fix strange kconfig dependency on RC_CORE As the code in saa7134-input is not a module, but the config for it is set as a boolean instead of a tristate, this causes a strange dependency on RC_CORE. VIDEO_SAA7134_RC (which determines if saa7134-input.o is built) depends on RC_CORE and VIDEO_SAA7134. If VIDEO_SAA7134 is compiled as 'y' but RC_CORE is compiled as 'm' VIDEO_SAA7134_RC can still be set to 'y' which causes undefined symbols that it needs from RC_CORE. The simplest solution is to not allow VIDEO_SAA7134_RC be enabled if RC_CORE compiled as a module (m) and VIDEO_SA7134 is compiled into the kernel (y). Suggested-by: Mauro Carvalho Chehab Cc: Michal Marek Cc: linux-kbuild Signed-off-by: Steven Rostedt Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 380f1b2..39fc018 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -28,6 +28,7 @@ config VIDEO_SAA7134_RC bool "Philips SAA7134 Remote Controller support" depends on RC_CORE depends on VIDEO_SAA7134 + depends on !(RC_CORE=m && VIDEO_SAA7134=y) default y ---help--- Enables Remote Controller support on saa7134 driver. -- cgit v0.10.2 From baf075eca42f217e8d297914ed6fecfd2452a0e4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Mar 2011 15:03:09 -0300 Subject: [media] drivers/media/rc/Kconfig: use tabs, instead of spaces Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 3785162..1143845 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -161,20 +161,20 @@ config IR_STREAMZAP module will be called streamzap. config IR_WINBOND_CIR - tristate "Winbond IR remote control" - depends on X86 && PNP + tristate "Winbond IR remote control" + depends on X86 && PNP depends on RC_CORE - select NEW_LEDS - select LEDS_CLASS - select LEDS_TRIGGERS - select BITREVERSE - ---help--- - Say Y here if you want to use the IR remote functionality found - in some Winbond SuperI/O chips. Currently only the WPCD376I - chip is supported (included in some Intel Media series + select NEW_LEDS + select LEDS_CLASS + select LEDS_TRIGGERS + select BITREVERSE + ---help--- + Say Y here if you want to use the IR remote functionality found + in some Winbond SuperI/O chips. Currently only the WPCD376I + chip is supported (included in some Intel Media series motherboards). - To compile this driver as a module, choose M here: the module will + To compile this driver as a module, choose M here: the module will be called winbond_cir. config RC_LOOPBACK -- cgit v0.10.2 From 620a32bba4a2dd57ccf1d5f87b9e6d2402ffec22 Mon Sep 17 00:00:00 2001 From: "Juan J. Garcia de Soria" Date: Wed, 16 Mar 2011 17:14:52 -0300 Subject: [media] rc: New rc-based ite-cir driver for several ITE CIRs This is a second version of an rc-core based driver for the ITE Tech IT8712F CIR and now for a pair of other variants of the IT8512 CIR too. This driver should replace the lirc_it87 and lirc_ite8709 currently living in the LIRC staging directory. The driver should support the ITE8704, ITE8713, ITE8708 and ITE8709 (this last one yet untested) PNP ID's. The code doesn'te reuse code from the pre-existing LIRC drivers, but has been written from scratch using the nuvoton.cir driver as a skeleton. This new driver shouldn't exhibit timing problems when running under load (or with interrupts disabled for relatively long times). It works OOTB with the RC6 MCE remote bundled with the ASUS EEEBox. TX support is implemented, but I'm unable to test it since my hardware lacks TX capability. Signed-off-by: Juan J. Garcia de Soria Tested-by: Stephan Raue Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 1143845..7f03142 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -135,6 +135,19 @@ config IR_MCEUSB To compile this driver as a module, choose M here: the module will be called mceusb. +config IR_ITE_CIR + tristate "ITE Tech Inc. IT8712/IT8512 Consumer Infrared Transceiver" + depends on PNP + depends on RC_CORE + ---help--- + Say Y here to enable support for integrated infrared receivers + /transceivers made by ITE Tech Inc. These are found in + several ASUS devices, like the ASUS Digimatrix or the ASUS + EEEBox 1501U. + + To compile this driver as a module, choose M here: the + module will be called ite-cir. + config IR_NUVOTON tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" depends on PNP diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 67b4f7f..c6cfe70 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o # stand-alone IR receivers/transmitters obj-$(CONFIG_IR_IMON) += imon.o +obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o obj-$(CONFIG_IR_MCEUSB) += mceusb.o obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o obj-$(CONFIG_IR_ENE) += ene_ir.o diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c new file mode 100644 index 0000000..10358d5 --- /dev/null +++ b/drivers/media/rc/ite-cir.c @@ -0,0 +1,1734 @@ +/* + * Driver for ITE Tech Inc. IT8712F/IT8512 CIR + * + * Copyright (C) 2010 Juan Jesús García de Soria + * + * 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. + * + * Inspired by the original lirc_it87 and lirc_ite8709 drivers, on top of the + * skeleton provided by the nuvoton-cir driver. + * + * The lirc_it87 driver was originally written by Hans-Gunter Lutke Uphues + * in 2001, with enhancements by Christoph Bartelmus + * , Andrew Calkin and James Edwards + * . + * + * The lirc_ite8709 driver was written by Grégory Lardière + * in 2008. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ite-cir.h" + +/* module parameters */ + +/* debug level */ +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging output"); + +/* low limit for RX carrier freq, Hz, 0 for no RX demodulation */ +static int rx_low_carrier_freq; +module_param(rx_low_carrier_freq, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(rx_low_carrier_freq, "Override low RX carrier frequency, Hz, " + "0 for no RX demodulation"); + +/* high limit for RX carrier freq, Hz, 0 for no RX demodulation */ +static int rx_high_carrier_freq; +module_param(rx_high_carrier_freq, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(rx_high_carrier_freq, "Override high RX carrier frequency, " + "Hz, 0 for no RX demodulation"); + +/* override tx carrier frequency */ +static int tx_carrier_freq; +module_param(tx_carrier_freq, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(tx_carrier_freq, "Override TX carrier frequency, Hz"); + +/* override tx duty cycle */ +static int tx_duty_cycle; +module_param(tx_duty_cycle, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(tx_duty_cycle, "Override TX duty cycle, 1-100"); + +/* override default sample period */ +static long sample_period; +module_param(sample_period, long, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(sample_period, "Override carrier sample period, us"); + +/* override detected model id */ +static int model_number = -1; +module_param(model_number, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(model_number, "Use this model number, don't autodetect"); + + +/* HW-independent code functions */ + +/* check whether carrier frequency is high frequency */ +static inline bool ite_is_high_carrier_freq(unsigned int freq) +{ + return freq >= ITE_HCF_MIN_CARRIER_FREQ; +} + +/* get the bits required to program the carrier frequency in CFQ bits, + * unshifted */ +static u8 ite_get_carrier_freq_bits(unsigned int freq) +{ + if (ite_is_high_carrier_freq(freq)) { + if (freq < 425000) + return ITE_CFQ_400; + + else if (freq < 465000) + return ITE_CFQ_450; + + else if (freq < 490000) + return ITE_CFQ_480; + + else + return ITE_CFQ_500; + } else { + /* trim to limits */ + if (freq < ITE_LCF_MIN_CARRIER_FREQ) + freq = ITE_LCF_MIN_CARRIER_FREQ; + if (freq > ITE_LCF_MAX_CARRIER_FREQ) + freq = ITE_LCF_MAX_CARRIER_FREQ; + + /* convert to kHz and subtract the base freq */ + freq = + DIV_ROUND_CLOSEST(freq - ITE_LCF_MIN_CARRIER_FREQ, + 1000); + + return (u8) freq; + } +} + +/* get the bits required to program the pulse with in TXMPW */ +static u8 ite_get_pulse_width_bits(unsigned int freq, int duty_cycle) +{ + unsigned long period_ns, on_ns; + + /* sanitize freq into range */ + if (freq < ITE_LCF_MIN_CARRIER_FREQ) + freq = ITE_LCF_MIN_CARRIER_FREQ; + if (freq > ITE_HCF_MAX_CARRIER_FREQ) + freq = ITE_HCF_MAX_CARRIER_FREQ; + + period_ns = 1000000000UL / freq; + on_ns = period_ns * duty_cycle / 100; + + if (ite_is_high_carrier_freq(freq)) { + if (on_ns < 750) + return ITE_TXMPW_A; + + else if (on_ns < 850) + return ITE_TXMPW_B; + + else if (on_ns < 950) + return ITE_TXMPW_C; + + else if (on_ns < 1080) + return ITE_TXMPW_D; + + else + return ITE_TXMPW_E; + } else { + if (on_ns < 6500) + return ITE_TXMPW_A; + + else if (on_ns < 7850) + return ITE_TXMPW_B; + + else if (on_ns < 9650) + return ITE_TXMPW_C; + + else if (on_ns < 11950) + return ITE_TXMPW_D; + + else + return ITE_TXMPW_E; + } +} + +/* decode raw bytes as received by the hardware, and push them to the ir-core + * layer */ +static void ite_decode_bytes(struct ite_dev *dev, const u8 * data, int + length) +{ + u32 sample_period; + unsigned long *ldata; + unsigned int next_one, next_zero, size; + DEFINE_IR_RAW_EVENT(ev); + + if (length == 0) + return; + + sample_period = dev->params.sample_period; + ldata = (unsigned long *)data; + size = length << 3; + next_one = generic_find_next_le_bit(ldata, size, 0); + if (next_one > 0) { + ev.pulse = true; + ev.duration = + ITE_BITS_TO_NS(next_one, sample_period); + ir_raw_event_store_with_filter(dev->rdev, &ev); + } + + while (next_one < size) { + next_zero = generic_find_next_zero_le_bit(ldata, size, next_one + 1); + ev.pulse = false; + ev.duration = ITE_BITS_TO_NS(next_zero - next_one, sample_period); + ir_raw_event_store_with_filter(dev->rdev, &ev); + + if (next_zero < size) { + next_one = + generic_find_next_le_bit(ldata, + size, + next_zero + 1); + ev.pulse = true; + ev.duration = + ITE_BITS_TO_NS(next_one - next_zero, + sample_period); + ir_raw_event_store_with_filter + (dev->rdev, &ev); + } else + next_one = size; + } + + ir_raw_event_handle(dev->rdev); + + ite_dbg_verbose("decoded %d bytes.", length); +} + +/* set all the rx/tx carrier parameters; this must be called with the device + * spinlock held */ +static void ite_set_carrier_params(struct ite_dev *dev) +{ + unsigned int freq, low_freq, high_freq; + int allowance; + bool use_demodulator; + bool for_tx = dev->transmitting; + + ite_dbg("%s called", __func__); + + if (for_tx) { + /* we don't need no stinking calculations */ + freq = dev->params.tx_carrier_freq; + allowance = ITE_RXDCR_DEFAULT; + use_demodulator = false; + } else { + low_freq = dev->params.rx_low_carrier_freq; + high_freq = dev->params.rx_high_carrier_freq; + + if (low_freq == 0) { + /* don't demodulate */ + freq = + ITE_DEFAULT_CARRIER_FREQ; + allowance = ITE_RXDCR_DEFAULT; + use_demodulator = false; + } else { + /* calculate the middle freq */ + freq = (low_freq + high_freq) / 2; + + /* calculate the allowance */ + allowance = + DIV_ROUND_CLOSEST(10000 * (high_freq - low_freq), + ITE_RXDCR_PER_10000_STEP + * (high_freq + low_freq)); + + if (allowance < 1) + allowance = 1; + + if (allowance > ITE_RXDCR_MAX) + allowance = ITE_RXDCR_MAX; + } + } + + /* set the carrier parameters in a device-dependent way */ + dev->params.set_carrier_params(dev, ite_is_high_carrier_freq(freq), + use_demodulator, ite_get_carrier_freq_bits(freq), allowance, + ite_get_pulse_width_bits(freq, dev->params.tx_duty_cycle)); +} + +/* interrupt service routine for incoming and outgoing CIR data */ +static irqreturn_t ite_cir_isr(int irq, void *data) +{ + struct ite_dev *dev = data; + unsigned long flags; + irqreturn_t ret = IRQ_RETVAL(IRQ_NONE); + u8 rx_buf[ITE_RX_FIFO_LEN]; + int rx_bytes; + int iflags; + + ite_dbg_verbose("%s firing", __func__); + + /* grab the spinlock */ + spin_lock_irqsave(&dev->lock, flags); + + /* read the interrupt flags */ + iflags = dev->params.get_irq_causes(dev); + + /* check for the receive interrupt */ + if (iflags & (ITE_IRQ_RX_FIFO | ITE_IRQ_RX_FIFO_OVERRUN)) { + /* read the FIFO bytes */ + rx_bytes = + dev->params.get_rx_bytes(dev, rx_buf, + ITE_RX_FIFO_LEN); + + if (rx_bytes > 0) { + /* drop the spinlock, since the ir-core layer + * may call us back again through + * ite_s_idle() */ + spin_unlock_irqrestore(&dev-> + lock, + flags); + + /* decode the data we've just received */ + ite_decode_bytes(dev, rx_buf, + rx_bytes); + + /* reacquire the spinlock */ + spin_lock_irqsave(&dev->lock, + flags); + + /* mark the interrupt as serviced */ + ret = IRQ_RETVAL(IRQ_HANDLED); + } + } else if (iflags & ITE_IRQ_TX_FIFO) { + /* FIFO space available interrupt */ + ite_dbg_verbose("got interrupt for TX FIFO"); + + /* wake any sleeping transmitter */ + wake_up_interruptible(&dev->tx_queue); + + /* mark the interrupt as serviced */ + ret = IRQ_RETVAL(IRQ_HANDLED); + } + + /* drop the spinlock */ + spin_unlock_irqrestore(&dev->lock, flags); + + ite_dbg_verbose("%s done returning %d", __func__, (int)ret); + + return ret; +} + +/* set the rx carrier freq range, guess it's in Hz... */ +static int ite_set_rx_carrier_range(struct rc_dev *rcdev, u32 carrier_low, u32 + carrier_high) +{ + unsigned long flags; + struct ite_dev *dev = rcdev->priv; + + spin_lock_irqsave(&dev->lock, flags); + dev->params.rx_low_carrier_freq = carrier_low; + dev->params.rx_high_carrier_freq = carrier_high; + ite_set_carrier_params(dev); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* set the tx carrier freq, guess it's in Hz... */ +static int ite_set_tx_carrier(struct rc_dev *rcdev, u32 carrier) +{ + unsigned long flags; + struct ite_dev *dev = rcdev->priv; + + spin_lock_irqsave(&dev->lock, flags); + dev->params.tx_carrier_freq = carrier; + ite_set_carrier_params(dev); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* set the tx duty cycle by controlling the pulse width */ +static int ite_set_tx_duty_cycle(struct rc_dev *rcdev, u32 duty_cycle) +{ + unsigned long flags; + struct ite_dev *dev = rcdev->priv; + + spin_lock_irqsave(&dev->lock, flags); + dev->params.tx_duty_cycle = duty_cycle; + ite_set_carrier_params(dev); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* transmit out IR pulses; what you get here is a batch of alternating + * pulse/space/pulse/space lengths that we should write out completely through + * the FIFO, blocking on a full FIFO */ +static int ite_tx_ir(struct rc_dev *rcdev, int *txbuf, u32 n) +{ + unsigned long flags; + struct ite_dev *dev = rcdev->priv; + bool is_pulse = false; + int remaining_us, fifo_avail, fifo_remaining, last_idx = 0; + int max_rle_us, next_rle_us; + int ret = n; + u8 last_sent[ITE_TX_FIFO_LEN]; + u8 val; + + ite_dbg("%s called", __func__); + + /* clear the array just in case */ + memset(last_sent, 0, ARRAY_SIZE(last_sent)); + + /* n comes in bytes; convert to ints */ + n /= sizeof(int); + + spin_lock_irqsave(&dev->lock, flags); + + /* let everybody know we're now transmitting */ + dev->transmitting = true; + + /* and set the carrier values for transmission */ + ite_set_carrier_params(dev); + + /* calculate how much time we can send in one byte */ + max_rle_us = + (ITE_BAUDRATE_DIVISOR * dev->params.sample_period * + ITE_TX_MAX_RLE) / 1000; + + /* disable the receiver */ + dev->params.disable_rx(dev); + + /* this is where we'll begin filling in the FIFO, until it's full. + * then we'll just activate the interrupt, wait for it to wake us up + * again, disable it, continue filling the FIFO... until everything + * has been pushed out */ + fifo_avail = + ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev); + + while (n > 0 && dev->in_use) { + /* transmit the next sample */ + is_pulse = !is_pulse; + remaining_us = *(txbuf++); + n--; + + ite_dbg("%s: %ld", + ((is_pulse) ? "pulse" : "space"), + (long int) + remaining_us); + + /* repeat while the pulse is non-zero length */ + while (remaining_us > 0 && dev->in_use) { + if (remaining_us > max_rle_us) + next_rle_us = max_rle_us; + + else + next_rle_us = remaining_us; + + remaining_us -= next_rle_us; + + /* check what's the length we have to pump out */ + val = (ITE_TX_MAX_RLE * next_rle_us) / max_rle_us; + + /* put it into the sent buffer */ + last_sent[last_idx++] = val; + last_idx &= (ITE_TX_FIFO_LEN); + + /* encode it for 7 bits */ + val = (val - 1) & ITE_TX_RLE_MASK; + + /* take into account pulse/space prefix */ + if (is_pulse) + val |= ITE_TX_PULSE; + + else + val |= ITE_TX_SPACE; + + /* if we get to 0 available, read again, just in case + * some other slot got freed */ + if (fifo_avail <= 0) + fifo_avail = ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev); + + /* if it's still full */ + if (fifo_avail <= 0) { + /* enable the tx interrupt */ + dev->params. + enable_tx_interrupt(dev); + + /* drop the spinlock */ + spin_unlock_irqrestore(&dev->lock, flags); + + /* wait for the FIFO to empty enough */ + wait_event_interruptible(dev->tx_queue, (fifo_avail = ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev)) >= 8); + + /* get the spinlock again */ + spin_lock_irqsave(&dev->lock, flags); + + /* disable the tx interrupt again. */ + dev->params. + disable_tx_interrupt(dev); + } + + /* now send the byte through the FIFO */ + dev->params.put_tx_byte(dev, val); + fifo_avail--; + } + } + + /* wait and don't return until the whole FIFO has been sent out; + * otherwise we could configure the RX carrier params instead of the + * TX ones while the transmission is still being performed! */ + fifo_remaining = dev->params.get_tx_used_slots(dev); + remaining_us = 0; + while (fifo_remaining > 0) { + fifo_remaining--; + last_idx--; + last_idx &= (ITE_TX_FIFO_LEN - 1); + remaining_us += last_sent[last_idx]; + } + remaining_us = (remaining_us * max_rle_us) / (ITE_TX_MAX_RLE); + + /* drop the spinlock while we sleep */ + spin_unlock_irqrestore(&dev->lock, flags); + + /* sleep remaining_us microseconds */ + mdelay(DIV_ROUND_UP(remaining_us, 1000)); + + /* reacquire the spinlock */ + spin_lock_irqsave(&dev->lock, flags); + + /* now we're not transmitting anymore */ + dev->transmitting = false; + + /* and set the carrier values for reception */ + ite_set_carrier_params(dev); + + /* reenable the receiver */ + if (dev->in_use) + dev->params.enable_rx(dev); + + /* notify transmission end */ + wake_up_interruptible(&dev->tx_ended); + + spin_unlock_irqrestore(&dev->lock, flags); + + return ret; +} + +/* idle the receiver if needed */ +static void ite_s_idle(struct rc_dev *rcdev, bool enable) +{ + unsigned long flags; + struct ite_dev *dev = rcdev->priv; + + ite_dbg("%s called", __func__); + + if (enable) { + spin_lock_irqsave(&dev->lock, flags); + dev->params.idle_rx(dev); + spin_unlock_irqrestore(&dev->lock, flags); + } +} + + +/* IT8712F HW-specific functions */ + +/* retrieve a bitmask of the current causes for a pending interrupt; this may + * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN + * */ +static int it87_get_irq_causes(struct ite_dev *dev) +{ + u8 iflags; + int ret = 0; + + ite_dbg("%s called", __func__); + + /* read the interrupt flags */ + iflags = inb(dev->cir_addr + IT87_IIR) & IT87_II; + + switch (iflags) { + case IT87_II_RXDS: + ret = ITE_IRQ_RX_FIFO; + break; + case IT87_II_RXFO: + ret = ITE_IRQ_RX_FIFO_OVERRUN; + break; + case IT87_II_TXLDL: + ret = ITE_IRQ_TX_FIFO; + break; + } + + return ret; +} + +/* set the carrier parameters; to be called with the spinlock held */ +static void it87_set_carrier_params(struct ite_dev *dev, bool high_freq, + bool use_demodulator, + u8 carrier_freq_bits, u8 allowance_bits, + u8 pulse_width_bits) +{ + u8 val; + + ite_dbg("%s called", __func__); + + /* program the RCR register */ + val = inb(dev->cir_addr + IT87_RCR) + & ~(IT87_HCFS | IT87_RXEND | IT87_RXDCR); + + if (high_freq) + val |= IT87_HCFS; + + if (use_demodulator) + val |= IT87_RXEND; + + val |= allowance_bits; + + outb(val, dev->cir_addr + IT87_RCR); + + /* program the TCR2 register */ + outb((carrier_freq_bits << IT87_CFQ_SHIFT) | pulse_width_bits, + dev->cir_addr + IT87_TCR2); +} + +/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock + * held */ +static int it87_get_rx_bytes(struct ite_dev *dev, u8 * buf, int buf_size) +{ + int fifo, read = 0; + + ite_dbg("%s called", __func__); + + /* read how many bytes are still in the FIFO */ + fifo = inb(dev->cir_addr + IT87_RSR) & IT87_RXFBC; + + while (fifo > 0 && buf_size > 0) { + *(buf++) = inb(dev->cir_addr + IT87_DR); + fifo--; + read++; + buf_size--; + } + + return read; +} + +/* return how many bytes are still in the FIFO; this will be called + * with the device spinlock NOT HELD while waiting for the TX FIFO to get + * empty; let's expect this won't be a problem */ +static int it87_get_tx_used_slots(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + return inb(dev->cir_addr + IT87_TSR) & IT87_TXFBC; +} + +/* put a byte to the TX fifo; this should be called with the spinlock held */ +static void it87_put_tx_byte(struct ite_dev *dev, u8 value) +{ + outb(value, dev->cir_addr + IT87_DR); +} + +/* idle the receiver so that we won't receive samples until another + pulse is detected; this must be called with the device spinlock held */ +static void it87_idle_rx(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable streaming by clearing RXACT writing it as 1 */ + outb(inb(dev->cir_addr + IT87_RCR) | IT87_RXACT, + dev->cir_addr + IT87_RCR); + + /* clear the FIFO */ + outb(inb(dev->cir_addr + IT87_TCR1) | IT87_FIFOCLR, + dev->cir_addr + IT87_TCR1); +} + +/* disable the receiver; this must be called with the device spinlock held */ +static void it87_disable_rx(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable the receiver interrupts */ + outb(inb(dev->cir_addr + IT87_IER) & ~(IT87_RDAIE | IT87_RFOIE), + dev->cir_addr + IT87_IER); + + /* disable the receiver */ + outb(inb(dev->cir_addr + IT87_RCR) & ~IT87_RXEN, + dev->cir_addr + IT87_RCR); + + /* clear the FIFO and RXACT (actually RXACT should have been cleared + * in the previous outb() call) */ + it87_idle_rx(dev); +} + +/* enable the receiver; this must be called with the device spinlock held */ +static void it87_enable_rx(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* enable the receiver by setting RXEN */ + outb(inb(dev->cir_addr + IT87_RCR) | IT87_RXEN, + dev->cir_addr + IT87_RCR); + + /* just prepare it to idle for the next reception */ + it87_idle_rx(dev); + + /* enable the receiver interrupts and master enable flag */ + outb(inb(dev->cir_addr + IT87_IER) | IT87_RDAIE | IT87_RFOIE | IT87_IEC, + dev->cir_addr + IT87_IER); +} + +/* disable the transmitter interrupt; this must be called with the device + * spinlock held */ +static void it87_disable_tx_interrupt(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable the transmitter interrupts */ + outb(inb(dev->cir_addr + IT87_IER) & ~IT87_TLDLIE, + dev->cir_addr + IT87_IER); +} + +/* enable the transmitter interrupt; this must be called with the device + * spinlock held */ +static void it87_enable_tx_interrupt(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* enable the transmitter interrupts and master enable flag */ + outb(inb(dev->cir_addr + IT87_IER) | IT87_TLDLIE | IT87_IEC, + dev->cir_addr + IT87_IER); +} + +/* disable the device; this must be called with the device spinlock held */ +static void it87_disable(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* clear out all interrupt enable flags */ + outb(inb(dev->cir_addr + IT87_IER) & + ~(IT87_IEC | IT87_RFOIE | IT87_RDAIE | IT87_TLDLIE), + dev->cir_addr + IT87_IER); + + /* disable the receiver */ + it87_disable_rx(dev); + + /* erase the FIFO */ + outb(IT87_FIFOCLR | inb(dev->cir_addr + IT87_TCR1), + dev->cir_addr + IT87_TCR1); +} + +/* initialize the hardware */ +static void it87_init_hardware(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* enable just the baud rate divisor register, + disabling all the interrupts at the same time */ + outb((inb(dev->cir_addr + IT87_IER) & + ~(IT87_IEC | IT87_RFOIE | IT87_RDAIE | IT87_TLDLIE)) | IT87_BR, + dev->cir_addr + IT87_IER); + + /* write out the baud rate divisor */ + outb(ITE_BAUDRATE_DIVISOR & 0xff, dev->cir_addr + IT87_BDLR); + outb((ITE_BAUDRATE_DIVISOR >> 8) & 0xff, dev->cir_addr + IT87_BDHR); + + /* disable the baud rate divisor register again */ + outb(inb(dev->cir_addr + IT87_IER) & ~IT87_BR, + dev->cir_addr + IT87_IER); + + /* program the RCR register defaults */ + outb(ITE_RXDCR_DEFAULT, dev->cir_addr + IT87_RCR); + + /* program the TCR1 register */ + outb(IT87_TXMPM_DEFAULT | IT87_TXENDF | IT87_TXRLE + | IT87_FIFOTL_DEFAULT | IT87_FIFOCLR, + dev->cir_addr + IT87_TCR1); + + /* program the carrier parameters */ + ite_set_carrier_params(dev); +} + +/* IT8512F on ITE8708 HW-specific functions */ + +/* retrieve a bitmask of the current causes for a pending interrupt; this may + * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN + * */ +static int it8708_get_irq_causes(struct ite_dev *dev) +{ + u8 iflags; + int ret = 0; + + ite_dbg("%s called", __func__); + + /* read the interrupt flags */ + iflags = inb(dev->cir_addr + IT8708_C0IIR); + + if (iflags & IT85_TLDLI) + ret |= ITE_IRQ_TX_FIFO; + if (iflags & IT85_RDAI) + ret |= ITE_IRQ_RX_FIFO; + if (iflags & IT85_RFOI) + ret |= ITE_IRQ_RX_FIFO_OVERRUN; + + return ret; +} + +/* set the carrier parameters; to be called with the spinlock held */ +static void it8708_set_carrier_params(struct ite_dev *dev, bool high_freq, + bool use_demodulator, + u8 carrier_freq_bits, u8 allowance_bits, + u8 pulse_width_bits) +{ + u8 val; + + ite_dbg("%s called", __func__); + + /* program the C0CFR register, with HRAE=1 */ + outb(inb(dev->cir_addr + IT8708_BANKSEL) | IT8708_HRAE, + dev->cir_addr + IT8708_BANKSEL); + + val = (inb(dev->cir_addr + IT8708_C0CFR) + & ~(IT85_HCFS | IT85_CFQ)) | carrier_freq_bits; + + if (high_freq) + val |= IT85_HCFS; + + outb(val, dev->cir_addr + IT8708_C0CFR); + + outb(inb(dev->cir_addr + IT8708_BANKSEL) & ~IT8708_HRAE, + dev->cir_addr + IT8708_BANKSEL); + + /* program the C0RCR register */ + val = inb(dev->cir_addr + IT8708_C0RCR) + & ~(IT85_RXEND | IT85_RXDCR); + + if (use_demodulator) + val |= IT85_RXEND; + + val |= allowance_bits; + + outb(val, dev->cir_addr + IT8708_C0RCR); + + /* program the C0TCR register */ + val = inb(dev->cir_addr + IT8708_C0TCR) & ~IT85_TXMPW; + val |= pulse_width_bits; + outb(val, dev->cir_addr + IT8708_C0TCR); +} + +/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock + * held */ +static int it8708_get_rx_bytes(struct ite_dev *dev, u8 * buf, int buf_size) +{ + int fifo, read = 0; + + ite_dbg("%s called", __func__); + + /* read how many bytes are still in the FIFO */ + fifo = inb(dev->cir_addr + IT8708_C0RFSR) & IT85_RXFBC; + + while (fifo > 0 && buf_size > 0) { + *(buf++) = inb(dev->cir_addr + IT8708_C0DR); + fifo--; + read++; + buf_size--; + } + + return read; +} + +/* return how many bytes are still in the FIFO; this will be called + * with the device spinlock NOT HELD while waiting for the TX FIFO to get + * empty; let's expect this won't be a problem */ +static int it8708_get_tx_used_slots(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + return inb(dev->cir_addr + IT8708_C0TFSR) & IT85_TXFBC; +} + +/* put a byte to the TX fifo; this should be called with the spinlock held */ +static void it8708_put_tx_byte(struct ite_dev *dev, u8 value) +{ + outb(value, dev->cir_addr + IT8708_C0DR); +} + +/* idle the receiver so that we won't receive samples until another + pulse is detected; this must be called with the device spinlock held */ +static void it8708_idle_rx(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable streaming by clearing RXACT writing it as 1 */ + outb(inb(dev->cir_addr + IT8708_C0RCR) | IT85_RXACT, + dev->cir_addr + IT8708_C0RCR); + + /* clear the FIFO */ + outb(inb(dev->cir_addr + IT8708_C0MSTCR) | IT85_FIFOCLR, + dev->cir_addr + IT8708_C0MSTCR); +} + +/* disable the receiver; this must be called with the device spinlock held */ +static void it8708_disable_rx(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable the receiver interrupts */ + outb(inb(dev->cir_addr + IT8708_C0IER) & + ~(IT85_RDAIE | IT85_RFOIE), + dev->cir_addr + IT8708_C0IER); + + /* disable the receiver */ + outb(inb(dev->cir_addr + IT8708_C0RCR) & ~IT85_RXEN, + dev->cir_addr + IT8708_C0RCR); + + /* clear the FIFO and RXACT (actually RXACT should have been cleared + * in the previous outb() call) */ + it8708_idle_rx(dev); +} + +/* enable the receiver; this must be called with the device spinlock held */ +static void it8708_enable_rx(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* enable the receiver by setting RXEN */ + outb(inb(dev->cir_addr + IT8708_C0RCR) | IT85_RXEN, + dev->cir_addr + IT8708_C0RCR); + + /* just prepare it to idle for the next reception */ + it8708_idle_rx(dev); + + /* enable the receiver interrupts and master enable flag */ + outb(inb(dev->cir_addr + IT8708_C0IER) + |IT85_RDAIE | IT85_RFOIE | IT85_IEC, + dev->cir_addr + IT8708_C0IER); +} + +/* disable the transmitter interrupt; this must be called with the device + * spinlock held */ +static void it8708_disable_tx_interrupt(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable the transmitter interrupts */ + outb(inb(dev->cir_addr + IT8708_C0IER) & ~IT85_TLDLIE, + dev->cir_addr + IT8708_C0IER); +} + +/* enable the transmitter interrupt; this must be called with the device + * spinlock held */ +static void it8708_enable_tx_interrupt(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* enable the transmitter interrupts and master enable flag */ + outb(inb(dev->cir_addr + IT8708_C0IER) + |IT85_TLDLIE | IT85_IEC, + dev->cir_addr + IT8708_C0IER); +} + +/* disable the device; this must be called with the device spinlock held */ +static void it8708_disable(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* clear out all interrupt enable flags */ + outb(inb(dev->cir_addr + IT8708_C0IER) & + ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE), + dev->cir_addr + IT8708_C0IER); + + /* disable the receiver */ + it8708_disable_rx(dev); + + /* erase the FIFO */ + outb(IT85_FIFOCLR | inb(dev->cir_addr + IT8708_C0MSTCR), + dev->cir_addr + IT8708_C0MSTCR); +} + +/* initialize the hardware */ +static void it8708_init_hardware(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable all the interrupts */ + outb(inb(dev->cir_addr + IT8708_C0IER) & + ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE), + dev->cir_addr + IT8708_C0IER); + + /* program the baud rate divisor */ + outb(inb(dev->cir_addr + IT8708_BANKSEL) | IT8708_HRAE, + dev->cir_addr + IT8708_BANKSEL); + + outb(ITE_BAUDRATE_DIVISOR & 0xff, dev->cir_addr + IT8708_C0BDLR); + outb((ITE_BAUDRATE_DIVISOR >> 8) & 0xff, + dev->cir_addr + IT8708_C0BDHR); + + outb(inb(dev->cir_addr + IT8708_BANKSEL) & ~IT8708_HRAE, + dev->cir_addr + IT8708_BANKSEL); + + /* program the C0MSTCR register defaults */ + outb((inb(dev->cir_addr + IT8708_C0MSTCR) & + ~(IT85_ILSEL | IT85_ILE | IT85_FIFOTL | + IT85_FIFOCLR | IT85_RESET)) | + IT85_FIFOTL_DEFAULT, + dev->cir_addr + IT8708_C0MSTCR); + + /* program the C0RCR register defaults */ + outb((inb(dev->cir_addr + IT8708_C0RCR) & + ~(IT85_RXEN | IT85_RDWOS | IT85_RXEND | + IT85_RXACT | IT85_RXDCR)) | + ITE_RXDCR_DEFAULT, + dev->cir_addr + IT8708_C0RCR); + + /* program the C0TCR register defaults */ + outb((inb(dev->cir_addr + IT8708_C0TCR) & + ~(IT85_TXMPM | IT85_TXMPW)) + |IT85_TXRLE | IT85_TXENDF | + IT85_TXMPM_DEFAULT | IT85_TXMPW_DEFAULT, + dev->cir_addr + IT8708_C0TCR); + + /* program the carrier parameters */ + ite_set_carrier_params(dev); +} + +/* IT8512F on ITE8709 HW-specific functions */ + +/* read a byte from the SRAM module */ +static inline u8 it8709_rm(struct ite_dev *dev, int index) +{ + outb(index, dev->cir_addr + IT8709_RAM_IDX); + return inb(dev->cir_addr + IT8709_RAM_VAL); +} + +/* write a byte to the SRAM module */ +static inline void it8709_wm(struct ite_dev *dev, u8 val, int index) +{ + outb(index, dev->cir_addr + IT8709_RAM_IDX); + outb(val, dev->cir_addr + IT8709_RAM_VAL); +} + +static void it8709_wait(struct ite_dev *dev) +{ + int i = 0; + /* + * loop until device tells it's ready to continue + * iterations count is usually ~750 but can sometimes achieve 13000 + */ + for (i = 0; i < 15000; i++) { + udelay(2); + if (it8709_rm(dev, IT8709_MODE) == IT8709_IDLE) + break; + } +} + +/* read the value of a CIR register */ +static u8 it8709_rr(struct ite_dev *dev, int index) +{ + /* just wait in case the previous access was a write */ + it8709_wait(dev); + it8709_wm(dev, index, IT8709_REG_IDX); + it8709_wm(dev, IT8709_READ, IT8709_MODE); + + /* wait for the read data to be available */ + it8709_wait(dev); + + /* return the read value */ + return it8709_rm(dev, IT8709_REG_VAL); +} + +/* write the value of a CIR register */ +static void it8709_wr(struct ite_dev *dev, u8 val, int index) +{ + /* we wait before writing, and not afterwards, since this allows us to + * pipeline the host CPU with the microcontroller */ + it8709_wait(dev); + it8709_wm(dev, val, IT8709_REG_VAL); + it8709_wm(dev, index, IT8709_REG_IDX); + it8709_wm(dev, IT8709_WRITE, IT8709_MODE); +} + +/* retrieve a bitmask of the current causes for a pending interrupt; this may + * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN + * */ +static int it8709_get_irq_causes(struct ite_dev *dev) +{ + u8 iflags; + int ret = 0; + + ite_dbg("%s called", __func__); + + /* read the interrupt flags */ + iflags = it8709_rm(dev, IT8709_IIR); + + if (iflags & IT85_TLDLI) + ret |= ITE_IRQ_TX_FIFO; + if (iflags & IT85_RDAI) + ret |= ITE_IRQ_RX_FIFO; + if (iflags & IT85_RFOI) + ret |= ITE_IRQ_RX_FIFO_OVERRUN; + + return ret; +} + +/* set the carrier parameters; to be called with the spinlock held */ +static void it8709_set_carrier_params(struct ite_dev *dev, bool high_freq, + bool use_demodulator, + u8 carrier_freq_bits, u8 allowance_bits, + u8 pulse_width_bits) +{ + u8 val; + + ite_dbg("%s called", __func__); + + val = (it8709_rr(dev, IT85_C0CFR) + &~(IT85_HCFS | IT85_CFQ)) | + carrier_freq_bits; + + if (high_freq) + val |= IT85_HCFS; + + it8709_wr(dev, val, IT85_C0CFR); + + /* program the C0RCR register */ + val = it8709_rr(dev, IT85_C0RCR) + & ~(IT85_RXEND | IT85_RXDCR); + + if (use_demodulator) + val |= IT85_RXEND; + + val |= allowance_bits; + + it8709_wr(dev, val, IT85_C0RCR); + + /* program the C0TCR register */ + val = it8709_rr(dev, IT85_C0TCR) & ~IT85_TXMPW; + val |= pulse_width_bits; + it8709_wr(dev, val, IT85_C0TCR); +} + +/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock + * held */ +static int it8709_get_rx_bytes(struct ite_dev *dev, u8 * buf, int buf_size) +{ + int fifo, read = 0; + + ite_dbg("%s called", __func__); + + /* read how many bytes are still in the FIFO */ + fifo = it8709_rm(dev, IT8709_RFSR) & IT85_RXFBC; + + while (fifo > 0 && buf_size > 0) { + *(buf++) = it8709_rm(dev, IT8709_FIFO + read); + fifo--; + read++; + buf_size--; + } + + /* 'clear' the FIFO by setting the writing index to 0; this is + * completely bound to be racy, but we can't help it, since it's a + * limitation of the protocol */ + it8709_wm(dev, 0, IT8709_RFSR); + + return read; +} + +/* return how many bytes are still in the FIFO; this will be called + * with the device spinlock NOT HELD while waiting for the TX FIFO to get + * empty; let's expect this won't be a problem */ +static int it8709_get_tx_used_slots(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + return it8709_rr(dev, IT85_C0TFSR) & IT85_TXFBC; +} + +/* put a byte to the TX fifo; this should be called with the spinlock held */ +static void it8709_put_tx_byte(struct ite_dev *dev, u8 value) +{ + it8709_wr(dev, value, IT85_C0DR); +} + +/* idle the receiver so that we won't receive samples until another + pulse is detected; this must be called with the device spinlock held */ +static void it8709_idle_rx(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable streaming by clearing RXACT writing it as 1 */ + it8709_wr(dev, it8709_rr(dev, IT85_C0RCR) | IT85_RXACT, + IT85_C0RCR); + + /* clear the FIFO */ + it8709_wr(dev, it8709_rr(dev, IT85_C0MSTCR) | IT85_FIFOCLR, + IT85_C0MSTCR); +} + +/* disable the receiver; this must be called with the device spinlock held */ +static void it8709_disable_rx(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable the receiver interrupts */ + it8709_wr(dev, it8709_rr(dev, IT85_C0IER) & + ~(IT85_RDAIE | IT85_RFOIE), + IT85_C0IER); + + /* disable the receiver */ + it8709_wr(dev, it8709_rr(dev, IT85_C0RCR) & ~IT85_RXEN, + IT85_C0RCR); + + /* clear the FIFO and RXACT (actually RXACT should have been cleared + * in the previous it8709_wr(dev, ) call) */ + it8709_idle_rx(dev); +} + +/* enable the receiver; this must be called with the device spinlock held */ +static void it8709_enable_rx(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* enable the receiver by setting RXEN */ + it8709_wr(dev, it8709_rr(dev, IT85_C0RCR) | IT85_RXEN, + IT85_C0RCR); + + /* just prepare it to idle for the next reception */ + it8709_idle_rx(dev); + + /* enable the receiver interrupts and master enable flag */ + it8709_wr(dev, it8709_rr(dev, IT85_C0IER) + |IT85_RDAIE | IT85_RFOIE | IT85_IEC, + IT85_C0IER); +} + +/* disable the transmitter interrupt; this must be called with the device + * spinlock held */ +static void it8709_disable_tx_interrupt(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable the transmitter interrupts */ + it8709_wr(dev, it8709_rr(dev, IT85_C0IER) & ~IT85_TLDLIE, + IT85_C0IER); +} + +/* enable the transmitter interrupt; this must be called with the device + * spinlock held */ +static void it8709_enable_tx_interrupt(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* enable the transmitter interrupts and master enable flag */ + it8709_wr(dev, it8709_rr(dev, IT85_C0IER) + |IT85_TLDLIE | IT85_IEC, + IT85_C0IER); +} + +/* disable the device; this must be called with the device spinlock held */ +static void it8709_disable(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* clear out all interrupt enable flags */ + it8709_wr(dev, + it8709_rr(dev, + IT85_C0IER) & ~(IT85_IEC | IT85_RFOIE | + IT85_RDAIE | + IT85_TLDLIE), IT85_C0IER); + + /* disable the receiver */ + it8709_disable_rx(dev); + + /* erase the FIFO */ + it8709_wr(dev, IT85_FIFOCLR | it8709_rr(dev, IT85_C0MSTCR), + IT85_C0MSTCR); +} + +/* initialize the hardware */ +static void it8709_init_hardware(struct ite_dev *dev) +{ + ite_dbg("%s called", __func__); + + /* disable all the interrupts */ + it8709_wr(dev, + it8709_rr(dev, + IT85_C0IER) & ~(IT85_IEC | IT85_RFOIE | + IT85_RDAIE | + IT85_TLDLIE), IT85_C0IER); + + /* program the baud rate divisor */ + it8709_wr(dev, ITE_BAUDRATE_DIVISOR & 0xff, IT85_C0BDLR); + it8709_wr(dev, (ITE_BAUDRATE_DIVISOR >> 8) & 0xff, + IT85_C0BDHR); + + /* program the C0MSTCR register defaults */ + it8709_wr(dev, (it8709_rr(dev, IT85_C0MSTCR) & ~(IT85_ILSEL | + IT85_ILE + | IT85_FIFOTL + | + IT85_FIFOCLR + | + IT85_RESET)) + | IT85_FIFOTL_DEFAULT, IT85_C0MSTCR); + + /* program the C0RCR register defaults */ + it8709_wr(dev, + (it8709_rr(dev, IT85_C0RCR) & + ~(IT85_RXEN | IT85_RDWOS | IT85_RXEND + | IT85_RXACT | IT85_RXDCR)) | + ITE_RXDCR_DEFAULT, IT85_C0RCR); + + /* program the C0TCR register defaults */ + it8709_wr(dev, (it8709_rr(dev, IT85_C0TCR) + &~(IT85_TXMPM | IT85_TXMPW)) + |IT85_TXRLE | IT85_TXENDF | + IT85_TXMPM_DEFAULT | + IT85_TXMPW_DEFAULT, IT85_C0TCR); + + /* program the carrier parameters */ + ite_set_carrier_params(dev); +} + + +/* generic hardware setup/teardown code */ + +/* activate the device for use */ +static int ite_open(struct rc_dev *rcdev) +{ + struct ite_dev *dev = rcdev->priv; + unsigned long flags; + + ite_dbg("%s called", __func__); + + spin_lock_irqsave(&dev->lock, flags); + dev->in_use = true; + + /* enable the receiver */ + dev->params.enable_rx(dev); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* deactivate the device for use */ +static void ite_close(struct rc_dev *rcdev) +{ + struct ite_dev *dev = rcdev->priv; + unsigned long flags; + + ite_dbg("%s called", __func__); + + spin_lock_irqsave(&dev->lock, flags); + dev->in_use = false; + + /* wait for any transmission to end */ + spin_unlock_irqrestore(&dev->lock, flags); + wait_event_interruptible(dev->tx_ended, !dev->transmitting); + spin_lock_irqsave(&dev->lock, flags); + + dev->params.disable(dev); + + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* supported models and their parameters */ +static const struct ite_dev_params ite_dev_descs[] = { + { /* 0: ITE8704 */ + .model = "ITE8704 CIR transceiver", + .io_region_size = IT87_IOREG_LENGTH, + .hw_tx_capable = true, + .sample_period = (u32) (1000000000ULL / 115200), + .tx_carrier_freq = 38000, + .tx_duty_cycle = 33, + .rx_low_carrier_freq = 0, + .rx_high_carrier_freq = 0, + + /* operations */ + .get_irq_causes = it87_get_irq_causes, + .enable_rx = it87_enable_rx, + .idle_rx = it87_idle_rx, + .disable_rx = it87_idle_rx, + .get_rx_bytes = it87_get_rx_bytes, + .enable_tx_interrupt = it87_enable_tx_interrupt, + .disable_tx_interrupt = it87_disable_tx_interrupt, + .get_tx_used_slots = it87_get_tx_used_slots, + .put_tx_byte = it87_put_tx_byte, + .disable = it87_disable, + .init_hardware = it87_init_hardware, + .set_carrier_params = it87_set_carrier_params, + }, + { /* 1: ITE8713 */ + .model = "ITE8713 CIR transceiver", + .io_region_size = IT87_IOREG_LENGTH, + .hw_tx_capable = true, + .sample_period = (u32) (1000000000ULL / 115200), + .tx_carrier_freq = 38000, + .tx_duty_cycle = 33, + .rx_low_carrier_freq = 0, + .rx_high_carrier_freq = 0, + + /* operations */ + .get_irq_causes = it87_get_irq_causes, + .enable_rx = it87_enable_rx, + .idle_rx = it87_idle_rx, + .disable_rx = it87_idle_rx, + .get_rx_bytes = it87_get_rx_bytes, + .enable_tx_interrupt = it87_enable_tx_interrupt, + .disable_tx_interrupt = it87_disable_tx_interrupt, + .get_tx_used_slots = it87_get_tx_used_slots, + .put_tx_byte = it87_put_tx_byte, + .disable = it87_disable, + .init_hardware = it87_init_hardware, + .set_carrier_params = it87_set_carrier_params, + }, + { /* 2: ITE8708 */ + .model = "ITE8708 CIR transceiver", + .io_region_size = IT8708_IOREG_LENGTH, + .hw_tx_capable = true, + .sample_period = (u32) (1000000000ULL / 115200), + .tx_carrier_freq = 38000, + .tx_duty_cycle = 33, + .rx_low_carrier_freq = 0, + .rx_high_carrier_freq = 0, + + /* operations */ + .get_irq_causes = it8708_get_irq_causes, + .enable_rx = it8708_enable_rx, + .idle_rx = it8708_idle_rx, + .disable_rx = it8708_idle_rx, + .get_rx_bytes = it8708_get_rx_bytes, + .enable_tx_interrupt = it8708_enable_tx_interrupt, + .disable_tx_interrupt = + it8708_disable_tx_interrupt, + .get_tx_used_slots = it8708_get_tx_used_slots, + .put_tx_byte = it8708_put_tx_byte, + .disable = it8708_disable, + .init_hardware = it8708_init_hardware, + .set_carrier_params = it8708_set_carrier_params, + }, + { /* 3: ITE8709 */ + .model = "ITE8709 CIR transceiver", + .io_region_size = IT8709_IOREG_LENGTH, + .hw_tx_capable = true, + .sample_period = (u32) (1000000000ULL / 115200), + .tx_carrier_freq = 38000, + .tx_duty_cycle = 33, + .rx_low_carrier_freq = 0, + .rx_high_carrier_freq = 0, + + /* operations */ + .get_irq_causes = it8709_get_irq_causes, + .enable_rx = it8709_enable_rx, + .idle_rx = it8709_idle_rx, + .disable_rx = it8709_idle_rx, + .get_rx_bytes = it8709_get_rx_bytes, + .enable_tx_interrupt = it8709_enable_tx_interrupt, + .disable_tx_interrupt = + it8709_disable_tx_interrupt, + .get_tx_used_slots = it8709_get_tx_used_slots, + .put_tx_byte = it8709_put_tx_byte, + .disable = it8709_disable, + .init_hardware = it8709_init_hardware, + .set_carrier_params = it8709_set_carrier_params, + }, +}; + +static const struct pnp_device_id ite_ids[] = { + {"ITE8704", 0}, /* Default model */ + {"ITE8713", 1}, /* CIR found in EEEBox 1501U */ + {"ITE8708", 2}, /* Bridged IT8512 */ + {"ITE8709", 3}, /* SRAM-Bridged IT8512 */ + {"", 0}, +}; + +/* allocate memory, probe hardware, and initialize everything */ +static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id + *dev_id) +{ + const struct ite_dev_params *dev_desc = NULL; + struct ite_dev *itdev = NULL; + struct rc_dev *rdev = NULL; + int ret = -ENOMEM; + int model_no; + + ite_dbg("%s called", __func__); + + itdev = kzalloc(sizeof(struct ite_dev), GFP_KERNEL); + if (!itdev) + return ret; + + /* input device for IR remote (and tx) */ + rdev = rc_allocate_device(); + if (!rdev) + goto failure; + + ret = -ENODEV; + + /* get the model number */ + model_no = (int)dev_id->driver_data; + ite_pr(KERN_NOTICE, "Auto-detected model: %s\n", + ite_dev_descs[model_no].model); + + if (model_number >= 0 && model_number < ARRAY_SIZE(ite_dev_descs)) { + model_no = model_number; + ite_pr(KERN_NOTICE, "The model has been fixed by a module " + "parameter."); + } + + ite_pr(KERN_NOTICE, "Using model: %s\n", ite_dev_descs[model_no].model); + + /* get the description for the device */ + dev_desc = &ite_dev_descs[model_no]; + + /* validate pnp resources */ + if (!pnp_port_valid(pdev, 0) || + pnp_port_len(pdev, 0) != dev_desc->io_region_size) { + dev_err(&pdev->dev, "IR PNP Port not valid!\n"); + goto failure; + } + + if (!pnp_irq_valid(pdev, 0)) { + dev_err(&pdev->dev, "PNP IRQ not valid!\n"); + goto failure; + } + + /* store resource values */ + itdev->cir_addr = pnp_port_start(pdev, 0); + itdev->cir_irq =pnp_irq(pdev, 0); + + /* initialize spinlocks */ + spin_lock_init(&itdev->lock); + + /* initialize raw event */ + init_ir_raw_event(&itdev->rawir); + + ret = -EBUSY; + /* now claim resources */ + if (!request_region(itdev->cir_addr, + dev_desc->io_region_size, ITE_DRIVER_NAME)) + goto failure; + + if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, + ITE_DRIVER_NAME, (void *)itdev)) + goto failure; + + /* set driver data into the pnp device */ + pnp_set_drvdata(pdev, itdev); + itdev->pdev = pdev; + + /* initialize waitqueues for transmission */ + init_waitqueue_head(&itdev->tx_queue); + init_waitqueue_head(&itdev->tx_ended); + + /* copy model-specific parameters */ + itdev->params = *dev_desc; + + /* apply any overrides */ + if (sample_period > 0) + itdev->params.sample_period = sample_period; + + if (tx_carrier_freq > 0) + itdev->params.tx_carrier_freq = tx_carrier_freq; + + if (tx_duty_cycle > 0 && tx_duty_cycle <= 100) + itdev->params.tx_duty_cycle = tx_duty_cycle; + + if (rx_low_carrier_freq > 0) + itdev->params.rx_low_carrier_freq = rx_low_carrier_freq; + + if (rx_high_carrier_freq > 0) + itdev->params.rx_high_carrier_freq = rx_high_carrier_freq; + + /* print out parameters */ + ite_pr(KERN_NOTICE, "TX-capable: %d\n", (int) + itdev->params.hw_tx_capable); + ite_pr(KERN_NOTICE, "Sample period (ns): %ld\n", (long) + itdev->params.sample_period); + ite_pr(KERN_NOTICE, "TX carrier frequency (Hz): %d\n", (int) + itdev->params.tx_carrier_freq); + ite_pr(KERN_NOTICE, "TX duty cycle (%%): %d\n", (int) + itdev->params.tx_duty_cycle); + ite_pr(KERN_NOTICE, "RX low carrier frequency (Hz): %d\n", (int) + itdev->params.rx_low_carrier_freq); + ite_pr(KERN_NOTICE, "RX high carrier frequency (Hz): %d\n", (int) + itdev->params.rx_high_carrier_freq); + + /* set up hardware initial state */ + itdev->params.init_hardware(itdev); + + /* set up ir-core props */ + rdev->priv = itdev; + rdev->driver_type = RC_DRIVER_IR_RAW; + rdev->allowed_protos = RC_TYPE_ALL; + rdev->open = ite_open; + rdev->close = ite_close; + rdev->s_idle = ite_s_idle; + rdev->s_rx_carrier_range = ite_set_rx_carrier_range; + rdev->min_timeout = ITE_MIN_IDLE_TIMEOUT; + rdev->max_timeout = ITE_MAX_IDLE_TIMEOUT; + rdev->timeout = ITE_IDLE_TIMEOUT; + rdev->rx_resolution = ITE_BAUDRATE_DIVISOR * + itdev->params.sample_period; + rdev->tx_resolution = ITE_BAUDRATE_DIVISOR * + itdev->params.sample_period; + + /* set up transmitter related values if needed */ + if (itdev->params.hw_tx_capable) { + rdev->tx_ir = ite_tx_ir; + rdev->s_tx_carrier = ite_set_tx_carrier; + rdev->s_tx_duty_cycle = ite_set_tx_duty_cycle; + } + + rdev->input_name = dev_desc->model; + rdev->input_id.bustype = BUS_HOST; + rdev->input_id.vendor = PCI_VENDOR_ID_ITE; + rdev->input_id.product = 0; + rdev->input_id.version = 0; + rdev->driver_name = ITE_DRIVER_NAME; + rdev->map_name = RC_MAP_RC6_MCE; + + ret = rc_register_device(rdev); + if (ret) + goto failure; + + itdev->rdev = rdev; + ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); + + return 0; + +failure: + if (itdev->cir_irq) + free_irq(itdev->cir_irq, itdev); + + if (itdev->cir_addr) + release_region(itdev->cir_addr, itdev->params.io_region_size); + + rc_free_device(rdev); + kfree(itdev); + + return ret; +} + +static void __devexit ite_remove(struct pnp_dev *pdev) +{ + struct ite_dev *dev = pnp_get_drvdata(pdev); + unsigned long flags; + + ite_dbg("%s called", __func__); + + spin_lock_irqsave(&dev->lock, flags); + + /* disable hardware */ + dev->params.disable(dev); + + spin_unlock_irqrestore(&dev->lock, flags); + + /* free resources */ + free_irq(dev->cir_irq, dev); + release_region(dev->cir_addr, dev->params.io_region_size); + + rc_unregister_device(dev->rdev); + + kfree(dev); +} + +static int ite_suspend(struct pnp_dev *pdev, pm_message_t state) +{ + struct ite_dev *dev = pnp_get_drvdata(pdev); + unsigned long flags; + + ite_dbg("%s called", __func__); + + spin_lock_irqsave(&dev->lock, flags); + + /* disable all interrupts */ + dev->params.disable(dev); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int ite_resume(struct pnp_dev *pdev) +{ + int ret = 0; + struct ite_dev *dev = pnp_get_drvdata(pdev); + unsigned long flags; + + ite_dbg("%s called", __func__); + + spin_lock_irqsave(&dev->lock, flags); + + if (dev->transmitting) { + /* wake up the transmitter */ + wake_up_interruptible(&dev->tx_queue); + } else { + /* enable the receiver */ + dev->params.enable_rx(dev); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return ret; +} + +static void ite_shutdown(struct pnp_dev *pdev) +{ + struct ite_dev *dev = pnp_get_drvdata(pdev); + unsigned long flags; + + ite_dbg("%s called", __func__); + + spin_lock_irqsave(&dev->lock, flags); + + /* disable all interrupts */ + dev->params.disable(dev); + + spin_unlock_irqrestore(&dev->lock, flags); +} + +static struct pnp_driver ite_driver = { + .name = ITE_DRIVER_NAME, + .id_table = ite_ids, + .probe = ite_probe, + .remove = __devexit_p(ite_remove), + .suspend = ite_suspend, + .resume = ite_resume, + .shutdown = ite_shutdown, +}; + +int ite_init(void) +{ + return pnp_register_driver(&ite_driver); +} + +void ite_exit(void) +{ + pnp_unregister_driver(&ite_driver); +} + +MODULE_DEVICE_TABLE(pnp, ite_ids); +MODULE_DESCRIPTION("ITE Tech Inc. IT8712F/ITE8512F CIR driver"); + +MODULE_AUTHOR("Juan J. Garcia de Soria "); +MODULE_LICENSE("GPL"); + +module_init(ite_init); +module_exit(ite_exit); diff --git a/drivers/media/rc/ite-cir.h b/drivers/media/rc/ite-cir.h new file mode 100644 index 0000000..43bac8d --- /dev/null +++ b/drivers/media/rc/ite-cir.h @@ -0,0 +1,478 @@ +/* + * Driver for ITE Tech Inc. IT8712F/IT8512F CIR + * + * Copyright (C) 2010 Juan Jesús García de Soria + * + * 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. + */ + +/* platform driver name to register */ +#define ITE_DRIVER_NAME "ite-cir" + +/* logging macros */ +#define ite_pr(level, text, ...) \ + printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__) +#define ite_dbg(text, ...) \ + if (debug) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) +#define ite_dbg_verbose(text, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + +/* FIFO sizes */ +#define ITE_TX_FIFO_LEN 32 +#define ITE_RX_FIFO_LEN 32 + +/* interrupt types */ +#define ITE_IRQ_TX_FIFO 1 +#define ITE_IRQ_RX_FIFO 2 +#define ITE_IRQ_RX_FIFO_OVERRUN 4 + +/* forward declaration */ +struct ite_dev; + +/* struct for storing the parameters of different recognized devices */ +struct ite_dev_params { + /* model of the device */ + const char *model; + + /* size of the I/O region */ + int io_region_size; + + /* true if the hardware supports transmission */ + bool hw_tx_capable; + + /* base sampling period, in ns */ + u32 sample_period; + + /* rx low carrier frequency, in Hz, 0 means no demodulation */ + unsigned int rx_low_carrier_freq; + + /* tx high carrier frequency, in Hz, 0 means no demodulation */ + unsigned int rx_high_carrier_freq; + + /* tx carrier frequency, in Hz */ + unsigned int tx_carrier_freq; + + /* duty cycle, 0-100 */ + int tx_duty_cycle; + + /* hw-specific operation function pointers; most of these must be + * called while holding the spin lock, except for the TX FIFO length + * one */ + /* get pending interrupt causes */ + int (*get_irq_causes) (struct ite_dev * dev); + + /* enable rx */ + void (*enable_rx) (struct ite_dev * dev); + + /* make rx enter the idle state; keep listening for a pulse, but stop + * streaming space bytes */ + void (*idle_rx) (struct ite_dev * dev); + + /* disable rx completely */ + void (*disable_rx) (struct ite_dev * dev); + + /* read bytes from RX FIFO; return read count */ + int (*get_rx_bytes) (struct ite_dev * dev, u8 * buf, int buf_size); + + /* enable tx FIFO space available interrupt */ + void (*enable_tx_interrupt) (struct ite_dev * dev); + + /* disable tx FIFO space available interrupt */ + void (*disable_tx_interrupt) (struct ite_dev * dev); + + /* get number of full TX FIFO slots */ + int (*get_tx_used_slots) (struct ite_dev * dev); + + /* put a byte to the TX FIFO */ + void (*put_tx_byte) (struct ite_dev * dev, u8 value); + + /* disable hardware completely */ + void (*disable) (struct ite_dev * dev); + + /* initialize the hardware */ + void (*init_hardware) (struct ite_dev * dev); + + /* set the carrier parameters */ + void (*set_carrier_params) (struct ite_dev * dev, bool high_freq, + bool use_demodulator, u8 carrier_freq_bits, + u8 allowance_bits, u8 pulse_width_bits); +}; + +/* ITE CIR device structure */ +struct ite_dev { + struct pnp_dev *pdev; + struct rc_dev *rdev; + struct ir_raw_event rawir; + + /* sync data */ + spinlock_t lock; + bool in_use, transmitting; + + /* transmit support */ + int tx_fifo_allowance; + wait_queue_head_t tx_queue, tx_ended; + + /* hardware I/O settings */ + unsigned long cir_addr; + int cir_irq; + + /* overridable copy of model parameters */ + struct ite_dev_params params; +}; + +/* common values for all kinds of hardware */ + +/* baud rate divisor default */ +#define ITE_BAUDRATE_DIVISOR 1 + +/* low-speed carrier frequency limits (Hz) */ +#define ITE_LCF_MIN_CARRIER_FREQ 27000 +#define ITE_LCF_MAX_CARRIER_FREQ 58000 + +/* high-speed carrier frequency limits (Hz) */ +#define ITE_HCF_MIN_CARRIER_FREQ 400000 +#define ITE_HCF_MAX_CARRIER_FREQ 500000 + +/* default carrier freq for when demodulator is off (Hz) */ +#define ITE_DEFAULT_CARRIER_FREQ 38000 + +/* default idling timeout in ns (0.2 seconds) */ +#define ITE_IDLE_TIMEOUT 200000000UL + +/* limit timeout values */ +#define ITE_MIN_IDLE_TIMEOUT 100000000UL +#define ITE_MAX_IDLE_TIMEOUT 1000000000UL + +/* convert bits to us */ +#define ITE_BITS_TO_NS(bits, sample_period) \ +((u32) ((bits) * ITE_BAUDRATE_DIVISOR * sample_period)) + +/* + * n in RDCR produces a tolerance of +/- n * 6.25% around the center + * carrier frequency... + * + * From two limit frequencies, L (low) and H (high), we can get both the + * center frequency F = (L + H) / 2 and the variation from the center + * frequency A = (H - L) / (H + L). We can use this in order to honor the + * s_rx_carrier_range() call in ir-core. We'll suppose that any request + * setting L=0 means we must shut down the demodulator. + */ +#define ITE_RXDCR_PER_10000_STEP 625 + +/* high speed carrier freq values */ +#define ITE_CFQ_400 0x03 +#define ITE_CFQ_450 0x08 +#define ITE_CFQ_480 0x0b +#define ITE_CFQ_500 0x0d + +/* values for pulse widths */ +#define ITE_TXMPW_A 0x02 +#define ITE_TXMPW_B 0x03 +#define ITE_TXMPW_C 0x04 +#define ITE_TXMPW_D 0x05 +#define ITE_TXMPW_E 0x06 + +/* values for demodulator carrier range allowance */ +#define ITE_RXDCR_DEFAULT 0x01 /* default carrier range */ +#define ITE_RXDCR_MAX 0x07 /* default carrier range */ + +/* DR TX bits */ +#define ITE_TX_PULSE 0x00 +#define ITE_TX_SPACE 0x80 +#define ITE_TX_MAX_RLE 0x80 +#define ITE_TX_RLE_MASK 0x7f + +/* + * IT8712F + * + * hardware data obtained from: + * + * IT8712F + * Environment Control – Low Pin Count Input / Output + * (EC - LPC I/O) + * Preliminary Specification V0. 81 + */ + +/* register offsets */ +#define IT87_DR 0x00 /* data register */ +#define IT87_IER 0x01 /* interrupt enable register */ +#define IT87_RCR 0x02 /* receiver control register */ +#define IT87_TCR1 0x03 /* transmitter control register 1 */ +#define IT87_TCR2 0x04 /* transmitter control register 2 */ +#define IT87_TSR 0x05 /* transmitter status register */ +#define IT87_RSR 0x06 /* receiver status register */ +#define IT87_BDLR 0x05 /* baud rate divisor low byte register */ +#define IT87_BDHR 0x06 /* baud rate divisor high byte register */ +#define IT87_IIR 0x07 /* interrupt identification register */ + +#define IT87_IOREG_LENGTH 0x08 /* length of register file */ + +/* IER bits */ +#define IT87_TLDLIE 0x01 /* transmitter low data interrupt enable */ +#define IT87_RDAIE 0x02 /* receiver data available interrupt enable */ +#define IT87_RFOIE 0x04 /* receiver FIFO overrun interrupt enable */ +#define IT87_IEC 0x08 /* interrupt enable control */ +#define IT87_BR 0x10 /* baud rate register enable */ +#define IT87_RESET 0x20 /* reset */ + +/* RCR bits */ +#define IT87_RXDCR 0x07 /* receiver demodulation carrier range mask */ +#define IT87_RXACT 0x08 /* receiver active */ +#define IT87_RXEND 0x10 /* receiver demodulation enable */ +#define IT87_RXEN 0x20 /* receiver enable */ +#define IT87_HCFS 0x40 /* high-speed carrier frequency select */ +#define IT87_RDWOS 0x80 /* receiver data without sync */ + +/* TCR1 bits */ +#define IT87_TXMPM 0x03 /* transmitter modulation pulse mode mask */ +#define IT87_TXMPM_DEFAULT 0x00 /* modulation pulse mode default */ +#define IT87_TXENDF 0x04 /* transmitter deferral */ +#define IT87_TXRLE 0x08 /* transmitter run length enable */ +#define IT87_FIFOTL 0x30 /* FIFO level threshold mask */ +#define IT87_FIFOTL_DEFAULT 0x20 /* FIFO level threshold default + * 0x00 -> 1, 0x10 -> 7, 0x20 -> 17, + * 0x30 -> 25 */ +#define IT87_ILE 0x40 /* internal loopback enable */ +#define IT87_FIFOCLR 0x80 /* FIFO clear bit */ + +/* TCR2 bits */ +#define IT87_TXMPW 0x07 /* transmitter modulation pulse width mask */ +#define IT87_TXMPW_DEFAULT 0x04 /* default modulation pulse width */ +#define IT87_CFQ 0xf8 /* carrier frequency mask */ +#define IT87_CFQ_SHIFT 3 /* carrier frequency bit shift */ + +/* TSR bits */ +#define IT87_TXFBC 0x3f /* transmitter FIFO byte count mask */ + +/* RSR bits */ +#define IT87_RXFBC 0x3f /* receiver FIFO byte count mask */ +#define IT87_RXFTO 0x80 /* receiver FIFO time-out */ + +/* IIR bits */ +#define IT87_IP 0x01 /* interrupt pending */ +#define IT87_II 0x06 /* interrupt identification mask */ +#define IT87_II_NOINT 0x00 /* no interrupt */ +#define IT87_II_TXLDL 0x02 /* transmitter low data level */ +#define IT87_II_RXDS 0x04 /* receiver data stored */ +#define IT87_II_RXFO 0x06 /* receiver FIFO overrun */ + +/* + * IT8512E/F + * + * Hardware data obtained from: + * + * IT8512E/F + * Embedded Controller + * Preliminary Specification V0.4.1 + * + * Note that the CIR registers are not directly available to the host, because + * they only are accessible to the integrated microcontroller. Thus, in order + * use it, some kind of bridging is required. As the bridging may depend on + * the controller firmware in use, we are going to use the PNP ID in order to + * determine the strategy and ports available. See after these generic + * IT8512E/F register definitions for register definitions for those + * strategies. + */ + +/* register offsets */ +#define IT85_C0DR 0x00 /* data register */ +#define IT85_C0MSTCR 0x01 /* master control register */ +#define IT85_C0IER 0x02 /* interrupt enable register */ +#define IT85_C0IIR 0x03 /* interrupt identification register */ +#define IT85_C0CFR 0x04 /* carrier frequency register */ +#define IT85_C0RCR 0x05 /* receiver control register */ +#define IT85_C0TCR 0x06 /* transmitter control register */ +#define IT85_C0SCK 0x07 /* slow clock control register */ +#define IT85_C0BDLR 0x08 /* baud rate divisor low byte register */ +#define IT85_C0BDHR 0x09 /* baud rate divisor high byte register */ +#define IT85_C0TFSR 0x0a /* transmitter FIFO status register */ +#define IT85_C0RFSR 0x0b /* receiver FIFO status register */ +#define IT85_C0WCL 0x0d /* wakeup code length register */ +#define IT85_C0WCR 0x0e /* wakeup code read/write register */ +#define IT85_C0WPS 0x0f /* wakeup power control/status register */ + +#define IT85_IOREG_LENGTH 0x10 /* length of register file */ + +/* C0MSTCR bits */ +#define IT85_RESET 0x01 /* reset */ +#define IT85_FIFOCLR 0x02 /* FIFO clear bit */ +#define IT85_FIFOTL 0x0c /* FIFO level threshold mask */ +#define IT85_FIFOTL_DEFAULT 0x08 /* FIFO level threshold default + * 0x00 -> 1, 0x04 -> 7, 0x08 -> 17, + * 0x0c -> 25 */ +#define IT85_ILE 0x10 /* internal loopback enable */ +#define IT85_ILSEL 0x20 /* internal loopback select */ + +/* C0IER bits */ +#define IT85_TLDLIE 0x01 /* TX low data level interrupt enable */ +#define IT85_RDAIE 0x02 /* RX data available interrupt enable */ +#define IT85_RFOIE 0x04 /* RX FIFO overrun interrupt enable */ +#define IT85_IEC 0x80 /* interrupt enable function control */ + +/* C0IIR bits */ +#define IT85_TLDLI 0x01 /* transmitter low data level interrupt */ +#define IT85_RDAI 0x02 /* receiver data available interrupt */ +#define IT85_RFOI 0x04 /* receiver FIFO overrun interrupt */ +#define IT85_NIP 0x80 /* no interrupt pending */ + +/* C0CFR bits */ +#define IT85_CFQ 0x1f /* carrier frequency mask */ +#define IT85_HCFS 0x20 /* high speed carrier frequency select */ + +/* C0RCR bits */ +#define IT85_RXDCR 0x07 /* receiver demodulation carrier range mask */ +#define IT85_RXACT 0x08 /* receiver active */ +#define IT85_RXEND 0x10 /* receiver demodulation enable */ +#define IT85_RDWOS 0x20 /* receiver data without sync */ +#define IT85_RXEN 0x80 /* receiver enable */ + +/* C0TCR bits */ +#define IT85_TXMPW 0x07 /* transmitter modulation pulse width mask */ +#define IT85_TXMPW_DEFAULT 0x04 /* default modulation pulse width */ +#define IT85_TXMPM 0x18 /* transmitter modulation pulse mode mask */ +#define IT85_TXMPM_DEFAULT 0x00 /* modulation pulse mode default */ +#define IT85_TXENDF 0x20 /* transmitter deferral */ +#define IT85_TXRLE 0x40 /* transmitter run length enable */ + +/* C0SCK bits */ +#define IT85_SCKS 0x01 /* slow clock select */ +#define IT85_TXDCKG 0x02 /* TXD clock gating */ +#define IT85_DLL1P8E 0x04 /* DLL 1.8432M enable */ +#define IT85_DLLTE 0x08 /* DLL test enable */ +#define IT85_BRCM 0x70 /* baud rate count mode */ +#define IT85_DLLOCK 0x80 /* DLL lock */ + +/* C0TFSR bits */ +#define IT85_TXFBC 0x3f /* transmitter FIFO count mask */ + +/* C0RFSR bits */ +#define IT85_RXFBC 0x3f /* receiver FIFO count mask */ +#define IT85_RXFTO 0x80 /* receiver FIFO time-out */ + +/* C0WCL bits */ +#define IT85_WCL 0x3f /* wakeup code length mask */ + +/* C0WPS bits */ +#define IT85_CIRPOSIE 0x01 /* power on/off status interrupt enable */ +#define IT85_CIRPOIS 0x02 /* power on/off interrupt status */ +#define IT85_CIRPOII 0x04 /* power on/off interrupt identification */ +#define IT85_RCRST 0x10 /* wakeup code reading counter reset bit */ +#define IT85_WCRST 0x20 /* wakeup code writing counter reset bit */ + +/* + * ITE8708 + * + * Hardware data obtained from hacked driver for IT8512 in this forum post: + * + * http://ubuntuforums.org/showthread.php?t=1028640 + * + * Although there's no official documentation for that driver, analysis would + * suggest that it maps the 16 registers of IT8512 onto two 8-register banks, + * selectable by a single bank-select bit that's mapped onto both banks. The + * IT8512 registers are mapped in a different order, so that the first bank + * maps the ones that are used more often, and two registers that share a + * reserved high-order bit are placed at the same offset in both banks in + * order to reuse the reserved bit as the bank select bit. + */ + +/* register offsets */ + +/* mapped onto both banks */ +#define IT8708_BANKSEL 0x07 /* bank select register */ +#define IT8708_HRAE 0x80 /* high registers access enable */ + +/* mapped onto the low bank */ +#define IT8708_C0DR 0x00 /* data register */ +#define IT8708_C0MSTCR 0x01 /* master control register */ +#define IT8708_C0IER 0x02 /* interrupt enable register */ +#define IT8708_C0IIR 0x03 /* interrupt identification register */ +#define IT8708_C0RFSR 0x04 /* receiver FIFO status register */ +#define IT8708_C0RCR 0x05 /* receiver control register */ +#define IT8708_C0TFSR 0x06 /* transmitter FIFO status register */ +#define IT8708_C0TCR 0x07 /* transmitter control register */ + +/* mapped onto the high bank */ +#define IT8708_C0BDLR 0x01 /* baud rate divisor low byte register */ +#define IT8708_C0BDHR 0x02 /* baud rate divisor high byte register */ +#define IT8708_C0CFR 0x04 /* carrier frequency register */ + +/* registers whose bank mapping we don't know, since they weren't being used + * in the hacked driver... most probably they belong to the high bank too, + * since they fit in the holes the other registers leave */ +#define IT8708_C0SCK 0x03 /* slow clock control register */ +#define IT8708_C0WCL 0x05 /* wakeup code length register */ +#define IT8708_C0WCR 0x06 /* wakeup code read/write register */ +#define IT8708_C0WPS 0x07 /* wakeup power control/status register */ + +#define IT8708_IOREG_LENGTH 0x08 /* length of register file */ + +/* two more registers that are defined in the hacked driver, but can't be + * found in the data sheets; no idea what they are or how they are accessed, + * since the hacked driver doesn't seem to use them */ +#define IT8708_CSCRR 0x00 +#define IT8708_CGPINTR 0x01 + +/* CSCRR bits */ +#define IT8708_CSCRR_SCRB 0x3f +#define IT8708_CSCRR_PM 0x80 + +/* CGPINTR bits */ +#define IT8708_CGPINT 0x01 + +/* + * ITE8709 + * + * Hardware interfacing data obtained from the original lirc_ite8709 driver. + * Verbatim from its sources: + * + * The ITE8709 device seems to be the combination of IT8512 superIO chip and + * a specific firmware running on the IT8512's embedded micro-controller. + * In addition of the embedded micro-controller, the IT8512 chip contains a + * CIR module and several other modules. A few modules are directly accessible + * by the host CPU, but most of them are only accessible by the + * micro-controller. The CIR module is only accessible by the + * micro-controller. + * + * The battery-backed SRAM module is accessible by the host CPU and the + * micro-controller. So one of the MC's firmware role is to act as a bridge + * between the host CPU and the CIR module. The firmware implements a kind of + * communication protocol using the SRAM module as a shared memory. The IT8512 + * specification is publicly available on ITE's web site, but the + * communication protocol is not, so it was reverse-engineered. + */ + +/* register offsets */ +#define IT8709_RAM_IDX 0x00 /* index into the SRAM module bytes */ +#define IT8709_RAM_VAL 0x01 /* read/write data to the indexed byte */ + +#define IT8709_IOREG_LENGTH 0x02 /* length of register file */ + +/* register offsets inside the SRAM module */ +#define IT8709_MODE 0x1a /* request/ack byte */ +#define IT8709_REG_IDX 0x1b /* index of the CIR register to access */ +#define IT8709_REG_VAL 0x1c /* value read/to be written */ +#define IT8709_IIR 0x1e /* interrupt identification register */ +#define IT8709_RFSR 0x1f /* receiver FIFO status register */ +#define IT8709_FIFO 0x20 /* start of in RAM RX FIFO copy */ + +/* MODE values */ +#define IT8709_IDLE 0x00 +#define IT8709_WRITE 0x01 +#define IT8709_READ 0x02 -- cgit v0.10.2 From 2ccb24ff3b94acb3f405b10a59967d52d95f91e0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Mar 2011 15:12:40 -0300 Subject: [media] ite-cir: Fix some CodingStyle issues Cc: Juan J. Garcia de Soria Cc: Stephan Raue Cc: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 10358d5..9be6a83 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -461,8 +461,10 @@ static int ite_tx_ir(struct rc_dev *rcdev, int *txbuf, u32 n) else val |= ITE_TX_SPACE; - /* if we get to 0 available, read again, just in case - * some other slot got freed */ + /* + * if we get to 0 available, read again, just in case + * some other slot got freed + */ if (fifo_avail <= 0) fifo_avail = ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev); @@ -1511,7 +1513,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id /* store resource values */ itdev->cir_addr = pnp_port_start(pdev, 0); - itdev->cir_irq =pnp_irq(pdev, 0); + itdev->cir_irq = pnp_irq(pdev, 0); /* initialize spinlocks */ spin_lock_init(&itdev->lock); diff --git a/drivers/media/rc/ite-cir.h b/drivers/media/rc/ite-cir.h index 43bac8d..16a19f5 100644 --- a/drivers/media/rc/ite-cir.h +++ b/drivers/media/rc/ite-cir.h @@ -24,15 +24,18 @@ /* logging macros */ #define ite_pr(level, text, ...) \ - printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__) -#define ite_dbg(text, ...) \ - if (debug) \ - printk(KERN_DEBUG \ - KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) -#define ite_dbg_verbose(text, ...) \ - if (debug > 1) \ - printk(KERN_DEBUG \ - KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__) +#define ite_dbg(text, ...) do { \ + if (debug) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__); \ +} while (0) + +#define ite_dbg_verbose(text, ...) do {\ + if (debug > 1) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__); \ +} while (0) /* FIFO sizes */ #define ITE_TX_FIFO_LEN 32 @@ -76,41 +79,41 @@ struct ite_dev_params { * called while holding the spin lock, except for the TX FIFO length * one */ /* get pending interrupt causes */ - int (*get_irq_causes) (struct ite_dev * dev); + int (*get_irq_causes) (struct ite_dev *dev); /* enable rx */ - void (*enable_rx) (struct ite_dev * dev); + void (*enable_rx) (struct ite_dev *dev); /* make rx enter the idle state; keep listening for a pulse, but stop * streaming space bytes */ - void (*idle_rx) (struct ite_dev * dev); + void (*idle_rx) (struct ite_dev *dev); /* disable rx completely */ - void (*disable_rx) (struct ite_dev * dev); + void (*disable_rx) (struct ite_dev *dev); /* read bytes from RX FIFO; return read count */ - int (*get_rx_bytes) (struct ite_dev * dev, u8 * buf, int buf_size); + int (*get_rx_bytes) (struct ite_dev *dev, u8 *buf, int buf_size); /* enable tx FIFO space available interrupt */ - void (*enable_tx_interrupt) (struct ite_dev * dev); + void (*enable_tx_interrupt) (struct ite_dev *dev); /* disable tx FIFO space available interrupt */ - void (*disable_tx_interrupt) (struct ite_dev * dev); + void (*disable_tx_interrupt) (struct ite_dev *dev); /* get number of full TX FIFO slots */ - int (*get_tx_used_slots) (struct ite_dev * dev); + int (*get_tx_used_slots) (struct ite_dev *dev); /* put a byte to the TX FIFO */ - void (*put_tx_byte) (struct ite_dev * dev, u8 value); + void (*put_tx_byte) (struct ite_dev *dev, u8 value); /* disable hardware completely */ - void (*disable) (struct ite_dev * dev); + void (*disable) (struct ite_dev *dev); /* initialize the hardware */ - void (*init_hardware) (struct ite_dev * dev); + void (*init_hardware) (struct ite_dev *dev); /* set the carrier parameters */ - void (*set_carrier_params) (struct ite_dev * dev, bool high_freq, + void (*set_carrier_params) (struct ite_dev *dev, bool high_freq, bool use_demodulator, u8 carrier_freq_bits, u8 allowance_bits, u8 pulse_width_bits); }; -- cgit v0.10.2 From 812738e11158e9b92c4f0e9278de48377cfa3cc8 Mon Sep 17 00:00:00 2001 From: "Juan J. Garcia de Soria" Date: Wed, 16 Mar 2011 17:14:53 -0300 Subject: [media] lirc: remove staging lirc_it87 and lirc_ite8709 drivers Remove older drivers lirc_it87 and lirc_ite8709 from the LIRC staging area, since they're now superceded by ite-cir. Signed-off-by: Juan J. Garcia de Soria Tested-by: Stephan Raue Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/lirc/Kconfig index cdaff59..526ec0f 100644 --- a/drivers/staging/lirc/Kconfig +++ b/drivers/staging/lirc/Kconfig @@ -32,18 +32,6 @@ config LIRC_IMON Current generation iMON devices use the input layer imon driver. -config LIRC_IT87 - tristate "ITE IT87XX CIR Port Receiver" - depends on LIRC && PNP - help - Driver for the ITE IT87xx IR Receiver - -config LIRC_ITE8709 - tristate "ITE8709 CIR Port Receiver" - depends on LIRC && PNP - help - Driver for the ITE8709 IR Receiver - config LIRC_PARALLEL tristate "Homebrew Parallel Port Receiver" depends on LIRC && PARPORT diff --git a/drivers/staging/lirc/Makefile b/drivers/staging/lirc/Makefile index 94af218..d76b0fa 100644 --- a/drivers/staging/lirc/Makefile +++ b/drivers/staging/lirc/Makefile @@ -6,8 +6,6 @@ obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o obj-$(CONFIG_LIRC_IMON) += lirc_imon.o -obj-$(CONFIG_LIRC_IT87) += lirc_it87.o -obj-$(CONFIG_LIRC_ITE8709) += lirc_ite8709.o obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o diff --git a/drivers/staging/lirc/lirc_it87.c b/drivers/staging/lirc/lirc_it87.c deleted file mode 100644 index 5938616..0000000 --- a/drivers/staging/lirc/lirc_it87.c +++ /dev/null @@ -1,1027 +0,0 @@ -/* - * LIRC driver for ITE IT8712/IT8705 CIR port - * - * Copyright (C) 2001 Hans-Gunter Lutke Uphues - * - * 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 - * - * ITE IT8705 and IT8712(not tested) and IT8720 CIR-port support for lirc based - * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula - * - * Attention: Sendmode only tested with debugging logs - * - * 2001/02/27 Christoph Bartelmus : - * reimplemented read function - * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix, - * based on work of the following member of the Outertrack Digimatrix - * Forum: Art103 - * 2009/12/24 James Edwards implemeted support - * for ITE8704/ITE8718, on my machine, the DSDT reports 8704, but the - * chip identifies as 18. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "lirc_it87.h" - -#ifdef LIRC_IT87_DIGIMATRIX -static int digimatrix = 1; -static int it87_freq = 36; /* kHz */ -static int irq = 9; -#else -static int digimatrix; -static int it87_freq = 38; /* kHz */ -static int irq = IT87_CIR_DEFAULT_IRQ; -#endif - -static unsigned long it87_bits_in_byte_out; -static unsigned long it87_send_counter; -static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN; - -#define RBUF_LEN 1024 - -#define LIRC_DRIVER_NAME "lirc_it87" - -/* timeout for sequences in jiffies (=5/100s) */ -/* must be longer than TIME_CONST */ -#define IT87_TIMEOUT (HZ*5/100) - -/* module parameters */ -static int debug; -#define dprintk(fmt, args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ - fmt, ## args); \ - } while (0) - -static int io = IT87_CIR_DEFAULT_IOBASE; -/* receiver demodulator default: off */ -static int it87_enable_demodulator; - -static int timer_enabled; -static DEFINE_SPINLOCK(timer_lock); -static struct timer_list timerlist; -/* time of last signal change detected */ -static struct timeval last_tv = {0, 0}; -/* time of last UART data ready interrupt */ -static struct timeval last_intr_tv = {0, 0}; -static int last_value; - -static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue); - -static DEFINE_SPINLOCK(hardware_lock); -static DEFINE_SPINLOCK(dev_lock); -static bool device_open; - -static int rx_buf[RBUF_LEN]; -unsigned int rx_tail, rx_head; - -static struct pnp_driver it87_pnp_driver; - -/* SECTION: Prototypes */ - -/* Communication with user-space */ -static int lirc_open(struct inode *inode, struct file *file); -static int lirc_close(struct inode *inode, struct file *file); -static unsigned int lirc_poll(struct file *file, poll_table *wait); -static ssize_t lirc_read(struct file *file, char *buf, - size_t count, loff_t *ppos); -static ssize_t lirc_write(struct file *file, const char *buf, - size_t n, loff_t *pos); -static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); -static void add_read_queue(int flag, unsigned long val); -static int init_chrdev(void); -static void drop_chrdev(void); -/* Hardware */ -static irqreturn_t it87_interrupt(int irq, void *dev_id); -static void send_space(unsigned long len); -static void send_pulse(unsigned long len); -static void init_send(void); -static void terminate_send(unsigned long len); -static int init_hardware(void); -static void drop_hardware(void); -/* Initialisation */ -static int init_port(void); -static void drop_port(void); - - -/* SECTION: Communication with user-space */ - -static int lirc_open(struct inode *inode, struct file *file) -{ - spin_lock(&dev_lock); - if (device_open) { - spin_unlock(&dev_lock); - return -EBUSY; - } - device_open = true; - spin_unlock(&dev_lock); - return 0; -} - - -static int lirc_close(struct inode *inode, struct file *file) -{ - spin_lock(&dev_lock); - device_open = false; - spin_unlock(&dev_lock); - return 0; -} - - -static unsigned int lirc_poll(struct file *file, poll_table *wait) -{ - poll_wait(file, &lirc_read_queue, wait); - if (rx_head != rx_tail) - return POLLIN | POLLRDNORM; - return 0; -} - - -static ssize_t lirc_read(struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - int n = 0; - int retval = 0; - - while (n < count) { - if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) { - retval = -EAGAIN; - break; - } - retval = wait_event_interruptible(lirc_read_queue, - rx_head != rx_tail); - if (retval) - break; - - if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head), - sizeof(int))) { - retval = -EFAULT; - break; - } - rx_head = (rx_head + 1) & (RBUF_LEN - 1); - n += sizeof(int); - } - if (n) - return n; - return retval; -} - - -static ssize_t lirc_write(struct file *file, const char *buf, - size_t n, loff_t *pos) -{ - int i = 0; - int *tx_buf; - - if (n % sizeof(int)) - return -EINVAL; - tx_buf = memdup_user(buf, n); - if (IS_ERR(tx_buf)) - return PTR_ERR(tx_buf); - n /= sizeof(int); - init_send(); - while (1) { - if (i >= n) - break; - if (tx_buf[i]) - send_pulse(tx_buf[i]); - i++; - if (i >= n) - break; - if (tx_buf[i]) - send_space(tx_buf[i]); - i++; - } - terminate_send(tx_buf[i - 1]); - kfree(tx_buf); - return n; -} - - -static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - int retval = 0; - __u32 value = 0; - unsigned long hw_flags; - - if (cmd == LIRC_GET_FEATURES) - value = LIRC_CAN_SEND_PULSE | - LIRC_CAN_SET_SEND_CARRIER | - LIRC_CAN_REC_MODE2; - else if (cmd == LIRC_GET_SEND_MODE) - value = LIRC_MODE_PULSE; - else if (cmd == LIRC_GET_REC_MODE) - value = LIRC_MODE_MODE2; - - switch (cmd) { - case LIRC_GET_FEATURES: - case LIRC_GET_SEND_MODE: - case LIRC_GET_REC_MODE: - retval = put_user(value, (__u32 *) arg); - break; - - case LIRC_SET_SEND_MODE: - case LIRC_SET_REC_MODE: - retval = get_user(value, (__u32 *) arg); - break; - - case LIRC_SET_SEND_CARRIER: - retval = get_user(value, (__u32 *) arg); - if (retval) - return retval; - value /= 1000; - if (value > IT87_CIR_FREQ_MAX || - value < IT87_CIR_FREQ_MIN) - return -EINVAL; - - it87_freq = value; - - spin_lock_irqsave(&hardware_lock, hw_flags); - outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) | - (it87_freq - IT87_CIR_FREQ_MIN) << 3), - io + IT87_CIR_TCR2); - spin_unlock_irqrestore(&hardware_lock, hw_flags); - dprintk("demodulation frequency: %d kHz\n", it87_freq); - - break; - - default: - retval = -EINVAL; - } - - if (retval) - return retval; - - if (cmd == LIRC_SET_REC_MODE) { - if (value != LIRC_MODE_MODE2) - retval = -ENOSYS; - } else if (cmd == LIRC_SET_SEND_MODE) { - if (value != LIRC_MODE_PULSE) - retval = -ENOSYS; - } - return retval; -} - -static void add_read_queue(int flag, unsigned long val) -{ - unsigned int new_rx_tail; - int newval; - - dprintk("add flag %d with val %lu\n", flag, val); - - newval = val & PULSE_MASK; - - /* - * statistically, pulses are ~TIME_CONST/2 too long. we could - * maybe make this more exact, but this is good enough - */ - if (flag) { - /* pulse */ - if (newval > TIME_CONST / 2) - newval -= TIME_CONST / 2; - else /* should not ever happen */ - newval = 1; - newval |= PULSE_BIT; - } else - newval += TIME_CONST / 2; - new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1); - if (new_rx_tail == rx_head) { - dprintk("Buffer overrun.\n"); - return; - } - rx_buf[rx_tail] = newval; - rx_tail = new_rx_tail; - wake_up_interruptible(&lirc_read_queue); -} - - -static const struct file_operations lirc_fops = { - .owner = THIS_MODULE, - .read = lirc_read, - .write = lirc_write, - .poll = lirc_poll, - .unlocked_ioctl = lirc_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = lirc_ioctl, -#endif - .open = lirc_open, - .release = lirc_close, - .llseek = noop_llseek, -}; - -static int set_use_inc(void *data) -{ - return 0; -} - -static void set_use_dec(void *data) -{ -} - -static struct lirc_driver driver = { - .name = LIRC_DRIVER_NAME, - .minor = -1, - .code_length = 1, - .sample_rate = 0, - .data = NULL, - .add_to_buf = NULL, - .set_use_inc = set_use_inc, - .set_use_dec = set_use_dec, - .fops = &lirc_fops, - .dev = NULL, - .owner = THIS_MODULE, -}; - - -static int init_chrdev(void) -{ - driver.minor = lirc_register_driver(&driver); - - if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); - return -EIO; - } - return 0; -} - - -static void drop_chrdev(void) -{ - lirc_unregister_driver(driver.minor); -} - - -/* SECTION: Hardware */ -static long delta(struct timeval *tv1, struct timeval *tv2) -{ - unsigned long deltv; - - deltv = tv2->tv_sec - tv1->tv_sec; - if (deltv > 15) - deltv = 0xFFFFFF; - else - deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec; - return deltv; -} - -static void it87_timeout(unsigned long data) -{ - unsigned long flags; - - /* avoid interference with interrupt */ - spin_lock_irqsave(&timer_lock, flags); - - if (digimatrix) { - /* We have timed out. Disable the RX mechanism. */ - - outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) | - IT87_CIR_RCR_RXACT, io + IT87_CIR_RCR); - if (it87_RXEN_mask) - outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, - io + IT87_CIR_RCR); - dprintk(" TIMEOUT\n"); - timer_enabled = 0; - - /* fifo clear */ - outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR, - io+IT87_CIR_TCR1); - - } else { - /* - * if last received signal was a pulse, but receiving stopped - * within the 9 bit frame, we need to finish this pulse and - * simulate a signal change to from pulse to space. Otherwise - * upper layers will receive two sequences next time. - */ - - if (last_value) { - unsigned long pulse_end; - - /* determine 'virtual' pulse end: */ - pulse_end = delta(&last_tv, &last_intr_tv); - dprintk("timeout add %d for %lu usec\n", - last_value, pulse_end); - add_read_queue(last_value, pulse_end); - last_value = 0; - last_tv = last_intr_tv; - } - } - spin_unlock_irqrestore(&timer_lock, flags); -} - -static irqreturn_t it87_interrupt(int irq, void *dev_id) -{ - unsigned char data; - struct timeval curr_tv; - static unsigned long deltv; - unsigned long deltintrtv; - unsigned long flags, hw_flags; - int iir, lsr; - int fifo = 0; - static char lastbit; - char bit; - - /* Bit duration in microseconds */ - const unsigned long bit_duration = 1000000ul / - (115200 / IT87_CIR_BAUDRATE_DIVISOR); - - - iir = inb(io + IT87_CIR_IIR); - - switch (iir & IT87_CIR_IIR_IID) { - case 0x4: - case 0x6: - lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO | - IT87_CIR_RSR_RXFBC); - fifo = lsr & IT87_CIR_RSR_RXFBC; - dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr); - - /* avoid interference with timer */ - spin_lock_irqsave(&timer_lock, flags); - spin_lock_irqsave(&hardware_lock, hw_flags); - if (digimatrix) { - static unsigned long acc_pulse; - static unsigned long acc_space; - - do { - data = inb(io + IT87_CIR_DR); - data = ~data; - fifo--; - if (data != 0x00) { - if (timer_enabled) - del_timer(&timerlist); - /* - * start timer for end of - * sequence detection - */ - timerlist.expires = jiffies + - IT87_TIMEOUT; - add_timer(&timerlist); - timer_enabled = 1; - } - /* Loop through */ - for (bit = 0; bit < 8; ++bit) { - if ((data >> bit) & 1) { - ++acc_pulse; - if (lastbit == 0) { - add_read_queue(0, - acc_space * - bit_duration); - acc_space = 0; - } - } else { - ++acc_space; - if (lastbit == 1) { - add_read_queue(1, - acc_pulse * - bit_duration); - acc_pulse = 0; - } - } - lastbit = (data >> bit) & 1; - } - - } while (fifo != 0); - } else { /* Normal Operation */ - do { - del_timer(&timerlist); - data = inb(io + IT87_CIR_DR); - - dprintk("data=%02x\n", data); - do_gettimeofday(&curr_tv); - deltv = delta(&last_tv, &curr_tv); - deltintrtv = delta(&last_intr_tv, &curr_tv); - - dprintk("t %lu , d %d\n", - deltintrtv, (int)data); - - /* - * if nothing came in last 2 cycles, - * it was gap - */ - if (deltintrtv > TIME_CONST * 2) { - if (last_value) { - dprintk("GAP\n"); - - /* simulate signal change */ - add_read_queue(last_value, - deltv - - deltintrtv); - last_value = 0; - last_tv.tv_sec = - last_intr_tv.tv_sec; - last_tv.tv_usec = - last_intr_tv.tv_usec; - deltv = deltintrtv; - } - } - data = 1; - if (data ^ last_value) { - /* - * deltintrtv > 2*TIME_CONST, - * remember ? the other case is - * timeout - */ - add_read_queue(last_value, - deltv-TIME_CONST); - last_value = data; - last_tv = curr_tv; - if (last_tv.tv_usec >= TIME_CONST) - last_tv.tv_usec -= TIME_CONST; - else { - last_tv.tv_sec--; - last_tv.tv_usec += 1000000 - - TIME_CONST; - } - } - last_intr_tv = curr_tv; - if (data) { - /* - * start timer for end of - * sequence detection - */ - timerlist.expires = - jiffies + IT87_TIMEOUT; - add_timer(&timerlist); - } - outb((inb(io + IT87_CIR_RCR) & - ~IT87_CIR_RCR_RXEN) | - IT87_CIR_RCR_RXACT, - io + IT87_CIR_RCR); - if (it87_RXEN_mask) - outb(inb(io + IT87_CIR_RCR) | - IT87_CIR_RCR_RXEN, - io + IT87_CIR_RCR); - fifo--; - } while (fifo != 0); - } - spin_unlock_irqrestore(&hardware_lock, hw_flags); - spin_unlock_irqrestore(&timer_lock, flags); - - return IRQ_RETVAL(IRQ_HANDLED); - - default: - /* not our irq */ - dprintk("unknown IRQ (shouldn't happen) !!\n"); - return IRQ_RETVAL(IRQ_NONE); - } -} - - -static void send_it87(unsigned long len, unsigned long stime, - unsigned char send_byte, unsigned int count_bits) -{ - long count = len / stime; - long time_left = 0; - static unsigned char byte_out; - unsigned long hw_flags; - - dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte); - - time_left = (long)len - (long)count * (long)stime; - count += ((2 * time_left) / stime); - while (count) { - long i = 0; - for (i = 0; i < count_bits; i++) { - byte_out = (byte_out << 1) | (send_byte & 1); - it87_bits_in_byte_out++; - } - if (it87_bits_in_byte_out == 8) { - dprintk("out=0x%x, tsr_txfbc: 0x%x\n", - byte_out, - inb(io + IT87_CIR_TSR) & - IT87_CIR_TSR_TXFBC); - - while ((inb(io + IT87_CIR_TSR) & - IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE) - ; - - spin_lock_irqsave(&hardware_lock, hw_flags); - outb(byte_out, io + IT87_CIR_DR); - spin_unlock_irqrestore(&hardware_lock, hw_flags); - - it87_bits_in_byte_out = 0; - it87_send_counter++; - byte_out = 0; - } - count--; - } -} - - -/*TODO: maybe exchange space and pulse because it8705 only modulates 0-bits */ - -static void send_space(unsigned long len) -{ - send_it87(len, TIME_CONST, IT87_CIR_SPACE, IT87_CIR_BAUDRATE_DIVISOR); -} - -static void send_pulse(unsigned long len) -{ - send_it87(len, TIME_CONST, IT87_CIR_PULSE, IT87_CIR_BAUDRATE_DIVISOR); -} - - -static void init_send() -{ - unsigned long flags; - - spin_lock_irqsave(&hardware_lock, flags); - /* RXEN=0: receiver disable */ - it87_RXEN_mask = 0; - outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN, - io + IT87_CIR_RCR); - spin_unlock_irqrestore(&hardware_lock, flags); - it87_bits_in_byte_out = 0; - it87_send_counter = 0; -} - - -static void terminate_send(unsigned long len) -{ - unsigned long flags; - unsigned long last = 0; - - last = it87_send_counter; - /* make sure all necessary data has been sent */ - while (last == it87_send_counter) - send_space(len); - /* wait until all data sent */ - while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0) - ; - /* then re-enable receiver */ - spin_lock_irqsave(&hardware_lock, flags); - it87_RXEN_mask = IT87_CIR_RCR_RXEN; - outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, - io + IT87_CIR_RCR); - spin_unlock_irqrestore(&hardware_lock, flags); -} - - -static int init_hardware(void) -{ - unsigned long flags; - unsigned char it87_rcr = 0; - - spin_lock_irqsave(&hardware_lock, flags); - /* init cir-port */ - /* enable r/w-access to Baudrate-Register */ - outb(IT87_CIR_IER_BR, io + IT87_CIR_IER); - outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR); - outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR); - /* Baudrate Register off, define IRQs: Input only */ - if (digimatrix) { - outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER); - /* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */ - } else { - outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER); - /* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */ - } - it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1; - if (it87_enable_demodulator) - it87_rcr |= IT87_CIR_RCR_RXEND; - outb(it87_rcr, io + IT87_CIR_RCR); - if (digimatrix) { - /* Set FIFO depth to 1 byte, and disable TX */ - outb(inb(io + IT87_CIR_TCR1) | 0x00, - io + IT87_CIR_TCR1); - - /* - * TX: it87_freq (36kHz), 'reserved' sensitivity - * setting (0x00) - */ - outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00, - io + IT87_CIR_TCR2); - } else { - /* TX: 38kHz, 13,3us (pulse-width) */ - outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06, - io + IT87_CIR_TCR2); - } - spin_unlock_irqrestore(&hardware_lock, flags); - return 0; -} - - -static void drop_hardware(void) -{ - unsigned long flags; - - spin_lock_irqsave(&hardware_lock, flags); - disable_irq(irq); - /* receiver disable */ - it87_RXEN_mask = 0; - outb(0x1, io + IT87_CIR_RCR); - /* turn off irqs */ - outb(0, io + IT87_CIR_IER); - /* fifo clear */ - outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1); - /* reset */ - outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER); - enable_irq(irq); - spin_unlock_irqrestore(&hardware_lock, flags); -} - - -static unsigned char it87_read(unsigned char port) -{ - outb(port, IT87_ADRPORT); - return inb(IT87_DATAPORT); -} - - -static void it87_write(unsigned char port, unsigned char data) -{ - outb(port, IT87_ADRPORT); - outb(data, IT87_DATAPORT); -} - - -/* SECTION: Initialisation */ - -static int init_port(void) -{ - unsigned long hw_flags; - int retval = 0; - - unsigned char init_bytes[4] = IT87_INIT; - unsigned char it87_chipid = 0; - unsigned char ldn = 0; - unsigned int it87_io = 0; - unsigned int it87_irq = 0; - - /* Enter MB PnP Mode */ - outb(init_bytes[0], IT87_ADRPORT); - outb(init_bytes[1], IT87_ADRPORT); - outb(init_bytes[2], IT87_ADRPORT); - outb(init_bytes[3], IT87_ADRPORT); - - /* 8712 or 8705 ? */ - it87_chipid = it87_read(IT87_CHIP_ID1); - if (it87_chipid != 0x87) { - retval = -ENXIO; - return retval; - } - it87_chipid = it87_read(IT87_CHIP_ID2); - if ((it87_chipid != 0x05) && - (it87_chipid != 0x12) && - (it87_chipid != 0x18) && - (it87_chipid != 0x20)) { - printk(KERN_INFO LIRC_DRIVER_NAME - ": no IT8704/05/12/18/20 found (claimed IT87%02x), " - "exiting..\n", it87_chipid); - retval = -ENXIO; - return retval; - } - printk(KERN_INFO LIRC_DRIVER_NAME - ": found IT87%02x.\n", - it87_chipid); - - /* get I/O-Port and IRQ */ - if (it87_chipid == 0x12 || it87_chipid == 0x18) - ldn = IT8712_CIR_LDN; - else - ldn = IT8705_CIR_LDN; - it87_write(IT87_LDN, ldn); - - it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 + - it87_read(IT87_CIR_BASE_LSB); - if (it87_io == 0) { - if (io == 0) - io = IT87_CIR_DEFAULT_IOBASE; - printk(KERN_INFO LIRC_DRIVER_NAME - ": set default io 0x%x\n", - io); - it87_write(IT87_CIR_BASE_MSB, io / 0x100); - it87_write(IT87_CIR_BASE_LSB, io % 0x100); - } else - io = it87_io; - - it87_irq = it87_read(IT87_CIR_IRQ); - if (digimatrix || it87_irq == 0) { - if (irq == 0) - irq = IT87_CIR_DEFAULT_IRQ; - printk(KERN_INFO LIRC_DRIVER_NAME - ": set default irq 0x%x\n", - irq); - it87_write(IT87_CIR_IRQ, irq); - } else - irq = it87_irq; - - spin_lock_irqsave(&hardware_lock, hw_flags); - /* reset */ - outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER); - /* fifo clear */ - outb(IT87_CIR_TCR1_FIFOCLR | - /* IT87_CIR_TCR1_ILE | */ - IT87_CIR_TCR1_TXRLE | - IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1); - spin_unlock_irqrestore(&hardware_lock, hw_flags); - - /* get I/O port access and IRQ line */ - if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": i/o port 0x%.4x already in use.\n", io); - /* Leaving MB PnP Mode */ - it87_write(IT87_CFGCTRL, 0x2); - return -EBUSY; - } - - /* activate CIR-Device */ - it87_write(IT87_CIR_ACT, 0x1); - - /* Leaving MB PnP Mode */ - it87_write(IT87_CFGCTRL, 0x2); - - retval = request_irq(irq, it87_interrupt, 0 /*IRQF_DISABLED*/, - LIRC_DRIVER_NAME, NULL); - if (retval < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": IRQ %d already in use.\n", - irq); - release_region(io, 8); - return retval; - } - - printk(KERN_INFO LIRC_DRIVER_NAME - ": I/O port 0x%.4x, IRQ %d.\n", io, irq); - - init_timer(&timerlist); - timerlist.function = it87_timeout; - timerlist.data = 0xabadcafe; - - return 0; -} - - -static void drop_port(void) -{ -#if 0 - unsigned char init_bytes[4] = IT87_INIT; - - /* Enter MB PnP Mode */ - outb(init_bytes[0], IT87_ADRPORT); - outb(init_bytes[1], IT87_ADRPORT); - outb(init_bytes[2], IT87_ADRPORT); - outb(init_bytes[3], IT87_ADRPORT); - - /* deactivate CIR-Device */ - it87_write(IT87_CIR_ACT, 0x0); - - /* Leaving MB PnP Mode */ - it87_write(IT87_CFGCTRL, 0x2); -#endif - - del_timer_sync(&timerlist); - free_irq(irq, NULL); - release_region(io, 8); -} - - -static int init_lirc_it87(void) -{ - int retval; - - init_waitqueue_head(&lirc_read_queue); - retval = init_port(); - if (retval < 0) - return retval; - init_hardware(); - printk(KERN_INFO LIRC_DRIVER_NAME ": Installed.\n"); - return 0; -} - -static int it87_probe(struct pnp_dev *pnp_dev, - const struct pnp_device_id *dev_id) -{ - int retval; - - driver.dev = &pnp_dev->dev; - - retval = init_chrdev(); - if (retval < 0) - return retval; - - retval = init_lirc_it87(); - if (retval) - goto init_lirc_it87_failed; - - return 0; - -init_lirc_it87_failed: - drop_chrdev(); - - return retval; -} - -static int __init lirc_it87_init(void) -{ - return pnp_register_driver(&it87_pnp_driver); -} - - -static void __exit lirc_it87_exit(void) -{ - drop_hardware(); - drop_chrdev(); - drop_port(); - pnp_unregister_driver(&it87_pnp_driver); - printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); -} - -/* SECTION: PNP for ITE8704/13/18 */ - -static const struct pnp_device_id pnp_dev_table[] = { - {"ITE8704", 0}, - {"ITE8713", 0}, - {} -}; - -MODULE_DEVICE_TABLE(pnp, pnp_dev_table); - -static struct pnp_driver it87_pnp_driver = { - .name = LIRC_DRIVER_NAME, - .id_table = pnp_dev_table, - .probe = it87_probe, -}; - -module_init(lirc_it87_init); -module_exit(lirc_it87_exit); - -MODULE_DESCRIPTION("LIRC driver for ITE IT8704/05/12/18/20 CIR port"); -MODULE_AUTHOR("Hans-Gunter Lutke Uphues"); -MODULE_LICENSE("GPL"); - -module_param(io, int, S_IRUGO); -MODULE_PARM_DESC(io, "I/O base address (default: 0x310)"); - -module_param(irq, int, S_IRUGO); -#ifdef LIRC_IT87_DIGIMATRIX -MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)"); -#else -MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)"); -#endif - -module_param(it87_enable_demodulator, bool, S_IRUGO); -MODULE_PARM_DESC(it87_enable_demodulator, - "Receiver demodulator enable/disable (1/0), default: 0"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging messages"); - -module_param(digimatrix, bool, S_IRUGO | S_IWUSR); -#ifdef LIRC_IT87_DIGIMATRIX -MODULE_PARM_DESC(digimatrix, - "Asus Digimatrix it87 compat. enable/disable (1/0), default: 1"); -#else -MODULE_PARM_DESC(digimatrix, - "Asus Digimatrix it87 compat. enable/disable (1/0), default: 0"); -#endif - - -module_param(it87_freq, int, S_IRUGO); -#ifdef LIRC_IT87_DIGIMATRIX -MODULE_PARM_DESC(it87_freq, - "Carrier demodulator frequency (kHz), (default: 36)"); -#else -MODULE_PARM_DESC(it87_freq, - "Carrier demodulator frequency (kHz), (default: 38)"); -#endif diff --git a/drivers/staging/lirc/lirc_it87.h b/drivers/staging/lirc/lirc_it87.h deleted file mode 100644 index cf021c8..0000000 --- a/drivers/staging/lirc/lirc_it87.h +++ /dev/null @@ -1,116 +0,0 @@ -/* lirc_it87.h */ -/* SECTION: Definitions */ - -/********************************* ITE IT87xx ************************/ - -/* based on the following documentation from ITE: - a) IT8712F Preliminary CIR Programming Guide V0.1 - b) IT8705F Simple LPC I/O Preliminary Specification V0.3 - c) IT8712F EC-LPC I/O Preliminary Specification V0.5 -*/ - -/* IT8712/05 Ports: */ -#define IT87_ADRPORT 0x2e -#define IT87_DATAPORT 0x2f -#define IT87_INIT {0x87, 0x01, 0x55, 0x55} - -/* alternate Ports: */ -/* -#define IT87_ADRPORT 0x4e -#define IT87_DATAPORT 0x4f -#define IT87_INIT {0x87, 0x01, 0x55, 0xaa} - */ - -/* IT8712/05 Registers */ -#define IT87_CFGCTRL 0x2 -#define IT87_LDN 0x7 -#define IT87_CHIP_ID1 0x20 -#define IT87_CHIP_ID2 0x21 -#define IT87_CFG_VERSION 0x22 -#define IT87_SWSUSPEND 0x23 - -#define IT8712_CIR_LDN 0xa -#define IT8705_CIR_LDN 0x7 - -/* CIR Configuration Registers: */ -#define IT87_CIR_ACT 0x30 -#define IT87_CIR_BASE_MSB 0x60 -#define IT87_CIR_BASE_LSB 0x61 -#define IT87_CIR_IRQ 0x70 -#define IT87_CIR_CONFIG 0xf0 - -/* List of IT87_CIR registers: offset to BaseAddr */ -#define IT87_CIR_DR 0 -#define IT87_CIR_IER 1 -#define IT87_CIR_RCR 2 -#define IT87_CIR_TCR1 3 -#define IT87_CIR_TCR2 4 -#define IT87_CIR_TSR 5 -#define IT87_CIR_RSR 6 -#define IT87_CIR_BDLR 5 -#define IT87_CIR_BDHR 6 -#define IT87_CIR_IIR 7 - -/* Bit Definition */ -/* IER: */ -#define IT87_CIR_IER_TM_EN 0x80 -#define IT87_CIR_IER_RESEVED 0x40 -#define IT87_CIR_IER_RESET 0x20 -#define IT87_CIR_IER_BR 0x10 -#define IT87_CIR_IER_IEC 0x8 -#define IT87_CIR_IER_RFOIE 0x4 -#define IT87_CIR_IER_RDAIE 0x2 -#define IT87_CIR_IER_TLDLIE 0x1 - -/* RCR: */ -#define IT87_CIR_RCR_RDWOS 0x80 -#define IT87_CIR_RCR_HCFS 0x40 -#define IT87_CIR_RCR_RXEN 0x20 -#define IT87_CIR_RCR_RXEND 0x10 -#define IT87_CIR_RCR_RXACT 0x8 -#define IT87_CIR_RCR_RXDCR 0x7 - -/* TCR1: */ -#define IT87_CIR_TCR1_FIFOCLR 0x80 -#define IT87_CIR_TCR1_ILE 0x40 -#define IT87_CIR_TCR1_FIFOTL 0x30 -#define IT87_CIR_TCR1_TXRLE 0x8 -#define IT87_CIR_TCR1_TXENDF 0x4 -#define IT87_CIR_TCR1_TXMPM 0x3 - -/* TCR2: */ -#define IT87_CIR_TCR2_CFQ 0xf8 -#define IT87_CIR_TCR2_TXMPW 0x7 - -/* TSR: */ -#define IT87_CIR_TSR_RESERVED 0xc0 -#define IT87_CIR_TSR_TXFBC 0x3f - -/* RSR: */ -#define IT87_CIR_RSR_RXFTO 0x80 -#define IT87_CIR_RSR_RESERVED 0x40 -#define IT87_CIR_RSR_RXFBC 0x3f - -/* IIR: */ -#define IT87_CIR_IIR_RESERVED 0xf8 -#define IT87_CIR_IIR_IID 0x6 -#define IT87_CIR_IIR_IIP 0x1 - -/* TM: */ -#define IT87_CIR_TM_IL_SEL 0x80 -#define IT87_CIR_TM_RESERVED 0x40 -#define IT87_CIR_TM_TM_REG 0x3f - -#define IT87_CIR_FIFO_SIZE 32 - -/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */ -#define IT87_CIR_BAUDRATE_DIVISOR 0x1 -#define IT87_CIR_DEFAULT_IOBASE 0x310 -#define IT87_CIR_DEFAULT_IRQ 0x7 -#define IT87_CIR_SPACE 0x00 -#define IT87_CIR_PULSE 0xff -#define IT87_CIR_FREQ_MIN 27 -#define IT87_CIR_FREQ_MAX 58 -#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul) - -/********************************* ITE IT87xx ************************/ diff --git a/drivers/staging/lirc/lirc_ite8709.c b/drivers/staging/lirc/lirc_ite8709.c deleted file mode 100644 index cb20cfd..0000000 --- a/drivers/staging/lirc/lirc_ite8709.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - * LIRC driver for ITE8709 CIR port - * - * Copyright (C) 2008 Grégory Lardière - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#define LIRC_DRIVER_NAME "lirc_ite8709" - -#define BUF_CHUNK_SIZE sizeof(int) -#define BUF_SIZE (128*BUF_CHUNK_SIZE) - -/* - * The ITE8709 device seems to be the combination of IT8512 superIO chip and - * a specific firmware running on the IT8512's embedded micro-controller. - * In addition of the embedded micro-controller, the IT8512 chip contains a - * CIR module and several other modules. A few modules are directly accessible - * by the host CPU, but most of them are only accessible by the - * micro-controller. The CIR module is only accessible by the micro-controller. - * The battery-backed SRAM module is accessible by the host CPU and the - * micro-controller. So one of the MC's firmware role is to act as a bridge - * between the host CPU and the CIR module. The firmware implements a kind of - * communication protocol using the SRAM module as a shared memory. The IT8512 - * specification is publicly available on ITE's web site, but the communication - * protocol is not, so it was reverse-engineered. - */ - -/* ITE8709 Registers addresses and values (reverse-engineered) */ -#define ITE8709_MODE 0x1a -#define ITE8709_REG_ADR 0x1b -#define ITE8709_REG_VAL 0x1c -#define ITE8709_IIR 0x1e /* Interrupt identification register */ -#define ITE8709_RFSR 0x1f /* Receiver FIFO status register */ -#define ITE8709_FIFO_START 0x20 - -#define ITE8709_MODE_READY 0X00 -#define ITE8709_MODE_WRITE 0X01 -#define ITE8709_MODE_READ 0X02 -#define ITE8709_IIR_RDAI 0x02 /* Receiver data available interrupt */ -#define ITE8709_IIR_RFOI 0x04 /* Receiver FIFO overrun interrupt */ -#define ITE8709_RFSR_MASK 0x3f /* FIFO byte count mask */ - -/* - * IT8512 CIR-module registers addresses and values - * (from IT8512 E/F specification v0.4.1) - */ -#define IT8512_REG_MSTCR 0x01 /* Master control register */ -#define IT8512_REG_IER 0x02 /* Interrupt enable register */ -#define IT8512_REG_CFR 0x04 /* Carrier frequency register */ -#define IT8512_REG_RCR 0x05 /* Receive control register */ -#define IT8512_REG_BDLR 0x08 /* Baud rate divisor low byte register */ -#define IT8512_REG_BDHR 0x09 /* Baud rate divisor high byte register */ - -#define IT8512_MSTCR_RESET 0x01 /* Reset registers to default value */ -#define IT8512_MSTCR_FIFOCLR 0x02 /* Clear FIFO */ -#define IT8512_MSTCR_FIFOTL_7 0x04 /* FIFO threshold level : 7 */ -#define IT8512_MSTCR_FIFOTL_25 0x0c /* FIFO threshold level : 25 */ -#define IT8512_IER_RDAIE 0x02 /* Enable data interrupt request */ -#define IT8512_IER_RFOIE 0x04 /* Enable FIFO overrun interrupt req */ -#define IT8512_IER_IEC 0x80 /* Enable interrupt request */ -#define IT8512_CFR_CF_36KHZ 0x09 /* Carrier freq : low speed, 36kHz */ -#define IT8512_RCR_RXDCR_1 0x01 /* Demodulation carrier range : 1 */ -#define IT8512_RCR_RXACT 0x08 /* Receiver active */ -#define IT8512_RCR_RXEN 0x80 /* Receiver enable */ -#define IT8512_BDR_6 6 /* Baud rate divisor : 6 */ - -/* Actual values used by this driver */ -#define CFG_FIFOTL IT8512_MSTCR_FIFOTL_25 -#define CFG_CR_FREQ IT8512_CFR_CF_36KHZ -#define CFG_DCR IT8512_RCR_RXDCR_1 -#define CFG_BDR IT8512_BDR_6 -#define CFG_TIMEOUT 100000 /* Rearm interrupt when a space is > 100 ms */ - -static int debug; - -struct ite8709_device { - int use_count; - int io; - int irq; - spinlock_t hardware_lock; - __u64 acc_pulse; - __u64 acc_space; - char lastbit; - struct timeval last_tv; - struct lirc_driver driver; - struct tasklet_struct tasklet; - char force_rearm; - char rearmed; - char device_busy; -}; - -#define dprintk(fmt, args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ - fmt, ## args); \ - } while (0) - - -static unsigned char ite8709_read(struct ite8709_device *dev, - unsigned char port) -{ - outb(port, dev->io); - return inb(dev->io+1); -} - -static void ite8709_write(struct ite8709_device *dev, unsigned char port, - unsigned char data) -{ - outb(port, dev->io); - outb(data, dev->io+1); -} - -static void ite8709_wait_device(struct ite8709_device *dev) -{ - int i = 0; - /* - * loop until device tells it's ready to continue - * iterations count is usually ~750 but can sometimes achieve 13000 - */ - for (i = 0; i < 15000; i++) { - udelay(2); - if (ite8709_read(dev, ITE8709_MODE) == ITE8709_MODE_READY) - break; - } -} - -static void ite8709_write_register(struct ite8709_device *dev, - unsigned char reg_adr, unsigned char reg_value) -{ - ite8709_wait_device(dev); - - ite8709_write(dev, ITE8709_REG_VAL, reg_value); - ite8709_write(dev, ITE8709_REG_ADR, reg_adr); - ite8709_write(dev, ITE8709_MODE, ITE8709_MODE_WRITE); -} - -static void ite8709_init_hardware(struct ite8709_device *dev) -{ - spin_lock_irq(&dev->hardware_lock); - dev->device_busy = 1; - spin_unlock_irq(&dev->hardware_lock); - - ite8709_write_register(dev, IT8512_REG_BDHR, (CFG_BDR >> 8) & 0xff); - ite8709_write_register(dev, IT8512_REG_BDLR, CFG_BDR & 0xff); - ite8709_write_register(dev, IT8512_REG_CFR, CFG_CR_FREQ); - ite8709_write_register(dev, IT8512_REG_IER, - IT8512_IER_IEC | IT8512_IER_RFOIE | IT8512_IER_RDAIE); - ite8709_write_register(dev, IT8512_REG_RCR, CFG_DCR); - ite8709_write_register(dev, IT8512_REG_MSTCR, - CFG_FIFOTL | IT8512_MSTCR_FIFOCLR); - ite8709_write_register(dev, IT8512_REG_RCR, - IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR); - - spin_lock_irq(&dev->hardware_lock); - dev->device_busy = 0; - spin_unlock_irq(&dev->hardware_lock); - - tasklet_enable(&dev->tasklet); -} - -static void ite8709_drop_hardware(struct ite8709_device *dev) -{ - tasklet_disable(&dev->tasklet); - - spin_lock_irq(&dev->hardware_lock); - dev->device_busy = 1; - spin_unlock_irq(&dev->hardware_lock); - - ite8709_write_register(dev, IT8512_REG_RCR, 0); - ite8709_write_register(dev, IT8512_REG_MSTCR, - IT8512_MSTCR_RESET | IT8512_MSTCR_FIFOCLR); - - spin_lock_irq(&dev->hardware_lock); - dev->device_busy = 0; - spin_unlock_irq(&dev->hardware_lock); -} - -static int ite8709_set_use_inc(void *data) -{ - struct ite8709_device *dev; - dev = data; - if (dev->use_count == 0) - ite8709_init_hardware(dev); - dev->use_count++; - return 0; -} - -static void ite8709_set_use_dec(void *data) -{ - struct ite8709_device *dev; - dev = data; - dev->use_count--; - if (dev->use_count == 0) - ite8709_drop_hardware(dev); -} - -static void ite8709_add_read_queue(struct ite8709_device *dev, int flag, - __u64 val) -{ - int value; - - dprintk("add a %llu usec %s\n", val, flag ? "pulse" : "space"); - - value = (val > PULSE_MASK) ? PULSE_MASK : val; - if (flag) - value |= PULSE_BIT; - - if (!lirc_buffer_full(dev->driver.rbuf)) { - lirc_buffer_write(dev->driver.rbuf, (void *) &value); - wake_up(&dev->driver.rbuf->wait_poll); - } -} - -static irqreturn_t ite8709_interrupt(int irq, void *dev_id) -{ - unsigned char data; - int iir, rfsr, i; - int fifo = 0; - char bit; - struct timeval curr_tv; - - /* Bit duration in microseconds */ - const unsigned long bit_duration = 1000000ul / (115200 / CFG_BDR); - - struct ite8709_device *dev; - dev = dev_id; - - /* - * If device is busy, we simply discard data because we are in one of - * these two cases : shutting down or rearming the device, so this - * doesn't really matter and this avoids waiting too long in IRQ ctx - */ - spin_lock(&dev->hardware_lock); - if (dev->device_busy) { - spin_unlock(&dev->hardware_lock); - return IRQ_RETVAL(IRQ_HANDLED); - } - - iir = ite8709_read(dev, ITE8709_IIR); - - switch (iir) { - case ITE8709_IIR_RFOI: - dprintk("fifo overrun, scheduling forced rearm just in case\n"); - dev->force_rearm = 1; - tasklet_schedule(&dev->tasklet); - spin_unlock(&dev->hardware_lock); - return IRQ_RETVAL(IRQ_HANDLED); - - case ITE8709_IIR_RDAI: - rfsr = ite8709_read(dev, ITE8709_RFSR); - fifo = rfsr & ITE8709_RFSR_MASK; - if (fifo > 32) - fifo = 32; - dprintk("iir: 0x%x rfsr: 0x%x fifo: %d\n", iir, rfsr, fifo); - - if (dev->rearmed) { - do_gettimeofday(&curr_tv); - dev->acc_space += 1000000ull - * (curr_tv.tv_sec - dev->last_tv.tv_sec) - + (curr_tv.tv_usec - dev->last_tv.tv_usec); - dev->rearmed = 0; - } - for (i = 0; i < fifo; i++) { - data = ite8709_read(dev, i+ITE8709_FIFO_START); - data = ~data; - /* Loop through */ - for (bit = 0; bit < 8; ++bit) { - if ((data >> bit) & 1) { - dev->acc_pulse += bit_duration; - if (dev->lastbit == 0) { - ite8709_add_read_queue(dev, 0, - dev->acc_space); - dev->acc_space = 0; - } - } else { - dev->acc_space += bit_duration; - if (dev->lastbit == 1) { - ite8709_add_read_queue(dev, 1, - dev->acc_pulse); - dev->acc_pulse = 0; - } - } - dev->lastbit = (data >> bit) & 1; - } - } - ite8709_write(dev, ITE8709_RFSR, 0); - - if (dev->acc_space > CFG_TIMEOUT) { - dprintk("scheduling rearm IRQ\n"); - do_gettimeofday(&dev->last_tv); - dev->force_rearm = 0; - tasklet_schedule(&dev->tasklet); - } - - spin_unlock(&dev->hardware_lock); - return IRQ_RETVAL(IRQ_HANDLED); - - default: - /* not our irq */ - dprintk("unknown IRQ (shouldn't happen) !!\n"); - spin_unlock(&dev->hardware_lock); - return IRQ_RETVAL(IRQ_NONE); - } -} - -static void ite8709_rearm_irq(unsigned long data) -{ - struct ite8709_device *dev; - unsigned long flags; - dev = (struct ite8709_device *) data; - - spin_lock_irqsave(&dev->hardware_lock, flags); - dev->device_busy = 1; - spin_unlock_irqrestore(&dev->hardware_lock, flags); - - if (dev->force_rearm || dev->acc_space > CFG_TIMEOUT) { - dprintk("rearming IRQ\n"); - ite8709_write_register(dev, IT8512_REG_RCR, - IT8512_RCR_RXACT | CFG_DCR); - ite8709_write_register(dev, IT8512_REG_MSTCR, - CFG_FIFOTL | IT8512_MSTCR_FIFOCLR); - ite8709_write_register(dev, IT8512_REG_RCR, - IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR); - if (!dev->force_rearm) - dev->rearmed = 1; - dev->force_rearm = 0; - } - - spin_lock_irqsave(&dev->hardware_lock, flags); - dev->device_busy = 0; - spin_unlock_irqrestore(&dev->hardware_lock, flags); -} - -static int ite8709_cleanup(struct ite8709_device *dev, int stage, int errno, - char *msg) -{ - if (msg != NULL) - printk(KERN_ERR LIRC_DRIVER_NAME ": %s\n", msg); - - switch (stage) { - case 6: - if (dev->use_count > 0) - ite8709_drop_hardware(dev); - case 5: - free_irq(dev->irq, dev); - case 4: - release_region(dev->io, 2); - case 3: - lirc_unregister_driver(dev->driver.minor); - case 2: - lirc_buffer_free(dev->driver.rbuf); - kfree(dev->driver.rbuf); - case 1: - kfree(dev); - case 0: - ; - } - - return errno; -} - -static int __devinit ite8709_pnp_probe(struct pnp_dev *dev, - const struct pnp_device_id *dev_id) -{ - struct lirc_driver *driver; - struct ite8709_device *ite8709_dev; - int ret; - - /* Check resources validity */ - if (!pnp_irq_valid(dev, 0)) - return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IRQ"); - if (!pnp_port_valid(dev, 2)) - return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IO port"); - - /* Allocate memory for device struct */ - ite8709_dev = kzalloc(sizeof(struct ite8709_device), GFP_KERNEL); - if (ite8709_dev == NULL) - return ite8709_cleanup(NULL, 0, -ENOMEM, "kzalloc failed"); - pnp_set_drvdata(dev, ite8709_dev); - - /* Initialize device struct */ - ite8709_dev->use_count = 0; - ite8709_dev->irq = pnp_irq(dev, 0); - ite8709_dev->io = pnp_port_start(dev, 2); - ite8709_dev->hardware_lock = - __SPIN_LOCK_UNLOCKED(ite8709_dev->hardware_lock); - ite8709_dev->acc_pulse = 0; - ite8709_dev->acc_space = 0; - ite8709_dev->lastbit = 0; - do_gettimeofday(&ite8709_dev->last_tv); - tasklet_init(&ite8709_dev->tasklet, ite8709_rearm_irq, - (long) ite8709_dev); - ite8709_dev->force_rearm = 0; - ite8709_dev->rearmed = 0; - ite8709_dev->device_busy = 0; - - /* Initialize driver struct */ - driver = &ite8709_dev->driver; - strcpy(driver->name, LIRC_DRIVER_NAME); - driver->minor = -1; - driver->code_length = sizeof(int) * 8; - driver->sample_rate = 0; - driver->features = LIRC_CAN_REC_MODE2; - driver->data = ite8709_dev; - driver->add_to_buf = NULL; - driver->set_use_inc = ite8709_set_use_inc; - driver->set_use_dec = ite8709_set_use_dec; - driver->dev = &dev->dev; - driver->owner = THIS_MODULE; - - /* Initialize LIRC buffer */ - driver->rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); - if (!driver->rbuf) - return ite8709_cleanup(ite8709_dev, 1, -ENOMEM, - "can't allocate lirc_buffer"); - if (lirc_buffer_init(driver->rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) - return ite8709_cleanup(ite8709_dev, 1, -ENOMEM, - "lirc_buffer_init() failed"); - - /* Register LIRC driver */ - ret = lirc_register_driver(driver); - if (ret < 0) - return ite8709_cleanup(ite8709_dev, 2, ret, - "lirc_register_driver() failed"); - - /* Reserve I/O port access */ - if (!request_region(ite8709_dev->io, 2, LIRC_DRIVER_NAME)) - return ite8709_cleanup(ite8709_dev, 3, -EBUSY, - "i/o port already in use"); - - /* Reserve IRQ line */ - ret = request_irq(ite8709_dev->irq, ite8709_interrupt, 0, - LIRC_DRIVER_NAME, ite8709_dev); - if (ret < 0) - return ite8709_cleanup(ite8709_dev, 4, ret, - "IRQ already in use"); - - /* Initialize hardware */ - ite8709_drop_hardware(ite8709_dev); /* Shutdown hw until first use */ - - printk(KERN_INFO LIRC_DRIVER_NAME ": device found : irq=%d io=0x%x\n", - ite8709_dev->irq, ite8709_dev->io); - - return 0; -} - -static void __devexit ite8709_pnp_remove(struct pnp_dev *dev) -{ - struct ite8709_device *ite8709_dev; - ite8709_dev = pnp_get_drvdata(dev); - - ite8709_cleanup(ite8709_dev, 6, 0, NULL); - - printk(KERN_INFO LIRC_DRIVER_NAME ": device removed\n"); -} - -#ifdef CONFIG_PM -static int ite8709_pnp_suspend(struct pnp_dev *dev, pm_message_t state) -{ - struct ite8709_device *ite8709_dev; - ite8709_dev = pnp_get_drvdata(dev); - - if (ite8709_dev->use_count > 0) - ite8709_drop_hardware(ite8709_dev); - - return 0; -} - -static int ite8709_pnp_resume(struct pnp_dev *dev) -{ - struct ite8709_device *ite8709_dev; - ite8709_dev = pnp_get_drvdata(dev); - - if (ite8709_dev->use_count > 0) - ite8709_init_hardware(ite8709_dev); - - return 0; -} -#else -#define ite8709_pnp_suspend NULL -#define ite8709_pnp_resume NULL -#endif - -static const struct pnp_device_id pnp_dev_table[] = { - {"ITE8709", 0}, - {} -}; - -MODULE_DEVICE_TABLE(pnp, pnp_dev_table); - -static struct pnp_driver ite8709_pnp_driver = { - .name = LIRC_DRIVER_NAME, - .probe = ite8709_pnp_probe, - .remove = __devexit_p(ite8709_pnp_remove), - .suspend = ite8709_pnp_suspend, - .resume = ite8709_pnp_resume, - .id_table = pnp_dev_table, -}; - -static int __init ite8709_init_module(void) -{ - return pnp_register_driver(&ite8709_pnp_driver); -} -module_init(ite8709_init_module); - -static void __exit ite8709_cleanup_module(void) -{ - pnp_unregister_driver(&ite8709_pnp_driver); -} -module_exit(ite8709_cleanup_module); - -MODULE_DESCRIPTION("LIRC driver for ITE8709 CIR port"); -MODULE_AUTHOR("Grégory Lardière"); -MODULE_LICENSE("GPL"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging messages"); -- cgit v0.10.2 From 2aefbc1acb5370b0753d16c9d6e65e01fe586c75 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Thu, 17 Mar 2011 21:08:55 -0300 Subject: [media] tm6000: fix s-video input Add compatibility for composite and s-video inputs. Some TV cards hasn't it. Fix S-Video input, the s-video cable has only video signals no audio. Call the function of audio configure kill chroma in signal. only b/w video. Known bugs: - after s-video the audio for radio didn't work, TV crashed hardly - after composite TV crashed hardly too. P.S. After this patch I'll want to rework the procedure of configure video. Now it has a lot of junk and dubles. With my best regards, Dmitry. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index 88144a1..146c7e8 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -235,11 +235,13 @@ struct tm6000_board tm6000_boards[] = { .avideo = TM6000_AIP_SIF1, .aradio = TM6000_AIP_LINE1, .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_input_comp = 1, + .has_input_svid = 1, }, .gpio = { .tuner_reset = TM6010_GPIO_0, @@ -255,11 +257,13 @@ struct tm6000_board tm6000_boards[] = { .avideo = TM6000_AIP_SIF1, .aradio = TM6000_AIP_LINE1, .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, - .has_remote = 1, + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 1, + .has_input_comp = 1, + .has_input_svid = 1, }, .gpio = { .tuner_reset = TM6010_GPIO_0, @@ -327,10 +331,13 @@ struct tm6000_board tm6000_boards[] = { .avideo = TM6000_AIP_SIF1, .aradio = TM6000_AIP_LINE1, .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 0, + .has_input_comp = 0, + .has_input_svid = 0, }, .gpio = { .tuner_reset = TM6010_GPIO_0, @@ -346,10 +353,13 @@ struct tm6000_board tm6000_boards[] = { .avideo = TM6000_AIP_SIF1, .aradio = TM6000_AIP_LINE1, .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 0, + .has_input_comp = 0, + .has_input_svid = 0, }, .gpio = { .tuner_reset = TM6010_GPIO_0, diff --git a/drivers/staging/tm6000/tm6000-stds.c b/drivers/staging/tm6000/tm6000-stds.c index a4c07e5..da3e51b 100644 --- a/drivers/staging/tm6000/tm6000-stds.c +++ b/drivers/staging/tm6000/tm6000-stds.c @@ -1161,8 +1161,6 @@ int tm6000_set_standard(struct tm6000_core *dev, v4l2_std_id * norm) rc = tm6000_load_std(dev, svideo_stds[i].common, sizeof(svideo_stds[i]. common)); - tm6000_set_audio_std(dev, svideo_stds[i].audio_default_std); - goto ret; } } diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/staging/tm6000/tm6000-video.c index b550340..c80a316 100644 --- a/drivers/staging/tm6000/tm6000-video.c +++ b/drivers/staging/tm6000/tm6000-video.c @@ -1080,18 +1080,27 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *norm) static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + switch (inp->index) { case TM6000_INPUT_TV: inp->type = V4L2_INPUT_TYPE_TUNER; strcpy(inp->name, "Television"); break; case TM6000_INPUT_COMPOSITE: - inp->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(inp->name, "Composite"); + if (dev->caps.has_input_comp) { + inp->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(inp->name, "Composite"); + } else + return -EINVAL; break; case TM6000_INPUT_SVIDEO: - inp->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(inp->name, "S-Video"); + if (dev->caps.has_input_svid) { + inp->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(inp->name, "S-Video"); + } else + return -EINVAL; break; default: return -EINVAL; diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/staging/tm6000/tm6000.h index ccd120f..99ae50e 100644 --- a/drivers/staging/tm6000/tm6000.h +++ b/drivers/staging/tm6000/tm6000.h @@ -129,6 +129,8 @@ struct tm6000_capabilities { unsigned int has_zl10353:1; unsigned int has_eeprom:1; unsigned int has_remote:1; + unsigned int has_input_comp:1; + unsigned int has_input_svid:1; }; struct tm6000_dvb { -- cgit v0.10.2 From fe06b11dea74d53156d4116dcd518193a2e42ba4 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 24 Jan 2011 18:22:12 -0300 Subject: [media] docs: fix typo in lirc_device_interface.xml Reported-by: Daniel Burr Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/lirc_device_interface.xml b/Documentation/DocBook/v4l/lirc_device_interface.xml index 68134c0..0e0453f 100644 --- a/Documentation/DocBook/v4l/lirc_device_interface.xml +++ b/Documentation/DocBook/v4l/lirc_device_interface.xml @@ -45,7 +45,7 @@ describing an IR signal are read from the chardev. The data written to the chardev is a pulse/space sequence of integer values. Pulses and spaces are only marked implicitly by their position. The data must start and end with a pulse, therefore, the data must always include -an unevent number of samples. The write function must block until the data has +an uneven number of samples. The write function must block until the data has been transmitted by the hardware.
-- cgit v0.10.2 From 53a5fd4dede974ba9727cda430e6714293be5e71 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Tue, 1 Feb 2011 16:27:05 -0300 Subject: [media] imon: add more panel scancode mappings Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index e7dc6b4..f714e1a 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -277,12 +277,21 @@ static const struct { u64 hw_code; u32 keycode; } imon_panel_key_table[] = { - { 0x000000000f00ffeell, KEY_PROG1 }, /* Go */ + { 0x000000000f00ffeell, KEY_MEDIA }, /* Go */ + { 0x000000001200ffeell, KEY_UP }, + { 0x000000001300ffeell, KEY_DOWN }, + { 0x000000001400ffeell, KEY_LEFT }, + { 0x000000001500ffeell, KEY_RIGHT }, + { 0x000000001600ffeell, KEY_ENTER }, + { 0x000000001700ffeell, KEY_ESC }, { 0x000000001f00ffeell, KEY_AUDIO }, { 0x000000002000ffeell, KEY_VIDEO }, { 0x000000002100ffeell, KEY_CAMERA }, { 0x000000002700ffeell, KEY_DVD }, { 0x000000002300ffeell, KEY_TV }, + { 0x000000002b00ffeell, KEY_EXIT }, + { 0x000000002c00ffeell, KEY_SELECT }, + { 0x000000002d00ffeell, KEY_MENU }, { 0x000000000500ffeell, KEY_PREVIOUS }, { 0x000000000700ffeell, KEY_REWIND }, { 0x000000000400ffeell, KEY_STOP }, -- cgit v0.10.2 From b443ac5a2836532af2315a4acb95c7a2cf721cbd Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Wed, 2 Mar 2011 13:23:52 -0300 Subject: [media] hdpvr: i2c master enhancements Make the hdpvr's i2c master implementation more closely mirror that of the pvrusb2 driver. Currently makes no significant difference in IR reception behavior with ir-kbd-i2c (i.e., it still sucks). Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c index e53fa55..3e06587 100644 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -62,15 +62,25 @@ struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev) } static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, - unsigned char addr, char *data, int len) + unsigned char addr, char *wdata, int wlen, + char *data, int len) { int ret; - if (len > sizeof(dev->i2c_buf)) + if ((len > sizeof(dev->i2c_buf)) || (wlen > sizeof(dev->i2c_buf))) return -EINVAL; - ret = usb_control_msg(dev->udev, - usb_rcvctrlpipe(dev->udev, 0), + if (wlen) { + memcpy(&dev->i2c_buf, wdata, wlen); + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, + (bus << 8) | addr, 0, &dev->i2c_buf, + wlen, 1000); + if (ret < 0) + return ret; + } + + ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), REQTYPE_I2C_READ, CTRL_READ_REQUEST, (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); @@ -92,16 +102,14 @@ static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus, return -EINVAL; memcpy(&dev->i2c_buf, data, len); - ret = usb_control_msg(dev->udev, - usb_sndctrlpipe(dev->udev, 0), + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); if (ret < 0) return ret; - ret = usb_control_msg(dev->udev, - usb_rcvctrlpipe(dev->udev, 0), + ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST, 0, 0, &dev->i2c_buf, 2, 1000); @@ -117,24 +125,49 @@ static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs, int num) { struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter); - int retval = 0, i, addr; + int retval = 0, addr; if (num <= 0) return 0; mutex_lock(&dev->i2c_mutex); - for (i = 0; i < num && !retval; i++) { - addr = msgs[i].addr << 1; + addr = msgs[0].addr << 1; - if (msgs[i].flags & I2C_M_RD) - retval = hdpvr_i2c_read(dev, 1, addr, msgs[i].buf, - msgs[i].len); + if (num == 1) { + if (msgs[0].flags & I2C_M_RD) + retval = hdpvr_i2c_read(dev, 1, addr, NULL, 0, + msgs[0].buf, msgs[0].len); else - retval = hdpvr_i2c_write(dev, 1, addr, msgs[i].buf, - msgs[i].len); + retval = hdpvr_i2c_write(dev, 1, addr, msgs[0].buf, + msgs[0].len); + } else if (num == 2) { + if (msgs[0].addr != msgs[1].addr) { + v4l2_warn(&dev->v4l2_dev, "refusing 2-phase i2c xfer " + "with conflicting target addresses\n"); + retval = -EINVAL; + goto out; + } + + if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) { + v4l2_warn(&dev->v4l2_dev, "refusing complex xfer with " + "r0=%d, r1=%d\n", msgs[0].flags & I2C_M_RD, + msgs[1].flags & I2C_M_RD); + retval = -EINVAL; + goto out; + } + + /* + * Write followed by atomic read is the only complex xfer that + * we actually support here. + */ + retval = hdpvr_i2c_read(dev, 1, addr, msgs[0].buf, msgs[0].len, + msgs[1].buf, msgs[1].len); + } else { + v4l2_warn(&dev->v4l2_dev, "refusing %d-phase i2c xfer\n", num); } +out: mutex_unlock(&dev->i2c_mutex); return retval ? retval : num; @@ -162,7 +195,7 @@ static int hdpvr_activate_ir(struct hdpvr_device *dev) mutex_lock(&dev->i2c_mutex); - hdpvr_i2c_read(dev, 0, 0x54, buffer, 1); + hdpvr_i2c_read(dev, 0, 0x54, NULL, 0, buffer, 1); buffer[0] = 0; buffer[1] = 0x8; -- cgit v0.10.2 From 12d896e1c17978599a98510fdfaff27a4e82c0bf Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 26 Jan 2011 21:06:43 -0300 Subject: [media] lirc_zilog: Restore checks for existence of the IR_tx object This reverts commit 8090232a237ab62e22307fc060097da1a283dd66 and adds an additional check for ir->tx == NULL. The user may need us to handle an RX only unit. Apparently there are TV capture units in existence with Rx only wiring and/or RX only firmware for the on-board Zilog Z8 IR unit. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 0aad0d7..7389b77 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -209,7 +209,8 @@ static int add_to_buf(struct IR *ir) return -ENODATA; } schedule_timeout((100 * HZ + 999) / 1000); - ir->tx->need_boot = 1; + if (ir->tx != NULL) + ir->tx->need_boot = 1; ++failures; mutex_unlock(&ir->ir_lock); @@ -1032,9 +1033,10 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) int result; unsigned long mode, features = 0; - features |= LIRC_CAN_SEND_PULSE; if (ir->rx != NULL) features |= LIRC_CAN_REC_LIRCCODE; + if (ir->tx != NULL) + features |= LIRC_CAN_SEND_PULSE; switch (cmd) { case LIRC_GET_LENGTH: @@ -1061,9 +1063,15 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) result = -EINVAL; break; case LIRC_GET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg); break; case LIRC_SET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + result = get_user(mode, (unsigned long *) arg); if (!result && mode != LIRC_MODE_PULSE) return -EINVAL; @@ -1242,8 +1250,10 @@ static int ir_remove(struct i2c_client *client) } /* Good-bye Tx */ - i2c_set_clientdata(ir->tx->c, NULL); - kfree(ir->tx); + if (ir->tx != NULL) { + i2c_set_clientdata(ir->tx->c, NULL); + kfree(ir->tx); + } /* Good-bye IR */ del_ir_device(ir); @@ -1393,9 +1403,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) * after registering with lirc as otherwise hotplug seems to take * 10s to create the lirc device. */ - ret = tx_init(ir->tx); - if (ret != 0) - goto out_unregister; + if (ir->tx != NULL) { + /* Special TX init */ + ret = tx_init(ir->tx); + if (ret != 0) + goto out_unregister; + } zilog_info("probe of IR %s on %s (i2c-%d) done. IR unit ready.\n", tx_probe ? "Tx" : "Rx", adap->name, adap->nr); -- cgit v0.10.2 From 9b28500a59d7ee5da5adb43bcb384391cac401c4 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 26 Jan 2011 21:25:47 -0300 Subject: [media] lirc_zilog: Remove broken, ineffective reference counting The set_use_inc() and set_use_dec() functions tried to lock the underlying bridge driver device instance in memory by changing the use count on the device's i2c_clients. This worked for PCI devices (ivtv, cx18, bttv). It doesn't work for hot-pluggable usb devices (pvrusb2 and hdpvr). With usb device instances, the driver may get locked into memory, but the unplugged hardware is gone. The set_use_inc() set_use_dec() functions also tried to have lirc_zilog change its own module refernce count, which is racy and not guaranteed to work. The lirc_dev module does actually perform proper module ref count manipulation on the lirc_zilog module, so there is need for lirc_zilog to attempt a buggy module get on itself anyway. lirc_zilog also errantly called these functions on itself in open() and close(), but lirc_dev did that already too. So let's just gut the bodies of the set_use_*() functions, and remove the extra calls to them from within lirc_zilog. Proper reference counting of the struct IR, IR_rx, and IR_tx objects -- to handle the case when the underlying bttv, ivtv, cx18, hdpvr, or pvrusb2 bridge driver module or device instance goes away -- will be added in subsequent patches. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 7389b77..3a91257 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -305,34 +305,12 @@ static int lirc_thread(void *arg) static int set_use_inc(void *data) { - struct IR *ir = data; - - if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0) - return -ENODEV; - - /* lock bttv in memory while /dev/lirc is in use */ - /* - * this is completely broken code. lirc_unregister_driver() - * must be possible even when the device is open - */ - if (ir->rx != NULL) - i2c_use_client(ir->rx->c); - if (ir->tx != NULL) - i2c_use_client(ir->tx->c); - return 0; } static void set_use_dec(void *data) { - struct IR *ir = data; - - if (ir->rx) - i2c_release_client(ir->rx->c); - if (ir->tx) - i2c_release_client(ir->tx->c); - if (ir->l.owner != NULL) - module_put(ir->l.owner); + return; } /* safe read of a uint32 (always network byte order) */ @@ -1098,7 +1076,6 @@ static struct IR *find_ir_device_by_minor(unsigned int minor) static int open(struct inode *node, struct file *filep) { struct IR *ir; - int ret; unsigned int minor = MINOR(node->i_rdev); /* find our IR struct */ @@ -1112,12 +1089,6 @@ static int open(struct inode *node, struct file *filep) /* increment in use count */ mutex_lock(&ir->ir_lock); ++ir->open; - ret = set_use_inc(ir); - if (ret != 0) { - --ir->open; - mutex_unlock(&ir->ir_lock); - return ret; - } mutex_unlock(&ir->ir_lock); /* stash our IR struct */ @@ -1139,7 +1110,6 @@ static int close(struct inode *node, struct file *filep) /* decrement in use count */ mutex_lock(&ir->ir_lock); --ir->open; - set_use_dec(ir); mutex_unlock(&ir->ir_lock); return 0; -- cgit v0.10.2 From 5c07134fff9e6b1f9a3c75e62bbb827d1faa72eb Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 26 Jan 2011 21:48:32 -0300 Subject: [media] lirc_zilog: Convert ir_device instance array to a linked list Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 3a91257..39f7b53 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -89,6 +89,8 @@ struct IR_tx { }; struct IR { + struct list_head list; + struct lirc_driver l; struct mutex ir_lock; @@ -99,9 +101,9 @@ struct IR { struct IR_tx *tx; }; -/* Minor -> data mapping */ -static struct mutex ir_devices_lock; -static struct IR *ir_devices[MAX_IRCTL_DEVICES]; +/* IR transceiver instance object list */ +static DEFINE_MUTEX(ir_devices_lock); +static LIST_HEAD(ir_devices_list); /* Block size for IR transmitter */ #define TX_BLOCK_SIZE 99 @@ -1063,10 +1065,16 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) /* ir_devices_lock must be held */ static struct IR *find_ir_device_by_minor(unsigned int minor) { - if (minor >= MAX_IRCTL_DEVICES) + struct IR *ir; + + if (list_empty(&ir_devices_list)) return NULL; - return ir_devices[minor]; + list_for_each_entry(ir, &ir_devices_list, list) + if (ir->l.minor == minor) + return ir; + + return NULL; } /* @@ -1172,25 +1180,21 @@ static void destroy_rx_kthread(struct IR_rx *rx) /* ir_devices_lock must be held */ static int add_ir_device(struct IR *ir) { - int i; - - for (i = 0; i < MAX_IRCTL_DEVICES; i++) - if (ir_devices[i] == NULL) { - ir_devices[i] = ir; - break; - } - - return i == MAX_IRCTL_DEVICES ? -ENOMEM : i; + list_add_tail(&ir->list, &ir_devices_list); + return 0; } /* ir_devices_lock must be held */ static void del_ir_device(struct IR *ir) { - int i; + struct IR *p; + + if (list_empty(&ir_devices_list)) + return; - for (i = 0; i < MAX_IRCTL_DEVICES; i++) - if (ir_devices[i] == ir) { - ir_devices[i] = NULL; + list_for_each_entry(p, &ir_devices_list, list) + if (p == ir) { + list_del(&p->list); break; } } @@ -1237,17 +1241,16 @@ static int ir_remove(struct i2c_client *client) /* ir_devices_lock must be held */ static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter) { - int i; - struct IR *ir = NULL; + struct IR *ir; - for (i = 0; i < MAX_IRCTL_DEVICES; i++) - if (ir_devices[i] != NULL && - ir_devices[i]->adapter == adapter) { - ir = ir_devices[i]; - break; - } + if (list_empty(&ir_devices_list)) + return NULL; + + list_for_each_entry(ir, &ir_devices_list, list) + if (ir->adapter == adapter) + return ir; - return ir; + return NULL; } static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -1284,6 +1287,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_no_ir; } /* store for use in ir_probe() again, and open() later on */ + INIT_LIST_HEAD(&ir->list); ret = add_ir_device(ir); if (ret) goto out_free_ir; @@ -1421,7 +1425,6 @@ static int __init zilog_init(void) zilog_notify("Zilog/Hauppauge IR driver initializing\n"); mutex_init(&tx_data_lock); - mutex_init(&ir_devices_lock); request_module("firmware_class"); -- cgit v0.10.2 From a30648550f2f758c9c00c493c8919f89f65438c4 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 26 Jan 2011 22:04:24 -0300 Subject: [media] lirc_zilog: Convert the instance open count to an atomic_t The open count is simply used for deciding if the Rx polling thread needs to poll the IR chip for userspace. Simplify the manipulation of the open count by using an atomic_t and not requiring a lock The polling thread errantly didn't try to take the lock anyway. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 39f7b53..c857b99 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -94,7 +94,7 @@ struct IR { struct lirc_driver l; struct mutex ir_lock; - int open; + atomic_t open_count; struct i2c_adapter *adapter; struct IR_rx *rx; @@ -279,7 +279,7 @@ static int lirc_thread(void *arg) set_current_state(TASK_INTERRUPTIBLE); /* if device not opened, we can sleep half a second */ - if (!ir->open) { + if (atomic_read(&ir->open_count) == 0) { schedule_timeout(HZ/2); continue; } @@ -1094,10 +1094,7 @@ static int open(struct inode *node, struct file *filep) if (ir == NULL) return -ENODEV; - /* increment in use count */ - mutex_lock(&ir->ir_lock); - ++ir->open; - mutex_unlock(&ir->ir_lock); + atomic_inc(&ir->open_count); /* stash our IR struct */ filep->private_data = ir; @@ -1115,10 +1112,7 @@ static int close(struct inode *node, struct file *filep) return -ENODEV; } - /* decrement in use count */ - mutex_lock(&ir->ir_lock); - --ir->open; - mutex_unlock(&ir->ir_lock); + atomic_dec(&ir->open_count); return 0; } @@ -1294,6 +1288,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->adapter = adap; mutex_init(&ir->ir_lock); + atomic_set(&ir->open_count, 0); /* set lirc_dev stuff */ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); -- cgit v0.10.2 From 9a55a2b37eb7e4941ef55aadb858b615d229afee Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 00:04:15 -0300 Subject: [media] lirc_zilog: Use kernel standard methods for marking device non-seekable lirc_zilog had its own llseek stub that returned -ESPIPE. Get rid of it and use the kernel's no_llseek() and nonseekable_open() functions instead. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index c857b99..720ef67 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -712,12 +712,6 @@ static int tx_init(struct IR_tx *tx) return 0; } -/* do nothing stub to make LIRC happy */ -static loff_t lseek(struct file *filep, loff_t offset, int orig) -{ - return -ESPIPE; -} - /* copied from lirc_dev */ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) { @@ -1099,6 +1093,7 @@ static int open(struct inode *node, struct file *filep) /* stash our IR struct */ filep->private_data = ir; + nonseekable_open(node, filep); return 0; } @@ -1150,7 +1145,7 @@ static struct i2c_driver driver = { static const struct file_operations lirc_fops = { .owner = THIS_MODULE, - .llseek = lseek, + .llseek = no_llseek, .read = read, .write = write, .poll = poll, -- cgit v0.10.2 From 915e54733de4a36742ab78b4768750ce72662f85 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 02:10:42 -0300 Subject: [media] lirc_zilog: Don't acquire the rx->buf_lock in the poll() function There is no need to take the rx->buf_lock in the the poll() function as all the underling calls made on objects in the rx->buf lirc_buffer object are protected by spinlocks. Corrected a bad error return value in poll(): return POLLERR instead of -ENODEV. Added some comments to poll() for when, in the future, I forget what poll() and poll_wait() are supposed to do. [Jarod: minor debug spew fix] Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 720ef67..695c3df 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -985,19 +985,26 @@ static unsigned int poll(struct file *filep, poll_table *wait) unsigned int ret; dprintk("poll called\n"); - if (rx == NULL) - return -ENODEV; - mutex_lock(&rx->buf_lock); + if (rx == NULL) { + /* + * Revisit this, if our poll function ever reports writeable + * status for Tx + */ + dprintk("poll result = POLLERR\n"); + return POLLERR; + } + /* + * Add our lirc_buffer's wait_queue to the poll_table. A wake up on + * that buffer's wait queue indicates we may have a new poll status. + */ poll_wait(filep, &rx->buf.wait_poll, wait); - dprintk("poll result = %s\n", - lirc_buffer_empty(&rx->buf) ? "0" : "POLLIN|POLLRDNORM"); - + /* Indicate what ops could happen immediately without blocking */ ret = lirc_buffer_empty(&rx->buf) ? 0 : (POLLIN|POLLRDNORM); - mutex_unlock(&rx->buf_lock); + dprintk("poll result = %s\n", ret ? "POLLIN|POLLRDNORM" : "none"); return ret; } -- cgit v0.10.2 From 8152b760effbff11cb4eef6292ae3b47719ee792 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 02:34:13 -0300 Subject: [media] lirc_zilog: Remove unneeded rx->buf_lock Remove the rx->buf_lock that protected the rx->buf lirc_buffer. The underlying operations on the objects within the lirc_buffer are already protected by spinlocks, or the objects are constant (e.g. chunk_size). Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 695c3df..a62b94b 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -67,9 +67,8 @@ struct IR_rx { /* RX device */ struct i2c_client *c; - /* RX device buffer & lock */ + /* RX device buffer */ struct lirc_buffer buf; - struct mutex buf_lock; /* RX polling thread data */ struct task_struct *task; @@ -718,18 +717,15 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) struct IR *ir = filep->private_data; struct IR_rx *rx = ir->rx; int ret = 0, written = 0; + unsigned int m; DECLARE_WAITQUEUE(wait, current); dprintk("read called\n"); if (rx == NULL) return -ENODEV; - if (mutex_lock_interruptible(&rx->buf_lock)) - return -ERESTARTSYS; - if (n % rx->buf.chunk_size) { dprintk("read result = -EINVAL\n"); - mutex_unlock(&rx->buf_lock); return -EINVAL; } @@ -767,19 +763,19 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) set_current_state(TASK_INTERRUPTIBLE); } else { unsigned char buf[rx->buf.chunk_size]; - lirc_buffer_read(&rx->buf, buf); - ret = copy_to_user((void *)outbuf+written, buf, - rx->buf.chunk_size); - written += rx->buf.chunk_size; + m = lirc_buffer_read(&rx->buf, buf); + if (m == rx->buf.chunk_size) { + ret = copy_to_user((void *)outbuf+written, buf, + rx->buf.chunk_size); + written += rx->buf.chunk_size; + } } } remove_wait_queue(&rx->buf.wait_poll, &wait); set_current_state(TASK_RUNNING); - mutex_unlock(&rx->buf_lock); - dprintk("read result = %s (%d)\n", - ret ? "-EFAULT" : "OK", ret); + dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK"); return ret ? ret : written; } @@ -1327,7 +1323,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) if (ret) goto out_free_xx; - mutex_init(&ir->rx->buf_lock); ir->rx->c = client; ir->rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; -- cgit v0.10.2 From bcbd165509da05a2317ac7dce3c6b952e98a01ef Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 22:32:44 -0300 Subject: [media] lirc_zilog: Always allocate a Rx lirc_buffer object Always allocate a lirc_buffer object, instead of just upon setup of the Rx i2c_client. If we do not allocate a lirc_buffer object, because we are not handling the Rx i2c_client, lirc_dev will allocate its own lirc_buffer anyway and not tell us about its location. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index a62b94b..053fddb 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -67,9 +67,6 @@ struct IR_rx { /* RX device */ struct i2c_client *c; - /* RX device buffer */ - struct lirc_buffer buf; - /* RX polling thread data */ struct task_struct *task; @@ -91,6 +88,7 @@ struct IR { struct list_head list; struct lirc_driver l; + struct lirc_buffer rbuf; struct mutex ir_lock; atomic_t open_count; @@ -157,12 +155,13 @@ static int add_to_buf(struct IR *ir) int ret; int failures = 0; unsigned char sendbuf[1] = { 0 }; + struct lirc_buffer *rbuf = ir->l.rbuf; struct IR_rx *rx = ir->rx; if (rx == NULL) return -ENXIO; - if (lirc_buffer_full(&rx->buf)) { + if (lirc_buffer_full(rbuf)) { dprintk("buffer overflow\n"); return -EOVERFLOW; } @@ -250,9 +249,9 @@ static int add_to_buf(struct IR *ir) codes[1] = code & 0xff; /* return it */ - lirc_buffer_write(&rx->buf, codes); + lirc_buffer_write(rbuf, codes); ++got_data; - } while (!lirc_buffer_full(&rx->buf)); + } while (!lirc_buffer_full(rbuf)); return 0; } @@ -270,7 +269,7 @@ static int add_to_buf(struct IR *ir) static int lirc_thread(void *arg) { struct IR *ir = arg; - struct IR_rx *rx = ir->rx; + struct lirc_buffer *rbuf = ir->l.rbuf; dprintk("poll thread started\n"); @@ -297,7 +296,7 @@ static int lirc_thread(void *arg) if (kthread_should_stop()) break; if (!add_to_buf(ir)) - wake_up_interruptible(&rx->buf.wait_poll); + wake_up_interruptible(&rbuf->wait_poll); } dprintk("poll thread ended\n"); @@ -716,6 +715,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) { struct IR *ir = filep->private_data; struct IR_rx *rx = ir->rx; + struct lirc_buffer *rbuf = ir->l.rbuf; int ret = 0, written = 0; unsigned int m; DECLARE_WAITQUEUE(wait, current); @@ -724,7 +724,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) if (rx == NULL) return -ENODEV; - if (n % rx->buf.chunk_size) { + if (n % rbuf->chunk_size) { dprintk("read result = -EINVAL\n"); return -EINVAL; } @@ -734,7 +734,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) * to avoid losing scan code (in case when queue is awaken somewhere * between while condition checking and scheduling) */ - add_wait_queue(&rx->buf.wait_poll, &wait); + add_wait_queue(&rbuf->wait_poll, &wait); set_current_state(TASK_INTERRUPTIBLE); /* @@ -742,7 +742,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) * mode and 'copy_to_user' is happy, wait for data. */ while (written < n && ret == 0) { - if (lirc_buffer_empty(&rx->buf)) { + if (lirc_buffer_empty(rbuf)) { /* * According to the read(2) man page, 'written' can be * returned as less than 'n', instead of blocking @@ -762,17 +762,17 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) schedule(); set_current_state(TASK_INTERRUPTIBLE); } else { - unsigned char buf[rx->buf.chunk_size]; - m = lirc_buffer_read(&rx->buf, buf); - if (m == rx->buf.chunk_size) { + unsigned char buf[rbuf->chunk_size]; + m = lirc_buffer_read(rbuf, buf); + if (m == rbuf->chunk_size) { ret = copy_to_user((void *)outbuf+written, buf, - rx->buf.chunk_size); - written += rx->buf.chunk_size; + rbuf->chunk_size); + written += rbuf->chunk_size; } } } - remove_wait_queue(&rx->buf.wait_poll, &wait); + remove_wait_queue(&rbuf->wait_poll, &wait); set_current_state(TASK_RUNNING); dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK"); @@ -978,6 +978,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) { struct IR *ir = filep->private_data; struct IR_rx *rx = ir->rx; + struct lirc_buffer *rbuf = ir->l.rbuf; unsigned int ret; dprintk("poll called\n"); @@ -995,10 +996,10 @@ static unsigned int poll(struct file *filep, poll_table *wait) * Add our lirc_buffer's wait_queue to the poll_table. A wake up on * that buffer's wait queue indicates we may have a new poll status. */ - poll_wait(filep, &rx->buf.wait_poll, wait); + poll_wait(filep, &rbuf->wait_poll, wait); /* Indicate what ops could happen immediately without blocking */ - ret = lirc_buffer_empty(&rx->buf) ? 0 : (POLLIN|POLLRDNORM); + ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM); dprintk("poll result = %s\n", ret ? "POLLIN|POLLRDNORM" : "none"); return ret; @@ -1209,8 +1210,6 @@ static int ir_remove(struct i2c_client *client) /* Good-bye Rx */ destroy_rx_kthread(ir->rx); if (ir->rx != NULL) { - if (ir->rx->buf.fifo_initialized) - lirc_buffer_free(&ir->rx->buf); i2c_set_clientdata(ir->rx->c, NULL); kfree(ir->rx); } @@ -1222,6 +1221,8 @@ static int ir_remove(struct i2c_client *client) } /* Good-bye IR */ + if (ir->rbuf.fifo_initialized) + lirc_buffer_free(&ir->rbuf); del_ir_device(ir); kfree(ir); @@ -1292,11 +1293,17 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); ir->l.minor = minor; /* module option */ ir->l.code_length = 13; - ir->l.rbuf = NULL; + ir->l.chunk_size = 2; + ir->l.buffer_size = BUFLEN / 2; + ir->l.rbuf = &ir->rbuf; ir->l.fops = &lirc_fops; ir->l.data = ir; ir->l.dev = &adap->dev; ir->l.sample_rate = 0; + ret = lirc_buffer_init(ir->l.rbuf, + ir->l.chunk_size, ir->l.buffer_size); + if (ret) + goto out_free_ir; } if (tx_probe) { @@ -1319,16 +1326,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_free_xx; } - ret = lirc_buffer_init(&ir->rx->buf, 2, BUFLEN / 2); - if (ret) - goto out_free_xx; - ir->rx->c = client; ir->rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; - - /* set lirc_dev stuff */ - ir->l.rbuf = &ir->rx->buf; } i2c_set_clientdata(client, ir); @@ -1388,8 +1388,6 @@ out_free_thread: destroy_rx_kthread(ir->rx); out_free_xx: if (ir->rx != NULL) { - if (ir->rx->buf.fifo_initialized) - lirc_buffer_free(&ir->rx->buf); if (ir->rx->c != NULL) i2c_set_clientdata(ir->rx->c, NULL); kfree(ir->rx); @@ -1399,6 +1397,8 @@ out_free_xx: i2c_set_clientdata(ir->tx->c, NULL); kfree(ir->tx); } + if (ir->rbuf.fifo_initialized) + lirc_buffer_free(&ir->rbuf); out_free_ir: del_ir_device(ir); kfree(ir); -- cgit v0.10.2 From 534c1eab1e2770dca3cf4616cc4059683020114b Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Jan 2011 23:02:20 -0300 Subject: [media] lirc_zilog: Move constants from ir_probe() into the lirc_driver template ir_probe() makes a number of constant assignments into the lirc_driver object after copying in a template. Make better use of the template. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 053fddb..d067885 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -1116,13 +1116,6 @@ static int close(struct inode *node, struct file *filep) return 0; } -static struct lirc_driver lirc_template = { - .name = "lirc_zilog", - .set_use_inc = set_use_inc, - .set_use_dec = set_use_dec, - .owner = THIS_MODULE -}; - static int ir_remove(struct i2c_client *client); static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id); @@ -1161,6 +1154,19 @@ static const struct file_operations lirc_fops = { .release = close }; +static struct lirc_driver lirc_template = { + .name = "lirc_zilog", + .minor = -1, + .code_length = 13, + .buffer_size = BUFLEN / 2, + .sample_rate = 0, /* tell lirc_dev to not start its own kthread */ + .chunk_size = 2, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .owner = THIS_MODULE, +}; + static void destroy_rx_kthread(struct IR_rx *rx) { /* end up polling thread */ @@ -1292,14 +1298,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* set lirc_dev stuff */ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); ir->l.minor = minor; /* module option */ - ir->l.code_length = 13; - ir->l.chunk_size = 2; - ir->l.buffer_size = BUFLEN / 2; ir->l.rbuf = &ir->rbuf; - ir->l.fops = &lirc_fops; ir->l.data = ir; ir->l.dev = &adap->dev; - ir->l.sample_rate = 0; ret = lirc_buffer_init(ir->l.rbuf, ir->l.chunk_size, ir->l.buffer_size); if (ret) @@ -1314,6 +1315,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_free_xx; } + ir->l.features |= LIRC_CAN_SEND_PULSE; ir->tx->c = client; ir->tx->need_boot = 1; ir->tx->post_tx_ready_poll = @@ -1326,6 +1328,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_free_xx; } + ir->l.features |= LIRC_CAN_REC_LIRCCODE; ir->rx->c = client; ir->rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; -- cgit v0.10.2 From 5bd6b0464b68d429bc8a3fe6595d19c39dfc4d95 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Mon, 7 Feb 2011 22:30:55 -0300 Subject: [media] lirc_zilog: Add ref counting of struct IR, IR_tx, and IR_rx This is a major change to add pointer reference counting for struct IR, struct IR_tx, and struct IR_rx object instances. This ref counting gets lirc_zilog closer to gracefully handling bridge drivers and hot-unplugged USB devices disappearing out from under lirc_zilog when the /dev/lircN node is still open. (mutexes to protect the i2c_client pointers in struct IR_tx and struct IR_rx still need to be added.) This reference counting also helps lirc_zilog clean up properly when the i2c_clients disappear. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index d067885..5df90db 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -63,8 +63,14 @@ #include #include +struct IR; + struct IR_rx { + struct kref ref; + struct IR *ir; + /* RX device */ + /* FIXME mutex lock access to this pointer */ struct i2c_client *c; /* RX polling thread data */ @@ -76,7 +82,11 @@ struct IR_rx { }; struct IR_tx { + struct kref ref; + struct IR *ir; + /* TX device */ + /* FIXME mutex lock access to this pointer */ struct i2c_client *c; /* TX additional actions needed */ @@ -85,8 +95,10 @@ struct IR_tx { }; struct IR { + struct kref ref; struct list_head list; + /* FIXME spinlock access to l.features */ struct lirc_driver l; struct lirc_buffer rbuf; @@ -94,11 +106,21 @@ struct IR { atomic_t open_count; struct i2c_adapter *adapter; + + spinlock_t rx_ref_lock; /* struct IR_rx kref get()/put() */ struct IR_rx *rx; + + spinlock_t tx_ref_lock; /* struct IR_tx kref get()/put() */ struct IR_tx *tx; }; /* IR transceiver instance object list */ +/* + * This lock is used for the following: + * a. ir_devices_list access, insertions, deletions + * b. struct IR kref get()s and put()s + * c. serialization of ir_probe() for the two i2c_clients for a Z8 + */ static DEFINE_MUTEX(ir_devices_lock); static LIST_HEAD(ir_devices_list); @@ -146,6 +168,157 @@ static int minor = -1; /* minor number */ ## args); \ } while (0) + +/* struct IR reference counting */ +static struct IR *get_ir_device(struct IR *ir, bool ir_devices_lock_held) +{ + if (ir_devices_lock_held) { + kref_get(&ir->ref); + } else { + mutex_lock(&ir_devices_lock); + kref_get(&ir->ref); + mutex_unlock(&ir_devices_lock); + } + return ir; +} + +static void release_ir_device(struct kref *ref) +{ + struct IR *ir = container_of(ref, struct IR, ref); + + /* + * Things should be in this state by now: + * ir->rx set to NULL and deallocated - happens before ir->rx->ir put() + * ir->rx->task kthread stopped - happens before ir->rx->ir put() + * ir->tx set to NULL and deallocated - happens before ir->tx->ir put() + * ir->open_count == 0 - happens on final close() + * ir_lock, tx_ref_lock, rx_ref_lock, all released + */ + if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) { + lirc_unregister_driver(ir->l.minor); + ir->l.minor = MAX_IRCTL_DEVICES; + } + if (ir->rbuf.fifo_initialized) + lirc_buffer_free(&ir->rbuf); + list_del(&ir->list); + kfree(ir); +} + +static int put_ir_device(struct IR *ir, bool ir_devices_lock_held) +{ + int released; + + if (ir_devices_lock_held) + return kref_put(&ir->ref, release_ir_device); + + mutex_lock(&ir_devices_lock); + released = kref_put(&ir->ref, release_ir_device); + mutex_unlock(&ir_devices_lock); + + return released; +} + +/* struct IR_rx reference counting */ +static struct IR_rx *get_ir_rx(struct IR *ir) +{ + struct IR_rx *rx; + + spin_lock(&ir->rx_ref_lock); + rx = ir->rx; + if (rx != NULL) + kref_get(&rx->ref); + spin_unlock(&ir->rx_ref_lock); + return rx; +} + +static void destroy_rx_kthread(struct IR_rx *rx, bool ir_devices_lock_held) +{ + /* end up polling thread */ + if (!IS_ERR_OR_NULL(rx->task)) { + kthread_stop(rx->task); + rx->task = NULL; + /* Put the ir ptr that ir_probe() gave to the rx poll thread */ + put_ir_device(rx->ir, ir_devices_lock_held); + } +} + +static void release_ir_rx(struct kref *ref) +{ + struct IR_rx *rx = container_of(ref, struct IR_rx, ref); + struct IR *ir = rx->ir; + + /* + * This release function can't do all the work, as we want + * to keep the rx_ref_lock a spinlock, and killing the poll thread + * and releasing the ir reference can cause a sleep. That work is + * performed by put_ir_rx() + */ + ir->l.features &= ~LIRC_CAN_REC_LIRCCODE; + /* Don't put_ir_device(rx->ir) here; lock can't be freed yet */ + ir->rx = NULL; + /* Don't do the kfree(rx) here; we still need to kill the poll thread */ + return; +} + +static int put_ir_rx(struct IR_rx *rx, bool ir_devices_lock_held) +{ + int released; + struct IR *ir = rx->ir; + + spin_lock(&ir->rx_ref_lock); + released = kref_put(&rx->ref, release_ir_rx); + spin_unlock(&ir->rx_ref_lock); + /* Destroy the rx kthread while not holding the spinlock */ + if (released) { + destroy_rx_kthread(rx, ir_devices_lock_held); + kfree(rx); + /* Make sure we're not still in a poll_table somewhere */ + wake_up_interruptible(&ir->rbuf.wait_poll); + } + /* Do a reference put() for the rx->ir reference, if we released rx */ + if (released) + put_ir_device(ir, ir_devices_lock_held); + return released; +} + +/* struct IR_tx reference counting */ +static struct IR_tx *get_ir_tx(struct IR *ir) +{ + struct IR_tx *tx; + + spin_lock(&ir->tx_ref_lock); + tx = ir->tx; + if (tx != NULL) + kref_get(&tx->ref); + spin_unlock(&ir->tx_ref_lock); + return tx; +} + +static void release_ir_tx(struct kref *ref) +{ + struct IR_tx *tx = container_of(ref, struct IR_tx, ref); + struct IR *ir = tx->ir; + + ir->l.features &= ~LIRC_CAN_SEND_PULSE; + /* Don't put_ir_device(tx->ir) here, so our lock doesn't get freed */ + ir->tx = NULL; + kfree(tx); +} + +static int put_ir_tx(struct IR_tx *tx, bool ir_devices_lock_held) +{ + int released; + struct IR *ir = tx->ir; + + spin_lock(&ir->tx_ref_lock); + released = kref_put(&tx->ref, release_ir_tx); + spin_unlock(&ir->tx_ref_lock); + /* Do a reference put() for the tx->ir reference, if we released tx */ + if (released) + put_ir_device(ir, ir_devices_lock_held); + return released; +} + static int add_to_buf(struct IR *ir) { __u16 code; @@ -156,23 +329,29 @@ static int add_to_buf(struct IR *ir) int failures = 0; unsigned char sendbuf[1] = { 0 }; struct lirc_buffer *rbuf = ir->l.rbuf; - struct IR_rx *rx = ir->rx; - - if (rx == NULL) - return -ENXIO; + struct IR_rx *rx; + struct IR_tx *tx; if (lirc_buffer_full(rbuf)) { dprintk("buffer overflow\n"); return -EOVERFLOW; } + rx = get_ir_rx(ir); + if (rx == NULL) + return -ENXIO; + + tx = get_ir_tx(ir); + /* * service the device as long as it is returning * data and we have space */ do { - if (kthread_should_stop()) - return -ENODATA; + if (kthread_should_stop()) { + ret = -ENODATA; + break; + } /* * Lock i2c bus for the duration. RX/TX chips interfere so @@ -182,7 +361,8 @@ static int add_to_buf(struct IR *ir) if (kthread_should_stop()) { mutex_unlock(&ir->ir_lock); - return -ENODATA; + ret = -ENODATA; + break; } /* @@ -196,7 +376,7 @@ static int add_to_buf(struct IR *ir) mutex_unlock(&ir->ir_lock); zilog_error("unable to read from the IR chip " "after 3 resets, giving up\n"); - return ret; + break; } /* Looks like the chip crashed, reset it */ @@ -206,20 +386,23 @@ static int add_to_buf(struct IR *ir) set_current_state(TASK_UNINTERRUPTIBLE); if (kthread_should_stop()) { mutex_unlock(&ir->ir_lock); - return -ENODATA; + ret = -ENODATA; + break; } schedule_timeout((100 * HZ + 999) / 1000); - if (ir->tx != NULL) - ir->tx->need_boot = 1; + if (tx != NULL) + tx->need_boot = 1; ++failures; mutex_unlock(&ir->ir_lock); + ret = 0; continue; } if (kthread_should_stop()) { mutex_unlock(&ir->ir_lock); - return -ENODATA; + ret = -ENODATA; + break; } ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); mutex_unlock(&ir->ir_lock); @@ -235,12 +418,17 @@ static int add_to_buf(struct IR *ir) /* key pressed ? */ if (rx->hdpvr_data_fmt) { - if (got_data && (keybuf[0] == 0x80)) - return 0; - else if (got_data && (keybuf[0] == 0x00)) - return -ENODATA; - } else if ((rx->b[0] & 0x80) == 0) - return got_data ? 0 : -ENODATA; + if (got_data && (keybuf[0] == 0x80)) { + ret = 0; + break; + } else if (got_data && (keybuf[0] == 0x00)) { + ret = -ENODATA; + break; + } + } else if ((rx->b[0] & 0x80) == 0) { + ret = got_data ? 0 : -ENODATA; + break; + } /* look what we have */ code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2); @@ -251,9 +439,13 @@ static int add_to_buf(struct IR *ir) /* return it */ lirc_buffer_write(rbuf, codes); ++got_data; + ret = 0; } while (!lirc_buffer_full(rbuf)); - return 0; + if (tx != NULL) + put_ir_tx(tx, false); + put_ir_rx(rx, false); + return ret; } /* @@ -274,14 +466,14 @@ static int lirc_thread(void *arg) dprintk("poll thread started\n"); while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - /* if device not opened, we can sleep half a second */ if (atomic_read(&ir->open_count) == 0) { schedule_timeout(HZ/2); continue; } + set_current_state(TASK_INTERRUPTIBLE); + /* * This is ~113*2 + 24 + jitter (2*repeat gap + code length). * We use this interval as the chip resets every time you poll @@ -564,7 +756,7 @@ static int fw_load(struct IR_tx *tx) } /* Request codeset data file */ - ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &tx->c->dev); + ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", tx->ir->l.dev); if (ret != 0) { zilog_error("firmware haup-ir-blaster.bin not available " "(%d)\n", ret); @@ -690,45 +882,26 @@ out: return ret; } -/* initialise the IR TX device */ -static int tx_init(struct IR_tx *tx) -{ - int ret; - - /* Load 'firmware' */ - ret = fw_load(tx); - if (ret != 0) - return ret; - - /* Send boot block */ - ret = send_boot_data(tx); - if (ret != 0) - return ret; - tx->need_boot = 0; - - /* Looks good */ - return 0; -} - /* copied from lirc_dev */ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) { struct IR *ir = filep->private_data; - struct IR_rx *rx = ir->rx; + struct IR_rx *rx; struct lirc_buffer *rbuf = ir->l.rbuf; int ret = 0, written = 0; unsigned int m; DECLARE_WAITQUEUE(wait, current); dprintk("read called\n"); - if (rx == NULL) - return -ENODEV; - if (n % rbuf->chunk_size) { dprintk("read result = -EINVAL\n"); return -EINVAL; } + rx = get_ir_rx(ir); + if (rx == NULL) + return -ENXIO; + /* * we add ourselves to the task queue before buffer check * to avoid losing scan code (in case when queue is awaken somewhere @@ -773,6 +946,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) } remove_wait_queue(&rbuf->wait_poll, &wait); + put_ir_rx(rx, false); set_current_state(TASK_RUNNING); dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK"); @@ -902,17 +1076,19 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, loff_t *ppos) { struct IR *ir = filep->private_data; - struct IR_tx *tx = ir->tx; + struct IR_tx *tx; size_t i; int failures = 0; - if (tx == NULL) - return -ENODEV; - /* Validate user parameters */ if (n % sizeof(int)) return -EINVAL; + /* Get a struct IR_tx reference */ + tx = get_ir_tx(ir); + if (tx == NULL) + return -ENXIO; + /* Lock i2c bus for the duration */ mutex_lock(&ir->ir_lock); @@ -923,11 +1099,22 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, if (copy_from_user(&command, buf + i, sizeof(command))) { mutex_unlock(&ir->ir_lock); + put_ir_tx(tx, false); return -EFAULT; } /* Send boot data first if required */ if (tx->need_boot == 1) { + /* Make sure we have the 'firmware' loaded, first */ + ret = fw_load(tx); + if (ret != 0) { + mutex_unlock(&ir->ir_lock); + put_ir_tx(tx, false); + if (ret != -ENOMEM) + ret = -EIO; + return ret; + } + /* Prep the chip for transmitting codes */ ret = send_boot_data(tx); if (ret == 0) tx->need_boot = 0; @@ -939,6 +1126,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, (unsigned)command & 0xFFFF); if (ret == -EPROTO) { mutex_unlock(&ir->ir_lock); + put_ir_tx(tx, false); return ret; } } @@ -956,6 +1144,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, zilog_error("unable to send to the IR chip " "after 3 resets, giving up\n"); mutex_unlock(&ir->ir_lock); + put_ir_tx(tx, false); return ret; } set_current_state(TASK_UNINTERRUPTIBLE); @@ -969,6 +1158,9 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, /* Release i2c bus */ mutex_unlock(&ir->ir_lock); + /* Give back our struct IR_tx reference */ + put_ir_tx(tx, false); + /* All looks good */ return n; } @@ -977,12 +1169,13 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, static unsigned int poll(struct file *filep, poll_table *wait) { struct IR *ir = filep->private_data; - struct IR_rx *rx = ir->rx; + struct IR_rx *rx; struct lirc_buffer *rbuf = ir->l.rbuf; unsigned int ret; dprintk("poll called\n"); + rx = get_ir_rx(ir); if (rx == NULL) { /* * Revisit this, if our poll function ever reports writeable @@ -1009,12 +1202,9 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { struct IR *ir = filep->private_data; int result; - unsigned long mode, features = 0; + unsigned long mode, features; - if (ir->rx != NULL) - features |= LIRC_CAN_REC_LIRCCODE; - if (ir->tx != NULL) - features |= LIRC_CAN_SEND_PULSE; + features = ir->l.features; switch (cmd) { case LIRC_GET_LENGTH: @@ -1060,19 +1250,24 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) return result; } -/* ir_devices_lock must be held */ -static struct IR *find_ir_device_by_minor(unsigned int minor) +static struct IR *get_ir_device_by_minor(unsigned int minor) { struct IR *ir; + struct IR *ret = NULL; - if (list_empty(&ir_devices_list)) - return NULL; + mutex_lock(&ir_devices_lock); - list_for_each_entry(ir, &ir_devices_list, list) - if (ir->l.minor == minor) - return ir; + if (!list_empty(&ir_devices_list)) { + list_for_each_entry(ir, &ir_devices_list, list) { + if (ir->l.minor == minor) { + ret = get_ir_device(ir, true); + break; + } + } + } - return NULL; + mutex_unlock(&ir_devices_lock); + return ret; } /* @@ -1085,9 +1280,7 @@ static int open(struct inode *node, struct file *filep) unsigned int minor = MINOR(node->i_rdev); /* find our IR struct */ - mutex_lock(&ir_devices_lock); - ir = find_ir_device_by_minor(minor); - mutex_unlock(&ir_devices_lock); + ir = get_ir_device_by_minor(minor); if (ir == NULL) return -ENODEV; @@ -1113,6 +1306,7 @@ static int close(struct inode *node, struct file *filep) atomic_dec(&ir->open_count); + put_ir_device(ir, false); return 0; } @@ -1167,78 +1361,23 @@ static struct lirc_driver lirc_template = { .owner = THIS_MODULE, }; -static void destroy_rx_kthread(struct IR_rx *rx) -{ - /* end up polling thread */ - if (rx != NULL && !IS_ERR_OR_NULL(rx->task)) { - kthread_stop(rx->task); - rx->task = NULL; - } -} - -/* ir_devices_lock must be held */ -static int add_ir_device(struct IR *ir) -{ - list_add_tail(&ir->list, &ir_devices_list); - return 0; -} - -/* ir_devices_lock must be held */ -static void del_ir_device(struct IR *ir) -{ - struct IR *p; - - if (list_empty(&ir_devices_list)) - return; - - list_for_each_entry(p, &ir_devices_list, list) - if (p == ir) { - list_del(&p->list); - break; - } -} - static int ir_remove(struct i2c_client *client) { - struct IR *ir = i2c_get_clientdata(client); - - mutex_lock(&ir_devices_lock); - - if (ir == NULL) { - /* We destroyed everything when the first client came through */ - mutex_unlock(&ir_devices_lock); - return 0; + if (strncmp("ir_tx_z8", client->name, 8) == 0) { + struct IR_tx *tx = i2c_get_clientdata(client); + if (tx != NULL) + put_ir_tx(tx, false); + } else if (strncmp("ir_rx_z8", client->name, 8) == 0) { + struct IR_rx *rx = i2c_get_clientdata(client); + if (rx != NULL) + put_ir_rx(rx, false); } - - /* Good-bye LIRC */ - lirc_unregister_driver(ir->l.minor); - - /* Good-bye Rx */ - destroy_rx_kthread(ir->rx); - if (ir->rx != NULL) { - i2c_set_clientdata(ir->rx->c, NULL); - kfree(ir->rx); - } - - /* Good-bye Tx */ - if (ir->tx != NULL) { - i2c_set_clientdata(ir->tx->c, NULL); - kfree(ir->tx); - } - - /* Good-bye IR */ - if (ir->rbuf.fifo_initialized) - lirc_buffer_free(&ir->rbuf); - del_ir_device(ir); - kfree(ir); - - mutex_unlock(&ir_devices_lock); return 0; } /* ir_devices_lock must be held */ -static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter) +static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter) { struct IR *ir; @@ -1246,8 +1385,10 @@ static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter) return NULL; list_for_each_entry(ir, &ir_devices_list, list) - if (ir->adapter == adapter) + if (ir->adapter == adapter) { + get_ir_device(ir, true); return ir; + } return NULL; } @@ -1255,6 +1396,8 @@ static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter) static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct IR *ir; + struct IR_tx *tx; + struct IR_rx *rx; struct i2c_adapter *adap = client->adapter; int ret; bool tx_probe = false; @@ -1278,133 +1421,166 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) mutex_lock(&ir_devices_lock); /* Use a single struct IR instance for both the Rx and Tx functions */ - ir = find_ir_device_by_adapter(adap); + ir = get_ir_device_by_adapter(adap); if (ir == NULL) { ir = kzalloc(sizeof(struct IR), GFP_KERNEL); if (ir == NULL) { ret = -ENOMEM; goto out_no_ir; } + kref_init(&ir->ref); + /* store for use in ir_probe() again, and open() later on */ INIT_LIST_HEAD(&ir->list); - ret = add_ir_device(ir); - if (ret) - goto out_free_ir; + list_add_tail(&ir->list, &ir_devices_list); ir->adapter = adap; mutex_init(&ir->ir_lock); atomic_set(&ir->open_count, 0); + spin_lock_init(&ir->tx_ref_lock); + spin_lock_init(&ir->rx_ref_lock); /* set lirc_dev stuff */ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); - ir->l.minor = minor; /* module option */ - ir->l.rbuf = &ir->rbuf; - ir->l.data = ir; - ir->l.dev = &adap->dev; + /* + * FIXME this is a pointer reference to us, but no refcount. + * + * This OK for now, since lirc_dev currently won't touch this + * buffer as we provide our own lirc_fops. + * + * Currently our own lirc_fops rely on this ir->l.rbuf pointer + */ + ir->l.rbuf = &ir->rbuf; + ir->l.dev = &adap->dev; ret = lirc_buffer_init(ir->l.rbuf, ir->l.chunk_size, ir->l.buffer_size); if (ret) - goto out_free_ir; + goto out_put_ir; } if (tx_probe) { + /* Get the IR_rx instance for later, if already allocated */ + rx = get_ir_rx(ir); + /* Set up a struct IR_tx instance */ - ir->tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); - if (ir->tx == NULL) { + tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); + if (tx == NULL) { ret = -ENOMEM; - goto out_free_xx; + goto out_put_xx; } + kref_init(&tx->ref); + ir->tx = tx; ir->l.features |= LIRC_CAN_SEND_PULSE; - ir->tx->c = client; - ir->tx->need_boot = 1; - ir->tx->post_tx_ready_poll = + tx->c = client; + tx->need_boot = 1; + tx->post_tx_ready_poll = (id->driver_data & ID_FLAG_HDPVR) ? false : true; + + /* An ir ref goes to the struct IR_tx instance */ + tx->ir = get_ir_device(ir, true); + + /* A tx ref goes to the i2c_client */ + i2c_set_clientdata(client, get_ir_tx(ir)); + + /* + * Load the 'firmware'. We do this before registering with + * lirc_dev, so the first firmware load attempt does not happen + * after a open() or write() call on the device. + * + * Failure here is not deemed catastrophic, so the receiver will + * still be usable. Firmware load will be retried in write(), + * if it is needed. + */ + fw_load(tx); + + /* Proceed only if the Rx client is also ready or not needed */ + if (rx == NULL && !tx_only) { + zilog_info("probe of IR Tx on %s (i2c-%d) done. Waiting" + " on IR Rx.\n", adap->name, adap->nr); + goto out_ok; + } } else { + /* Get the IR_tx instance for later, if already allocated */ + tx = get_ir_tx(ir); + /* Set up a struct IR_rx instance */ - ir->rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); - if (ir->rx == NULL) { + rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); + if (rx == NULL) { ret = -ENOMEM; - goto out_free_xx; + goto out_put_xx; } + kref_init(&rx->ref); + ir->rx = rx; ir->l.features |= LIRC_CAN_REC_LIRCCODE; - ir->rx->c = client; - ir->rx->hdpvr_data_fmt = + rx->c = client; + rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; - } - i2c_set_clientdata(client, ir); + /* An ir ref goes to the struct IR_rx instance */ + rx->ir = get_ir_device(ir, true); - /* Proceed only if we have the required Tx and Rx clients ready to go */ - if (ir->tx == NULL || - (ir->rx == NULL && !tx_only)) { - zilog_info("probe of IR %s on %s (i2c-%d) done. Waiting on " - "IR %s.\n", tx_probe ? "Tx" : "Rx", adap->name, - adap->nr, tx_probe ? "Rx" : "Tx"); - goto out_ok; - } + /* An rx ref goes to the i2c_client */ + i2c_set_clientdata(client, get_ir_rx(ir)); - /* initialise RX device */ - if (ir->rx != NULL) { - /* try to fire up polling thread */ - ir->rx->task = kthread_run(lirc_thread, ir, - "zilog-rx-i2c-%d", adap->nr); - if (IS_ERR(ir->rx->task)) { - ret = PTR_ERR(ir->rx->task); + /* + * Start the polling thread. + * It will only perform an empty loop around schedule_timeout() + * until we register with lirc_dev and the first user open() + */ + /* An ir ref goes to the new rx polling kthread */ + rx->task = kthread_run(lirc_thread, get_ir_device(ir, true), + "zilog-rx-i2c-%d", adap->nr); + if (IS_ERR(rx->task)) { + ret = PTR_ERR(rx->task); zilog_error("%s: could not start IR Rx polling thread" "\n", __func__); - goto out_free_xx; + /* Failed kthread, so put back the ir ref */ + put_ir_device(ir, true); + /* Failure exit, so put back rx ref from i2c_client */ + i2c_set_clientdata(client, NULL); + put_ir_rx(rx, true); + ir->l.features &= ~LIRC_CAN_REC_LIRCCODE; + goto out_put_xx; + } + + /* Proceed only if the Tx client is also ready */ + if (tx == NULL) { + zilog_info("probe of IR Rx on %s (i2c-%d) done. Waiting" + " on IR Tx.\n", adap->name, adap->nr); + goto out_ok; } } /* register with lirc */ + ir->l.minor = minor; /* module option: user requested minor number */ ir->l.minor = lirc_register_driver(&ir->l); if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { zilog_error("%s: \"minor\" must be between 0 and %d (%d)!\n", __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); ret = -EBADRQC; - goto out_free_thread; - } - - /* - * if we have the tx device, load the 'firmware'. We do this - * after registering with lirc as otherwise hotplug seems to take - * 10s to create the lirc device. - */ - if (ir->tx != NULL) { - /* Special TX init */ - ret = tx_init(ir->tx); - if (ret != 0) - goto out_unregister; + goto out_put_xx; } +out_ok: + if (rx != NULL) + put_ir_rx(rx, true); + if (tx != NULL) + put_ir_tx(tx, true); + put_ir_device(ir, true); zilog_info("probe of IR %s on %s (i2c-%d) done. IR unit ready.\n", tx_probe ? "Tx" : "Rx", adap->name, adap->nr); -out_ok: mutex_unlock(&ir_devices_lock); return 0; -out_unregister: - lirc_unregister_driver(ir->l.minor); -out_free_thread: - destroy_rx_kthread(ir->rx); -out_free_xx: - if (ir->rx != NULL) { - if (ir->rx->c != NULL) - i2c_set_clientdata(ir->rx->c, NULL); - kfree(ir->rx); - } - if (ir->tx != NULL) { - if (ir->tx->c != NULL) - i2c_set_clientdata(ir->tx->c, NULL); - kfree(ir->tx); - } - if (ir->rbuf.fifo_initialized) - lirc_buffer_free(&ir->rbuf); -out_free_ir: - del_ir_device(ir); - kfree(ir); +out_put_xx: + if (rx != NULL) + put_ir_rx(rx, true); + if (tx != NULL) + put_ir_tx(tx, true); +out_put_ir: + put_ir_device(ir, true); out_no_ir: zilog_error("%s: probing IR %s on %s (i2c-%d) failed with %d\n", __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, -- cgit v0.10.2 From d6dbd939b97d271766a58a66d69b27023bc90993 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 17 Feb 2011 20:50:38 -0300 Subject: [media] lirc_zilog: Add locking of the i2c_clients when in use Lock the i2c_client pointers and prevent i2c_client removal when lirc_zilog is perfoming a series of operations that require valid i2c_client pointers. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 5df90db..5946a95 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -70,7 +70,7 @@ struct IR_rx { struct IR *ir; /* RX device */ - /* FIXME mutex lock access to this pointer */ + struct mutex client_lock; struct i2c_client *c; /* RX polling thread data */ @@ -86,7 +86,7 @@ struct IR_tx { struct IR *ir; /* TX device */ - /* FIXME mutex lock access to this pointer */ + struct mutex client_lock; struct i2c_client *c; /* TX additional actions needed */ @@ -341,6 +341,14 @@ static int add_to_buf(struct IR *ir) if (rx == NULL) return -ENXIO; + /* Ensure our rx->c i2c_client remains valid for the duration */ + mutex_lock(&rx->client_lock); + if (rx->c == NULL) { + mutex_unlock(&rx->client_lock); + put_ir_rx(rx, false); + return -ENXIO; + } + tx = get_ir_tx(ir); /* @@ -442,6 +450,7 @@ static int add_to_buf(struct IR *ir) ret = 0; } while (!lirc_buffer_full(rbuf)); + mutex_unlock(&rx->client_lock); if (tx != NULL) put_ir_tx(tx, false); put_ir_rx(rx, false); @@ -1089,6 +1098,14 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, if (tx == NULL) return -ENXIO; + /* Ensure our tx->c i2c_client remains valid for the duration */ + mutex_lock(&tx->client_lock); + if (tx->c == NULL) { + mutex_unlock(&tx->client_lock); + put_ir_tx(tx, false); + return -ENXIO; + } + /* Lock i2c bus for the duration */ mutex_lock(&ir->ir_lock); @@ -1099,6 +1116,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, if (copy_from_user(&command, buf + i, sizeof(command))) { mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); return -EFAULT; } @@ -1109,6 +1127,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, ret = fw_load(tx); if (ret != 0) { mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); if (ret != -ENOMEM) ret = -EIO; @@ -1126,6 +1145,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, (unsigned)command & 0xFFFF); if (ret == -EPROTO) { mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); return ret; } @@ -1144,6 +1164,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, zilog_error("unable to send to the IR chip " "after 3 resets, giving up\n"); mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); return ret; } @@ -1158,6 +1179,8 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, /* Release i2c bus */ mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); + /* Give back our struct IR_tx reference */ put_ir_tx(tx, false); @@ -1365,12 +1388,20 @@ static int ir_remove(struct i2c_client *client) { if (strncmp("ir_tx_z8", client->name, 8) == 0) { struct IR_tx *tx = i2c_get_clientdata(client); - if (tx != NULL) + if (tx != NULL) { + mutex_lock(&tx->client_lock); + tx->c = NULL; + mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); + } } else if (strncmp("ir_rx_z8", client->name, 8) == 0) { struct IR_rx *rx = i2c_get_clientdata(client); - if (rx != NULL) + if (rx != NULL) { + mutex_lock(&rx->client_lock); + rx->c = NULL; + mutex_unlock(&rx->client_lock); put_ir_rx(rx, false); + } } return 0; } @@ -1472,6 +1503,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->tx = tx; ir->l.features |= LIRC_CAN_SEND_PULSE; + mutex_init(&tx->client_lock); tx->c = client; tx->need_boot = 1; tx->post_tx_ready_poll = @@ -1514,6 +1546,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->rx = rx; ir->l.features |= LIRC_CAN_REC_LIRCCODE; + mutex_init(&rx->client_lock); rx->c = client; rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; -- cgit v0.10.2 From 4933fc9d72104d66c54620537eb6f43457a6896f Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 17 Feb 2011 21:14:13 -0300 Subject: [media] lirc_zilog: Fix somewhat confusing information messages in ir_probe() The total sequence of messages emitted by the ir_porbe() calls for a transceiver's two i2c_clients was confusing. Clean it up a bit. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 5946a95..407d4b4 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -1595,6 +1595,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ret = -EBADRQC; goto out_put_xx; } + zilog_info("IR unit on %s (i2c-%d) registered as lirc%d and ready\n", + adap->name, adap->nr, ir->l.minor); out_ok: if (rx != NULL) @@ -1602,7 +1604,7 @@ out_ok: if (tx != NULL) put_ir_tx(tx, true); put_ir_device(ir, true); - zilog_info("probe of IR %s on %s (i2c-%d) done. IR unit ready.\n", + zilog_info("probe of IR %s on %s (i2c-%d) done\n", tx_probe ? "Tx" : "Rx", adap->name, adap->nr); mutex_unlock(&ir_devices_lock); return 0; -- cgit v0.10.2 From aa3f4e446e784372498f009ea37177857f23331e Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 17 Feb 2011 21:32:06 -0300 Subject: [media] lirc_zilog: Update TODO list based on work completed and revised plans Update the TODO.lirc_zilog based on what has been completed. Also revised the development plan for lirc_zilog to not try and split Tx/Rx for one IR transceiver unit between lirc_zilog and ir-kbd-i2c, since that would be a ref-counting nightmare. Signed-off-by: Andy Walls Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/TODO.lirc_zilog b/drivers/staging/lirc/TODO.lirc_zilog index 2d0263f..a97800a 100644 --- a/drivers/staging/lirc/TODO.lirc_zilog +++ b/drivers/staging/lirc/TODO.lirc_zilog @@ -1,34 +1,33 @@ -1. Both ir-kbd-i2c and lirc_zilog provide support for RX events. -The 'tx_only' lirc_zilog module parameter will allow ir-kbd-i2c -and lirc_zilog to coexist in the kernel, if the user requires such a set-up. -However the IR unit will not work well without coordination between the -two modules. A shared mutex, for transceiver access locking, needs to be -supplied by bridge drivers, in struct IR_i2_init_data, to both ir-kbd-i2c -and lirc_zilog, before they will coexist usefully. This should be fixed -before moving out of staging. - -2. References and locking need careful examination. For cx18 and ivtv PCI -cards, which are not easily "hot unplugged", the imperfect state of reference -counting and locking is acceptable if not correct. For USB connected units -like HD PVR, PVR USB2, HVR-1900, and HVR1950, the likelyhood of an Ooops on -unplug is probably great. Proper reference counting and locking needs to be -implemented before this module is moved out of staging. - -3. The binding between hdpvr and lirc_zilog is currently disabled, -due to an OOPS reported a few years ago when both the hdpvr and cx18 -drivers were loaded in his system. More details can be seen at: - http://www.mail-archive.com/linux-media@vger.kernel.org/msg09163.html -More tests need to be done, in order to fix the reported issue. - -4. In addition to providing a shared mutex for transceiver access -locking, bridge drivers, if able, should provide a chip reset() callback +1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for +the chips supported by lirc_zilog. Before moving lirc_zilog out of staging: + +a. ir-kbd-i2c needs a module parameter added to allow the user to tell + ir-kbd-i2c to ignore Z8 IR units. + +b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c + does. + + +2. lirc_zilog module ref-counting need examination. It has not been +verified that cdev and lirc_dev will take the proper module references on +lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node +is open. + +(The good news is ref-counting of lirc_zilog internal structures appears to be +complete. Testing has shown the cx18 module can be unloaded out from under +irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse +effects. The cx18 module could then be reloaded and irw properly began +receiving button presses again and ir_send worked without error.) + + +3. Bridge drivers, if able, should provide a chip reset() callback to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines -to perform Z8 chip resets via GPIO manipulations. This will allow lirc_zilog +to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog to bring the chip back to normal when it hangs, in the same places the original lirc_pvr150 driver code does. This is not strictly needed, so it is not required to move lirc_zilog out of staging. -5. Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed +Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed and installed on Hauppauge products. When working on either module, developers must consider at least the following bridge drivers which mention an IR Rx unit at address 0x71 (indicative of a Z8): -- cgit v0.10.2 From 444cc20b3d6141a5fa9a9774a6b710e16af6110d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:36 -0300 Subject: [media] rc/keymaps: use KEY_CAMERA for snapshots On a few places, KEY_MHP were used for snapshots. However, KEY_CAMERA is used for it on all the other keyboards that have a snapshot/Picture button. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index f2dbce7..f6344cd 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -176,7 +176,7 @@ static struct rc_map_table rc_map_digitv_table[] = { { 0xaf59, KEY_AUX }, { 0x5f5a, KEY_DVD }, { 0x6f5a, KEY_POWER }, - { 0x9f5a, KEY_MHP }, /* labelled 'Picture' */ + { 0x9f5a, KEY_CAMERA }, /* labelled 'Picture' */ { 0xaf5a, KEY_AUDIO }, { 0x5f65, KEY_INFO }, { 0x6f65, KEY_F13 }, /* 16:9 */ diff --git a/drivers/media/rc/keymaps/rc-avermedia-m135a.c b/drivers/media/rc/keymaps/rc-avermedia-m135a.c index 357fea58..3d2cbe4 100644 --- a/drivers/media/rc/keymaps/rc-avermedia-m135a.c +++ b/drivers/media/rc/keymaps/rc-avermedia-m135a.c @@ -108,7 +108,7 @@ static struct rc_map_table avermedia_m135a[] = { { 0x0414, KEY_TEXT }, { 0x0415, KEY_EPG }, { 0x041a, KEY_TV2 }, /* PIP */ - { 0x041b, KEY_MHP }, /* Snapshot */ + { 0x041b, KEY_CAMERA }, /* Snapshot */ { 0x0417, KEY_RECORD }, { 0x0416, KEY_PLAYPAUSE }, diff --git a/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c index e694e6e..8cd7f28 100644 --- a/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c +++ b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c @@ -56,7 +56,7 @@ static struct rc_map_table avermedia_m733a_rm_k6[] = { { 0x0414, KEY_TEXT }, { 0x0415, KEY_EPG }, { 0x041a, KEY_TV2 }, /* PIP */ - { 0x041b, KEY_MHP }, /* Snapshot */ + { 0x041b, KEY_CAMERA }, /* Snapshot */ { 0x0417, KEY_RECORD }, { 0x0416, KEY_PLAYPAUSE }, diff --git a/drivers/media/rc/keymaps/rc-budget-ci-old.c b/drivers/media/rc/keymaps/rc-budget-ci-old.c index 97fc386..2f66e43 100644 --- a/drivers/media/rc/keymaps/rc-budget-ci-old.c +++ b/drivers/media/rc/keymaps/rc-budget-ci-old.c @@ -12,7 +12,8 @@ #include -/* From reading the following remotes: +/* + * From reading the following remotes: * Zenith Universal 7 / TV Mode 807 / VCR Mode 837 * Hauppauge (from NOVA-CI-s box product) * This is a "middle of the road" approach, differences are noted diff --git a/drivers/media/rc/keymaps/rc-encore-enltv.c b/drivers/media/rc/keymaps/rc-encore-enltv.c index afa4e92..1a0e590b 100644 --- a/drivers/media/rc/keymaps/rc-encore-enltv.c +++ b/drivers/media/rc/keymaps/rc-encore-enltv.c @@ -24,7 +24,7 @@ static struct rc_map_table encore_enltv[] = { { 0x1e, KEY_TV }, { 0x00, KEY_VIDEO }, { 0x01, KEY_AUDIO }, /* music */ - { 0x02, KEY_MHP }, /* picture */ + { 0x02, KEY_CAMERA }, /* picture */ { 0x1f, KEY_1 }, { 0x03, KEY_2 }, diff --git a/drivers/media/rc/keymaps/rc-hauppauge-new.c b/drivers/media/rc/keymaps/rc-hauppauge-new.c index bd11da4..b6a12fe 100644 --- a/drivers/media/rc/keymaps/rc-hauppauge-new.c +++ b/drivers/media/rc/keymaps/rc-hauppauge-new.c @@ -48,7 +48,7 @@ static struct rc_map_table hauppauge_new[] = { "Multimedia Home Platform" - no "PICTURES" key in input.h */ - { 0x1a, KEY_MHP }, + { 0x1a, KEY_CAMERA }, { 0x1b, KEY_EPG }, /* Guide */ { 0x1c, KEY_TV }, diff --git a/drivers/media/rc/keymaps/rc-nebula.c b/drivers/media/rc/keymaps/rc-nebula.c index 3e6f077..ddae20e 100644 --- a/drivers/media/rc/keymaps/rc-nebula.c +++ b/drivers/media/rc/keymaps/rc-nebula.c @@ -27,7 +27,7 @@ static struct rc_map_table nebula[] = { { 0x0b, KEY_AUX }, { 0x0c, KEY_DVD }, { 0x0d, KEY_POWER }, - { 0x0e, KEY_MHP }, /* labelled 'Picture' */ + { 0x0e, KEY_CAMERA }, /* labelled 'Picture' */ { 0x0f, KEY_AUDIO }, { 0x10, KEY_INFO }, { 0x11, KEY_F13 }, /* 16:9 */ diff --git a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c index dfc9b15..2ca825b 100644 --- a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c +++ b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c @@ -51,7 +51,7 @@ static struct rc_map_table rc5_hauppauge_new[] = { "Multimedia Home Platform" - no "PICTURES" key in input.h */ - { 0x1e1a, KEY_MHP }, + { 0x1e1a, KEY_CAMERA }, { 0x1e1b, KEY_EPG }, /* Guide */ { 0x1e1c, KEY_TV }, -- cgit v0.10.2 From 6f9e46b44785eb9cc59e638970e7aa443ff6fe5e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:37 -0300 Subject: [media] rc/keymaps: Use KEY_VIDEO for Video Source Each keyboard map were using a different definition for the Source/Video Source key. Behold Columbus were the only one using KEY_PROPS. As we want to standardize those keys at X11 and at userspace applications, we need to use just one code for it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/rc/keymaps/rc-avermedia-dvbt.c b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c index 3ddb41b..c25809d 100644 --- a/drivers/media/rc/keymaps/rc-avermedia-dvbt.c +++ b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c @@ -26,12 +26,12 @@ static struct rc_map_table avermedia_dvbt[] = { { 0x16, KEY_8 }, /* '8' / 'down arrow' */ { 0x36, KEY_9 }, /* '9' */ - { 0x20, KEY_LIST }, /* 'source' */ + { 0x20, KEY_VIDEO }, /* 'source' */ { 0x10, KEY_TEXT }, /* 'teletext' */ { 0x00, KEY_POWER }, /* 'power' */ { 0x04, KEY_AUDIO }, /* 'audio' */ { 0x06, KEY_ZOOM }, /* 'full screen' */ - { 0x18, KEY_VIDEO }, /* 'display' */ + { 0x18, KEY_SWITCHVIDEOMODE }, /* 'display' */ { 0x38, KEY_SEARCH }, /* 'loop' */ { 0x08, KEY_INFO }, /* 'preview' */ { 0x2a, KEY_REWIND }, /* 'backward <<' */ diff --git a/drivers/media/rc/keymaps/rc-behold-columbus.c b/drivers/media/rc/keymaps/rc-behold-columbus.c index 4b787fa..8bf058f 100644 --- a/drivers/media/rc/keymaps/rc-behold-columbus.c +++ b/drivers/media/rc/keymaps/rc-behold-columbus.c @@ -28,7 +28,7 @@ static struct rc_map_table behold_columbus[] = { * */ { 0x13, KEY_MUTE }, - { 0x11, KEY_PROPS }, + { 0x11, KEY_VIDEO }, { 0x1C, KEY_TUNER }, /* KEY_TV/KEY_RADIO */ { 0x12, KEY_POWER }, diff --git a/drivers/media/rc/keymaps/rc-behold.c b/drivers/media/rc/keymaps/rc-behold.c index 0ee1f14..c909a23 100644 --- a/drivers/media/rc/keymaps/rc-behold.c +++ b/drivers/media/rc/keymaps/rc-behold.c @@ -97,7 +97,7 @@ static struct rc_map_table behold[] = { { 0x6b861a, KEY_STOP }, { 0x6b860e, KEY_TEXT }, { 0x6b861f, KEY_RED }, /*XXX KEY_AUDIO */ - { 0x6b861e, KEY_YELLOW }, /*XXX KEY_SOURCE */ + { 0x6b861e, KEY_VIDEO }, /* 0x1d 0x13 0x19 * * SLEEP PREVIEW DVB * diff --git a/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c index 43912bd..82c0200 100644 --- a/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c +++ b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c @@ -32,7 +32,7 @@ static struct rc_map_table dntv_live_dvb_t[] = { { 0x0c, KEY_SEARCH }, /* scan */ { 0x0d, KEY_STOP }, { 0x0e, KEY_PAUSE }, - { 0x0f, KEY_LIST }, /* source */ + { 0x0f, KEY_VIDEO }, /* source */ { 0x10, KEY_MUTE }, { 0x11, KEY_REWIND }, /* backward << */ diff --git a/drivers/media/rc/keymaps/rc-encore-enltv2.c b/drivers/media/rc/keymaps/rc-encore-enltv2.c index 7d5b00e..b6264f1 100644 --- a/drivers/media/rc/keymaps/rc-encore-enltv2.c +++ b/drivers/media/rc/keymaps/rc-encore-enltv2.c @@ -32,7 +32,7 @@ static struct rc_map_table encore_enltv2[] = { { 0x64, KEY_LAST }, /* +100 */ { 0x4e, KEY_AGAIN }, /* Recall */ - { 0x6c, KEY_SWITCHVIDEOMODE }, /* Video Source */ + { 0x6c, KEY_VIDEO }, /* Video Source */ { 0x5e, KEY_MENU }, { 0x56, KEY_SCREEN }, { 0x7a, KEY_SETUP }, diff --git a/drivers/media/rc/keymaps/rc-flydvb.c b/drivers/media/rc/keymaps/rc-flydvb.c index aea2f4a..a8b0f66 100644 --- a/drivers/media/rc/keymaps/rc-flydvb.c +++ b/drivers/media/rc/keymaps/rc-flydvb.c @@ -37,8 +37,8 @@ static struct rc_map_table flydvb[] = { { 0x13, KEY_CHANNELDOWN }, /* CH- */ { 0x1d, KEY_ENTER }, /* Enter */ - { 0x1a, KEY_MODE }, /* PIP */ - { 0x18, KEY_TUNER }, /* Source */ + { 0x1a, KEY_TV2 }, /* PIP */ + { 0x18, KEY_VIDEO }, /* Source */ { 0x1e, KEY_RECORD }, /* Record/Pause */ { 0x15, KEY_ANGLE }, /* Swap (no label on key) */ diff --git a/drivers/media/rc/keymaps/rc-hauppauge-new.c b/drivers/media/rc/keymaps/rc-hauppauge-new.c index b6a12fe..44f3283 100644 --- a/drivers/media/rc/keymaps/rc-hauppauge-new.c +++ b/drivers/media/rc/keymaps/rc-hauppauge-new.c @@ -42,7 +42,7 @@ static struct rc_map_table hauppauge_new[] = { { 0x15, KEY_DOWN }, { 0x16, KEY_LEFT }, { 0x17, KEY_RIGHT }, - { 0x18, KEY_VIDEO }, /* Videos */ + { 0x18, KEY_VCR }, /* Videos */ { 0x19, KEY_AUDIO }, /* Music */ /* 0x1a: Pictures - presume this means "Multimedia Home Platform" - @@ -56,7 +56,7 @@ static struct rc_map_table hauppauge_new[] = { { 0x1f, KEY_EXIT }, /* back/exit */ { 0x20, KEY_CHANNELUP }, /* channel / program + */ { 0x21, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x22, KEY_CHANNEL }, /* source (old black remote) */ + { 0x22, KEY_VIDEO }, /* source (old black remote) */ { 0x24, KEY_PREVIOUSSONG }, /* replay |< */ { 0x25, KEY_ENTER }, /* OK */ { 0x26, KEY_SLEEP }, /* minimize (old black remote) */ diff --git a/drivers/media/rc/keymaps/rc-kworld-315u.c b/drivers/media/rc/keymaps/rc-kworld-315u.c index 3ce6ef7..7f33edb 100644 --- a/drivers/media/rc/keymaps/rc-kworld-315u.c +++ b/drivers/media/rc/keymaps/rc-kworld-315u.c @@ -17,7 +17,7 @@ static struct rc_map_table kworld_315u[] = { { 0x6143, KEY_POWER }, - { 0x6101, KEY_TUNER }, /* source */ + { 0x6101, KEY_VIDEO }, /* source */ { 0x610b, KEY_ZOOM }, { 0x6103, KEY_POWER2 }, /* shutdown */ diff --git a/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c index fa8fd0a..8e9969d 100644 --- a/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c +++ b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c @@ -62,7 +62,7 @@ static struct rc_map_table msi_tvanywhere_plus[] = { { 0x13, KEY_AGAIN }, /* Recall */ { 0x1e, KEY_POWER }, /* Power */ - { 0x07, KEY_TUNER }, /* Source */ + { 0x07, KEY_VIDEO }, /* Source */ { 0x1c, KEY_SEARCH }, /* Scan */ { 0x18, KEY_MUTE }, /* Mute */ diff --git a/drivers/media/rc/keymaps/rc-norwood.c b/drivers/media/rc/keymaps/rc-norwood.c index 629ee9d..f1c1281 100644 --- a/drivers/media/rc/keymaps/rc-norwood.c +++ b/drivers/media/rc/keymaps/rc-norwood.c @@ -29,7 +29,7 @@ static struct rc_map_table norwood[] = { { 0x28, KEY_8 }, { 0x29, KEY_9 }, - { 0x78, KEY_TUNER }, /* Video Source */ + { 0x78, KEY_VIDEO }, /* Video Source */ { 0x2c, KEY_EXIT }, /* Open/Close software */ { 0x2a, KEY_SELECT }, /* 2 Digit Select */ { 0x69, KEY_AGAIN }, /* Recall */ diff --git a/drivers/media/rc/keymaps/rc-pctv-sedna.c b/drivers/media/rc/keymaps/rc-pctv-sedna.c index fa5ae59..7cdef6e 100644 --- a/drivers/media/rc/keymaps/rc-pctv-sedna.c +++ b/drivers/media/rc/keymaps/rc-pctv-sedna.c @@ -36,7 +36,7 @@ static struct rc_map_table pctv_sedna[] = { { 0x0e, KEY_STOP }, { 0x0f, KEY_PREVIOUSSONG }, { 0x10, KEY_ZOOM }, - { 0x11, KEY_TUNER }, /* Source */ + { 0x11, KEY_VIDEO }, /* Source */ { 0x12, KEY_POWER }, { 0x13, KEY_MUTE }, { 0x15, KEY_CHANNELDOWN }, diff --git a/drivers/media/rc/keymaps/rc-pixelview-mk12.c b/drivers/media/rc/keymaps/rc-pixelview-mk12.c index 8d9f664..125fc39 100644 --- a/drivers/media/rc/keymaps/rc-pixelview-mk12.c +++ b/drivers/media/rc/keymaps/rc-pixelview-mk12.c @@ -34,7 +34,7 @@ static struct rc_map_table pixelview_mk12[] = { { 0x866b13, KEY_AGAIN }, /* loop */ { 0x866b10, KEY_DIGITS }, /* +100 */ - { 0x866b00, KEY_MEDIA }, /* source */ + { 0x866b00, KEY_VIDEO }, /* source */ { 0x866b18, KEY_MUTE }, /* mute */ { 0x866b19, KEY_CAMERA }, /* snapshot */ { 0x866b1a, KEY_SEARCH }, /* scan */ diff --git a/drivers/media/rc/keymaps/rc-pixelview-new.c b/drivers/media/rc/keymaps/rc-pixelview-new.c index 777a700..bd78d6a 100644 --- a/drivers/media/rc/keymaps/rc-pixelview-new.c +++ b/drivers/media/rc/keymaps/rc-pixelview-new.c @@ -33,7 +33,7 @@ static struct rc_map_table pixelview_new[] = { { 0x3e, KEY_0 }, { 0x1c, KEY_AGAIN }, /* LOOP */ - { 0x3f, KEY_MEDIA }, /* Source */ + { 0x3f, KEY_VIDEO }, /* Source */ { 0x1f, KEY_LAST }, /* +100 */ { 0x1b, KEY_MUTE }, diff --git a/drivers/media/rc/keymaps/rc-pixelview.c b/drivers/media/rc/keymaps/rc-pixelview.c index 0ec5988..06187e7 100644 --- a/drivers/media/rc/keymaps/rc-pixelview.c +++ b/drivers/media/rc/keymaps/rc-pixelview.c @@ -15,7 +15,7 @@ static struct rc_map_table pixelview[] = { { 0x1e, KEY_POWER }, /* power */ - { 0x07, KEY_MEDIA }, /* source */ + { 0x07, KEY_VIDEO }, /* source */ { 0x1c, KEY_SEARCH }, /* scan */ diff --git a/drivers/media/rc/keymaps/rc-pv951.c b/drivers/media/rc/keymaps/rc-pv951.c index 83a418d..5e8beee 100644 --- a/drivers/media/rc/keymaps/rc-pv951.c +++ b/drivers/media/rc/keymaps/rc-pv951.c @@ -46,10 +46,10 @@ static struct rc_map_table pv951[] = { { 0x0c, KEY_SEARCH }, /* AUTOSCAN */ /* Not sure what to do with these ones! */ - { 0x0f, KEY_SELECT }, /* SOURCE */ + { 0x0f, KEY_VIDEO }, /* SOURCE */ { 0x0a, KEY_KPPLUS }, /* +100 */ { 0x14, KEY_EQUAL }, /* SYNC */ - { 0x1c, KEY_MEDIA }, /* PC/TV */ + { 0x1c, KEY_TV }, /* PC/TV */ }; static struct rc_map_list pv951_map = { diff --git a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c index 2ca825b..a581c86 100644 --- a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c +++ b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c @@ -45,7 +45,7 @@ static struct rc_map_table rc5_hauppauge_new[] = { { 0x1e15, KEY_DOWN }, { 0x1e16, KEY_LEFT }, { 0x1e17, KEY_RIGHT }, - { 0x1e18, KEY_VIDEO }, /* Videos */ + { 0x1e18, KEY_VCR }, /* Videos */ { 0x1e19, KEY_AUDIO }, /* Music */ /* 0x1e1a: Pictures - presume this means "Multimedia Home Platform" - @@ -59,7 +59,7 @@ static struct rc_map_table rc5_hauppauge_new[] = { { 0x1e1f, KEY_EXIT }, /* back/exit */ { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */ + { 0x1e22, KEY_VIDEO }, /* source (old black remote) */ { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ { 0x1e25, KEY_ENTER }, /* OK */ { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */ diff --git a/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c index 2d14598..6813d11 100644 --- a/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c +++ b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c @@ -35,7 +35,7 @@ static struct rc_map_table real_audio_220_32_keys[] = { { 0x15, KEY_CHANNELDOWN}, { 0x16, KEY_ENTER}, - { 0x11, KEY_LIST}, /* Source */ + { 0x11, KEY_VIDEO}, /* Source */ { 0x0d, KEY_AUDIO}, /* stereo */ { 0x0f, KEY_PREVIOUS}, /* Prev */ -- cgit v0.10.2 From d9e9f41c45536b68b14934949104cb40149e94f1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:38 -0300 Subject: [media] rc/keymaps: Fix most KEY_PROG[n] keycodes Those KEY_PROG[n] keys were used on places where the developer didn't know for sure what key should be used. On several cases, using KEY_RED, KEY_GREEN, KEY_YELLOW would be enough. On others, there are specific keys for that already. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c index 136d395..9a8752f 100644 --- a/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c +++ b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c @@ -50,9 +50,9 @@ static struct rc_map_table adstech_dvb_t_pci[] = { { 0x13, KEY_TUNER }, /* Live */ { 0x0a, KEY_A }, { 0x12, KEY_B }, - { 0x03, KEY_PROG1 }, /* 1 */ - { 0x01, KEY_PROG2 }, /* 2 */ - { 0x00, KEY_PROG3 }, /* 3 */ + { 0x03, KEY_RED }, /* 1 */ + { 0x01, KEY_GREEN }, /* 2 */ + { 0x00, KEY_YELLOW }, /* 3 */ { 0x06, KEY_DVD }, { 0x48, KEY_AUX }, /* Photo */ { 0x40, KEY_VIDEO }, diff --git a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c index f4ca1ff..9d68af2 100644 --- a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c +++ b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c @@ -31,7 +31,7 @@ static struct rc_map_table avermedia_rm_ks[] = { { 0x0505, KEY_VOLUMEDOWN }, { 0x0506, KEY_MUTE }, { 0x0507, KEY_RIGHT }, - { 0x0508, KEY_PROG1 }, + { 0x0508, KEY_RED }, { 0x0509, KEY_1 }, { 0x050a, KEY_2 }, { 0x050b, KEY_3 }, diff --git a/drivers/media/rc/keymaps/rc-cinergy.c b/drivers/media/rc/keymaps/rc-cinergy.c index 99520ff..cf3a6bf 100644 --- a/drivers/media/rc/keymaps/rc-cinergy.c +++ b/drivers/media/rc/keymaps/rc-cinergy.c @@ -25,7 +25,7 @@ static struct rc_map_table cinergy[] = { { 0x09, KEY_9 }, { 0x0a, KEY_POWER }, - { 0x0b, KEY_PROG1 }, /* app */ + { 0x0b, KEY_MEDIA }, /* app */ { 0x0c, KEY_ZOOM }, /* zoom/fullscreen */ { 0x0d, KEY_CHANNELUP }, /* channel */ { 0x0e, KEY_CHANNELDOWN }, /* channel- */ diff --git a/drivers/media/rc/keymaps/rc-encore-enltv.c b/drivers/media/rc/keymaps/rc-encore-enltv.c index 1a0e590b..e56ac6e 100644 --- a/drivers/media/rc/keymaps/rc-encore-enltv.c +++ b/drivers/media/rc/keymaps/rc-encore-enltv.c @@ -77,7 +77,7 @@ static struct rc_map_table encore_enltv[] = { { 0x50, KEY_SLEEP }, /* shutdown */ { 0x51, KEY_MODE }, /* stereo > main */ { 0x52, KEY_SELECT }, /* stereo > sap */ - { 0x53, KEY_PROG1 }, /* teletext */ + { 0x53, KEY_TEXT }, /* teletext */ { 0x59, KEY_RED }, /* AP1 */ -- cgit v0.10.2 From 04efa85e03b25c5b519e495485d005a8b69ccdab Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:39 -0300 Subject: [media] rc/keymaps: Use KEY_LEFTMETA were pertinent Using xev and testing the "Windows" key on a normal keyboard, it is mapped as KEY_LEFTMETA. So, as this is the standard code for it, use it, instead of a generic, meaningless KEY_PROG1. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/rc/keymaps/rc-imon-mce.c b/drivers/media/rc/keymaps/rc-imon-mce.c index cb67184..937a819 100644 --- a/drivers/media/rc/keymaps/rc-imon-mce.c +++ b/drivers/media/rc/keymaps/rc-imon-mce.c @@ -111,7 +111,7 @@ static struct rc_map_table imon_mce[] = { { 0x800ff44d, KEY_TITLE }, { 0x800ff40c, KEY_POWER }, - { 0x800ff40d, KEY_PROG1 }, /* Windows MCE button */ + { 0x800ff40d, KEY_LEFTMETA }, /* Windows MCE button */ }; diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c index eef46b7..63d42bd 100644 --- a/drivers/media/rc/keymaps/rc-imon-pad.c +++ b/drivers/media/rc/keymaps/rc-imon-pad.c @@ -125,7 +125,7 @@ static struct rc_map_table imon_pad[] = { { 0x2b8195b7, KEY_CONTEXT_MENU }, /* Left Menu*/ { 0x02000065, KEY_COMPOSE }, /* RightMenu */ { 0x28b715b7, KEY_COMPOSE }, /* RightMenu */ - { 0x2ab195b7, KEY_PROG1 }, /* Go or MultiMon */ + { 0x2ab195b7, KEY_LEFTMETA }, /* Go or MultiMon */ { 0x29b715b7, KEY_DASHBOARD }, /* AppLauncher */ }; diff --git a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c index e45f0b8..08d1831 100644 --- a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c +++ b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c @@ -17,7 +17,7 @@ */ static struct rc_map_table kworld_plus_tv_analog[] = { - { 0x0c, KEY_PROG1 }, /* Kworld key */ + { 0x0c, KEY_LEFTMETA }, /* Kworld key */ { 0x16, KEY_CLOSECD }, /* -> ) */ { 0x1d, KEY_POWER2 }, diff --git a/drivers/media/rc/keymaps/rc-rc6-mce.c b/drivers/media/rc/keymaps/rc-rc6-mce.c index 2f5dc06..8dd519e 100644 --- a/drivers/media/rc/keymaps/rc-rc6-mce.c +++ b/drivers/media/rc/keymaps/rc-rc6-mce.c @@ -30,7 +30,7 @@ static struct rc_map_table rc6_mce[] = { { 0x800f040a, KEY_DELETE }, { 0x800f040b, KEY_ENTER }, { 0x800f040c, KEY_POWER }, /* PC Power */ - { 0x800f040d, KEY_PROG1 }, /* Windows MCE button */ + { 0x800f040d, KEY_LEFTMETA }, /* Windows MCE button */ { 0x800f040e, KEY_MUTE }, { 0x800f040f, KEY_INFO }, -- cgit v0.10.2 From 991bd3c25e8e07960ecd6bcc7079737f83f17c36 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:40 -0300 Subject: [media] dw2102: Use multimedia keys instead of an app-specific mapping This driver uses an app-specific keymap for one of the tables. This is wrong. Instead, use the standard keycodes. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 98cf302..f5b9da1 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1224,8 +1224,8 @@ static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) } static struct rc_map_table rc_map_dw210x_table[] = { - { 0xf80a, KEY_Q }, /*power*/ - { 0xf80c, KEY_M }, /*mute*/ + { 0xf80a, KEY_POWER2 }, /*power*/ + { 0xf80c, KEY_MUTE }, /*mute*/ { 0xf811, KEY_1 }, { 0xf812, KEY_2 }, { 0xf813, KEY_3 }, @@ -1236,25 +1236,25 @@ static struct rc_map_table rc_map_dw210x_table[] = { { 0xf818, KEY_8 }, { 0xf819, KEY_9 }, { 0xf810, KEY_0 }, - { 0xf81c, KEY_PAGEUP }, /*ch+*/ - { 0xf80f, KEY_PAGEDOWN }, /*ch-*/ - { 0xf81a, KEY_O }, /*vol+*/ - { 0xf80e, KEY_Z }, /*vol-*/ - { 0xf804, KEY_R }, /*rec*/ - { 0xf809, KEY_D }, /*fav*/ - { 0xf808, KEY_BACKSPACE }, /*rewind*/ - { 0xf807, KEY_A }, /*fast*/ - { 0xf80b, KEY_P }, /*pause*/ - { 0xf802, KEY_ESC }, /*cancel*/ - { 0xf803, KEY_G }, /*tab*/ + { 0xf81c, KEY_CHANNELUP }, /*ch+*/ + { 0xf80f, KEY_CHANNELDOWN }, /*ch-*/ + { 0xf81a, KEY_VOLUMEUP }, /*vol+*/ + { 0xf80e, KEY_VOLUMEDOWN }, /*vol-*/ + { 0xf804, KEY_RECORD }, /*rec*/ + { 0xf809, KEY_FAVORITES }, /*fav*/ + { 0xf808, KEY_REWIND }, /*rewind*/ + { 0xf807, KEY_FASTFORWARD }, /*fast*/ + { 0xf80b, KEY_PAUSE }, /*pause*/ + { 0xf802, KEY_ESC }, /*cancel*/ + { 0xf803, KEY_TAB }, /*tab*/ { 0xf800, KEY_UP }, /*up*/ - { 0xf81f, KEY_ENTER }, /*ok*/ - { 0xf801, KEY_DOWN }, /*down*/ - { 0xf805, KEY_C }, /*cap*/ - { 0xf806, KEY_S }, /*stop*/ - { 0xf840, KEY_F }, /*full*/ - { 0xf81e, KEY_W }, /*tvmode*/ - { 0xf81b, KEY_B }, /*recall*/ + { 0xf81f, KEY_OK }, /*ok*/ + { 0xf801, KEY_DOWN }, /*down*/ + { 0xf805, KEY_CAMERA }, /*cap*/ + { 0xf806, KEY_STOP }, /*stop*/ + { 0xf840, KEY_ZOOM }, /*full*/ + { 0xf81e, KEY_TV }, /*tvmode*/ + { 0xf81b, KEY_LAST }, /*recall*/ }; static struct rc_map_table rc_map_tevii_table[] = { -- cgit v0.10.2 From 76fd93b62322e5e4bfdf2744efe92f0de9e6ac4a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:41 -0300 Subject: [media] opera1: Use multimedia keys instead of an app-specific mapping This driver uses an app-specific keymap for one of the tables. This is wrong. Instead, use the standard keycodes. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c index 1f1b7d6..7e569f4 100644 --- a/drivers/media/dvb/dvb-usb/opera1.c +++ b/drivers/media/dvb/dvb-usb/opera1.c @@ -342,23 +342,22 @@ static struct rc_map_table rc_map_opera1_table[] = { {0x49b6, KEY_8}, {0x05fa, KEY_9}, {0x45ba, KEY_0}, - {0x09f6, KEY_UP}, /*chanup */ - {0x1be5, KEY_DOWN}, /*chandown */ - {0x5da3, KEY_LEFT}, /*voldown */ - {0x5fa1, KEY_RIGHT}, /*volup */ - {0x07f8, KEY_SPACE}, /*tab */ - {0x1fe1, KEY_ENTER}, /*play ok */ - {0x1be4, KEY_Z}, /*zoom */ - {0x59a6, KEY_M}, /*mute */ - {0x5ba5, KEY_F}, /*tv/f */ - {0x19e7, KEY_R}, /*rec */ - {0x01fe, KEY_S}, /*Stop */ - {0x03fd, KEY_P}, /*pause */ - {0x03fc, KEY_W}, /*<- -> */ - {0x07f9, KEY_C}, /*capture */ - {0x47b9, KEY_Q}, /*exit */ - {0x43bc, KEY_O}, /*power */ - + {0x09f6, KEY_CHANNELUP}, /*chanup */ + {0x1be5, KEY_CHANNELDOWN}, /*chandown */ + {0x5da3, KEY_VOLUMEDOWN}, /*voldown */ + {0x5fa1, KEY_VOLUMEUP}, /*volup */ + {0x07f8, KEY_SPACE}, /*tab */ + {0x1fe1, KEY_OK}, /*play ok */ + {0x1be4, KEY_ZOOM}, /*zoom */ + {0x59a6, KEY_MUTE}, /*mute */ + {0x5ba5, KEY_RADIO}, /*tv/f */ + {0x19e7, KEY_RECORD}, /*rec */ + {0x01fe, KEY_STOP}, /*Stop */ + {0x03fd, KEY_PAUSE}, /*pause */ + {0x03fc, KEY_SCREEN}, /*<- -> */ + {0x07f9, KEY_CAMERA}, /*capture */ + {0x47b9, KEY_ESC}, /*exit */ + {0x43bc, KEY_POWER2}, /*power */ }; static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) -- cgit v0.10.2 From 898a83e9243e2398e863887b36476c3cc227074a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:42 -0300 Subject: [media] a800: Fix a few wrong IR key assignments Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c index 53b93a4..f8e9bf1 100644 --- a/drivers/media/dvb/dvb-usb/a800.c +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -38,8 +38,8 @@ static int a800_identify_state(struct usb_device *udev, struct dvb_usb_device_pr } static struct rc_map_table rc_map_a800_table[] = { - { 0x0201, KEY_PROG1 }, /* SOURCE */ - { 0x0200, KEY_POWER }, /* POWER */ + { 0x0201, KEY_MODE }, /* SOURCE */ + { 0x0200, KEY_POWER2 }, /* POWER */ { 0x0205, KEY_1 }, /* 1 */ { 0x0206, KEY_2 }, /* 2 */ { 0x0207, KEY_3 }, /* 3 */ @@ -52,8 +52,8 @@ static struct rc_map_table rc_map_a800_table[] = { { 0x0212, KEY_LEFT }, /* L / DISPLAY */ { 0x0211, KEY_0 }, /* 0 */ { 0x0213, KEY_RIGHT }, /* R / CH RTN */ - { 0x0217, KEY_PROG2 }, /* SNAP SHOT */ - { 0x0210, KEY_PROG3 }, /* 16-CH PREV */ + { 0x0217, KEY_CAMERA }, /* SNAP SHOT */ + { 0x0210, KEY_LAST }, /* 16-CH PREV */ { 0x021e, KEY_VOLUMEDOWN }, /* VOL DOWN */ { 0x020c, KEY_ZOOM }, /* FULL SCREEN */ { 0x021f, KEY_VOLUMEUP }, /* VOL UP */ -- cgit v0.10.2 From 7f961c6989a98c10faea76a39228a63624f10690 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:43 -0300 Subject: [media] rc-winfast: Fix the keycode tables One of the remotes has a picture available at: http://lirc.sourceforge.net/remotes/leadtek/Y04G0004.jpg As there's one variant with a set direction keys plus vol/chann keys, and the same table is used for both models, change it to represent all keys, avoiding the usage of weird function keys. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/rc/keymaps/rc-winfast.c b/drivers/media/rc/keymaps/rc-winfast.c index 2747db4..0062ca2 100644 --- a/drivers/media/rc/keymaps/rc-winfast.c +++ b/drivers/media/rc/keymaps/rc-winfast.c @@ -27,15 +27,15 @@ static struct rc_map_table winfast[] = { { 0x0e, KEY_8 }, { 0x0f, KEY_9 }, - { 0x00, KEY_POWER }, + { 0x00, KEY_POWER2 }, { 0x1b, KEY_AUDIO }, /* Audio Source */ { 0x02, KEY_TUNER }, /* TV/FM, not on Y0400052 */ { 0x1e, KEY_VIDEO }, /* Video Source */ { 0x16, KEY_INFO }, /* Display information */ - { 0x04, KEY_VOLUMEUP }, - { 0x08, KEY_VOLUMEDOWN }, - { 0x0c, KEY_CHANNELUP }, - { 0x10, KEY_CHANNELDOWN }, + { 0x04, KEY_LEFT }, + { 0x08, KEY_RIGHT }, + { 0x0c, KEY_UP }, + { 0x10, KEY_DOWN }, { 0x03, KEY_ZOOM }, /* fullscreen */ { 0x1f, KEY_TEXT }, /* closed caption/teletext */ { 0x20, KEY_SLEEP }, @@ -47,7 +47,7 @@ static struct rc_map_table winfast[] = { { 0x2e, KEY_BLUE }, { 0x18, KEY_KPPLUS }, /* fine tune + , not on Y040052 */ { 0x19, KEY_KPMINUS }, /* fine tune - , not on Y040052 */ - { 0x2a, KEY_MEDIA }, /* PIP (Picture in picture */ + { 0x2a, KEY_TV2 }, /* PIP (Picture in picture */ { 0x21, KEY_DOT }, { 0x13, KEY_ENTER }, { 0x11, KEY_LAST }, /* Recall (last channel */ @@ -57,7 +57,7 @@ static struct rc_map_table winfast[] = { { 0x25, KEY_TIME }, /* Time Shifting */ { 0x26, KEY_STOP }, { 0x27, KEY_RECORD }, - { 0x28, KEY_SAVE }, /* Screenshot */ + { 0x28, KEY_CAMERA }, /* Screenshot */ { 0x2f, KEY_MENU }, { 0x30, KEY_CANCEL }, { 0x31, KEY_CHANNEL }, /* Channel Surf */ @@ -70,10 +70,10 @@ static struct rc_map_table winfast[] = { { 0x38, KEY_DVD }, { 0x1a, KEY_MODE}, /* change to MCE mode on Y04G0051 */ - { 0x3e, KEY_F21 }, /* MCE +VOL, on Y04G0033 */ - { 0x3a, KEY_F22 }, /* MCE -VOL, on Y04G0033 */ - { 0x3b, KEY_F23 }, /* MCE +CH, on Y04G0033 */ - { 0x3f, KEY_F24 } /* MCE -CH, on Y04G0033 */ + { 0x3e, KEY_VOLUMEUP }, /* MCE +VOL, on Y04G0033 */ + { 0x3a, KEY_VOLUMEDOWN }, /* MCE -VOL, on Y04G0033 */ + { 0x3b, KEY_CHANNELUP }, /* MCE +CH, on Y04G0033 */ + { 0x3f, KEY_CHANNELDOWN } /* MCE -CH, on Y04G0033 */ }; static struct rc_map_list winfast_map = { -- cgit v0.10.2 From 0684f5b0fc2b543287abad03fc40e65d5e89d1ef Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:43 -0300 Subject: [media] rc-rc5-hauppauge-new: Add the old control to the table Adds the old grey remote controller to Hauppauge table. Hans borrowed me an old gray Hauppauge RC. Thanks to that, we can fix the RC5 table for Hauppauge. Thanks-to: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c index a581c86..dcbf3bd 100644 --- a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c +++ b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c @@ -20,6 +20,10 @@ */ static struct rc_map_table rc5_hauppauge_new[] = { + /* + * Remote Controller Hauppauge Gray found on modern devices + * Keycodes start with address = 0x1e + */ /* Keys 0 to 9 */ { 0x1e00, KEY_0 }, { 0x1e01, KEY_1 }, @@ -76,7 +80,57 @@ static struct rc_map_table rc5_hauppauge_new[] = { { 0x1e3c, KEY_ZOOM }, /* full */ { 0x1e3d, KEY_POWER }, /* system power (green button) */ - /* Keycodes for DSR-0112 remote bundled with Haupauge MiniStick */ + /* + * Old Remote Controller Hauppauge Gray with a golden screen + * Keycodes start with address = 0x1d + */ + { 0x1f3d, KEY_POWER2 }, /* system power (green button) */ + { 0x1f3b, KEY_SELECT }, /* GO */ + + /* Keys 0 to 9 */ + { 0x1f00, KEY_0 }, + { 0x1f01, KEY_1 }, + { 0x1f02, KEY_2 }, + { 0x1f03, KEY_3 }, + { 0x1f04, KEY_4 }, + { 0x1f05, KEY_5 }, + { 0x1f06, KEY_6 }, + { 0x1f07, KEY_7 }, + { 0x1f08, KEY_8 }, + { 0x1f09, KEY_9 }, + + { 0x1f1f, KEY_EXIT }, /* back/exit */ + { 0x1f0d, KEY_MENU }, + + { 0x1f10, KEY_VOLUMEUP }, + { 0x1f11, KEY_VOLUMEDOWN }, + { 0x1f20, KEY_CHANNELUP }, /* channel / program + */ + { 0x1f21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x1f25, KEY_ENTER }, /* OK */ + + { 0x1f0b, KEY_RED }, /* red button */ + { 0x1f2e, KEY_GREEN }, /* green button */ + { 0x1f38, KEY_YELLOW }, /* yellow key */ + { 0x1f29, KEY_BLUE }, /* blue key */ + + { 0x1f0f, KEY_MUTE }, + { 0x1f0c, KEY_RADIO }, /* There's no indicator on this key */ + { 0x1f3c, KEY_ZOOM }, /* full */ + + { 0x1f32, KEY_REWIND }, /* backward << */ + { 0x1f35, KEY_PLAY }, + { 0x1f34, KEY_FASTFORWARD }, /* forward >> */ + + { 0x1f37, KEY_RECORD }, /* recording */ + { 0x1f36, KEY_STOP }, + { 0x1f30, KEY_PAUSE }, /* pause */ + + { 0x1f24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x1f1e, KEY_NEXTSONG }, /* skip >| */ + + /* + * Keycodes for DSR-0112 remote bundled with Haupauge MiniStick + */ { 0x1d00, KEY_0 }, { 0x1d01, KEY_1 }, { 0x1d02, KEY_2 }, -- cgit v0.10.2 From 9890a9edc8b8bba6d710499afb70e981e0b0dfae Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:44 -0300 Subject: [media] rc-rc5-hauppauge-new: Add support for the old Black RC Hans borrowed me an old Black Hauppauge RC. Thanks to that, we can fix the RC5 table for Hauppauge. Thanks-to: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c index dcbf3bd..4106008 100644 --- a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c +++ b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c @@ -82,7 +82,7 @@ static struct rc_map_table rc5_hauppauge_new[] = { /* * Old Remote Controller Hauppauge Gray with a golden screen - * Keycodes start with address = 0x1d + * Keycodes start with address = 0x1f */ { 0x1f3d, KEY_POWER2 }, /* system power (green button) */ { 0x1f3b, KEY_SELECT }, /* GO */ @@ -130,6 +130,7 @@ static struct rc_map_table rc5_hauppauge_new[] = { /* * Keycodes for DSR-0112 remote bundled with Haupauge MiniStick + * Keycodes start with address = 0x1d */ { 0x1d00, KEY_0 }, { 0x1d01, KEY_1 }, @@ -167,6 +168,39 @@ static struct rc_map_table rc5_hauppauge_new[] = { { 0x1d3b, KEY_GOTO }, { 0x1d3d, KEY_POWER }, { 0x1d3f, KEY_HOME }, + + /* + * Keycodes for the old Black Remote Controller + * This one also uses RC-5 protocol + * Keycodes start with address = 0x00 + */ + { 0x001f, KEY_TV }, + { 0x0020, KEY_CHANNELUP }, + { 0x000c, KEY_RADIO }, + + { 0x0011, KEY_VOLUMEDOWN }, + { 0x002e, KEY_ZOOM }, /* full screen */ + { 0x0010, KEY_VOLUMEUP }, + + { 0x000d, KEY_MUTE }, + { 0x0021, KEY_CHANNELDOWN }, + { 0x0022, KEY_VIDEO }, /* source */ + + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, + { 0x0003, KEY_3 }, + + { 0x0004, KEY_4 }, + { 0x0005, KEY_5 }, + { 0x0006, KEY_6 }, + + { 0x0007, KEY_7 }, + { 0x0008, KEY_8 }, + { 0x0009, KEY_9 }, + + { 0x001e, KEY_RED }, /* Reserved */ + { 0x0000, KEY_0 }, + { 0x0026, KEY_SLEEP }, /* Minimize */ }; static struct rc_map_list rc5_hauppauge_new_map = { -- cgit v0.10.2 From 0548757419c974d8c0851c13279b2c89a12b2978 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:45 -0300 Subject: [media] rc-rc5-hauppauge-new: Fix Hauppauge Grey mapping The keys for the old black were messed with the ones for the hauppauge grey. Fix it. Also, fixes some keycodes and order the keys according with the way they appear inside the remote controller. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c index 4106008..cb312da 100644 --- a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c +++ b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c @@ -24,61 +24,67 @@ static struct rc_map_table rc5_hauppauge_new[] = { * Remote Controller Hauppauge Gray found on modern devices * Keycodes start with address = 0x1e */ - /* Keys 0 to 9 */ - { 0x1e00, KEY_0 }, + + { 0x1e3b, KEY_SELECT }, /* GO / house symbol */ + { 0x1e3d, KEY_POWER2 }, /* system power (green button) */ + + { 0x1e1c, KEY_TV }, + { 0x1e18, KEY_VIDEO }, /* Videos */ + { 0x1e19, KEY_AUDIO }, /* Music */ + { 0x1e1a, KEY_CAMERA }, /* Pictures */ + + { 0x1e1b, KEY_EPG }, /* Guide */ + { 0x1e0c, KEY_RADIO }, + + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e25, KEY_OK }, /* OK */ + + { 0x1e1f, KEY_EXIT }, /* back/exit */ + { 0x1e0d, KEY_MENU }, + + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + + { 0x1e12, KEY_PREVIOUS }, /* previous channel */ + { 0x1e0f, KEY_MUTE }, + + { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ + { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ + + { 0x1e37, KEY_RECORD }, /* recording */ + { 0x1e36, KEY_STOP }, + + { 0x1e32, KEY_REWIND }, /* backward << */ + { 0x1e35, KEY_PLAY }, + { 0x1e34, KEY_FASTFORWARD }, /* forward >> */ + + { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x1e30, KEY_PAUSE }, /* pause */ + { 0x1e1e, KEY_NEXTSONG }, /* skip >| */ + { 0x1e01, KEY_1 }, { 0x1e02, KEY_2 }, { 0x1e03, KEY_3 }, + { 0x1e04, KEY_4 }, { 0x1e05, KEY_5 }, { 0x1e06, KEY_6 }, + { 0x1e07, KEY_7 }, { 0x1e08, KEY_8 }, { 0x1e09, KEY_9 }, { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */ - { 0x1e0b, KEY_RED }, /* red button */ - { 0x1e0c, KEY_RADIO }, - { 0x1e0d, KEY_MENU }, - { 0x1e0e, KEY_SUBTITLE }, /* also the # key */ - { 0x1e0f, KEY_MUTE }, - { 0x1e10, KEY_VOLUMEUP }, - { 0x1e11, KEY_VOLUMEDOWN }, - { 0x1e12, KEY_PREVIOUS }, /* previous channel */ - { 0x1e14, KEY_UP }, - { 0x1e15, KEY_DOWN }, - { 0x1e16, KEY_LEFT }, - { 0x1e17, KEY_RIGHT }, - { 0x1e18, KEY_VCR }, /* Videos */ - { 0x1e19, KEY_AUDIO }, /* Music */ - /* 0x1e1a: Pictures - presume this means - "Multimedia Home Platform" - - no "PICTURES" key in input.h - */ - { 0x1e1a, KEY_CAMERA }, + { 0x1e00, KEY_0 }, + { 0x1e0e, KEY_SUBTITLE }, /* also the Pound key (#) */ - { 0x1e1b, KEY_EPG }, /* Guide */ - { 0x1e1c, KEY_TV }, - { 0x1e1e, KEY_NEXTSONG }, /* skip >| */ - { 0x1e1f, KEY_EXIT }, /* back/exit */ - { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ - { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x1e22, KEY_VIDEO }, /* source (old black remote) */ - { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ - { 0x1e25, KEY_ENTER }, /* OK */ - { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */ - { 0x1e29, KEY_BLUE }, /* blue key */ + { 0x1e0b, KEY_RED }, /* red button */ { 0x1e2e, KEY_GREEN }, /* green button */ - { 0x1e30, KEY_PAUSE }, /* pause */ - { 0x1e32, KEY_REWIND }, /* backward << */ - { 0x1e34, KEY_FASTFORWARD }, /* forward >> */ - { 0x1e35, KEY_PLAY }, - { 0x1e36, KEY_STOP }, - { 0x1e37, KEY_RECORD }, /* recording */ { 0x1e38, KEY_YELLOW }, /* yellow key */ - { 0x1e3b, KEY_SELECT }, /* top right button */ - { 0x1e3c, KEY_ZOOM }, /* full */ - { 0x1e3d, KEY_POWER }, /* system power (green button) */ + { 0x1e29, KEY_BLUE }, /* blue key */ /* * Old Remote Controller Hauppauge Gray with a golden screen -- cgit v0.10.2 From 15195d3a83b59f0ca3bed52cbe5524042ce13fd6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:47 -0300 Subject: [media] rc/keymaps: Rename Hauppauge table as rc-hauppauge There are two "hauppauge-new" keymaps, one with protocol unknown, and the other with the protocol marked accordingly. However, both tables are miss-named. Also, the old rc-hauppauge-new is broken, as it mixes three different controllers as if they were just one. This patch solves half of the problem by renaming the correct keycode table as just rc-hauppauge. This table contains the codes for the four different types of remote controllers found on Hauppauge cards, properly mapped with their different addresses. create mode 100644 drivers/media/rc/keymaps/rc-hauppauge.c delete mode 100644 drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c [Jarod: fix up RC_MAP_HAUPPAUGE defines] Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c index 25b43e5..af121db 100644 --- a/drivers/media/dvb/siano/sms-cards.c +++ b/drivers/media/dvb/siano/sms-cards.c @@ -64,7 +64,7 @@ static struct sms_board sms_boards[] = { .type = SMS_NOVA_B0, .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw", .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", - .rc_codes = RC_MAP_RC5_HAUPPAUGE_NEW, + .rc_codes = RC_MAP_HAUPPAUGE, .board_cfg.leds_power = 26, .board_cfg.led0 = 27, .board_cfg.led1 = 28, diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index cb72121..1f446f9 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -68,7 +68,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-proteus-2309.o \ rc-purpletv.o \ rc-pv951.o \ - rc-rc5-hauppauge-new.o \ + rc-hauppauge.o \ rc-rc5-tv.o \ rc-rc6-mce.o \ rc-real-audio-220-32-keys.o \ diff --git a/drivers/media/rc/keymaps/rc-hauppauge.c b/drivers/media/rc/keymaps/rc-hauppauge.c new file mode 100644 index 0000000..cd3db77 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-hauppauge.c @@ -0,0 +1,241 @@ +/* rc-hauppauge.c - Keytable for Hauppauge Remote Controllers + * + * keymap imported from ir-keymaps.c + * + * This map currently contains the code for four different RCs: + * - New Hauppauge Gray; + * - Old Hauppauge Gray (with a golden screen for media keys); + * - Hauppauge Black; + * - DSR-0112 remote bundled with Haupauge MiniStick. + * + * Copyright (c) 2010-2011 by Mauro Carvalho Chehab + * + * 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 + +/* + * Hauppauge:the newer, gray remotes (seems there are multiple + * slightly different versions), shipped with cx88+ivtv cards. + * + * This table contains the complete RC5 code, instead of just the data part + */ + +static struct rc_map_table rc5_hauppauge_new[] = { + /* + * Remote Controller Hauppauge Gray found on modern devices + * Keycodes start with address = 0x1e + */ + + { 0x1e3b, KEY_SELECT }, /* GO / house symbol */ + { 0x1e3d, KEY_POWER2 }, /* system power (green button) */ + + { 0x1e1c, KEY_TV }, + { 0x1e18, KEY_VIDEO }, /* Videos */ + { 0x1e19, KEY_AUDIO }, /* Music */ + { 0x1e1a, KEY_CAMERA }, /* Pictures */ + + { 0x1e1b, KEY_EPG }, /* Guide */ + { 0x1e0c, KEY_RADIO }, + + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e25, KEY_OK }, /* OK */ + + { 0x1e1f, KEY_EXIT }, /* back/exit */ + { 0x1e0d, KEY_MENU }, + + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + + { 0x1e12, KEY_PREVIOUS }, /* previous channel */ + { 0x1e0f, KEY_MUTE }, + + { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ + { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ + + { 0x1e37, KEY_RECORD }, /* recording */ + { 0x1e36, KEY_STOP }, + + { 0x1e32, KEY_REWIND }, /* backward << */ + { 0x1e35, KEY_PLAY }, + { 0x1e34, KEY_FASTFORWARD }, /* forward >> */ + + { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x1e30, KEY_PAUSE }, /* pause */ + { 0x1e1e, KEY_NEXTSONG }, /* skip >| */ + + { 0x1e01, KEY_1 }, + { 0x1e02, KEY_2 }, + { 0x1e03, KEY_3 }, + + { 0x1e04, KEY_4 }, + { 0x1e05, KEY_5 }, + { 0x1e06, KEY_6 }, + + { 0x1e07, KEY_7 }, + { 0x1e08, KEY_8 }, + { 0x1e09, KEY_9 }, + + { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */ + { 0x1e00, KEY_0 }, + { 0x1e0e, KEY_SUBTITLE }, /* also the Pound key (#) */ + + { 0x1e0b, KEY_RED }, /* red button */ + { 0x1e2e, KEY_GREEN }, /* green button */ + { 0x1e38, KEY_YELLOW }, /* yellow key */ + { 0x1e29, KEY_BLUE }, /* blue key */ + + /* + * Old Remote Controller Hauppauge Gray with a golden screen + * Keycodes start with address = 0x1f + */ + { 0x1f3d, KEY_POWER2 }, /* system power (green button) */ + { 0x1f3b, KEY_SELECT }, /* GO */ + + /* Keys 0 to 9 */ + { 0x1f00, KEY_0 }, + { 0x1f01, KEY_1 }, + { 0x1f02, KEY_2 }, + { 0x1f03, KEY_3 }, + { 0x1f04, KEY_4 }, + { 0x1f05, KEY_5 }, + { 0x1f06, KEY_6 }, + { 0x1f07, KEY_7 }, + { 0x1f08, KEY_8 }, + { 0x1f09, KEY_9 }, + + { 0x1f1f, KEY_EXIT }, /* back/exit */ + { 0x1f0d, KEY_MENU }, + + { 0x1f10, KEY_VOLUMEUP }, + { 0x1f11, KEY_VOLUMEDOWN }, + { 0x1f20, KEY_CHANNELUP }, /* channel / program + */ + { 0x1f21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x1f25, KEY_ENTER }, /* OK */ + + { 0x1f0b, KEY_RED }, /* red button */ + { 0x1f2e, KEY_GREEN }, /* green button */ + { 0x1f38, KEY_YELLOW }, /* yellow key */ + { 0x1f29, KEY_BLUE }, /* blue key */ + + { 0x1f0f, KEY_MUTE }, + { 0x1f0c, KEY_RADIO }, /* There's no indicator on this key */ + { 0x1f3c, KEY_ZOOM }, /* full */ + + { 0x1f32, KEY_REWIND }, /* backward << */ + { 0x1f35, KEY_PLAY }, + { 0x1f34, KEY_FASTFORWARD }, /* forward >> */ + + { 0x1f37, KEY_RECORD }, /* recording */ + { 0x1f36, KEY_STOP }, + { 0x1f30, KEY_PAUSE }, /* pause */ + + { 0x1f24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x1f1e, KEY_NEXTSONG }, /* skip >| */ + + /* + * Keycodes for DSR-0112 remote bundled with Haupauge MiniStick + * Keycodes start with address = 0x1d + */ + { 0x1d00, KEY_0 }, + { 0x1d01, KEY_1 }, + { 0x1d02, KEY_2 }, + { 0x1d03, KEY_3 }, + { 0x1d04, KEY_4 }, + { 0x1d05, KEY_5 }, + { 0x1d06, KEY_6 }, + { 0x1d07, KEY_7 }, + { 0x1d08, KEY_8 }, + { 0x1d09, KEY_9 }, + { 0x1d0a, KEY_TEXT }, + { 0x1d0d, KEY_MENU }, + { 0x1d0f, KEY_MUTE }, + { 0x1d10, KEY_VOLUMEUP }, + { 0x1d11, KEY_VOLUMEDOWN }, + { 0x1d12, KEY_PREVIOUS }, /* Prev.Ch .. ??? */ + { 0x1d14, KEY_UP }, + { 0x1d15, KEY_DOWN }, + { 0x1d16, KEY_LEFT }, + { 0x1d17, KEY_RIGHT }, + { 0x1d1c, KEY_TV }, + { 0x1d1e, KEY_NEXT }, /* >| */ + { 0x1d1f, KEY_EXIT }, + { 0x1d20, KEY_CHANNELUP }, + { 0x1d21, KEY_CHANNELDOWN }, + { 0x1d24, KEY_LAST }, /* <| */ + { 0x1d25, KEY_OK }, + { 0x1d30, KEY_PAUSE }, + { 0x1d32, KEY_REWIND }, + { 0x1d34, KEY_FASTFORWARD }, + { 0x1d35, KEY_PLAY }, + { 0x1d36, KEY_STOP }, + { 0x1d37, KEY_RECORD }, + { 0x1d3b, KEY_GOTO }, + { 0x1d3d, KEY_POWER }, + { 0x1d3f, KEY_HOME }, + + /* + * Keycodes for the old Black Remote Controller + * This one also uses RC-5 protocol + * Keycodes start with address = 0x00 + */ + { 0x001f, KEY_TV }, + { 0x0020, KEY_CHANNELUP }, + { 0x000c, KEY_RADIO }, + + { 0x0011, KEY_VOLUMEDOWN }, + { 0x002e, KEY_ZOOM }, /* full screen */ + { 0x0010, KEY_VOLUMEUP }, + + { 0x000d, KEY_MUTE }, + { 0x0021, KEY_CHANNELDOWN }, + { 0x0022, KEY_VIDEO }, /* source */ + + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, + { 0x0003, KEY_3 }, + + { 0x0004, KEY_4 }, + { 0x0005, KEY_5 }, + { 0x0006, KEY_6 }, + + { 0x0007, KEY_7 }, + { 0x0008, KEY_8 }, + { 0x0009, KEY_9 }, + + { 0x001e, KEY_RED }, /* Reserved */ + { 0x0000, KEY_0 }, + { 0x0026, KEY_SLEEP }, /* Minimize */ +}; + +static struct rc_map_list rc5_hauppauge_new_map = { + .map = { + .scan = rc5_hauppauge_new, + .size = ARRAY_SIZE(rc5_hauppauge_new), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_HAUPPAUGE, + } +}; + +static int __init init_rc_map_rc5_hauppauge_new(void) +{ + return rc_map_register(&rc5_hauppauge_new_map); +} + +static void __exit exit_rc_map_rc5_hauppauge_new(void) +{ + rc_map_unregister(&rc5_hauppauge_new_map); +} + +module_init(init_rc_map_rc5_hauppauge_new) +module_exit(exit_rc_map_rc5_hauppauge_new) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); diff --git a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c b/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c deleted file mode 100644 index cb312da..0000000 --- a/drivers/media/rc/keymaps/rc-rc5-hauppauge-new.c +++ /dev/null @@ -1,235 +0,0 @@ -/* rc5-hauppauge-new.h - Keytable for rc5_hauppauge_new Remote Controller - * - * keymap imported from ir-keymaps.c - * - * Copyright (c) 2010 by Mauro Carvalho Chehab - * - * 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 - -/* - * Hauppauge:the newer, gray remotes (seems there are multiple - * slightly different versions), shipped with cx88+ivtv cards. - * - * This table contains the complete RC5 code, instead of just the data part - */ - -static struct rc_map_table rc5_hauppauge_new[] = { - /* - * Remote Controller Hauppauge Gray found on modern devices - * Keycodes start with address = 0x1e - */ - - { 0x1e3b, KEY_SELECT }, /* GO / house symbol */ - { 0x1e3d, KEY_POWER2 }, /* system power (green button) */ - - { 0x1e1c, KEY_TV }, - { 0x1e18, KEY_VIDEO }, /* Videos */ - { 0x1e19, KEY_AUDIO }, /* Music */ - { 0x1e1a, KEY_CAMERA }, /* Pictures */ - - { 0x1e1b, KEY_EPG }, /* Guide */ - { 0x1e0c, KEY_RADIO }, - - { 0x1e14, KEY_UP }, - { 0x1e15, KEY_DOWN }, - { 0x1e16, KEY_LEFT }, - { 0x1e17, KEY_RIGHT }, - { 0x1e25, KEY_OK }, /* OK */ - - { 0x1e1f, KEY_EXIT }, /* back/exit */ - { 0x1e0d, KEY_MENU }, - - { 0x1e10, KEY_VOLUMEUP }, - { 0x1e11, KEY_VOLUMEDOWN }, - - { 0x1e12, KEY_PREVIOUS }, /* previous channel */ - { 0x1e0f, KEY_MUTE }, - - { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ - { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ - - { 0x1e37, KEY_RECORD }, /* recording */ - { 0x1e36, KEY_STOP }, - - { 0x1e32, KEY_REWIND }, /* backward << */ - { 0x1e35, KEY_PLAY }, - { 0x1e34, KEY_FASTFORWARD }, /* forward >> */ - - { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ - { 0x1e30, KEY_PAUSE }, /* pause */ - { 0x1e1e, KEY_NEXTSONG }, /* skip >| */ - - { 0x1e01, KEY_1 }, - { 0x1e02, KEY_2 }, - { 0x1e03, KEY_3 }, - - { 0x1e04, KEY_4 }, - { 0x1e05, KEY_5 }, - { 0x1e06, KEY_6 }, - - { 0x1e07, KEY_7 }, - { 0x1e08, KEY_8 }, - { 0x1e09, KEY_9 }, - - { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */ - { 0x1e00, KEY_0 }, - { 0x1e0e, KEY_SUBTITLE }, /* also the Pound key (#) */ - - { 0x1e0b, KEY_RED }, /* red button */ - { 0x1e2e, KEY_GREEN }, /* green button */ - { 0x1e38, KEY_YELLOW }, /* yellow key */ - { 0x1e29, KEY_BLUE }, /* blue key */ - - /* - * Old Remote Controller Hauppauge Gray with a golden screen - * Keycodes start with address = 0x1f - */ - { 0x1f3d, KEY_POWER2 }, /* system power (green button) */ - { 0x1f3b, KEY_SELECT }, /* GO */ - - /* Keys 0 to 9 */ - { 0x1f00, KEY_0 }, - { 0x1f01, KEY_1 }, - { 0x1f02, KEY_2 }, - { 0x1f03, KEY_3 }, - { 0x1f04, KEY_4 }, - { 0x1f05, KEY_5 }, - { 0x1f06, KEY_6 }, - { 0x1f07, KEY_7 }, - { 0x1f08, KEY_8 }, - { 0x1f09, KEY_9 }, - - { 0x1f1f, KEY_EXIT }, /* back/exit */ - { 0x1f0d, KEY_MENU }, - - { 0x1f10, KEY_VOLUMEUP }, - { 0x1f11, KEY_VOLUMEDOWN }, - { 0x1f20, KEY_CHANNELUP }, /* channel / program + */ - { 0x1f21, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x1f25, KEY_ENTER }, /* OK */ - - { 0x1f0b, KEY_RED }, /* red button */ - { 0x1f2e, KEY_GREEN }, /* green button */ - { 0x1f38, KEY_YELLOW }, /* yellow key */ - { 0x1f29, KEY_BLUE }, /* blue key */ - - { 0x1f0f, KEY_MUTE }, - { 0x1f0c, KEY_RADIO }, /* There's no indicator on this key */ - { 0x1f3c, KEY_ZOOM }, /* full */ - - { 0x1f32, KEY_REWIND }, /* backward << */ - { 0x1f35, KEY_PLAY }, - { 0x1f34, KEY_FASTFORWARD }, /* forward >> */ - - { 0x1f37, KEY_RECORD }, /* recording */ - { 0x1f36, KEY_STOP }, - { 0x1f30, KEY_PAUSE }, /* pause */ - - { 0x1f24, KEY_PREVIOUSSONG }, /* replay |< */ - { 0x1f1e, KEY_NEXTSONG }, /* skip >| */ - - /* - * Keycodes for DSR-0112 remote bundled with Haupauge MiniStick - * Keycodes start with address = 0x1d - */ - { 0x1d00, KEY_0 }, - { 0x1d01, KEY_1 }, - { 0x1d02, KEY_2 }, - { 0x1d03, KEY_3 }, - { 0x1d04, KEY_4 }, - { 0x1d05, KEY_5 }, - { 0x1d06, KEY_6 }, - { 0x1d07, KEY_7 }, - { 0x1d08, KEY_8 }, - { 0x1d09, KEY_9 }, - { 0x1d0a, KEY_TEXT }, - { 0x1d0d, KEY_MENU }, - { 0x1d0f, KEY_MUTE }, - { 0x1d10, KEY_VOLUMEUP }, - { 0x1d11, KEY_VOLUMEDOWN }, - { 0x1d12, KEY_PREVIOUS }, /* Prev.Ch .. ??? */ - { 0x1d14, KEY_UP }, - { 0x1d15, KEY_DOWN }, - { 0x1d16, KEY_LEFT }, - { 0x1d17, KEY_RIGHT }, - { 0x1d1c, KEY_TV }, - { 0x1d1e, KEY_NEXT }, /* >| */ - { 0x1d1f, KEY_EXIT }, - { 0x1d20, KEY_CHANNELUP }, - { 0x1d21, KEY_CHANNELDOWN }, - { 0x1d24, KEY_LAST }, /* <| */ - { 0x1d25, KEY_OK }, - { 0x1d30, KEY_PAUSE }, - { 0x1d32, KEY_REWIND }, - { 0x1d34, KEY_FASTFORWARD }, - { 0x1d35, KEY_PLAY }, - { 0x1d36, KEY_STOP }, - { 0x1d37, KEY_RECORD }, - { 0x1d3b, KEY_GOTO }, - { 0x1d3d, KEY_POWER }, - { 0x1d3f, KEY_HOME }, - - /* - * Keycodes for the old Black Remote Controller - * This one also uses RC-5 protocol - * Keycodes start with address = 0x00 - */ - { 0x001f, KEY_TV }, - { 0x0020, KEY_CHANNELUP }, - { 0x000c, KEY_RADIO }, - - { 0x0011, KEY_VOLUMEDOWN }, - { 0x002e, KEY_ZOOM }, /* full screen */ - { 0x0010, KEY_VOLUMEUP }, - - { 0x000d, KEY_MUTE }, - { 0x0021, KEY_CHANNELDOWN }, - { 0x0022, KEY_VIDEO }, /* source */ - - { 0x0001, KEY_1 }, - { 0x0002, KEY_2 }, - { 0x0003, KEY_3 }, - - { 0x0004, KEY_4 }, - { 0x0005, KEY_5 }, - { 0x0006, KEY_6 }, - - { 0x0007, KEY_7 }, - { 0x0008, KEY_8 }, - { 0x0009, KEY_9 }, - - { 0x001e, KEY_RED }, /* Reserved */ - { 0x0000, KEY_0 }, - { 0x0026, KEY_SLEEP }, /* Minimize */ -}; - -static struct rc_map_list rc5_hauppauge_new_map = { - .map = { - .scan = rc5_hauppauge_new, - .size = ARRAY_SIZE(rc5_hauppauge_new), - .rc_type = RC_TYPE_RC5, - .name = RC_MAP_RC5_HAUPPAUGE_NEW, - } -}; - -static int __init init_rc_map_rc5_hauppauge_new(void) -{ - return rc_map_register(&rc5_hauppauge_new_map); -} - -static void __exit exit_rc_map_rc5_hauppauge_new(void) -{ - rc_map_unregister(&rc5_hauppauge_new_map); -} - -module_init(init_rc_map_rc5_hauppauge_new) -module_exit(exit_rc_map_rc5_hauppauge_new) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index e4f8eac..eedefce 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -186,7 +186,7 @@ static const struct mceusb_model mceusb_model[] = { * remotes, but we should have something handy, * to allow testing it */ - .rc_map = RC_MAP_RC5_HAUPPAUGE_NEW, + .rc_map = RC_MAP_HAUPPAUGE, .name = "Conexant Hybrid TV (cx231xx) MCE IR", }, [CX_HYBRID_TV] = { diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c index 199b996..e97cafd 100644 --- a/drivers/media/video/cx23885/cx23885-input.c +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -264,7 +264,7 @@ int cx23885_input_init(struct cx23885_dev *dev) driver_type = RC_DRIVER_IR_RAW; allowed_protos = RC_TYPE_ALL; /* The grey Hauppauge RC-5 remote */ - rc_map = RC_MAP_RC5_HAUPPAUGE_NEW; + rc_map = RC_MAP_HAUPPAUGE; break; case CX23885_BOARD_TEVII_S470: /* Integrated CX23885 IR controller */ diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 87f77a3..aa4f45e 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -911,7 +911,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW, + .ir_codes = RC_MAP_HAUPPAUGE, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -2430,7 +2430,7 @@ void em28xx_register_i2c_ir(struct em28xx *dev) dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; break; case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW; + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; dev->init_data.get_key = em28xx_get_key_em_haup; dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; break; diff --git a/include/media/rc-map.h b/include/media/rc-map.h index d843afc..9184751 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -94,7 +94,7 @@ void rc_map_init(void); #define RC_MAP_GADMEI_RM008Z "rc-gadmei-rm008z" #define RC_MAP_GENIUS_TVGO_A11MCE "rc-genius-tvgo-a11mce" #define RC_MAP_GOTVIEW7135 "rc-gotview7135" -#define RC_MAP_HAUPPAUGE_NEW "rc-hauppauge-new" +#define RC_MAP_HAUPPAUGE_NEW "rc-hauppauge" #define RC_MAP_IMON_MCE "rc-imon-mce" #define RC_MAP_IMON_PAD "rc-imon-pad" #define RC_MAP_IODATA_BCTV7E "rc-iodata-bctv7e" @@ -125,7 +125,7 @@ void rc_map_init(void); #define RC_MAP_PROTEUS_2309 "rc-proteus-2309" #define RC_MAP_PURPLETV "rc-purpletv" #define RC_MAP_PV951 "rc-pv951" -#define RC_MAP_RC5_HAUPPAUGE_NEW "rc-rc5-hauppauge-new" +#define RC_MAP_HAUPPAUGE "rc-hauppauge" #define RC_MAP_RC5_TV "rc-rc5-tv" #define RC_MAP_RC6_MCE "rc-rc6-mce" #define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys" -- cgit v0.10.2 From af86ce79f020a31e4a30661e41471d31face9985 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 12:18:48 -0300 Subject: [media] remove the old RC_MAP_HAUPPAUGE_NEW RC map The rc-hauppauge-new map is a messy thing, as it bundles 3 different remote controllers as if they were just one, discarding the address byte. Also, some key maps are wrong. With the conversion to the new rc-core, it is likely that most of the devices won't be working properly, as the i2c driver and the raw decoders are now providing 16 bits for the remote, instead of just 8. delete mode 100644 drivers/media/rc/keymaps/rc-hauppauge-new.c Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index b82756d..1d79ada 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -26,7 +26,7 @@ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html * * - * the project's page is at http://www.linuxtv.org/ + * the project's page is at http://www.linuxtv.org/ */ #include @@ -102,6 +102,7 @@ struct budget_ci_ir { int rc5_device; u32 ir_key; bool have_command; + bool full_rc5; /* Outputs a full RC5 code */ }; struct budget_ci { @@ -154,11 +155,18 @@ static void msp430_ir_interrupt(unsigned long data) return; budget_ci->ir.have_command = false; - /* FIXME: We should generate complete scancodes with device info */ if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != (command & 0x1f)) return; + if (budget_ci->ir.full_rc5) { + rc_keydown(dev, + budget_ci->ir.rc5_device <<8 | budget_ci->ir.ir_key, + (command & 0x20) ? 1 : 0); + return; + } + + /* FIXME: We should generate complete scancodes for all devices */ rc_keydown(dev, budget_ci->ir.ir_key, (command & 0x20) ? 1 : 0); } @@ -206,7 +214,8 @@ static int msp430_ir_init(struct budget_ci *budget_ci) case 0x1011: case 0x1012: /* The hauppauge keymap is a superset of these remotes */ - dev->map_name = RC_MAP_HAUPPAUGE_NEW; + dev->map_name = RC_MAP_HAUPPAUGE; + budget_ci->ir.full_rc5 = true; if (rc5_device < 0) budget_ci->ir.rc5_device = 0x1f; diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 1f446f9..2fb566b 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -37,7 +37,6 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-gadmei-rm008z.o \ rc-genius-tvgo-a11mce.o \ rc-gotview7135.o \ - rc-hauppauge-new.o \ rc-imon-mce.o \ rc-imon-pad.o \ rc-iodata-bctv7e.o \ diff --git a/drivers/media/rc/keymaps/rc-hauppauge-new.c b/drivers/media/rc/keymaps/rc-hauppauge-new.c deleted file mode 100644 index 44f3283..0000000 --- a/drivers/media/rc/keymaps/rc-hauppauge-new.c +++ /dev/null @@ -1,100 +0,0 @@ -/* hauppauge-new.h - Keytable for hauppauge_new Remote Controller - * - * keymap imported from ir-keymaps.c - * - * Copyright (c) 2010 by Mauro Carvalho Chehab - * - * 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 - -/* Hauppauge: the newer, gray remotes (seems there are multiple - * slightly different versions), shipped with cx88+ivtv cards. - * almost rc5 coding, but some non-standard keys */ - -static struct rc_map_table hauppauge_new[] = { - /* Keys 0 to 9 */ - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x0a, KEY_TEXT }, /* keypad asterisk as well */ - { 0x0b, KEY_RED }, /* red button */ - { 0x0c, KEY_RADIO }, - { 0x0d, KEY_MENU }, - { 0x0e, KEY_SUBTITLE }, /* also the # key */ - { 0x0f, KEY_MUTE }, - { 0x10, KEY_VOLUMEUP }, - { 0x11, KEY_VOLUMEDOWN }, - { 0x12, KEY_PREVIOUS }, /* previous channel */ - { 0x14, KEY_UP }, - { 0x15, KEY_DOWN }, - { 0x16, KEY_LEFT }, - { 0x17, KEY_RIGHT }, - { 0x18, KEY_VCR }, /* Videos */ - { 0x19, KEY_AUDIO }, /* Music */ - /* 0x1a: Pictures - presume this means - "Multimedia Home Platform" - - no "PICTURES" key in input.h - */ - { 0x1a, KEY_CAMERA }, - - { 0x1b, KEY_EPG }, /* Guide */ - { 0x1c, KEY_TV }, - { 0x1e, KEY_NEXTSONG }, /* skip >| */ - { 0x1f, KEY_EXIT }, /* back/exit */ - { 0x20, KEY_CHANNELUP }, /* channel / program + */ - { 0x21, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x22, KEY_VIDEO }, /* source (old black remote) */ - { 0x24, KEY_PREVIOUSSONG }, /* replay |< */ - { 0x25, KEY_ENTER }, /* OK */ - { 0x26, KEY_SLEEP }, /* minimize (old black remote) */ - { 0x29, KEY_BLUE }, /* blue key */ - { 0x2e, KEY_GREEN }, /* green button */ - { 0x30, KEY_PAUSE }, /* pause */ - { 0x32, KEY_REWIND }, /* backward << */ - { 0x34, KEY_FASTFORWARD }, /* forward >> */ - { 0x35, KEY_PLAY }, - { 0x36, KEY_STOP }, - { 0x37, KEY_RECORD }, /* recording */ - { 0x38, KEY_YELLOW }, /* yellow key */ - { 0x3b, KEY_SELECT }, /* top right button */ - { 0x3c, KEY_ZOOM }, /* full */ - { 0x3d, KEY_POWER }, /* system power (green button) */ -}; - -static struct rc_map_list hauppauge_new_map = { - .map = { - .scan = hauppauge_new, - .size = ARRAY_SIZE(hauppauge_new), - .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */ - .name = RC_MAP_HAUPPAUGE_NEW, - } -}; - -static int __init init_rc_map_hauppauge_new(void) -{ - return rc_map_register(&hauppauge_new_map); -} - -static void __exit exit_rc_map_hauppauge_new(void) -{ - rc_map_unregister(&hauppauge_new_map); -} - -module_init(init_rc_map_hauppauge_new) -module_exit(exit_rc_map_hauppauge_new) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index c330fb9..040aaa8 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -96,7 +96,7 @@ static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case CX18_HW_Z8F0811_IR_RX_HAUP: - init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; + init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = RC_TYPE_RC5; init_data->name = cx->card_name; diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index fbfbba5..c820e2f 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -283,7 +283,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_PCHDTV_HD3000: case CX88_BOARD_PCHDTV_HD5500: case CX88_BOARD_HAUPPAUGE_IRONLY: - ir_codes = RC_MAP_HAUPPAUGE_NEW; + ir_codes = RC_MAP_HAUPPAUGE; ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: @@ -604,7 +604,7 @@ void cx88_i2c_init_ir(struct cx88_core *core) if (*addrp == 0x71) { /* Hauppauge XVR */ core->init_data.name = "cx88 Hauppauge XVR remote"; - core->init_data.ir_codes = RC_MAP_HAUPPAUGE_NEW; + core->init_data.ir_codes = RC_MAP_HAUPPAUGE; core->init_data.type = RC_TYPE_RC5; core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index aa4f45e..69fcea8 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -834,7 +834,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = RC_MAP_HAUPPAUGE_NEW, + .ir_codes = RC_MAP_HAUPPAUGE, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -859,7 +859,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, - .ir_codes = RC_MAP_HAUPPAUGE_NEW, + .ir_codes = RC_MAP_HAUPPAUGE, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -885,7 +885,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = RC_MAP_HAUPPAUGE_NEW, + .ir_codes = RC_MAP_HAUPPAUGE, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c index 3e06587..de69bae 100644 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -52,7 +52,7 @@ struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev) }; /* Our default information for ir-kbd-i2c.c to use */ - init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; + init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = RC_TYPE_RC5; init_data->name = "HD-PVR"; diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index a221ad6..be7064f 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -313,7 +313,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->get_key = get_key_haup; rc_type = RC_TYPE_RC5; if (hauppauge == 1) { - ir_codes = RC_MAP_HAUPPAUGE_NEW; + ir_codes = RC_MAP_HAUPPAUGE; } else { ir_codes = RC_MAP_RC5_TV; } @@ -340,7 +340,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) name = "Hauppauge/Zilog Z8"; ir->get_key = get_key_haup_xvr; rc_type = RC_TYPE_RC5; - ir_codes = hauppauge ? RC_MAP_HAUPPAUGE_NEW : RC_MAP_RC5_TV; + ir_codes = hauppauge ? RC_MAP_HAUPPAUGE : RC_MAP_RC5_TV; break; } diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index 9fb86a0..3c89d6f 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -213,7 +213,7 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) break; case IVTV_HW_Z8F0811_IR_RX_HAUP: /* Default to grey remote */ - init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; + init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = RC_TYPE_RC5; init_data->name = itv->card_name; diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 451ecd4..e72d510 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -578,7 +578,7 @@ static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) switch (hdw->ir_scheme_active) { case PVR2_IR_SCHEME_24XXX: /* FX2-controlled IR */ case PVR2_IR_SCHEME_29XXX: /* Original 29xxx device */ - init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; + init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; init_data->type = RC_TYPE_RC5; init_data->name = hdw->hdw_desc->description; @@ -593,7 +593,7 @@ static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) break; case PVR2_IR_SCHEME_ZILOG: /* HVR-1950 style */ case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */ - init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; + init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = RC_TYPE_RC5; init_data->name = hdw->hdw_desc->description; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 790d667..be1c2a2 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -893,7 +893,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_HAUPPAUGE_HVR1110: dev->init_data.name = "HVR 1110"; dev->init_data.get_key = get_key_hvr1110; - dev->init_data.ir_codes = RC_MAP_HAUPPAUGE_NEW; + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; info.addr = 0x71; break; case SAA7134_BOARD_BEHOLD_607FM_MK3: -- cgit v0.10.2 From 206241069ecfa52c3b8873f8d7e31d434d2fcae1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Jan 2011 22:23:08 -0300 Subject: [media] rc/keymaps: Remove the obsolete rc-rc5-tv keymap This keymap were used for the Hauppauge Black remote controller only. It also contains some keycodes not found there. As the Hauppauge Black is now part of the hauppauge keymap, just remove it. Also, remove the modprobe hacks to select between the Gray and the Black versions of the remote controller as: - Both are supported by default by the keymap; - If the user just wants one keyboard supported, it is just a matter of changing the keymap via the userspace tool (ir-keytable), removing the keys that he doesn't desire. As ir-keytable auto-loads the keys via udev, this is better than obscure modprobe parameters. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jarod Wilson diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 2fb566b..85cac7d 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -68,7 +68,6 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-purpletv.o \ rc-pv951.o \ rc-hauppauge.o \ - rc-rc5-tv.o \ rc-rc6-mce.o \ rc-real-audio-220-32-keys.o \ rc-streamzap.o \ diff --git a/drivers/media/rc/keymaps/rc-rc5-tv.c b/drivers/media/rc/keymaps/rc-rc5-tv.c deleted file mode 100644 index 4fcef9f..0000000 --- a/drivers/media/rc/keymaps/rc-rc5-tv.c +++ /dev/null @@ -1,81 +0,0 @@ -/* rc5-tv.h - Keytable for rc5_tv Remote Controller - * - * keymap imported from ir-keymaps.c - * - * Copyright (c) 2010 by Mauro Carvalho Chehab - * - * 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 - -/* generic RC5 keytable */ -/* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */ -/* used by old (black) Hauppauge remotes */ - -static struct rc_map_table rc5_tv[] = { - /* Keys 0 to 9 */ - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x0b, KEY_CHANNEL }, /* channel / program (japan: 11) */ - { 0x0c, KEY_POWER }, /* standby */ - { 0x0d, KEY_MUTE }, /* mute / demute */ - { 0x0f, KEY_TV }, /* display */ - { 0x10, KEY_VOLUMEUP }, - { 0x11, KEY_VOLUMEDOWN }, - { 0x12, KEY_BRIGHTNESSUP }, - { 0x13, KEY_BRIGHTNESSDOWN }, - { 0x1e, KEY_SEARCH }, /* search + */ - { 0x20, KEY_CHANNELUP }, /* channel / program + */ - { 0x21, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x22, KEY_CHANNEL }, /* alt / channel */ - { 0x23, KEY_LANGUAGE }, /* 1st / 2nd language */ - { 0x26, KEY_SLEEP }, /* sleeptimer */ - { 0x2e, KEY_MENU }, /* 2nd controls (USA: menu) */ - { 0x30, KEY_PAUSE }, - { 0x32, KEY_REWIND }, - { 0x33, KEY_GOTO }, - { 0x35, KEY_PLAY }, - { 0x36, KEY_STOP }, - { 0x37, KEY_RECORD }, /* recording */ - { 0x3c, KEY_TEXT }, /* teletext submode (Japan: 12) */ - { 0x3d, KEY_SUSPEND }, /* system standby */ - -}; - -static struct rc_map_list rc5_tv_map = { - .map = { - .scan = rc5_tv, - .size = ARRAY_SIZE(rc5_tv), - .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */ - .name = RC_MAP_RC5_TV, - } -}; - -static int __init init_rc_map_rc5_tv(void) -{ - return rc_map_register(&rc5_tv_map); -} - -static void __exit exit_rc_map_rc5_tv(void) -{ - rc_map_unregister(&rc5_tv_map); -} - -module_init(init_rc_map_rc5_tv) -module_exit(exit_rc_map_rc5_tv) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index be7064f..672935f 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -55,10 +55,6 @@ static int debug; module_param(debug, int, 0644); /* debug level (0,1,2) */ -static int hauppauge; -module_param(hauppauge, int, 0644); /* Choose Hauppauge remote */ -MODULE_PARM_DESC(hauppauge, "Specify Hauppauge remote: 0=black, 1=grey (defaults to 0)"); - #define MODULE_NAME "ir-kbd-i2c" #define dprintk(level, fmt, arg...) if (debug >= level) \ @@ -105,10 +101,6 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, /* invalid key press */ return 0; - if (dev!=0x1e && dev!=0x1f) - /* not a hauppauge remote */ - return 0; - if (!range) code += 64; @@ -312,11 +304,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) name = "Hauppauge"; ir->get_key = get_key_haup; rc_type = RC_TYPE_RC5; - if (hauppauge == 1) { - ir_codes = RC_MAP_HAUPPAUGE; - } else { - ir_codes = RC_MAP_RC5_TV; - } + ir_codes = RC_MAP_HAUPPAUGE; break; case 0x30: name = "KNC One"; @@ -340,7 +328,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) name = "Hauppauge/Zilog Z8"; ir->get_key = get_key_haup_xvr; rc_type = RC_TYPE_RC5; - ir_codes = hauppauge ? RC_MAP_HAUPPAUGE : RC_MAP_RC5_TV; + ir_codes = RC_MAP_HAUPPAUGE; break; } diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index 3c89d6f..d47f41a 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -205,8 +205,7 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) break; case IVTV_HW_I2C_IR_RX_HAUP_EXT: case IVTV_HW_I2C_IR_RX_HAUP_INT: - /* Default to old black remote */ - init_data->ir_codes = RC_MAP_RC5_TV; + init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; init_data->type = RC_TYPE_RC5; init_data->name = itv->card_name; -- cgit v0.10.2 From 6e5b960176980852187b6f0bafa3db78130be42f Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 4 Mar 2011 17:30:17 -0300 Subject: [media] ir-kbd-i2c: pass device code w/key in hauppauge case The new hauppauge key tables use both device code button code. Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 672935f..3ab875d 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -108,7 +108,7 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, start, range, toggle, dev, code); /* return key */ - *ir_key = code; + *ir_key = (dev << 8) | code; *ir_raw = ircode; return 1; } -- cgit v0.10.2 From dc8e2aa3b5a6c13baa77709bcaa2e7e483d9d006 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 4 Mar 2011 17:31:11 -0300 Subject: [media] hdpvr: use same polling interval as other OS The hdpvr's IR part, in short, sucks. As observed with a usb traffic sniffer, the Windows software for it uses a polling interval of 405ms. Its still not behaving as well as I'd like even with this change, but this inches us closer and closer to that point... Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c index de69bae..2a1ac28 100644 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -56,6 +56,7 @@ struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev) init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = RC_TYPE_RC5; init_data->name = "HD-PVR"; + init_data->polling_interval = 405; /* ms, duplicated from Windows */ hdpvr_ir_rx_i2c_board_info.platform_data = init_data; return i2c_new_device(&dev->i2c_adapter, &hdpvr_ir_rx_i2c_board_info); @@ -191,7 +192,7 @@ static struct i2c_adapter hdpvr_i2c_adapter_template = { static int hdpvr_activate_ir(struct hdpvr_device *dev) { - char buffer[8]; + char buffer[2]; mutex_lock(&dev->i2c_mutex); -- cgit v0.10.2 From 55734785cdaff596be9a5238af54fc0f4ace2e63 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 4 Mar 2011 17:57:24 -0300 Subject: [media] lirc: silence some compile warnings Both lirc_imon and lirc_sasem were causing gcc to complain about the possible use of uninitialized variables. Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_imon.c b/drivers/staging/lirc/lirc_imon.c index 235cab0..4039eda 100644 --- a/drivers/staging/lirc/lirc_imon.c +++ b/drivers/staging/lirc/lirc_imon.c @@ -379,7 +379,7 @@ static ssize_t vfd_write(struct file *file, const char *buf, struct imon_context *context; const unsigned char vfd_packet6[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; - int *data_buf; + int *data_buf = NULL; context = file->private_data; if (!context) { diff --git a/drivers/staging/lirc/lirc_sasem.c b/drivers/staging/lirc/lirc_sasem.c index 925eabe..63a438d 100644 --- a/drivers/staging/lirc/lirc_sasem.c +++ b/drivers/staging/lirc/lirc_sasem.c @@ -364,7 +364,7 @@ static ssize_t vfd_write(struct file *file, const char *buf, int i; int retval = 0; struct sasem_context *context; - int *data_buf; + int *data_buf = NULL; context = (struct sasem_context *) file->private_data; if (!context) { -- cgit v0.10.2 From cc664ae026f1b0f3254564a7d45cc29e735c7fed Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 4 Mar 2011 19:53:05 -0300 Subject: [media] lirc_zilog: error out if buffer read bytes != chunk size Give it a few tries, then exit. Prevents a possible endless loop situation. Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 407d4b4..dd6a57c 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -897,7 +897,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) struct IR *ir = filep->private_data; struct IR_rx *rx; struct lirc_buffer *rbuf = ir->l.rbuf; - int ret = 0, written = 0; + int ret = 0, written = 0, retries = 0; unsigned int m; DECLARE_WAITQUEUE(wait, current); @@ -950,6 +950,12 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) ret = copy_to_user((void *)outbuf+written, buf, rbuf->chunk_size); written += rbuf->chunk_size; + } else { + retries++; + } + if (retries >= 5) { + zilog_error("Buffer read failed!\n"); + ret = -EIO; } } } -- cgit v0.10.2 From 7d9a46f9d5e0bea8e862143be73df2bbc9acb2a3 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 4 Mar 2011 20:20:47 -0300 Subject: [media] mceusb: topseed 0x0011 needs gen3 init for tx to work Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index eedefce..044fb7a 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -261,7 +261,7 @@ static struct usb_device_id mceusb_dev_table[] = { .driver_info = MCE_GEN2_TX_INV }, /* Topseed eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_TOPSEED, 0x0011), - .driver_info = MCE_GEN2_TX_INV }, + .driver_info = MCE_GEN3 }, /* Ricavision internal Infrared Transceiver */ { USB_DEVICE(VENDOR_RICAVISION, 0x0010) }, /* Itron ione Libra Q-11 */ -- cgit v0.10.2 From 4be22b6a7f2f2b7eb6f7aab8902068a367cda8ba Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Tue, 22 Mar 2011 17:23:15 -0300 Subject: [media] rc: interim support for 32-bit NEC-ish scancodes The Apple and TiVo remotes I've got use an NEC-ish protocol, but rather than a command/not_command pair, they have what appear to be vendor ID bytes. This change makes the NEC decoder warn if the command/not_command checksum fails, but then passes along a full 32-bit scancode for keymap lookup. This change should make no difference for existing keymaps, since they simply won't have 32-bit scancodes, but allows for a 32-bit keymap. At the moment, that'll have to be uploaded by the user, but I've got Apple and TiVo remote keymaps forthcoming. In the long run (2.6.40, hopefully), we should probably just always use all 32 bits for all NEC keymaps, but this should get us by for 2.6.39. (Note that a few of the TiVo keys actuallly *do* pass the command checksum, so for now, the keymap for this remote will have to be a mix of 24-bit and 32-bit scancodes, but so be it). Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index 7b58b4a..63ee722 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -49,6 +49,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) struct nec_dec *data = &dev->raw->nec; u32 scancode; u8 address, not_address, command, not_command; + bool send_32bits = false; if (!(dev->raw->enabled_protocols & RC_TYPE_NEC)) return 0; @@ -164,10 +165,15 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) if ((command ^ not_command) != 0xff) { IR_dprintk(1, "NEC checksum error: received 0x%08x\n", data->bits); - break; + send_32bits = true; } - if ((address ^ not_address) != 0xff) { + if (send_32bits) { + /* NEC transport, but modified protocol, used by at + * least Apple and TiVo remotes */ + scancode = data->bits; + IR_dprintk(1, "NEC (modified) scancode 0x%08x\n", scancode); + } else if ((address ^ not_address) != 0xff) { /* Extended NEC */ scancode = address << 16 | not_address << 8 | -- cgit v0.10.2 From 0e4c180d3e2cc11e248f29d4c604b6194739d05a Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Mon, 7 Mar 2011 21:49:48 -0300 Subject: [media] v4l: soc-camera: Store negotiated buffer settings This fixes the problem in which a host driver sets a personalized sizeimage or bytesperline field, and gets ignored when doing G_FMT. Signed-off-by: Sergio Aguirre Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index fa80a4a..07525e7 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -389,6 +389,8 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, icd->user_width = pix->width; icd->user_height = pix->height; + icd->bytesperline = pix->bytesperline; + icd->sizeimage = pix->sizeimage; icd->colorspace = pix->colorspace; icd->field = pix->field; if (ici->ops->init_videobuf) @@ -661,14 +663,11 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, pix->width = icd->user_width; pix->height = icd->user_height; + pix->bytesperline = icd->bytesperline; + pix->sizeimage = icd->sizeimage; pix->field = icd->field; pix->pixelformat = icd->current_fmt->host_fmt->fourcc; - pix->bytesperline = soc_mbus_bytes_per_line(pix->width, - icd->current_fmt->host_fmt); pix->colorspace = icd->colorspace; - if (pix->bytesperline < 0) - return pix->bytesperline; - pix->sizeimage = pix->height * pix->bytesperline; dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n", icd->current_fmt->host_fmt->fourcc); return 0; diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 6e96b26..f80b537 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -30,6 +30,8 @@ struct soc_camera_device { struct device *pdev; /* Platform device */ s32 user_width; s32 user_height; + u32 bytesperline; /* for padding, zero if unused */ + u32 sizeimage; enum v4l2_colorspace colorspace; unsigned char iface; /* Host number */ unsigned char devnum; /* Device number per host */ -- cgit v0.10.2 From d366d4a08a4e5e1fddc2cc820302be8e7a8143d4 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 21 Mar 2011 06:33:25 -0300 Subject: [media] V4L: soc-camera: explicitly require V4L2_BUF_TYPE_VIDEO_CAPTURE The soc-camera core accesses the "pix" member of the struct v4l2_format::fmt union, which is only valid for V4L2_BUF_TYPE_VIDEO_CAPTURE streams. This patch adds explicit checks for this to {g,s,try}_fmt methods. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 07525e7..4628448 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -144,6 +144,10 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, WARN_ON(priv != file->private_data); + /* Only single-plane capture is supported so far */ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + /* limit format to hardware capabilities */ return ici->ops->try_fmt(icd, f); } @@ -396,10 +400,6 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, if (ici->ops->init_videobuf) icd->vb_vidq.field = pix->field; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", - f->type); - dev_dbg(&icd->dev, "set width: %d height: %d\n", icd->user_width, icd->user_height); @@ -618,6 +618,11 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, WARN_ON(priv != file->private_data); + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_warn(&icd->dev, "Wrong buf-type %d\n", f->type); + return -EINVAL; + } + if (icd->streamer && icd->streamer != file) return -EBUSY; @@ -661,6 +666,9 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, WARN_ON(priv != file->private_data); + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + pix->width = icd->user_width; pix->height = icd->user_height; pix->bytesperline = icd->bytesperline; -- cgit v0.10.2 From d6db5bfe7fd6d4334a1460923ba8175f1a0ece2d Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Mon, 14 Mar 2011 10:38:23 -0300 Subject: [media] sh_mobile_ceu_camera: Do not call vb2's mem_ops directly Use vb2_dma_contig_plane_paddr to retrieve a physical address for a plane instead of calling an internal mem_ops callback. Signed-off-by: Pawel Osciak Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 61f3701..3fe54bf 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -302,9 +302,7 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) bottom2 = CDBCR; } - /* mem_ops->cookie must not be NULL */ - phys_addr_top = (dma_addr_t)icd->vb2_vidq.mem_ops->cookie(pcdev-> - active->planes[0].mem_priv); + phys_addr_top = vb2_dma_contig_plane_paddr(pcdev->active, 0); ceu_write(pcdev, top1, phys_addr_top); if (V4L2_FIELD_NONE != pcdev->field) { -- cgit v0.10.2 From 472af2b05bdefcaee7e754e22cbf131110017ad6 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Mon, 14 Mar 2011 10:38:24 -0300 Subject: [media] videobuf2-dma-contig: make cookie() return a pointer to dma_addr_t dma_addr_t may not fit into void* on some architectures. To be safe, make vb2_dma_contig_cookie() return a pointer to dma_addr_t and dereference it in vb2_dma_contig_plane_paddr() back to dma_addr_t. Signed-off-by: Pawel Osciak Reported-by: Hans Verkuil Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index 90495b7..58205d5 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -78,7 +78,7 @@ static void *vb2_dma_contig_cookie(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; - return (void *)buf->paddr; + return &buf->paddr; } static void *vb2_dma_contig_vaddr(void *buf_priv) diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h index 1d6188d..7e6c68b 100644 --- a/include/media/videobuf2-dma-contig.h +++ b/include/media/videobuf2-dma-contig.h @@ -14,11 +14,14 @@ #define _MEDIA_VIDEOBUF2_DMA_COHERENT_H #include +#include -static inline unsigned long vb2_dma_contig_plane_paddr( - struct vb2_buffer *vb, unsigned int plane_no) +static inline dma_addr_t +vb2_dma_contig_plane_paddr(struct vb2_buffer *vb, unsigned int plane_no) { - return (unsigned long)vb2_plane_cookie(vb, plane_no); + dma_addr_t *paddr = vb2_plane_cookie(vb, plane_no); + + return *paddr; } void *vb2_dma_contig_init_ctx(struct device *dev); -- cgit v0.10.2