diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-02-09 19:08:43 -0200 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-02-09 19:08:43 -0200 |
commit | 803b1cde37f44f2d09bb97bef92d74ead3279578 (patch) | |
tree | 19bca69318c3ca400d01349582322fcf1d5da1e4 /linux | |
parent | 41579b5b51a952f7bfaf05f5147f83148a7a3a68 (diff) | |
parent | 97ff0b0275491dc62dbee36a7e53c4c3455efa8e (diff) | |
download | mediapointer-dvb-s2-803b1cde37f44f2d09bb97bef92d74ead3279578.tar.gz mediapointer-dvb-s2-803b1cde37f44f2d09bb97bef92d74ead3279578.tar.bz2 |
merge: http://linuxtv.org/hg/~dougsland/radios
From: Mauro Carvalho Chehab <mchehab@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'linux')
22 files changed, 1303 insertions, 324 deletions
diff --git a/linux/drivers/media/common/tuners/Kconfig b/linux/drivers/media/common/tuners/Kconfig index 6f92beaa5..7969b695c 100644 --- a/linux/drivers/media/common/tuners/Kconfig +++ b/linux/drivers/media/common/tuners/Kconfig @@ -29,6 +29,7 @@ config MEDIA_TUNER select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMIZE menuconfig MEDIA_TUNER_CUSTOMIZE bool "Customize analog and hybrid tuner modules to build" @@ -164,4 +165,11 @@ config MEDIA_TUNER_MXL5007T help A driver for the silicon tuner MxL5007T from MaxLinear. +config MEDIA_TUNER_MC44S803 + tristate "Freescale MC44S803 Low Power CMOS Broadband tuners" + depends on VIDEO_MEDIA && I2C + default m if DVB_FE_CUSTOMISE + help + Say Y here to support the Freescale MC44S803 based tuners + endif # MEDIA_TUNER_CUSTOMIZE diff --git a/linux/drivers/media/common/tuners/Makefile b/linux/drivers/media/common/tuners/Makefile index 4dfbe5b82..4132b2be7 100644 --- a/linux/drivers/media/common/tuners/Makefile +++ b/linux/drivers/media/common/tuners/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o +obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/linux/drivers/media/common/tuners/mc44s803.c b/linux/drivers/media/common/tuners/mc44s803.c new file mode 100644 index 000000000..20c4485ce --- /dev/null +++ b/linux/drivers/media/common/tuners/mc44s803.c @@ -0,0 +1,371 @@ +/* + * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner + * + * Copyright (c) 2009 Jochen Friedrich <jochen@scram.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.= + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +#include "dvb_frontend.h" + +#include "mc44s803.h" +#include "mc44s803_priv.h" + +#define mc_printk(level, format, arg...) \ + printk(level "mc44s803: " format , ## arg) + +/* Writes a single register */ +static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val) +{ + u8 buf[3]; + struct i2c_msg msg = { + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3 + }; + + buf[0] = (val & 0xff0000) >> 16; + buf[1] = (val & 0xff00) >> 8; + buf[2] = (val & 0xff); + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + mc_printk(KERN_WARNING, "I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +/* Reads a single register */ +static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val) +{ + u32 wval; + u8 buf[3]; + int ret; + struct i2c_msg msg[] = { + { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, + .buf = buf, .len = 3 }, + }; + + wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) | + MC44S803_REG_SM(reg, MC44S803_D); + + ret = mc44s803_writereg(priv, wval); + if (ret) + return ret; + + if (i2c_transfer(priv->i2c, msg, 1) != 1) { + mc_printk(KERN_WARNING, "I2C read failed\n"); + return -EREMOTEIO; + } + + *val = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + + return 0; +} + +static int mc44s803_release(struct dvb_frontend *fe) +{ + struct mc44s803_priv *priv = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(priv); + + return 0; +} + +static int mc44s803_init(struct dvb_frontend *fe) +{ + struct mc44s803_priv *priv = fe->tuner_priv; + u32 val; + int err; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + +/* Reset chip */ + val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_RS); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + +/* Power Up and Start Osc */ + + val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | + MC44S803_REG_SM(0xC0, MC44S803_REFOSC) | + MC44S803_REG_SM(1, MC44S803_OSCSEL); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) | + MC44S803_REG_SM(0x200, MC44S803_POWER); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + msleep(10); + + val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | + MC44S803_REG_SM(0x40, MC44S803_REFOSC) | + MC44S803_REG_SM(1, MC44S803_OSCSEL); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + msleep(20); + +/* Setup Mixer */ + + val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_TRI_STATE) | + MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + +/* Setup Cirquit Adjust */ + + val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_G1) | + MC44S803_REG_SM(1, MC44S803_G3) | + MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | + MC44S803_REG_SM(1, MC44S803_G6) | + MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | + MC44S803_REG_SM(0x3, MC44S803_LP) | + MC44S803_REG_SM(1, MC44S803_CLRF) | + MC44S803_REG_SM(1, MC44S803_CLIF); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_G1) | + MC44S803_REG_SM(1, MC44S803_G3) | + MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | + MC44S803_REG_SM(1, MC44S803_G6) | + MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | + MC44S803_REG_SM(0x3, MC44S803_LP); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + +/* Setup Digtune */ + + val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | + MC44S803_REG_SM(3, MC44S803_XOD); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + +/* Setup AGC */ + + val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_AT1) | + MC44S803_REG_SM(1, MC44S803_AT2) | + MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) | + MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) | + MC44S803_REG_SM(1, MC44S803_LNA0); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + return 0; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + mc_printk(KERN_WARNING, "I/O Error\n"); + return err; +} + +static int mc44s803_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct mc44s803_priv *priv = fe->tuner_priv; + u32 r1, r2, n1, n2, lo1, lo2, freq, val; + int err; + + priv->frequency = params->frequency; + + r1 = MC44S803_OSC / 1000000; + r2 = MC44S803_OSC / 100000; + + n1 = (params->frequency + MC44S803_IF1 + 500000) / 1000000; + freq = MC44S803_OSC / r1 * n1; + lo1 = ((60 * n1) + (r1 / 2)) / r1; + freq = freq - params->frequency; + + n2 = (freq - MC44S803_IF2 + 50000) / 100000; + lo2 = ((60 * n2) + (r2 / 2)) / r2; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) | + MC44S803_REG_SM(r1-1, MC44S803_R1) | + MC44S803_REG_SM(r2-1, MC44S803_R2) | + MC44S803_REG_SM(1, MC44S803_REFBUF_EN); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) | + MC44S803_REG_SM(n1-2, MC44S803_LO1); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) | + MC44S803_REG_SM(n2-2, MC44S803_LO2); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_DA) | + MC44S803_REG_SM(lo1, MC44S803_LO_REF) | + MC44S803_REG_SM(1, MC44S803_AT); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | + MC44S803_REG_SM(2, MC44S803_DA) | + MC44S803_REG_SM(lo2, MC44S803_LO_REF) | + MC44S803_REG_SM(1, MC44S803_AT); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + mc_printk(KERN_WARNING, "I/O Error\n"); + return err; +} + +static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mc44s803_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static const struct dvb_tuner_ops mc44s803_tuner_ops = { + .info = { + .name = "Freescale MC44S803", + .frequency_min = 48000000, + .frequency_max = 1000000000, + .frequency_step = 100000, + }, + + .release = mc44s803_release, + .init = mc44s803_init, + .set_params = mc44s803_set_params, + .get_frequency = mc44s803_get_frequency +}; + +/* This functions tries to identify a MC44S803 tuner by reading the ID + register. This is hasty. */ +struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct mc44s803_config *cfg) +{ + struct mc44s803_priv *priv; + u32 reg; + u8 id; + int ret; + + reg = 0; + + priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + priv->fe = fe; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + ret = mc44s803_readreg(priv, MC44S803_REG_ID, ®); + if (ret) + goto error; + + id = MC44S803_REG_MS(reg, MC44S803_ID); + + if (id != 0x14) { + mc_printk(KERN_ERR, "unsupported ID " + "(%x should be 0x14)\n", id); + goto error; + } + + mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id); + memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return fe; + +error: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + kfree(priv); + return NULL; +} +EXPORT_SYMBOL(mc44s803_attach); + +MODULE_AUTHOR("Jochen Friedrich"); +MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/common/tuners/mc44s803.h b/linux/drivers/media/common/tuners/mc44s803.h new file mode 100644 index 000000000..34f3892d3 --- /dev/null +++ b/linux/drivers/media/common/tuners/mc44s803.h @@ -0,0 +1,46 @@ +/* + * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner + * + * Copyright (c) 2009 Jochen Friedrich <jochen@scram.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.= + */ + +#ifndef MC44S803_H +#define MC44S803_H + +struct dvb_frontend; +struct i2c_adapter; + +struct mc44s803_config { + u8 i2c_address; + u8 dig_out; +}; + +#if defined(CONFIG_MEDIA_TUNER_MC44S803) || \ + (defined(CONFIG_MEDIA_TUNER_MC44S803_MODULE) && defined(MODULE)) +extern struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct mc44s803_config *cfg); +#else +static inline struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct mc44s803_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_MEDIA_TUNER_MC44S803 */ + +#endif diff --git a/linux/drivers/media/common/tuners/mc44s803_priv.h b/linux/drivers/media/common/tuners/mc44s803_priv.h new file mode 100644 index 000000000..14a927809 --- /dev/null +++ b/linux/drivers/media/common/tuners/mc44s803_priv.h @@ -0,0 +1,208 @@ +/* + * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner + * + * Copyright (c) 2009 Jochen Friedrich <jochen@scram.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.= + */ + +#ifndef MC44S803_PRIV_H +#define MC44S803_PRIV_H + +/* This driver is based on the information available in the datasheet + http://www.freescale.com/files/rf_if/doc/data_sheet/MC44S803.pdf + + SPI or I2C Address : 0xc0-0xc6 + + Reg.No | Function + ------------------------------------------- + 00 | Power Down + 01 | Reference Oszillator + 02 | Reference Dividers + 03 | Mixer and Reference Buffer + 04 | Reset/Serial Out + 05 | LO 1 + 06 | LO 2 + 07 | Circuit Adjust + 08 | Test + 09 | Digital Tune + 0A | LNA AGC + 0B | Data Register Address + 0C | Regulator Test + 0D | VCO Test + 0E | LNA Gain/Input Power + 0F | ID Bits + +*/ + +#define MC44S803_OSC 26000000 /* 26 MHz */ +#define MC44S803_IF1 1086000000 /* 1086 MHz */ +#define MC44S803_IF2 36125000 /* 36.125 MHz */ + +#define MC44S803_REG_POWER 0 +#define MC44S803_REG_REFOSC 1 +#define MC44S803_REG_REFDIV 2 +#define MC44S803_REG_MIXER 3 +#define MC44S803_REG_RESET 4 +#define MC44S803_REG_LO1 5 +#define MC44S803_REG_LO2 6 +#define MC44S803_REG_CIRCADJ 7 +#define MC44S803_REG_TEST 8 +#define MC44S803_REG_DIGTUNE 9 +#define MC44S803_REG_LNAAGC 0x0A +#define MC44S803_REG_DATAREG 0x0B +#define MC44S803_REG_REGTEST 0x0C +#define MC44S803_REG_VCOTEST 0x0D +#define MC44S803_REG_LNAGAIN 0x0E +#define MC44S803_REG_ID 0x0F + +/* Register definitions */ +#define MC44S803_ADDR 0x0F +#define MC44S803_ADDR_S 0 +/* REG_POWER */ +#define MC44S803_POWER 0xFFFFF0 +#define MC44S803_POWER_S 4 +/* REG_REFOSC */ +#define MC44S803_REFOSC 0x1FF0 +#define MC44S803_REFOSC_S 4 +#define MC44S803_OSCSEL 0x2000 +#define MC44S803_OSCSEL_S 13 +/* REG_REFDIV */ +#define MC44S803_R2 0x1FF0 +#define MC44S803_R2_S 4 +#define MC44S803_REFBUF_EN 0x2000 +#define MC44S803_REFBUF_EN_S 13 +#define MC44S803_R1 0x7C000 +#define MC44S803_R1_S 14 +/* REG_MIXER */ +#define MC44S803_R3 0x70 +#define MC44S803_R3_S 4 +#define MC44S803_MUX3 0x80 +#define MC44S803_MUX3_S 7 +#define MC44S803_MUX4 0x100 +#define MC44S803_MUX4_S 8 +#define MC44S803_OSC_SCR 0x200 +#define MC44S803_OSC_SCR_S 9 +#define MC44S803_TRI_STATE 0x400 +#define MC44S803_TRI_STATE_S 10 +#define MC44S803_BUF_GAIN 0x800 +#define MC44S803_BUF_GAIN_S 11 +#define MC44S803_BUF_IO 0x1000 +#define MC44S803_BUF_IO_S 12 +#define MC44S803_MIXER_RES 0xFE000 +#define MC44S803_MIXER_RES_S 13 +/* REG_RESET */ +#define MC44S803_RS 0x10 +#define MC44S803_RS_S 4 +#define MC44S803_SO 0x20 +#define MC44S803_SO_S 5 +/* REG_LO1 */ +#define MC44S803_LO1 0xFFF0 +#define MC44S803_LO1_S 4 +/* REG_LO2 */ +#define MC44S803_LO2 0x7FFF0 +#define MC44S803_LO2_S 4 +/* REG_CIRCADJ */ +#define MC44S803_G1 0x20 +#define MC44S803_G1_S 5 +#define MC44S803_G3 0x80 +#define MC44S803_G3_S 7 +#define MC44S803_CIRCADJ_RES 0x300 +#define MC44S803_CIRCADJ_RES_S 8 +#define MC44S803_G6 0x400 +#define MC44S803_G6_S 10 +#define MC44S803_G7 0x800 +#define MC44S803_G7_S 11 +#define MC44S803_S1 0x1000 +#define MC44S803_S1_S 12 +#define MC44S803_LP 0x7E000 +#define MC44S803_LP_S 13 +#define MC44S803_CLRF 0x80000 +#define MC44S803_CLRF_S 19 +#define MC44S803_CLIF 0x100000 +#define MC44S803_CLIF_S 20 +/* REG_TEST */ +/* REG_DIGTUNE */ +#define MC44S803_DA 0xF0 +#define MC44S803_DA_S 4 +#define MC44S803_XOD 0x300 +#define MC44S803_XOD_S 8 +#define MC44S803_RST 0x10000 +#define MC44S803_RST_S 16 +#define MC44S803_LO_REF 0x1FFF00 +#define MC44S803_LO_REF_S 8 +#define MC44S803_AT 0x200000 +#define MC44S803_AT_S 21 +#define MC44S803_MT 0x400000 +#define MC44S803_MT_S 22 +/* REG_LNAAGC */ +#define MC44S803_G 0x3F0 +#define MC44S803_G_S 4 +#define MC44S803_AT1 0x400 +#define MC44S803_AT1_S 10 +#define MC44S803_AT2 0x800 +#define MC44S803_AT2_S 11 +#define MC44S803_HL_GR_EN 0x8000 +#define MC44S803_HL_GR_EN_S 15 +#define MC44S803_AGC_AN_DIG 0x10000 +#define MC44S803_AGC_AN_DIG_S 16 +#define MC44S803_ATTEN_EN 0x20000 +#define MC44S803_ATTEN_EN_S 17 +#define MC44S803_AGC_READ_EN 0x40000 +#define MC44S803_AGC_READ_EN_S 18 +#define MC44S803_LNA0 0x80000 +#define MC44S803_LNA0_S 19 +#define MC44S803_AGC_SEL 0x100000 +#define MC44S803_AGC_SEL_S 20 +#define MC44S803_AT0 0x200000 +#define MC44S803_AT0_S 21 +#define MC44S803_B 0xC00000 +#define MC44S803_B_S 22 +/* REG_DATAREG */ +#define MC44S803_D 0xF0 +#define MC44S803_D_S 4 +/* REG_REGTEST */ +/* REG_VCOTEST */ +/* REG_LNAGAIN */ +#define MC44S803_IF_PWR 0x700 +#define MC44S803_IF_PWR_S 8 +#define MC44S803_RF_PWR 0x3800 +#define MC44S803_RF_PWR_S 11 +#define MC44S803_LNA_GAIN 0xFC000 +#define MC44S803_LNA_GAIN_S 14 +/* REG_ID */ +#define MC44S803_ID 0x3E00 +#define MC44S803_ID_S 9 + +/* Some macros to read/write fields */ + +/* First shift, then mask */ +#define MC44S803_REG_SM(_val, _reg) \ + (((_val) << _reg##_S) & (_reg)) + +/* First mask, then shift */ +#define MC44S803_REG_MS(_val, _reg) \ + (((_val) & (_reg)) >> _reg##_S) + +struct mc44s803_priv { + struct mc44s803_config *cfg; + struct i2c_adapter *i2c; + struct dvb_frontend *fe; + + u32 frequency; +}; + +#endif diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig index 49f7b20c2..bbddc9fb6 100644 --- a/linux/drivers/media/dvb/dvb-usb/Kconfig +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -297,5 +297,6 @@ config DVB_USB_AF9015 select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index e518d47fe..88a365ad9 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -27,9 +27,7 @@ #include "qt1010.h" #include "tda18271.h" #include "mxl5005s.h" -#if 0 /* keep */ -#include "mc44s80x.h" -#endif +#include "mc44s803.h" static int dvb_usb_af9015_debug; module_param_named(debug, dvb_usb_af9015_debug, int, 0644); @@ -280,6 +278,21 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate. req.data = &msg[i+1].buf[0]; ret = af9015_ctrl_msg(d, &req); i += 2; + } else if (msg[i].flags & I2C_M_RD) { + ret = -EINVAL; + if (msg[i].addr == + af9015_af9013_config[0].demod_address) + goto error; + else + req.cmd = READ_I2C; + req.i2c_addr = msg[i].addr; + req.addr = addr; + req.mbox = mbox; + req.addr_len = addr_len; + req.data_len = msg[i].len; + req.data = &msg[i].buf[0]; + ret = af9015_ctrl_msg(d, &req); + i += 1; } else { if (msg[i].addr == af9015_af9013_config[0].demod_address) @@ -942,7 +955,6 @@ static int af9015_read_config(struct usb_device *udev) switch (val) { case AF9013_TUNER_ENV77H11D5: case AF9013_TUNER_MT2060: - case AF9013_TUNER_MC44S803: case AF9013_TUNER_QT1010: case AF9013_TUNER_UNKNOWN: case AF9013_TUNER_MT2060_2: @@ -955,6 +967,10 @@ static int af9015_read_config(struct usb_device *udev) case AF9013_TUNER_MXL5005R: af9015_af9013_config[i].rf_spec_inv = 0; break; + case AF9013_TUNER_MC44S803: + af9015_af9013_config[i].gpio[1] = AF9013_GPIO_LO; + af9015_af9013_config[i].rf_spec_inv = 1; + break; default: warn("tuner id:%d not supported, please report!", val); return -ENODEV; @@ -1142,6 +1158,11 @@ static struct mxl5005s_config af9015_mxl5005_config = { .AgcMasterByte = 0x00, }; +static struct mc44s803_config af9015_mc44s803_config = { + .i2c_address = 0xc0, + .dig_out = 1, +}; + static int af9015_tuner_attach(struct dvb_usb_adapter *adap) { struct af9015_state *state = adap->dev->priv; @@ -1186,15 +1207,8 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap) DVB_PLL_TDA665X) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_MC44S803: -#if 0 /* keep */ - ret = dvb_attach(mc44s80x_attach, adap->fe, i2c_adap) - == NULL ? -ENODEV : 0; -#else - ret = -ENODEV; - info("Freescale MC44S803 tuner found but no driver for that" \ - "tuner. Look at the Linuxtv.org for tuner driver" \ - "status."); -#endif + ret = dvb_attach(mc44s803_attach, adap->fe, i2c_adap, + &af9015_mc44s803_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_UNKNOWN: default: diff --git a/linux/drivers/media/video/cx18/cx18-audio.c b/linux/drivers/media/video/cx18/cx18-audio.c index 57beddf0a..d19bd778c 100644 --- a/linux/drivers/media/video/cx18/cx18-audio.c +++ b/linux/drivers/media/video/cx18/cx18-audio.c @@ -64,8 +64,7 @@ int cx18_audio_set_io(struct cx18 *cx) val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30; val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : (audio_input << 4); - cx18_write_reg(cx, val | 0xb00, CX18_AUDIO_ENABLE); - cx18_vapi(cx, CX18_APU_RESETAI, 1, 0); + cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30); return 0; } diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 780125002..a3bd2c95f 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/drivers/media/video/cx18/cx18-av-core.c @@ -169,9 +169,14 @@ static void cx18_av_initialize(struct cx18 *cx) /* Set VGA_TRACK_RANGE to 0x20 */ cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000); - /* Enable VBI capture */ - cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F); - /* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */ + /* + * Initial VBI setup + * VIP-1.1, 10 bit mode, enable Raw, disable sliced, + * don't clamp raw samples when codes are in use, 1 byte user D-words, + * IDID0 has line #, RP code V bit transition on VBLANK, data during + * blanking intervals + */ + cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4013252e); /* Set the video input. The setting in MODE_CTRL gets lost when we do the above setup */ @@ -213,6 +218,7 @@ void cx18_av_std_setup(struct cx18 *cx) cx18_av_write(cx, 0x49f, 0x14); if (std & V4L2_STD_625_50) { + /* FIXME - revisit these for Sliced VBI */ hblank = 132; hactive = 720; burst = 93; @@ -236,13 +242,34 @@ void cx18_av_std_setup(struct cx18 *cx) sc = 672351; } } else { + /* + * The following relationships of half line counts should hold: + * 525 = vsync + vactive + vblank656 + * 12 = vblank656 - vblank + * + * vsync: always 6 half-lines of vsync pulses + * vactive: half lines of active video + * vblank656: half lines, after line 3, of blanked video + * vblank: half lines, after line 9, of blanked video + * + * vblank656 starts counting from the falling edge of the first + * vsync pulse (start of line 4) + * vblank starts counting from the after the 6 vsync pulses and + * 6 equalization pulses (start of line 10) + * + * For 525 line systems the driver will extract VBI information + * from lines 10 through 21. To avoid the EAV RP code from + * toggling at the start of hblank at line 22, where sliced VBI + * data from line 21 is stuffed, also treat line 22 as blanked. + */ + vblank656 = 38; /* lines 4 through 22 */ + vblank = 26; /* lines 10 through 22 */ + vactive = 481; /* lines 23 through 262.5 */ + hactive = 720; hblank = 122; - vactive = 487; luma_lpf = 1; uv_lpf = 1; - vblank = 26; - vblank656 = 26; src_decimation = 0x21f; if (std == V4L2_STD_PAL_60) { @@ -325,14 +352,14 @@ void cx18_av_std_setup(struct cx18 *cx) cx18_av_write(cx, 0x47d, 0xff & sc >> 8); cx18_av_write(cx, 0x47e, 0xff & sc >> 16); - /* Sets VBI parameters */ if (std & V4L2_STD_625_50) { - cx18_av_write(cx, 0x47f, 0x01); - state->vbi_line_offset = 5; + state->slicer_line_delay = 1; + state->slicer_line_offset = (6 + state->slicer_line_delay - 2); } else { - cx18_av_write(cx, 0x47f, 0x00); - state->vbi_line_offset = 8; + state->slicer_line_delay = 0; + state->slicer_line_offset = (10 + state->slicer_line_delay - 2); } + cx18_av_write(cx, 0x47f, state->slicer_line_delay); } /* ----------------------------------------------------------------------- */ @@ -680,19 +707,45 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) /* ----------------------------------------------------------------------- */ +static int valid_av_cmd(unsigned int cmd) +{ + switch (cmd) { + /* All commands supported by cx18_av_cmd() */ + case VIDIOC_INT_DECODE_VBI_LINE: + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + case VIDIOC_STREAMON: + case VIDIOC_STREAMOFF: + case VIDIOC_LOG_STATUS: + case VIDIOC_G_CTRL: + case VIDIOC_S_CTRL: + case VIDIOC_QUERYCTRL: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case AUDC_SET_RADIO: + case VIDIOC_INT_G_VIDEO_ROUTING: + case VIDIOC_INT_S_VIDEO_ROUTING: + case VIDIOC_INT_G_AUDIO_ROUTING: + case VIDIOC_INT_S_AUDIO_ROUTING: + case VIDIOC_S_FREQUENCY: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_INT_RESET: + return 1; + default: + return 0; + } + return 0; +} + int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) { struct cx18_av_state *state = &cx->av_state; struct v4l2_tuner *vt = arg; struct v4l2_routing *route = arg; - /* ignore these commands */ - switch (cmd) { - case TUNER_SET_TYPE_ADDR: - return 0; - } - - if (!state->is_initialized) { + if (!state->is_initialized && valid_av_cmd(cmd)) { CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd); /* initialize on first use */ state->is_initialized = 1; diff --git a/linux/drivers/media/video/cx18/cx18-av-core.h b/linux/drivers/media/video/cx18/cx18-av-core.h index cf68a6039..d83760cae 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.h +++ b/linux/drivers/media/video/cx18/cx18-av-core.h @@ -79,11 +79,28 @@ struct cx18_av_state { enum cx18_av_audio_input aud_input; u32 audclk_freq; int audmode; - int vbi_line_offset; int default_volume; u32 id; u32 rev; int is_initialized; + + /* + * The VBI slicer starts operating and counting lines, begining at + * slicer line count of 1, at D lines after the deassertion of VRESET + * This staring field line, S, is 6 or 10 for 625 or 525 line systems. + * Sliced ancillary data captured on VBI slicer line M is sent at the + * beginning of the next VBI slicer line, VBI slicer line count N = M+1. + * Thus when the VBI slicer reports a VBI slicer line number with + * ancillary data, the IDID0 byte indicates VBI slicer line N. + * The actual field line that the captured data comes from is + * L = M+(S+D-1) = N-1+(S+D-1) = N + (S+D-2). + * + * D is the slicer_line_delay value programmed into register 0x47f. + * (S+D-2) is the slicer_line_offset used to convert slicer reported + * line counts to actual field lines. + */ + int slicer_line_delay; + int slicer_line_offset; }; diff --git a/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c index b374c74d3..940ea9352 100644 --- a/linux/drivers/media/video/cx18/cx18-av-firmware.c +++ b/linux/drivers/media/video/cx18/cx18-av-firmware.c @@ -131,7 +131,8 @@ int cx18_av_loadfw(struct cx18 *cx) v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); /* If bit 11 is 1, clear bit 10 */ if (v & 0x800) - cx18_write_reg(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); + cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE, + 0, 0x400); /* Enable WW auto audio standard detection */ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); diff --git a/linux/drivers/media/video/cx18/cx18-av-vbi.c b/linux/drivers/media/video/cx18/cx18-av-vbi.c index 1527ea4f6..43267d1af 100644 --- a/linux/drivers/media/video/cx18/cx18-av-vbi.c +++ b/linux/drivers/media/video/cx18/cx18-av-vbi.c @@ -24,6 +24,52 @@ #include "cx18-driver.h" +/* + * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode, + * NN counts 1 byte Dwords, an IDID with the VBI line # in it. + * Thus, according to the VIP-2 Spec, our VBI ancillary data lines + * (should!) look like: + * 4 byte EAV code: 0xff 0x00 0x00 0xRP + * unknown number of possible idle bytes + * 3 byte Anc data preamble: 0x00 0xff 0xff + * 1 byte data identifier: ne010iii (parity bits, 010, DID bits) + * 1 byte secondary data id: nessssss (parity bits, SDID bits) + * 1 byte data word count: necccccc (parity bits, NN Dword count) + * 2 byte Internal DID: VBI-line-# 0x80 + * NN data bytes + * 1 byte checksum + * Fill bytes needed to fil out to 4*NN bytes of payload + * + * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, & + * in the vertical blanking interval are: + * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0) + * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0) + * + * Since the V bit is only allowed to toggle in the EAV RP code, just + * before the first active region line and for active lines, they are: + * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0) + * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0) + * + * The user application DID bytes we care about are: + * 0x91 (1 0 010 0 !ActiveLine AncDataPresent) + * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent) + * + */ +static const u8 sliced_vbi_did[2] = { 0x91, 0x55 }; + +struct vbi_anc_data { + /* u8 eav[4]; */ + /* u8 idle[]; Variable number of idle bytes */ + u8 preamble[3]; + u8 did; + u8 sdid; + u8 data_count; + u8 idid[2]; + u8 payload[1]; /* data_count of payload */ + /* u8 checksum; */ + /* u8 fill[]; Variable number of fill bytes */ +}; + static int odd_parity(u8 c) { c ^= (c >> 4); @@ -96,7 +142,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ V4L2_SLICED_CAPTION_525, /* 6 */ - 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ + V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 - unlike cx25840 */ 0, 0, 0, 0 }; int is_pal = !(state->std & V4L2_STD_525_60); @@ -136,7 +182,6 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) case VIDIOC_S_FMT: { int is_pal = !(state->std & V4L2_STD_525_60); - int vbi_offset = is_pal ? 1 : 0; int i, x; u8 lcr[24]; @@ -153,7 +198,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) cx18_av_std_setup(cx); /* VBI Offset */ - cx18_av_write(cx, 0x47f, vbi_offset); + cx18_av_write(cx, 0x47f, state->slicer_line_delay); cx18_av_write(cx, 0x404, 0x2e); break; } @@ -167,8 +212,9 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) /* Sliced VBI */ cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ cx18_av_write(cx, 0x406, 0x13); - cx18_av_write(cx, 0x47f, vbi_offset); + cx18_av_write(cx, 0x47f, state->slicer_line_delay); + /* Force impossible lines to 0 */ if (is_pal) { for (i = 0; i <= 6; i++) svbi->service_lines[0][i] = @@ -183,6 +229,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) svbi->service_lines[1][i] = 0; } + /* Build register values for requested service lines */ for (i = 7; i <= 23; i++) { for (x = 0; x <= 1; x++) { switch (svbi->service_lines[1-x][i]) { @@ -196,7 +243,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) lcr[i] |= 6 << (4 * x); break; case V4L2_SLICED_VPS: - lcr[i] |= 9 << (4 * x); + lcr[i] |= 7 << (4 * x); /*'840 differs*/ break; } } @@ -213,54 +260,61 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) } cx18_av_write(cx, 0x43c, 0x16); - cx18_av_write(cx, 0x474, is_pal ? 0x2a : 0x22); + /* FIXME - should match vblank set in cx18_av_std_setup() */ + cx18_av_write(cx, 0x474, is_pal ? 0x2a : 26); break; } case VIDIOC_INT_DECODE_VBI_LINE: { struct v4l2_decode_vbi_line *vbi = arg; - u8 *p = vbi->p; - int id1, id2, l, err = 0; - - if (p[0] || p[1] != 0xff || p[2] != 0xff || - (p[3] != 0x55 && p[3] != 0x91)) { + u8 *p; + struct vbi_anc_data *anc = (struct vbi_anc_data *) vbi->p; + int did, sdid, l, err = 0; + + /* + * Check for the ancillary data header for sliced VBI + */ + if (anc->preamble[0] || + anc->preamble[1] != 0xff || anc->preamble[2] != 0xff || + (anc->did != sliced_vbi_did[0] && + anc->did != sliced_vbi_did[1])) { vbi->line = vbi->type = 0; break; } - p += 4; - id1 = p[-1]; - id2 = p[0] & 0xf; - l = p[2] & 0x3f; - l += state->vbi_line_offset; - p += 4; + did = anc->did; + sdid = anc->sdid & 0xf; + l = anc->idid[0] & 0x3f; + l += state->slicer_line_offset; + p = anc->payload; - switch (id2) { + /* Decode the SDID set by the slicer */ + switch (sdid) { case 1: - id2 = V4L2_SLICED_TELETEXT_B; + sdid = V4L2_SLICED_TELETEXT_B; break; case 4: - id2 = V4L2_SLICED_WSS_625; + sdid = V4L2_SLICED_WSS_625; break; case 6: - id2 = V4L2_SLICED_CAPTION_525; + sdid = V4L2_SLICED_CAPTION_525; err = !odd_parity(p[0]) || !odd_parity(p[1]); break; - case 9: - id2 = V4L2_SLICED_VPS; + case 7: /* Differs from cx25840 */ + sdid = V4L2_SLICED_VPS; if (decode_vps(p, p) != 0) err = 1; break; default: - id2 = 0; + sdid = 0; err = 1; break; } - vbi->type = err ? 0 : id2; + vbi->type = err ? 0 : sdid; vbi->line = err ? 0 : l; - vbi->is_second_field = err ? 0 : (id1 == 0x55); + vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]); vbi->p = p; break; } diff --git a/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c index 53217cf45..c0177ce39 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.c +++ b/linux/drivers/media/video/cx18/cx18-cards.c @@ -51,7 +51,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = { static const struct cx18_card cx18_card_hvr1600_esmt = { .type = CX18_CARD_HVR_1600_ESMT, .name = "Hauppauge HVR-1600", - .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", + .comment = "Simultaneous Digital and Analog TV capture supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_muxer = CX18_HW_CS5345, @@ -97,7 +97,7 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { static const struct cx18_card cx18_card_hvr1600_samsung = { .type = CX18_CARD_HVR_1600_SAMSUNG, .name = "Hauppauge HVR-1600 (Preproduction)", - .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", + .comment = "Simultaneous Digital and Analog TV capture supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_muxer = CX18_HW_CS5345, @@ -152,7 +152,7 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = { static const struct cx18_card cx18_card_h900 = { .type = CX18_CARD_COMPRO_H900, .name = "Compro VideoMate H900", - .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", + .comment = "Analog TV capture supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_all = CX18_HW_TUNER, @@ -249,7 +249,7 @@ static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = { static const struct cx18_card cx18_card_cnxt_raptor_pal = { .type = CX18_CARD_CNXT_RAPTOR_PAL, .name = "Conexant Raptor PAL/SECAM", - .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", + .comment = "Analog TV capture supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_muxer = CX18_HW_GPIO, diff --git a/linux/drivers/media/video/cx18/cx18-cards.h b/linux/drivers/media/video/cx18/cx18-cards.h index 6fa7bcb42..f8ee29f10 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.h +++ b/linux/drivers/media/video/cx18/cx18-cards.h @@ -49,8 +49,7 @@ /* V4L2 capability aliases */ #define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \ - V4L2_CAP_VBI_CAPTURE) -/* | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */ + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) struct cx18_card_video_input { u8 video_type; /* video input type */ diff --git a/linux/drivers/media/video/cx18/cx18-controls.c b/linux/drivers/media/video/cx18/cx18-controls.c index 17edf305d..6af4d5c19 100644 --- a/linux/drivers/media/video/cx18/cx18-controls.c +++ b/linux/drivers/media/video/cx18/cx18-controls.c @@ -178,8 +178,8 @@ static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt int i; for (i = 0; i < CX18_VBI_FRAMES; i++) { - /* Yuck, hardcoded. Needs to be a define */ - cx->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL); + cx->vbi.sliced_mpeg_data[i] = + kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, GFP_KERNEL); if (cx->vbi.sliced_mpeg_data[i] == NULL) { while (--i >= 0) { kfree(cx->vbi.sliced_mpeg_data[i]); diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index fe2324ef2..f78566bcd 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -448,34 +448,38 @@ static void cx18_process_options(struct cx18 *cx) cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize; - cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = 0; /* computed later */ + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */ - /* Except for VBI ensure stream_buffers & stream_buf_size are valid */ + /* Ensure stream_buffers & stream_buf_size are valid */ for (i = 0; i < CX18_MAX_STREAMS; i++) { - /* User said to use 0 buffers */ - if (cx->stream_buffers[i] == 0) { - cx->options.megabytes[i] = 0; - cx->stream_buf_size[i] = 0; - continue; - } - /* User said to use 0 MB total */ - if (cx->options.megabytes[i] <= 0) { + if (cx->stream_buffers[i] == 0 || /* User said 0 buffers */ + cx->options.megabytes[i] <= 0 || /* User said 0 MB total */ + cx->stream_buf_size[i] <= 0) { /* User said buf size 0 */ cx->options.megabytes[i] = 0; cx->stream_buffers[i] = 0; cx->stream_buf_size[i] = 0; continue; } - /* VBI is computed later or user said buffer has size 0 */ - if (cx->stream_buf_size[i] <= 0) { - if (i != CX18_ENC_STREAM_TYPE_VBI) { - cx->options.megabytes[i] = 0; - cx->stream_buffers[i] = 0; - cx->stream_buf_size[i] = 0; + /* + * VBI is a special case where the stream_buf_size is fixed + * and already in bytes + */ + if (i == CX18_ENC_STREAM_TYPE_VBI) { + if (cx->stream_buffers[i] < 0) { + cx->stream_buffers[i] = + cx->options.megabytes[i] * 1024 * 1024 + / cx->stream_buf_size[i]; + } else { + /* N.B. This might round down to 0 */ + cx->options.megabytes[i] = + cx->stream_buffers[i] + * cx->stream_buf_size[i]/(1024 * 1024); } continue; } + /* All other streams have stream_buf_size in kB at this point */ if (cx->stream_buffers[i] < 0) { cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 / cx->stream_buf_size[i]; @@ -563,8 +567,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) mutex_init(&cx->epu2apu_mb_lock); mutex_init(&cx->epu2cpu_mb_lock); - spin_lock_init(&cx->lock); - cx->work_queue = create_singlethread_workqueue(cx->name); if (cx->work_queue == NULL) { CX18_ERR("Unable to create work hander thread\n"); @@ -593,7 +595,8 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) (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_AC3; + cx->params.capabilities = + CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_AC3 | CX2341X_CAP_HAS_SLICED_VBI; init_waitqueue_head(&cx->cap_w); init_waitqueue_head(&cx->mb_apu_waitq); init_waitqueue_head(&cx->mb_cpu_waitq); @@ -603,49 +606,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; - /* - * The VBI line sizes depend on the pixel clock and the horiz rate - * - * (1/Fh)*(2*Fp) = Samples/line - * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples - * - * Sliced VBI is sent as ancillary data during horizontal blanking - * Raw VBI is sent as active video samples during vertcal blanking - * - * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line - * length of 720 pixels @ 4:2:2 sampling. Thus... - * - * For systems that use a 15.734 kHz horizontal rate, such as - * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have: - * - * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line = - * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples - * - * For systems that use a 15.625 kHz horizontal rate, such as - * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have: - * - * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line = - * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples - * - */ - - /* FIXME: init these based on tuner std & modify when std changes */ - /* CX18-AV-Core number of VBI samples output per horizontal line */ - cx->vbi.raw_decoder_line_size = 1444; /* 4 byte SAV + 2 * 720 */ - cx->vbi.sliced_decoder_line_size = 272; /* 60 Hz: 268+4, 50 Hz: 280+4 */ - - /* CX18-AV-Core VBI samples/line possibly rounded up */ - cx->vbi.raw_size = 1444; /* Real max size is 1444 */ - cx->vbi.sliced_size = 284; /* Real max size is 284 */ - - /* - * CX18-AV-Core SAV/EAV RP codes in VIP 1.x mode - * Task Field VerticalBlank HorizontalBlank 0 0 0 0 - */ - cx->vbi.raw_decoder_sav_odd_field = 0x20; /* V */ - cx->vbi.raw_decoder_sav_even_field = 0x60; /* FV */ - cx->vbi.sliced_decoder_sav_odd_field = 0xB0; /* T VH - actually EAV */ - cx->vbi.sliced_decoder_sav_even_field = 0xF0; /* TFVH - actually EAV */ return 0; } @@ -678,8 +638,9 @@ static void __devinit cx18_init_struct2(struct cx18 *cx) cx->av_state.aud_input = CX18_AV_AUDIO8; cx->av_state.audclk_freq = 48000; cx->av_state.audmode = V4L2_TUNER_MODE_LANG1; - /* FIXME - 8 is NTSC value, investigate */ - cx->av_state.vbi_line_offset = 8; + cx->av_state.slicer_line_delay = 0; + cx->av_state.slicer_line_offset = + (10 + cx->av_state.slicer_line_delay - 2); } static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev, @@ -782,7 +743,6 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, { int retval = 0; int i; - int vbi_buf_size; u32 devtype; struct cx18 *cx; @@ -938,23 +898,6 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, } cx->params.video_gop_size = cx->is_60hz ? 15 : 12; - /* - * FIXME: setting the buffer size based on the tuner standard is - * suboptimal, as the CVBS and SVideo inputs could use a different std - * and the buffer could end up being too small in that case. - */ - vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2; - cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size; - - if (cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] < 0) - cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = - cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] * 1024 * 1024 - / vbi_buf_size; - else - cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = - cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] * vbi_buf_size - / (1024 * 1024); - if (cx->options.radio > 0) cx->v4l2_cap |= V4L2_CAP_RADIO; @@ -1085,6 +1028,19 @@ int cx18_init_on_first_open(struct cx18 *cx) return -ENXIO; } + /* + * The second firmware load requires us to normalize the APU state, + * or the audio for the first analog capture will be badly incorrect. + * + * I can't seem to call APU_RESETAI and have it succeed without the + * APU capturing audio, so we start and stop it here to do the reset + */ + + /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */ + cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); + cx18_vapi(cx, CX18_APU_RESETAI, 0); + cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); + vf.tuner = 0; vf.type = V4L2_TUNER_ANALOG_TV; vf.frequency = 6400; /* the tuner 'baseline' frequency */ diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 3a21013cd..7fc914c52 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -293,7 +293,6 @@ struct cx18_stream { int dma; /* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or PCI_DMA_NONE */ - u64 dma_pts; wait_queue_head_t waitq; /* Buffer Stats */ @@ -319,59 +318,121 @@ struct cx18_open_id { /* forward declaration of struct defined in cx18-cards.h */ struct cx18_card; +/* + * A note about "sliced" VBI data as implemented in this driver: + * + * Currently we collect the sliced VBI in the form of Ancillary Data + * packets, inserted by the AV core decoder/digitizer/slicer in the + * horizontal blanking region of the VBI lines, in "raw" mode as far as + * the Encoder is concerned. We don't ever tell the Encoder itself + * to provide sliced VBI. (AV Core: sliced mode - Encoder: raw mode) + * + * We then process the ancillary data ourselves to send the sliced data + * to the user application directly or build up MPEG-2 private stream 1 + * packets to splice into (only!) MPEG-2 PS streams for the user app. + * + * (That's how ivtv essentially does it.) + * + * The Encoder should be able to extract certain sliced VBI data for + * us and provide it in a separate stream or splice it into any type of + * MPEG PS or TS stream, but this isn't implemented yet. + */ + +/* + * Number of "raw" VBI samples per horizontal line we tell the Encoder to + * grab from the decoder/digitizer/slicer output for raw or sliced VBI. + * It depends on the pixel clock and the horiz rate: + * + * (1/Fh)*(2*Fp) = Samples/line + * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples + * + * Sliced VBI data is sent as ancillary data during horizontal blanking + * Raw VBI is sent as active video samples during vertcal blanking + * + * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line + * length of 720 pixels @ 4:2:2 sampling. Thus... + * + * For systems that use a 15.734 kHz horizontal rate, such as + * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have: + * + * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line = + * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples + * + * For systems that use a 15.625 kHz horizontal rate, such as + * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have: + * + * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line = + * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples + */ +static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */ +static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */ +static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */ #define CX18_VBI_FRAMES 32 -/* VBI data */ struct vbi_info { - u32 enc_size; - u32 frame; - u8 cc_data_odd[256]; - u8 cc_data_even[256]; - int cc_pos; - u8 cc_no_update; - u8 vps[5]; - u8 vps_found; - int wss; - u8 wss_found; - u8 wss_no_update; - u32 raw_decoder_line_size; - u8 raw_decoder_sav_odd_field; - u8 raw_decoder_sav_even_field; - u32 sliced_decoder_line_size; - u8 sliced_decoder_sav_odd_field; - u8 sliced_decoder_sav_even_field; + /* Current state of v4l2 VBI settings for this device */ struct v4l2_format in; - /* convenience pointer to sliced struct in vbi_in union */ - struct v4l2_sliced_vbi_format *sliced_in; - u32 service_set_in; - int insert_mpeg; + struct v4l2_sliced_vbi_format *sliced_in; /* pointer to in.fmt.sliced */ + u32 count; /* Count of VBI data lines: 60 Hz: 12 or 50 Hz: 18 */ + u32 start[2]; /* First VBI data line per field: 10 & 273 or 6 & 318 */ - /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. - One for /dev/vbi0 and one for /dev/vbi8 */ - struct v4l2_sliced_vbi_data sliced_data[36]; + u32 frame; /* Count of VBI buffers/frames received from Encoder */ + + /* + * Vars for creation and insertion of MPEG Private Stream 1 packets + * of sliced VBI data into an MPEG PS + */ - /* Buffer for VBI data inserted into MPEG stream. - The first byte is a dummy byte that's never used. - The next 16 bytes contain the MPEG header for the VBI data, - the remainder is the actual VBI data. - The max size accepted by the MPEG VBI reinsertion turns out - to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, - where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is - a single line header byte and 2 * 18 is the number of VBI lines per frame. + /* Boolean: create and insert Private Stream 1 packets into the PS */ + int insert_mpeg; - However, it seems that the data must be 1K aligned, so we have to - pad the data until the 1 or 2 K boundary. + /* + * Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. + * Used in cx18-vbi.c only for collecting sliced data, and as a source + * during conversion of sliced VBI data into MPEG Priv Stream 1 packets. + * We don't need to save state here, but the array may have been a bit + * too big (2304 bytes) to alloc from the stack. + */ + struct v4l2_sliced_vbi_data sliced_data[36]; - This pointer array will allocate 2049 bytes to store each VBI frame. */ + /* + * A ring buffer of driver-generated MPEG-2 PS + * Program Pack/Private Stream 1 packets for sliced VBI data insertion + * into the MPEG PS stream. + * + * In each sliced_mpeg_data[] buffer is: + * 16 byte MPEG-2 PS Program Pack Header + * 16 byte MPEG-2 Private Stream 1 PES Header + * 4 byte magic number: "itv0" or "ITV0" + * 4 byte first field line mask, if "itv0" + * 4 byte second field line mask, if "itv0" + * 36 lines, if "ITV0"; or <36 lines, if "itv0"; of sliced VBI data + * + * Each line in the payload is + * 1 byte line header derived from the SDID (WSS, CC, VPS, etc.) + * 42 bytes of line data + * + * That's a maximum 1552 bytes of payload in the Private Stream 1 packet + * which is the payload size a PVR-350 (CX23415) MPEG decoder will + * accept for VBI data. So, including the headers, it's a maximum 1584 + * bytes total. + */ +#define CX18_SLICED_MPEG_DATA_MAXSZ 1584 + /* copy_vbi_buf() needs 8 temp bytes on the end for the worst case */ +#define CX18_SLICED_MPEG_DATA_BUFSZ (CX18_SLICED_MPEG_DATA_MAXSZ+8) u8 *sliced_mpeg_data[CX18_VBI_FRAMES]; u32 sliced_mpeg_size[CX18_VBI_FRAMES]; - struct cx18_buffer sliced_mpeg_buf; + + /* Count of Program Pack/Program Stream 1 packets inserted into PS */ u32 inserted_frame; - u32 start[2], count; - u32 raw_size; - u32 sliced_size; + /* + * A dummy driver stream transfer buffer with a copy of the next + * sliced_mpeg_data[] buffer for output to userland apps. + * Only used in cx18-fileops.c, but its state needs to persist at times. + */ + struct cx18_buffer sliced_mpeg_buf; }; /* Per cx23418, per I2C bus private algo callback data */ @@ -418,9 +479,6 @@ struct cx18 { unsigned long dualwatch_jiffies; u32 dualwatch_stereo_mode; - /* Digitizer type */ - int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */ - struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ struct cx18_options options; /* User options */ int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */ @@ -429,7 +487,6 @@ struct cx18 { unsigned long i_flags; /* global cx18 flags */ atomic_t ana_capturing; /* count number of active analog capture streams */ atomic_t tot_capturing; /* total count number of active capture streams */ - spinlock_t lock; /* lock access to this struct */ int search_pack_header; int open_id; /* incremented each time an open occurs, used as diff --git a/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index 36c380b66..c73fbe65b 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/drivers/media/video/cx18/cx18-fileops.c @@ -209,9 +209,9 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, *err = 0; while (1) { if (s->type == CX18_ENC_STREAM_TYPE_MPG) { -#if 0 /* Process pending program info updates and pending VBI data */ +#if 0 cx18_update_pgm_info(cx); #endif @@ -224,7 +224,6 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { /* byteswap and process VBI data */ cx18_process_vbi_data(cx, buf, - s_vbi->dma_pts, s_vbi->type); cx18_stream_put_buf_fw(s_vbi, buf); } @@ -245,8 +244,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, cx18_buf_swap(buf); else { /* byteswap and process VBI data */ - cx18_process_vbi_data(cx, buf, - s->dma_pts, s->type); + cx18_process_vbi_data(cx, buf, s->type); } return buf; } @@ -298,6 +296,20 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, len = ucount; if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) { + /* + * Try to find a good splice point in the PS, just before + * an MPEG-2 Program Pack start code, and provide only + * up to that point to the user, so it's easy to insert VBI data + * the next time around. + */ + /* FIXME - This only works for an MPEG-2 PS, not a TS */ + /* + * An MPEG-2 Program Stream (PS) is a series of + * MPEG-2 Program Packs terminated by an + * MPEG Program End Code after the last Program Pack. + * A Program Pack may hold a PS System Header packet and any + * number of Program Elementary Stream (PES) Packets + */ const char *start = buf->buf + buf->readpos; const char *p = start + 1; const u8 *q; @@ -305,38 +317,54 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, int stuffing, i; while (start + len > p) { + /* Scan for a 0 to find a potential MPEG-2 start code */ q = memchr(p, 0, start + len - p); if (q == NULL) break; p = q + 1; + /* + * Keep looking if not a + * MPEG-2 Pack header start code: 0x00 0x00 0x01 0xba + * or MPEG-2 video PES start code: 0x00 0x00 0x01 0xe0 + */ if ((char *)q + 15 >= buf->buf + buf->bytesused || q[1] != 0 || q[2] != 1 || q[3] != ch) continue; + + /* If expecting the primary video PES */ if (!cx->search_pack_header) { + /* Continue if it couldn't be a PES packet */ if ((q[6] & 0xc0) != 0x80) continue; - if (((q[7] & 0xc0) == 0x80 && - (q[9] & 0xf0) == 0x20) || - ((q[7] & 0xc0) == 0xc0 && - (q[9] & 0xf0) == 0x30)) { - ch = 0xba; + /* Check if a PTS or PTS & DTS follow */ + if (((q[7] & 0xc0) == 0x80 && /* PTS only */ + (q[9] & 0xf0) == 0x20) || /* PTS only */ + ((q[7] & 0xc0) == 0xc0 && /* PTS & DTS */ + (q[9] & 0xf0) == 0x30)) { /* DTS follows */ + /* Assume we found the video PES hdr */ + ch = 0xba; /* next want a Program Pack*/ cx->search_pack_header = 1; - p = q + 9; + p = q + 9; /* Skip this video PES hdr */ } continue; } + + /* We may have found a Program Pack start code */ + + /* Get the count of stuffing bytes & verify them */ stuffing = q[13] & 7; /* all stuffing bytes must be 0xff */ for (i = 0; i < stuffing; i++) if (q[14 + i] != 0xff) break; - if (i == stuffing && - (q[4] & 0xc4) == 0x44 && - (q[12] & 3) == 3 && - q[14 + stuffing] == 0 && + if (i == stuffing && /* right number of stuffing bytes*/ + (q[4] & 0xc4) == 0x44 && /* marker check */ + (q[12] & 3) == 3 && /* marker check */ + q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */ q[15 + stuffing] == 0 && q[16 + stuffing] == 1) { - cx->search_pack_header = 0; + /* We declare we actually found a Program Pack*/ + cx->search_pack_header = 0; /* expect vid PES */ len = (char *)q - start; cx18_setup_sliced_vbi_buf(cx); break; diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c index 4a49b0819..c023c79cf 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.c +++ b/linux/drivers/media/video/cx18/cx18-ioctl.c @@ -58,12 +58,21 @@ u16 cx18_service2vbi(int type) } } +/* Check if VBI services are allowed on the (field, line) for the video std */ static int valid_service_line(int field, int line, int is_pal) { - return (is_pal && line >= 6 && (line != 23 || field == 0)) || + return (is_pal && line >= 6 && + ((field == 0 && line <= 23) || (field == 1 && line <= 22))) || (!is_pal && line >= 10 && line < 22); } +/* + * For a (field, line, std) and inbound potential set of services for that line, + * return the first valid service of those passed in the incoming set for that + * line in priority order: + * CC, VPS, or WSS over TELETEXT for well known lines + * TELETEXT, before VPS, before CC, before WSS, for other lines + */ static u16 select_service_from_set(int field, int line, u16 set, int is_pal) { u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); @@ -90,6 +99,10 @@ static u16 select_service_from_set(int field, int line, u16 set, int is_pal) return 0; } +/* + * Expand the service_set of *fmt into valid service_lines for the std, + * and clear the passed in fmt->service_set + */ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) { u16 set = fmt->service_set; @@ -102,7 +115,10 @@ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) } } -#if 0 +/* + * Sanitize the service_lines in *fmt per the video std, and return 1 + * if any service_line is left as valid after santization + */ static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) { int f, l; @@ -116,8 +132,8 @@ static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) } return set != 0; } -#endif +/* Compute the service_set from the assumed valid service_lines of *fmt */ u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) { int f, l; @@ -164,8 +180,8 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; vbifmt->sampling_rate = 27000000; - vbifmt->offset = 248; - vbifmt->samples_per_line = cx->vbi.raw_decoder_line_size - 4; + vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */ + vbifmt->samples_per_line = vbi_active_samples - 4; vbifmt->sample_format = V4L2_PIX_FMT_GREY; vbifmt->start[0] = cx->vbi.start[0]; vbifmt->start[1] = cx->vbi.start[1]; @@ -179,22 +195,30 @@ 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) { -#if 0 - /* Supported by the cx23418 but not yet implemented. */ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + /* sane, V4L2 spec compliant, defaults */ vbifmt->reserved[0] = 0; vbifmt->reserved[1] = 0; vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); + vbifmt->service_set = 0; + + /* + * Fetch the configured service_lines and total service_set from the + * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in + * fmt->fmt.sliced under valid calling conditions + */ + if (cx18_av_cmd(cx, VIDIOC_G_FMT, fmt)) + return -EINVAL; - cx18_av_cmd(cx, VIDIOC_G_FMT, fmt); + /* Ensure V4L2 spec compliant output */ + vbifmt->reserved[0] = 0; + vbifmt->reserved[1] = 0; + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; vbifmt->service_set = cx18_get_service_set(vbifmt); return 0; -#else - return -EINVAL; -#endif } static int cx18_try_fmt_vid_cap(struct file *file, void *fh, @@ -224,8 +248,6 @@ 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) { -#if 0 - /* Supported by the cx23418 but not yet implemented. */ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; @@ -233,14 +255,13 @@ static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh, vbifmt->reserved[0] = 0; vbifmt->reserved[1] = 0; + /* If given a service set, expand it validly & clear passed in set */ if (vbifmt->service_set) cx18_expand_service_set(vbifmt, cx->is_50hz); - check_service_set(vbifmt, cx->is_50hz); - vbifmt->service_set = cx18_get_service_set(vbifmt); + /* Sanitize the service_lines, and compute the new set if any valid */ + if (check_service_set(vbifmt, cx->is_50hz)) + vbifmt->service_set = cx18_get_service_set(vbifmt); return 0; -#else - return -EINVAL; -#endif } static int cx18_s_fmt_vid_cap(struct file *file, void *fh, @@ -284,20 +305,32 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, if (ret) return ret; + /* + * Changing the Encoder's Raw VBI parameters won't have any effect + * if any analog capture is ongoing + */ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) return -EBUSY; + /* + * Set the digitizer registers for raw active VBI. + * Note cx18_av_vbi_wipes out alot of the passed in fmt under valid + * calling conditions + */ + ret = cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); + if (ret) + return ret; + + /* Store our new v4l2 (non-)sliced VBI state */ cx->vbi.sliced_in->service_set = 0; cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; - cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); + return cx18_g_fmt_vbi_cap(file, fh, fmt); } static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { -#if 0 - /* Supported by the cx23418 but not yet implemented. */ struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; int ret; @@ -307,22 +340,27 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, if (ret) return ret; - ret = cx18_try_fmt_sliced_vbi_cap(file, fh, fmt); - if (ret) - return ret; - - if (check_service_set(vbifmt, cx->is_50hz) == 0) - return -EINVAL; + cx18_try_fmt_sliced_vbi_cap(file, fh, fmt); + /* + * Changing the Encoder's Raw VBI parameters won't have any effect + * if any analog capture is ongoing + */ if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) return -EBUSY; + + /* + * Set the service_lines requested in the digitizer/slicer registers. + * Note, cx18_av_vbi() wipes some "impossible" service lines in the + * passed in fmt->fmt.sliced under valid calling conditions + */ + ret = cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); + if (ret) + return ret; + /* Store our current v4l2 sliced VBI settings */ cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in)); return 0; -#else - return -EINVAL; -#endif } static int cx18_g_chip_ident(struct file *file, void *fh, @@ -620,7 +658,6 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) 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; - cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284; CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long) cx->std); @@ -671,23 +708,30 @@ 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) { -#if 0 - /* Supported by the cx23418 but not yet implemented. */ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; int f, l; - if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - for (f = 0; f < 2; f++) { - for (l = 0; l < 24; l++) { - if (valid_service_line(f, l, cx->is_50hz)) - cap->service_lines[f][l] = set; - } + if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + + cap->service_set = 0; + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + if (valid_service_line(f, l, cx->is_50hz)) { + /* + * We can find all v4l2 supported vbi services + * for the standard, on a valid line for the std + */ + cap->service_lines[f][l] = set; + cap->service_set |= set; + } else + cap->service_lines[f][l] = 0; } - return 0; } -#endif - return -EINVAL; + for (f = 0; f < 3; f++) + cap->reserved[f] = 0; + return 0; } static int cx18_g_enc_index(struct file *file, void *fh, diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 88b101401..e4a70cefc 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -349,44 +349,87 @@ static void cx18_vbi_setup(struct cx18_stream *s) /* setup VBI registers */ cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); - /* determine number of lines and total number of VBI bytes. - A raw line takes 1444 bytes: 4 byte SAV code + 2 * 720 - A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal - header, 42 data bytes + checksum (to be confirmed) */ + /* + * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw + * VBI when the first analog capture channel starts, as once it starts + * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup + * (i.e. for the VBI capture channels). We also send it for each + * analog capture channel anyway just to make sure we get the proper + * behavior + */ if (raw) { lines = cx->vbi.count * 2; } else { - lines = cx->is_60hz ? 24 : 38; - if (cx->is_60hz) - lines += 2; + /* + * For 525/60 systems, according to the VIP 2 & BT.656 std: + * The EAV RP code's Field bit toggles on line 4, a few lines + * after the Vertcal Blank bit has already toggled. + * Tell the encoder to capture 21-4+1=18 lines per field, + * since we want lines 10 through 21. + * + * FIXME - revisit for 625/50 systems + */ + lines = cx->is_60hz ? (21 - 4 + 1) * 2 : 38; } - cx->vbi.enc_size = lines * - (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); - data[0] = s->handle; /* Lines per field */ data[1] = (lines / 2) | ((lines / 2) << 16); /* bytes per line */ - data[2] = (raw ? cx->vbi.raw_decoder_line_size - : cx->vbi.sliced_decoder_line_size); + data[2] = (raw ? vbi_active_samples + : (cx->is_60hz ? vbi_hblank_samples_60Hz + : vbi_hblank_samples_50Hz)); /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ data[3] = 1; - /* Setup VBI for the cx25840 digitizer */ + /* + * Set the SAV/EAV RP codes to look for as start/stop points + * when in VIP-1.1 mode + */ if (raw) { + /* + * Start codes for beginning of "active" line in vertical blank + * 0x20 ( VerticalBlank ) + * 0x60 ( EvenField VerticalBlank ) + */ data[4] = 0x20602060; + /* + * End codes for end of "active" raw lines and regular lines + * 0x30 ( VerticalBlank HorizontalBlank) + * 0x70 ( EvenField VerticalBlank HorizontalBlank) + * 0x90 (Task HorizontalBlank) + * 0xd0 (Task EvenField HorizontalBlank) + */ data[5] = 0x307090d0; } else { + /* + * End codes for active video, we want data in the hblank region + * 0xb0 (Task 0 VerticalBlank HorizontalBlank) + * 0xf0 (Task EvenField VerticalBlank HorizontalBlank) + * + * Since the V bit is only allowed to toggle in the EAV RP code, + * just before the first active region line, these two + * are problematic: + * 0x90 (Task HorizontalBlank) + * 0xd0 (Task EvenField HorizontalBlank) + * + * We have set the digitzer to consider the first active line + * as part of VerticalBlank as well so we don't have to look for + * these problem codes nor lose the last line of sliced data. + */ data[4] = 0xB0F0B0F0; + /* + * Start codes for beginning of active line in vertical blank + * 0xa0 (Task VerticalBlank ) + * 0xe0 (Task EvenField VerticalBlank ) + */ data[5] = 0xA0E0A0E0; } CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", data[0], data[1], data[2], data[3], data[4], data[5]); - if (s->type == CX18_ENC_STREAM_TYPE_VBI) - cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); + cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); } struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, @@ -435,8 +478,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) u32 data[MAX_MB_ARGUMENTS]; struct cx18 *cx = s->cx; struct cx18_buffer *buf; - int ts = 0; int captype = 0; + struct cx18_api_func_private priv; if (s->video_dev == NULL && s->dvb.enabled == 0) return -EINVAL; @@ -454,7 +497,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) case CX18_ENC_STREAM_TYPE_TS: captype = CAPTURE_CHANNEL_TYPE_TS; - ts = 1; break; case CX18_ENC_STREAM_TYPE_YUV: captype = CAPTURE_CHANNEL_TYPE_YUV; @@ -463,8 +505,16 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) captype = CAPTURE_CHANNEL_TYPE_PCM; break; case CX18_ENC_STREAM_TYPE_VBI: +#ifdef CX18_ENCODER_PARSES_SLICED captype = cx18_raw_vbi(cx) ? CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI; +#else + /* + * Currently we set things up so that Sliced VBI from the + * digitizer is handled as Raw VBI by the encoder + */ + captype = CAPTURE_CHANNEL_TYPE_VBI; +#endif cx->vbi.frame = 0; cx->vbi.inserted_frame = 0; memset(cx->vbi.sliced_mpeg_size, @@ -474,10 +524,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) return -EINVAL; } - /* mute/unmute video */ - cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, - s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)); - /* Clear Streamoff flags in case left from last capture */ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); @@ -485,31 +531,63 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) s->handle = data[0]; cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); - if (atomic_read(&cx->ana_capturing) == 0 && !ts) { - struct cx18_api_func_private priv; - - /* Stuff from Windows, we don't know what it is */ + /* + * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and + * set up all the parameters, as it is not obvious which parameters the + * firmware shares across capture channel types and which it does not. + * + * Some of the cx18_vapi() calls below apply to only certain capture + * channel types. We're hoping there's no harm in calling most of them + * anyway, as long as the values are all consistent. Setting some + * shared parameters will have no effect once an analog capture channel + * has started streaming. + */ + if (captype != CAPTURE_CHANNEL_TYPE_TS) { cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); - cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12); + /* + * Audio related reset according to + * Documentation/video4linux/cx2341x/fw-encoder-api.txt + */ + if (atomic_read(&cx->ana_capturing) == 0) + cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, + s->handle, 12); + + /* + * Number of lines for Field 1 & Field 2 according to + * Documentation/video4linux/cx2341x/fw-encoder-api.txt + * Field 1 is 312 for 625 line systems in BT.656 + * Field 2 is 313 for 625 line systems in BT.656 + */ cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, - s->handle, cx->digitizer, cx->digitizer); + s->handle, 312, 313); - /* Setup VBI */ if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) cx18_vbi_setup(s); - /* assign program index info. - Mask 7: select I/P/B, Num_req: 400 max */ + /* + * assign program index info. + * Mask 7: select I/P/B, Num_req: 400 max + * FIXME - currently we have this hardcoded as disabled + */ cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0); - /* Setup API for Stream */ + /* 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); + + /* + * When starting a capture and we're set for radio, + * ensure the video is muted, despite the user control. + */ + if (!cx->params.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); } if (atomic_read(&cx->tot_capturing) == 0) { @@ -553,7 +631,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) } /* you're live! sit back and await interrupts :) */ - if (!ts) + if (captype != CAPTURE_CHANNEL_TYPE_TS) atomic_inc(&cx->ana_capturing); atomic_inc(&cx->tot_capturing); return 0; diff --git a/linux/drivers/media/video/cx18/cx18-vbi.c b/linux/drivers/media/video/cx18/cx18-vbi.c index fb595bd54..8e6f4d4af 100644 --- a/linux/drivers/media/video/cx18/cx18-vbi.c +++ b/linux/drivers/media/video/cx18/cx18-vbi.c @@ -27,6 +27,16 @@ #include "cx18-queue.h" #include "cx18-av-core.h" +/* + * Raster Reference/Protection (RP) bytes, used in Start/End Active + * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start + * of VBI sample or VBI ancilliary data regions in the digitial ratser line. + * + * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0 + */ +static const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 }; /* __V_, _FV_ */ +static const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */ + static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) { int line = 0; @@ -34,10 +44,17 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) u32 linemask[2] = { 0, 0 }; unsigned short size; static const u8 mpeg_hdr_data[] = { - 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66, - 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff, - 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80, - 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff + /* MPEG-2 Program Pack */ + 0x00, 0x00, 0x01, 0xba, /* Prog Pack start code */ + 0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */ + 0x01, 0xd1, 0xd3, /* Mux Rate, markers */ + 0xfa, 0xff, 0xff, /* Res, Suff cnt, Stuff */ + /* MPEG-2 Private Stream 1 PES Packet */ + 0x00, 0x00, 0x01, 0xbd, /* Priv Stream 1 start */ + 0x00, 0x1a, /* length */ + 0x84, 0x80, 0x07, /* flags, hdr data len */ + 0x21, 0x00, 0x5d, 0x63, 0xa7, /* PTS, markers */ + 0xff, 0xff /* stuffing */ }; const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */ int idx = cx->vbi.frame % CX18_VBI_FRAMES; @@ -71,7 +88,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) memcpy(dst + sd + 4, dst + sd + 12, line * 43); size = 4 + ((43 * line + 3) & ~3); } else { - memcpy(dst + sd, "cx0", 4); + memcpy(dst + sd, "itv0", 4); memcpy(dst + sd + 4, &linemask[0], 8); size = 12 + ((43 * line + 3) & ~3); } @@ -86,14 +103,13 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) } /* Compress raw VBI format, removes leading SAV codes and surplus space - after the field. - Returns new compressed size. */ + after the frame. Returns new compressed size. */ static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size) { - u32 line_size = cx->vbi.raw_decoder_line_size; - u32 lines = cx->vbi.count; - u8 sav1 = cx->vbi.raw_decoder_sav_odd_field; - u8 sav2 = cx->vbi.raw_decoder_sav_even_field; + u32 line_size = vbi_active_samples; + u32 lines = cx->vbi.count * 2; + u8 sav1 = raw_vbi_sav_rp[0]; + u8 sav2 = raw_vbi_sav_rp[1]; u8 *q = buf; u8 *p; int i; @@ -115,15 +131,16 @@ static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size) /* Compressed VBI format, all found sliced blocks put next to one another Returns new compressed size */ static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf, - u32 size, u8 sav) + u32 size, u8 eav) { - u32 line_size = cx->vbi.sliced_decoder_line_size; struct v4l2_decode_vbi_line vbi; int i; + u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz + : vbi_hblank_samples_50Hz; /* find the first valid line */ for (i = 0; i < size; i++, buf++) { - if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav) + if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == eav) break; } @@ -133,8 +150,8 @@ static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf, for (i = 0; i < size / line_size; i++) { u8 *p = buf + i * line_size; - /* Look for SAV code */ - if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) + /* Look for EAV code */ + if (p[0] != 0xff || p[1] || p[2] || p[3] != eav) continue; vbi.p = p + 4; cx18_av_cmd(cx, VIDIOC_INT_DECODE_VBI_LINE, &vbi); @@ -150,51 +167,78 @@ static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf, } void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, - u64 pts_stamp, int streamtype) + int streamtype) { u8 *p = (u8 *) buf->buf; + u32 *q = (u32 *) buf->buf; u32 size = buf->bytesused; + u32 pts; int lines; if (streamtype != CX18_ENC_STREAM_TYPE_VBI) return; + /* + * The CX23418 sends us data that is 32 bit LE swapped, but we want + * the raw VBI bytes in the order they were in the raster line + */ + cx18_buf_swap(buf); + + /* + * The CX23418 provides a 12 byte header in it's raw VBI buffers to us: + * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp?] + */ + /* Raw VBI data */ if (cx18_raw_vbi(cx)) { u8 type; - cx18_buf_swap(buf); - - /* Skip 12 bytes of header that gets stuffed in */ + /* + * We've set up to get a frame's worth of VBI data at a time. + * Skip 12 bytes of header prefixing the first field. + */ size -= 12; memcpy(p, &buf->buf[12], size); type = p[3]; + /* Extrapolate the last 12 bytes of the frame's last line */ + memset(&p[size], (int) p[size - 1], 12); + size += 12; + size = buf->bytesused = compress_raw_buf(cx, p, size); - /* second field of the frame? */ - if (type == cx->vbi.raw_decoder_sav_even_field) { - /* Dirty hack needed for backwards - compatibility of old VBI software. */ - p += size - 4; - memcpy(p, &cx->vbi.frame, 4); - cx->vbi.frame++; - } + /* + * Hack needed for compatibility with old VBI software. + * Write the frame # at the last 4 bytes of the frame + */ + p += size - 4; + memcpy(p, &cx->vbi.frame, 4); + cx->vbi.frame++; return; } /* Sliced VBI data with data insertion */ - cx18_buf_swap(buf); + + pts = (be32_to_cpu(q[0] == 0x3fffffff)) ? be32_to_cpu(q[2]) : 0; + + /* + * For calls to compress_sliced_buf(), ensure there are an integral + * number of lines by shifting the real data up over the 12 bytes header + * that got stuffed in. + * FIXME - there's a smarter way to do this with pointers, but for some + * reason I can't get it to work correctly right now. + */ + memcpy(p, &buf->buf[12], size-12); /* first field */ - lines = compress_sliced_buf(cx, 0, p, size / 2, - cx->vbi.sliced_decoder_sav_odd_field); - /* second field */ - /* experimentation shows that the second half does not always - begin at the exact address. So start a bit earlier - (hence 32). */ + lines = compress_sliced_buf(cx, 0, p, size / 2, sliced_vbi_eav_rp[0]); + /* + * second field + * In case the second half does not always begin at the exact address, + * start a bit earlier (hence 32). + */ lines = compress_sliced_buf(cx, lines, p + size / 2 - 32, - size / 2 + 32, cx->vbi.sliced_decoder_sav_even_field); + size / 2 + 32, sliced_vbi_eav_rp[1]); /* always return at least one empty line */ if (lines == 0) { cx->vbi.sliced_data[0].id = 0; @@ -206,6 +250,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, memcpy(p, &cx->vbi.sliced_data[0], size); if (cx->vbi.insert_mpeg) - copy_vbi_data(cx, lines, pts_stamp); + copy_vbi_data(cx, lines, pts); cx->vbi.frame++; } diff --git a/linux/drivers/media/video/cx18/cx18-vbi.h b/linux/drivers/media/video/cx18/cx18-vbi.h index c56ff7d28..e7e1ae427 100644 --- a/linux/drivers/media/video/cx18/cx18-vbi.h +++ b/linux/drivers/media/video/cx18/cx18-vbi.h @@ -22,5 +22,5 @@ */ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, - u64 pts_stamp, int streamtype); + int streamtype); int cx18_used_line(struct cx18 *cx, int line, int field); |