summaryrefslogtreecommitdiff
path: root/drivers/media/common
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/common')
-rw-r--r--drivers/media/common/Kconfig21
-rw-r--r--drivers/media/common/Makefile4
-rw-r--r--drivers/media/common/b2c2/Kconfig23
-rw-r--r--drivers/media/common/b2c2/Makefile8
-rw-r--r--drivers/media/common/b2c2/flexcop-common.h185
-rw-r--r--drivers/media/common/b2c2/flexcop-eeprom.c147
-rw-r--r--drivers/media/common/b2c2/flexcop-fe-tuner.c678
-rw-r--r--drivers/media/common/b2c2/flexcop-hw-filter.c232
-rw-r--r--drivers/media/common/b2c2/flexcop-i2c.c288
-rw-r--r--drivers/media/common/b2c2/flexcop-misc.c86
-rw-r--r--drivers/media/common/b2c2/flexcop-reg.h166
-rw-r--r--drivers/media/common/b2c2/flexcop-sram.c363
-rw-r--r--drivers/media/common/b2c2/flexcop.c325
-rw-r--r--drivers/media/common/b2c2/flexcop.h29
-rw-r--r--drivers/media/common/b2c2/flexcop_ibi_value_be.h455
-rw-r--r--drivers/media/common/b2c2/flexcop_ibi_value_le.h455
-rw-r--r--drivers/media/common/btcx-risc.h28
-rw-r--r--drivers/media/common/cx2341x.c1756
-rw-r--r--drivers/media/common/cypress_firmware.c132
-rw-r--r--drivers/media/common/cypress_firmware.h28
-rw-r--r--drivers/media/common/saa7146/Kconfig9
-rw-r--r--drivers/media/common/saa7146/Makefile5
-rw-r--r--drivers/media/common/saa7146/saa7146_core.c587
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c648
-rw-r--r--drivers/media/common/saa7146/saa7146_hlp.c1048
-rw-r--r--drivers/media/common/saa7146/saa7146_i2c.c423
-rw-r--r--drivers/media/common/saa7146/saa7146_vbi.c498
-rw-r--r--drivers/media/common/saa7146/saa7146_video.c1309
-rw-r--r--drivers/media/common/siano/Kconfig32
-rw-r--r--drivers/media/common/siano/Makefile16
-rw-r--r--drivers/media/common/siano/sms-cards.c360
-rw-r--r--drivers/media/common/siano/sms-cards.h139
-rw-r--r--drivers/media/common/siano/smscoreapi.c2175
-rw-r--r--drivers/media/common/siano/smscoreapi.h1180
-rw-r--r--drivers/media/common/siano/smsdvb-debugfs.c549
-rw-r--r--drivers/media/common/siano/smsdvb-main.c1244
-rw-r--r--drivers/media/common/siano/smsdvb.h130
-rw-r--r--drivers/media/common/siano/smsendian.c103
-rw-r--r--drivers/media/common/siano/smsendian.h32
-rw-r--r--drivers/media/common/siano/smsir.c113
-rw-r--r--drivers/media/common/siano/smsir.h63
-rw-r--r--drivers/media/common/tveeprom.c798
42 files changed, 16870 insertions, 0 deletions
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
new file mode 100644
index 000000000..21154dd87
--- /dev/null
+++ b/drivers/media/common/Kconfig
@@ -0,0 +1,21 @@
+# Used by common drivers, when they need to ask questions
+config MEDIA_COMMON_OPTIONS
+ bool
+
+comment "common driver options"
+ depends on MEDIA_COMMON_OPTIONS
+
+config VIDEO_CX2341X
+ tristate
+
+config VIDEO_TVEEPROM
+ tristate
+ depends on I2C
+
+config CYPRESS_FIRMWARE
+ tristate "Cypress firmware helper routines"
+ depends on USB
+
+source "drivers/media/common/b2c2/Kconfig"
+source "drivers/media/common/saa7146/Kconfig"
+source "drivers/media/common/siano/Kconfig"
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile
new file mode 100644
index 000000000..89b795df2
--- /dev/null
+++ b/drivers/media/common/Makefile
@@ -0,0 +1,4 @@
+obj-y += b2c2/ saa7146/ siano/
+obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
+obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
+obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o
diff --git a/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig
new file mode 100644
index 000000000..a8c6cdfaa
--- /dev/null
+++ b/drivers/media/common/b2c2/Kconfig
@@ -0,0 +1,23 @@
+config DVB_B2C2_FLEXCOP
+ tristate
+ depends on DVB_CORE && I2C
+ depends on DVB_B2C2_FLEXCOP_PCI || DVB_B2C2_FLEXCOP_USB
+ default y
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_BCM3510 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUNER_ITD1000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUNER_CX24113 if MEDIA_SUBDRV_AUTOSELECT
+
+# Selected via the PCI or USB flexcop drivers
+config DVB_B2C2_FLEXCOP_DEBUG
+ bool
diff --git a/drivers/media/common/b2c2/Makefile b/drivers/media/common/b2c2/Makefile
new file mode 100644
index 000000000..24993a5b3
--- /dev/null
+++ b/drivers/media/common/b2c2/Makefile
@@ -0,0 +1,8 @@
+b2c2-flexcop-objs += flexcop.o flexcop-fe-tuner.o flexcop-i2c.o
+b2c2-flexcop-objs += flexcop-sram.o flexcop-eeprom.o flexcop-misc.o
+b2c2-flexcop-objs += flexcop-hw-filter.o
+obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
diff --git a/drivers/media/common/b2c2/flexcop-common.h b/drivers/media/common/b2c2/flexcop-common.h
new file mode 100644
index 000000000..437912e49
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop-common.h
@@ -0,0 +1,185 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-common.h - common header file for device-specific source files
+ * see flexcop.c for copyright information
+ */
+#ifndef __FLEXCOP_COMMON_H__
+#define __FLEXCOP_COMMON_H__
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+
+#include "flexcop-reg.h"
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+#include "dvb_frontend.h"
+
+#define FC_MAX_FEED 256
+
+#ifndef FC_LOG_PREFIX
+#warning please define a log prefix for your file, using a default one
+#define FC_LOG_PREFIX "b2c2-undef"
+#endif
+
+/* Steal from usb.h */
+#undef err
+#define err(format, arg...) \
+ printk(KERN_ERR FC_LOG_PREFIX ": " format "\n" , ## arg)
+#undef info
+#define info(format, arg...) \
+ printk(KERN_INFO FC_LOG_PREFIX ": " format "\n" , ## arg)
+#undef warn
+#define warn(format, arg...) \
+ printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg)
+
+struct flexcop_dma {
+ struct pci_dev *pdev;
+
+ u8 *cpu_addr0;
+ dma_addr_t dma_addr0;
+ u8 *cpu_addr1;
+ dma_addr_t dma_addr1;
+ u32 size; /* size of each address in bytes */
+};
+
+struct flexcop_i2c_adapter {
+ struct flexcop_device *fc;
+ struct i2c_adapter i2c_adap;
+
+ u8 no_base_addr;
+ flexcop_i2c_port_t port;
+};
+
+/* Control structure for data definitions that are common to
+ * the B2C2-based PCI and USB devices.
+ */
+struct flexcop_device {
+ /* general */
+ struct device *dev; /* for firmware_class */
+
+#define FC_STATE_DVB_INIT 0x01
+#define FC_STATE_I2C_INIT 0x02
+#define FC_STATE_FE_INIT 0x04
+ int init_state;
+
+ /* device information */
+ int has_32_hw_pid_filter;
+ flexcop_revision_t rev;
+ flexcop_device_type_t dev_type;
+ flexcop_bus_t bus_type;
+
+ /* dvb stuff */
+ struct dvb_adapter dvb_adapter;
+ struct dvb_frontend *fe;
+ struct dvb_net dvbnet;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ int (*fe_sleep) (struct dvb_frontend *);
+
+ struct flexcop_i2c_adapter fc_i2c_adap[3];
+ struct mutex i2c_mutex;
+ struct module *owner;
+
+ /* options and status */
+ int extra_feedcount;
+ int feedcount;
+ int pid_filtering;
+ int fullts_streaming_state;
+
+ /* bus specific callbacks */
+ flexcop_ibi_value(*read_ibi_reg) (struct flexcop_device *,
+ flexcop_ibi_register);
+ int (*write_ibi_reg) (struct flexcop_device *,
+ flexcop_ibi_register, flexcop_ibi_value);
+ int (*i2c_request) (struct flexcop_i2c_adapter *,
+ flexcop_access_op_t, u8 chipaddr, u8 addr, u8 *buf, u16 len);
+ int (*stream_control) (struct flexcop_device *, int);
+ int (*get_mac_addr) (struct flexcop_device *fc, int extended);
+ void *bus_specific;
+};
+
+/* exported prototypes */
+
+/* from flexcop.c */
+void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len);
+void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no);
+
+struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len);
+void flexcop_device_kfree(struct flexcop_device *);
+
+int flexcop_device_initialize(struct flexcop_device *);
+void flexcop_device_exit(struct flexcop_device *fc);
+void flexcop_reset_block_300(struct flexcop_device *fc);
+
+/* from flexcop-dma.c */
+int flexcop_dma_allocate(struct pci_dev *pdev,
+ struct flexcop_dma *dma, u32 size);
+void flexcop_dma_free(struct flexcop_dma *dma);
+
+int flexcop_dma_control_timer_irq(struct flexcop_device *fc,
+ flexcop_dma_index_t no, int onoff);
+int flexcop_dma_control_size_irq(struct flexcop_device *fc,
+ flexcop_dma_index_t no, int onoff);
+int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma,
+ flexcop_dma_index_t dma_idx);
+int flexcop_dma_xfer_control(struct flexcop_device *fc,
+ flexcop_dma_index_t dma_idx, flexcop_dma_addr_index_t index,
+ int onoff);
+int flexcop_dma_config_timer(struct flexcop_device *fc,
+ flexcop_dma_index_t dma_idx, u8 cycles);
+
+/* from flexcop-eeprom.c */
+/* the PCI part uses this call to get the MAC address, the USB part has its own */
+int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended);
+
+/* from flexcop-i2c.c */
+/* the PCI part uses this a i2c_request callback, whereas the usb part has its own
+ * one. We have it in flexcop-i2c.c, because it is going via the actual
+ * I2C-channel of the flexcop.
+ */
+int flexcop_i2c_request(struct flexcop_i2c_adapter*, flexcop_access_op_t,
+ u8 chipaddr, u8 addr, u8 *buf, u16 len);
+
+/* from flexcop-sram.c */
+int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest,
+ flexcop_sram_dest_target_t target);
+void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s);
+void flexcop_sram_ctrl(struct flexcop_device *fc,
+ int usb_wan, int sramdma, int maximumfill);
+
+/* global prototypes for the flexcop-chip */
+/* from flexcop-fe-tuner.c */
+int flexcop_frontend_init(struct flexcop_device *fc);
+void flexcop_frontend_exit(struct flexcop_device *fc);
+
+/* from flexcop-i2c.c */
+int flexcop_i2c_init(struct flexcop_device *fc);
+void flexcop_i2c_exit(struct flexcop_device *fc);
+
+/* from flexcop-sram.c */
+int flexcop_sram_init(struct flexcop_device *fc);
+
+/* from flexcop-misc.c */
+void flexcop_determine_revision(struct flexcop_device *fc);
+void flexcop_device_name(struct flexcop_device *fc,
+ const char *prefix, const char *suffix);
+void flexcop_dump_reg(struct flexcop_device *fc,
+ flexcop_ibi_register reg, int num);
+
+/* from flexcop-hw-filter.c */
+int flexcop_pid_feed_control(struct flexcop_device *fc,
+ struct dvb_demux_feed *dvbdmxfeed, int onoff);
+void flexcop_hw_filter_init(struct flexcop_device *fc);
+
+void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff);
+
+void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]);
+void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff);
+
+#endif
diff --git a/drivers/media/common/b2c2/flexcop-eeprom.c b/drivers/media/common/b2c2/flexcop-eeprom.c
new file mode 100644
index 000000000..a25373a9b
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop-eeprom.c
@@ -0,0 +1,147 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-eeprom.c - eeprom access methods (currently only MAC address reading)
+ * see flexcop.c for copyright information
+ */
+#include "flexcop.h"
+
+#if 0
+/*EEPROM (Skystar2 has one "24LC08B" chip on board) */
+static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len)
+{
+ return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len);
+}
+
+static int eeprom_lrc_write(struct adapter *adapter, u32 addr,
+ u32 len, u8 *wbuf, u8 *rbuf, int retries)
+{
+int i;
+
+for (i = 0; i < retries; i++) {
+ if (eeprom_write(adapter, addr, wbuf, len) == len) {
+ if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* These functions could be used to unlock SkyStar2 cards. */
+
+static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len)
+{
+ u8 rbuf[20];
+ u8 wbuf[20];
+
+ if (len != 16)
+ return 0;
+
+ memcpy(wbuf, key, len);
+ wbuf[16] = 0;
+ wbuf[17] = 0;
+ wbuf[18] = 0;
+ wbuf[19] = calc_lrc(wbuf, 19);
+ return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4);
+}
+
+static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len)
+{
+ u8 buf[20];
+
+ if (len != 16)
+ return 0;
+
+ if (eeprom_lrc_read(adapter, 0x3e4, 20, buf, 4) == 0)
+ return 0;
+
+ memcpy(key, buf, len);
+ return 1;
+}
+
+static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac)
+{
+ u8 tmp[8];
+
+ if (type != 0) {
+ tmp[0] = mac[0];
+ tmp[1] = mac[1];
+ tmp[2] = mac[2];
+ tmp[3] = mac[5];
+ tmp[4] = mac[6];
+ tmp[5] = mac[7];
+ } else {
+ tmp[0] = mac[0];
+ tmp[1] = mac[1];
+ tmp[2] = mac[2];
+ tmp[3] = mac[3];
+ tmp[4] = mac[4];
+ tmp[5] = mac[5];
+ }
+
+ tmp[6] = 0;
+ tmp[7] = calc_lrc(tmp, 7);
+
+ if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8)
+ return 1;
+ return 0;
+}
+
+static int flexcop_eeprom_read(struct flexcop_device *fc,
+ u16 addr, u8 *buf, u16 len)
+{
+ return fc->i2c_request(fc,FC_READ,FC_I2C_PORT_EEPROM,0x50,addr,buf,len);
+}
+
+#endif
+
+static u8 calc_lrc(u8 *buf, int len)
+{
+ int i;
+ u8 sum = 0;
+ for (i = 0; i < len; i++)
+ sum = sum ^ buf[i];
+ return sum;
+}
+
+static int flexcop_eeprom_request(struct flexcop_device *fc,
+ flexcop_access_op_t op, u16 addr, u8 *buf, u16 len, int retries)
+{
+ int i,ret = 0;
+ u8 chipaddr = 0x50 | ((addr >> 8) & 3);
+ for (i = 0; i < retries; i++) {
+ ret = fc->i2c_request(&fc->fc_i2c_adap[1], op, chipaddr,
+ addr & 0xff, buf, len);
+ if (ret == 0)
+ break;
+ }
+ return ret;
+}
+
+static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr,
+ u8 *buf, u16 len, int retries)
+{
+ int ret = flexcop_eeprom_request(fc, FC_READ, addr, buf, len, retries);
+ if (ret == 0)
+ if (calc_lrc(buf, len - 1) != buf[len - 1])
+ ret = -EINVAL;
+ return ret;
+}
+
+/* JJ's comment about extended == 1: it is not presently used anywhere but was
+ * added to the low-level functions for possible support of EUI64 */
+int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended)
+{
+ u8 buf[8];
+ int ret = 0;
+
+ if ((ret = flexcop_eeprom_lrc_read(fc,0x3f8,buf,8,4)) == 0) {
+ if (extended != 0) {
+ err("TODO: extended (EUI64) MAC addresses aren't "
+ "completely supported yet");
+ ret = -EINVAL;
+ } else
+ memcpy(fc->dvb_adapter.proposed_mac,buf,6);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(flexcop_eeprom_check_mac_addr);
diff --git a/drivers/media/common/b2c2/flexcop-fe-tuner.c b/drivers/media/common/b2c2/flexcop-fe-tuner.c
new file mode 100644
index 000000000..7415aec33
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop-fe-tuner.c
@@ -0,0 +1,678 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-fe-tuner.c - methods for frontend attachment and DiSEqC controlling
+ * see flexcop.c for copyright information
+ */
+#include <media/tuner.h>
+#include "flexcop.h"
+#include "mt312.h"
+#include "stv0299.h"
+#include "s5h1420.h"
+#include "itd1000.h"
+#include "cx24113.h"
+#include "cx24123.h"
+#include "isl6421.h"
+#include "mt352.h"
+#include "bcm3510.h"
+#include "nxt200x.h"
+#include "dvb-pll.h"
+#include "lgdt330x.h"
+#include "tuner-simple.h"
+#include "stv0297.h"
+
+
+/* Can we use the specified front-end? Remember that if we are compiled
+ * into the kernel we can't call code that's in modules. */
+#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \
+ (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE)))
+
+/* lnb control */
+#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299)
+static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct flexcop_device *fc = fe->dvb->priv;
+ flexcop_ibi_value v;
+ deb_tuner("polarity/voltage = %u\n", voltage);
+
+ v = fc->read_ibi_reg(fc, misc_204);
+ switch (voltage) {
+ case SEC_VOLTAGE_OFF:
+ v.misc_204.ACPI1_sig = 1;
+ break;
+ case SEC_VOLTAGE_13:
+ v.misc_204.ACPI1_sig = 0;
+ v.misc_204.LNB_L_H_sig = 0;
+ break;
+ case SEC_VOLTAGE_18:
+ v.misc_204.ACPI1_sig = 0;
+ v.misc_204.LNB_L_H_sig = 1;
+ break;
+ default:
+ err("unknown SEC_VOLTAGE value");
+ return -EINVAL;
+ }
+ return fc->write_ibi_reg(fc, misc_204, v);
+}
+#endif
+
+#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312)
+static int flexcop_sleep(struct dvb_frontend* fe)
+{
+ struct flexcop_device *fc = fe->dvb->priv;
+ if (fc->fe_sleep)
+ return fc->fe_sleep(fe);
+ return 0;
+}
+#endif
+
+/* SkyStar2 DVB-S rev 2.3 */
+#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL)
+static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
+ struct flexcop_device *fc = fe->dvb->priv;
+ flexcop_ibi_value v;
+ u16 ax;
+ v.raw = 0;
+ deb_tuner("tone = %u\n",tone);
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ ax = 0x01ff;
+ break;
+ case SEC_TONE_OFF:
+ ax = 0;
+ break;
+ default:
+ err("unknown SEC_TONE value");
+ return -EINVAL;
+ }
+
+ v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */
+ v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax;
+ v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax == 0 ? 0x1ff : ax;
+ return fc->write_ibi_reg(fc,lnb_switch_freq_200,v);
+}
+
+static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
+{
+ flexcop_set_tone(fe, SEC_TONE_ON);
+ udelay(data ? 500 : 1000);
+ flexcop_set_tone(fe, SEC_TONE_OFF);
+ udelay(data ? 1000 : 500);
+}
+
+static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
+{
+ int i, par = 1, d;
+ for (i = 7; i >= 0; i--) {
+ d = (data >> i) & 1;
+ par ^= d;
+ flexcop_diseqc_send_bit(fe, d);
+ }
+ flexcop_diseqc_send_bit(fe, par);
+}
+
+static int flexcop_send_diseqc_msg(struct dvb_frontend *fe,
+ int len, u8 *msg, unsigned long burst)
+{
+ int i;
+
+ flexcop_set_tone(fe, SEC_TONE_OFF);
+ mdelay(16);
+
+ for (i = 0; i < len; i++)
+ flexcop_diseqc_send_byte(fe,msg[i]);
+ mdelay(16);
+
+ if (burst != -1) {
+ if (burst)
+ flexcop_diseqc_send_byte(fe, 0xff);
+ else {
+ flexcop_set_tone(fe, SEC_TONE_ON);
+ mdelay(12);
+ udelay(500);
+ flexcop_set_tone(fe, SEC_TONE_OFF);
+ }
+ msleep(20);
+ }
+ return 0;
+}
+
+static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *cmd)
+{
+ return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
+}
+
+static int flexcop_diseqc_send_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t minicmd)
+{
+ return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
+}
+
+static struct mt312_config skystar23_samsung_tbdu18132_config = {
+ .demod_address = 0x0e,
+};
+
+static int skystar2_rev23_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ struct dvb_frontend_ops *ops;
+
+ fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c);
+ if (!fc->fe)
+ return 0;
+
+ if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
+ DVB_PLL_SAMSUNG_TBDU18132))
+ return 0;
+
+ ops = &fc->fe->ops;
+ ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
+ ops->diseqc_send_burst = flexcop_diseqc_send_burst;
+ ops->set_tone = flexcop_set_tone;
+ ops->set_voltage = flexcop_set_voltage;
+ fc->fe_sleep = ops->sleep;
+ ops->sleep = flexcop_sleep;
+ return 1;
+}
+#else
+#define skystar2_rev23_attach NULL
+#endif
+
+/* SkyStar2 DVB-S rev 2.6 */
+#if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL)
+static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe,
+ u32 srate, u32 ratio)
+{
+ u8 aclk = 0;
+ u8 bclk = 0;
+
+ if (srate < 1500000) {
+ aclk = 0xb7; bclk = 0x47;
+ } else if (srate < 3000000) {
+ aclk = 0xb7; bclk = 0x4b;
+ } else if (srate < 7000000) {
+ aclk = 0xb7; bclk = 0x4f;
+ } else if (srate < 14000000) {
+ aclk = 0xb7; bclk = 0x53;
+ } else if (srate < 30000000) {
+ aclk = 0xb6; bclk = 0x53;
+ } else if (srate < 45000000) {
+ aclk = 0xb4; bclk = 0x51;
+ }
+
+ stv0299_writereg(fe, 0x13, aclk);
+ stv0299_writereg(fe, 0x14, bclk);
+ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+ stv0299_writereg(fe, 0x21, ratio & 0xf0);
+ return 0;
+}
+
+static u8 samsung_tbmu24112_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x30,
+ 0x03, 0x00,
+ 0x04, 0x7D,
+ 0x05, 0x35,
+ 0x06, 0x02,
+ 0x07, 0x00,
+ 0x08, 0xC3,
+ 0x0C, 0x00,
+ 0x0D, 0x81,
+ 0x0E, 0x23,
+ 0x0F, 0x12,
+ 0x10, 0x7E,
+ 0x11, 0x84,
+ 0x12, 0xB9,
+ 0x13, 0x88,
+ 0x14, 0x89,
+ 0x15, 0xC9,
+ 0x16, 0x00,
+ 0x17, 0x5C,
+ 0x18, 0x00,
+ 0x19, 0x00,
+ 0x1A, 0x00,
+ 0x1C, 0x00,
+ 0x1D, 0x00,
+ 0x1E, 0x00,
+ 0x1F, 0x3A,
+ 0x20, 0x2E,
+ 0x21, 0x80,
+ 0x22, 0xFF,
+ 0x23, 0xC1,
+ 0x28, 0x00,
+ 0x29, 0x1E,
+ 0x2A, 0x14,
+ 0x2B, 0x0F,
+ 0x2C, 0x09,
+ 0x2D, 0x05,
+ 0x31, 0x1F,
+ 0x32, 0x19,
+ 0x33, 0xFE,
+ 0x34, 0x93,
+ 0xff, 0xff,
+};
+
+static struct stv0299_config samsung_tbmu24112_config = {
+ .demod_address = 0x68,
+ .inittab = samsung_tbmu24112_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0299_LOCKOUTPUT_LK,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
+};
+
+static int skystar2_rev26_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c);
+ if (!fc->fe)
+ return 0;
+
+ if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
+ DVB_PLL_SAMSUNG_TBMU24112))
+ return 0;
+
+ fc->fe->ops.set_voltage = flexcop_set_voltage;
+ fc->fe_sleep = fc->fe->ops.sleep;
+ fc->fe->ops.sleep = flexcop_sleep;
+ return 1;
+
+}
+#else
+#define skystar2_rev26_attach NULL
+#endif
+
+/* SkyStar2 DVB-S rev 2.7 */
+#if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000)
+static struct s5h1420_config skystar2_rev2_7_s5h1420_config = {
+ .demod_address = 0x53,
+ .invert = 1,
+ .repeated_start_workaround = 1,
+ .serial_mpeg = 1,
+};
+
+static struct itd1000_config skystar2_rev2_7_itd1000_config = {
+ .i2c_address = 0x61,
+};
+
+static int skystar2_rev27_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ flexcop_ibi_value r108;
+ struct i2c_adapter *i2c_tuner;
+
+ /* enable no_base_addr - no repeated start when reading */
+ fc->fc_i2c_adap[0].no_base_addr = 1;
+ fc->fe = dvb_attach(s5h1420_attach, &skystar2_rev2_7_s5h1420_config,
+ i2c);
+ if (!fc->fe)
+ goto fail;
+
+ i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe);
+ if (!i2c_tuner)
+ goto fail;
+
+ fc->fe_sleep = fc->fe->ops.sleep;
+ fc->fe->ops.sleep = flexcop_sleep;
+
+ /* enable no_base_addr - no repeated start when reading */
+ fc->fc_i2c_adap[2].no_base_addr = 1;
+ if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
+ 0x08, 1, 1, false)) {
+ err("ISL6421 could NOT be attached");
+ goto fail_isl;
+ }
+ info("ISL6421 successfully attached");
+
+ /* the ITD1000 requires a lower i2c clock - is it a problem ? */
+ r108.raw = 0x00000506;
+ fc->write_ibi_reg(fc, tw_sm_c_108, r108);
+ if (!dvb_attach(itd1000_attach, fc->fe, i2c_tuner,
+ &skystar2_rev2_7_itd1000_config)) {
+ err("ITD1000 could NOT be attached");
+ /* Should i2c clock be restored? */
+ goto fail_isl;
+ }
+ info("ITD1000 successfully attached");
+
+ return 1;
+
+fail_isl:
+ fc->fc_i2c_adap[2].no_base_addr = 0;
+fail:
+ /* for the next devices we need it again */
+ fc->fc_i2c_adap[0].no_base_addr = 0;
+ return 0;
+}
+#else
+#define skystar2_rev27_attach NULL
+#endif
+
+/* SkyStar2 rev 2.8 */
+#if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113)
+static struct cx24123_config skystar2_rev2_8_cx24123_config = {
+ .demod_address = 0x55,
+ .dont_use_pll = 1,
+ .agc_callback = cx24113_agc_callback,
+};
+
+static const struct cx24113_config skystar2_rev2_8_cx24113_config = {
+ .i2c_addr = 0x54,
+ .xtal_khz = 10111,
+};
+
+static int skystar2_rev28_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ struct i2c_adapter *i2c_tuner;
+
+ fc->fe = dvb_attach(cx24123_attach, &skystar2_rev2_8_cx24123_config,
+ i2c);
+ if (!fc->fe)
+ return 0;
+
+ i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe);
+ if (!i2c_tuner)
+ return 0;
+
+ if (!dvb_attach(cx24113_attach, fc->fe, &skystar2_rev2_8_cx24113_config,
+ i2c_tuner)) {
+ err("CX24113 could NOT be attached");
+ return 0;
+ }
+ info("CX24113 successfully attached");
+
+ fc->fc_i2c_adap[2].no_base_addr = 1;
+ if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
+ 0x08, 0, 0, false)) {
+ err("ISL6421 could NOT be attached");
+ fc->fc_i2c_adap[2].no_base_addr = 0;
+ return 0;
+ }
+ info("ISL6421 successfully attached");
+ /* TODO on i2c_adap[1] addr 0x11 (EEPROM) there seems to be an
+ * IR-receiver (PIC16F818) - but the card has no input for that ??? */
+ return 1;
+}
+#else
+#define skystar2_rev28_attach NULL
+#endif
+
+/* AirStar DVB-T */
+#if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL)
+static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe)
+{
+ static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d };
+ static u8 mt352_reset[] = { 0x50, 0x80 };
+ static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 };
+ static u8 mt352_agc_cfg[] = { 0x67, 0x28, 0xa1 };
+ static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+ mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+ udelay(2000);
+ mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+ mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+ mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
+ mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+ return 0;
+}
+
+static struct mt352_config samsung_tdtc9251dh0_config = {
+ .demod_address = 0x0f,
+ .demod_init = samsung_tdtc9251dh0_demod_init,
+};
+
+static int airstar_dvbt_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c);
+ if (!fc->fe)
+ return 0;
+
+ return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
+ DVB_PLL_SAMSUNG_TDTC9251DH0);
+}
+#else
+#define airstar_dvbt_attach NULL
+#endif
+
+/* AirStar ATSC 1st generation */
+#if FE_SUPPORTED(BCM3510)
+static int flexcop_fe_request_firmware(struct dvb_frontend *fe,
+ const struct firmware **fw, char* name)
+{
+ struct flexcop_device *fc = fe->dvb->priv;
+ return reject_firmware(fw, name, fc->dev);
+}
+
+static struct bcm3510_config air2pc_atsc_first_gen_config = {
+ .demod_address = 0x0f,
+ .request_firmware = flexcop_fe_request_firmware,
+};
+
+static int airstar_atsc1_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c);
+ return fc->fe != NULL;
+}
+#else
+#define airstar_atsc1_attach NULL
+#endif
+
+/* AirStar ATSC 2nd generation */
+#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL)
+static struct nxt200x_config samsung_tbmv_config = {
+ .demod_address = 0x0a,
+};
+
+static int airstar_atsc2_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, i2c);
+ if (!fc->fe)
+ return 0;
+
+ return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
+ DVB_PLL_SAMSUNG_TBMV);
+}
+#else
+#define airstar_atsc2_attach NULL
+#endif
+
+/* AirStar ATSC 3rd generation */
+#if FE_SUPPORTED(LGDT330X)
+static struct lgdt330x_config air2pc_atsc_hd5000_config = {
+ .demod_address = 0x59,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x04,
+ .clock_polarity_flip = 1,
+};
+
+static int airstar_atsc3_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c);
+ if (!fc->fe)
+ return 0;
+
+ return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61,
+ TUNER_LG_TDVS_H06XF);
+}
+#else
+#define airstar_atsc3_attach NULL
+#endif
+
+/* CableStar2 DVB-C */
+#if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL)
+static u8 alps_tdee4_stv0297_inittab[] = {
+ 0x80, 0x01,
+ 0x80, 0x00,
+ 0x81, 0x01,
+ 0x81, 0x00,
+ 0x00, 0x48,
+ 0x01, 0x58,
+ 0x03, 0x00,
+ 0x04, 0x00,
+ 0x07, 0x00,
+ 0x08, 0x00,
+ 0x30, 0xff,
+ 0x31, 0x9d,
+ 0x32, 0xff,
+ 0x33, 0x00,
+ 0x34, 0x29,
+ 0x35, 0x55,
+ 0x36, 0x80,
+ 0x37, 0x6e,
+ 0x38, 0x9c,
+ 0x40, 0x1a,
+ 0x41, 0xfe,
+ 0x42, 0x33,
+ 0x43, 0x00,
+ 0x44, 0xff,
+ 0x45, 0x00,
+ 0x46, 0x00,
+ 0x49, 0x04,
+ 0x4a, 0x51,
+ 0x4b, 0xf8,
+ 0x52, 0x30,
+ 0x53, 0x06,
+ 0x59, 0x06,
+ 0x5a, 0x5e,
+ 0x5b, 0x04,
+ 0x61, 0x49,
+ 0x62, 0x0a,
+ 0x70, 0xff,
+ 0x71, 0x04,
+ 0x72, 0x00,
+ 0x73, 0x00,
+ 0x74, 0x0c,
+ 0x80, 0x20,
+ 0x81, 0x00,
+ 0x82, 0x30,
+ 0x83, 0x00,
+ 0x84, 0x04,
+ 0x85, 0x22,
+ 0x86, 0x08,
+ 0x87, 0x1b,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x90, 0x00,
+ 0x91, 0x04,
+ 0xa0, 0x86,
+ 0xa1, 0x00,
+ 0xa2, 0x00,
+ 0xb0, 0x91,
+ 0xb1, 0x0b,
+ 0xc0, 0x5b,
+ 0xc1, 0x10,
+ 0xc2, 0x12,
+ 0xd0, 0x02,
+ 0xd1, 0x00,
+ 0xd2, 0x00,
+ 0xd3, 0x00,
+ 0xd4, 0x02,
+ 0xd5, 0x00,
+ 0xde, 0x00,
+ 0xdf, 0x01,
+ 0xff, 0xff,
+};
+
+static struct stv0297_config alps_tdee4_stv0297_config = {
+ .demod_address = 0x1c,
+ .inittab = alps_tdee4_stv0297_inittab,
+};
+
+static int cablestar2_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ fc->fc_i2c_adap[0].no_base_addr = 1;
+ fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c);
+ if (!fc->fe)
+ goto fail;
+
+ /* This tuner doesn't use the stv0297's I2C gate, but instead the
+ * tuner is connected to a different flexcop I2C adapter. */
+ if (fc->fe->ops.i2c_gate_ctrl)
+ fc->fe->ops.i2c_gate_ctrl(fc->fe, 0);
+ fc->fe->ops.i2c_gate_ctrl = NULL;
+
+ if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61,
+ &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4))
+ goto fail;
+
+ return 1;
+
+fail:
+ /* Reset for next frontend to try */
+ fc->fc_i2c_adap[0].no_base_addr = 0;
+ return 0;
+}
+#else
+#define cablestar2_attach NULL
+#endif
+
+static struct {
+ flexcop_device_type_t type;
+ int (*attach)(struct flexcop_device *, struct i2c_adapter *);
+} flexcop_frontends[] = {
+ { FC_SKY_REV27, skystar2_rev27_attach },
+ { FC_SKY_REV28, skystar2_rev28_attach },
+ { FC_SKY_REV26, skystar2_rev26_attach },
+ { FC_AIR_DVBT, airstar_dvbt_attach },
+ { FC_AIR_ATSC2, airstar_atsc2_attach },
+ { FC_AIR_ATSC3, airstar_atsc3_attach },
+ { FC_AIR_ATSC1, airstar_atsc1_attach },
+ { FC_CABLE, cablestar2_attach },
+ { FC_SKY_REV23, skystar2_rev23_attach },
+};
+
+/* try to figure out the frontend */
+int flexcop_frontend_init(struct flexcop_device *fc)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) {
+ if (!flexcop_frontends[i].attach)
+ continue;
+ /* type needs to be set before, because of some workarounds
+ * done based on the probed card type */
+ fc->dev_type = flexcop_frontends[i].type;
+ if (flexcop_frontends[i].attach(fc, &fc->fc_i2c_adap[0].i2c_adap))
+ goto fe_found;
+ /* Clean up partially attached frontend */
+ if (fc->fe) {
+ dvb_frontend_detach(fc->fe);
+ fc->fe = NULL;
+ }
+ }
+ fc->dev_type = FC_UNK;
+ err("no frontend driver found for this B2C2/FlexCop adapter");
+ return -ENODEV;
+
+fe_found:
+ info("found '%s' .", fc->fe->ops.info.name);
+ if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) {
+ err("frontend registration failed!");
+ dvb_frontend_detach(fc->fe);
+ fc->fe = NULL;
+ return -EINVAL;
+ }
+ fc->init_state |= FC_STATE_FE_INIT;
+ return 0;
+}
+
+void flexcop_frontend_exit(struct flexcop_device *fc)
+{
+ if (fc->init_state & FC_STATE_FE_INIT) {
+ dvb_unregister_frontend(fc->fe);
+ dvb_frontend_detach(fc->fe);
+ }
+ fc->init_state &= ~FC_STATE_FE_INIT;
+}
diff --git a/drivers/media/common/b2c2/flexcop-hw-filter.c b/drivers/media/common/b2c2/flexcop-hw-filter.c
new file mode 100644
index 000000000..77e45475f
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop-hw-filter.c
@@ -0,0 +1,232 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-hw-filter.c - pid and mac address filtering and control functions
+ * see flexcop.c for copyright information
+ */
+#include "flexcop.h"
+
+static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff)
+{
+ flexcop_set_ibi_value(ctrl_208, Rcv_Data_sig, onoff);
+ deb_ts("rcv_data is now: '%s'\n", onoff ? "on" : "off");
+}
+
+void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff)
+{
+ flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff);
+}
+
+static void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff)
+{
+ flexcop_set_ibi_value(ctrl_208, Null_filter_sig, onoff);
+}
+
+void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6])
+{
+ flexcop_ibi_value v418, v41c;
+ v41c = fc->read_ibi_reg(fc, mac_address_41c);
+
+ v418.mac_address_418.MAC1 = mac[0];
+ v418.mac_address_418.MAC2 = mac[1];
+ v418.mac_address_418.MAC3 = mac[2];
+ v418.mac_address_418.MAC6 = mac[3];
+ v41c.mac_address_41c.MAC7 = mac[4];
+ v41c.mac_address_41c.MAC8 = mac[5];
+
+ fc->write_ibi_reg(fc, mac_address_418, v418);
+ fc->write_ibi_reg(fc, mac_address_41c, v41c);
+}
+
+void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff)
+{
+ flexcop_set_ibi_value(ctrl_208, MAC_filter_Mode_sig, onoff);
+}
+
+static void flexcop_pid_group_filter(struct flexcop_device *fc,
+ u16 pid, u16 mask)
+{
+ /* index_reg_310.extra_index_reg need to 0 or 7 to work */
+ flexcop_ibi_value v30c;
+ v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid;
+ v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask;
+ fc->write_ibi_reg(fc, pid_filter_30c, v30c);
+}
+
+static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff)
+{
+ flexcop_set_ibi_value(ctrl_208, Mask_filter_sig, onoff);
+}
+
+/* this fancy define reduces the code size of the quite similar PID controlling of
+ * the first 6 PIDs
+ */
+
+#define pid_ctrl(vregname,field,enablefield,trans_field,transval) \
+ flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \
+v208 = fc->read_ibi_reg(fc, ctrl_208); \
+vpid.vregname.field = onoff ? pid : 0x1fff; \
+vpid.vregname.trans_field = transval; \
+v208.ctrl_208.enablefield = onoff; \
+fc->write_ibi_reg(fc, vregname, vpid); \
+fc->write_ibi_reg(fc, ctrl_208, v208);
+
+static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc,
+ u16 pid, int onoff)
+{
+ pid_ctrl(pid_filter_300, Stream1_PID, Stream1_filter_sig,
+ Stream1_trans, 0);
+}
+
+static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc,
+ u16 pid, int onoff)
+{
+ pid_ctrl(pid_filter_300, Stream2_PID, Stream2_filter_sig,
+ Stream2_trans, 0);
+}
+
+static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc,
+ u16 pid, int onoff)
+{
+ pid_ctrl(pid_filter_304, PCR_PID, PCR_filter_sig, PCR_trans, 0);
+}
+
+static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc,
+ u16 pid, int onoff)
+{
+ pid_ctrl(pid_filter_304, PMT_PID, PMT_filter_sig, PMT_trans, 0);
+}
+
+static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc,
+ u16 pid, int onoff)
+{
+ pid_ctrl(pid_filter_308, EMM_PID, EMM_filter_sig, EMM_trans, 0);
+}
+
+static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc,
+ u16 pid, int onoff)
+{
+ pid_ctrl(pid_filter_308, ECM_PID, ECM_filter_sig, ECM_trans, 0);
+}
+
+static void flexcop_pid_control(struct flexcop_device *fc,
+ int index, u16 pid, int onoff)
+{
+ if (pid == 0x2000)
+ return;
+
+ deb_ts("setting pid: %5d %04x at index %d '%s'\n",
+ pid, pid, index, onoff ? "on" : "off");
+
+ /* We could use bit magic here to reduce source code size.
+ * I decided against it, but to use the real register names */
+ switch (index) {
+ case 0:
+ flexcop_pid_Stream1_PID_ctrl(fc, pid, onoff);
+ break;
+ case 1:
+ flexcop_pid_Stream2_PID_ctrl(fc, pid, onoff);
+ break;
+ case 2:
+ flexcop_pid_PCR_PID_ctrl(fc, pid, onoff);
+ break;
+ case 3:
+ flexcop_pid_PMT_PID_ctrl(fc, pid, onoff);
+ break;
+ case 4:
+ flexcop_pid_EMM_PID_ctrl(fc, pid, onoff);
+ break;
+ case 5:
+ flexcop_pid_ECM_PID_ctrl(fc, pid, onoff);
+ break;
+ default:
+ if (fc->has_32_hw_pid_filter && index < 38) {
+ flexcop_ibi_value vpid, vid;
+
+ /* set the index */
+ vid = fc->read_ibi_reg(fc, index_reg_310);
+ vid.index_reg_310.index_reg = index - 6;
+ fc->write_ibi_reg(fc, index_reg_310, vid);
+
+ vpid = fc->read_ibi_reg(fc, pid_n_reg_314);
+ vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff;
+ vpid.pid_n_reg_314.PID_enable_bit = onoff;
+ fc->write_ibi_reg(fc, pid_n_reg_314, vpid);
+ }
+ break;
+ }
+}
+
+static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff)
+{
+ if (fc->fullts_streaming_state != onoff) {
+ deb_ts("%s full TS transfer\n",onoff ? "enabling" : "disabling");
+ flexcop_pid_group_filter(fc, 0, 0x1fe0 * (!onoff));
+ flexcop_pid_group_filter_ctrl(fc, onoff);
+ fc->fullts_streaming_state = onoff;
+ }
+ return 0;
+}
+
+int flexcop_pid_feed_control(struct flexcop_device *fc,
+ struct dvb_demux_feed *dvbdmxfeed, int onoff)
+{
+ int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32;
+
+ fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */
+ if (dvbdmxfeed->index >= max_pid_filter)
+ fc->extra_feedcount += onoff ? 1 : -1;
+
+ /* toggle complete-TS-streaming when:
+ * - pid_filtering is not enabled and it is the first or last feed requested
+ * - pid_filtering is enabled,
+ * - but the number of requested feeds is exceeded
+ * - or the requested pid is 0x2000 */
+
+ if (!fc->pid_filtering && fc->feedcount == onoff)
+ flexcop_toggle_fullts_streaming(fc, onoff);
+
+ if (fc->pid_filtering) {
+ flexcop_pid_control \
+ (fc, dvbdmxfeed->index, dvbdmxfeed->pid, onoff);
+
+ if (fc->extra_feedcount > 0)
+ flexcop_toggle_fullts_streaming(fc, 1);
+ else if (dvbdmxfeed->pid == 0x2000)
+ flexcop_toggle_fullts_streaming(fc, onoff);
+ else
+ flexcop_toggle_fullts_streaming(fc, 0);
+ }
+
+ /* if it was the first or last feed request change the stream-status */
+ if (fc->feedcount == onoff) {
+ flexcop_rcv_data_ctrl(fc, onoff);
+ if (fc->stream_control) /* device specific stream control */
+ fc->stream_control(fc, onoff);
+
+ /* feeding stopped -> reset the flexcop filter*/
+ if (onoff == 0) {
+ flexcop_reset_block_300(fc);
+ flexcop_hw_filter_init(fc);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(flexcop_pid_feed_control);
+
+void flexcop_hw_filter_init(struct flexcop_device *fc)
+{
+ int i;
+ flexcop_ibi_value v;
+ for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++)
+ flexcop_pid_control(fc, i, 0x1fff, 0);
+
+ flexcop_pid_group_filter(fc, 0, 0x1fe0);
+ flexcop_pid_group_filter_ctrl(fc, 0);
+
+ v = fc->read_ibi_reg(fc, pid_filter_308);
+ v.pid_filter_308.EMM_filter_4 = 1;
+ v.pid_filter_308.EMM_filter_6 = 0;
+ fc->write_ibi_reg(fc, pid_filter_308, v);
+
+ flexcop_null_filter_ctrl(fc, 1);
+}
diff --git a/drivers/media/common/b2c2/flexcop-i2c.c b/drivers/media/common/b2c2/flexcop-i2c.c
new file mode 100644
index 000000000..965d5eb33
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop-i2c.c
@@ -0,0 +1,288 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization
+ * see flexcop.c for copyright information
+ */
+#include "flexcop.h"
+
+#define FC_MAX_I2C_RETRIES 100000
+
+static int flexcop_i2c_operation(struct flexcop_device *fc,
+ flexcop_ibi_value *r100)
+{
+ int i;
+ flexcop_ibi_value r;
+
+ r100->tw_sm_c_100.working_start = 1;
+ deb_i2c("r100 before: %08x\n",r100->raw);
+
+ fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero);
+ fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */
+
+ for (i = 0; i < FC_MAX_I2C_RETRIES; i++) {
+ r = fc->read_ibi_reg(fc, tw_sm_c_100);
+
+ if (!r.tw_sm_c_100.no_base_addr_ack_error) {
+ if (r.tw_sm_c_100.st_done) {
+ *r100 = r;
+ deb_i2c("i2c success\n");
+ return 0;
+ }
+ } else {
+ deb_i2c("suffering from an i2c ack_error\n");
+ return -EREMOTEIO;
+ }
+ }
+ deb_i2c("tried %d times i2c operation, "
+ "never finished or too many ack errors.\n", i);
+ return -EREMOTEIO;
+}
+
+static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c,
+ flexcop_ibi_value r100, u8 *buf)
+{
+ flexcop_ibi_value r104;
+ int len = r100.tw_sm_c_100.total_bytes,
+ /* remember total_bytes is buflen-1 */
+ ret;
+
+ /* work-around to have CableStar2 and SkyStar2 rev 2.7 work
+ * correctly:
+ *
+ * the ITD1000 is behind an i2c-gate which closes automatically
+ * after an i2c-transaction the STV0297 needs 2 consecutive reads
+ * one with no_base_addr = 0 and one with 1
+ *
+ * those two work-arounds are conflictin: we check for the card
+ * type, it is set when probing the ITD1000 */
+ if (i2c->fc->dev_type == FC_SKY_REV27)
+ r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr;
+
+ ret = flexcop_i2c_operation(i2c->fc, &r100);
+ if (ret != 0) {
+ deb_i2c("Retrying operation\n");
+ r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr;
+ ret = flexcop_i2c_operation(i2c->fc, &r100);
+ }
+ if (ret != 0) {
+ deb_i2c("read failed. %d\n", ret);
+ return ret;
+ }
+
+ buf[0] = r100.tw_sm_c_100.data1_reg;
+
+ if (len > 0) {
+ r104 = i2c->fc->read_ibi_reg(i2c->fc, tw_sm_c_104);
+ deb_i2c("read: r100: %08x, r104: %08x\n", r100.raw, r104.raw);
+
+ /* there is at least one more byte, otherwise we wouldn't be here */
+ buf[1] = r104.tw_sm_c_104.data2_reg;
+ if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg;
+ if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg;
+ }
+ return 0;
+}
+
+static int flexcop_i2c_write4(struct flexcop_device *fc,
+ flexcop_ibi_value r100, u8 *buf)
+{
+ flexcop_ibi_value r104;
+ int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */
+ r104.raw = 0;
+
+ /* there is at least one byte, otherwise we wouldn't be here */
+ r100.tw_sm_c_100.data1_reg = buf[0];
+ r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0;
+ r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0;
+ r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0;
+
+ deb_i2c("write: r100: %08x, r104: %08x\n", r100.raw, r104.raw);
+
+ /* write the additional i2c data before doing the actual i2c operation */
+ fc->write_ibi_reg(fc, tw_sm_c_104, r104);
+ return flexcop_i2c_operation(fc, &r100);
+}
+
+int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c,
+ flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
+{
+ int ret;
+
+#ifdef DUMP_I2C_MESSAGES
+ int i;
+#endif
+
+ u16 bytes_to_transfer;
+ flexcop_ibi_value r100;
+
+ deb_i2c("op = %d\n",op);
+ r100.raw = 0;
+ r100.tw_sm_c_100.chipaddr = chipaddr;
+ r100.tw_sm_c_100.twoWS_rw = op;
+ r100.tw_sm_c_100.twoWS_port_reg = i2c->port;
+
+#ifdef DUMP_I2C_MESSAGES
+ printk(KERN_DEBUG "%d ", i2c->port);
+ if (op == FC_READ)
+ printk("rd(");
+ else
+ printk("wr(");
+ printk("%02x): %02x ", chipaddr, addr);
+#endif
+
+ /* in that case addr is the only value ->
+ * we write it twice as baseaddr and val0
+ * BBTI is doing it like that for ISL6421 at least */
+ if (i2c->no_base_addr && len == 0 && op == FC_WRITE) {
+ buf = &addr;
+ len = 1;
+ }
+
+ while (len != 0) {
+ bytes_to_transfer = len > 4 ? 4 : len;
+
+ r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1;
+ r100.tw_sm_c_100.baseaddr = addr;
+
+ if (op == FC_READ)
+ ret = flexcop_i2c_read4(i2c, r100, buf);
+ else
+ ret = flexcop_i2c_write4(i2c->fc, r100, buf);
+
+#ifdef DUMP_I2C_MESSAGES
+ for (i = 0; i < bytes_to_transfer; i++)
+ printk("%02x ", buf[i]);
+#endif
+
+ if (ret < 0)
+ return ret;
+
+ buf += bytes_to_transfer;
+ addr += bytes_to_transfer;
+ len -= bytes_to_transfer;
+ }
+
+#ifdef DUMP_I2C_MESSAGES
+ printk("\n");
+#endif
+
+ return 0;
+}
+/* exported for PCI i2c */
+EXPORT_SYMBOL(flexcop_i2c_request);
+
+/* master xfer callback for demodulator */
+static int flexcop_master_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct flexcop_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
+ int i, ret = 0;
+
+ /* Some drivers use 1 byte or 0 byte reads as probes, which this
+ * driver doesn't support. These probes will always fail, so this
+ * hack makes them always succeed. If one knew how, it would of
+ * course be better to actually do the read. */
+ if (num == 1 && msgs[0].flags == I2C_M_RD && msgs[0].len <= 1)
+ return 1;
+
+ if (mutex_lock_interruptible(&i2c->fc->i2c_mutex))
+ return -ERESTARTSYS;
+
+ for (i = 0; i < num; i++) {
+ /* reading */
+ if (i+1 < num && (msgs[i+1].flags == I2C_M_RD)) {
+ ret = i2c->fc->i2c_request(i2c, FC_READ, msgs[i].addr,
+ msgs[i].buf[0], msgs[i+1].buf,
+ msgs[i+1].len);
+ i++; /* skip the following message */
+ } else /* writing */
+ ret = i2c->fc->i2c_request(i2c, FC_WRITE, msgs[i].addr,
+ msgs[i].buf[0], &msgs[i].buf[1],
+ msgs[i].len - 1);
+ if (ret < 0) {
+ deb_i2c("i2c master_xfer failed");
+ break;
+ }
+ }
+
+ mutex_unlock(&i2c->fc->i2c_mutex);
+
+ if (ret == 0)
+ ret = num;
+ return ret;
+}
+
+static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm flexcop_algo = {
+ .master_xfer = flexcop_master_xfer,
+ .functionality = flexcop_i2c_func,
+};
+
+int flexcop_i2c_init(struct flexcop_device *fc)
+{
+ int ret;
+ mutex_init(&fc->i2c_mutex);
+
+ fc->fc_i2c_adap[0].fc = fc;
+ fc->fc_i2c_adap[1].fc = fc;
+ fc->fc_i2c_adap[2].fc = fc;
+ fc->fc_i2c_adap[0].port = FC_I2C_PORT_DEMOD;
+ fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM;
+ fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER;
+
+ strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod",
+ sizeof(fc->fc_i2c_adap[0].i2c_adap.name));
+ strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom",
+ sizeof(fc->fc_i2c_adap[1].i2c_adap.name));
+ strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner",
+ sizeof(fc->fc_i2c_adap[2].i2c_adap.name));
+
+ i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]);
+ i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]);
+ i2c_set_adapdata(&fc->fc_i2c_adap[2].i2c_adap, &fc->fc_i2c_adap[2]);
+
+ fc->fc_i2c_adap[0].i2c_adap.algo =
+ fc->fc_i2c_adap[1].i2c_adap.algo =
+ fc->fc_i2c_adap[2].i2c_adap.algo = &flexcop_algo;
+ fc->fc_i2c_adap[0].i2c_adap.algo_data =
+ fc->fc_i2c_adap[1].i2c_adap.algo_data =
+ fc->fc_i2c_adap[2].i2c_adap.algo_data = NULL;
+ fc->fc_i2c_adap[0].i2c_adap.dev.parent =
+ fc->fc_i2c_adap[1].i2c_adap.dev.parent =
+ fc->fc_i2c_adap[2].i2c_adap.dev.parent = fc->dev;
+
+ ret = i2c_add_adapter(&fc->fc_i2c_adap[0].i2c_adap);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_add_adapter(&fc->fc_i2c_adap[1].i2c_adap);
+ if (ret < 0)
+ goto adap_1_failed;
+
+ ret = i2c_add_adapter(&fc->fc_i2c_adap[2].i2c_adap);
+ if (ret < 0)
+ goto adap_2_failed;
+
+ fc->init_state |= FC_STATE_I2C_INIT;
+ return 0;
+
+adap_2_failed:
+ i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap);
+adap_1_failed:
+ i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap);
+ return ret;
+}
+
+void flexcop_i2c_exit(struct flexcop_device *fc)
+{
+ if (fc->init_state & FC_STATE_I2C_INIT) {
+ i2c_del_adapter(&fc->fc_i2c_adap[2].i2c_adap);
+ i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap);
+ i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap);
+ }
+ fc->init_state &= ~FC_STATE_I2C_INIT;
+}
diff --git a/drivers/media/common/b2c2/flexcop-misc.c b/drivers/media/common/b2c2/flexcop-misc.c
new file mode 100644
index 000000000..f06f3a907
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop-misc.c
@@ -0,0 +1,86 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-misc.c - miscellaneous functions
+ * see flexcop.c for copyright information
+ */
+#include "flexcop.h"
+
+void flexcop_determine_revision(struct flexcop_device *fc)
+{
+ flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204);
+
+ switch (v.misc_204.Rev_N_sig_revision_hi) {
+ case 0x2:
+ deb_info("found a FlexCopII.\n");
+ fc->rev = FLEXCOP_II;
+ break;
+ case 0x3:
+ deb_info("found a FlexCopIIb.\n");
+ fc->rev = FLEXCOP_IIB;
+ break;
+ case 0x0:
+ deb_info("found a FlexCopIII.\n");
+ fc->rev = FLEXCOP_III;
+ break;
+ default:
+ err("unknown FlexCop Revision: %x. Please report this to "
+ "linux-dvb@linuxtv.org.",
+ v.misc_204.Rev_N_sig_revision_hi);
+ break;
+ }
+
+ if ((fc->has_32_hw_pid_filter = v.misc_204.Rev_N_sig_caps))
+ deb_info("this FlexCop has "
+ "the additional 32 hardware pid filter.\n");
+ else
+ deb_info("this FlexCop has "
+ "the 6 basic main hardware pid filter.\n");
+ /* bus parts have to decide if hw pid filtering is used or not. */
+}
+
+static const char *flexcop_revision_names[] = {
+ "Unknown chip",
+ "FlexCopII",
+ "FlexCopIIb",
+ "FlexCopIII",
+};
+
+static const char *flexcop_device_names[] = {
+ [FC_UNK] = "Unknown device",
+ [FC_CABLE] = "Cable2PC/CableStar 2 DVB-C",
+ [FC_AIR_DVBT] = "Air2PC/AirStar 2 DVB-T",
+ [FC_AIR_ATSC1] = "Air2PC/AirStar 2 ATSC 1st generation",
+ [FC_AIR_ATSC2] = "Air2PC/AirStar 2 ATSC 2nd generation",
+ [FC_AIR_ATSC3] = "Air2PC/AirStar 2 ATSC 3rd generation (HD5000)",
+ [FC_SKY_REV23] = "Sky2PC/SkyStar 2 DVB-S rev 2.3 (old version)",
+ [FC_SKY_REV26] = "Sky2PC/SkyStar 2 DVB-S rev 2.6",
+ [FC_SKY_REV27] = "Sky2PC/SkyStar 2 DVB-S rev 2.7a/u",
+ [FC_SKY_REV28] = "Sky2PC/SkyStar 2 DVB-S rev 2.8",
+};
+
+static const char *flexcop_bus_names[] = {
+ "USB",
+ "PCI",
+};
+
+void flexcop_device_name(struct flexcop_device *fc,
+ const char *prefix, const char *suffix)
+{
+ info("%s '%s' at the '%s' bus controlled by a '%s' %s",
+ prefix, flexcop_device_names[fc->dev_type],
+ flexcop_bus_names[fc->bus_type],
+ flexcop_revision_names[fc->rev], suffix);
+}
+
+void flexcop_dump_reg(struct flexcop_device *fc,
+ flexcop_ibi_register reg, int num)
+{
+ flexcop_ibi_value v;
+ int i;
+ for (i = 0; i < num; i++) {
+ v = fc->read_ibi_reg(fc, reg+4*i);
+ deb_rdump("0x%03x: %08x, ", reg+4*i, v.raw);
+ }
+ deb_rdump("\n");
+}
+EXPORT_SYMBOL(flexcop_dump_reg);
diff --git a/drivers/media/common/b2c2/flexcop-reg.h b/drivers/media/common/b2c2/flexcop-reg.h
new file mode 100644
index 000000000..dc4528dcb
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop-reg.h
@@ -0,0 +1,166 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-reg.h - register abstraction for FlexCopII, FlexCopIIb and FlexCopIII
+ * see flexcop.c for copyright information
+ */
+#ifndef __FLEXCOP_REG_H__
+#define __FLEXCOP_REG_H__
+
+typedef enum {
+ FLEXCOP_UNK = 0,
+ FLEXCOP_II,
+ FLEXCOP_IIB,
+ FLEXCOP_III,
+} flexcop_revision_t;
+
+typedef enum {
+ FC_UNK = 0,
+ FC_CABLE,
+ FC_AIR_DVBT,
+ FC_AIR_ATSC1,
+ FC_AIR_ATSC2,
+ FC_AIR_ATSC3,
+ FC_SKY_REV23,
+ FC_SKY_REV26,
+ FC_SKY_REV27,
+ FC_SKY_REV28,
+} flexcop_device_type_t;
+
+typedef enum {
+ FC_USB = 0,
+ FC_PCI,
+} flexcop_bus_t;
+
+/* FlexCop IBI Registers */
+#if defined(__LITTLE_ENDIAN)
+#include "flexcop_ibi_value_le.h"
+#else
+#if defined(__BIG_ENDIAN)
+#include "flexcop_ibi_value_be.h"
+#else
+#error no endian defined
+#endif
+#endif
+
+#define fc_data_Tag_ID_DVB 0x3e
+#define fc_data_Tag_ID_ATSC 0x3f
+#define fc_data_Tag_ID_IDSB 0x8b
+
+#define fc_key_code_default 0x1
+#define fc_key_code_even 0x2
+#define fc_key_code_odd 0x3
+
+extern flexcop_ibi_value ibi_zero;
+
+typedef enum {
+ FC_I2C_PORT_DEMOD = 1,
+ FC_I2C_PORT_EEPROM = 2,
+ FC_I2C_PORT_TUNER = 3,
+} flexcop_i2c_port_t;
+
+typedef enum {
+ FC_WRITE = 0,
+ FC_READ = 1,
+} flexcop_access_op_t;
+
+typedef enum {
+ FC_SRAM_DEST_NET = 1,
+ FC_SRAM_DEST_CAI = 2,
+ FC_SRAM_DEST_CAO = 4,
+ FC_SRAM_DEST_MEDIA = 8
+} flexcop_sram_dest_t;
+
+typedef enum {
+ FC_SRAM_DEST_TARGET_WAN_USB = 0,
+ FC_SRAM_DEST_TARGET_DMA1 = 1,
+ FC_SRAM_DEST_TARGET_DMA2 = 2,
+ FC_SRAM_DEST_TARGET_FC3_CA = 3
+} flexcop_sram_dest_target_t;
+
+typedef enum {
+ FC_SRAM_2_32KB = 0, /* 64KB */
+ FC_SRAM_1_32KB = 1, /* 32KB - default fow FCII */
+ FC_SRAM_1_128KB = 2, /* 128KB */
+ FC_SRAM_1_48KB = 3, /* 48KB - default for FCIII */
+} flexcop_sram_type_t;
+
+typedef enum {
+ FC_WAN_SPEED_4MBITS = 0,
+ FC_WAN_SPEED_8MBITS = 1,
+ FC_WAN_SPEED_12MBITS = 2,
+ FC_WAN_SPEED_16MBITS = 3,
+} flexcop_wan_speed_t;
+
+typedef enum {
+ FC_DMA_1 = 1,
+ FC_DMA_2 = 2,
+} flexcop_dma_index_t;
+
+typedef enum {
+ FC_DMA_SUBADDR_0 = 1,
+ FC_DMA_SUBADDR_1 = 2,
+} flexcop_dma_addr_index_t;
+
+/* names of the particular registers */
+typedef enum {
+ dma1_000 = 0x000,
+ dma1_004 = 0x004,
+ dma1_008 = 0x008,
+ dma1_00c = 0x00c,
+ dma2_010 = 0x010,
+ dma2_014 = 0x014,
+ dma2_018 = 0x018,
+ dma2_01c = 0x01c,
+
+ tw_sm_c_100 = 0x100,
+ tw_sm_c_104 = 0x104,
+ tw_sm_c_108 = 0x108,
+ tw_sm_c_10c = 0x10c,
+ tw_sm_c_110 = 0x110,
+
+ lnb_switch_freq_200 = 0x200,
+ misc_204 = 0x204,
+ ctrl_208 = 0x208,
+ irq_20c = 0x20c,
+ sw_reset_210 = 0x210,
+ misc_214 = 0x214,
+ mbox_v8_to_host_218 = 0x218,
+ mbox_host_to_v8_21c = 0x21c,
+
+ pid_filter_300 = 0x300,
+ pid_filter_304 = 0x304,
+ pid_filter_308 = 0x308,
+ pid_filter_30c = 0x30c,
+ index_reg_310 = 0x310,
+ pid_n_reg_314 = 0x314,
+ mac_low_reg_318 = 0x318,
+ mac_high_reg_31c = 0x31c,
+
+ data_tag_400 = 0x400,
+ card_id_408 = 0x408,
+ card_id_40c = 0x40c,
+ mac_address_418 = 0x418,
+ mac_address_41c = 0x41c,
+
+ ci_600 = 0x600,
+ pi_604 = 0x604,
+ pi_608 = 0x608,
+ dvb_reg_60c = 0x60c,
+
+ sram_ctrl_reg_700 = 0x700,
+ net_buf_reg_704 = 0x704,
+ cai_buf_reg_708 = 0x708,
+ cao_buf_reg_70c = 0x70c,
+ media_buf_reg_710 = 0x710,
+ sram_dest_reg_714 = 0x714,
+ net_buf_reg_718 = 0x718,
+ wan_ctrl_reg_71c = 0x71c,
+} flexcop_ibi_register;
+
+#define flexcop_set_ibi_value(reg,attr,val) { \
+ flexcop_ibi_value v = fc->read_ibi_reg(fc,reg); \
+ v.reg.attr = val; \
+ fc->write_ibi_reg(fc,reg,v); \
+}
+
+#endif
diff --git a/drivers/media/common/b2c2/flexcop-sram.c b/drivers/media/common/b2c2/flexcop-sram.c
new file mode 100644
index 000000000..185c285f7
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop-sram.c
@@ -0,0 +1,363 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-sram.c - functions for controlling the SRAM
+ * see flexcop.c for copyright information
+ */
+#include "flexcop.h"
+
+static void flexcop_sram_set_chip(struct flexcop_device *fc,
+ flexcop_sram_type_t type)
+{
+ flexcop_set_ibi_value(wan_ctrl_reg_71c, sram_chip, type);
+}
+
+int flexcop_sram_init(struct flexcop_device *fc)
+{
+ switch (fc->rev) {
+ case FLEXCOP_II:
+ case FLEXCOP_IIB:
+ flexcop_sram_set_chip(fc, FC_SRAM_1_32KB);
+ break;
+ case FLEXCOP_III:
+ flexcop_sram_set_chip(fc, FC_SRAM_1_48KB);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest,
+ flexcop_sram_dest_target_t target)
+{
+ flexcop_ibi_value v;
+ v = fc->read_ibi_reg(fc, sram_dest_reg_714);
+
+ if (fc->rev != FLEXCOP_III && target == FC_SRAM_DEST_TARGET_FC3_CA) {
+ err("SRAM destination target to available on FlexCopII(b)\n");
+ return -EINVAL;
+ }
+ deb_sram("sram dest: %x target: %x\n", dest, target);
+
+ if (dest & FC_SRAM_DEST_NET)
+ v.sram_dest_reg_714.NET_Dest = target;
+ if (dest & FC_SRAM_DEST_CAI)
+ v.sram_dest_reg_714.CAI_Dest = target;
+ if (dest & FC_SRAM_DEST_CAO)
+ v.sram_dest_reg_714.CAO_Dest = target;
+ if (dest & FC_SRAM_DEST_MEDIA)
+ v.sram_dest_reg_714.MEDIA_Dest = target;
+
+ fc->write_ibi_reg(fc,sram_dest_reg_714,v);
+ udelay(1000); /* TODO delay really necessary */
+
+ return 0;
+}
+EXPORT_SYMBOL(flexcop_sram_set_dest);
+
+void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s)
+{
+ flexcop_set_ibi_value(wan_ctrl_reg_71c,wan_speed_sig,s);
+}
+EXPORT_SYMBOL(flexcop_wan_set_speed);
+
+void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill)
+{
+ flexcop_ibi_value v = fc->read_ibi_reg(fc,sram_dest_reg_714);
+ v.sram_dest_reg_714.ctrl_usb_wan = usb_wan;
+ v.sram_dest_reg_714.ctrl_sramdma = sramdma;
+ v.sram_dest_reg_714.ctrl_maximumfill = maximumfill;
+ fc->write_ibi_reg(fc,sram_dest_reg_714,v);
+}
+EXPORT_SYMBOL(flexcop_sram_ctrl);
+
+#if 0
+static void flexcop_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
+{
+ int i, retries;
+ u32 command;
+
+ for (i = 0; i < len; i++) {
+ command = bank | addr | 0x04000000 | (*buf << 0x10);
+
+ retries = 2;
+
+ while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
+ mdelay(1);
+ retries--;
+ }
+
+ if (retries == 0)
+ printk("%s: SRAM timeout\n", __func__);
+
+ write_reg_dw(adapter, 0x700, command);
+
+ buf++;
+ addr++;
+ }
+}
+
+static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
+{
+ int i, retries;
+ u32 command, value;
+
+ for (i = 0; i < len; i++) {
+ command = bank | addr | 0x04008000;
+
+ retries = 10000;
+
+ while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
+ mdelay(1);
+ retries--;
+ }
+
+ if (retries == 0)
+ printk("%s: SRAM timeout\n", __func__);
+
+ write_reg_dw(adapter, 0x700, command);
+
+ retries = 10000;
+
+ while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
+ mdelay(1);
+ retries--;
+ }
+
+ if (retries == 0)
+ printk("%s: SRAM timeout\n", __func__);
+
+ value = read_reg_dw(adapter, 0x700) >> 0x10;
+
+ *buf = (value & 0xff);
+
+ addr++;
+ buf++;
+ }
+}
+
+static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
+{
+ u32 bank;
+
+ bank = 0;
+
+ if (adapter->dw_sram_type == 0x20000) {
+ bank = (addr & 0x18000) << 0x0d;
+ }
+
+ if (adapter->dw_sram_type == 0x00000) {
+ if ((addr >> 0x0f) == 0)
+ bank = 0x20000000;
+ else
+ bank = 0x10000000;
+ }
+ flex_sram_write(adapter, bank, addr & 0x7fff, buf, len);
+}
+
+static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
+{
+ u32 bank;
+ bank = 0;
+
+ if (adapter->dw_sram_type == 0x20000) {
+ bank = (addr & 0x18000) << 0x0d;
+ }
+
+ if (adapter->dw_sram_type == 0x00000) {
+ if ((addr >> 0x0f) == 0)
+ bank = 0x20000000;
+ else
+ bank = 0x10000000;
+ }
+ flex_sram_read(adapter, bank, addr & 0x7fff, buf, len);
+}
+
+static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+{
+ u32 length;
+ while (len != 0) {
+ length = len;
+ /* check if the address range belongs to the same
+ * 32K memory chip. If not, the data is read
+ * from one chip at a time */
+ if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
+ length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
+ }
+
+ sram_read_chunk(adapter, addr, buf, length);
+ addr = addr + length;
+ buf = buf + length;
+ len = len - length;
+ }
+}
+
+static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+{
+ u32 length;
+ while (len != 0) {
+ length = len;
+
+ /* check if the address range belongs to the same
+ * 32K memory chip. If not, the data is
+ * written to one chip at a time */
+ if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
+ length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
+ }
+
+ sram_write_chunk(adapter, addr, buf, length);
+ addr = addr + length;
+ buf = buf + length;
+ len = len - length;
+ }
+}
+
+static void sram_set_size(struct adapter *adapter, u32 mask)
+{
+ write_reg_dw(adapter, 0x71c,
+ (mask | (~0x30000 & read_reg_dw(adapter, 0x71c))));
+}
+
+static void sram_init(struct adapter *adapter)
+{
+ u32 tmp;
+ tmp = read_reg_dw(adapter, 0x71c);
+ write_reg_dw(adapter, 0x71c, 1);
+
+ if (read_reg_dw(adapter, 0x71c) != 0) {
+ write_reg_dw(adapter, 0x71c, tmp);
+ adapter->dw_sram_type = tmp & 0x30000;
+ ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type);
+ } else {
+ adapter->dw_sram_type = 0x10000;
+ ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type);
+ }
+}
+
+static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr)
+{
+ u8 tmp1, tmp2;
+ dprintk("%s: mask = %x, addr = %x\n", __func__, mask, addr);
+
+ sram_set_size(adapter, mask);
+ sram_init(adapter);
+
+ tmp2 = 0xa5;
+ tmp1 = 0x4f;
+
+ sram_write(adapter, addr, &tmp2, 1);
+ sram_write(adapter, addr + 4, &tmp1, 1);
+
+ tmp2 = 0;
+ mdelay(20);
+
+ sram_read(adapter, addr, &tmp2, 1);
+ sram_read(adapter, addr, &tmp2, 1);
+
+ dprintk("%s: wrote 0xa5, read 0x%2x\n", __func__, tmp2);
+
+ if (tmp2 != 0xa5)
+ return 0;
+
+ tmp2 = 0x5a;
+ tmp1 = 0xf4;
+
+ sram_write(adapter, addr, &tmp2, 1);
+ sram_write(adapter, addr + 4, &tmp1, 1);
+
+ tmp2 = 0;
+ mdelay(20);
+
+ sram_read(adapter, addr, &tmp2, 1);
+ sram_read(adapter, addr, &tmp2, 1);
+
+ dprintk("%s: wrote 0x5a, read 0x%2x\n", __func__, tmp2);
+
+ if (tmp2 != 0x5a)
+ return 0;
+ return 1;
+}
+
+static u32 sram_length(struct adapter *adapter)
+{
+ if (adapter->dw_sram_type == 0x10000)
+ return 32768; /* 32K */
+ if (adapter->dw_sram_type == 0x00000)
+ return 65536; /* 64K */
+ if (adapter->dw_sram_type == 0x20000)
+ return 131072; /* 128K */
+ return 32768; /* 32K */
+}
+
+/* FlexcopII can work with 32K, 64K or 128K of external SRAM memory.
+ - for 128K there are 4x32K chips at bank 0,1,2,3.
+ - for 64K there are 2x32K chips at bank 1,2.
+ - for 32K there is one 32K chip at bank 0.
+
+ FlexCop works only with one bank at a time. The bank is selected
+ by bits 28-29 of the 0x700 register.
+
+ bank 0 covers addresses 0x00000-0x07fff
+ bank 1 covers addresses 0x08000-0x0ffff
+ bank 2 covers addresses 0x10000-0x17fff
+ bank 3 covers addresses 0x18000-0x1ffff */
+
+static int flexcop_sram_detect(struct flexcop_device *fc)
+{
+ flexcop_ibi_value r208, r71c_0, vr71c_1;
+ r208 = fc->read_ibi_reg(fc, ctrl_208);
+ fc->write_ibi_reg(fc, ctrl_208, ibi_zero);
+
+ r71c_0 = fc->read_ibi_reg(fc, wan_ctrl_reg_71c);
+ write_reg_dw(adapter, 0x71c, 1);
+ tmp3 = read_reg_dw(adapter, 0x71c);
+ dprintk("%s: tmp3 = %x\n", __func__, tmp3);
+ write_reg_dw(adapter, 0x71c, tmp2);
+
+ // check for internal SRAM ???
+ tmp3--;
+ if (tmp3 != 0) {
+ sram_set_size(adapter, 0x10000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+ dprintk("%s: sram size = 32K\n", __func__);
+ return 32;
+ }
+
+ if (sram_test_location(adapter, 0x20000, 0x18000) != 0) {
+ sram_set_size(adapter, 0x20000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+ dprintk("%s: sram size = 128K\n", __func__);
+ return 128;
+ }
+
+ if (sram_test_location(adapter, 0x00000, 0x10000) != 0) {
+ sram_set_size(adapter, 0x00000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+ dprintk("%s: sram size = 64K\n", __func__);
+ return 64;
+ }
+
+ if (sram_test_location(adapter, 0x10000, 0x00000) != 0) {
+ sram_set_size(adapter, 0x10000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+ dprintk("%s: sram size = 32K\n", __func__);
+ return 32;
+ }
+
+ sram_set_size(adapter, 0x10000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+ dprintk("%s: SRAM detection failed. Set to 32K \n", __func__);
+ return 0;
+}
+
+static void sll_detect_sram_size(struct adapter *adapter)
+{
+ sram_detect_for_flex2(adapter);
+}
+
+#endif
diff --git a/drivers/media/common/b2c2/flexcop.c b/drivers/media/common/b2c2/flexcop.c
new file mode 100644
index 000000000..412c5daf2
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop.c
@@ -0,0 +1,325 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop.c - main module part
+ * Copyright (C) 2004-9 Patrick Boettcher <patrick.boettcher@desy.de>
+ * based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
+ *
+ * Acknowledgements:
+ * John Jurrius from BBTI, Inc. for extensive support
+ * with code examples and data books
+ * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting)
+ *
+ * Contributions to the skystar2-driver have been done by
+ * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes)
+ * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code)
+ * Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu)
+ * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac
+ * filtering)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser 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 "flexcop.h"
+
+#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de"
+
+#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+#define DEBSTATUS ""
+#else
+#define DEBSTATUS " (debugging is not enabled)"
+#endif
+
+int b2c2_flexcop_debug;
+EXPORT_SYMBOL_GPL(b2c2_flexcop_debug);
+module_param_named(debug, b2c2_flexcop_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "set debug level (1=info,2=tuner,4=i2c,8=ts,"
+ "16=sram,32=reg (|-able))."
+ DEBSTATUS);
+#undef DEBSTATUS
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* global zero for ibi values */
+flexcop_ibi_value ibi_zero;
+
+static int flexcop_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct flexcop_device *fc = dvbdmxfeed->demux->priv;
+ return flexcop_pid_feed_control(fc, dvbdmxfeed, 1);
+}
+
+static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct flexcop_device *fc = dvbdmxfeed->demux->priv;
+ return flexcop_pid_feed_control(fc, dvbdmxfeed, 0);
+}
+
+static int flexcop_dvb_init(struct flexcop_device *fc)
+{
+ int ret = dvb_register_adapter(&fc->dvb_adapter,
+ "FlexCop Digital TV device", fc->owner,
+ fc->dev, adapter_nr);
+ if (ret < 0) {
+ err("error registering DVB adapter");
+ return ret;
+ }
+ fc->dvb_adapter.priv = fc;
+
+ fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING
+ | DMX_MEMORY_BASED_FILTERING);
+ fc->demux.priv = fc;
+ fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED;
+ fc->demux.start_feed = flexcop_dvb_start_feed;
+ fc->demux.stop_feed = flexcop_dvb_stop_feed;
+ fc->demux.write_to_decoder = NULL;
+
+ ret = dvb_dmx_init(&fc->demux);
+ if (ret < 0) {
+ err("dvb_dmx failed: error %d", ret);
+ goto err_dmx;
+ }
+
+ fc->hw_frontend.source = DMX_FRONTEND_0;
+
+ fc->dmxdev.filternum = fc->demux.feednum;
+ fc->dmxdev.demux = &fc->demux.dmx;
+ fc->dmxdev.capabilities = 0;
+ ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter);
+ if (ret < 0) {
+ err("dvb_dmxdev_init failed: error %d", ret);
+ goto err_dmx_dev;
+ }
+
+ ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend);
+ if (ret < 0) {
+ err("adding hw_frontend to dmx failed: error %d", ret);
+ goto err_dmx_add_hw_frontend;
+ }
+
+ fc->mem_frontend.source = DMX_MEMORY_FE;
+ ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend);
+ if (ret < 0) {
+ err("adding mem_frontend to dmx failed: error %d", ret);
+ goto err_dmx_add_mem_frontend;
+ }
+
+ ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend);
+ if (ret < 0) {
+ err("connect frontend failed: error %d", ret);
+ goto err_connect_frontend;
+ }
+
+ ret = dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx);
+ if (ret < 0) {
+ err("dvb_net_init failed: error %d", ret);
+ goto err_net;
+ }
+
+ fc->init_state |= FC_STATE_DVB_INIT;
+ return 0;
+
+err_net:
+ fc->demux.dmx.disconnect_frontend(&fc->demux.dmx);
+err_connect_frontend:
+ fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->mem_frontend);
+err_dmx_add_mem_frontend:
+ fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->hw_frontend);
+err_dmx_add_hw_frontend:
+ dvb_dmxdev_release(&fc->dmxdev);
+err_dmx_dev:
+ dvb_dmx_release(&fc->demux);
+err_dmx:
+ dvb_unregister_adapter(&fc->dvb_adapter);
+ return ret;
+}
+
+static void flexcop_dvb_exit(struct flexcop_device *fc)
+{
+ if (fc->init_state & FC_STATE_DVB_INIT) {
+ dvb_net_release(&fc->dvbnet);
+
+ fc->demux.dmx.close(&fc->demux.dmx);
+ fc->demux.dmx.remove_frontend(&fc->demux.dmx,
+ &fc->mem_frontend);
+ fc->demux.dmx.remove_frontend(&fc->demux.dmx,
+ &fc->hw_frontend);
+ dvb_dmxdev_release(&fc->dmxdev);
+ dvb_dmx_release(&fc->demux);
+ dvb_unregister_adapter(&fc->dvb_adapter);
+ deb_info("deinitialized dvb stuff\n");
+ }
+ fc->init_state &= ~FC_STATE_DVB_INIT;
+}
+
+/* these methods are necessary to achieve the long-term-goal of hiding the
+ * struct flexcop_device from the bus-parts */
+void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len)
+{
+ dvb_dmx_swfilter(&fc->demux, buf, len);
+}
+EXPORT_SYMBOL(flexcop_pass_dmx_data);
+
+void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no)
+{
+ dvb_dmx_swfilter_packets(&fc->demux, buf, no);
+}
+EXPORT_SYMBOL(flexcop_pass_dmx_packets);
+
+static void flexcop_reset(struct flexcop_device *fc)
+{
+ flexcop_ibi_value v210, v204;
+
+ /* reset the flexcop itself */
+ fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
+
+ v210.raw = 0;
+ v210.sw_reset_210.reset_block_000 = 1;
+ v210.sw_reset_210.reset_block_100 = 1;
+ v210.sw_reset_210.reset_block_200 = 1;
+ v210.sw_reset_210.reset_block_300 = 1;
+ v210.sw_reset_210.reset_block_400 = 1;
+ v210.sw_reset_210.reset_block_500 = 1;
+ v210.sw_reset_210.reset_block_600 = 1;
+ v210.sw_reset_210.reset_block_700 = 1;
+ v210.sw_reset_210.Block_reset_enable = 0xb2;
+ v210.sw_reset_210.Special_controls = 0xc259;
+ fc->write_ibi_reg(fc,sw_reset_210,v210);
+ msleep(1);
+
+ /* reset the periphical devices */
+
+ v204 = fc->read_ibi_reg(fc,misc_204);
+ v204.misc_204.Per_reset_sig = 0;
+ fc->write_ibi_reg(fc,misc_204,v204);
+ msleep(1);
+ v204.misc_204.Per_reset_sig = 1;
+ fc->write_ibi_reg(fc,misc_204,v204);
+}
+
+void flexcop_reset_block_300(struct flexcop_device *fc)
+{
+ flexcop_ibi_value v208_save = fc->read_ibi_reg(fc, ctrl_208),
+ v210 = fc->read_ibi_reg(fc, sw_reset_210);
+
+ deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw);
+ fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
+
+ v210.sw_reset_210.reset_block_300 = 1;
+ v210.sw_reset_210.Block_reset_enable = 0xb2;
+
+ fc->write_ibi_reg(fc,sw_reset_210,v210);
+ fc->write_ibi_reg(fc,ctrl_208,v208_save);
+}
+
+struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
+{
+ void *bus;
+ struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device),
+ GFP_KERNEL);
+ if (!fc) {
+ err("no memory");
+ return NULL;
+ }
+
+ bus = kzalloc(bus_specific_len, GFP_KERNEL);
+ if (!bus) {
+ err("no memory");
+ kfree(fc);
+ return NULL;
+ }
+
+ fc->bus_specific = bus;
+
+ return fc;
+}
+EXPORT_SYMBOL(flexcop_device_kmalloc);
+
+void flexcop_device_kfree(struct flexcop_device *fc)
+{
+ kfree(fc->bus_specific);
+ kfree(fc);
+}
+EXPORT_SYMBOL(flexcop_device_kfree);
+
+int flexcop_device_initialize(struct flexcop_device *fc)
+{
+ int ret;
+ ibi_zero.raw = 0;
+
+ flexcop_reset(fc);
+ flexcop_determine_revision(fc);
+ flexcop_sram_init(fc);
+ flexcop_hw_filter_init(fc);
+ flexcop_smc_ctrl(fc, 0);
+
+ ret = flexcop_dvb_init(fc);
+ if (ret)
+ goto error;
+
+ /* i2c has to be done before doing EEProm stuff -
+ * because the EEProm is accessed via i2c */
+ ret = flexcop_i2c_init(fc);
+ if (ret)
+ goto error;
+
+ /* do the MAC address reading after initializing the dvb_adapter */
+ if (fc->get_mac_addr(fc, 0) == 0) {
+ u8 *b = fc->dvb_adapter.proposed_mac;
+ info("MAC address = %pM", b);
+ flexcop_set_mac_filter(fc,b);
+ flexcop_mac_filter_ctrl(fc,1);
+ } else
+ warn("reading of MAC address failed.\n");
+
+ ret = flexcop_frontend_init(fc);
+ if (ret)
+ goto error;
+
+ flexcop_device_name(fc,"initialization of","complete");
+ return 0;
+
+error:
+ flexcop_device_exit(fc);
+ return ret;
+}
+EXPORT_SYMBOL(flexcop_device_initialize);
+
+void flexcop_device_exit(struct flexcop_device *fc)
+{
+ flexcop_frontend_exit(fc);
+ flexcop_i2c_exit(fc);
+ flexcop_dvb_exit(fc);
+}
+EXPORT_SYMBOL(flexcop_device_exit);
+
+static int flexcop_module_init(void)
+{
+ info(DRIVER_NAME " loaded successfully");
+ return 0;
+}
+
+static void flexcop_module_cleanup(void)
+{
+ info(DRIVER_NAME " unloaded successfully");
+}
+
+module_init(flexcop_module_init);
+module_exit(flexcop_module_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/b2c2/flexcop.h b/drivers/media/common/b2c2/flexcop.h
new file mode 100644
index 000000000..8942bdacb
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop.h
@@ -0,0 +1,29 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop.h - private header file for all flexcop-chip-source files
+ * see flexcop.c for copyright information
+ */
+#ifndef __FLEXCOP_H__
+#define __FLEXCOP_H__
+
+#define FC_LOG_PREFIX "b2c2-flexcop"
+#include "flexcop-common.h"
+
+extern int b2c2_flexcop_debug;
+
+/* debug */
+#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+#define dprintk(level,args...) \
+ do { if ((b2c2_flexcop_debug & level)) printk(args); } while (0)
+#else
+#define dprintk(level,args...)
+#endif
+
+#define deb_info(args...) dprintk(0x01, args)
+#define deb_tuner(args...) dprintk(0x02, args)
+#define deb_i2c(args...) dprintk(0x04, args)
+#define deb_ts(args...) dprintk(0x08, args)
+#define deb_sram(args...) dprintk(0x10, args)
+#define deb_rdump(args...) dprintk(0x20, args)
+
+#endif
diff --git a/drivers/media/common/b2c2/flexcop_ibi_value_be.h b/drivers/media/common/b2c2/flexcop_ibi_value_be.h
new file mode 100644
index 000000000..8f64bdbd7
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop_ibi_value_be.h
@@ -0,0 +1,455 @@
+/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * register descriptions
+ * see flexcop.c for copyright information
+ */
+/* This file is automatically generated, do not edit things here. */
+#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__
+#define __FLEXCOP_IBI_VALUE_INCLUDED__
+
+typedef union {
+ u32 raw;
+
+ struct {
+ u32 dma_address0 :30;
+ u32 dma_0No_update : 1;
+ u32 dma_0start : 1;
+ } dma_0x0;
+
+ struct {
+ u32 dma_addr_size :24;
+ u32 DMA_maxpackets : 8;
+ } dma_0x4_remap;
+
+ struct {
+ u32 dma_addr_size :24;
+ u32 unused : 1;
+ u32 dma1timer : 7;
+ } dma_0x4_read;
+
+ struct {
+ u32 dma_addr_size :24;
+ u32 dmatimer : 7;
+ u32 unused : 1;
+ } dma_0x4_write;
+
+ struct {
+ u32 dma_cur_addr :30;
+ u32 unused : 2;
+ } dma_0x8;
+
+ struct {
+ u32 dma_address1 :30;
+ u32 remap_enable : 1;
+ u32 dma_1start : 1;
+ } dma_0xc;
+
+ struct {
+ u32 st_done : 1;
+ u32 no_base_addr_ack_error : 1;
+ u32 twoWS_port_reg : 2;
+ u32 total_bytes : 2;
+ u32 twoWS_rw : 1;
+ u32 working_start : 1;
+ u32 data1_reg : 8;
+ u32 baseaddr : 8;
+ u32 reserved1 : 1;
+ u32 chipaddr : 7;
+ } tw_sm_c_100;
+
+ struct {
+ u32 unused : 6;
+ u32 force_stop : 1;
+ u32 exlicit_stops : 1;
+ u32 data4_reg : 8;
+ u32 data3_reg : 8;
+ u32 data2_reg : 8;
+ } tw_sm_c_104;
+
+ struct {
+ u32 reserved2 :19;
+ u32 tlo1 : 5;
+ u32 reserved1 : 2;
+ u32 thi1 : 6;
+ } tw_sm_c_108;
+
+ struct {
+ u32 reserved2 :19;
+ u32 tlo1 : 5;
+ u32 reserved1 : 2;
+ u32 thi1 : 6;
+ } tw_sm_c_10c;
+
+ struct {
+ u32 reserved2 :19;
+ u32 tlo1 : 5;
+ u32 reserved1 : 2;
+ u32 thi1 : 6;
+ } tw_sm_c_110;
+
+ struct {
+ u32 LNB_CTLPrescaler_sig : 2;
+ u32 LNB_CTLLowCount_sig :15;
+ u32 LNB_CTLHighCount_sig :15;
+ } lnb_switch_freq_200;
+
+ struct {
+ u32 Rev_N_sig_reserved2 : 1;
+ u32 Rev_N_sig_caps : 1;
+ u32 Rev_N_sig_reserved1 : 2;
+ u32 Rev_N_sig_revision_hi : 4;
+ u32 reserved :20;
+ u32 Per_reset_sig : 1;
+ u32 LNB_L_H_sig : 1;
+ u32 ACPI3_sig : 1;
+ u32 ACPI1_sig : 1;
+ } misc_204;
+
+ struct {
+ u32 unused : 9;
+ u32 Mailbox_from_V8_Enable_sig : 1;
+ u32 DMA2_Size_IRQ_Enable_sig : 1;
+ u32 DMA1_Size_IRQ_Enable_sig : 1;
+ u32 DMA2_Timer_Enable_sig : 1;
+ u32 DMA2_IRQ_Enable_sig : 1;
+ u32 DMA1_Timer_Enable_sig : 1;
+ u32 DMA1_IRQ_Enable_sig : 1;
+ u32 Rcv_Data_sig : 1;
+ u32 MAC_filter_Mode_sig : 1;
+ u32 Multi2_Enable_sig : 1;
+ u32 Per_CA_Enable_sig : 1;
+ u32 SMC_Enable_sig : 1;
+ u32 CA_Enable_sig : 1;
+ u32 WAN_CA_Enable_sig : 1;
+ u32 WAN_Enable_sig : 1;
+ u32 Mask_filter_sig : 1;
+ u32 Null_filter_sig : 1;
+ u32 ECM_filter_sig : 1;
+ u32 EMM_filter_sig : 1;
+ u32 PMT_filter_sig : 1;
+ u32 PCR_filter_sig : 1;
+ u32 Stream2_filter_sig : 1;
+ u32 Stream1_filter_sig : 1;
+ } ctrl_208;
+
+ struct {
+ u32 reserved :21;
+ u32 Transport_Error : 1;
+ u32 LLC_SNAP_FLAG_set : 1;
+ u32 Continuity_error_flag : 1;
+ u32 Data_receiver_error : 1;
+ u32 Mailbox_from_V8_Status_sig : 1;
+ u32 DMA2_Size_IRQ_Status : 1;
+ u32 DMA1_Size_IRQ_Status : 1;
+ u32 DMA2_Timer_Status : 1;
+ u32 DMA2_IRQ_Status : 1;
+ u32 DMA1_Timer_Status : 1;
+ u32 DMA1_IRQ_Status : 1;
+ } irq_20c;
+
+ struct {
+ u32 Special_controls :16;
+ u32 Block_reset_enable : 8;
+ u32 reset_block_700 : 1;
+ u32 reset_block_600 : 1;
+ u32 reset_block_500 : 1;
+ u32 reset_block_400 : 1;
+ u32 reset_block_300 : 1;
+ u32 reset_block_200 : 1;
+ u32 reset_block_100 : 1;
+ u32 reset_block_000 : 1;
+ } sw_reset_210;
+
+ struct {
+ u32 unused2 :20;
+ u32 polarity_PS_ERR_sig : 1;
+ u32 polarity_PS_SYNC_sig : 1;
+ u32 polarity_PS_VALID_sig : 1;
+ u32 polarity_PS_CLK_sig : 1;
+ u32 unused1 : 3;
+ u32 s2p_sel_sig : 1;
+ u32 section_pkg_enable_sig : 1;
+ u32 halt_V8_sig : 1;
+ u32 v2WS_oe_sig : 1;
+ u32 vuart_oe_sig : 1;
+ } misc_214;
+
+ struct {
+ u32 Mailbox_from_V8 :32;
+ } mbox_v8_to_host_218;
+
+ struct {
+ u32 sysramaccess_busmuster : 1;
+ u32 sysramaccess_write : 1;
+ u32 unused : 7;
+ u32 sysramaccess_addr :15;
+ u32 sysramaccess_data : 8;
+ } mbox_host_to_v8_21c;
+
+ struct {
+ u32 debug_fifo_problem : 1;
+ u32 debug_flag_write_status00 : 1;
+ u32 Stream2_trans : 1;
+ u32 Stream2_PID :13;
+ u32 debug_flag_pid_saved : 1;
+ u32 MAC_Multicast_filter : 1;
+ u32 Stream1_trans : 1;
+ u32 Stream1_PID :13;
+ } pid_filter_300;
+
+ struct {
+ u32 reserved : 2;
+ u32 PMT_trans : 1;
+ u32 PMT_PID :13;
+ u32 debug_overrun2 : 1;
+ u32 debug_overrun3 : 1;
+ u32 PCR_trans : 1;
+ u32 PCR_PID :13;
+ } pid_filter_304;
+
+ struct {
+ u32 reserved : 2;
+ u32 ECM_trans : 1;
+ u32 ECM_PID :13;
+ u32 EMM_filter_6 : 1;
+ u32 EMM_filter_4 : 1;
+ u32 EMM_trans : 1;
+ u32 EMM_PID :13;
+ } pid_filter_308;
+
+ struct {
+ u32 unused2 : 3;
+ u32 Group_mask :13;
+ u32 unused1 : 2;
+ u32 Group_trans : 1;
+ u32 Group_PID :13;
+ } pid_filter_30c_ext_ind_0_7;
+
+ struct {
+ u32 unused :15;
+ u32 net_master_read :17;
+ } pid_filter_30c_ext_ind_1;
+
+ struct {
+ u32 unused :15;
+ u32 net_master_write :17;
+ } pid_filter_30c_ext_ind_2;
+
+ struct {
+ u32 unused :15;
+ u32 next_net_master_write :17;
+ } pid_filter_30c_ext_ind_3;
+
+ struct {
+ u32 reserved2 : 5;
+ u32 stack_read :10;
+ u32 reserved1 : 6;
+ u32 state_write :10;
+ u32 unused1 : 1;
+ } pid_filter_30c_ext_ind_4;
+
+ struct {
+ u32 unused :22;
+ u32 stack_cnt :10;
+ } pid_filter_30c_ext_ind_5;
+
+ struct {
+ u32 unused : 4;
+ u32 data_size_reg :12;
+ u32 write_status4 : 2;
+ u32 write_status1 : 2;
+ u32 pid_fsm_save_reg300 : 2;
+ u32 pid_fsm_save_reg4 : 2;
+ u32 pid_fsm_save_reg3 : 2;
+ u32 pid_fsm_save_reg2 : 2;
+ u32 pid_fsm_save_reg1 : 2;
+ u32 pid_fsm_save_reg0 : 2;
+ } pid_filter_30c_ext_ind_6;
+
+ struct {
+ u32 unused :22;
+ u32 pass_alltables : 1;
+ u32 AB_select : 1;
+ u32 extra_index_reg : 3;
+ u32 index_reg : 5;
+ } index_reg_310;
+
+ struct {
+ u32 reserved :17;
+ u32 PID_enable_bit : 1;
+ u32 PID_trans : 1;
+ u32 PID :13;
+ } pid_n_reg_314;
+
+ struct {
+ u32 reserved : 6;
+ u32 HighAB_bit : 1;
+ u32 Enable_bit : 1;
+ u32 A6_byte : 8;
+ u32 A5_byte : 8;
+ u32 A4_byte : 8;
+ } mac_low_reg_318;
+
+ struct {
+ u32 reserved : 8;
+ u32 A3_byte : 8;
+ u32 A2_byte : 8;
+ u32 A1_byte : 8;
+ } mac_high_reg_31c;
+
+ struct {
+ u32 data_Tag_ID :16;
+ u32 reserved :16;
+ } data_tag_400;
+
+ struct {
+ u32 Card_IDbyte3 : 8;
+ u32 Card_IDbyte4 : 8;
+ u32 Card_IDbyte5 : 8;
+ u32 Card_IDbyte6 : 8;
+ } card_id_408;
+
+ struct {
+ u32 Card_IDbyte1 : 8;
+ u32 Card_IDbyte2 : 8;
+ } card_id_40c;
+
+ struct {
+ u32 MAC6 : 8;
+ u32 MAC3 : 8;
+ u32 MAC2 : 8;
+ u32 MAC1 : 8;
+ } mac_address_418;
+
+ struct {
+ u32 reserved :16;
+ u32 MAC8 : 8;
+ u32 MAC7 : 8;
+ } mac_address_41c;
+
+ struct {
+ u32 reserved :21;
+ u32 txbuffempty : 1;
+ u32 ReceiveByteFrameError : 1;
+ u32 ReceiveDataReady : 1;
+ u32 transmitter_data_byte : 8;
+ } ci_600;
+
+ struct {
+ u32 pi_component_reg : 3;
+ u32 pi_rw : 1;
+ u32 pi_ha :20;
+ u32 pi_d : 8;
+ } pi_604;
+
+ struct {
+ u32 pi_busy_n : 1;
+ u32 pi_wait_n : 1;
+ u32 pi_timeout_status : 1;
+ u32 pi_CiMax_IRQ_n : 1;
+ u32 config_cclk : 1;
+ u32 config_cs_n : 1;
+ u32 config_wr_n : 1;
+ u32 config_Prog_n : 1;
+ u32 config_Init_stat : 1;
+ u32 config_Done_stat : 1;
+ u32 pcmcia_b_mod_pwr_n : 1;
+ u32 pcmcia_a_mod_pwr_n : 1;
+ u32 reserved : 3;
+ u32 Timer_addr : 5;
+ u32 unused : 1;
+ u32 timer_data : 7;
+ u32 Timer_Load_req : 1;
+ u32 Timer_Read_req : 1;
+ u32 oncecycle_read : 1;
+ u32 serialReset : 1;
+ } pi_608;
+
+ struct {
+ u32 reserved : 6;
+ u32 rw_flag : 1;
+ u32 dvb_en : 1;
+ u32 key_array_row : 5;
+ u32 key_array_col : 3;
+ u32 key_code : 2;
+ u32 key_enable : 1;
+ u32 PID :13;
+ } dvb_reg_60c;
+
+ struct {
+ u32 start_sram_ibi : 1;
+ u32 reserved2 : 1;
+ u32 ce_pin_reg : 1;
+ u32 oe_pin_reg : 1;
+ u32 reserved1 : 3;
+ u32 sc_xfer_bit : 1;
+ u32 sram_data : 8;
+ u32 sram_rw : 1;
+ u32 sram_addr :15;
+ } sram_ctrl_reg_700;
+
+ struct {
+ u32 net_addr_write :16;
+ u32 net_addr_read :16;
+ } net_buf_reg_704;
+
+ struct {
+ u32 cai_cnt : 4;
+ u32 reserved2 : 6;
+ u32 cai_write :11;
+ u32 reserved1 : 5;
+ u32 cai_read :11;
+ } cai_buf_reg_708;
+
+ struct {
+ u32 cao_cnt : 4;
+ u32 reserved2 : 6;
+ u32 cap_write :11;
+ u32 reserved1 : 5;
+ u32 cao_read :11;
+ } cao_buf_reg_70c;
+
+ struct {
+ u32 media_cnt : 4;
+ u32 reserved2 : 6;
+ u32 media_write :11;
+ u32 reserved1 : 5;
+ u32 media_read :11;
+ } media_buf_reg_710;
+
+ struct {
+ u32 reserved :17;
+ u32 ctrl_maximumfill : 1;
+ u32 ctrl_sramdma : 1;
+ u32 ctrl_usb_wan : 1;
+ u32 cao_ovflow_error : 1;
+ u32 cai_ovflow_error : 1;
+ u32 media_ovflow_error : 1;
+ u32 net_ovflow_error : 1;
+ u32 MEDIA_Dest : 2;
+ u32 CAO_Dest : 2;
+ u32 CAI_Dest : 2;
+ u32 NET_Dest : 2;
+ } sram_dest_reg_714;
+
+ struct {
+ u32 reserved3 :11;
+ u32 net_addr_write : 1;
+ u32 reserved2 : 3;
+ u32 net_addr_read : 1;
+ u32 reserved1 : 4;
+ u32 net_cnt :12;
+ } net_buf_reg_718;
+
+ struct {
+ u32 reserved3 : 4;
+ u32 wan_pkt_frame : 4;
+ u32 reserved2 : 4;
+ u32 sram_memmap : 2;
+ u32 sram_chip : 2;
+ u32 wan_wait_state : 8;
+ u32 reserved1 : 6;
+ u32 wan_speed_sig : 2;
+ } wan_ctrl_reg_71c;
+} flexcop_ibi_value;
+
+#endif
diff --git a/drivers/media/common/b2c2/flexcop_ibi_value_le.h b/drivers/media/common/b2c2/flexcop_ibi_value_le.h
new file mode 100644
index 000000000..c75830d7d
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop_ibi_value_le.h
@@ -0,0 +1,455 @@
+/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * register descriptions
+ * see flexcop.c for copyright information
+ */
+/* This file is automatically generated, do not edit things here. */
+#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__
+#define __FLEXCOP_IBI_VALUE_INCLUDED__
+
+typedef union {
+ u32 raw;
+
+ struct {
+ u32 dma_0start : 1;
+ u32 dma_0No_update : 1;
+ u32 dma_address0 :30;
+ } dma_0x0;
+
+ struct {
+ u32 DMA_maxpackets : 8;
+ u32 dma_addr_size :24;
+ } dma_0x4_remap;
+
+ struct {
+ u32 dma1timer : 7;
+ u32 unused : 1;
+ u32 dma_addr_size :24;
+ } dma_0x4_read;
+
+ struct {
+ u32 unused : 1;
+ u32 dmatimer : 7;
+ u32 dma_addr_size :24;
+ } dma_0x4_write;
+
+ struct {
+ u32 unused : 2;
+ u32 dma_cur_addr :30;
+ } dma_0x8;
+
+ struct {
+ u32 dma_1start : 1;
+ u32 remap_enable : 1;
+ u32 dma_address1 :30;
+ } dma_0xc;
+
+ struct {
+ u32 chipaddr : 7;
+ u32 reserved1 : 1;
+ u32 baseaddr : 8;
+ u32 data1_reg : 8;
+ u32 working_start : 1;
+ u32 twoWS_rw : 1;
+ u32 total_bytes : 2;
+ u32 twoWS_port_reg : 2;
+ u32 no_base_addr_ack_error : 1;
+ u32 st_done : 1;
+ } tw_sm_c_100;
+
+ struct {
+ u32 data2_reg : 8;
+ u32 data3_reg : 8;
+ u32 data4_reg : 8;
+ u32 exlicit_stops : 1;
+ u32 force_stop : 1;
+ u32 unused : 6;
+ } tw_sm_c_104;
+
+ struct {
+ u32 thi1 : 6;
+ u32 reserved1 : 2;
+ u32 tlo1 : 5;
+ u32 reserved2 :19;
+ } tw_sm_c_108;
+
+ struct {
+ u32 thi1 : 6;
+ u32 reserved1 : 2;
+ u32 tlo1 : 5;
+ u32 reserved2 :19;
+ } tw_sm_c_10c;
+
+ struct {
+ u32 thi1 : 6;
+ u32 reserved1 : 2;
+ u32 tlo1 : 5;
+ u32 reserved2 :19;
+ } tw_sm_c_110;
+
+ struct {
+ u32 LNB_CTLHighCount_sig :15;
+ u32 LNB_CTLLowCount_sig :15;
+ u32 LNB_CTLPrescaler_sig : 2;
+ } lnb_switch_freq_200;
+
+ struct {
+ u32 ACPI1_sig : 1;
+ u32 ACPI3_sig : 1;
+ u32 LNB_L_H_sig : 1;
+ u32 Per_reset_sig : 1;
+ u32 reserved :20;
+ u32 Rev_N_sig_revision_hi : 4;
+ u32 Rev_N_sig_reserved1 : 2;
+ u32 Rev_N_sig_caps : 1;
+ u32 Rev_N_sig_reserved2 : 1;
+ } misc_204;
+
+ struct {
+ u32 Stream1_filter_sig : 1;
+ u32 Stream2_filter_sig : 1;
+ u32 PCR_filter_sig : 1;
+ u32 PMT_filter_sig : 1;
+ u32 EMM_filter_sig : 1;
+ u32 ECM_filter_sig : 1;
+ u32 Null_filter_sig : 1;
+ u32 Mask_filter_sig : 1;
+ u32 WAN_Enable_sig : 1;
+ u32 WAN_CA_Enable_sig : 1;
+ u32 CA_Enable_sig : 1;
+ u32 SMC_Enable_sig : 1;
+ u32 Per_CA_Enable_sig : 1;
+ u32 Multi2_Enable_sig : 1;
+ u32 MAC_filter_Mode_sig : 1;
+ u32 Rcv_Data_sig : 1;
+ u32 DMA1_IRQ_Enable_sig : 1;
+ u32 DMA1_Timer_Enable_sig : 1;
+ u32 DMA2_IRQ_Enable_sig : 1;
+ u32 DMA2_Timer_Enable_sig : 1;
+ u32 DMA1_Size_IRQ_Enable_sig : 1;
+ u32 DMA2_Size_IRQ_Enable_sig : 1;
+ u32 Mailbox_from_V8_Enable_sig : 1;
+ u32 unused : 9;
+ } ctrl_208;
+
+ struct {
+ u32 DMA1_IRQ_Status : 1;
+ u32 DMA1_Timer_Status : 1;
+ u32 DMA2_IRQ_Status : 1;
+ u32 DMA2_Timer_Status : 1;
+ u32 DMA1_Size_IRQ_Status : 1;
+ u32 DMA2_Size_IRQ_Status : 1;
+ u32 Mailbox_from_V8_Status_sig : 1;
+ u32 Data_receiver_error : 1;
+ u32 Continuity_error_flag : 1;
+ u32 LLC_SNAP_FLAG_set : 1;
+ u32 Transport_Error : 1;
+ u32 reserved :21;
+ } irq_20c;
+
+ struct {
+ u32 reset_block_000 : 1;
+ u32 reset_block_100 : 1;
+ u32 reset_block_200 : 1;
+ u32 reset_block_300 : 1;
+ u32 reset_block_400 : 1;
+ u32 reset_block_500 : 1;
+ u32 reset_block_600 : 1;
+ u32 reset_block_700 : 1;
+ u32 Block_reset_enable : 8;
+ u32 Special_controls :16;
+ } sw_reset_210;
+
+ struct {
+ u32 vuart_oe_sig : 1;
+ u32 v2WS_oe_sig : 1;
+ u32 halt_V8_sig : 1;
+ u32 section_pkg_enable_sig : 1;
+ u32 s2p_sel_sig : 1;
+ u32 unused1 : 3;
+ u32 polarity_PS_CLK_sig : 1;
+ u32 polarity_PS_VALID_sig : 1;
+ u32 polarity_PS_SYNC_sig : 1;
+ u32 polarity_PS_ERR_sig : 1;
+ u32 unused2 :20;
+ } misc_214;
+
+ struct {
+ u32 Mailbox_from_V8 :32;
+ } mbox_v8_to_host_218;
+
+ struct {
+ u32 sysramaccess_data : 8;
+ u32 sysramaccess_addr :15;
+ u32 unused : 7;
+ u32 sysramaccess_write : 1;
+ u32 sysramaccess_busmuster : 1;
+ } mbox_host_to_v8_21c;
+
+ struct {
+ u32 Stream1_PID :13;
+ u32 Stream1_trans : 1;
+ u32 MAC_Multicast_filter : 1;
+ u32 debug_flag_pid_saved : 1;
+ u32 Stream2_PID :13;
+ u32 Stream2_trans : 1;
+ u32 debug_flag_write_status00 : 1;
+ u32 debug_fifo_problem : 1;
+ } pid_filter_300;
+
+ struct {
+ u32 PCR_PID :13;
+ u32 PCR_trans : 1;
+ u32 debug_overrun3 : 1;
+ u32 debug_overrun2 : 1;
+ u32 PMT_PID :13;
+ u32 PMT_trans : 1;
+ u32 reserved : 2;
+ } pid_filter_304;
+
+ struct {
+ u32 EMM_PID :13;
+ u32 EMM_trans : 1;
+ u32 EMM_filter_4 : 1;
+ u32 EMM_filter_6 : 1;
+ u32 ECM_PID :13;
+ u32 ECM_trans : 1;
+ u32 reserved : 2;
+ } pid_filter_308;
+
+ struct {
+ u32 Group_PID :13;
+ u32 Group_trans : 1;
+ u32 unused1 : 2;
+ u32 Group_mask :13;
+ u32 unused2 : 3;
+ } pid_filter_30c_ext_ind_0_7;
+
+ struct {
+ u32 net_master_read :17;
+ u32 unused :15;
+ } pid_filter_30c_ext_ind_1;
+
+ struct {
+ u32 net_master_write :17;
+ u32 unused :15;
+ } pid_filter_30c_ext_ind_2;
+
+ struct {
+ u32 next_net_master_write :17;
+ u32 unused :15;
+ } pid_filter_30c_ext_ind_3;
+
+ struct {
+ u32 unused1 : 1;
+ u32 state_write :10;
+ u32 reserved1 : 6;
+ u32 stack_read :10;
+ u32 reserved2 : 5;
+ } pid_filter_30c_ext_ind_4;
+
+ struct {
+ u32 stack_cnt :10;
+ u32 unused :22;
+ } pid_filter_30c_ext_ind_5;
+
+ struct {
+ u32 pid_fsm_save_reg0 : 2;
+ u32 pid_fsm_save_reg1 : 2;
+ u32 pid_fsm_save_reg2 : 2;
+ u32 pid_fsm_save_reg3 : 2;
+ u32 pid_fsm_save_reg4 : 2;
+ u32 pid_fsm_save_reg300 : 2;
+ u32 write_status1 : 2;
+ u32 write_status4 : 2;
+ u32 data_size_reg :12;
+ u32 unused : 4;
+ } pid_filter_30c_ext_ind_6;
+
+ struct {
+ u32 index_reg : 5;
+ u32 extra_index_reg : 3;
+ u32 AB_select : 1;
+ u32 pass_alltables : 1;
+ u32 unused :22;
+ } index_reg_310;
+
+ struct {
+ u32 PID :13;
+ u32 PID_trans : 1;
+ u32 PID_enable_bit : 1;
+ u32 reserved :17;
+ } pid_n_reg_314;
+
+ struct {
+ u32 A4_byte : 8;
+ u32 A5_byte : 8;
+ u32 A6_byte : 8;
+ u32 Enable_bit : 1;
+ u32 HighAB_bit : 1;
+ u32 reserved : 6;
+ } mac_low_reg_318;
+
+ struct {
+ u32 A1_byte : 8;
+ u32 A2_byte : 8;
+ u32 A3_byte : 8;
+ u32 reserved : 8;
+ } mac_high_reg_31c;
+
+ struct {
+ u32 reserved :16;
+ u32 data_Tag_ID :16;
+ } data_tag_400;
+
+ struct {
+ u32 Card_IDbyte6 : 8;
+ u32 Card_IDbyte5 : 8;
+ u32 Card_IDbyte4 : 8;
+ u32 Card_IDbyte3 : 8;
+ } card_id_408;
+
+ struct {
+ u32 Card_IDbyte2 : 8;
+ u32 Card_IDbyte1 : 8;
+ } card_id_40c;
+
+ struct {
+ u32 MAC1 : 8;
+ u32 MAC2 : 8;
+ u32 MAC3 : 8;
+ u32 MAC6 : 8;
+ } mac_address_418;
+
+ struct {
+ u32 MAC7 : 8;
+ u32 MAC8 : 8;
+ u32 reserved :16;
+ } mac_address_41c;
+
+ struct {
+ u32 transmitter_data_byte : 8;
+ u32 ReceiveDataReady : 1;
+ u32 ReceiveByteFrameError : 1;
+ u32 txbuffempty : 1;
+ u32 reserved :21;
+ } ci_600;
+
+ struct {
+ u32 pi_d : 8;
+ u32 pi_ha :20;
+ u32 pi_rw : 1;
+ u32 pi_component_reg : 3;
+ } pi_604;
+
+ struct {
+ u32 serialReset : 1;
+ u32 oncecycle_read : 1;
+ u32 Timer_Read_req : 1;
+ u32 Timer_Load_req : 1;
+ u32 timer_data : 7;
+ u32 unused : 1;
+ u32 Timer_addr : 5;
+ u32 reserved : 3;
+ u32 pcmcia_a_mod_pwr_n : 1;
+ u32 pcmcia_b_mod_pwr_n : 1;
+ u32 config_Done_stat : 1;
+ u32 config_Init_stat : 1;
+ u32 config_Prog_n : 1;
+ u32 config_wr_n : 1;
+ u32 config_cs_n : 1;
+ u32 config_cclk : 1;
+ u32 pi_CiMax_IRQ_n : 1;
+ u32 pi_timeout_status : 1;
+ u32 pi_wait_n : 1;
+ u32 pi_busy_n : 1;
+ } pi_608;
+
+ struct {
+ u32 PID :13;
+ u32 key_enable : 1;
+ u32 key_code : 2;
+ u32 key_array_col : 3;
+ u32 key_array_row : 5;
+ u32 dvb_en : 1;
+ u32 rw_flag : 1;
+ u32 reserved : 6;
+ } dvb_reg_60c;
+
+ struct {
+ u32 sram_addr :15;
+ u32 sram_rw : 1;
+ u32 sram_data : 8;
+ u32 sc_xfer_bit : 1;
+ u32 reserved1 : 3;
+ u32 oe_pin_reg : 1;
+ u32 ce_pin_reg : 1;
+ u32 reserved2 : 1;
+ u32 start_sram_ibi : 1;
+ } sram_ctrl_reg_700;
+
+ struct {
+ u32 net_addr_read :16;
+ u32 net_addr_write :16;
+ } net_buf_reg_704;
+
+ struct {
+ u32 cai_read :11;
+ u32 reserved1 : 5;
+ u32 cai_write :11;
+ u32 reserved2 : 6;
+ u32 cai_cnt : 4;
+ } cai_buf_reg_708;
+
+ struct {
+ u32 cao_read :11;
+ u32 reserved1 : 5;
+ u32 cap_write :11;
+ u32 reserved2 : 6;
+ u32 cao_cnt : 4;
+ } cao_buf_reg_70c;
+
+ struct {
+ u32 media_read :11;
+ u32 reserved1 : 5;
+ u32 media_write :11;
+ u32 reserved2 : 6;
+ u32 media_cnt : 4;
+ } media_buf_reg_710;
+
+ struct {
+ u32 NET_Dest : 2;
+ u32 CAI_Dest : 2;
+ u32 CAO_Dest : 2;
+ u32 MEDIA_Dest : 2;
+ u32 net_ovflow_error : 1;
+ u32 media_ovflow_error : 1;
+ u32 cai_ovflow_error : 1;
+ u32 cao_ovflow_error : 1;
+ u32 ctrl_usb_wan : 1;
+ u32 ctrl_sramdma : 1;
+ u32 ctrl_maximumfill : 1;
+ u32 reserved :17;
+ } sram_dest_reg_714;
+
+ struct {
+ u32 net_cnt :12;
+ u32 reserved1 : 4;
+ u32 net_addr_read : 1;
+ u32 reserved2 : 3;
+ u32 net_addr_write : 1;
+ u32 reserved3 :11;
+ } net_buf_reg_718;
+
+ struct {
+ u32 wan_speed_sig : 2;
+ u32 reserved1 : 6;
+ u32 wan_wait_state : 8;
+ u32 sram_chip : 2;
+ u32 sram_memmap : 2;
+ u32 reserved2 : 4;
+ u32 wan_pkt_frame : 4;
+ u32 reserved3 : 4;
+ } wan_ctrl_reg_71c;
+} flexcop_ibi_value;
+
+#endif
diff --git a/drivers/media/common/btcx-risc.h b/drivers/media/common/btcx-risc.h
new file mode 100644
index 000000000..03583ef90
--- /dev/null
+++ b/drivers/media/common/btcx-risc.h
@@ -0,0 +1,28 @@
+/*
+ */
+struct btcx_riscmem {
+ unsigned int size;
+ __le32 *cpu;
+ __le32 *jmp;
+ dma_addr_t dma;
+};
+
+struct btcx_skiplist {
+ int start;
+ int end;
+};
+
+int btcx_riscmem_alloc(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ unsigned int size);
+void btcx_riscmem_free(struct pci_dev *pci,
+ struct btcx_riscmem *risc);
+
+int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win,
+ struct v4l2_clip *clips, unsigned int n);
+int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips,
+ unsigned int n, int mask);
+void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips);
+void btcx_calc_skips(int line, int width, int *maxy,
+ struct btcx_skiplist *skips, unsigned int *nskips,
+ const struct v4l2_clip *clips, unsigned int nclips);
diff --git a/drivers/media/common/cx2341x.c b/drivers/media/common/cx2341x.c
new file mode 100644
index 000000000..c07b9db51
--- /dev/null
+++ b/drivers/media/common/cx2341x.c
@@ -0,0 +1,1756 @@
+/*
+ * cx2341x - generic code for cx23415/6/8 based devices
+ *
+ * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/tuner.h>
+#include <media/cx2341x.h>
+#include <media/v4l2-common.h>
+
+MODULE_DESCRIPTION("cx23415/6/8 driver");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/********************** COMMON CODE *********************/
+
+/* definitions for audio properties bits 29-28 */
+#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0
+#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1
+#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2
+
+static const char *cx2341x_get_name(u32 id)
+{
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return "Spatial Filter Mode";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ return "Spatial Filter";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ return "Spatial Luma Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ return "Spatial Chroma Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return "Temporal Filter Mode";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ return "Temporal Filter";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return "Median Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ return "Median Luma Filter Maximum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ return "Median Luma Filter Minimum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ return "Median Chroma Filter Maximum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ return "Median Chroma Filter Minimum";
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return "Insert Navigation Packets";
+ }
+ return NULL;
+}
+
+static const char **cx2341x_get_menu(u32 id)
+{
+ static const char *cx2341x_video_spatial_filter_mode_menu[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+
+ static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
+ "Off",
+ "1D Horizontal",
+ "1D Vertical",
+ "2D H/V Separable",
+ "2D Symmetric non-separable",
+ NULL
+ };
+
+ static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
+ "Off",
+ "1D Horizontal",
+ NULL
+ };
+
+ static const char *cx2341x_video_temporal_filter_mode_menu[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+
+ static const char *cx2341x_video_median_filter_type_menu[] = {
+ "Off",
+ "Horizontal",
+ "Vertical",
+ "Horizontal/Vertical",
+ "Diagonal",
+ NULL
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return cx2341x_video_spatial_filter_mode_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ return cx2341x_video_luma_spatial_filter_type_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ return cx2341x_video_chroma_spatial_filter_type_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return cx2341x_video_temporal_filter_mode_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return cx2341x_video_median_filter_type_menu;
+ }
+ return NULL;
+}
+
+static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+ *name = cx2341x_get_name(id);
+ *flags = 0;
+
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ *type = V4L2_CTRL_TYPE_MENU;
+ *min = 0;
+ *step = 0;
+ break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ *type = V4L2_CTRL_TYPE_BOOLEAN;
+ *min = 0;
+ *max = *step = 1;
+ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+ }
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ *flags |= V4L2_CTRL_FLAG_UPDATE;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ *flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
+ }
+}
+
+
+/********************** OLD CODE *********************/
+
+/* Must be sorted from low to high control ID! */
+const u32 cx2341x_mpeg_ctrls[] = {
+ V4L2_CID_MPEG_CLASS,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_CID_MPEG_STREAM_VBI_FMT,
+ V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ V4L2_CID_MPEG_AUDIO_MODE,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+ V4L2_CID_MPEG_AUDIO_EMPHASIS,
+ V4L2_CID_MPEG_AUDIO_CRC,
+ V4L2_CID_MPEG_AUDIO_MUTE,
+ V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
+ V4L2_CID_MPEG_VIDEO_MUTE,
+ V4L2_CID_MPEG_VIDEO_MUTE_YUV,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+ V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
+ 0
+};
+EXPORT_SYMBOL(cx2341x_mpeg_ctrls);
+
+static const struct cx2341x_mpeg_params default_params = {
+ /* misc */
+ .capabilities = 0,
+ .port = CX2341X_PORT_MEMORY,
+ .width = 720,
+ .height = 480,
+ .is_50hz = 0,
+
+ /* stream */
+ .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+ .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ .stream_insert_nav_packets = 0,
+
+ /* audio */
+ .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+ .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K,
+ .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K,
+ .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO,
+ .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
+ .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE,
+ .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE,
+ .audio_mute = 0,
+
+ /* video */
+ .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
+ .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3,
+ .video_b_frames = 2,
+ .video_gop_size = 12,
+ .video_gop_closure = 1,
+ .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ .video_bitrate = 6000000,
+ .video_bitrate_peak = 8000000,
+ .video_temporal_decimation = 0,
+ .video_mute = 0,
+ .video_mute_yuv = 0x008080, /* YCbCr value for black */
+
+ /* encoding filters */
+ .video_spatial_filter_mode =
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ .video_spatial_filter = 0,
+ .video_luma_spatial_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ .video_chroma_spatial_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ .video_temporal_filter_mode =
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ .video_temporal_filter = 8,
+ .video_median_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ .video_luma_median_filter_top = 255,
+ .video_luma_median_filter_bottom = 0,
+ .video_chroma_median_filter_top = 255,
+ .video_chroma_median_filter_bottom = 0,
+};
+/* Map the control ID to the correct field in the cx2341x_mpeg_params
+ struct. Return -EINVAL if the ID is unknown, else return 0. */
+static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
+ struct v4l2_ext_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ ctrl->value = params->audio_sampling_freq;
+ break;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ ctrl->value = params->audio_encoding;
+ break;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ ctrl->value = params->audio_l2_bitrate;
+ break;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ ctrl->value = params->audio_ac3_bitrate;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ ctrl->value = params->audio_mode;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ ctrl->value = params->audio_mode_extension;
+ break;
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ ctrl->value = params->audio_emphasis;
+ break;
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ ctrl->value = params->audio_crc;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ ctrl->value = params->audio_mute;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ ctrl->value = params->video_encoding;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ ctrl->value = params->video_aspect;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ ctrl->value = params->video_b_frames;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctrl->value = params->video_gop_size;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ ctrl->value = params->video_gop_closure;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ ctrl->value = params->video_bitrate_mode;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctrl->value = params->video_bitrate;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ ctrl->value = params->video_bitrate_peak;
+ break;
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ ctrl->value = params->video_temporal_decimation;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ ctrl->value = params->video_mute;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV:
+ ctrl->value = params->video_mute_yuv;
+ break;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ ctrl->value = params->stream_type;
+ break;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ ctrl->value = params->stream_vbi_fmt;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ ctrl->value = params->video_spatial_filter_mode;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ ctrl->value = params->video_spatial_filter;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ ctrl->value = params->video_luma_spatial_filter_type;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ ctrl->value = params->video_chroma_spatial_filter_type;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ ctrl->value = params->video_temporal_filter_mode;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ ctrl->value = params->video_temporal_filter;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ ctrl->value = params->video_median_filter_type;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ ctrl->value = params->video_luma_median_filter_top;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ ctrl->value = params->video_luma_median_filter_bottom;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ ctrl->value = params->video_chroma_median_filter_top;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ ctrl->value = params->video_chroma_median_filter_bottom;
+ break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ ctrl->value = params->stream_insert_nav_packets;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Map the control ID to the correct field in the cx2341x_mpeg_params
+ struct. Return -EINVAL if the ID is unknown, else return 0. */
+static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy,
+ struct v4l2_ext_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ if (busy)
+ return -EBUSY;
+ params->audio_sampling_freq = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ if (busy)
+ return -EBUSY;
+ if (params->capabilities & CX2341X_CAP_HAS_AC3)
+ if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
+ ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3)
+ return -ERANGE;
+ params->audio_encoding = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ if (busy)
+ return -EBUSY;
+ params->audio_l2_bitrate = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ if (busy)
+ return -EBUSY;
+ if (!(params->capabilities & CX2341X_CAP_HAS_AC3))
+ return -EINVAL;
+ params->audio_ac3_bitrate = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ params->audio_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ params->audio_mode_extension = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ params->audio_emphasis = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ params->audio_crc = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ params->audio_mute = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ params->video_aspect = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: {
+ int b = ctrl->value + 1;
+ int gop = params->video_gop_size;
+ params->video_b_frames = ctrl->value;
+ params->video_gop_size = b * ((gop + b - 1) / b);
+ /* Max GOP size = 34 */
+ while (params->video_gop_size > 34)
+ params->video_gop_size -= b;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: {
+ int b = params->video_b_frames + 1;
+ int gop = ctrl->value;
+ params->video_gop_size = b * ((gop + b - 1) / b);
+ /* Max GOP size = 34 */
+ while (params->video_gop_size > 34)
+ params->video_gop_size -= b;
+ ctrl->value = params->video_gop_size;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ params->video_gop_closure = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ if (busy)
+ return -EBUSY;
+ /* MPEG-1 only allows CBR */
+ if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 &&
+ ctrl->value != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ return -EINVAL;
+ params->video_bitrate_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ if (busy)
+ return -EBUSY;
+ params->video_bitrate = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ if (busy)
+ return -EBUSY;
+ params->video_bitrate_peak = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ params->video_temporal_decimation = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ params->video_mute = (ctrl->value != 0);
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV:
+ params->video_mute_yuv = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ if (busy)
+ return -EBUSY;
+ params->stream_type = ctrl->value;
+ params->video_encoding =
+ (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+ params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ /* MPEG-1 implies CBR */
+ params->video_bitrate_mode =
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+ break;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ params->stream_vbi_fmt = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ params->video_spatial_filter_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ params->video_spatial_filter = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ params->video_luma_spatial_filter_type = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ params->video_chroma_spatial_filter_type = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ params->video_temporal_filter_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ params->video_temporal_filter = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ params->video_median_filter_type = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ params->video_luma_median_filter_top = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ params->video_luma_median_filter_bottom = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ params->video_chroma_median_filter_top = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ params->video_chroma_median_filter_bottom = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ params->stream_insert_nav_packets = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl,
+ s32 min, s32 max, s32 step, s32 def)
+{
+ const char *name;
+
+ switch (qctrl->id) {
+ /* MPEG controls */
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type,
+ &min, &max, &step, &def, &qctrl->flags);
+ qctrl->minimum = min;
+ qctrl->maximum = max;
+ qctrl->step = step;
+ qctrl->default_value = def;
+ qctrl->reserved[0] = qctrl->reserved[1] = 0;
+ strlcpy(qctrl->name, name, sizeof(qctrl->name));
+ return 0;
+
+ default:
+ return v4l2_ctrl_query_fill(qctrl, min, max, step, def);
+ }
+}
+
+int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
+ struct v4l2_queryctrl *qctrl)
+{
+ int err;
+
+ switch (qctrl->id) {
+ case V4L2_CID_MPEG_CLASS:
+ return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI)
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE);
+ return cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE, 1,
+ default_params.stream_vbi_fmt);
+
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ if (params->capabilities & CX2341X_CAP_HAS_AC3) {
+ /*
+ * The state of L2 & AC3 bitrate controls can change
+ * when this control changes, but v4l2_ctrl_query_fill()
+ * already sets V4L2_CTRL_FLAG_UPDATE for
+ * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here.
+ */
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_MPEG_AUDIO_ENCODING_AC3, 1,
+ default_params.audio_encoding);
+ }
+
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1,
+ default_params.audio_encoding);
+
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_L2_BITRATE_192K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
+ default_params.audio_l2_bitrate);
+ if (err)
+ return err;
+ if (params->capabilities & CX2341X_CAP_HAS_AC3 &&
+ params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_MODE_STEREO,
+ V4L2_MPEG_AUDIO_MODE_MONO, 1,
+ V4L2_MPEG_AUDIO_MODE_STEREO);
+
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
+ if (err == 0 &&
+ params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE,
+ V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1,
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE);
+
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_CRC_NONE,
+ V4L2_MPEG_AUDIO_CRC_CRC16, 1,
+ V4L2_MPEG_AUDIO_CRC_NONE);
+
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_48K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1,
+ default_params.audio_ac3_bitrate);
+ if (err)
+ return err;
+ if (params->capabilities & CX2341X_CAP_HAS_AC3) {
+ if (params->audio_encoding !=
+ V4L2_MPEG_AUDIO_ENCODING_AC3)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ } else
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ /* this setting is read-only for the cx2341x since the
+ V4L2_CID_MPEG_STREAM_TYPE really determines the
+ MPEG-1/2 setting */
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+ if (err == 0)
+ qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ return err;
+
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_ASPECT_1x1,
+ V4L2_MPEG_VIDEO_ASPECT_221x100, 1,
+ V4L2_MPEG_VIDEO_ASPECT_4x3);
+
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2);
+
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ return v4l2_ctrl_query_fill(qctrl, 1, 34, 1,
+ params->is_50hz ? 12 : 15);
+
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ if (err == 0 &&
+ params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
+ if (err == 0 &&
+ params->video_bitrate_mode ==
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */
+ return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080);
+
+ /* CX23415/6 specific */
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1,
+ default_params.video_spatial_filter_mode);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ cx2341x_ctrl_query_fill(qctrl, 0, 15, 1,
+ default_params.video_spatial_filter);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+ 1,
+ default_params.video_luma_spatial_filter_type);
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ 1,
+ default_params.video_chroma_spatial_filter_type);
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1,
+ default_params.video_temporal_filter_mode);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ cx2341x_ctrl_query_fill(qctrl, 0, 31, 1,
+ default_params.video_temporal_filter);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_temporal_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1,
+ default_params.video_median_filter_type);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+ default_params.video_luma_median_filter_top);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+ default_params.video_luma_median_filter_bottom);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+ default_params.video_chroma_median_filter_top);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+ default_params.video_chroma_median_filter_bottom);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1,
+ default_params.stream_insert_nav_packets);
+
+ default:
+ return -EINVAL;
+
+ }
+}
+EXPORT_SYMBOL(cx2341x_ctrl_query);
+
+const char * const *cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
+{
+ static const char * const mpeg_stream_type_without_ts[] = {
+ "MPEG-2 Program Stream",
+ "",
+ "MPEG-1 System Stream",
+ "MPEG-2 DVD-compatible Stream",
+ "MPEG-1 VCD-compatible Stream",
+ "MPEG-2 SVCD-compatible Stream",
+ NULL
+ };
+
+ static const char *mpeg_stream_type_with_ts[] = {
+ "MPEG-2 Program Stream",
+ "MPEG-2 Transport Stream",
+ "MPEG-1 System Stream",
+ "MPEG-2 DVD-compatible Stream",
+ "MPEG-1 VCD-compatible Stream",
+ "MPEG-2 SVCD-compatible Stream",
+ NULL
+ };
+
+ static const char *mpeg_audio_encoding_l2_ac3[] = {
+ "",
+ "MPEG-1/2 Layer II",
+ "",
+ "",
+ "AC-3",
+ NULL
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return (p->capabilities & CX2341X_CAP_HAS_TS) ?
+ mpeg_stream_type_with_ts : mpeg_stream_type_without_ts;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return (p->capabilities & CX2341X_CAP_HAS_AC3) ?
+ mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id);
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ return NULL;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return cx2341x_get_menu(id);
+ default:
+ return v4l2_ctrl_get_menu(id);
+ }
+}
+EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
+
+static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
+{
+ params->audio_properties =
+ (params->audio_sampling_freq << 0) |
+ (params->audio_mode << 8) |
+ (params->audio_mode_extension << 10) |
+ (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+ ? 3 : params->audio_emphasis) << 12) |
+ (params->audio_crc << 14);
+
+ if ((params->capabilities & CX2341X_CAP_HAS_AC3) &&
+ params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+ params->audio_properties |=
+ /* Not sure if this MPEG Layer II setting is required */
+ ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
+ (params->audio_ac3_bitrate << 4) |
+ (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
+ } else {
+ /* Assuming MPEG Layer II */
+ params->audio_properties |=
+ ((3 - params->audio_encoding) << 2) |
+ ((1 + params->audio_l2_bitrate) << 4);
+ }
+}
+
+/* Check for correctness of the ctrl's value based on the data from
+ struct v4l2_queryctrl and the available menu items. Note that
+ menu_items may be NULL, in that case it is ignored. */
+static int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
+ const char * const *menu_items)
+{
+ if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+ return -EBUSY;
+ if (qctrl->type == V4L2_CTRL_TYPE_STRING)
+ return 0;
+ if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
+ qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return 0;
+ if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum)
+ return -ERANGE;
+ if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) {
+ if (menu_items[ctrl->value] == NULL ||
+ menu_items[ctrl->value][0] == '\0')
+ return -EINVAL;
+ }
+ if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
+ (ctrl->value & ~qctrl->maximum))
+ return -ERANGE;
+ return 0;
+}
+
+int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
+ struct v4l2_ext_controls *ctrls, unsigned int cmd)
+{
+ int err = 0;
+ int i;
+
+ if (cmd == VIDIOC_G_EXT_CTRLS) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = cx2341x_get_ctrl(params, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+ struct v4l2_queryctrl qctrl;
+ const char * const *menu_items = NULL;
+
+ qctrl.id = ctrl->id;
+ err = cx2341x_ctrl_query(params, &qctrl);
+ if (err)
+ break;
+ if (qctrl.type == V4L2_CTRL_TYPE_MENU)
+ menu_items = cx2341x_ctrl_get_menu(params, qctrl.id);
+ err = v4l2_ctrl_check(ctrl, &qctrl, menu_items);
+ if (err)
+ break;
+ err = cx2341x_set_ctrl(params, busy, ctrl);
+ if (err)
+ break;
+ }
+ if (err == 0 &&
+ params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ params->video_bitrate_peak < params->video_bitrate) {
+ err = -ERANGE;
+ ctrls->error_idx = ctrls->count;
+ }
+ if (err)
+ ctrls->error_idx = i;
+ else
+ cx2341x_calc_audio_properties(params);
+ return err;
+}
+EXPORT_SYMBOL(cx2341x_ext_ctrls);
+
+void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
+{
+ *p = default_params;
+ cx2341x_calc_audio_properties(p);
+}
+EXPORT_SYMBOL(cx2341x_fill_defaults);
+
+static int cx2341x_api(void *priv, cx2341x_mbox_func func,
+ u32 cmd, int args, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i;
+
+ va_start(vargs, args);
+
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(vargs, int);
+ va_end(vargs);
+ return func(priv, cmd, args, 0, data);
+}
+
+#define NEQ(field) (old->field != new->field)
+
+int cx2341x_update(void *priv, cx2341x_mbox_func func,
+ const struct cx2341x_mpeg_params *old,
+ const struct cx2341x_mpeg_params *new)
+{
+ static int mpeg_stream_type[] = {
+ 0, /* MPEG-2 PS */
+ 1, /* MPEG-2 TS */
+ 2, /* MPEG-1 SS */
+ 14, /* DVD */
+ 11, /* VCD */
+ 12, /* SVCD */
+ };
+
+ int err = 0;
+ int force = (old == NULL);
+ u16 temporal = new->video_temporal_filter;
+
+ cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0);
+
+ if (force || NEQ(is_50hz)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1,
+ new->is_50hz);
+ if (err) return err;
+ }
+
+ if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) {
+ u16 w = new->width;
+ u16 h = new->height;
+
+ if (new->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+ w /= 2;
+ h /= 2;
+ }
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2,
+ h, w);
+ if (err) return err;
+ }
+ if (force || NEQ(stream_type)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1,
+ mpeg_stream_type[new->stream_type]);
+ if (err) return err;
+ }
+ if (force || NEQ(video_aspect)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1,
+ 1 + new->video_aspect);
+ if (err) return err;
+ }
+ if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
+ new->video_gop_size, new->video_b_frames + 1);
+ if (err) return err;
+ }
+ if (force || NEQ(video_gop_closure)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1,
+ new->video_gop_closure);
+ if (err) return err;
+ }
+ if (force || NEQ(audio_properties)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES,
+ 1, new->audio_properties);
+ if (err) return err;
+ }
+ if (force || NEQ(audio_mute)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1,
+ new->audio_mute);
+ if (err) return err;
+ }
+ if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) ||
+ NEQ(video_bitrate_peak)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5,
+ new->video_bitrate_mode, new->video_bitrate,
+ new->video_bitrate_peak / 400, 0, 0);
+ if (err) return err;
+ }
+ if (force || NEQ(video_spatial_filter_mode) ||
+ NEQ(video_temporal_filter_mode) ||
+ NEQ(video_median_filter_type)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE,
+ 2, new->video_spatial_filter_mode |
+ (new->video_temporal_filter_mode << 1),
+ new->video_median_filter_type);
+ if (err) return err;
+ }
+ if (force || NEQ(video_luma_median_filter_bottom) ||
+ NEQ(video_luma_median_filter_top) ||
+ NEQ(video_chroma_median_filter_bottom) ||
+ NEQ(video_chroma_median_filter_top)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4,
+ new->video_luma_median_filter_bottom,
+ new->video_luma_median_filter_top,
+ new->video_chroma_median_filter_bottom,
+ new->video_chroma_median_filter_top);
+ if (err) return err;
+ }
+ if (force || NEQ(video_luma_spatial_filter_type) ||
+ NEQ(video_chroma_spatial_filter_type)) {
+ err = cx2341x_api(priv, func,
+ CX2341X_ENC_SET_SPATIAL_FILTER_TYPE,
+ 2, new->video_luma_spatial_filter_type,
+ new->video_chroma_spatial_filter_type);
+ if (err) return err;
+ }
+ if (force || NEQ(video_spatial_filter) ||
+ old->video_temporal_filter != temporal) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS,
+ 2, new->video_spatial_filter, temporal);
+ if (err) return err;
+ }
+ if (force || NEQ(video_temporal_decimation)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE,
+ 1, new->video_temporal_decimation);
+ if (err) return err;
+ }
+ if (force || NEQ(video_mute) ||
+ (new->video_mute && NEQ(video_mute_yuv))) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1,
+ new->video_mute | (new->video_mute_yuv << 8));
+ if (err) return err;
+ }
+ if (force || NEQ(stream_insert_nav_packets)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2,
+ 7, new->stream_insert_nav_packets);
+ if (err) return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cx2341x_update);
+
+static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id)
+{
+ const char * const *menu = cx2341x_ctrl_get_menu(p, id);
+ struct v4l2_ext_control ctrl;
+
+ if (menu == NULL)
+ goto invalid;
+ ctrl.id = id;
+ if (cx2341x_get_ctrl(p, &ctrl))
+ goto invalid;
+ while (ctrl.value-- && *menu) menu++;
+ if (*menu == NULL)
+ goto invalid;
+ return *menu;
+
+invalid:
+ return "<invalid>";
+}
+
+void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
+{
+ int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+
+ /* Stream */
+ printk(KERN_INFO "%s: Stream: %s",
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE));
+ if (p->stream_insert_nav_packets)
+ printk(" (with navigation packets)");
+ printk("\n");
+ printk(KERN_INFO "%s: VBI Format: %s\n",
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT));
+
+ /* Video */
+ printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n",
+ prefix,
+ p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1),
+ p->is_50hz ? 25 : 30,
+ (p->video_mute) ? " (muted)" : "");
+ printk(KERN_INFO "%s: Video: %s, %s, %s, %d",
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE),
+ p->video_bitrate);
+ if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
+ printk(", Peak %d", p->video_bitrate_peak);
+ printk("\n");
+ printk(KERN_INFO
+ "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n",
+ prefix,
+ p->video_gop_size, p->video_b_frames,
+ p->video_gop_closure ? "" : "No ");
+ if (p->video_temporal_decimation)
+ printk(KERN_INFO "%s: Video: Temporal Decimation %d\n",
+ prefix, p->video_temporal_decimation);
+
+ /* Audio */
+ printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s",
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING),
+ cx2341x_menu_item(p,
+ p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3
+ ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE
+ : V4L2_CID_MPEG_AUDIO_L2_BITRATE),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE),
+ p->audio_mute ? " (muted)" : "");
+ if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
+ printk(", %s", cx2341x_menu_item(p,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION));
+ printk(", %s, %s\n",
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC));
+
+ /* Encoding filters */
+ printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n",
+ prefix,
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE),
+ p->video_spatial_filter);
+
+ printk(KERN_INFO "%s: Temporal Filter: %s, %d\n",
+ prefix,
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE),
+ p->video_temporal_filter);
+ printk(KERN_INFO
+ "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
+ prefix,
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE),
+ p->video_luma_median_filter_bottom,
+ p->video_luma_median_filter_top,
+ p->video_chroma_median_filter_bottom,
+ p->video_chroma_median_filter_top);
+}
+EXPORT_SYMBOL(cx2341x_log_status);
+
+
+
+/********************** NEW CODE *********************/
+
+static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct cx2341x_handler, hdl);
+}
+
+static int cx2341x_hdl_api(struct cx2341x_handler *hdl,
+ u32 cmd, int args, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i;
+
+ va_start(vargs, args);
+
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(vargs, int);
+ va_end(vargs);
+ return hdl->func(hdl->priv, cmd, args, 0, data);
+}
+
+/* ctrl->handler->lock is held, so it is safe to access cur.val */
+static inline int cx2341x_neq(struct v4l2_ctrl *ctrl)
+{
+ return ctrl && ctrl->val != ctrl->cur.val;
+}
+
+static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+ s32 val = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: {
+ /* video gop cluster */
+ int b = val + 1;
+ int gop = hdl->video_gop_size->val;
+
+ gop = b * ((gop + b - 1) / b);
+
+ /* Max GOP size = 34 */
+ while (gop > 34)
+ gop -= b;
+ hdl->video_gop_size->val = gop;
+ break;
+ }
+
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ /* stream type cluster */
+ hdl->video_encoding->val =
+ (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+ hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ /* MPEG-1 implies CBR */
+ hdl->video_bitrate_mode->val =
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+ /* peak bitrate shall be >= normal bitrate */
+ if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ hdl->video_bitrate_peak->val < hdl->video_bitrate->val)
+ hdl->video_bitrate_peak->val = hdl->video_bitrate->val;
+ break;
+ }
+ return 0;
+}
+
+static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ static const int mpeg_stream_type[] = {
+ 0, /* MPEG-2 PS */
+ 1, /* MPEG-2 TS */
+ 2, /* MPEG-1 SS */
+ 14, /* DVD */
+ 11, /* VCD */
+ 12, /* SVCD */
+ };
+ struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+ s32 val = ctrl->val;
+ u32 props;
+ int err;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ if (hdl->ops && hdl->ops->s_stream_vbi_fmt)
+ return hdl->ops->s_stream_vbi_fmt(hdl, val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1);
+
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val);
+
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val);
+
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val);
+
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val);
+
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ /* audio properties cluster */
+ props = (hdl->audio_sampling_freq->val << 0) |
+ (hdl->audio_mode->val << 8) |
+ (hdl->audio_mode_extension->val << 10) |
+ (hdl->audio_crc->val << 14);
+ if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+ props |= 3 << 12;
+ else
+ props |= hdl->audio_emphasis->val << 12;
+
+ if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+ props |=
+#if 1
+ /* Not sure if this MPEG Layer II setting is required */
+ ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
+#endif
+ (hdl->audio_ac3_bitrate->val << 4) |
+ (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
+ } else {
+ /* Assuming MPEG Layer II */
+ props |=
+ ((3 - hdl->audio_encoding->val) << 2) |
+ ((1 + hdl->audio_l2_bitrate->val) << 4);
+ }
+ err = cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props);
+ if (err)
+ return err;
+
+ hdl->audio_properties = props;
+ if (hdl->audio_ac3_bitrate) {
+ int is_ac3 = hdl->audio_encoding->val ==
+ V4L2_MPEG_AUDIO_ENCODING_AC3;
+
+ v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3);
+ v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3);
+ }
+ v4l2_ctrl_activate(hdl->audio_mode_extension,
+ hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO);
+ if (cx2341x_neq(hdl->audio_sampling_freq) &&
+ hdl->ops && hdl->ops->s_audio_sampling_freq)
+ return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val);
+ if (cx2341x_neq(hdl->audio_mode) &&
+ hdl->ops && hdl->ops->s_audio_mode)
+ return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ /* video gop cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
+ hdl->video_gop_size->val,
+ hdl->video_b_frames->val + 1);
+
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ /* stream type cluster */
+ err = cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]);
+ if (err)
+ return err;
+
+ err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5,
+ hdl->video_bitrate_mode->val,
+ hdl->video_bitrate->val,
+ hdl->video_bitrate_peak->val / 400, 0, 0);
+ if (err)
+ return err;
+
+ v4l2_ctrl_activate(hdl->video_bitrate_mode,
+ hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1);
+ v4l2_ctrl_activate(hdl->video_bitrate_peak,
+ hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+ if (cx2341x_neq(hdl->video_encoding) &&
+ hdl->ops && hdl->ops->s_video_encoding)
+ return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ /* video mute cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1,
+ hdl->video_mute->val |
+ (hdl->video_mute_yuv->val << 8));
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: {
+ int active_filter;
+
+ /* video filter mode */
+ err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
+ hdl->video_spatial_filter_mode->val |
+ (hdl->video_temporal_filter_mode->val << 1),
+ hdl->video_median_filter_type->val);
+ if (err)
+ return err;
+
+ active_filter = hdl->video_spatial_filter_mode->val !=
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO;
+ v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter);
+ v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter);
+ active_filter = hdl->video_temporal_filter_mode->val !=
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO;
+ v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter);
+ active_filter = hdl->video_median_filter_type->val !=
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF;
+ v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter);
+ v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter);
+ return 0;
+ }
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ /* video filter type cluster */
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
+ hdl->video_luma_spatial_filter_type->val,
+ hdl->video_chroma_spatial_filter_type->val);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ /* video filter cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
+ hdl->video_spatial_filter->val,
+ hdl->video_temporal_filter->val);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ /* video median cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4,
+ hdl->video_luma_median_filter_bottom->val,
+ hdl->video_luma_median_filter_top->val,
+ hdl->video_chroma_median_filter_bottom->val,
+ hdl->video_chroma_median_filter_top->val);
+ }
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops cx2341x_ops = {
+ .try_ctrl = cx2341x_try_ctrl,
+ .s_ctrl = cx2341x_s_ctrl,
+};
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+ struct v4l2_ctrl_config cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+ cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags);
+ cfg.ops = &cx2341x_ops;
+ cfg.id = id;
+ cfg.min = min;
+ cfg.max = max;
+ cfg.def = def;
+ if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+ cfg.step = 0;
+ cfg.menu_skip_mask = step;
+ cfg.qmenu = cx2341x_get_menu(id);
+ } else {
+ cfg.step = step;
+ cfg.menu_skip_mask = 0;
+ }
+ return v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+ return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 max, s32 mask, s32 def)
+{
+ return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def);
+}
+
+int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
+ unsigned nr_of_controls_hint)
+{
+ struct v4l2_ctrl_handler *hdl = &cxhdl->hdl;
+ u32 caps = cxhdl->capabilities;
+ int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI;
+ int has_ac3 = caps & CX2341X_CAP_HAS_AC3;
+ int has_ts = caps & CX2341X_CAP_HAS_TS;
+
+ cxhdl->width = 720;
+ cxhdl->height = 480;
+
+ v4l2_ctrl_handler_init(hdl, nr_of_controls_hint);
+
+ /* Add controls in ascending control ID order for fastest
+ insertion time. */
+ cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+ cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_STREAM_VBI_FMT,
+ V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE);
+ cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+ cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+ cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff,
+ V4L2_MPEG_AUDIO_L2_BITRATE_224K);
+ cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_MODE,
+ V4L2_MPEG_AUDIO_MODE_MONO, 0,
+ V4L2_MPEG_AUDIO_MODE_STEREO);
+ cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
+ cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_EMPHASIS,
+ V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0,
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE);
+ cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_CRC,
+ V4L2_MPEG_AUDIO_CRC_CRC16, 0,
+ V4L2_MPEG_AUDIO_CRC_NONE);
+
+ cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0);
+ if (has_ac3)
+ cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_224K);
+ cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+ cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_MPEG_VIDEO_ASPECT_221x100, 0,
+ V4L2_MPEG_VIDEO_ASPECT_4x3);
+ cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2);
+ cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ 1, 34, 1, cxhdl->is_50hz ? 12 : 15);
+ cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1);
+ cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 0, 27000000, 1, 6000000);
+ cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ 0, 27000000, 1, 8000000);
+ cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0);
+ cxhdl->video_mute = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0);
+ cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080);
+
+ /* CX23415/6 specific */
+ cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
+ cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ 0, 15, 1, 0);
+ cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR);
+ cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR);
+ cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
+ cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+ 0, 31, 1, 8);
+ cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
+ cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+ 0, 255, 1, 0);
+ cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+ 0, 255, 1, 255);
+ cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+ 0, 255, 1, 0);
+ cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+ 0, 255, 1, 255);
+ cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
+ 0, 1, 1, 0);
+
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq);
+ v4l2_ctrl_cluster(2, &cxhdl->video_b_frames);
+ v4l2_ctrl_cluster(5, &cxhdl->stream_type);
+ v4l2_ctrl_cluster(2, &cxhdl->video_mute);
+ v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode);
+ v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type);
+ v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter);
+ v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx2341x_handler_init);
+
+void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz)
+{
+ cxhdl->is_50hz = is_50hz;
+ cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15;
+}
+EXPORT_SYMBOL(cx2341x_handler_set_50hz);
+
+int cx2341x_handler_setup(struct cx2341x_handler *cxhdl)
+{
+ int h = cxhdl->height;
+ int w = cxhdl->width;
+ int err;
+
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0);
+ if (err)
+ return err;
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz);
+ if (err)
+ return err;
+
+ if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+ w /= 2;
+ h /= 2;
+ }
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
+ if (err)
+ return err;
+ return v4l2_ctrl_handler_setup(&cxhdl->hdl);
+}
+EXPORT_SYMBOL(cx2341x_handler_setup);
+
+void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy)
+{
+ v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy);
+ v4l2_ctrl_grab(cxhdl->audio_encoding, busy);
+ v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy);
+ v4l2_ctrl_grab(cxhdl->stream_type, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy);
+}
+EXPORT_SYMBOL(cx2341x_handler_set_busy);
diff --git a/drivers/media/common/cypress_firmware.c b/drivers/media/common/cypress_firmware.c
new file mode 100644
index 000000000..577e82058
--- /dev/null
+++ b/drivers/media/common/cypress_firmware.c
@@ -0,0 +1,132 @@
+/* cypress_firmware.c is part of the DVB USB library.
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * see dvb-usb-init.c for copyright information.
+ *
+ * This file contains functions for downloading the firmware to Cypress FX 1
+ * and 2 based devices.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include "cypress_firmware.h"
+
+struct usb_cypress_controller {
+ u8 id;
+ const char *name; /* name of the usb controller */
+ u16 cs_reg; /* needs to be restarted,
+ * when the firmware has been downloaded */
+};
+
+static const struct usb_cypress_controller cypress[] = {
+ { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cs_reg = 0x7f92 },
+ { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cs_reg = 0x7f92 },
+ { .id = CYPRESS_FX2, .name = "Cypress FX2", .cs_reg = 0xe600 },
+};
+
+/*
+ * load a firmware packet to the device
+ */
+static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data,
+ u8 len)
+{
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);
+}
+
+static int cypress_get_hexline(const struct firmware *fw,
+ struct hexline *hx, int *pos)
+{
+ u8 *b = (u8 *) &fw->data[*pos];
+ int data_offs = 4;
+
+ if (*pos >= fw->size)
+ return 0;
+
+ memset(hx, 0, sizeof(struct hexline));
+ hx->len = b[0];
+
+ if ((*pos + hx->len + 4) >= fw->size)
+ return -EINVAL;
+
+ hx->addr = b[1] | (b[2] << 8);
+ hx->type = b[3];
+
+ if (hx->type == 0x04) {
+ /* b[4] and b[5] are the Extended linear address record data
+ * field */
+ hx->addr |= (b[4] << 24) | (b[5] << 16);
+ }
+
+ memcpy(hx->data, &b[data_offs], hx->len);
+ hx->chk = b[hx->len + data_offs];
+ *pos += hx->len + 5;
+
+ return *pos;
+}
+
+int cypress_load_firmware(struct usb_device *udev,
+ const struct firmware *fw, int type)
+{
+ struct hexline *hx;
+ int ret, pos = 0;
+
+ hx = kmalloc(sizeof(struct hexline), GFP_KERNEL);
+ if (!hx) {
+ dev_err(&udev->dev, "%s: kmalloc() failed\n", KBUILD_MODNAME);
+ return -ENOMEM;
+ }
+
+ /* stop the CPU */
+ hx->data[0] = 1;
+ ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
+ if (ret != 1) {
+ dev_err(&udev->dev, "%s: CPU stop failed=%d\n",
+ KBUILD_MODNAME, ret);
+ ret = -EIO;
+ goto err_kfree;
+ }
+
+ /* write firmware to memory */
+ for (;;) {
+ ret = cypress_get_hexline(fw, hx, &pos);
+ if (ret < 0)
+ goto err_kfree;
+ else if (ret == 0)
+ break;
+
+ ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len);
+ if (ret < 0) {
+ goto err_kfree;
+ } else if (ret != hx->len) {
+ dev_err(&udev->dev,
+ "%s: error while transferring firmware (transferred size=%d, block size=%d)\n",
+ KBUILD_MODNAME, ret, hx->len);
+ ret = -EIO;
+ goto err_kfree;
+ }
+ }
+
+ /* start the CPU */
+ hx->data[0] = 0;
+ ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
+ if (ret != 1) {
+ dev_err(&udev->dev, "%s: CPU start failed=%d\n",
+ KBUILD_MODNAME, ret);
+ ret = -EIO;
+ goto err_kfree;
+ }
+
+ ret = 0;
+err_kfree:
+ kfree(hx);
+ return ret;
+}
+EXPORT_SYMBOL(cypress_load_firmware);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Cypress firmware download");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/cypress_firmware.h b/drivers/media/common/cypress_firmware.h
new file mode 100644
index 000000000..e493cbc7a
--- /dev/null
+++ b/drivers/media/common/cypress_firmware.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * see dvb-usb-init.c for copyright information.
+ *
+ * This file contains functions for downloading the firmware to Cypress FX 1
+ * and 2 based devices.
+ *
+ */
+
+#ifndef CYPRESS_FIRMWARE_H
+#define CYPRESS_FIRMWARE_H
+
+#define CYPRESS_AN2135 0
+#define CYPRESS_AN2235 1
+#define CYPRESS_FX2 2
+
+/* commonly used firmware download types and function */
+struct hexline {
+ u8 len;
+ u32 addr;
+ u8 type;
+ u8 data[255];
+ u8 chk;
+};
+
+int cypress_load_firmware(struct usb_device *, const struct firmware *, int);
+
+#endif
diff --git a/drivers/media/common/saa7146/Kconfig b/drivers/media/common/saa7146/Kconfig
new file mode 100644
index 000000000..769c6f814
--- /dev/null
+++ b/drivers/media/common/saa7146/Kconfig
@@ -0,0 +1,9 @@
+config VIDEO_SAA7146
+ tristate
+ depends on I2C && PCI
+
+config VIDEO_SAA7146_VV
+ tristate
+ depends on VIDEO_V4L2
+ select VIDEOBUF_DMA_SG
+ select VIDEO_SAA7146
diff --git a/drivers/media/common/saa7146/Makefile b/drivers/media/common/saa7146/Makefile
new file mode 100644
index 000000000..3219b00a8
--- /dev/null
+++ b/drivers/media/common/saa7146/Makefile
@@ -0,0 +1,5 @@
+saa7146-objs := saa7146_i2c.o saa7146_core.o
+saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
+
+obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
+obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o
diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c
new file mode 100644
index 000000000..1ff9f5323
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_core.c
@@ -0,0 +1,587 @@
+/*
+ saa7146.o - driver for generic saa7146-based hardware
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.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.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/saa7146.h>
+#include <linux/module.h>
+
+static int saa7146_num;
+
+unsigned int saa7146_debug;
+
+module_param(saa7146_debug, uint, 0644);
+MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");
+
+#if 0
+static void dump_registers(struct saa7146_dev* dev)
+{
+ int i = 0;
+
+ pr_info(" @ %li jiffies:\n", jiffies);
+ for (i = 0; i <= 0x148; i += 4)
+ pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i));
+}
+#endif
+
+/****************************************************************************
+ * gpio and debi helper functions
+ ****************************************************************************/
+
+void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
+{
+ u32 value = 0;
+
+ BUG_ON(port > 3);
+
+ value = saa7146_read(dev, GPIO_CTRL);
+ value &= ~(0xff << (8*port));
+ value |= (data << (8*port));
+ saa7146_write(dev, GPIO_CTRL, value);
+}
+
+/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */
+static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
+ unsigned long us1, unsigned long us2)
+{
+ unsigned long timeout;
+ int err;
+
+ /* wait for registers to be programmed */
+ timeout = jiffies + usecs_to_jiffies(us1);
+ while (1) {
+ err = time_after(jiffies, timeout);
+ if (saa7146_read(dev, MC2) & 2)
+ break;
+ if (err) {
+ pr_debug("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+
+ /* wait for transfer to complete */
+ timeout = jiffies + usecs_to_jiffies(us2);
+ while (1) {
+ err = time_after(jiffies, timeout);
+ if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
+ break;
+ saa7146_read(dev, MC2);
+ if (err) {
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+
+ return 0;
+}
+
+static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
+ unsigned long us1, unsigned long us2)
+{
+ unsigned long loops;
+
+ /* wait for registers to be programmed */
+ loops = us1;
+ while (1) {
+ if (saa7146_read(dev, MC2) & 2)
+ break;
+ if (!loops--) {
+ pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ /* wait for transfer to complete */
+ loops = us2 / 5;
+ while (1) {
+ if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
+ break;
+ saa7146_read(dev, MC2);
+ if (!loops--) {
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ udelay(5);
+ }
+
+ return 0;
+}
+
+int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
+{
+ if (nobusyloop)
+ return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000);
+ else
+ return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000);
+}
+
+/****************************************************************************
+ * general helper functions
+ ****************************************************************************/
+
+/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c
+ make sure virt has been allocated with vmalloc_32(), otherwise the BUG()
+ may be triggered on highmem machines */
+static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
+{
+ struct scatterlist *sglist;
+ struct page *pg;
+ int i;
+
+ sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
+ if (NULL == sglist)
+ return NULL;
+ sg_init_table(sglist, nr_pages);
+ for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
+ pg = vmalloc_to_page(virt);
+ if (NULL == pg)
+ goto err;
+ BUG_ON(PageHighMem(pg));
+ sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
+ }
+ return sglist;
+
+ err:
+ kfree(sglist);
+ return NULL;
+}
+
+/********************************************************************************/
+/* common page table functions */
+
+void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
+{
+ int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
+ void *mem = vmalloc_32(length);
+ int slen = 0;
+
+ if (NULL == mem)
+ goto err_null;
+
+ if (!(pt->slist = vmalloc_to_sg(mem, pages)))
+ goto err_free_mem;
+
+ if (saa7146_pgtable_alloc(pci, pt))
+ goto err_free_slist;
+
+ pt->nents = pages;
+ slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE);
+ if (0 == slen)
+ goto err_free_pgtable;
+
+ if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen))
+ goto err_unmap_sg;
+
+ return mem;
+
+err_unmap_sg:
+ pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
+err_free_pgtable:
+ saa7146_pgtable_free(pci, pt);
+err_free_slist:
+ kfree(pt->slist);
+ pt->slist = NULL;
+err_free_mem:
+ vfree(mem);
+err_null:
+ return NULL;
+}
+
+void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)
+{
+ pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
+ saa7146_pgtable_free(pci, pt);
+ kfree(pt->slist);
+ pt->slist = NULL;
+ vfree(mem);
+}
+
+void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+ if (NULL == pt->cpu)
+ return;
+ pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+ pt->cpu = NULL;
+}
+
+int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+ __le32 *cpu;
+ dma_addr_t dma_addr = 0;
+
+ cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
+ if (NULL == cpu) {
+ return -ENOMEM;
+ }
+ pt->size = PAGE_SIZE;
+ pt->cpu = cpu;
+ pt->dma = dma_addr;
+
+ return 0;
+}
+
+int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
+ struct scatterlist *list, int sglen )
+{
+ __le32 *ptr, fill;
+ int nr_pages = 0;
+ int i,p;
+
+ BUG_ON(0 == sglen);
+ BUG_ON(list->offset > PAGE_SIZE);
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt->offset = list->offset;
+
+ ptr = pt->cpu;
+ for (i = 0; i < sglen; i++, list++) {
+/*
+ pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n",
+ i, sg_dma_address(list), sg_dma_len(list),
+ list->offset);
+*/
+ for (p = 0; p * 4096 < list->length; p++, ptr++) {
+ *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
+ nr_pages++;
+ }
+ }
+
+
+ /* safety; fill the page table up with the last valid page */
+ fill = *(ptr-1);
+ for(i=nr_pages;i<1024;i++) {
+ *ptr++ = fill;
+ }
+
+/*
+ ptr = pt->cpu;
+ pr_debug("offset: %d\n", pt->offset);
+ for(i=0;i<5;i++) {
+ pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]);
+ }
+*/
+ return 0;
+}
+
+/********************************************************************************/
+/* interrupt handler */
+static irqreturn_t interrupt_hw(int irq, void *dev_id)
+{
+ struct saa7146_dev *dev = dev_id;
+ u32 isr;
+ u32 ack_isr;
+
+ /* read out the interrupt status register */
+ ack_isr = isr = saa7146_read(dev, ISR);
+
+ /* is this our interrupt? */
+ if ( 0 == isr ) {
+ /* nope, some other device */
+ return IRQ_NONE;
+ }
+
+ if (dev->ext) {
+ if (dev->ext->irq_mask & isr) {
+ if (dev->ext->irq_func)
+ dev->ext->irq_func(dev, &isr);
+ isr &= ~dev->ext->irq_mask;
+ }
+ }
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
+ if (dev->vv_data && dev->vv_callback)
+ dev->vv_callback(dev,isr);
+ isr &= ~MASK_27;
+ }
+ if (0 != (isr & (MASK_28))) {
+ if (dev->vv_data && dev->vv_callback)
+ dev->vv_callback(dev,isr);
+ isr &= ~MASK_28;
+ }
+ if (0 != (isr & (MASK_16|MASK_17))) {
+ SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+ /* only wake up if we expect something */
+ if (0 != dev->i2c_op) {
+ dev->i2c_op = 0;
+ wake_up(&dev->i2c_wq);
+ } else {
+ u32 psr = saa7146_read(dev, PSR);
+ u32 ssr = saa7146_read(dev, SSR);
+ pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
+ dev->name, isr, psr, ssr);
+ }
+ isr &= ~(MASK_16|MASK_17);
+ }
+ if( 0 != isr ) {
+ ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n",
+ isr);
+ ERR("disabling interrupt source(s)!\n");
+ SAA7146_IER_DISABLE(dev,isr);
+ }
+ saa7146_write(dev, ISR, ack_isr);
+ return IRQ_HANDLED;
+}
+
+/*********************************************************************************/
+/* configuration-functions */
+
+static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)
+{
+ struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;
+ struct saa7146_extension *ext = pci_ext->ext;
+ struct saa7146_dev *dev;
+ int err = -ENOMEM;
+
+ /* clear out mem for sure */
+ dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
+ if (!dev) {
+ ERR("out of memory\n");
+ goto out;
+ }
+
+ /* create a nice device name */
+ sprintf(dev->name, "saa7146 (%d)", saa7146_num);
+
+ DEB_EE("pci:%p\n", pci);
+
+ err = pci_enable_device(pci);
+ if (err < 0) {
+ ERR("pci_enable_device() failed\n");
+ goto err_free;
+ }
+
+ /* enable bus-mastering */
+ pci_set_master(pci);
+
+ dev->pci = pci;
+
+ /* get chip-revision; this is needed to enable bug-fixes */
+ dev->revision = pci->revision;
+
+ /* remap the memory from virtual to physical address */
+
+ err = pci_request_region(pci, 0, "saa7146");
+ if (err < 0)
+ goto err_disable;
+
+ dev->mem = ioremap(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ if (!dev->mem) {
+ ERR("ioremap() failed\n");
+ err = -ENODEV;
+ goto err_release;
+ }
+
+ /* we don't do a master reset here anymore, it screws up
+ some boards that don't have an i2c-eeprom for configuration
+ values */
+/*
+ saa7146_write(dev, MC1, MASK_31);
+*/
+
+ /* disable all irqs */
+ saa7146_write(dev, IER, 0);
+
+ /* shut down all dma transfers and rps tasks */
+ saa7146_write(dev, MC1, 0x30ff0000);
+
+ /* clear out any rps-signals pending */
+ saa7146_write(dev, MC2, 0xf8000000);
+
+ /* request an interrupt for the saa7146 */
+ err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED,
+ dev->name, dev);
+ if (err < 0) {
+ ERR("request_irq() failed\n");
+ goto err_unmap;
+ }
+
+ err = -ENOMEM;
+
+ /* get memory for various stuff */
+ dev->d_rps0.cpu_addr = pci_zalloc_consistent(pci, SAA7146_RPS_MEM,
+ &dev->d_rps0.dma_handle);
+ if (!dev->d_rps0.cpu_addr)
+ goto err_free_irq;
+
+ dev->d_rps1.cpu_addr = pci_zalloc_consistent(pci, SAA7146_RPS_MEM,
+ &dev->d_rps1.dma_handle);
+ if (!dev->d_rps1.cpu_addr)
+ goto err_free_rps0;
+
+ dev->d_i2c.cpu_addr = pci_zalloc_consistent(pci, SAA7146_RPS_MEM,
+ &dev->d_i2c.dma_handle);
+ if (!dev->d_i2c.cpu_addr)
+ goto err_free_rps1;
+
+ /* the rest + print status message */
+
+ pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
+ dev->mem, dev->revision, pci->irq,
+ pci->subsystem_vendor, pci->subsystem_device);
+ dev->ext = ext;
+
+ mutex_init(&dev->v4l2_lock);
+ spin_lock_init(&dev->int_slock);
+ spin_lock_init(&dev->slock);
+
+ mutex_init(&dev->i2c_lock);
+
+ dev->module = THIS_MODULE;
+ init_waitqueue_head(&dev->i2c_wq);
+
+ /* set some sane pci arbitrition values */
+ saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+
+ /* TODO: use the status code of the callback */
+
+ err = -ENODEV;
+
+ if (ext->probe && ext->probe(dev)) {
+ DEB_D("ext->probe() failed for %p. skipping device.\n", dev);
+ goto err_free_i2c;
+ }
+
+ if (ext->attach(dev, pci_ext)) {
+ DEB_D("ext->attach() failed for %p. skipping device.\n", dev);
+ goto err_free_i2c;
+ }
+ /* V4L extensions will set the pci drvdata to the v4l2_device in the
+ attach() above. So for those cards that do not use V4L we have to
+ set it explicitly. */
+ pci_set_drvdata(pci, &dev->v4l2_dev);
+
+ saa7146_num++;
+
+ err = 0;
+out:
+ return err;
+
+err_free_i2c:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
+ dev->d_i2c.dma_handle);
+err_free_rps1:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
+ dev->d_rps1.dma_handle);
+err_free_rps0:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
+ dev->d_rps0.dma_handle);
+err_free_irq:
+ free_irq(pci->irq, (void *)dev);
+err_unmap:
+ iounmap(dev->mem);
+err_release:
+ pci_release_region(pci, 0);
+err_disable:
+ pci_disable_device(pci);
+err_free:
+ kfree(dev);
+ goto out;
+}
+
+static void saa7146_remove_one(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+ struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
+ struct {
+ void *addr;
+ dma_addr_t dma;
+ } dev_map[] = {
+ { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle },
+ { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle },
+ { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle },
+ { NULL, 0 }
+ }, *p;
+
+ DEB_EE("dev:%p\n", dev);
+
+ dev->ext->detach(dev);
+
+ /* shut down all video dma transfers */
+ saa7146_write(dev, MC1, 0x00ff0000);
+
+ /* disable all irqs, release irq-routine */
+ saa7146_write(dev, IER, 0);
+
+ free_irq(pdev->irq, dev);
+
+ for (p = dev_map; p->addr; p++)
+ pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma);
+
+ iounmap(dev->mem);
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+ kfree(dev);
+
+ saa7146_num--;
+}
+
+/*********************************************************************************/
+/* extension handling functions */
+
+int saa7146_register_extension(struct saa7146_extension* ext)
+{
+ DEB_EE("ext:%p\n", ext);
+
+ ext->driver.name = ext->name;
+ ext->driver.id_table = ext->pci_tbl;
+ ext->driver.probe = saa7146_init_one;
+ ext->driver.remove = saa7146_remove_one;
+
+ pr_info("register extension '%s'\n", ext->name);
+ return pci_register_driver(&ext->driver);
+}
+
+int saa7146_unregister_extension(struct saa7146_extension* ext)
+{
+ DEB_EE("ext:%p\n", ext);
+ pr_info("unregister extension '%s'\n", ext->name);
+ pci_unregister_driver(&ext->driver);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(saa7146_register_extension);
+EXPORT_SYMBOL_GPL(saa7146_unregister_extension);
+
+/* misc functions used by extension modules */
+EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
+EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);
+EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable);
+EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);
+
+EXPORT_SYMBOL_GPL(saa7146_setgpio);
+
+EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
+
+EXPORT_SYMBOL_GPL(saa7146_debug);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c
new file mode 100644
index 000000000..df1e8c975
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_fops.c
@@ -0,0 +1,648 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/saa7146_vv.h>
+#include <linux/module.h>
+
+/****************************************************************************/
+/* resource management functions, shamelessly stolen from saa7134 driver */
+
+int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (fh->resources & bit) {
+ DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
+ bit, vv->resources);
+ /* have it already allocated */
+ return 1;
+ }
+
+ /* is it free? */
+ if (vv->resources & bit) {
+ DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
+ vv->resources, bit);
+ /* no, someone else uses it */
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ vv->resources |= bit;
+ DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
+ return 1;
+}
+
+void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ BUG_ON((fh->resources & bits) != bits);
+
+ fh->resources &= ~bits;
+ vv->resources &= ~bits;
+ DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
+}
+
+
+/********************************************************************************/
+/* common dma functions */
+
+void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
+ struct saa7146_buf *buf)
+{
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ DEB_EE("dev:%p, buf:%p\n", dev, buf);
+
+ BUG_ON(in_interrupt());
+
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+
+/********************************************************************************/
+/* common buffer functions */
+
+int saa7146_buffer_queue(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ struct saa7146_buf *buf)
+{
+ assert_spin_locked(&dev->slock);
+ DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
+
+ BUG_ON(!q);
+
+ if (NULL == q->curr) {
+ q->curr = buf;
+ DEB_D("immediately activating buffer %p\n", buf);
+ buf->activate(dev,buf,NULL);
+ } else {
+ list_add_tail(&buf->vb.queue,&q->queue);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ DEB_D("adding buffer %p to queue. (active buffer present)\n",
+ buf);
+ }
+ return 0;
+}
+
+void saa7146_buffer_finish(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ int state)
+{
+ assert_spin_locked(&dev->slock);
+ DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
+ DEB_EE("q->curr:%p\n", q->curr);
+
+ BUG_ON(!q->curr);
+
+ /* finish current buffer */
+ if (NULL == q->curr) {
+ DEB_D("aiii. no current buffer\n");
+ return;
+ }
+
+ q->curr->vb.state = state;
+ v4l2_get_timestamp(&q->curr->vb.ts);
+ wake_up(&q->curr->vb.done);
+
+ q->curr = NULL;
+}
+
+void saa7146_buffer_next(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q, int vbi)
+{
+ struct saa7146_buf *buf,*next = NULL;
+
+ BUG_ON(!q);
+
+ DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
+
+ assert_spin_locked(&dev->slock);
+ if (!list_empty(&q->queue)) {
+ /* activate next one from queue */
+ buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
+ list_del(&buf->vb.queue);
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
+ q->curr = buf;
+ DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
+ buf, q->queue.prev, q->queue.next);
+ buf->activate(dev,buf,next);
+ } else {
+ DEB_INT("no next buffer. stopping.\n");
+ if( 0 != vbi ) {
+ /* turn off video-dma3 */
+ saa7146_write(dev,MC1, MASK_20);
+ } else {
+ /* nothing to do -- just prevent next video-dma1 transfer
+ by lowering the protection address */
+
+ // fixme: fix this for vflip != 0
+
+ saa7146_write(dev, PROT_ADDR1, 0);
+ saa7146_write(dev, MC2, (MASK_02|MASK_18));
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+
+/*
+ printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
+ printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
+ printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
+ printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
+ printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
+ printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
+*/
+ }
+ del_timer(&q->timeout);
+ }
+}
+
+void saa7146_buffer_timeout(unsigned long data)
+{
+ struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
+ struct saa7146_dev *dev = q->dev;
+ unsigned long flags;
+
+ DEB_EE("dev:%p, dmaq:%p\n", dev, q);
+
+ spin_lock_irqsave(&dev->slock,flags);
+ if (q->curr) {
+ DEB_D("timeout on %p\n", q->curr);
+ saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
+ }
+
+ /* we don't restart the transfer here like other drivers do. when
+ a streaming capture is disabled, the timeout function will be
+ called for the current buffer. if we activate the next buffer now,
+ we mess up our capture logic. if a timeout occurs on another buffer,
+ then something is seriously broken before, so no need to buffer the
+ next capture IMHO... */
+/*
+ saa7146_buffer_next(dev,q);
+*/
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/********************************************************************************/
+/* file operations */
+
+static int fops_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_fh *fh = NULL;
+ int result = 0;
+
+ DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
+
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+
+ DEB_D("using: %p\n", dev);
+
+ /* check if an extension is registered */
+ if( NULL == dev->ext ) {
+ DEB_S("no extension registered for this device\n");
+ result = -ENODEV;
+ goto out;
+ }
+
+ /* allocate per open data */
+ fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh) {
+ DEB_S("cannot allocate memory for per open data\n");
+ result = -ENOMEM;
+ goto out;
+ }
+
+ v4l2_fh_init(&fh->fh, vdev);
+
+ file->private_data = &fh->fh;
+ fh->dev = dev;
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ DEB_S("initializing vbi...\n");
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ result = saa7146_vbi_uops.open(dev,file);
+ if (dev->ext_vv_data->vbi_fops.open)
+ dev->ext_vv_data->vbi_fops.open(file);
+ } else {
+ DEB_S("initializing video...\n");
+ result = saa7146_video_uops.open(dev,file);
+ }
+
+ if (0 != result) {
+ goto out;
+ }
+
+ if( 0 == try_module_get(dev->ext->module)) {
+ result = -EINVAL;
+ goto out;
+ }
+
+ result = 0;
+ v4l2_fh_add(&fh->fh);
+out:
+ if (fh && result != 0) {
+ kfree(fh);
+ file->private_data = NULL;
+ }
+ mutex_unlock(vdev->lock);
+ return result;
+}
+
+static int fops_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ DEB_EE("file:%p\n", file);
+
+ mutex_lock(vdev->lock);
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ saa7146_vbi_uops.release(dev,file);
+ if (dev->ext_vv_data->vbi_fops.release)
+ dev->ext_vv_data->vbi_fops.release(file);
+ } else {
+ saa7146_video_uops.release(dev,file);
+ }
+
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
+ module_put(dev->ext->module);
+ file->private_data = NULL;
+ kfree(fh);
+
+ mutex_unlock(vdev->lock);
+
+ return 0;
+}
+
+static int fops_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ struct videobuf_queue *q;
+ int res;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER: {
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
+ q = &fh->video_q;
+ break;
+ }
+ case VFL_TYPE_VBI: {
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
+ return -ENODEV;
+ q = &fh->vbi_q;
+ break;
+ }
+ default:
+ BUG();
+ }
+
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ res = videobuf_mmap_mapper(q, vma);
+ mutex_unlock(vdev->lock);
+ return res;
+}
+
+static unsigned int __fops_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ struct videobuf_buffer *buf = NULL;
+ struct videobuf_queue *q;
+ unsigned int res = v4l2_ctrl_poll(file, wait);
+
+ DEB_EE("file:%p, poll:%p\n", file, wait);
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
+ return res | POLLOUT | POLLWRNORM;
+ if( 0 == fh->vbi_q.streaming )
+ return res | videobuf_poll_stream(file, &fh->vbi_q, wait);
+ q = &fh->vbi_q;
+ } else {
+ DEB_D("using video queue\n");
+ q = &fh->video_q;
+ }
+
+ if (!list_empty(&q->stream))
+ buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
+
+ if (!buf) {
+ DEB_D("buf == NULL!\n");
+ return res | POLLERR;
+ }
+
+ poll_wait(file, &buf->done, wait);
+ if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
+ DEB_D("poll succeeded!\n");
+ return res | POLLIN | POLLRDNORM;
+ }
+
+ DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
+ return res;
+}
+
+static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ unsigned int res;
+
+ mutex_lock(vdev->lock);
+ res = __fops_poll(file, wait);
+ mutex_unlock(vdev->lock);
+ return res;
+}
+
+static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ int ret;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
+ file, data, (unsigned long)count);
+*/
+ return saa7146_video_uops.read(file,data,count,ppos);
+ case VFL_TYPE_VBI:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
+ file, data, (unsigned long)count);
+*/
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) {
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ ret = saa7146_vbi_uops.read(file, data, count, ppos);
+ mutex_unlock(vdev->lock);
+ return ret;
+ }
+ return -EINVAL;
+ default:
+ BUG();
+ }
+}
+
+static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ int ret;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ return -EINVAL;
+ case VFL_TYPE_VBI:
+ if (fh->dev->ext_vv_data->vbi_fops.write) {
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
+ mutex_unlock(vdev->lock);
+ return ret;
+ }
+ return -EINVAL;
+ default:
+ BUG();
+ }
+}
+
+static const struct v4l2_file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = fops_open,
+ .release = fops_release,
+ .read = fops_read,
+ .write = fops_write,
+ .poll = fops_poll,
+ .mmap = fops_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static void vv_callback(struct saa7146_dev *dev, unsigned long status)
+{
+ u32 isr = status;
+
+ DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
+
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
+ saa7146_video_uops.irq_done(dev,isr);
+ }
+
+ if (0 != (isr & (MASK_28))) {
+ u32 mc2 = saa7146_read(dev, MC2);
+ if( 0 != (mc2 & MASK_15)) {
+ DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
+ wake_up(&dev->vv_data->vbi_wq);
+ saa7146_write(dev,MC2, MASK_31);
+ return;
+ }
+ DEB_INT("irq: RPS1 (0x%08x)\n", isr);
+ saa7146_vbi_uops.irq_done(dev,isr);
+ }
+}
+
+static const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
+ .s_ctrl = saa7146_s_ctrl,
+};
+
+int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
+{
+ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+ struct v4l2_pix_format *fmt;
+ struct v4l2_vbi_format *vbi;
+ struct saa7146_vv *vv;
+ int err;
+
+ err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
+ if (err)
+ return err;
+
+ v4l2_ctrl_handler_init(hdl, 6);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (hdl->error) {
+ err = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+ dev->v4l2_dev.ctrl_handler = hdl;
+
+ vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
+ if (vv == NULL) {
+ ERR("out of memory. aborting.\n");
+ v4l2_ctrl_handler_free(hdl);
+ return -ENOMEM;
+ }
+ ext_vv->vid_ops = saa7146_video_ioctl_ops;
+ ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
+ ext_vv->core_ops = &saa7146_video_ioctl_ops;
+
+ DEB_EE("dev:%p\n", dev);
+
+ /* set default values for video parts of the saa7146 */
+ saa7146_write(dev, BCS_CTRL, 0x80400040);
+
+ /* enable video-port pins */
+ saa7146_write(dev, MC1, (MASK_10 | MASK_26));
+
+ /* save per-device extension data (one extension can
+ handle different devices that might need different
+ configuration data) */
+ dev->ext_vv_data = ext_vv;
+
+ vv->d_clipping.cpu_addr =
+ pci_zalloc_consistent(dev->pci, SAA7146_CLIPPING_MEM,
+ &vv->d_clipping.dma_handle);
+ if( NULL == vv->d_clipping.cpu_addr ) {
+ ERR("out of memory. aborting.\n");
+ kfree(vv);
+ v4l2_ctrl_handler_free(hdl);
+ return -1;
+ }
+
+ saa7146_video_uops.init(dev,vv);
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ saa7146_vbi_uops.init(dev,vv);
+
+ vv->ov_fb.fmt.width = vv->standard->h_max_out;
+ vv->ov_fb.fmt.height = vv->standard->v_max_out;
+ vv->ov_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565;
+ vv->ov_fb.fmt.bytesperline = 2 * vv->ov_fb.fmt.width;
+ vv->ov_fb.fmt.sizeimage = vv->ov_fb.fmt.bytesperline * vv->ov_fb.fmt.height;
+ vv->ov_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt = &vv->video_fmt;
+ fmt->width = 384;
+ fmt->height = 288;
+ fmt->pixelformat = V4L2_PIX_FMT_BGR24;
+ fmt->field = V4L2_FIELD_ANY;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->bytesperline = 3 * fmt->width;
+ fmt->sizeimage = fmt->bytesperline * fmt->height;
+
+ vbi = &vv->vbi_fmt;
+ vbi->sampling_rate = 27000000;
+ vbi->offset = 248; /* todo */
+ vbi->samples_per_line = 720 * 2;
+ vbi->sample_format = V4L2_PIX_FMT_GREY;
+
+ /* fixme: this only works for PAL */
+ vbi->start[0] = 5;
+ vbi->count[0] = 16;
+ vbi->start[1] = 312;
+ vbi->count[1] = 16;
+
+ init_timer(&vv->vbi_read_timeout);
+
+ vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
+ dev->vv_data = vv;
+ dev->vv_callback = &vv_callback;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_vv_init);
+
+int saa7146_vv_release(struct saa7146_dev* dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE("dev:%p\n", dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+ pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ kfree(vv);
+ dev->vv_data = NULL;
+ dev->vv_callback = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_vv_release);
+
+int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev,
+ char *name, int type)
+{
+ int err;
+ int i;
+
+ DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
+
+ vfd->fops = &video_fops;
+ if (type == VFL_TYPE_GRABBER)
+ vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
+ else
+ vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
+ vfd->release = video_device_release_empty;
+ vfd->lock = &dev->v4l2_lock;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->tvnorms = 0;
+ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
+ vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
+ strlcpy(vfd->name, name, sizeof(vfd->name));
+ video_set_drvdata(vfd, dev);
+
+ err = video_register_device(vfd, type, -1);
+ if (err < 0) {
+ ERR("cannot register v4l2 device. skipping.\n");
+ return err;
+ }
+
+ pr_info("%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(vfd));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_register_device);
+
+int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev)
+{
+ DEB_EE("dev:%p\n", dev);
+
+ video_unregister_device(vfd);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_unregister_device);
+
+static int __init saa7146_vv_init_module(void)
+{
+ return 0;
+}
+
+
+static void __exit saa7146_vv_cleanup_module(void)
+{
+}
+
+module_init(saa7146_vv_init_module);
+module_exit(saa7146_vv_cleanup_module);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146/saa7146_hlp.c b/drivers/media/common/saa7146/saa7146_hlp.c
new file mode 100644
index 000000000..be746d1ae
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_hlp.c
@@ -0,0 +1,1048 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <media/saa7146_vv.h>
+
+static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format)
+{
+ /* clear out the necessary bits */
+ *clip_format &= 0x0000ffff;
+ /* set these bits new */
+ *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16));
+}
+
+static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl)
+{
+ *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28);
+ *hps_ctrl |= (source << 30) | (sync << 28);
+}
+
+static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl)
+{
+ int hyo = 0, hxo = 0;
+
+ hyo = vv->standard->v_offset;
+ hxo = vv->standard->h_offset;
+
+ *hps_h_scale &= ~(MASK_B0 | 0xf00);
+ *hps_h_scale |= (hxo << 0);
+
+ *hps_ctrl &= ~(MASK_W0 | MASK_B2);
+ *hps_ctrl |= (hyo << 12);
+}
+
+/* helper functions for the calculation of the horizontal- and vertical
+ scaling registers, clip-format-register etc ...
+ these functions take pointers to the (most-likely read-out
+ original-values) and manipulate them according to the requested
+ changes.
+*/
+
+/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */
+static struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_h_coeff_tab [] = {
+ {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8},
+ {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8},
+ {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8},
+ {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8},
+ {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8},
+ {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8},
+ {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8},
+ {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8},
+ {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8},
+ {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16}
+};
+
+/* table of attenuation values for horizontal scaling */
+static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0};
+
+/* calculate horizontal scale registers */
+static int calculate_h_scale_registers(struct saa7146_dev *dev,
+ int in_x, int out_x, int flip_lr,
+ u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale)
+{
+ /* horizontal prescaler */
+ u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0;
+ /* horizontal scaler */
+ u32 xim = 0, xp = 0, xsci =0;
+ /* vertical scale & gain */
+ u32 pfuv = 0;
+
+ /* helper variables */
+ u32 h_atten = 0, i = 0;
+
+ if ( 0 == out_x ) {
+ return -EINVAL;
+ }
+
+ /* mask out vanity-bit */
+ *hps_ctrl &= ~MASK_29;
+
+ /* calculate prescale-(xspc)-value: [n .. 1/2) : 1
+ [1/2 .. 1/3) : 2
+ [1/3 .. 1/4) : 3
+ ... */
+ if (in_x > out_x) {
+ xpsc = in_x / out_x;
+ }
+ else {
+ /* zooming */
+ xpsc = 1;
+ }
+
+ /* if flip_lr-bit is set, number of pixels after
+ horizontal prescaling must be < 384 */
+ if ( 0 != flip_lr ) {
+
+ /* set vanity bit */
+ *hps_ctrl |= MASK_29;
+
+ while (in_x / xpsc >= 384 )
+ xpsc++;
+ }
+ /* if zooming is wanted, number of pixels after
+ horizontal prescaling must be < 768 */
+ else {
+ while ( in_x / xpsc >= 768 )
+ xpsc++;
+ }
+
+ /* maximum prescale is 64 (p.69) */
+ if ( xpsc > 64 )
+ xpsc = 64;
+
+ /* keep xacm clear*/
+ xacm = 0;
+
+ /* set horizontal filter parameters (CXY = CXUV) */
+ cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff;
+ cxuv = cxy;
+
+ /* calculate and set horizontal fine scale (xsci) */
+
+ /* bypass the horizontal scaler ? */
+ if ( (in_x == out_x) && ( 1 == xpsc ) )
+ xsci = 0x400;
+ else
+ xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc;
+
+ /* set start phase for horizontal fine scale (xp) to 0 */
+ xp = 0;
+
+ /* set xim, if we bypass the horizontal scaler */
+ if ( 0x400 == xsci )
+ xim = 1;
+ else
+ xim = 0;
+
+ /* if the prescaler is bypassed, enable horizontal
+ accumulation mode (xacm) and clear dcgx */
+ if( 1 == xpsc ) {
+ xacm = 1;
+ dcgx = 0;
+ } else {
+ xacm = 0;
+ /* get best match in the table of attenuations
+ for horizontal scaling */
+ h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum;
+
+ for (i = 0; h_attenuation[i] != 0; i++) {
+ if (h_attenuation[i] >= h_atten)
+ break;
+ }
+
+ dcgx = i;
+ }
+
+ /* the horizontal scaling increment controls the UV filter
+ to reduce the bandwidth to improve the display quality,
+ so set it ... */
+ if ( xsci == 0x400)
+ pfuv = 0x00;
+ else if ( xsci < 0x600)
+ pfuv = 0x01;
+ else if ( xsci < 0x680)
+ pfuv = 0x11;
+ else if ( xsci < 0x700)
+ pfuv = 0x22;
+ else
+ pfuv = 0x33;
+
+
+ *hps_v_gain &= MASK_W0|MASK_B2;
+ *hps_v_gain |= (pfuv << 24);
+
+ *hps_h_scale &= ~(MASK_W1 | 0xf000);
+ *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12);
+
+ *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0);
+
+ return 0;
+}
+
+static struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_v_coeff_tab [] = {
+ {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8},
+ {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16},
+ {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16},
+ {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32},
+ {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32},
+ {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32},
+ {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64},
+ {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64},
+ {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64},
+ {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128}
+};
+
+/* table of attenuation values for vertical scaling */
+static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0};
+
+/* calculate vertical scale registers */
+static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field,
+ int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain)
+{
+ int lpi = 0;
+
+ /* vertical scaling */
+ u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0;
+ /* vertical scale & gain */
+ u32 dcgy = 0, cya_cyb = 0;
+
+ /* helper variables */
+ u32 v_atten = 0, i = 0;
+
+ /* error, if vertical zooming */
+ if ( in_y < out_y ) {
+ return -EINVAL;
+ }
+
+ /* linear phase interpolation may be used
+ if scaling is between 1 and 1/2 (both fields used)
+ or scaling is between 1/2 and 1/4 (if only one field is used) */
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ if( 2*out_y >= in_y) {
+ lpi = 1;
+ }
+ } else if (field == V4L2_FIELD_TOP
+ || field == V4L2_FIELD_ALTERNATE
+ || field == V4L2_FIELD_BOTTOM) {
+ if( 4*out_y >= in_y ) {
+ lpi = 1;
+ }
+ out_y *= 2;
+ }
+ if( 0 != lpi ) {
+
+ yacm = 0;
+ yacl = 0;
+ cya_cyb = 0x00ff;
+
+ /* calculate scaling increment */
+ if ( in_y > out_y )
+ ysci = ((1024 * in_y) / (out_y + 1)) - 1024;
+ else
+ ysci = 0;
+
+ dcgy = 0;
+
+ /* calculate ype and ypo */
+ ype = ysci / 16;
+ ypo = ype + (ysci / 64);
+
+ } else {
+ yacm = 1;
+
+ /* calculate scaling increment */
+ ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10;
+
+ /* calculate ype and ypo */
+ ypo = ype = ((ysci + 15) / 16);
+
+ /* the sequence length interval (yacl) has to be set according
+ to the prescale value, e.g. [n .. 1/2) : 0
+ [1/2 .. 1/3) : 1
+ [1/3 .. 1/4) : 2
+ ... */
+ if ( ysci < 512) {
+ yacl = 0;
+ } else {
+ yacl = ( ysci / (1024 - ysci) );
+ }
+
+ /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
+ cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff;
+
+ /* get best match in the table of attenuations for vertical scaling */
+ v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum;
+
+ for (i = 0; v_attenuation[i] != 0; i++) {
+ if (v_attenuation[i] >= v_atten)
+ break;
+ }
+
+ dcgy = i;
+ }
+
+ /* ypo and ype swapped in spec ? */
+ *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1);
+
+ *hps_v_gain &= ~(MASK_W0|MASK_B2);
+ *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0);
+
+ return 0;
+}
+
+/* simple bubble-sort algorithm with duplicate elimination */
+static int sort_and_eliminate(u32* values, int* count)
+{
+ int low = 0, high = 0, top = 0, temp = 0;
+ int cur = 0, next = 0;
+
+ /* sanity checks */
+ if( (0 > *count) || (NULL == values) ) {
+ return -EINVAL;
+ }
+
+ /* bubble sort the first @count items of the array @values */
+ for( top = *count; top > 0; top--) {
+ for( low = 0, high = 1; high < top; low++, high++) {
+ if( values[low] > values[high] ) {
+ temp = values[low];
+ values[low] = values[high];
+ values[high] = temp;
+ }
+ }
+ }
+
+ /* remove duplicate items */
+ for( cur = 0, next = 1; next < *count; next++) {
+ if( values[cur] != values[next])
+ values[++cur] = values[next];
+ }
+
+ *count = cur + 1;
+
+ return 0;
+}
+
+static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh,
+ struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ __le32 *clipping = vv->d_clipping.cpu_addr;
+
+ int width = vv->ov.win.w.width;
+ int height = vv->ov.win.w.height;
+ int clipcount = vv->ov.nclips;
+
+ u32 line_list[32];
+ u32 pixel_list[32];
+ int numdwords = 0;
+
+ int i = 0, j = 0;
+ int cnt_line = 0, cnt_pixel = 0;
+
+ int x[32], y[32], w[32], h[32];
+
+ /* clear out memory */
+ memset(&line_list[0], 0x00, sizeof(u32)*32);
+ memset(&pixel_list[0], 0x00, sizeof(u32)*32);
+ memset(clipping, 0x00, SAA7146_CLIPPING_MEM);
+
+ /* fill the line and pixel-lists */
+ for(i = 0; i < clipcount; i++) {
+ int l = 0, r = 0, t = 0, b = 0;
+
+ x[i] = vv->ov.clips[i].c.left;
+ y[i] = vv->ov.clips[i].c.top;
+ w[i] = vv->ov.clips[i].c.width;
+ h[i] = vv->ov.clips[i].c.height;
+
+ if( w[i] < 0) {
+ x[i] += w[i]; w[i] = -w[i];
+ }
+ if( h[i] < 0) {
+ y[i] += h[i]; h[i] = -h[i];
+ }
+ if( x[i] < 0) {
+ w[i] += x[i]; x[i] = 0;
+ }
+ if( y[i] < 0) {
+ h[i] += y[i]; y[i] = 0;
+ }
+ if( 0 != vv->vflip ) {
+ y[i] = height - y[i] - h[i];
+ }
+
+ l = x[i];
+ r = x[i]+w[i];
+ t = y[i];
+ b = y[i]+h[i];
+
+ /* insert left/right coordinates */
+ pixel_list[ 2*i ] = min_t(int, l, width);
+ pixel_list[(2*i)+1] = min_t(int, r, width);
+ /* insert top/bottom coordinates */
+ line_list[ 2*i ] = min_t(int, t, height);
+ line_list[(2*i)+1] = min_t(int, b, height);
+ }
+
+ /* sort and eliminate lists */
+ cnt_line = cnt_pixel = 2*clipcount;
+ sort_and_eliminate( &pixel_list[0], &cnt_pixel );
+ sort_and_eliminate( &line_list[0], &cnt_line );
+
+ /* calculate the number of used u32s */
+ numdwords = max_t(int, (cnt_line+1), (cnt_pixel+1))*2;
+ numdwords = max_t(int, 4, numdwords);
+ numdwords = min_t(int, 64, numdwords);
+
+ /* fill up cliptable */
+ for(i = 0; i < cnt_pixel; i++) {
+ clipping[2*i] |= cpu_to_le32(pixel_list[i] << 16);
+ }
+ for(i = 0; i < cnt_line; i++) {
+ clipping[(2*i)+1] |= cpu_to_le32(line_list[i] << 16);
+ }
+
+ /* fill up cliptable with the display infos */
+ for(j = 0; j < clipcount; j++) {
+
+ for(i = 0; i < cnt_pixel; i++) {
+
+ if( x[j] < 0)
+ x[j] = 0;
+
+ if( pixel_list[i] < (x[j] + w[j])) {
+
+ if ( pixel_list[i] >= x[j] ) {
+ clipping[2*i] |= cpu_to_le32(1 << j);
+ }
+ }
+ }
+ for(i = 0; i < cnt_line; i++) {
+
+ if( y[j] < 0)
+ y[j] = 0;
+
+ if( line_list[i] < (y[j] + h[j]) ) {
+
+ if( line_list[i] >= y[j] ) {
+ clipping[(2*i)+1] |= cpu_to_le32(1 << j);
+ }
+ }
+ }
+ }
+
+ /* adjust arbitration control register */
+ *arbtr_ctrl &= 0xffff00ff;
+ *arbtr_ctrl |= 0x00001c00;
+
+ vdma2->base_even = vv->d_clipping.dma_handle;
+ vdma2->base_odd = vv->d_clipping.dma_handle;
+ vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords));
+ vdma2->base_page = 0x04;
+ vdma2->pitch = 0x00;
+ vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) );
+
+ /* set clipping-mode. this depends on the field(s) used */
+ *clip_format &= 0xfffffff7;
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ *clip_format |= 0x00000008;
+ } else {
+ *clip_format |= 0x00000000;
+ }
+}
+
+/* disable clipping */
+static void saa7146_disable_clipping(struct saa7146_dev *dev)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* mask out relevant bits (=lower word)*/
+ clip_format &= MASK_W1;
+
+ /* upload clipping-registers*/
+ saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ /* disable video dma2 */
+ saa7146_write(dev, MC1, MASK_21);
+}
+
+static void saa7146_set_clipping_rect(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ enum v4l2_field field = vv->ov.win.field;
+ struct saa7146_video_dma vdma2;
+ u32 clip_format;
+ u32 arbtr_ctrl;
+
+ /* check clipcount, disable clipping if clipcount == 0*/
+ if (vv->ov.nclips == 0) {
+ saa7146_disable_clipping(dev);
+ return;
+ }
+
+ clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+ arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
+
+ calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field);
+
+ /* set clipping format */
+ clip_format &= 0xffff0008;
+ clip_format |= (SAA7146_CLIPPING_RECT << 4);
+
+ /* prepare video dma2 */
+ saa7146_write(dev, BASE_EVEN2, vdma2.base_even);
+ saa7146_write(dev, BASE_ODD2, vdma2.base_odd);
+ saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr);
+ saa7146_write(dev, BASE_PAGE2, vdma2.base_page);
+ saa7146_write(dev, PITCH2, vdma2.pitch);
+ saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte);
+
+ /* prepare the rest */
+ saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
+ saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
+
+ /* upload clip_control-register, clipping-registers, enable video dma2 */
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19));
+ saa7146_write(dev, MC1, (MASK_05 | MASK_21));
+}
+
+static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ int source = vv->current_hps_source;
+ int sync = vv->current_hps_sync;
+
+ u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
+
+ /* set vertical scale */
+ hps_v_scale = 0; /* all bits get set by the function-call */
+ hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/
+ calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain);
+
+ /* set horizontal scale */
+ hps_ctrl = 0;
+ hps_h_prescale = 0; /* all bits get set in the function */
+ hps_h_scale = 0;
+ calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
+
+ /* set hyo and hxo */
+ calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl);
+ calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl);
+
+ /* write out new register contents */
+ saa7146_write(dev, HPS_V_SCALE, hps_v_scale);
+ saa7146_write(dev, HPS_V_GAIN, hps_v_gain);
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale);
+ saa7146_write(dev, HPS_H_SCALE, hps_h_scale);
+
+ /* upload shadow-ram registers */
+ saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) );
+}
+
+/* calculate the new memory offsets for a desired position */
+static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field, u32 pixelformat)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pixelformat);
+
+ int b_depth = vv->ov_fmt->depth;
+ int b_bpl = vv->ov_fb.fmt.bytesperline;
+ /* The unsigned long cast is to remove a 64-bit compile warning since
+ it looks like a 64-bit address is cast to a 32-bit value, even
+ though the base pointer is really a 32-bit physical address that
+ goes into a 32-bit DMA register.
+ FIXME: might not work on some 64-bit platforms, but see the FIXME
+ in struct v4l2_framebuffer (videodev2.h) for that.
+ */
+ u32 base = (u32)(unsigned long)vv->ov_fb.base;
+
+ struct saa7146_video_dma vdma1;
+
+ /* calculate memory offsets for picture, look if we shall top-down-flip */
+ vdma1.pitch = 2*b_bpl;
+ if ( 0 == vv->vflip ) {
+ vdma1.base_even = base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2);
+ vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2));
+ }
+ else {
+ vdma1.base_even = base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2);
+ vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2));
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+
+ if ( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ }
+
+ vdma1.base_page = sfmt->swap;
+ vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels;
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+}
+
+static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* call helper function */
+ calculate_output_format_register(dev,palette,&clip_format);
+
+ /* update the hps registers */
+ saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+}
+
+/* select input-source */
+void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 hps_ctrl = 0;
+
+ /* read old state */
+ hps_ctrl = saa7146_read(dev, HPS_CTRL);
+
+ hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 );
+ hps_ctrl |= (source << 30) | (sync << 28);
+
+ /* write back & upload register */
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ vv->current_hps_source = source;
+ vv->current_hps_sync = sync;
+}
+EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync);
+
+int saa7146_enable_overlay(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field);
+ saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat);
+ saa7146_set_output_format(dev, vv->ov_fmt->trans);
+ saa7146_set_clipping_rect(fh);
+
+ /* enable video dma1 */
+ saa7146_write(dev, MC1, (MASK_06 | MASK_22));
+ return 0;
+}
+
+void saa7146_disable_overlay(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+
+ /* disable clipping + video dma1 */
+ saa7146_disable_clipping(dev);
+ saa7146_write(dev, MC1, MASK_22);
+}
+
+void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma)
+{
+ int where = 0;
+
+ if( which < 1 || which > 3) {
+ return;
+ }
+
+ /* calculate starting address */
+ where = (which-1)*0x18;
+
+ saa7146_write(dev, where, vdma->base_odd);
+ saa7146_write(dev, where+0x04, vdma->base_even);
+ saa7146_write(dev, where+0x08, vdma->prot_addr);
+ saa7146_write(dev, where+0x0c, vdma->pitch);
+ saa7146_write(dev, where+0x10, vdma->base_page);
+ saa7146_write(dev, where+0x14, vdma->num_line_byte);
+
+ /* upload */
+ saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1)));
+/*
+ printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even);
+ printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd);
+ printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr);
+ printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page);
+ printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch);
+ printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte);
+*/
+}
+
+static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_video_dma vdma1;
+
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ int width = buf->fmt->width;
+ int height = buf->fmt->height;
+ int bytesperline = buf->fmt->bytesperline;
+ enum v4l2_field field = buf->fmt->field;
+
+ int depth = sfmt->depth;
+
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
+
+ if( bytesperline != 0) {
+ vdma1.pitch = bytesperline*2;
+ } else {
+ vdma1.pitch = (width*depth*2)/8;
+ }
+ vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1 | sfmt->swap;
+
+ if( 0 != vv->vflip ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ if ( vv->last_field == V4L2_FIELD_TOP ) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+
+ if( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ return 0;
+}
+
+static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
+{
+ int height = buf->fmt->height;
+ int width = buf->fmt->width;
+
+ vdma2->pitch = width;
+ vdma3->pitch = width;
+
+ /* fixme: look at bytesperline! */
+
+ if( 0 != vv->vflip ) {
+ vdma2->prot_addr = buf->pt[1].offset;
+ vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
+
+ vdma3->prot_addr = buf->pt[2].offset;
+ vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
+ } else {
+ vdma3->base_even = buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2);
+ vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
+
+ vdma2->base_even = buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2);
+ vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
+ }
+
+ return 0;
+}
+
+static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
+{
+ int height = buf->fmt->height;
+ int width = buf->fmt->width;
+
+ vdma2->pitch = width/2;
+ vdma3->pitch = width/2;
+
+ if( 0 != vv->vflip ) {
+ vdma2->prot_addr = buf->pt[2].offset;
+ vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset;
+ vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
+
+ vdma3->prot_addr = buf->pt[1].offset;
+ vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset;
+ vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
+
+ } else {
+ vdma3->base_even = buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even + (vdma3->pitch);
+ vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
+
+ vdma2->base_even = buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even + (vdma2->pitch);
+ vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
+ }
+ return 0;
+}
+
+static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_video_dma vdma1;
+ struct saa7146_video_dma vdma2;
+ struct saa7146_video_dma vdma3;
+
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ int width = buf->fmt->width;
+ int height = buf->fmt->height;
+ enum v4l2_field field = buf->fmt->field;
+
+ BUG_ON(0 == buf->pt[0].dma);
+ BUG_ON(0 == buf->pt[1].dma);
+ BUG_ON(0 == buf->pt[2].dma);
+
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
+
+ /* fixme: look at bytesperline! */
+
+ /* fixme: what happens for user space buffers here?. The offsets are
+ most likely wrong, this version here only works for page-aligned
+ buffers, modifications to the pagetable-functions are necessary...*/
+
+ vdma1.pitch = width*2;
+ vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1;
+
+ if( 0 != vv->vflip ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset;
+ }
+
+ vdma2.num_line_byte = 0; /* unused */
+ vdma2.base_page = buf->pt[1].dma | ME1;
+
+ vdma3.num_line_byte = 0; /* unused */
+ vdma3.base_page = buf->pt[2].dma | ME1;
+
+ switch( sfmt->depth ) {
+ case 12: {
+ calc_planar_420(vv,buf,&vdma2,&vdma3);
+ break;
+ }
+ case 16: {
+ calc_planar_422(vv,buf,&vdma2,&vdma3);
+ break;
+ }
+ default: {
+ return -1;
+ }
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.base_even;
+ vdma2.base_even = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.base_even;
+ vdma3.base_even = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ }
+
+ if( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ vdma2.pitch *= -1;
+ vdma3.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) {
+ saa7146_write_out_dma(dev, 3, &vdma2);
+ saa7146_write_out_dma(dev, 2, &vdma3);
+ } else {
+ saa7146_write_out_dma(dev, 2, &vdma2);
+ saa7146_write_out_dma(dev, 3, &vdma3);
+ }
+ return 0;
+}
+
+static void program_capture_engine(struct saa7146_dev *dev, int planar)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ int count = 0;
+
+ unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/
+ WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait);
+ WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait);
+
+ /* set rps register 0 */
+ WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4));
+ WRITE_RPS0(MASK_27 | MASK_11);
+
+ /* turn on video-dma1 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_06 | MASK_22); /* => mask */
+ WRITE_RPS0(MASK_06 | MASK_22); /* => values */
+ if( 0 != planar ) {
+ /* turn on video-dma2 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
+ WRITE_RPS0(MASK_05 | MASK_21); /* => values */
+
+ /* turn on video-dma3 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS0(MASK_04 | MASK_20); /* => values */
+ }
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
+ WRITE_RPS0(CMD_PAUSE | o_wait);
+ WRITE_RPS0(CMD_PAUSE | e_wait);
+ } else if ( vv->last_field == V4L2_FIELD_TOP ) {
+ WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
+ WRITE_RPS0(CMD_PAUSE | o_wait);
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
+ WRITE_RPS0(CMD_PAUSE | e_wait);
+ }
+
+ /* turn off video-dma1 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_22 | MASK_06); /* => mask */
+ WRITE_RPS0(MASK_22); /* => values */
+ if( 0 != planar ) {
+ /* turn off video-dma2 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
+ WRITE_RPS0(MASK_21); /* => values */
+
+ /* turn off video-dma3 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS0(MASK_20); /* => values */
+ }
+
+ /* generate interrupt */
+ WRITE_RPS0(CMD_INTERRUPT);
+
+ /* stop */
+ WRITE_RPS0(CMD_STOP);
+}
+
+void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 vdma1_prot_addr;
+
+ DEB_CAP("buf:%p, next:%p\n", buf, next);
+
+ vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1);
+ if( 0 == vdma1_prot_addr ) {
+ /* clear out beginning of streaming bit (rps register 0)*/
+ DEB_CAP("forcing sync to new frame\n");
+ saa7146_write(dev, MC2, MASK_27 );
+ }
+
+ saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field);
+ saa7146_set_output_format(dev, sfmt->trans);
+ saa7146_disable_clipping(dev);
+
+ if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
+ } else if ( vv->last_field == V4L2_FIELD_TOP ) {
+ vv->last_field = V4L2_FIELD_BOTTOM;
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ vv->last_field = V4L2_FIELD_TOP;
+ }
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ calculate_video_dma_grab_planar(dev, buf);
+ program_capture_engine(dev,1);
+ } else {
+ calculate_video_dma_grab_packed(dev, buf);
+ program_capture_engine(dev,0);
+ }
+
+/*
+ printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
+ printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
+ printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
+ printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
+ printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
+ printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
+ printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1));
+*/
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+}
diff --git a/drivers/media/common/saa7146/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c
new file mode 100644
index 000000000..220271981
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_i2c.c
@@ -0,0 +1,423 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/saa7146_vv.h>
+
+static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
+{
+ /* DEB_I2C("'%s'\n", adapter->name); */
+
+ return I2C_FUNC_I2C
+ | I2C_FUNC_SMBUS_QUICK
+ | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE
+ | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+/* this function returns the status-register of our i2c-device */
+static inline u32 saa7146_i2c_status(struct saa7146_dev *dev)
+{
+ u32 iicsta = saa7146_read(dev, I2C_STATUS);
+ /* DEB_I2C("status: 0x%08x\n", iicsta); */
+ return iicsta;
+}
+
+/* this function runs through the i2c-messages and prepares the data to be
+ sent through the saa7146. have a look at the specifications p. 122 ff
+ to understand this. it returns the number of u32s to send, or -1
+ in case of an error. */
+static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op)
+{
+ int h1, h2;
+ int i, j, addr;
+ int mem = 0, op_count = 0;
+
+ /* first determine size of needed memory */
+ for(i = 0; i < num; i++) {
+ mem += m[i].len + 1;
+ }
+
+ /* worst case: we need one u32 for three bytes to be send
+ plus one extra byte to address the device */
+ mem = 1 + ((mem-1) / 3);
+
+ /* we assume that op points to a memory of at least
+ * SAA7146_I2C_MEM bytes size. if we exceed this limit...
+ */
+ if ((4 * mem) > SAA7146_I2C_MEM) {
+ /* DEB_I2C("cannot prepare i2c-message\n"); */
+ return -ENOMEM;
+ }
+
+ /* be careful: clear out the i2c-mem first */
+ memset(op,0,sizeof(__le32)*mem);
+
+ /* loop through all messages */
+ for(i = 0; i < num; i++) {
+
+ /* insert the address of the i2c-slave.
+ note: we get 7 bit i2c-addresses,
+ so we have to perform a translation */
+ addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0);
+ h1 = op_count/3; h2 = op_count%3;
+ op[h1] |= cpu_to_le32( (u8)addr << ((3-h2)*8));
+ op[h1] |= cpu_to_le32(SAA7146_I2C_START << ((3-h2)*2));
+ op_count++;
+
+ /* loop through all bytes of message i */
+ for(j = 0; j < m[i].len; j++) {
+ /* insert the data bytes */
+ h1 = op_count/3; h2 = op_count%3;
+ op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
+ op[h1] |= cpu_to_le32( SAA7146_I2C_CONT << ((3-h2)*2));
+ op_count++;
+ }
+
+ }
+
+ /* have a look at the last byte inserted:
+ if it was: ...CONT change it to ...STOP */
+ h1 = (op_count-1)/3; h2 = (op_count-1)%3;
+ if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) {
+ op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2));
+ op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2));
+ }
+
+ /* return the number of u32s to send */
+ return mem;
+}
+
+/* this functions loops through all i2c-messages. normally, it should determine
+ which bytes were read through the adapter and write them back to the corresponding
+ i2c-message. but instead, we simply write back all bytes.
+ fixme: this could be improved. */
+static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op)
+{
+ int i, j;
+ int op_count = 0;
+
+ /* loop through all messages */
+ for(i = 0; i < num; i++) {
+
+ op_count++;
+
+ /* loop through all bytes of message i */
+ for(j = 0; j < m[i].len; j++) {
+ /* write back all bytes that could have been read */
+ m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8));
+ op_count++;
+ }
+ }
+
+ return 0;
+}
+
+/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */
+static int saa7146_i2c_reset(struct saa7146_dev *dev)
+{
+ /* get current status */
+ u32 status = saa7146_i2c_status(dev);
+
+ /* clear registers for sure */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, 0);
+
+ /* check if any operation is still in progress */
+ if ( 0 != ( status & SAA7146_I2C_BUSY) ) {
+
+ /* yes, kill ongoing operation */
+ DEB_I2C("busy_state detected\n");
+
+ /* set "ABORT-OPERATION"-bit (bit 7)*/
+ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+
+ /* clear all error-bits pending; this is needed because p.123, note 1 */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+ }
+
+ /* check if any error is (still) present. (this can be necessary because p.123, note 1) */
+ status = saa7146_i2c_status(dev);
+
+ if ( dev->i2c_bitrate != status ) {
+
+ DEB_I2C("error_state detected. status:0x%08x\n", status);
+
+ /* Repeat the abort operation. This seems to be necessary
+ after serious protocol errors caused by e.g. the SAA7740 */
+ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+
+ /* clear all error-bits pending */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+
+ /* the data sheet says it might be necessary to clear the status
+ twice after an abort */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+ }
+
+ /* if any error is still present, a fatal error has occurred ... */
+ status = saa7146_i2c_status(dev);
+ if ( dev->i2c_bitrate != status ) {
+ DEB_I2C("fatal error. status:0x%08x\n", status);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* this functions writes out the data-byte 'dword' to the i2c-device.
+ it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
+ failed badly (e.g. address error) */
+static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay)
+{
+ u32 status = 0, mc2 = 0;
+ int trial = 0;
+ unsigned long timeout;
+
+ /* write out i2c-command */
+ DEB_I2C("before: 0x%08x (status: 0x%08x), %d\n",
+ *dword, saa7146_read(dev, I2C_STATUS), dev->i2c_op);
+
+ if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
+
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword));
+
+ dev->i2c_op = 1;
+ SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
+ SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+
+ timeout = HZ/100 + 1; /* 10ms */
+ timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout);
+ if (timeout == -ERESTARTSYS || dev->i2c_op) {
+ SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+ SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
+ if (timeout == -ERESTARTSYS)
+ /* a signal arrived */
+ return -ERESTARTSYS;
+
+ pr_warn("%s %s [irq]: timed out waiting for end of xfer\n",
+ dev->name, __func__);
+ return -EIO;
+ }
+ status = saa7146_read(dev, I2C_STATUS);
+ } else {
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+
+ /* do not poll for i2c-status before upload is complete */
+ timeout = jiffies + HZ/100 + 1; /* 10ms */
+ while(1) {
+ mc2 = (saa7146_read(dev, MC2) & 0x1);
+ if( 0 != mc2 ) {
+ break;
+ }
+ if (time_after(jiffies,timeout)) {
+ pr_warn("%s %s: timed out waiting for MC2\n",
+ dev->name, __func__);
+ return -EIO;
+ }
+ }
+ /* wait until we get a transfer done or error */
+ timeout = jiffies + HZ/100 + 1; /* 10ms */
+ /* first read usually delivers bogus results... */
+ saa7146_i2c_status(dev);
+ while(1) {
+ status = saa7146_i2c_status(dev);
+ if ((status & 0x3) != 1)
+ break;
+ if (time_after(jiffies,timeout)) {
+ /* this is normal when probing the bus
+ * (no answer from nonexisistant device...)
+ */
+ pr_warn("%s %s [poll]: timed out waiting for end of xfer\n",
+ dev->name, __func__);
+ return -EIO;
+ }
+ if (++trial < 50 && short_delay)
+ udelay(10);
+ else
+ msleep(1);
+ }
+ }
+
+ /* give a detailed status report */
+ if ( 0 != (status & (SAA7146_I2C_SPERR | SAA7146_I2C_APERR |
+ SAA7146_I2C_DTERR | SAA7146_I2C_DRERR |
+ SAA7146_I2C_AL | SAA7146_I2C_ERR |
+ SAA7146_I2C_BUSY)) ) {
+
+ if ( 0 == (status & SAA7146_I2C_ERR) ||
+ 0 == (status & SAA7146_I2C_BUSY) ) {
+ /* it may take some time until ERR goes high - ignore */
+ DEB_I2C("unexpected i2c status %04x\n", status);
+ }
+ if( 0 != (status & SAA7146_I2C_SPERR) ) {
+ DEB_I2C("error due to invalid start/stop condition\n");
+ }
+ if( 0 != (status & SAA7146_I2C_DTERR) ) {
+ DEB_I2C("error in data transmission\n");
+ }
+ if( 0 != (status & SAA7146_I2C_DRERR) ) {
+ DEB_I2C("error when receiving data\n");
+ }
+ if( 0 != (status & SAA7146_I2C_AL) ) {
+ DEB_I2C("error because arbitration lost\n");
+ }
+
+ /* we handle address-errors here */
+ if( 0 != (status & SAA7146_I2C_APERR) ) {
+ DEB_I2C("error in address phase\n");
+ return -EREMOTEIO;
+ }
+
+ return -EIO;
+ }
+
+ /* read back data, just in case we were reading ... */
+ *dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER));
+
+ DEB_I2C("after: 0x%08x\n", *dword);
+ return 0;
+}
+
+static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries)
+{
+ int i = 0, count = 0;
+ __le32 *buffer = dev->d_i2c.cpu_addr;
+ int err = 0;
+ int short_delay = 0;
+
+ if (mutex_lock_interruptible(&dev->i2c_lock))
+ return -ERESTARTSYS;
+
+ for(i=0;i<num;i++) {
+ DEB_I2C("msg:%d/%d\n", i+1, num);
+ }
+
+ /* prepare the message(s), get number of u32s to transfer */
+ count = saa7146_i2c_msg_prepare(msgs, num, buffer);
+ if ( 0 > count ) {
+ err = -1;
+ goto out;
+ }
+
+ if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) )
+ short_delay = 1;
+
+ do {
+ /* reset the i2c-device if necessary */
+ err = saa7146_i2c_reset(dev);
+ if ( 0 > err ) {
+ DEB_I2C("could not reset i2c-device\n");
+ goto out;
+ }
+
+ /* write out the u32s one after another */
+ for(i = 0; i < count; i++) {
+ err = saa7146_i2c_writeout(dev, &buffer[i], short_delay);
+ if ( 0 != err) {
+ /* this one is unsatisfying: some i2c slaves on some
+ dvb cards don't acknowledge correctly, so the saa7146
+ thinks that an address error occurred. in that case, the
+ transaction should be retrying, even if an address error
+ occurred. analog saa7146 based cards extensively rely on
+ i2c address probing, however, and address errors indicate that a
+ device is really *not* there. retrying in that case
+ increases the time the device needs to probe greatly, so
+ it should be avoided. So we bail out in irq mode after an
+ address error and trust the saa7146 address error detection. */
+ if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags))
+ goto out;
+ DEB_I2C("error while sending message(s). starting again\n");
+ break;
+ }
+ }
+ if( 0 == err ) {
+ err = num;
+ break;
+ }
+
+ /* delay a bit before retrying */
+ msleep(10);
+
+ } while (err != num && retries--);
+
+ /* quit if any error occurred */
+ if (err != num)
+ goto out;
+
+ /* if any things had to be read, get the results */
+ if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
+ DEB_I2C("could not cleanup i2c-message\n");
+ err = -1;
+ goto out;
+ }
+
+ /* return the number of delivered messages */
+ DEB_I2C("transmission successful. (msg:%d)\n", err);
+out:
+ /* another bug in revision 0: the i2c-registers get uploaded randomly by other
+ uploads, so we better clear them out before continuing */
+ if( 0 == dev->revision ) {
+ __le32 zero = 0;
+ saa7146_i2c_reset(dev);
+ if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
+ pr_info("revision 0 error. this should never happen\n");
+ }
+ }
+
+ mutex_unlock(&dev->i2c_lock);
+ return err;
+}
+
+/* utility functions */
+static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num)
+{
+ struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter);
+ struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
+
+ /* use helper function to transfer data */
+ return saa7146_i2c_transfer(dev, msg, num, adapter->retries);
+}
+
+
+/*****************************************************************************/
+/* i2c-adapter helper functions */
+
+/* exported algorithm data */
+static struct i2c_algorithm saa7146_algo = {
+ .master_xfer = saa7146_i2c_xfer,
+ .functionality = saa7146_i2c_func,
+};
+
+int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate)
+{
+ DEB_EE("bitrate: 0x%08x\n", bitrate);
+
+ /* enable i2c-port pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24));
+
+ dev->i2c_bitrate = bitrate;
+ saa7146_i2c_reset(dev);
+
+ if (i2c_adapter) {
+ i2c_set_adapdata(i2c_adapter, &dev->v4l2_dev);
+ i2c_adapter->dev.parent = &dev->pci->dev;
+ i2c_adapter->algo = &saa7146_algo;
+ i2c_adapter->algo_data = NULL;
+ i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
+ i2c_adapter->retries = SAA7146_I2C_RETRIES;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c
new file mode 100644
index 000000000..2da995758
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_vbi.c
@@ -0,0 +1,498 @@
+#include <media/saa7146_vv.h>
+
+static int vbi_pixel_to_capture = 720 * 2;
+
+static int vbi_workaround(struct saa7146_dev *dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ u32 *cpu;
+ dma_addr_t dma_addr;
+
+ int count = 0;
+ int i;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ DEB_VBI("dev:%p\n", dev);
+
+ /* once again, a bug in the saa7146: the brs acquisition
+ is buggy and especially the BXO-counter does not work
+ as specified. there is this workaround, but please
+ don't let me explain it. ;-) */
+
+ cpu = pci_alloc_consistent(dev->pci, 4096, &dma_addr);
+ if (NULL == cpu)
+ return -ENOMEM;
+
+ /* setup some basic programming, just for the workaround */
+ saa7146_write(dev, BASE_EVEN3, dma_addr);
+ saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture);
+ saa7146_write(dev, PROT_ADDR3, dma_addr+4096);
+ saa7146_write(dev, PITCH3, vbi_pixel_to_capture);
+ saa7146_write(dev, BASE_PAGE3, 0x0);
+ saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* BXO = 1h, BRS to outbound */
+ WRITE_RPS1(0xc000008c);
+ /* wait for vbi_a or vbi_b*/
+ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
+ DEB_D("...using port b\n");
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B);
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B);
+/*
+ WRITE_RPS1(CMD_PAUSE | MASK_09);
+*/
+ } else {
+ DEB_D("...using port a\n");
+ WRITE_RPS1(CMD_PAUSE | MASK_10);
+ }
+ /* upload brs */
+ WRITE_RPS1(CMD_UPLOAD | MASK_08);
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */
+ WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19);
+ /* wait for brs_done */
+ WRITE_RPS1(CMD_PAUSE | MASK_08);
+ /* upload brs */
+ WRITE_RPS1(CMD_UPLOAD | MASK_08);
+ /* load video-dma3 NumLines3 and NumBytes3 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4));
+ /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */
+ WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture));
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */
+ WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start
+ /* wait for brs_done */
+ WRITE_RPS1(CMD_PAUSE | MASK_08);
+ /* upload brs and video-dma3*/
+ WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04);
+ /* load mc2 register: enable dma3 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4));
+ WRITE_RPS1(MASK_20 | MASK_04);
+ /* generate interrupt */
+ WRITE_RPS1(CMD_INTERRUPT);
+ /* stop rps1 */
+ WRITE_RPS1(CMD_STOP);
+
+ /* we have to do the workaround twice to be sure that
+ everything is ok */
+ for(i = 0; i < 2; i++) {
+
+ /* indicate to the irq handler that we do the workaround */
+ saa7146_write(dev, MC2, MASK_31|MASK_15);
+
+ saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+ /* enable rps1 irqs */
+ SAA7146_IER_ENABLE(dev,MASK_28);
+
+ /* prepare to wait to be woken up by the irq-handler */
+ add_wait_queue(&vv->vbi_wq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* start rps1 to enable workaround */
+ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+ schedule();
+
+ DEB_VBI("brs bug workaround %d/1\n", i);
+
+ remove_wait_queue(&vv->vbi_wq, &wait);
+ __set_current_state(TASK_RUNNING);
+
+ /* disable rps1 irqs */
+ SAA7146_IER_DISABLE(dev,MASK_28);
+
+ /* stop video-dma3 */
+ saa7146_write(dev, MC1, MASK_20);
+
+ if(signal_pending(current)) {
+
+ DEB_VBI("aborted (rps:0x%08x)\n",
+ saa7146_read(dev, RPS_ADDR1));
+
+ /* stop rps1 for sure */
+ saa7146_write(dev, MC1, MASK_29);
+
+ pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
+ return -EINTR;
+ }
+ }
+
+ pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
+ return 0;
+}
+
+static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ struct saa7146_video_dma vdma3;
+
+ int count = 0;
+ unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+/*
+ vdma3.base_even = 0xc8000000+2560*70;
+ vdma3.base_odd = 0xc8000000;
+ vdma3.prot_addr = 0xc8000000+2560*164;
+ vdma3.pitch = 2560;
+ vdma3.base_page = 0;
+ vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above!
+*/
+ vdma3.base_even = buf->pt[2].offset;
+ vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture;
+ vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture;
+ vdma3.pitch = vbi_pixel_to_capture;
+ vdma3.base_page = buf->pt[2].dma | ME1;
+ vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture;
+
+ saa7146_write_out_dma(dev, 3, &vdma3);
+
+ /* write beginning of rps-program */
+ count = 0;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */
+
+ /* we don't wait here for the first field anymore. this is different from the video
+ capture and might cause that the first buffer is only half filled (with only
+ one field). but since this is some sort of streaming data, this is not that negative.
+ but by doing this, we can use the whole engine from videobuf-dma-sg.c... */
+
+/*
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait);
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait);
+*/
+ /* set bit 1 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4));
+ WRITE_RPS1(MASK_28 | MASK_12);
+
+ /* turn on video-dma3 */
+ WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS1(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS1(MASK_04 | MASK_20); /* => values */
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ WRITE_RPS1(CMD_PAUSE | o_wait);
+ WRITE_RPS1(CMD_PAUSE | e_wait);
+
+ /* generate interrupt */
+ WRITE_RPS1(CMD_INTERRUPT);
+
+ /* stop */
+ WRITE_RPS1(CMD_STOP);
+
+ /* enable rps1 irqs */
+ SAA7146_IER_ENABLE(dev, MASK_28);
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+}
+
+static int buffer_activate(struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ buf->vb.state = VIDEOBUF_ACTIVE;
+
+ DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next);
+ saa7146_set_vbi_capture(dev,buf,next);
+
+ mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ int err = 0;
+ int lines, llength, size;
+
+ lines = 16 * 2 ; /* 2 fields */
+ llength = vbi_pixel_to_capture;
+ size = lines * llength;
+
+ DEB_VBI("vb:%p\n", vb);
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < size) {
+ DEB_VBI("size mismatch\n");
+ return -EINVAL;
+ }
+
+ if (buf->vb.size != size)
+ saa7146_dma_free(dev,q,buf);
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ buf->vb.width = llength;
+ buf->vb.height = lines;
+ buf->vb.size = size;
+ buf->vb.field = field; // FIXME: check this
+
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+
+ err = videobuf_iolock(q,&buf->vb, NULL);
+ if (err)
+ goto oops;
+ err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2],
+ dma->sglist, dma->sglen);
+ if (0 != err)
+ return err;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ oops:
+ DEB_VBI("error out\n");
+ saa7146_dma_free(dev,q,buf);
+
+ return err;
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ int llength,lines;
+
+ lines = 16 * 2 ; /* 2 fields */
+ llength = vbi_pixel_to_capture;
+
+ *size = lines * llength;
+ *count = 2;
+
+ DEB_VBI("count:%d, size:%d\n", *count, *size);
+
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_VBI("vb:%p\n", vb);
+ saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_VBI("vb:%p\n", vb);
+ saa7146_dma_free(dev,q,buf);
+}
+
+static struct videobuf_queue_ops vbi_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static void vbi_stop(struct saa7146_fh *fh, struct file *file)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ unsigned long flags;
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps1 */
+ saa7146_write(dev, MC1, MASK_29);
+
+ /* disable rps1 irqs */
+ SAA7146_IER_DISABLE(dev, MASK_28);
+
+ /* shut down dma 3 transfers */
+ saa7146_write(dev, MC1, MASK_20);
+
+ if (vv->vbi_dmaq.curr)
+ saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
+
+ videobuf_queue_cancel(&fh->vbi_q);
+
+ vv->vbi_streaming = NULL;
+
+ del_timer(&vv->vbi_dmaq.timeout);
+ del_timer(&vv->vbi_read_timeout);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static void vbi_read_timeout(unsigned long data)
+{
+ struct file *file = (struct file*)data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ vbi_stop(fh, file);
+}
+
+static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
+{
+ DEB_VBI("dev:%p\n", dev);
+
+ INIT_LIST_HEAD(&vv->vbi_dmaq.queue);
+
+ init_timer(&vv->vbi_dmaq.timeout);
+ vv->vbi_dmaq.timeout.function = saa7146_buffer_timeout;
+ vv->vbi_dmaq.timeout.data = (unsigned long)(&vv->vbi_dmaq);
+ vv->vbi_dmaq.dev = dev;
+
+ init_waitqueue_head(&vv->vbi_wq);
+}
+
+static int vbi_open(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = fh->dev->vv_data;
+
+ u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
+ int ret = 0;
+
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS);
+ if (0 == ret) {
+ DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n");
+ return -EBUSY;
+ }
+
+ /* adjust arbitrition control for video dma 3 */
+ arbtr_ctrl &= ~0x1f0000;
+ arbtr_ctrl |= 0x1d0000;
+ saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
+ saa7146_write(dev, MC2, (MASK_04|MASK_20));
+
+ videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB, // FIXME: does this really work?
+ sizeof(struct saa7146_buf),
+ file, &dev->v4l2_lock);
+
+ vv->vbi_read_timeout.function = vbi_read_timeout;
+ vv->vbi_read_timeout.data = (unsigned long)file;
+
+ /* initialize the brs */
+ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
+ saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19));
+ } else {
+ saa7146_write(dev, BRS_CTRL, 0x00000001);
+
+ if (0 != (ret = vbi_workaround(dev))) {
+ DEB_VBI("vbi workaround failed!\n");
+ /* return ret;*/
+ }
+ }
+
+ /* upload brs register */
+ saa7146_write(dev, MC2, (MASK_08|MASK_24));
+ return 0;
+}
+
+static void vbi_close(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = dev->vv_data;
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ if( fh == vv->vbi_streaming ) {
+ vbi_stop(fh, file);
+ }
+ saa7146_res_free(fh, RESOURCE_DMA3_BRS);
+}
+
+static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ spin_lock(&dev->slock);
+
+ if (vv->vbi_dmaq.curr) {
+ DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr);
+ /* this must be += 2, one count for each field */
+ vv->vbi_fieldcount+=2;
+ vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount;
+ saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
+ } else {
+ DEB_VBI("dev:%p\n", dev);
+ }
+ saa7146_buffer_next(dev, &vv->vbi_dmaq, 1);
+
+ spin_unlock(&dev->slock);
+}
+
+static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ ssize_t ret = 0;
+
+ DEB_VBI("dev:%p, fh:%p\n", dev, fh);
+
+ if( NULL == vv->vbi_streaming ) {
+ // fixme: check if dma3 is available
+ // fixme: activate vbi engine here if necessary. (really?)
+ vv->vbi_streaming = fh;
+ }
+
+ if( fh != vv->vbi_streaming ) {
+ DEB_VBI("open %p is already using vbi capture\n",
+ vv->vbi_streaming);
+ return -EBUSY;
+ }
+
+ mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
+ ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+/*
+ printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3));
+ printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3));
+ printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3));
+ printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3));
+ printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3));
+ printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3));
+ printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL));
+*/
+ return ret;
+}
+
+struct saa7146_use_ops saa7146_vbi_uops = {
+ .init = vbi_init,
+ .open = vbi_open,
+ .release = vbi_close,
+ .irq_done = vbi_irq_done,
+ .read = vbi_read,
+};
diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c
new file mode 100644
index 000000000..30779498c
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_video.c
@@ -0,0 +1,1309 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/saa7146_vv.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <linux/module.h>
+
+static int max_memory = 32;
+
+module_param(max_memory, int, 0644);
+MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)");
+
+#define IS_CAPTURE_ACTIVE(fh) \
+ (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh))
+
+#define IS_OVERLAY_ACTIVE(fh) \
+ (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh))
+
+/* format descriptions for capture and preview */
+static struct saa7146_format formats[] = {
+ {
+ .name = "RGB-8 (3-3-2)",
+ .pixelformat = V4L2_PIX_FMT_RGB332,
+ .trans = RGB08_COMPOSED,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .name = "RGB-16 (5/B-6/G-5/R)",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .trans = RGB16_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }, {
+ .name = "RGB-24 (B-G-R)",
+ .pixelformat = V4L2_PIX_FMT_BGR24,
+ .trans = RGB24_COMPOSED,
+ .depth = 24,
+ .flags = 0,
+ }, {
+ .name = "RGB-32 (B-G-R)",
+ .pixelformat = V4L2_PIX_FMT_BGR32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ }, {
+ .name = "RGB-32 (R-G-B)",
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ .swap = 0x2,
+ }, {
+ .name = "Greyscale-8",
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .trans = Y8,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .name = "YUV 4:2:2 planar (Y-Cb-Cr)",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ .trans = YUV422_DECOMPOSED,
+ .depth = 16,
+ .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
+ }, {
+ .name = "YVU 4:2:0 planar (Y-Cb-Cr)",
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
+ }, {
+ .name = "YUV 4:2:0 planar (Y-Cb-Cr)",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_IS_PLANAR,
+ }, {
+ .name = "YUV 4:2:2 (U-Y-V-Y)",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .trans = YUV422_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }
+};
+
+/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
+ due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped
+ (like V4L2_PIX_FMT_YUYV) ... 8-( */
+
+static int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format);
+
+struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc)
+{
+ int i, j = NUM_FORMATS;
+
+ for (i = 0; i < j; i++) {
+ if (formats[i].pixelformat == fourcc) {
+ return formats+i;
+ }
+ }
+
+ DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
+ return NULL;
+}
+
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f);
+
+int saa7146_start_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct v4l2_format fmt;
+ int ret = 0, err = 0;
+
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ /* check if we have overlay information */
+ if (vv->ov.fh == NULL) {
+ DEB_D("no overlay data available. try S_FMT first.\n");
+ return -EAGAIN;
+ }
+
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D("streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh == fh) {
+ DEB_D("overlay is already active\n");
+ return 0;
+ }
+ DEB_D("overlay is already active in another open\n");
+ return -EBUSY;
+ }
+
+ if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
+ DEB_D("cannot get necessary overlay resources\n");
+ return -EBUSY;
+ }
+
+ fmt.fmt.win = vv->ov.win;
+ err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt);
+ if (0 != err) {
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return -EBUSY;
+ }
+ vv->ov.win = fmt.fmt.win;
+
+ DEB_D("%dx%d+%d+%d %s field=%s\n",
+ vv->ov.win.w.width, vv->ov.win.w.height,
+ vv->ov.win.w.left, vv->ov.win.w.top,
+ vv->ov_fmt->name, v4l2_field_names[vv->ov.win.field]);
+
+ if (0 != (ret = saa7146_enable_overlay(fh))) {
+ DEB_D("enabling overlay failed: %d\n", ret);
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return ret;
+ }
+
+ vv->video_status = STATUS_OVERLAY;
+ vv->video_fh = fh;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_start_preview);
+
+int saa7146_stop_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D("streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ /* check if overlay is running at all */
+ if ((vv->video_status & STATUS_OVERLAY) == 0) {
+ DEB_D("no active overlay\n");
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_D("overlay is active, but in another open\n");
+ return -EBUSY;
+ }
+
+ vv->video_status = 0;
+ vv->video_fh = NULL;
+
+ saa7146_disable_overlay(fh);
+
+ saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_stop_preview);
+
+/********************************************************************************/
+/* common pagetable functions */
+
+static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ struct pci_dev *pci = dev->pci;
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ struct scatterlist *list = dma->sglist;
+ int length = dma->sglen;
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length);
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ struct saa7146_pgtable *pt1 = &buf->pt[0];
+ struct saa7146_pgtable *pt2 = &buf->pt[1];
+ struct saa7146_pgtable *pt3 = &buf->pt[2];
+ __le32 *ptr1, *ptr2, *ptr3;
+ __le32 fill;
+
+ int size = buf->fmt->width*buf->fmt->height;
+ int i,p,m1,m2,m3,o1,o2;
+
+ switch( sfmt->depth ) {
+ case 12: {
+ /* create some offsets inside the page table */
+ m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
+ m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1;
+ m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
+ o1 = size%PAGE_SIZE;
+ o2 = (size+(size/4))%PAGE_SIZE;
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
+ break;
+ }
+ case 16: {
+ /* create some offsets inside the page table */
+ m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
+ m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
+ m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1;
+ o1 = size%PAGE_SIZE;
+ o2 = (size+(size/2))%PAGE_SIZE;
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
+ break;
+ }
+ default: {
+ return -1;
+ }
+ }
+
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+
+ /* walk all pages, copy all page addresses to ptr1 */
+ for (i = 0; i < length; i++, list++) {
+ for (p = 0; p * 4096 < list->length; p++, ptr1++) {
+ *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset);
+ }
+ }
+/*
+ ptr1 = pt1->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+*/
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt1->offset = dma->sglist->offset;
+ pt2->offset = pt1->offset+o1;
+ pt3->offset = pt1->offset+o2;
+
+ /* create video-dma2 page table */
+ ptr1 = pt1->cpu;
+ for(i = m1; i <= m2 ; i++, ptr2++) {
+ *ptr2 = ptr1[i];
+ }
+ fill = *(ptr2-1);
+ for(;i<1024;i++,ptr2++) {
+ *ptr2 = fill;
+ }
+ /* create video-dma3 page table */
+ ptr1 = pt1->cpu;
+ for(i = m2; i <= m3; i++,ptr3++) {
+ *ptr3 = ptr1[i];
+ }
+ fill = *(ptr3-1);
+ for(;i<1024;i++,ptr3++) {
+ *ptr3 = fill;
+ }
+ /* finally: finish up video-dma1 page table */
+ ptr1 = pt1->cpu+m1;
+ fill = pt1->cpu[m1];
+ for(i=m1;i<1024;i++,ptr1++) {
+ *ptr1 = fill;
+ }
+/*
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr2 %d: 0x%08x\n",j,ptr2[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr3 %d: 0x%08x\n",j,ptr3[j]);
+ }
+*/
+ } else {
+ struct saa7146_pgtable *pt = &buf->pt[0];
+ return saa7146_pgtable_build_single(pci, pt, list, length);
+ }
+
+ return 0;
+}
+
+
+/********************************************************************************/
+/* file operations */
+
+static int video_begin(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt = NULL;
+ unsigned int resource;
+ int ret = 0, err = 0;
+
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
+ if (vv->video_fh == fh) {
+ DEB_S("already capturing\n");
+ return 0;
+ }
+ DEB_S("already capturing in another open\n");
+ return -EBUSY;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ DEB_S("warning: suspending overlay video for streaming capture\n");
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (0 != err) {
+ DEB_D("suspending video failed. aborting\n");
+ return err;
+ }
+ }
+
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ BUG_ON(NULL == fmt);
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ }
+
+ ret = saa7146_res_get(fh, resource);
+ if (0 == ret) {
+ DEB_S("cannot get capture resource %d\n", resource);
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+ return -EBUSY;
+ }
+
+ /* clear out beginning of streaming bit (rps register 0)*/
+ saa7146_write(dev, MC2, MASK_27 );
+
+ /* enable rps0 irqs */
+ SAA7146_IER_ENABLE(dev, MASK_27);
+
+ vv->video_fh = fh;
+ vv->video_status = STATUS_CAPTURE;
+
+ return 0;
+}
+
+static int video_end(struct saa7146_fh *fh, struct file *file)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt = NULL;
+ unsigned long flags;
+ unsigned int resource;
+ u32 dmas = 0;
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+ DEB_S("not capturing\n");
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S("capturing, but in another open\n");
+ return -EBUSY;
+ }
+
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ BUG_ON(NULL == fmt);
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ dmas = MASK_22 | MASK_21 | MASK_20;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ dmas = MASK_22;
+ }
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps0 */
+ saa7146_write(dev, MC1, MASK_28);
+
+ /* disable rps0 irqs */
+ SAA7146_IER_DISABLE(dev, MASK_27);
+
+ /* shut down all used video dma transfers */
+ saa7146_write(dev, MC1, dmas);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ vv->video_fh = NULL;
+ vv->video_status = 0;
+
+ saa7146_res_free(fh, resource);
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ strcpy((char *)cap->driver, "saa7146 v4l2");
+ strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
+ sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->device_caps =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ cap->device_caps |= dev->ext_vv_data->capabilities;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ if (vdev->vfl_type == VFL_TYPE_GRABBER)
+ cap->device_caps &=
+ ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT);
+ else
+ cap->device_caps &=
+ ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO);
+ return 0;
+}
+
+static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ *fb = vv->ov_fb;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ fb->flags = V4L2_FBUF_FLAG_PRIMARY;
+ return 0;
+}
+
+static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt;
+
+ DEB_EE("VIDIOC_S_FBUF\n");
+
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ /* planar formats are not allowed for overlay video, clipping and video dma would clash */
+ if (fmt->flags & FORMAT_IS_PLANAR)
+ DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n",
+ (char *)&fmt->pixelformat);
+
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh != fh) {
+ DEB_D("refusing to change framebuffer informations while overlay is active in another open\n");
+ return -EBUSY;
+ }
+ }
+
+ /* ok, accept it */
+ vv->ov_fb = *fb;
+ vv->ov_fmt = fmt;
+
+ if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) {
+ vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8;
+ DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline);
+ }
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+ strlcpy((char *)f->description, formats[f->index].name,
+ sizeof(f->description));
+ f->pixelformat = formats[f->index].pixelformat;
+ return 0;
+}
+
+int saa7146_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct saa7146_dev *dev = container_of(ctrl->handler,
+ struct saa7146_dev, ctrl_handler);
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0x00ffffff;
+ val |= (ctrl->val << 24);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xff00ffff;
+ val |= (ctrl->val << 16);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_SATURATION:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xffffff00;
+ val |= (ctrl->val << 0);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_HFLIP:
+ /* fixme: we can support changing VFLIP and HFLIP here... */
+ if ((vv->video_status & STATUS_CAPTURE))
+ return -EBUSY;
+ vv->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ if ((vv->video_status & STATUS_CAPTURE))
+ return -EBUSY;
+ vv->vflip = ctrl->val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */
+ struct saa7146_fh *fh = vv->video_fh;
+
+ saa7146_stop_preview(fh);
+ saa7146_start_preview(fh);
+ }
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ parm->parm.capture.readbuffers = 1;
+ v4l2_video_std_frame_period(vv->standard->id,
+ &parm->parm.capture.timeperframe);
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.pix = vv->video_fmt;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.win = vv->ov.win;
+ return 0;
+}
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.vbi = vv->vbi_fmt;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt;
+ enum v4l2_field field;
+ int maxw, maxh;
+ int calc_bpl;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
+
+ fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = vv->standard->h_max_out;
+ maxh = vv->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+ switch (field) {
+ case V4L2_FIELD_ALTERNATE:
+ vv->last_field = V4L2_FIELD_TOP;
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ vv->last_field = V4L2_FIELD_INTERLACED;
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ vv->last_field = V4L2_FIELD_INTERLACED;
+ break;
+ default:
+ DEB_D("no known field mode '%d'\n", field);
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+
+ calc_bpl = (f->fmt.pix.width * fmt->depth) / 8;
+
+ if (f->fmt.pix.bytesperline < calc_bpl)
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+ DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+ return 0;
+}
+
+
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct v4l2_window *win = &f->fmt.win;
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ DEB_EE("dev:%p\n", dev);
+
+ if (NULL == vv->ov_fb.base) {
+ DEB_D("no fb base set\n");
+ return -EINVAL;
+ }
+ if (NULL == vv->ov_fmt) {
+ DEB_D("no fb fmt set\n");
+ return -EINVAL;
+ }
+ if (win->w.width < 48 || win->w.height < 32) {
+ DEB_D("min width/height. (%d,%d)\n",
+ win->w.width, win->w.height);
+ return -EINVAL;
+ }
+ if (win->clipcount > 16) {
+ DEB_D("clipcount too big\n");
+ return -EINVAL;
+ }
+
+ field = win->field;
+ maxw = vv->standard->h_max_out;
+ maxh = vv->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (win->w.height > maxh / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ DEB_D("no known field mode '%d'\n", field);
+ return -EINVAL;
+ }
+
+ win->field = field;
+ if (win->w.width > maxw)
+ win->w.width = maxw;
+ if (win->w.height > maxh)
+ win->w.height = maxh;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_EE("streaming capture is active\n");
+ return -EBUSY;
+ }
+ err = vidioc_try_fmt_vid_cap(file, fh, f);
+ if (0 != err)
+ return err;
+ vv->video_fmt = f->fmt.pix;
+ DEB_EE("set to pixelformat '%4.4s'\n",
+ (char *)&vv->video_fmt.pixelformat);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh);
+ err = vidioc_try_fmt_vid_overlay(file, fh, f);
+ if (0 != err)
+ return err;
+ vv->ov.win = f->fmt.win;
+ vv->ov.nclips = f->fmt.win.clipcount;
+ if (vv->ov.nclips > 16)
+ vv->ov.nclips = 16;
+ if (copy_from_user(vv->ov.clips, f->fmt.win.clips,
+ sizeof(struct v4l2_clip) * vv->ov.nclips)) {
+ return -EFAULT;
+ }
+
+ /* vv->ov.fh is used to indicate that we have valid overlay informations, too */
+ vv->ov.fh = fh;
+
+ /* check if our current overlay is active */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ saa7146_stop_preview(fh);
+ saa7146_start_preview(fh);
+ }
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ *norm = vv->standard->id;
+ return 0;
+}
+
+ /* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
+ PAL / NTSC / SECAM. if your hardware does not (or does more)
+ -- override this function in your extension */
+/*
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *e = arg;
+ if (e->index < 0 )
+ return -EINVAL;
+ if( e->index < dev->ext_vv_data->num_stds ) {
+ DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index);
+ v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
+ return 0;
+ }
+ return -EINVAL;
+ }
+ */
+
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int found = 0;
+ int err, i;
+
+ DEB_EE("VIDIOC_S_STD\n");
+
+ if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
+ DEB_D("cannot change video standard while streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (0 != err) {
+ DEB_D("suspending video failed. aborting\n");
+ return err;
+ }
+ }
+
+ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
+ if (id & dev->ext_vv_data->stds[i].id)
+ break;
+ if (i != dev->ext_vv_data->num_stds) {
+ vv->standard = &dev->ext_vv_data->stds[i];
+ if (NULL != dev->ext_vv_data->std_callback)
+ dev->ext_vv_data->std_callback(dev, vv->standard);
+ found = 1;
+ }
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ if (!found) {
+ DEB_EE("VIDIOC_S_STD: standard not found\n");
+ return -EINVAL;
+ }
+
+ DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name);
+ return 0;
+}
+
+static int vidioc_overlay(struct file *file, void *fh, unsigned int on)
+{
+ int err;
+
+ DEB_D("VIDIOC_OVERLAY on:%d\n", on);
+ if (on)
+ err = saa7146_start_preview(fh);
+ else
+ err = saa7146_stop_preview(fh);
+ return err;
+}
+
+static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_reqbufs(&fh->video_q, b);
+ if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_reqbufs(&fh->vbi_q, b);
+ return -EINVAL;
+}
+
+static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_querybuf(&fh->video_q, buf);
+ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_querybuf(&fh->vbi_q, buf);
+ return -EINVAL;
+}
+
+static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_qbuf(&fh->video_q, buf);
+ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_qbuf(&fh->vbi_q, buf);
+ return -EINVAL;
+}
+
+static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK);
+ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK);
+ return -EINVAL;
+}
+
+static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct saa7146_fh *fh = __fh;
+ int err;
+
+ DEB_D("VIDIOC_STREAMON, type:%d\n", type);
+
+ err = video_begin(fh);
+ if (err)
+ return err;
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_streamon(&fh->video_q);
+ if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_streamon(&fh->vbi_q);
+ return -EINVAL;
+}
+
+static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_D("VIDIOC_STREAMOFF, type:%d\n", type);
+
+ /* ugly: we need to copy some checks from video_end(),
+ because videobuf_streamoff() relies on the capture running.
+ check and fix this */
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+ DEB_S("not capturing\n");
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S("capturing, but in another open\n");
+ return -EBUSY;
+ }
+
+ err = -EINVAL;
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ err = videobuf_streamoff(&fh->video_q);
+ else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ err = videobuf_streamoff(&fh->vbi_q);
+ if (0 != err) {
+ DEB_D("warning: videobuf_streamoff() failed\n");
+ video_end(fh, file);
+ } else {
+ err = video_end(fh, file);
+ }
+ return err;
+}
+
+const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
+
+ .vidioc_overlay = vidioc_overlay,
+ .vidioc_g_fbuf = vidioc_g_fbuf,
+ .vidioc_s_fbuf = vidioc_s_fbuf,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*********************************************************************************/
+/* buffer handling functions */
+
+static int buffer_activate (struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ saa7146_set_capture(dev,buf,next);
+
+ mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+ int size,err = 0;
+
+ DEB_CAP("vbuf:%p\n", vb);
+
+ /* sanity checks */
+ if (vv->video_fmt.width < 48 ||
+ vv->video_fmt.height < 32 ||
+ vv->video_fmt.width > vv->standard->h_max_out ||
+ vv->video_fmt.height > vv->standard->v_max_out) {
+ DEB_D("w (%d) / h (%d) out of bounds\n",
+ vv->video_fmt.width, vv->video_fmt.height);
+ return -EINVAL;
+ }
+
+ size = vv->video_fmt.sizeimage;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size) {
+ DEB_D("size mismatch\n");
+ return -EINVAL;
+ }
+
+ DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
+ vv->video_fmt.width, vv->video_fmt.height,
+ size, v4l2_field_names[vv->video_fmt.field]);
+ if (buf->vb.width != vv->video_fmt.width ||
+ buf->vb.bytesperline != vv->video_fmt.bytesperline ||
+ buf->vb.height != vv->video_fmt.height ||
+ buf->vb.size != size ||
+ buf->vb.field != field ||
+ buf->vb.field != vv->video_fmt.field ||
+ buf->fmt != &vv->video_fmt) {
+ saa7146_dma_free(dev,q,buf);
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ struct saa7146_format *sfmt;
+
+ buf->vb.bytesperline = vv->video_fmt.bytesperline;
+ buf->vb.width = vv->video_fmt.width;
+ buf->vb.height = vv->video_fmt.height;
+ buf->vb.size = size;
+ buf->vb.field = field;
+ buf->fmt = &vv->video_fmt;
+ buf->vb.field = vv->video_fmt.field;
+
+ sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ release_all_pagetables(dev, buf);
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+ } else {
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ }
+
+ err = videobuf_iolock(q,&buf->vb, &vv->ov_fb);
+ if (err)
+ goto oops;
+ err = saa7146_pgtable_build(dev,buf);
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ oops:
+ DEB_D("error out\n");
+ saa7146_dma_free(dev,q,buf);
+
+ return err;
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = fh->dev->vv_data;
+
+ if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS)
+ *count = MAX_SAA7146_CAPTURE_BUFFERS;
+
+ *size = vv->video_fmt.sizeimage;
+
+ /* check if we exceed the "max_memory" parameter */
+ if( (*count * *size) > (max_memory*1048576) ) {
+ *count = (max_memory*1048576) / *size;
+ }
+
+ DEB_CAP("%d buffers, %d bytes each\n", *count, *size);
+
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP("vbuf:%p\n", vb);
+ saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP("vbuf:%p\n", vb);
+
+ saa7146_dma_free(dev,q,buf);
+
+ release_all_pagetables(dev, buf);
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************************************************************************/
+/* file operations */
+
+static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
+{
+ INIT_LIST_HEAD(&vv->video_dmaq.queue);
+
+ init_timer(&vv->video_dmaq.timeout);
+ vv->video_dmaq.timeout.function = saa7146_buffer_timeout;
+ vv->video_dmaq.timeout.data = (unsigned long)(&vv->video_dmaq);
+ vv->video_dmaq.dev = dev;
+
+ /* set some default values */
+ vv->standard = &dev->ext_vv_data->stds[0];
+
+ /* FIXME: what's this? */
+ vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A;
+ vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A;
+}
+
+
+static int video_open(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+
+ videobuf_queue_sg_init(&fh->video_q, &video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct saa7146_buf),
+ file, &dev->v4l2_lock);
+
+ return 0;
+}
+
+
+static void video_close(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct videobuf_queue *q = &fh->video_q;
+
+ if (IS_CAPTURE_ACTIVE(fh) != 0)
+ video_end(fh, file);
+ else if (IS_OVERLAY_ACTIVE(fh) != 0)
+ saa7146_stop_preview(fh);
+
+ videobuf_stop(q);
+ /* hmm, why is this function declared void? */
+}
+
+
+static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_dmaqueue *q = &vv->video_dmaq;
+
+ spin_lock(&dev->slock);
+ DEB_CAP("called\n");
+
+ /* only finish the buffer if we have one... */
+ if( NULL != q->curr ) {
+ saa7146_buffer_finish(dev,q,VIDEOBUF_DONE);
+ }
+ saa7146_buffer_next(dev,q,0);
+
+ spin_unlock(&dev->slock);
+}
+
+static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ ssize_t ret = 0;
+
+ DEB_EE("called\n");
+
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
+ /* fixme: should we allow read() captures while streaming capture? */
+ if (vv->video_fh == fh) {
+ DEB_S("already capturing\n");
+ return -EBUSY;
+ }
+ DEB_S("already capturing in another open\n");
+ return -EBUSY;
+ }
+
+ ret = video_begin(fh);
+ if( 0 != ret) {
+ goto out;
+ }
+
+ ret = videobuf_read_one(&fh->video_q , data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (ret != 0) {
+ video_end(fh, file);
+ } else {
+ ret = video_end(fh, file);
+ }
+out:
+ /* restart overlay if it was active before */
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ return ret;
+}
+
+struct saa7146_use_ops saa7146_video_uops = {
+ .init = video_init,
+ .open = video_open,
+ .release = video_close,
+ .irq_done = video_irq_done,
+ .read = video_read,
+};
diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig
new file mode 100644
index 000000000..4bfbd5f46
--- /dev/null
+++ b/drivers/media/common/siano/Kconfig
@@ -0,0 +1,32 @@
+#
+# Siano Mobile Silicon Digital TV device configuration
+#
+
+config SMS_SIANO_MDTV
+ tristate
+ depends on DVB_CORE && HAS_DMA
+ depends on !RC_CORE || RC_CORE
+ depends on SMS_USB_DRV || SMS_SDIO_DRV
+ default y
+
+config SMS_SIANO_RC
+ bool "Enable Remote Controller support for Siano devices"
+ depends on SMS_SIANO_MDTV && RC_CORE
+ depends on SMS_USB_DRV || SMS_SDIO_DRV
+ depends on MEDIA_COMMON_OPTIONS
+ default y
+ ---help---
+ Choose Y to select Remote Controller support for Siano driver.
+
+config SMS_SIANO_DEBUGFS
+ bool "Enable debugfs for smsdvb"
+ depends on SMS_SIANO_MDTV
+ depends on DEBUG_FS
+ depends on SMS_USB_DRV = SMS_SDIO_DRV
+
+ ---help---
+ Choose Y to enable visualizing a dump of the frontend
+ statistics response packets via debugfs. Currently, works
+ only with Siano USB devices.
+
+ Useful only for developers. In doubt, say N.
diff --git a/drivers/media/common/siano/Makefile b/drivers/media/common/siano/Makefile
new file mode 100644
index 000000000..4c0567f10
--- /dev/null
+++ b/drivers/media/common/siano/Makefile
@@ -0,0 +1,16 @@
+smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o
+smsdvb-objs := smsdvb-main.o
+
+obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o
+
+ifeq ($(CONFIG_SMS_SIANO_RC),y)
+ smsmdtv-objs += smsir.o
+endif
+
+ifeq ($(CONFIG_SMS_SIANO_DEBUGFS),y)
+ smsdvb-objs += smsdvb-debugfs.o
+endif
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
+
diff --git a/drivers/media/common/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c
new file mode 100644
index 000000000..ca2f80c77
--- /dev/null
+++ b/drivers/media/common/siano/sms-cards.c
@@ -0,0 +1,360 @@
+/*
+ * Card-specific functions for the Siano SMS1xxx USB dongle
+ *
+ * Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *
+ * 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 "sms-cards.h"
+#include "smsir.h"
+#include <linux/module.h>
+
+static struct sms_board sms_boards[] = {
+ [SMS_BOARD_UNKNOWN] = {
+ .name = "Unknown board",
+ .type = SMS_UNKNOWN_TYPE,
+ .default_mode = DEVICE_MODE_NONE,
+ },
+ [SMS1XXX_BOARD_SIANO_STELLAR] = {
+ .name = "Siano Stellar Digital Receiver",
+ .type = SMS_STELLAR,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_NOVA_A] = {
+ .name = "Siano Nova A Digital Receiver",
+ .type = SMS_NOVA_A0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_NOVA_B] = {
+ .name = "Siano Nova B Digital Receiver",
+ .type = SMS_NOVA_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_VEGA] = {
+ .name = "Siano Vega Digital Receiver",
+ .type = SMS_VEGA,
+ .default_mode = DEVICE_MODE_CMMB,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = {
+ .name = "Hauppauge Catamount",
+ .type = SMS_STELLAR,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_STELLAR,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = {
+ .name = "Hauppauge Okemo-A",
+ .type = SMS_NOVA_A0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_NOVA_A,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = {
+ .name = "Hauppauge Okemo-B",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_NOVA_B,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = {
+ .name = "Hauppauge WinTV MiniStick",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_HCW_55XXX,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .rc_codes = RC_MAP_HAUPPAUGE,
+ .board_cfg.leds_power = 26,
+ .board_cfg.led0 = 27,
+ .board_cfg.led1 = 28,
+ .board_cfg.ir = 9,
+ .led_power = 26,
+ .led_lo = 27,
+ .led_hi = 28,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD] = {
+ .name = "Hauppauge WinTV MiniCard",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .lna_ctrl = 29,
+ .board_cfg.foreign_lna0_ctrl = 29,
+ .rf_switch = 17,
+ .board_cfg.rf_switch_uhf = 17,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = {
+ .name = "Hauppauge WinTV MiniCard",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .lna_ctrl = -1,
+ },
+ [SMS1XXX_BOARD_SIANO_NICE] = {
+ .name = "Siano Nice Digital Receiver",
+ .type = SMS_NOVA_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_VENICE] = {
+ .name = "Siano Venice Digital Receiver",
+ .type = SMS_VEGA,
+ .default_mode = DEVICE_MODE_CMMB,
+ },
+ [SMS1XXX_BOARD_SIANO_STELLAR_ROM] = {
+ .name = "Siano Stellar Digital Receiver ROM",
+ .type = SMS_STELLAR,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .intf_num = 1,
+ },
+ [SMS1XXX_BOARD_ZTE_DVB_DATA_CARD] = {
+ .name = "ZTE Data Card Digital Receiver",
+ .type = SMS_NOVA_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .intf_num = 5,
+ .mtu = 15792,
+ },
+ [SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD] = {
+ .name = "ONDA Data Card Digital Receiver",
+ .type = SMS_NOVA_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .intf_num = 6,
+ .mtu = 15792,
+ },
+ [SMS1XXX_BOARD_SIANO_MING] = {
+ .name = "Siano Ming Digital Receiver",
+ .type = SMS_MING,
+ .default_mode = DEVICE_MODE_CMMB,
+ },
+ [SMS1XXX_BOARD_SIANO_PELE] = {
+ .name = "Siano Pele Digital Receiver",
+ .type = SMS_PELE,
+ .default_mode = DEVICE_MODE_ISDBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_RIO] = {
+ .name = "Siano Rio Digital Receiver",
+ .type = SMS_RIO,
+ .default_mode = DEVICE_MODE_ISDBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_DENVER_1530] = {
+ .name = "Siano Denver (ATSC-M/H) Digital Receiver",
+ .type = SMS_DENVER_1530,
+ .default_mode = DEVICE_MODE_ATSC,
+ .crystal = 2400,
+ },
+ [SMS1XXX_BOARD_SIANO_DENVER_2160] = {
+ .name = "Siano Denver (TDMB) Digital Receiver",
+ .type = SMS_DENVER_2160,
+ .default_mode = DEVICE_MODE_DAB_TDMB,
+ },
+ [SMS1XXX_BOARD_PCTV_77E] = {
+ .name = "Hauppauge microStick 77e",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+};
+
+struct sms_board *sms_get_board(unsigned id)
+{
+ BUG_ON(id >= ARRAY_SIZE(sms_boards));
+
+ return &sms_boards[id];
+}
+EXPORT_SYMBOL_GPL(sms_get_board);
+static inline void sms_gpio_assign_11xx_default_led_config(
+ struct smscore_config_gpio *p_gpio_config) {
+ p_gpio_config->direction = SMS_GPIO_DIRECTION_OUTPUT;
+ p_gpio_config->inputcharacteristics =
+ SMS_GPIO_INPUTCHARACTERISTICS_NORMAL;
+ p_gpio_config->outputdriving = SMS_GPIO_OUTPUTDRIVING_4mA;
+ p_gpio_config->outputslewrate = SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS;
+ p_gpio_config->pullupdown = SMS_GPIO_PULLUPDOWN_NONE;
+}
+
+int sms_board_event(struct smscore_device_t *coredev,
+ enum SMS_BOARD_EVENTS gevent)
+{
+ struct smscore_config_gpio my_gpio_config;
+
+ sms_gpio_assign_11xx_default_led_config(&my_gpio_config);
+
+ switch (gevent) {
+ case BOARD_EVENT_POWER_INIT: /* including hotplug */
+ break; /* BOARD_EVENT_BIND */
+
+ case BOARD_EVENT_POWER_SUSPEND:
+ break; /* BOARD_EVENT_POWER_SUSPEND */
+
+ case BOARD_EVENT_POWER_RESUME:
+ break; /* BOARD_EVENT_POWER_RESUME */
+
+ case BOARD_EVENT_BIND:
+ break; /* BOARD_EVENT_BIND */
+
+ case BOARD_EVENT_SCAN_PROG:
+ break; /* BOARD_EVENT_SCAN_PROG */
+ case BOARD_EVENT_SCAN_COMP:
+ break; /* BOARD_EVENT_SCAN_COMP */
+ case BOARD_EVENT_EMERGENCY_WARNING_SIGNAL:
+ break; /* BOARD_EVENT_EMERGENCY_WARNING_SIGNAL */
+ case BOARD_EVENT_FE_LOCK:
+ break; /* BOARD_EVENT_FE_LOCK */
+ case BOARD_EVENT_FE_UNLOCK:
+ break; /* BOARD_EVENT_FE_UNLOCK */
+ case BOARD_EVENT_DEMOD_LOCK:
+ break; /* BOARD_EVENT_DEMOD_LOCK */
+ case BOARD_EVENT_DEMOD_UNLOCK:
+ break; /* BOARD_EVENT_DEMOD_UNLOCK */
+ case BOARD_EVENT_RECEPTION_MAX_4:
+ break; /* BOARD_EVENT_RECEPTION_MAX_4 */
+ case BOARD_EVENT_RECEPTION_3:
+ break; /* BOARD_EVENT_RECEPTION_3 */
+ case BOARD_EVENT_RECEPTION_2:
+ break; /* BOARD_EVENT_RECEPTION_2 */
+ case BOARD_EVENT_RECEPTION_1:
+ break; /* BOARD_EVENT_RECEPTION_1 */
+ case BOARD_EVENT_RECEPTION_LOST_0:
+ break; /* BOARD_EVENT_RECEPTION_LOST_0 */
+ case BOARD_EVENT_MULTIPLEX_OK:
+ break; /* BOARD_EVENT_MULTIPLEX_OK */
+ case BOARD_EVENT_MULTIPLEX_ERRORS:
+ break; /* BOARD_EVENT_MULTIPLEX_ERRORS */
+
+ default:
+ pr_err("Unknown SMS board event\n");
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_event);
+
+static int sms_set_gpio(struct smscore_device_t *coredev, int pin, int enable)
+{
+ int lvl, ret;
+ u32 gpio;
+ struct smscore_config_gpio gpioconfig = {
+ .direction = SMS_GPIO_DIRECTION_OUTPUT,
+ .pullupdown = SMS_GPIO_PULLUPDOWN_NONE,
+ .inputcharacteristics = SMS_GPIO_INPUTCHARACTERISTICS_NORMAL,
+ .outputslewrate = SMS_GPIO_OUTPUT_SLEW_RATE_FAST,
+ .outputdriving = SMS_GPIO_OUTPUTDRIVING_S_4mA,
+ };
+
+ if (pin == 0)
+ return -EINVAL;
+
+ if (pin < 0) {
+ /* inverted gpio */
+ gpio = pin * -1;
+ lvl = enable ? 0 : 1;
+ } else {
+ gpio = pin;
+ lvl = enable ? 1 : 0;
+ }
+
+ ret = smscore_configure_gpio(coredev, gpio, &gpioconfig);
+ if (ret < 0)
+ return ret;
+
+ return smscore_set_gpio(coredev, gpio, lvl);
+}
+
+int sms_board_setup(struct smscore_device_t *coredev)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ /* turn off all LEDs */
+ sms_set_gpio(coredev, board->led_power, 0);
+ sms_set_gpio(coredev, board->led_hi, 0);
+ sms_set_gpio(coredev, board->led_lo, 0);
+ break;
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ /* turn off LNA */
+ sms_set_gpio(coredev, board->lna_ctrl, 0);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_setup);
+
+int sms_board_power(struct smscore_device_t *coredev, int onoff)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ /* power LED */
+ sms_set_gpio(coredev,
+ board->led_power, onoff ? 1 : 0);
+ break;
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ /* LNA */
+ if (!onoff)
+ sms_set_gpio(coredev, board->lna_ctrl, 0);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_power);
+
+int sms_board_led_feedback(struct smscore_device_t *coredev, int led)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ /* dont touch GPIO if LEDs are already set */
+ if (smscore_led_state(coredev, -1) == led)
+ return 0;
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ sms_set_gpio(coredev,
+ board->led_lo, (led & SMS_LED_LO) ? 1 : 0);
+ sms_set_gpio(coredev,
+ board->led_hi, (led & SMS_LED_HI) ? 1 : 0);
+
+ smscore_led_state(coredev, led);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_led_feedback);
+
+int sms_board_lna_control(struct smscore_device_t *coredev, int onoff)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ pr_debug("%s: LNA %s\n", __func__, onoff ? "enabled" : "disabled");
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ sms_set_gpio(coredev,
+ board->rf_switch, onoff ? 1 : 0);
+ return sms_set_gpio(coredev,
+ board->lna_ctrl, onoff ? 1 : 0);
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(sms_board_lna_control);
+
+int sms_board_load_modules(int id)
+{
+ request_module("smsdvb");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_load_modules);
diff --git a/drivers/media/common/siano/sms-cards.h b/drivers/media/common/siano/sms-cards.h
new file mode 100644
index 000000000..bb3d733f0
--- /dev/null
+++ b/drivers/media/common/siano/sms-cards.h
@@ -0,0 +1,139 @@
+/*
+ * Card-specific functions for the Siano SMS1xxx USB dongle
+ *
+ * Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *
+ * 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 __SMS_CARDS_H__
+#define __SMS_CARDS_H__
+
+#include "smscoreapi.h"
+
+#include <linux/usb.h>
+#include "smsir.h"
+
+#define SMS_BOARD_UNKNOWN 0
+#define SMS1XXX_BOARD_SIANO_STELLAR 1
+#define SMS1XXX_BOARD_SIANO_NOVA_A 2
+#define SMS1XXX_BOARD_SIANO_NOVA_B 3
+#define SMS1XXX_BOARD_SIANO_VEGA 4
+#define SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT 5
+#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6
+#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7
+#define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8
+#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD 9
+#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10
+#define SMS1XXX_BOARD_SIANO_NICE 11
+#define SMS1XXX_BOARD_SIANO_VENICE 12
+#define SMS1XXX_BOARD_SIANO_STELLAR_ROM 13
+#define SMS1XXX_BOARD_ZTE_DVB_DATA_CARD 14
+#define SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD 15
+#define SMS1XXX_BOARD_SIANO_MING 16
+#define SMS1XXX_BOARD_SIANO_PELE 17
+#define SMS1XXX_BOARD_SIANO_RIO 18
+#define SMS1XXX_BOARD_SIANO_DENVER_1530 19
+#define SMS1XXX_BOARD_SIANO_DENVER_2160 20
+#define SMS1XXX_BOARD_PCTV_77E 21
+
+struct sms_board_gpio_cfg {
+ int lna_vhf_exist;
+ int lna_vhf_ctrl;
+ int lna_uhf_exist;
+ int lna_uhf_ctrl;
+ int lna_uhf_d_ctrl;
+ int lna_sband_exist;
+ int lna_sband_ctrl;
+ int lna_sband_d_ctrl;
+ int foreign_lna0_ctrl;
+ int foreign_lna1_ctrl;
+ int foreign_lna2_ctrl;
+ int rf_switch_vhf;
+ int rf_switch_uhf;
+ int rf_switch_sband;
+ int leds_power;
+ int led0;
+ int led1;
+ int led2;
+ int led3;
+ int led4;
+ int ir;
+ int eeprom_wp;
+ int mrc_sense;
+ int mrc_pdn_resetn;
+ int mrc_gp0; /* mrcs spi int */
+ int mrc_gp1;
+ int mrc_gp2;
+ int mrc_gp3;
+ int mrc_gp4;
+ int host_spi_gsp_ts_int;
+};
+
+struct sms_board {
+ enum sms_device_type_st type;
+ char *name, *fw[DEVICE_MODE_MAX];
+ struct sms_board_gpio_cfg board_cfg;
+ char *rc_codes; /* Name of IR codes table */
+
+ /* gpios */
+ int led_power, led_hi, led_lo, lna_ctrl, rf_switch;
+
+ char intf_num;
+ int default_mode;
+ unsigned int mtu;
+ unsigned int crystal;
+ struct sms_antenna_config_ST *antenna_config;
+};
+
+struct sms_board *sms_get_board(unsigned id);
+
+extern struct smscore_device_t *coredev;
+
+enum SMS_BOARD_EVENTS {
+ BOARD_EVENT_POWER_INIT,
+ BOARD_EVENT_POWER_SUSPEND,
+ BOARD_EVENT_POWER_RESUME,
+ BOARD_EVENT_BIND,
+ BOARD_EVENT_SCAN_PROG,
+ BOARD_EVENT_SCAN_COMP,
+ BOARD_EVENT_EMERGENCY_WARNING_SIGNAL,
+ BOARD_EVENT_FE_LOCK,
+ BOARD_EVENT_FE_UNLOCK,
+ BOARD_EVENT_DEMOD_LOCK,
+ BOARD_EVENT_DEMOD_UNLOCK,
+ BOARD_EVENT_RECEPTION_MAX_4,
+ BOARD_EVENT_RECEPTION_3,
+ BOARD_EVENT_RECEPTION_2,
+ BOARD_EVENT_RECEPTION_1,
+ BOARD_EVENT_RECEPTION_LOST_0,
+ BOARD_EVENT_MULTIPLEX_OK,
+ BOARD_EVENT_MULTIPLEX_ERRORS
+};
+
+int sms_board_event(struct smscore_device_t *coredev,
+ enum SMS_BOARD_EVENTS gevent);
+
+int sms_board_setup(struct smscore_device_t *coredev);
+
+#define SMS_LED_OFF 0
+#define SMS_LED_LO 1
+#define SMS_LED_HI 2
+int sms_board_led_feedback(struct smscore_device_t *coredev, int led);
+int sms_board_power(struct smscore_device_t *coredev, int onoff);
+int sms_board_lna_control(struct smscore_device_t *coredev, int onoff);
+
+extern int sms_board_load_modules(int id);
+
+#endif /* __SMS_CARDS_H__ */
diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c
new file mode 100644
index 000000000..30eaa97a5
--- /dev/null
+++ b/drivers/media/common/siano/smscoreapi.c
@@ -0,0 +1,2175 @@
+/*
+ * Siano core API module
+ *
+ * This file contains implementation for the interface to sms core component
+ *
+ * author: Uri Shkolnik
+ *
+ * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *
+ * 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 "smscoreapi.h"
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <linux/firmware.h>
+#include <linux/wait.h>
+#include <asm/byteorder.h>
+
+#include "sms-cards.h"
+#include "smsir.h"
+
+struct smscore_device_notifyee_t {
+ struct list_head entry;
+ hotplug_t hotplug;
+};
+
+struct smscore_idlist_t {
+ struct list_head entry;
+ int id;
+ int data_type;
+};
+
+struct smscore_client_t {
+ struct list_head entry;
+ struct smscore_device_t *coredev;
+ void *context;
+ struct list_head idlist;
+ onresponse_t onresponse_handler;
+ onremove_t onremove_handler;
+};
+
+static char *siano_msgs[] = {
+ [MSG_TYPE_BASE_VAL - MSG_TYPE_BASE_VAL] = "MSG_TYPE_BASE_VAL",
+ [MSG_SMS_GET_VERSION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_REQ",
+ [MSG_SMS_GET_VERSION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_RES",
+ [MSG_SMS_MULTI_BRIDGE_CFG - MSG_TYPE_BASE_VAL] = "MSG_SMS_MULTI_BRIDGE_CFG",
+ [MSG_SMS_GPIO_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_REQ",
+ [MSG_SMS_GPIO_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_RES",
+ [MSG_SMS_GPIO_SET_LEVEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_SET_LEVEL_REQ",
+ [MSG_SMS_GPIO_SET_LEVEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_SET_LEVEL_RES",
+ [MSG_SMS_GPIO_GET_LEVEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_GET_LEVEL_REQ",
+ [MSG_SMS_GPIO_GET_LEVEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_GET_LEVEL_RES",
+ [MSG_SMS_EEPROM_BURN_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_EEPROM_BURN_IND",
+ [MSG_SMS_LOG_ENABLE_CHANGE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_ENABLE_CHANGE_REQ",
+ [MSG_SMS_LOG_ENABLE_CHANGE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_ENABLE_CHANGE_RES",
+ [MSG_SMS_SET_MAX_TX_MSG_LEN_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_MAX_TX_MSG_LEN_REQ",
+ [MSG_SMS_SET_MAX_TX_MSG_LEN_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_MAX_TX_MSG_LEN_RES",
+ [MSG_SMS_SPI_HALFDUPLEX_TOKEN_HOST_TO_DEVICE - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_HALFDUPLEX_TOKEN_HOST_TO_DEVICE",
+ [MSG_SMS_SPI_HALFDUPLEX_TOKEN_DEVICE_TO_HOST - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_HALFDUPLEX_TOKEN_DEVICE_TO_HOST",
+ [MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_REQ",
+ [MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_RES",
+ [MSG_SMS_BACKGROUND_SCAN_SIGNAL_DETECTED_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_SIGNAL_DETECTED_IND",
+ [MSG_SMS_BACKGROUND_SCAN_NO_SIGNAL_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_NO_SIGNAL_IND",
+ [MSG_SMS_CONFIGURE_RF_SWITCH_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIGURE_RF_SWITCH_REQ",
+ [MSG_SMS_CONFIGURE_RF_SWITCH_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIGURE_RF_SWITCH_RES",
+ [MSG_SMS_MRC_PATH_DISCONNECT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_PATH_DISCONNECT_REQ",
+ [MSG_SMS_MRC_PATH_DISCONNECT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_PATH_DISCONNECT_RES",
+ [MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_REQ",
+ [MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_RES",
+ [MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_REQ",
+ [MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_RES",
+ [MSG_WR_REG_RFT_REQ - MSG_TYPE_BASE_VAL] = "MSG_WR_REG_RFT_REQ",
+ [MSG_WR_REG_RFT_RES - MSG_TYPE_BASE_VAL] = "MSG_WR_REG_RFT_RES",
+ [MSG_RD_REG_RFT_REQ - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_RFT_REQ",
+ [MSG_RD_REG_RFT_RES - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_RFT_RES",
+ [MSG_RD_REG_ALL_RFT_REQ - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_ALL_RFT_REQ",
+ [MSG_RD_REG_ALL_RFT_RES - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_ALL_RFT_RES",
+ [MSG_HELP_INT - MSG_TYPE_BASE_VAL] = "MSG_HELP_INT",
+ [MSG_RUN_SCRIPT_INT - MSG_TYPE_BASE_VAL] = "MSG_RUN_SCRIPT_INT",
+ [MSG_SMS_EWS_INBAND_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EWS_INBAND_REQ",
+ [MSG_SMS_EWS_INBAND_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EWS_INBAND_RES",
+ [MSG_SMS_RFS_SELECT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RFS_SELECT_REQ",
+ [MSG_SMS_RFS_SELECT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RFS_SELECT_RES",
+ [MSG_SMS_MB_GET_VER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_GET_VER_REQ",
+ [MSG_SMS_MB_GET_VER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_GET_VER_RES",
+ [MSG_SMS_MB_WRITE_CFGFILE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_WRITE_CFGFILE_REQ",
+ [MSG_SMS_MB_WRITE_CFGFILE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_WRITE_CFGFILE_RES",
+ [MSG_SMS_MB_READ_CFGFILE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_READ_CFGFILE_REQ",
+ [MSG_SMS_MB_READ_CFGFILE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_READ_CFGFILE_RES",
+ [MSG_SMS_RD_MEM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RD_MEM_REQ",
+ [MSG_SMS_RD_MEM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RD_MEM_RES",
+ [MSG_SMS_WR_MEM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_WR_MEM_REQ",
+ [MSG_SMS_WR_MEM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_WR_MEM_RES",
+ [MSG_SMS_UPDATE_MEM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_UPDATE_MEM_REQ",
+ [MSG_SMS_UPDATE_MEM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_UPDATE_MEM_RES",
+ [MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_REQ",
+ [MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_RES",
+ [MSG_SMS_RF_TUNE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RF_TUNE_REQ",
+ [MSG_SMS_RF_TUNE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RF_TUNE_RES",
+ [MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_REQ",
+ [MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_RES",
+ [MSG_SMS_ISDBT_SB_RECEPTION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_SB_RECEPTION_REQ",
+ [MSG_SMS_ISDBT_SB_RECEPTION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_SB_RECEPTION_RES",
+ [MSG_SMS_GENERIC_EPROM_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_WRITE_REQ",
+ [MSG_SMS_GENERIC_EPROM_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_WRITE_RES",
+ [MSG_SMS_GENERIC_EPROM_READ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_READ_REQ",
+ [MSG_SMS_GENERIC_EPROM_READ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_READ_RES",
+ [MSG_SMS_EEPROM_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EEPROM_WRITE_REQ",
+ [MSG_SMS_EEPROM_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EEPROM_WRITE_RES",
+ [MSG_SMS_CUSTOM_READ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_READ_REQ",
+ [MSG_SMS_CUSTOM_READ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_READ_RES",
+ [MSG_SMS_CUSTOM_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_WRITE_REQ",
+ [MSG_SMS_CUSTOM_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_WRITE_RES",
+ [MSG_SMS_INIT_DEVICE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_INIT_DEVICE_REQ",
+ [MSG_SMS_INIT_DEVICE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_INIT_DEVICE_RES",
+ [MSG_SMS_ATSC_SET_ALL_IP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_SET_ALL_IP_REQ",
+ [MSG_SMS_ATSC_SET_ALL_IP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_SET_ALL_IP_RES",
+ [MSG_SMS_ATSC_START_ENSEMBLE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_START_ENSEMBLE_REQ",
+ [MSG_SMS_ATSC_START_ENSEMBLE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_START_ENSEMBLE_RES",
+ [MSG_SMS_SET_OUTPUT_MODE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_OUTPUT_MODE_REQ",
+ [MSG_SMS_SET_OUTPUT_MODE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_OUTPUT_MODE_RES",
+ [MSG_SMS_ATSC_IP_FILTER_GET_LIST_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_GET_LIST_REQ",
+ [MSG_SMS_ATSC_IP_FILTER_GET_LIST_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_GET_LIST_RES",
+ [MSG_SMS_SUB_CHANNEL_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_START_REQ",
+ [MSG_SMS_SUB_CHANNEL_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_START_RES",
+ [MSG_SMS_SUB_CHANNEL_STOP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_STOP_REQ",
+ [MSG_SMS_SUB_CHANNEL_STOP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_STOP_RES",
+ [MSG_SMS_ATSC_IP_FILTER_ADD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_ADD_REQ",
+ [MSG_SMS_ATSC_IP_FILTER_ADD_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_ADD_RES",
+ [MSG_SMS_ATSC_IP_FILTER_REMOVE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_REQ",
+ [MSG_SMS_ATSC_IP_FILTER_REMOVE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_RES",
+ [MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_REQ",
+ [MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_RES",
+ [MSG_SMS_WAIT_CMD - MSG_TYPE_BASE_VAL] = "MSG_SMS_WAIT_CMD",
+ [MSG_SMS_ADD_PID_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ADD_PID_FILTER_REQ",
+ [MSG_SMS_ADD_PID_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ADD_PID_FILTER_RES",
+ [MSG_SMS_REMOVE_PID_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_REMOVE_PID_FILTER_REQ",
+ [MSG_SMS_REMOVE_PID_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_REMOVE_PID_FILTER_RES",
+ [MSG_SMS_FAST_INFORMATION_CHANNEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_FAST_INFORMATION_CHANNEL_REQ",
+ [MSG_SMS_FAST_INFORMATION_CHANNEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_FAST_INFORMATION_CHANNEL_RES",
+ [MSG_SMS_DAB_CHANNEL - MSG_TYPE_BASE_VAL] = "MSG_SMS_DAB_CHANNEL",
+ [MSG_SMS_GET_PID_FILTER_LIST_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_PID_FILTER_LIST_REQ",
+ [MSG_SMS_GET_PID_FILTER_LIST_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_PID_FILTER_LIST_RES",
+ [MSG_SMS_POWER_DOWN_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_REQ",
+ [MSG_SMS_POWER_DOWN_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_RES",
+ [MSG_SMS_ATSC_SLT_EXIST_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_SLT_EXIST_IND",
+ [MSG_SMS_ATSC_NO_SLT_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_NO_SLT_IND",
+ [MSG_SMS_GET_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_REQ",
+ [MSG_SMS_GET_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_RES",
+ [MSG_SMS_SEND_DUMP - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_DUMP",
+ [MSG_SMS_SCAN_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_START_REQ",
+ [MSG_SMS_SCAN_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_START_RES",
+ [MSG_SMS_SCAN_STOP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_STOP_REQ",
+ [MSG_SMS_SCAN_STOP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_STOP_RES",
+ [MSG_SMS_SCAN_PROGRESS_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_PROGRESS_IND",
+ [MSG_SMS_SCAN_COMPLETE_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_COMPLETE_IND",
+ [MSG_SMS_LOG_ITEM - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_ITEM",
+ [MSG_SMS_DAB_SUBCHANNEL_RECONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DAB_SUBCHANNEL_RECONFIG_REQ",
+ [MSG_SMS_DAB_SUBCHANNEL_RECONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DAB_SUBCHANNEL_RECONFIG_RES",
+ [MSG_SMS_HO_PER_SLICES_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PER_SLICES_IND",
+ [MSG_SMS_HO_INBAND_POWER_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_INBAND_POWER_IND",
+ [MSG_SMS_MANUAL_DEMOD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MANUAL_DEMOD_REQ",
+ [MSG_SMS_HO_TUNE_ON_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_ON_REQ",
+ [MSG_SMS_HO_TUNE_ON_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_ON_RES",
+ [MSG_SMS_HO_TUNE_OFF_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_OFF_REQ",
+ [MSG_SMS_HO_TUNE_OFF_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_OFF_RES",
+ [MSG_SMS_HO_PEEK_FREQ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PEEK_FREQ_REQ",
+ [MSG_SMS_HO_PEEK_FREQ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PEEK_FREQ_RES",
+ [MSG_SMS_HO_PEEK_FREQ_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PEEK_FREQ_IND",
+ [MSG_SMS_MB_ATTEN_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_ATTEN_SET_REQ",
+ [MSG_SMS_MB_ATTEN_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_ATTEN_SET_RES",
+ [MSG_SMS_ENABLE_STAT_IN_I2C_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_STAT_IN_I2C_REQ",
+ [MSG_SMS_ENABLE_STAT_IN_I2C_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_STAT_IN_I2C_RES",
+ [MSG_SMS_SET_ANTENNA_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_ANTENNA_CONFIG_REQ",
+ [MSG_SMS_SET_ANTENNA_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_ANTENNA_CONFIG_RES",
+ [MSG_SMS_GET_STATISTICS_EX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_EX_REQ",
+ [MSG_SMS_GET_STATISTICS_EX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_EX_RES",
+ [MSG_SMS_SLEEP_RESUME_COMP_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SLEEP_RESUME_COMP_IND",
+ [MSG_SMS_SWITCH_HOST_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWITCH_HOST_INTERFACE_REQ",
+ [MSG_SMS_SWITCH_HOST_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWITCH_HOST_INTERFACE_RES",
+ [MSG_SMS_DATA_DOWNLOAD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_DOWNLOAD_REQ",
+ [MSG_SMS_DATA_DOWNLOAD_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_DOWNLOAD_RES",
+ [MSG_SMS_DATA_VALIDITY_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_VALIDITY_REQ",
+ [MSG_SMS_DATA_VALIDITY_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_VALIDITY_RES",
+ [MSG_SMS_SWDOWNLOAD_TRIGGER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_TRIGGER_REQ",
+ [MSG_SMS_SWDOWNLOAD_TRIGGER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_TRIGGER_RES",
+ [MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ",
+ [MSG_SMS_SWDOWNLOAD_BACKDOOR_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_BACKDOOR_RES",
+ [MSG_SMS_GET_VERSION_EX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_EX_REQ",
+ [MSG_SMS_GET_VERSION_EX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_EX_RES",
+ [MSG_SMS_CLOCK_OUTPUT_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CLOCK_OUTPUT_CONFIG_REQ",
+ [MSG_SMS_CLOCK_OUTPUT_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CLOCK_OUTPUT_CONFIG_RES",
+ [MSG_SMS_I2C_SET_FREQ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_I2C_SET_FREQ_REQ",
+ [MSG_SMS_I2C_SET_FREQ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_I2C_SET_FREQ_RES",
+ [MSG_SMS_GENERIC_I2C_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_I2C_REQ",
+ [MSG_SMS_GENERIC_I2C_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_I2C_RES",
+ [MSG_SMS_DVBT_BDA_DATA - MSG_TYPE_BASE_VAL] = "MSG_SMS_DVBT_BDA_DATA",
+ [MSG_SW_RELOAD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_REQ",
+ [MSG_SMS_DATA_MSG - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_MSG",
+ [MSG_TABLE_UPLOAD_REQ - MSG_TYPE_BASE_VAL] = "MSG_TABLE_UPLOAD_REQ",
+ [MSG_TABLE_UPLOAD_RES - MSG_TYPE_BASE_VAL] = "MSG_TABLE_UPLOAD_RES",
+ [MSG_SW_RELOAD_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_START_REQ",
+ [MSG_SW_RELOAD_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_START_RES",
+ [MSG_SW_RELOAD_EXEC_REQ - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_EXEC_REQ",
+ [MSG_SW_RELOAD_EXEC_RES - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_EXEC_RES",
+ [MSG_SMS_SPI_INT_LINE_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_INT_LINE_SET_REQ",
+ [MSG_SMS_SPI_INT_LINE_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_INT_LINE_SET_RES",
+ [MSG_SMS_GPIO_CONFIG_EX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_EX_REQ",
+ [MSG_SMS_GPIO_CONFIG_EX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_EX_RES",
+ [MSG_SMS_WATCHDOG_ACT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_WATCHDOG_ACT_REQ",
+ [MSG_SMS_WATCHDOG_ACT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_WATCHDOG_ACT_RES",
+ [MSG_SMS_LOOPBACK_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOOPBACK_REQ",
+ [MSG_SMS_LOOPBACK_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOOPBACK_RES",
+ [MSG_SMS_RAW_CAPTURE_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_START_REQ",
+ [MSG_SMS_RAW_CAPTURE_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_START_RES",
+ [MSG_SMS_RAW_CAPTURE_ABORT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_ABORT_REQ",
+ [MSG_SMS_RAW_CAPTURE_ABORT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_ABORT_RES",
+ [MSG_SMS_RAW_CAPTURE_COMPLETE_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_COMPLETE_IND",
+ [MSG_SMS_DATA_PUMP_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_PUMP_IND",
+ [MSG_SMS_DATA_PUMP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_PUMP_REQ",
+ [MSG_SMS_DATA_PUMP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_PUMP_RES",
+ [MSG_SMS_FLASH_DL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_FLASH_DL_REQ",
+ [MSG_SMS_EXEC_TEST_1_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXEC_TEST_1_REQ",
+ [MSG_SMS_EXEC_TEST_1_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXEC_TEST_1_RES",
+ [MSG_SMS_ENBALE_TS_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENBALE_TS_INTERFACE_REQ",
+ [MSG_SMS_ENBALE_TS_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENBALE_TS_INTERFACE_RES",
+ [MSG_SMS_SPI_SET_BUS_WIDTH_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_SET_BUS_WIDTH_REQ",
+ [MSG_SMS_SPI_SET_BUS_WIDTH_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_SET_BUS_WIDTH_RES",
+ [MSG_SMS_SEND_EMM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_EMM_REQ",
+ [MSG_SMS_SEND_EMM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_EMM_RES",
+ [MSG_SMS_DISABLE_TS_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DISABLE_TS_INTERFACE_REQ",
+ [MSG_SMS_DISABLE_TS_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DISABLE_TS_INTERFACE_RES",
+ [MSG_SMS_IS_BUF_FREE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_IS_BUF_FREE_REQ",
+ [MSG_SMS_IS_BUF_FREE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_IS_BUF_FREE_RES",
+ [MSG_SMS_EXT_ANTENNA_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXT_ANTENNA_REQ",
+ [MSG_SMS_EXT_ANTENNA_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXT_ANTENNA_RES",
+ [MSG_SMS_CMMB_GET_NET_OF_FREQ_REQ_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NET_OF_FREQ_REQ_OBSOLETE",
+ [MSG_SMS_CMMB_GET_NET_OF_FREQ_RES_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NET_OF_FREQ_RES_OBSOLETE",
+ [MSG_SMS_BATTERY_LEVEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_BATTERY_LEVEL_REQ",
+ [MSG_SMS_BATTERY_LEVEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_BATTERY_LEVEL_RES",
+ [MSG_SMS_CMMB_INJECT_TABLE_REQ_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_INJECT_TABLE_REQ_OBSOLETE",
+ [MSG_SMS_CMMB_INJECT_TABLE_RES_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_INJECT_TABLE_RES_OBSOLETE",
+ [MSG_SMS_FM_RADIO_BLOCK_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_FM_RADIO_BLOCK_IND",
+ [MSG_SMS_HOST_NOTIFICATION_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HOST_NOTIFICATION_IND",
+ [MSG_SMS_CMMB_GET_CONTROL_TABLE_REQ_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_CONTROL_TABLE_REQ_OBSOLETE",
+ [MSG_SMS_CMMB_GET_CONTROL_TABLE_RES_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_CONTROL_TABLE_RES_OBSOLETE",
+ [MSG_SMS_CMMB_GET_NETWORKS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NETWORKS_REQ",
+ [MSG_SMS_CMMB_GET_NETWORKS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NETWORKS_RES",
+ [MSG_SMS_CMMB_START_SERVICE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_SERVICE_REQ",
+ [MSG_SMS_CMMB_START_SERVICE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_SERVICE_RES",
+ [MSG_SMS_CMMB_STOP_SERVICE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_SERVICE_REQ",
+ [MSG_SMS_CMMB_STOP_SERVICE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_SERVICE_RES",
+ [MSG_SMS_CMMB_ADD_CHANNEL_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_ADD_CHANNEL_FILTER_REQ",
+ [MSG_SMS_CMMB_ADD_CHANNEL_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_ADD_CHANNEL_FILTER_RES",
+ [MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_REQ",
+ [MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_RES",
+ [MSG_SMS_CMMB_START_CONTROL_INFO_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_CONTROL_INFO_REQ",
+ [MSG_SMS_CMMB_START_CONTROL_INFO_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_CONTROL_INFO_RES",
+ [MSG_SMS_CMMB_STOP_CONTROL_INFO_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_CONTROL_INFO_REQ",
+ [MSG_SMS_CMMB_STOP_CONTROL_INFO_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_CONTROL_INFO_RES",
+ [MSG_SMS_ISDBT_TUNE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_TUNE_REQ",
+ [MSG_SMS_ISDBT_TUNE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_TUNE_RES",
+ [MSG_SMS_TRANSMISSION_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_TRANSMISSION_IND",
+ [MSG_SMS_PID_STATISTICS_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_PID_STATISTICS_IND",
+ [MSG_SMS_POWER_DOWN_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_IND",
+ [MSG_SMS_POWER_DOWN_CONF - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_CONF",
+ [MSG_SMS_POWER_UP_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_UP_IND",
+ [MSG_SMS_POWER_UP_CONF - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_UP_CONF",
+ [MSG_SMS_POWER_MODE_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_MODE_SET_REQ",
+ [MSG_SMS_POWER_MODE_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_MODE_SET_RES",
+ [MSG_SMS_DEBUG_HOST_EVENT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DEBUG_HOST_EVENT_REQ",
+ [MSG_SMS_DEBUG_HOST_EVENT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DEBUG_HOST_EVENT_RES",
+ [MSG_SMS_NEW_CRYSTAL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NEW_CRYSTAL_REQ",
+ [MSG_SMS_NEW_CRYSTAL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NEW_CRYSTAL_RES",
+ [MSG_SMS_CONFIG_SPI_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIG_SPI_REQ",
+ [MSG_SMS_CONFIG_SPI_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIG_SPI_RES",
+ [MSG_SMS_I2C_SHORT_STAT_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_I2C_SHORT_STAT_IND",
+ [MSG_SMS_START_IR_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_START_IR_REQ",
+ [MSG_SMS_START_IR_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_START_IR_RES",
+ [MSG_SMS_IR_SAMPLES_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_IR_SAMPLES_IND",
+ [MSG_SMS_CMMB_CA_SERVICE_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_CA_SERVICE_IND",
+ [MSG_SMS_SLAVE_DEVICE_DETECTED - MSG_TYPE_BASE_VAL] = "MSG_SMS_SLAVE_DEVICE_DETECTED",
+ [MSG_SMS_INTERFACE_LOCK_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_INTERFACE_LOCK_IND",
+ [MSG_SMS_INTERFACE_UNLOCK_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_INTERFACE_UNLOCK_IND",
+ [MSG_SMS_SEND_ROSUM_BUFF_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_ROSUM_BUFF_REQ",
+ [MSG_SMS_SEND_ROSUM_BUFF_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_ROSUM_BUFF_RES",
+ [MSG_SMS_ROSUM_BUFF - MSG_TYPE_BASE_VAL] = "MSG_SMS_ROSUM_BUFF",
+ [MSG_SMS_SET_AES128_KEY_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_AES128_KEY_REQ",
+ [MSG_SMS_SET_AES128_KEY_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_AES128_KEY_RES",
+ [MSG_SMS_MBBMS_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MBBMS_WRITE_REQ",
+ [MSG_SMS_MBBMS_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MBBMS_WRITE_RES",
+ [MSG_SMS_MBBMS_READ_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_MBBMS_READ_IND",
+ [MSG_SMS_IQ_STREAM_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_START_REQ",
+ [MSG_SMS_IQ_STREAM_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_START_RES",
+ [MSG_SMS_IQ_STREAM_STOP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_STOP_REQ",
+ [MSG_SMS_IQ_STREAM_STOP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_STOP_RES",
+ [MSG_SMS_IQ_STREAM_DATA_BLOCK - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_DATA_BLOCK",
+ [MSG_SMS_GET_EEPROM_VERSION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_EEPROM_VERSION_REQ",
+ [MSG_SMS_GET_EEPROM_VERSION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_EEPROM_VERSION_RES",
+ [MSG_SMS_SIGNAL_DETECTED_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SIGNAL_DETECTED_IND",
+ [MSG_SMS_NO_SIGNAL_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_NO_SIGNAL_IND",
+ [MSG_SMS_MRC_SHUTDOWN_SLAVE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_SHUTDOWN_SLAVE_REQ",
+ [MSG_SMS_MRC_SHUTDOWN_SLAVE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_SHUTDOWN_SLAVE_RES",
+ [MSG_SMS_MRC_BRINGUP_SLAVE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_BRINGUP_SLAVE_REQ",
+ [MSG_SMS_MRC_BRINGUP_SLAVE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_BRINGUP_SLAVE_RES",
+ [MSG_SMS_EXTERNAL_LNA_CTRL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXTERNAL_LNA_CTRL_REQ",
+ [MSG_SMS_EXTERNAL_LNA_CTRL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXTERNAL_LNA_CTRL_RES",
+ [MSG_SMS_SET_PERIODIC_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_PERIODIC_STATISTICS_REQ",
+ [MSG_SMS_SET_PERIODIC_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_PERIODIC_STATISTICS_RES",
+ [MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_REQ",
+ [MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_RES",
+ [LOCAL_TUNE - MSG_TYPE_BASE_VAL] = "LOCAL_TUNE",
+ [LOCAL_IFFT_H_ICI - MSG_TYPE_BASE_VAL] = "LOCAL_IFFT_H_ICI",
+ [MSG_RESYNC_REQ - MSG_TYPE_BASE_VAL] = "MSG_RESYNC_REQ",
+ [MSG_SMS_CMMB_GET_MRC_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_MRC_STATISTICS_REQ",
+ [MSG_SMS_CMMB_GET_MRC_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_MRC_STATISTICS_RES",
+ [MSG_SMS_LOG_EX_ITEM - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_EX_ITEM",
+ [MSG_SMS_DEVICE_DATA_LOSS_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_DEVICE_DATA_LOSS_IND",
+ [MSG_SMS_MRC_WATCHDOG_TRIGGERED_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_WATCHDOG_TRIGGERED_IND",
+ [MSG_SMS_USER_MSG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_USER_MSG_REQ",
+ [MSG_SMS_USER_MSG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_USER_MSG_RES",
+ [MSG_SMS_SMART_CARD_INIT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_INIT_REQ",
+ [MSG_SMS_SMART_CARD_INIT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_INIT_RES",
+ [MSG_SMS_SMART_CARD_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_WRITE_REQ",
+ [MSG_SMS_SMART_CARD_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_WRITE_RES",
+ [MSG_SMS_SMART_CARD_READ_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_READ_IND",
+ [MSG_SMS_TSE_ENABLE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_TSE_ENABLE_REQ",
+ [MSG_SMS_TSE_ENABLE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_TSE_ENABLE_RES",
+ [MSG_SMS_CMMB_GET_SHORT_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_SHORT_STATISTICS_REQ",
+ [MSG_SMS_CMMB_GET_SHORT_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_SHORT_STATISTICS_RES",
+ [MSG_SMS_LED_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_LED_CONFIG_REQ",
+ [MSG_SMS_LED_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_LED_CONFIG_RES",
+ [MSG_PWM_ANTENNA_REQ - MSG_TYPE_BASE_VAL] = "MSG_PWM_ANTENNA_REQ",
+ [MSG_PWM_ANTENNA_RES - MSG_TYPE_BASE_VAL] = "MSG_PWM_ANTENNA_RES",
+ [MSG_SMS_CMMB_SMD_SN_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SMD_SN_REQ",
+ [MSG_SMS_CMMB_SMD_SN_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SMD_SN_RES",
+ [MSG_SMS_CMMB_SET_CA_CW_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_CW_REQ",
+ [MSG_SMS_CMMB_SET_CA_CW_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_CW_RES",
+ [MSG_SMS_CMMB_SET_CA_SALT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_SALT_REQ",
+ [MSG_SMS_CMMB_SET_CA_SALT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_SALT_RES",
+ [MSG_SMS_NSCD_INIT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_INIT_REQ",
+ [MSG_SMS_NSCD_INIT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_INIT_RES",
+ [MSG_SMS_NSCD_PROCESS_SECTION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_PROCESS_SECTION_REQ",
+ [MSG_SMS_NSCD_PROCESS_SECTION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_PROCESS_SECTION_RES",
+ [MSG_SMS_DBD_CREATE_OBJECT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CREATE_OBJECT_REQ",
+ [MSG_SMS_DBD_CREATE_OBJECT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CREATE_OBJECT_RES",
+ [MSG_SMS_DBD_CONFIGURE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CONFIGURE_REQ",
+ [MSG_SMS_DBD_CONFIGURE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CONFIGURE_RES",
+ [MSG_SMS_DBD_SET_KEYS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_SET_KEYS_REQ",
+ [MSG_SMS_DBD_SET_KEYS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_SET_KEYS_RES",
+ [MSG_SMS_DBD_PROCESS_HEADER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_HEADER_REQ",
+ [MSG_SMS_DBD_PROCESS_HEADER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_HEADER_RES",
+ [MSG_SMS_DBD_PROCESS_DATA_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_DATA_REQ",
+ [MSG_SMS_DBD_PROCESS_DATA_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_DATA_RES",
+ [MSG_SMS_DBD_PROCESS_GET_DATA_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_GET_DATA_REQ",
+ [MSG_SMS_DBD_PROCESS_GET_DATA_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_GET_DATA_RES",
+ [MSG_SMS_NSCD_OPEN_SESSION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_OPEN_SESSION_REQ",
+ [MSG_SMS_NSCD_OPEN_SESSION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_OPEN_SESSION_RES",
+ [MSG_SMS_SEND_HOST_DATA_TO_DEMUX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_HOST_DATA_TO_DEMUX_REQ",
+ [MSG_SMS_SEND_HOST_DATA_TO_DEMUX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_HOST_DATA_TO_DEMUX_RES",
+ [MSG_LAST_MSG_TYPE - MSG_TYPE_BASE_VAL] = "MSG_LAST_MSG_TYPE",
+};
+
+char *smscore_translate_msg(enum msg_types msgtype)
+{
+ int i = msgtype - MSG_TYPE_BASE_VAL;
+ char *msg;
+
+ if (i < 0 || i >= ARRAY_SIZE(siano_msgs))
+ return "Unknown msg type";
+
+ msg = siano_msgs[i];
+
+ if (!*msg)
+ return "Unknown msg type";
+
+ return msg;
+}
+EXPORT_SYMBOL_GPL(smscore_translate_msg);
+
+void smscore_set_board_id(struct smscore_device_t *core, int id)
+{
+ core->board_id = id;
+}
+
+int smscore_led_state(struct smscore_device_t *core, int led)
+{
+ if (led >= 0)
+ core->led_state = led;
+ return core->led_state;
+}
+EXPORT_SYMBOL_GPL(smscore_set_board_id);
+
+int smscore_get_board_id(struct smscore_device_t *core)
+{
+ return core->board_id;
+}
+EXPORT_SYMBOL_GPL(smscore_get_board_id);
+
+struct smscore_registry_entry_t {
+ struct list_head entry;
+ char devpath[32];
+ int mode;
+ enum sms_device_type_st type;
+};
+
+static struct list_head g_smscore_notifyees;
+static struct list_head g_smscore_devices;
+static struct mutex g_smscore_deviceslock;
+
+static struct list_head g_smscore_registry;
+static struct mutex g_smscore_registrylock;
+
+static int default_mode = DEVICE_MODE_NONE;
+
+module_param(default_mode, int, 0644);
+MODULE_PARM_DESC(default_mode, "default firmware id (device mode)");
+
+static struct smscore_registry_entry_t *smscore_find_registry(char *devpath)
+{
+ struct smscore_registry_entry_t *entry;
+ struct list_head *next;
+
+ kmutex_lock(&g_smscore_registrylock);
+ for (next = g_smscore_registry.next;
+ next != &g_smscore_registry;
+ next = next->next) {
+ entry = (struct smscore_registry_entry_t *) next;
+ if (!strcmp(entry->devpath, devpath)) {
+ kmutex_unlock(&g_smscore_registrylock);
+ return entry;
+ }
+ }
+ entry = kmalloc(sizeof(struct smscore_registry_entry_t), GFP_KERNEL);
+ if (entry) {
+ entry->mode = default_mode;
+ strcpy(entry->devpath, devpath);
+ list_add(&entry->entry, &g_smscore_registry);
+ } else
+ pr_err("failed to create smscore_registry.\n");
+ kmutex_unlock(&g_smscore_registrylock);
+ return entry;
+}
+
+int smscore_registry_getmode(char *devpath)
+{
+ struct smscore_registry_entry_t *entry;
+
+ entry = smscore_find_registry(devpath);
+ if (entry)
+ return entry->mode;
+ else
+ pr_err("No registry found.\n");
+
+ return default_mode;
+}
+EXPORT_SYMBOL_GPL(smscore_registry_getmode);
+
+static enum sms_device_type_st smscore_registry_gettype(char *devpath)
+{
+ struct smscore_registry_entry_t *entry;
+
+ entry = smscore_find_registry(devpath);
+ if (entry)
+ return entry->type;
+ else
+ pr_err("No registry found.\n");
+
+ return -EINVAL;
+}
+
+static void smscore_registry_setmode(char *devpath, int mode)
+{
+ struct smscore_registry_entry_t *entry;
+
+ entry = smscore_find_registry(devpath);
+ if (entry)
+ entry->mode = mode;
+ else
+ pr_err("No registry found.\n");
+}
+
+static void smscore_registry_settype(char *devpath,
+ enum sms_device_type_st type)
+{
+ struct smscore_registry_entry_t *entry;
+
+ entry = smscore_find_registry(devpath);
+ if (entry)
+ entry->type = type;
+ else
+ pr_err("No registry found.\n");
+}
+
+
+static void list_add_locked(struct list_head *new, struct list_head *head,
+ spinlock_t *lock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(lock, flags);
+
+ list_add(new, head);
+
+ spin_unlock_irqrestore(lock, flags);
+}
+
+/**
+ * register a client callback that called when device plugged in/unplugged
+ * NOTE: if devices exist callback is called immediately for each device
+ *
+ * @param hotplug callback
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_register_hotplug(hotplug_t hotplug)
+{
+ struct smscore_device_notifyee_t *notifyee;
+ struct list_head *next, *first;
+ int rc = 0;
+
+ kmutex_lock(&g_smscore_deviceslock);
+
+ notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t),
+ GFP_KERNEL);
+ if (notifyee) {
+ /* now notify callback about existing devices */
+ first = &g_smscore_devices;
+ for (next = first->next;
+ next != first && !rc;
+ next = next->next) {
+ struct smscore_device_t *coredev =
+ (struct smscore_device_t *) next;
+ rc = hotplug(coredev, coredev->device, 1);
+ }
+
+ if (rc >= 0) {
+ notifyee->hotplug = hotplug;
+ list_add(&notifyee->entry, &g_smscore_notifyees);
+ } else
+ kfree(notifyee);
+ } else
+ rc = -ENOMEM;
+
+ kmutex_unlock(&g_smscore_deviceslock);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(smscore_register_hotplug);
+
+/**
+ * unregister a client callback that called when device plugged in/unplugged
+ *
+ * @param hotplug callback
+ *
+ */
+void smscore_unregister_hotplug(hotplug_t hotplug)
+{
+ struct list_head *next, *first;
+
+ kmutex_lock(&g_smscore_deviceslock);
+
+ first = &g_smscore_notifyees;
+
+ for (next = first->next; next != first;) {
+ struct smscore_device_notifyee_t *notifyee =
+ (struct smscore_device_notifyee_t *) next;
+ next = next->next;
+
+ if (notifyee->hotplug == hotplug) {
+ list_del(&notifyee->entry);
+ kfree(notifyee);
+ }
+ }
+
+ kmutex_unlock(&g_smscore_deviceslock);
+}
+EXPORT_SYMBOL_GPL(smscore_unregister_hotplug);
+
+static void smscore_notify_clients(struct smscore_device_t *coredev)
+{
+ struct smscore_client_t *client;
+
+ /* the client must call smscore_unregister_client from remove handler */
+ while (!list_empty(&coredev->clients)) {
+ client = (struct smscore_client_t *) coredev->clients.next;
+ client->onremove_handler(client->context);
+ }
+}
+
+static int smscore_notify_callbacks(struct smscore_device_t *coredev,
+ struct device *device, int arrival)
+{
+ struct smscore_device_notifyee_t *elem;
+ int rc = 0;
+
+ /* note: must be called under g_deviceslock */
+
+ list_for_each_entry(elem, &g_smscore_notifyees, entry) {
+ rc = elem->hotplug(coredev, device, arrival);
+ if (rc < 0)
+ break;
+ }
+
+ return rc;
+}
+
+static struct
+smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer,
+ dma_addr_t common_buffer_phys)
+{
+ struct smscore_buffer_t *cb;
+
+ cb = kzalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL);
+ if (!cb)
+ return NULL;
+
+ cb->p = buffer;
+ cb->offset_in_common = buffer - (u8 *) common_buffer;
+ cb->phys = common_buffer_phys + cb->offset_in_common;
+
+ return cb;
+}
+
+/**
+ * creates coredev object for a device, prepares buffers,
+ * creates buffer mappings, notifies registered hotplugs about new device.
+ *
+ * @param params device pointer to struct with device specific parameters
+ * and handlers
+ * @param coredev pointer to a value that receives created coredev object
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_register_device(struct smsdevice_params_t *params,
+ struct smscore_device_t **coredev,
+ void *mdev)
+{
+ struct smscore_device_t *dev;
+ u8 *buffer;
+
+ dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ dev->media_dev = mdev;
+#endif
+
+ /* init list entry so it could be safe in smscore_unregister_device */
+ INIT_LIST_HEAD(&dev->entry);
+
+ /* init queues */
+ INIT_LIST_HEAD(&dev->clients);
+ INIT_LIST_HEAD(&dev->buffers);
+
+ /* init locks */
+ spin_lock_init(&dev->clientslock);
+ spin_lock_init(&dev->bufferslock);
+
+ /* init completion events */
+ init_completion(&dev->version_ex_done);
+ init_completion(&dev->data_download_done);
+ init_completion(&dev->data_validity_done);
+ init_completion(&dev->trigger_done);
+ init_completion(&dev->init_device_done);
+ init_completion(&dev->reload_start_done);
+ init_completion(&dev->resume_done);
+ init_completion(&dev->gpio_configuration_done);
+ init_completion(&dev->gpio_set_level_done);
+ init_completion(&dev->gpio_get_level_done);
+ init_completion(&dev->ir_init_done);
+
+ /* Buffer management */
+ init_waitqueue_head(&dev->buffer_mng_waitq);
+
+ /* alloc common buffer */
+ dev->common_buffer_size = params->buffer_size * params->num_buffers;
+ dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size,
+ &dev->common_buffer_phys,
+ GFP_KERNEL | GFP_DMA);
+ if (!dev->common_buffer) {
+ smscore_unregister_device(dev);
+ return -ENOMEM;
+ }
+
+ /* prepare dma buffers */
+ for (buffer = dev->common_buffer;
+ dev->num_buffers < params->num_buffers;
+ dev->num_buffers++, buffer += params->buffer_size) {
+ struct smscore_buffer_t *cb;
+
+ cb = smscore_createbuffer(buffer, dev->common_buffer,
+ dev->common_buffer_phys);
+ if (!cb) {
+ smscore_unregister_device(dev);
+ return -ENOMEM;
+ }
+
+ smscore_putbuffer(dev, cb);
+ }
+
+ pr_debug("allocated %d buffers\n", dev->num_buffers);
+
+ dev->mode = DEVICE_MODE_NONE;
+ dev->board_id = SMS_BOARD_UNKNOWN;
+ dev->context = params->context;
+ dev->device = params->device;
+ dev->setmode_handler = params->setmode_handler;
+ dev->detectmode_handler = params->detectmode_handler;
+ dev->sendrequest_handler = params->sendrequest_handler;
+ dev->preload_handler = params->preload_handler;
+ dev->postload_handler = params->postload_handler;
+
+ dev->device_flags = params->flags;
+ strcpy(dev->devpath, params->devpath);
+
+ smscore_registry_settype(dev->devpath, params->device_type);
+
+ /* add device to devices list */
+ kmutex_lock(&g_smscore_deviceslock);
+ list_add(&dev->entry, &g_smscore_devices);
+ kmutex_unlock(&g_smscore_deviceslock);
+
+ *coredev = dev;
+
+ pr_debug("device %p created\n", dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(smscore_register_device);
+
+
+static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev,
+ void *buffer, size_t size, struct completion *completion) {
+ int rc;
+
+ if (completion == NULL)
+ return -EINVAL;
+ init_completion(completion);
+
+ rc = coredev->sendrequest_handler(coredev->context, buffer, size);
+ if (rc < 0) {
+ pr_info("sendrequest returned error %d\n", rc);
+ return rc;
+ }
+
+ return wait_for_completion_timeout(completion,
+ msecs_to_jiffies(SMS_PROTOCOL_MAX_RAOUNDTRIP_MS)) ?
+ 0 : -ETIME;
+}
+
+/**
+ * Starts & enables IR operations
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int smscore_init_ir(struct smscore_device_t *coredev)
+{
+ int ir_io;
+ int rc;
+ void *buffer;
+
+ coredev->ir.dev = NULL;
+ ir_io = sms_get_board(smscore_get_board_id(coredev))->board_cfg.ir;
+ if (ir_io) {/* only if IR port exist we use IR sub-module */
+ pr_debug("IR loading\n");
+ rc = sms_ir_init(coredev);
+
+ if (rc != 0)
+ pr_err("Error initialization DTV IR sub-module\n");
+ else {
+ buffer = kmalloc(sizeof(struct sms_msg_data2) +
+ SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | GFP_DMA);
+ if (buffer) {
+ struct sms_msg_data2 *msg =
+ (struct sms_msg_data2 *)
+ SMS_ALIGN_ADDRESS(buffer);
+
+ SMS_INIT_MSG(&msg->x_msg_header,
+ MSG_SMS_START_IR_REQ,
+ sizeof(struct sms_msg_data2));
+ msg->msg_data[0] = coredev->ir.controller;
+ msg->msg_data[1] = coredev->ir.timeout;
+
+ rc = smscore_sendrequest_and_wait(coredev, msg,
+ msg->x_msg_header. msg_length,
+ &coredev->ir_init_done);
+
+ kfree(buffer);
+ } else
+ pr_err("Sending IR initialization message failed\n");
+ }
+ } else
+ pr_info("IR port has not been detected\n");
+
+ return 0;
+}
+
+/**
+ * configures device features according to board configuration structure.
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int smscore_configure_board(struct smscore_device_t *coredev)
+{
+ struct sms_board *board;
+
+ board = sms_get_board(coredev->board_id);
+ if (!board) {
+ pr_err("no board configuration exist.\n");
+ return -EINVAL;
+ }
+
+ if (board->mtu) {
+ struct sms_msg_data mtu_msg;
+ pr_debug("set max transmit unit %d\n", board->mtu);
+
+ mtu_msg.x_msg_header.msg_src_id = 0;
+ mtu_msg.x_msg_header.msg_dst_id = HIF_TASK;
+ mtu_msg.x_msg_header.msg_flags = 0;
+ mtu_msg.x_msg_header.msg_type = MSG_SMS_SET_MAX_TX_MSG_LEN_REQ;
+ mtu_msg.x_msg_header.msg_length = sizeof(mtu_msg);
+ mtu_msg.msg_data[0] = board->mtu;
+
+ coredev->sendrequest_handler(coredev->context, &mtu_msg,
+ sizeof(mtu_msg));
+ }
+
+ if (board->crystal) {
+ struct sms_msg_data crys_msg;
+ pr_debug("set crystal value %d\n", board->crystal);
+
+ SMS_INIT_MSG(&crys_msg.x_msg_header,
+ MSG_SMS_NEW_CRYSTAL_REQ,
+ sizeof(crys_msg));
+ crys_msg.msg_data[0] = board->crystal;
+
+ coredev->sendrequest_handler(coredev->context, &crys_msg,
+ sizeof(crys_msg));
+ }
+
+ return 0;
+}
+
+/**
+ * sets initial device mode and notifies client hotplugs that device is ready
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_start_device(struct smscore_device_t *coredev)
+{
+ int rc;
+ int board_id = smscore_get_board_id(coredev);
+ int mode = smscore_registry_getmode(coredev->devpath);
+
+ /* Device is initialized as DEVICE_MODE_NONE */
+ if (board_id != SMS_BOARD_UNKNOWN && mode == DEVICE_MODE_NONE)
+ mode = sms_get_board(board_id)->default_mode;
+
+ rc = smscore_set_device_mode(coredev, mode);
+ if (rc < 0) {
+ pr_info("set device mode failed , rc %d\n", rc);
+ return rc;
+ }
+ rc = smscore_configure_board(coredev);
+ if (rc < 0) {
+ pr_info("configure board failed , rc %d\n", rc);
+ return rc;
+ }
+
+ kmutex_lock(&g_smscore_deviceslock);
+
+ rc = smscore_notify_callbacks(coredev, coredev->device, 1);
+ smscore_init_ir(coredev);
+
+ pr_debug("device %p started, rc %d\n", coredev, rc);
+
+ kmutex_unlock(&g_smscore_deviceslock);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(smscore_start_device);
+
+
+static int smscore_load_firmware_family2(struct smscore_device_t *coredev,
+ void *buffer, size_t size)
+{
+ struct sms_firmware *firmware = (struct sms_firmware *) buffer;
+ struct sms_msg_data4 *msg;
+ u32 mem_address, calc_checksum = 0;
+ u32 i, *ptr;
+ u8 *payload = firmware->payload;
+ int rc = 0;
+ firmware->start_address = le32_to_cpup((__le32 *)&firmware->start_address);
+ firmware->length = le32_to_cpup((__le32 *)&firmware->length);
+
+ mem_address = firmware->start_address;
+
+ pr_debug("loading FW to addr 0x%x size %d\n",
+ mem_address, firmware->length);
+ if (coredev->preload_handler) {
+ rc = coredev->preload_handler(coredev->context);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* PAGE_SIZE buffer shall be enough and dma aligned */
+ msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!msg)
+ return -ENOMEM;
+
+ if (coredev->mode != DEVICE_MODE_NONE) {
+ pr_debug("sending reload command.\n");
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SW_RELOAD_START_REQ,
+ sizeof(struct sms_msg_hdr));
+ rc = smscore_sendrequest_and_wait(coredev, msg,
+ msg->x_msg_header.msg_length,
+ &coredev->reload_start_done);
+ if (rc < 0) {
+ pr_err("device reload failed, rc %d\n", rc);
+ goto exit_fw_download;
+ }
+ mem_address = *(u32 *) &payload[20];
+ }
+
+ for (i = 0, ptr = (u32 *)firmware->payload; i < firmware->length/4 ;
+ i++, ptr++)
+ calc_checksum += *ptr;
+
+ while (size && rc >= 0) {
+ struct sms_data_download *data_msg =
+ (struct sms_data_download *) msg;
+ int payload_size = min_t(int, size, SMS_MAX_PAYLOAD_SIZE);
+
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_DATA_DOWNLOAD_REQ,
+ (u16)(sizeof(struct sms_msg_hdr) +
+ sizeof(u32) + payload_size));
+
+ data_msg->mem_addr = mem_address;
+ memcpy(data_msg->payload, payload, payload_size);
+
+ rc = smscore_sendrequest_and_wait(coredev, data_msg,
+ data_msg->x_msg_header.msg_length,
+ &coredev->data_download_done);
+
+ payload += payload_size;
+ size -= payload_size;
+ mem_address += payload_size;
+ }
+
+ if (rc < 0)
+ goto exit_fw_download;
+
+ pr_debug("sending MSG_SMS_DATA_VALIDITY_REQ expecting 0x%x\n",
+ calc_checksum);
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_DATA_VALIDITY_REQ,
+ sizeof(msg->x_msg_header) +
+ sizeof(u32) * 3);
+ msg->msg_data[0] = firmware->start_address;
+ /* Entry point */
+ msg->msg_data[1] = firmware->length;
+ msg->msg_data[2] = 0; /* Regular checksum*/
+ rc = smscore_sendrequest_and_wait(coredev, msg,
+ msg->x_msg_header.msg_length,
+ &coredev->data_validity_done);
+ if (rc < 0)
+ goto exit_fw_download;
+
+ if (coredev->mode == DEVICE_MODE_NONE) {
+ struct sms_msg_data *trigger_msg =
+ (struct sms_msg_data *) msg;
+
+ pr_debug("sending MSG_SMS_SWDOWNLOAD_TRIGGER_REQ\n");
+ SMS_INIT_MSG(&msg->x_msg_header,
+ MSG_SMS_SWDOWNLOAD_TRIGGER_REQ,
+ sizeof(struct sms_msg_hdr) +
+ sizeof(u32) * 5);
+
+ trigger_msg->msg_data[0] = firmware->start_address;
+ /* Entry point */
+ trigger_msg->msg_data[1] = 6; /* Priority */
+ trigger_msg->msg_data[2] = 0x200; /* Stack size */
+ trigger_msg->msg_data[3] = 0; /* Parameter */
+ trigger_msg->msg_data[4] = 4; /* Task ID */
+
+ rc = smscore_sendrequest_and_wait(coredev, trigger_msg,
+ trigger_msg->x_msg_header.msg_length,
+ &coredev->trigger_done);
+ } else {
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SW_RELOAD_EXEC_REQ,
+ sizeof(struct sms_msg_hdr));
+ rc = coredev->sendrequest_handler(coredev->context, msg,
+ msg->x_msg_header.msg_length);
+ }
+
+ if (rc < 0)
+ goto exit_fw_download;
+
+ /*
+ * backward compatibility - wait to device_ready_done for
+ * not more than 400 ms
+ */
+ msleep(400);
+
+exit_fw_download:
+ kfree(msg);
+
+ if (coredev->postload_handler) {
+ pr_debug("rc=%d, postload=0x%p\n",
+ rc, coredev->postload_handler);
+ if (rc >= 0)
+ return coredev->postload_handler(coredev->context);
+ }
+
+ pr_debug("rc=%d\n", rc);
+ return rc;
+}
+
+static char *smscore_fw_lkup[][DEVICE_MODE_MAX] = {
+ [SMS_NOVA_A0] = {
+ [DEVICE_MODE_DVBT] = SMS_FW_DVB_NOVA_12MHZ,
+ [DEVICE_MODE_DVBH] = SMS_FW_DVB_NOVA_12MHZ,
+ [DEVICE_MODE_DAB_TDMB] = SMS_FW_TDMB_NOVA_12MHZ,
+ [DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ,
+ [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_NOVA_12MHZ,
+ [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_NOVA_12MHZ,
+ },
+ [SMS_NOVA_B0] = {
+ [DEVICE_MODE_DVBT] = SMS_FW_DVB_NOVA_12MHZ_B0,
+ [DEVICE_MODE_DVBH] = SMS_FW_DVB_NOVA_12MHZ_B0,
+ [DEVICE_MODE_DAB_TDMB] = SMS_FW_TDMB_NOVA_12MHZ_B0,
+ [DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ_B0,
+ [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_NOVA_12MHZ_B0,
+ [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_NOVA_12MHZ_B0,
+ [DEVICE_MODE_FM_RADIO] = SMS_FW_FM_RADIO,
+ [DEVICE_MODE_FM_RADIO_BDA] = SMS_FW_FM_RADIO,
+ },
+ [SMS_VEGA] = {
+ [DEVICE_MODE_CMMB] = SMS_FW_CMMB_VEGA_12MHZ,
+ },
+ [SMS_VENICE] = {
+ [DEVICE_MODE_CMMB] = SMS_FW_CMMB_VENICE_12MHZ,
+ },
+ [SMS_MING] = {
+ [DEVICE_MODE_CMMB] = SMS_FW_CMMB_MING_APP,
+ },
+ [SMS_PELE] = {
+ [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_PELE,
+ [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_PELE,
+ },
+ [SMS_RIO] = {
+ [DEVICE_MODE_DVBT] = SMS_FW_DVB_RIO,
+ [DEVICE_MODE_DVBH] = SMS_FW_DVBH_RIO,
+ [DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_RIO,
+ [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_RIO,
+ [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_RIO,
+ [DEVICE_MODE_FM_RADIO] = SMS_FW_FM_RADIO_RIO,
+ [DEVICE_MODE_FM_RADIO_BDA] = SMS_FW_FM_RADIO_RIO,
+ },
+ [SMS_DENVER_1530] = {
+ [DEVICE_MODE_ATSC] = SMS_FW_ATSC_DENVER,
+ },
+ [SMS_DENVER_2160] = {
+ [DEVICE_MODE_DAB_TDMB] = SMS_FW_TDMB_DENVER,
+ },
+};
+
+/**
+ * get firmware file name from one of the two mechanisms : sms_boards or
+ * smscore_fw_lkup.
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param mode requested mode of operation
+ * @param lookup if 1, always get the fw filename from smscore_fw_lkup
+ * table. if 0, try first to get from sms_boards
+ *
+ * @return 0 on success, <0 on error.
+ */
+static char *smscore_get_fw_filename(struct smscore_device_t *coredev,
+ int mode)
+{
+ char **fw;
+ int board_id = smscore_get_board_id(coredev);
+ enum sms_device_type_st type;
+
+ type = smscore_registry_gettype(coredev->devpath);
+
+ /* Prevent looking outside the smscore_fw_lkup table */
+ if (type <= SMS_UNKNOWN_TYPE || type >= SMS_NUM_OF_DEVICE_TYPES)
+ return NULL;
+ if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX)
+ return NULL;
+
+ pr_debug("trying to get fw name from sms_boards board_id %d mode %d\n",
+ board_id, mode);
+ fw = sms_get_board(board_id)->fw;
+ if (!fw || !fw[mode]) {
+ pr_debug("cannot find fw name in sms_boards, getting from lookup table mode %d type %d\n",
+ mode, type);
+ return smscore_fw_lkup[type][mode];
+ }
+
+ return fw[mode];
+}
+
+/**
+ * loads specified firmware into a buffer and calls device loadfirmware_handler
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param filename null-terminated string specifies firmware file name
+ * @param loadfirmware_handler device handler that loads firmware
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int smscore_load_firmware_from_file(struct smscore_device_t *coredev,
+ int mode,
+ loadfirmware_t loadfirmware_handler)
+{
+ int rc = -ENOENT;
+ u8 *fw_buf;
+ u32 fw_buf_size;
+ const struct firmware *fw;
+
+ char *fw_filename = smscore_get_fw_filename(coredev, mode);
+ if (!fw_filename) {
+ pr_err("mode %d not supported on this device\n", mode);
+ return -ENOENT;
+ }
+ pr_debug("Firmware name: %s\n", fw_filename);
+
+ if (loadfirmware_handler == NULL && !(coredev->device_flags
+ & SMS_DEVICE_FAMILY2))
+ return -EINVAL;
+
+ rc = reject_firmware(&fw, fw_filename, coredev->device);
+ if (rc < 0) {
+ pr_err("failed to open firmware file '%s'\n", fw_filename);
+ return rc;
+ }
+ pr_debug("read fw %s, buffer size=0x%zx\n", fw_filename, fw->size);
+ fw_buf = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT),
+ GFP_KERNEL | GFP_DMA);
+ if (!fw_buf) {
+ pr_err("failed to allocate firmware buffer\n");
+ rc = -ENOMEM;
+ } else {
+ memcpy(fw_buf, fw->data, fw->size);
+ fw_buf_size = fw->size;
+
+ rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ?
+ smscore_load_firmware_family2(coredev, fw_buf, fw_buf_size)
+ : loadfirmware_handler(coredev->context, fw_buf,
+ fw_buf_size);
+ }
+
+ kfree(fw_buf);
+ release_firmware(fw);
+
+ return rc;
+}
+
+/**
+ * notifies all clients registered with the device, notifies hotplugs,
+ * frees all buffers and coredev object
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * @return 0 on success, <0 on error.
+ */
+void smscore_unregister_device(struct smscore_device_t *coredev)
+{
+ struct smscore_buffer_t *cb;
+ int num_buffers = 0;
+ int retry = 0;
+
+ kmutex_lock(&g_smscore_deviceslock);
+
+ /* Release input device (IR) resources */
+ sms_ir_exit(coredev);
+
+ smscore_notify_clients(coredev);
+ smscore_notify_callbacks(coredev, NULL, 0);
+
+ /* at this point all buffers should be back
+ * onresponse must no longer be called */
+
+ while (1) {
+ while (!list_empty(&coredev->buffers)) {
+ cb = (struct smscore_buffer_t *) coredev->buffers.next;
+ list_del(&cb->entry);
+ kfree(cb);
+ num_buffers++;
+ }
+ if (num_buffers == coredev->num_buffers)
+ break;
+ if (++retry > 10) {
+ pr_info("exiting although not all buffers released.\n");
+ break;
+ }
+
+ pr_debug("waiting for %d buffer(s)\n",
+ coredev->num_buffers - num_buffers);
+ kmutex_unlock(&g_smscore_deviceslock);
+ msleep(100);
+ kmutex_lock(&g_smscore_deviceslock);
+ }
+
+ pr_debug("freed %d buffers\n", num_buffers);
+
+ if (coredev->common_buffer)
+ dma_free_coherent(NULL, coredev->common_buffer_size,
+ coredev->common_buffer, coredev->common_buffer_phys);
+
+ kfree(coredev->fw_buf);
+
+ list_del(&coredev->entry);
+ kfree(coredev);
+
+ kmutex_unlock(&g_smscore_deviceslock);
+
+ pr_debug("device %p destroyed\n", coredev);
+}
+EXPORT_SYMBOL_GPL(smscore_unregister_device);
+
+static int smscore_detect_mode(struct smscore_device_t *coredev)
+{
+ void *buffer = kmalloc(sizeof(struct sms_msg_hdr) + SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | GFP_DMA);
+ struct sms_msg_hdr *msg =
+ (struct sms_msg_hdr *) SMS_ALIGN_ADDRESS(buffer);
+ int rc;
+
+ if (!buffer)
+ return -ENOMEM;
+
+ SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ,
+ sizeof(struct sms_msg_hdr));
+
+ rc = smscore_sendrequest_and_wait(coredev, msg, msg->msg_length,
+ &coredev->version_ex_done);
+ if (rc == -ETIME) {
+ pr_err("MSG_SMS_GET_VERSION_EX_REQ failed first try\n");
+
+ if (wait_for_completion_timeout(&coredev->resume_done,
+ msecs_to_jiffies(5000))) {
+ rc = smscore_sendrequest_and_wait(
+ coredev, msg, msg->msg_length,
+ &coredev->version_ex_done);
+ if (rc < 0)
+ pr_err("MSG_SMS_GET_VERSION_EX_REQ failed second try, rc %d\n",
+ rc);
+ } else
+ rc = -ETIME;
+ }
+
+ kfree(buffer);
+
+ return rc;
+}
+
+/**
+ * send init device request and wait for response
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param mode requested mode of operation
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int smscore_init_device(struct smscore_device_t *coredev, int mode)
+{
+ void *buffer;
+ struct sms_msg_data *msg;
+ int rc = 0;
+
+ buffer = kmalloc(sizeof(struct sms_msg_data) +
+ SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ pr_err("Could not allocate buffer for init device message.\n");
+ return -ENOMEM;
+ }
+
+ msg = (struct sms_msg_data *)SMS_ALIGN_ADDRESS(buffer);
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ,
+ sizeof(struct sms_msg_data));
+ msg->msg_data[0] = mode;
+
+ rc = smscore_sendrequest_and_wait(coredev, msg,
+ msg->x_msg_header. msg_length,
+ &coredev->init_device_done);
+
+ kfree(buffer);
+ return rc;
+}
+
+/**
+ * calls device handler to change mode of operation
+ * NOTE: stellar/usb may disconnect when changing mode
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param mode requested mode of operation
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_set_device_mode(struct smscore_device_t *coredev, int mode)
+{
+ int rc = 0;
+
+ pr_debug("set device mode to %d\n", mode);
+ if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
+ if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX) {
+ pr_err("invalid mode specified %d\n", mode);
+ return -EINVAL;
+ }
+
+ smscore_registry_setmode(coredev->devpath, mode);
+
+ if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) {
+ rc = smscore_detect_mode(coredev);
+ if (rc < 0) {
+ pr_err("mode detect failed %d\n", rc);
+ return rc;
+ }
+ }
+
+ if (coredev->mode == mode) {
+ pr_debug("device mode %d already set\n", mode);
+ return 0;
+ }
+
+ if (!(coredev->modes_supported & (1 << mode))) {
+ rc = smscore_load_firmware_from_file(coredev,
+ mode, NULL);
+ if (rc >= 0)
+ pr_debug("firmware download success\n");
+ } else {
+ pr_debug("mode %d is already supported by running firmware\n",
+ mode);
+ }
+ if (coredev->fw_version >= 0x800) {
+ rc = smscore_init_device(coredev, mode);
+ if (rc < 0)
+ pr_err("device init failed, rc %d.\n", rc);
+ }
+ } else {
+ if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX) {
+ pr_err("invalid mode specified %d\n", mode);
+ return -EINVAL;
+ }
+
+ smscore_registry_setmode(coredev->devpath, mode);
+
+ if (coredev->detectmode_handler)
+ coredev->detectmode_handler(coredev->context,
+ &coredev->mode);
+
+ if (coredev->mode != mode && coredev->setmode_handler)
+ rc = coredev->setmode_handler(coredev->context, mode);
+ }
+
+ if (rc >= 0) {
+ char *buffer;
+ coredev->mode = mode;
+ coredev->device_flags &= ~SMS_DEVICE_NOT_READY;
+
+ buffer = kmalloc(sizeof(struct sms_msg_data) +
+ SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
+ if (buffer) {
+ struct sms_msg_data *msg = (struct sms_msg_data *) SMS_ALIGN_ADDRESS(buffer);
+
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ,
+ sizeof(struct sms_msg_data));
+ msg->msg_data[0] = mode;
+
+ rc = smscore_sendrequest_and_wait(
+ coredev, msg, msg->x_msg_header.msg_length,
+ &coredev->init_device_done);
+
+ kfree(buffer);
+ }
+ }
+
+ if (rc < 0)
+ pr_err("return error code %d.\n", rc);
+ else
+ pr_debug("Success setting device mode.\n");
+
+ return rc;
+}
+
+/**
+ * calls device handler to get current mode of operation
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * @return current mode
+ */
+int smscore_get_device_mode(struct smscore_device_t *coredev)
+{
+ return coredev->mode;
+}
+EXPORT_SYMBOL_GPL(smscore_get_device_mode);
+
+/**
+ * find client by response id & type within the clients list.
+ * return client handle or NULL.
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param data_type client data type (SMS_DONT_CARE for all types)
+ * @param id client id (SMS_DONT_CARE for all id)
+ *
+ */
+static struct
+smscore_client_t *smscore_find_client(struct smscore_device_t *coredev,
+ int data_type, int id)
+{
+ struct list_head *first;
+ struct smscore_client_t *client;
+ unsigned long flags;
+ struct list_head *firstid;
+ struct smscore_idlist_t *client_id;
+
+ spin_lock_irqsave(&coredev->clientslock, flags);
+ first = &coredev->clients;
+ list_for_each_entry(client, first, entry) {
+ firstid = &client->idlist;
+ list_for_each_entry(client_id, firstid, entry) {
+ if ((client_id->id == id) &&
+ (client_id->data_type == data_type ||
+ (client_id->data_type == 0)))
+ goto found;
+ }
+ }
+ client = NULL;
+found:
+ spin_unlock_irqrestore(&coredev->clientslock, flags);
+ return client;
+}
+
+/**
+ * find client by response id/type, call clients onresponse handler
+ * return buffer to pool on error
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param cb pointer to response buffer descriptor
+ *
+ */
+void smscore_onresponse(struct smscore_device_t *coredev,
+ struct smscore_buffer_t *cb) {
+ struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) ((u8 *) cb->p
+ + cb->offset);
+ struct smscore_client_t *client;
+ int rc = -EBUSY;
+ static unsigned long last_sample_time; /* = 0; */
+ static int data_total; /* = 0; */
+ unsigned long time_now = jiffies_to_msecs(jiffies);
+
+ if (!last_sample_time)
+ last_sample_time = time_now;
+
+ if (time_now - last_sample_time > 10000) {
+ pr_debug("data rate %d bytes/secs\n",
+ (int)((data_total * 1000) /
+ (time_now - last_sample_time)));
+
+ last_sample_time = time_now;
+ data_total = 0;
+ }
+
+ data_total += cb->size;
+ /* Do we need to re-route? */
+ if ((phdr->msg_type == MSG_SMS_HO_PER_SLICES_IND) ||
+ (phdr->msg_type == MSG_SMS_TRANSMISSION_IND)) {
+ if (coredev->mode == DEVICE_MODE_DVBT_BDA)
+ phdr->msg_dst_id = DVBT_BDA_CONTROL_MSG_ID;
+ }
+
+
+ client = smscore_find_client(coredev, phdr->msg_type, phdr->msg_dst_id);
+
+ /* If no client registered for type & id,
+ * check for control client where type is not registered */
+ if (client)
+ rc = client->onresponse_handler(client->context, cb);
+
+ if (rc < 0) {
+ switch (phdr->msg_type) {
+ case MSG_SMS_ISDBT_TUNE_RES:
+ break;
+ case MSG_SMS_RF_TUNE_RES:
+ break;
+ case MSG_SMS_SIGNAL_DETECTED_IND:
+ break;
+ case MSG_SMS_NO_SIGNAL_IND:
+ break;
+ case MSG_SMS_SPI_INT_LINE_SET_RES:
+ break;
+ case MSG_SMS_INTERFACE_LOCK_IND:
+ break;
+ case MSG_SMS_INTERFACE_UNLOCK_IND:
+ break;
+ case MSG_SMS_GET_VERSION_EX_RES:
+ {
+ struct sms_version_res *ver =
+ (struct sms_version_res *) phdr;
+ pr_debug("Firmware id %d prots 0x%x ver %d.%d\n",
+ ver->firmware_id, ver->supported_protocols,
+ ver->rom_ver_major, ver->rom_ver_minor);
+
+ coredev->mode = ver->firmware_id == 255 ?
+ DEVICE_MODE_NONE : ver->firmware_id;
+ coredev->modes_supported = ver->supported_protocols;
+ coredev->fw_version = ver->rom_ver_major << 8 |
+ ver->rom_ver_minor;
+
+ complete(&coredev->version_ex_done);
+ break;
+ }
+ case MSG_SMS_INIT_DEVICE_RES:
+ complete(&coredev->init_device_done);
+ break;
+ case MSG_SW_RELOAD_START_RES:
+ complete(&coredev->reload_start_done);
+ break;
+ case MSG_SMS_DATA_VALIDITY_RES:
+ {
+ struct sms_msg_data *validity = (struct sms_msg_data *) phdr;
+
+ pr_debug("MSG_SMS_DATA_VALIDITY_RES, checksum = 0x%x\n",
+ validity->msg_data[0]);
+ complete(&coredev->data_validity_done);
+ break;
+ }
+ case MSG_SMS_DATA_DOWNLOAD_RES:
+ complete(&coredev->data_download_done);
+ break;
+ case MSG_SW_RELOAD_EXEC_RES:
+ break;
+ case MSG_SMS_SWDOWNLOAD_TRIGGER_RES:
+ complete(&coredev->trigger_done);
+ break;
+ case MSG_SMS_SLEEP_RESUME_COMP_IND:
+ complete(&coredev->resume_done);
+ break;
+ case MSG_SMS_GPIO_CONFIG_EX_RES:
+ complete(&coredev->gpio_configuration_done);
+ break;
+ case MSG_SMS_GPIO_SET_LEVEL_RES:
+ complete(&coredev->gpio_set_level_done);
+ break;
+ case MSG_SMS_GPIO_GET_LEVEL_RES:
+ {
+ u32 *msgdata = (u32 *) phdr;
+ coredev->gpio_get_res = msgdata[1];
+ pr_debug("gpio level %d\n",
+ coredev->gpio_get_res);
+ complete(&coredev->gpio_get_level_done);
+ break;
+ }
+ case MSG_SMS_START_IR_RES:
+ complete(&coredev->ir_init_done);
+ break;
+ case MSG_SMS_IR_SAMPLES_IND:
+ sms_ir_event(coredev,
+ (const char *)
+ ((char *)phdr
+ + sizeof(struct sms_msg_hdr)),
+ (int)phdr->msg_length
+ - sizeof(struct sms_msg_hdr));
+ break;
+
+ case MSG_SMS_DVBT_BDA_DATA:
+ /*
+ * It can be received here, if the frontend is
+ * tuned into a valid channel and the proper firmware
+ * is loaded. That happens when the module got removed
+ * and re-inserted, without powering the device off
+ */
+ break;
+
+ default:
+ pr_debug("message %s(%d) not handled.\n",
+ smscore_translate_msg(phdr->msg_type),
+ phdr->msg_type);
+ break;
+ }
+ smscore_putbuffer(coredev, cb);
+ }
+}
+EXPORT_SYMBOL_GPL(smscore_onresponse);
+
+/**
+ * return pointer to next free buffer descriptor from core pool
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * @return pointer to descriptor on success, NULL on error.
+ */
+
+static struct smscore_buffer_t *get_entry(struct smscore_device_t *coredev)
+{
+ struct smscore_buffer_t *cb = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&coredev->bufferslock, flags);
+ if (!list_empty(&coredev->buffers)) {
+ cb = (struct smscore_buffer_t *) coredev->buffers.next;
+ list_del(&cb->entry);
+ }
+ spin_unlock_irqrestore(&coredev->bufferslock, flags);
+ return cb;
+}
+
+struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev)
+{
+ struct smscore_buffer_t *cb = NULL;
+
+ wait_event(coredev->buffer_mng_waitq, (cb = get_entry(coredev)));
+
+ return cb;
+}
+EXPORT_SYMBOL_GPL(smscore_getbuffer);
+
+/**
+ * return buffer descriptor to a pool
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param cb pointer buffer descriptor
+ *
+ */
+void smscore_putbuffer(struct smscore_device_t *coredev,
+ struct smscore_buffer_t *cb) {
+ wake_up_interruptible(&coredev->buffer_mng_waitq);
+ list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock);
+}
+EXPORT_SYMBOL_GPL(smscore_putbuffer);
+
+static int smscore_validate_client(struct smscore_device_t *coredev,
+ struct smscore_client_t *client,
+ int data_type, int id)
+{
+ struct smscore_idlist_t *listentry;
+ struct smscore_client_t *registered_client;
+
+ if (!client) {
+ pr_err("bad parameter.\n");
+ return -EINVAL;
+ }
+ registered_client = smscore_find_client(coredev, data_type, id);
+ if (registered_client == client)
+ return 0;
+
+ if (registered_client) {
+ pr_err("The msg ID already registered to another client.\n");
+ return -EEXIST;
+ }
+ listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL);
+ if (!listentry) {
+ pr_err("Can't allocate memory for client id.\n");
+ return -ENOMEM;
+ }
+ listentry->id = id;
+ listentry->data_type = data_type;
+ list_add_locked(&listentry->entry, &client->idlist,
+ &coredev->clientslock);
+ return 0;
+}
+
+/**
+ * creates smsclient object, check that id is taken by another client
+ *
+ * @param coredev pointer to a coredev object from clients hotplug
+ * @param initial_id all messages with this id would be sent to this client
+ * @param data_type all messages of this type would be sent to this client
+ * @param onresponse_handler client handler that is called to
+ * process incoming messages
+ * @param onremove_handler client handler that is called when device is removed
+ * @param context client-specific context
+ * @param client pointer to a value that receives created smsclient object
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_register_client(struct smscore_device_t *coredev,
+ struct smsclient_params_t *params,
+ struct smscore_client_t **client)
+{
+ struct smscore_client_t *newclient;
+ /* check that no other channel with same parameters exists */
+ if (smscore_find_client(coredev, params->data_type,
+ params->initial_id)) {
+ pr_err("Client already exist.\n");
+ return -EEXIST;
+ }
+
+ newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL);
+ if (!newclient) {
+ pr_err("Failed to allocate memory for client.\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&newclient->idlist);
+ newclient->coredev = coredev;
+ newclient->onresponse_handler = params->onresponse_handler;
+ newclient->onremove_handler = params->onremove_handler;
+ newclient->context = params->context;
+ list_add_locked(&newclient->entry, &coredev->clients,
+ &coredev->clientslock);
+ smscore_validate_client(coredev, newclient, params->data_type,
+ params->initial_id);
+ *client = newclient;
+ pr_debug("%p %d %d\n", params->context, params->data_type,
+ params->initial_id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(smscore_register_client);
+
+/**
+ * frees smsclient object and all subclients associated with it
+ *
+ * @param client pointer to smsclient object returned by
+ * smscore_register_client
+ *
+ */
+void smscore_unregister_client(struct smscore_client_t *client)
+{
+ struct smscore_device_t *coredev = client->coredev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&coredev->clientslock, flags);
+
+
+ while (!list_empty(&client->idlist)) {
+ struct smscore_idlist_t *identry =
+ (struct smscore_idlist_t *) client->idlist.next;
+ list_del(&identry->entry);
+ kfree(identry);
+ }
+
+ pr_debug("%p\n", client->context);
+
+ list_del(&client->entry);
+ kfree(client);
+
+ spin_unlock_irqrestore(&coredev->clientslock, flags);
+}
+EXPORT_SYMBOL_GPL(smscore_unregister_client);
+
+/**
+ * verifies that source id is not taken by another client,
+ * calls device handler to send requests to the device
+ *
+ * @param client pointer to smsclient object returned by
+ * smscore_register_client
+ * @param buffer pointer to a request buffer
+ * @param size size (in bytes) of request buffer
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smsclient_sendrequest(struct smscore_client_t *client,
+ void *buffer, size_t size)
+{
+ struct smscore_device_t *coredev;
+ struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer;
+ int rc;
+
+ if (client == NULL) {
+ pr_err("Got NULL client\n");
+ return -EINVAL;
+ }
+
+ coredev = client->coredev;
+
+ /* check that no other channel with same id exists */
+ if (coredev == NULL) {
+ pr_err("Got NULL coredev\n");
+ return -EINVAL;
+ }
+
+ rc = smscore_validate_client(client->coredev, client, 0,
+ phdr->msg_src_id);
+ if (rc < 0)
+ return rc;
+
+ return coredev->sendrequest_handler(coredev->context, buffer, size);
+}
+EXPORT_SYMBOL_GPL(smsclient_sendrequest);
+
+
+/* old GPIO managements implementation */
+int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
+ struct smscore_config_gpio *pinconfig)
+{
+ struct {
+ struct sms_msg_hdr hdr;
+ u32 data[6];
+ } msg;
+
+ if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
+ msg.hdr.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.hdr.msg_dst_id = HIF_TASK;
+ msg.hdr.msg_flags = 0;
+ msg.hdr.msg_type = MSG_SMS_GPIO_CONFIG_EX_REQ;
+ msg.hdr.msg_length = sizeof(msg);
+
+ msg.data[0] = pin;
+ msg.data[1] = pinconfig->pullupdown;
+
+ /* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */
+ msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0;
+
+ switch (pinconfig->outputdriving) {
+ case SMS_GPIO_OUTPUTDRIVING_S_16mA:
+ msg.data[3] = 7; /* Nova - 16mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_S_12mA:
+ msg.data[3] = 5; /* Nova - 11mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_S_8mA:
+ msg.data[3] = 3; /* Nova - 7mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_S_4mA:
+ default:
+ msg.data[3] = 2; /* Nova - 4mA */
+ break;
+ }
+
+ msg.data[4] = pinconfig->direction;
+ msg.data[5] = 0;
+ } else /* TODO: SMS_DEVICE_FAMILY1 */
+ return -EINVAL;
+
+ return coredev->sendrequest_handler(coredev->context,
+ &msg, sizeof(msg));
+}
+
+int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level)
+{
+ struct {
+ struct sms_msg_hdr hdr;
+ u32 data[3];
+ } msg;
+
+ if (pin > MAX_GPIO_PIN_NUMBER)
+ return -EINVAL;
+
+ msg.hdr.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.hdr.msg_dst_id = HIF_TASK;
+ msg.hdr.msg_flags = 0;
+ msg.hdr.msg_type = MSG_SMS_GPIO_SET_LEVEL_REQ;
+ msg.hdr.msg_length = sizeof(msg);
+
+ msg.data[0] = pin;
+ msg.data[1] = level ? 1 : 0;
+ msg.data[2] = 0;
+
+ return coredev->sendrequest_handler(coredev->context,
+ &msg, sizeof(msg));
+}
+
+/* new GPIO management implementation */
+static int get_gpio_pin_params(u32 pin_num, u32 *p_translatedpin_num,
+ u32 *p_group_num, u32 *p_group_cfg) {
+
+ *p_group_cfg = 1;
+
+ if (pin_num <= 1) {
+ *p_translatedpin_num = 0;
+ *p_group_num = 9;
+ *p_group_cfg = 2;
+ } else if (pin_num >= 2 && pin_num <= 6) {
+ *p_translatedpin_num = 2;
+ *p_group_num = 0;
+ *p_group_cfg = 2;
+ } else if (pin_num >= 7 && pin_num <= 11) {
+ *p_translatedpin_num = 7;
+ *p_group_num = 1;
+ } else if (pin_num >= 12 && pin_num <= 15) {
+ *p_translatedpin_num = 12;
+ *p_group_num = 2;
+ *p_group_cfg = 3;
+ } else if (pin_num == 16) {
+ *p_translatedpin_num = 16;
+ *p_group_num = 23;
+ } else if (pin_num >= 17 && pin_num <= 24) {
+ *p_translatedpin_num = 17;
+ *p_group_num = 3;
+ } else if (pin_num == 25) {
+ *p_translatedpin_num = 25;
+ *p_group_num = 6;
+ } else if (pin_num >= 26 && pin_num <= 28) {
+ *p_translatedpin_num = 26;
+ *p_group_num = 4;
+ } else if (pin_num == 29) {
+ *p_translatedpin_num = 29;
+ *p_group_num = 5;
+ *p_group_cfg = 2;
+ } else if (pin_num == 30) {
+ *p_translatedpin_num = 30;
+ *p_group_num = 8;
+ } else if (pin_num == 31) {
+ *p_translatedpin_num = 31;
+ *p_group_num = 17;
+ } else
+ return -1;
+
+ *p_group_cfg <<= 24;
+
+ return 0;
+}
+
+int smscore_gpio_configure(struct smscore_device_t *coredev, u8 pin_num,
+ struct smscore_config_gpio *p_gpio_config) {
+
+ u32 total_len;
+ u32 translatedpin_num = 0;
+ u32 group_num = 0;
+ u32 electric_char;
+ u32 group_cfg;
+ void *buffer;
+ int rc;
+
+ struct set_gpio_msg {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[6];
+ } *p_msg;
+
+
+ if (pin_num > MAX_GPIO_PIN_NUMBER)
+ return -EINVAL;
+
+ if (p_gpio_config == NULL)
+ return -EINVAL;
+
+ total_len = sizeof(struct sms_msg_hdr) + (sizeof(u32) * 6);
+
+ buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | GFP_DMA);
+ if (!buffer)
+ return -ENOMEM;
+
+ p_msg = (struct set_gpio_msg *) SMS_ALIGN_ADDRESS(buffer);
+
+ p_msg->x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ p_msg->x_msg_header.msg_dst_id = HIF_TASK;
+ p_msg->x_msg_header.msg_flags = 0;
+ p_msg->x_msg_header.msg_length = (u16) total_len;
+ p_msg->msg_data[0] = pin_num;
+
+ if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) {
+ p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_CONFIG_REQ;
+ if (get_gpio_pin_params(pin_num, &translatedpin_num, &group_num,
+ &group_cfg) != 0) {
+ rc = -EINVAL;
+ goto free;
+ }
+
+ p_msg->msg_data[1] = translatedpin_num;
+ p_msg->msg_data[2] = group_num;
+ electric_char = (p_gpio_config->pullupdown)
+ | (p_gpio_config->inputcharacteristics << 2)
+ | (p_gpio_config->outputslewrate << 3)
+ | (p_gpio_config->outputdriving << 4);
+ p_msg->msg_data[3] = electric_char;
+ p_msg->msg_data[4] = p_gpio_config->direction;
+ p_msg->msg_data[5] = group_cfg;
+ } else {
+ p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_CONFIG_EX_REQ;
+ p_msg->msg_data[1] = p_gpio_config->pullupdown;
+ p_msg->msg_data[2] = p_gpio_config->outputslewrate;
+ p_msg->msg_data[3] = p_gpio_config->outputdriving;
+ p_msg->msg_data[4] = p_gpio_config->direction;
+ p_msg->msg_data[5] = 0;
+ }
+
+ rc = smscore_sendrequest_and_wait(coredev, p_msg, total_len,
+ &coredev->gpio_configuration_done);
+
+ if (rc != 0) {
+ if (rc == -ETIME)
+ pr_err("smscore_gpio_configure timeout\n");
+ else
+ pr_err("smscore_gpio_configure error\n");
+ }
+free:
+ kfree(buffer);
+
+ return rc;
+}
+
+int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 pin_num,
+ u8 new_level) {
+
+ u32 total_len;
+ int rc;
+ void *buffer;
+
+ struct set_gpio_msg {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[3]; /* keep it 3 ! */
+ } *p_msg;
+
+ if ((new_level > 1) || (pin_num > MAX_GPIO_PIN_NUMBER))
+ return -EINVAL;
+
+ total_len = sizeof(struct sms_msg_hdr) +
+ (3 * sizeof(u32)); /* keep it 3 ! */
+
+ buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | GFP_DMA);
+ if (!buffer)
+ return -ENOMEM;
+
+ p_msg = (struct set_gpio_msg *) SMS_ALIGN_ADDRESS(buffer);
+
+ p_msg->x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ p_msg->x_msg_header.msg_dst_id = HIF_TASK;
+ p_msg->x_msg_header.msg_flags = 0;
+ p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_SET_LEVEL_REQ;
+ p_msg->x_msg_header.msg_length = (u16) total_len;
+ p_msg->msg_data[0] = pin_num;
+ p_msg->msg_data[1] = new_level;
+
+ /* Send message to SMS */
+ rc = smscore_sendrequest_and_wait(coredev, p_msg, total_len,
+ &coredev->gpio_set_level_done);
+
+ if (rc != 0) {
+ if (rc == -ETIME)
+ pr_err("smscore_gpio_set_level timeout\n");
+ else
+ pr_err("smscore_gpio_set_level error\n");
+ }
+ kfree(buffer);
+
+ return rc;
+}
+
+int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num,
+ u8 *level) {
+
+ u32 total_len;
+ int rc;
+ void *buffer;
+
+ struct set_gpio_msg {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[2];
+ } *p_msg;
+
+
+ if (pin_num > MAX_GPIO_PIN_NUMBER)
+ return -EINVAL;
+
+ total_len = sizeof(struct sms_msg_hdr) + (2 * sizeof(u32));
+
+ buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | GFP_DMA);
+ if (!buffer)
+ return -ENOMEM;
+
+ p_msg = (struct set_gpio_msg *) SMS_ALIGN_ADDRESS(buffer);
+
+ p_msg->x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ p_msg->x_msg_header.msg_dst_id = HIF_TASK;
+ p_msg->x_msg_header.msg_flags = 0;
+ p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_GET_LEVEL_REQ;
+ p_msg->x_msg_header.msg_length = (u16) total_len;
+ p_msg->msg_data[0] = pin_num;
+ p_msg->msg_data[1] = 0;
+
+ /* Send message to SMS */
+ rc = smscore_sendrequest_and_wait(coredev, p_msg, total_len,
+ &coredev->gpio_get_level_done);
+
+ if (rc != 0) {
+ if (rc == -ETIME)
+ pr_err("smscore_gpio_get_level timeout\n");
+ else
+ pr_err("smscore_gpio_get_level error\n");
+ }
+ kfree(buffer);
+
+ /* Its a race between other gpio_get_level() and the copy of the single
+ * global 'coredev->gpio_get_res' to the function's variable 'level'
+ */
+ *level = coredev->gpio_get_res;
+
+ return rc;
+}
+
+static int __init smscore_module_init(void)
+{
+ INIT_LIST_HEAD(&g_smscore_notifyees);
+ INIT_LIST_HEAD(&g_smscore_devices);
+ kmutex_init(&g_smscore_deviceslock);
+
+ INIT_LIST_HEAD(&g_smscore_registry);
+ kmutex_init(&g_smscore_registrylock);
+
+ return 0;
+}
+
+static void __exit smscore_module_exit(void)
+{
+ kmutex_lock(&g_smscore_deviceslock);
+ while (!list_empty(&g_smscore_notifyees)) {
+ struct smscore_device_notifyee_t *notifyee =
+ (struct smscore_device_notifyee_t *)
+ g_smscore_notifyees.next;
+
+ list_del(&notifyee->entry);
+ kfree(notifyee);
+ }
+ kmutex_unlock(&g_smscore_deviceslock);
+
+ kmutex_lock(&g_smscore_registrylock);
+ while (!list_empty(&g_smscore_registry)) {
+ struct smscore_registry_entry_t *entry =
+ (struct smscore_registry_entry_t *)
+ g_smscore_registry.next;
+
+ list_del(&entry->entry);
+ kfree(entry);
+ }
+ kmutex_unlock(&g_smscore_registrylock);
+
+ pr_debug("\n");
+}
+
+module_init(smscore_module_init);
+module_exit(smscore_module_exit);
+
+MODULE_DESCRIPTION("Siano MDTV Core module");
+MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)");
+MODULE_LICENSE("GPL");
+
+/* This should match what's defined at smscoreapi.h */
+/*(DEBLOBBED)*/
diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h
new file mode 100644
index 000000000..8de8168f1
--- /dev/null
+++ b/drivers/media/common/siano/smscoreapi.h
@@ -0,0 +1,1180 @@
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2008, Uri Shkolnik, Anatoly Greenblat
+
+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, see <http://www.gnu.org/licenses/>.
+
+****************************************************************/
+
+#ifndef __SMS_CORE_API_H__
+#define __SMS_CORE_API_H__
+
+#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+
+#include <media/media-device.h>
+
+#include <asm/page.h>
+
+#include "smsir.h"
+
+#define kmutex_init(_p_) mutex_init(_p_)
+#define kmutex_lock(_p_) mutex_lock(_p_)
+#define kmutex_trylock(_p_) mutex_trylock(_p_)
+#define kmutex_unlock(_p_) mutex_unlock(_p_)
+
+/*
+ * Define the firmware names used by the driver.
+ * Those should match what's used at smscoreapi.c and sms-cards.c
+ * including the MODULE_FIRMWARE() macros at the end of smscoreapi.c
+ */
+#define SMS_FW_ATSC_DENVER "/*(DEBLOBBED)*/"
+#define SMS_FW_CMMB_MING_APP "/*(DEBLOBBED)*/"
+#define SMS_FW_CMMB_VEGA_12MHZ "/*(DEBLOBBED)*/"
+#define SMS_FW_CMMB_VENICE_12MHZ "/*(DEBLOBBED)*/"
+#define SMS_FW_DVBH_RIO "/*(DEBLOBBED)*/"
+#define SMS_FW_DVB_NOVA_12MHZ_B0 "/*(DEBLOBBED)*/"
+#define SMS_FW_DVB_NOVA_12MHZ "/*(DEBLOBBED)*/"
+#define SMS_FW_DVB_RIO "/*(DEBLOBBED)*/"
+#define SMS_FW_FM_RADIO "/*(DEBLOBBED)*/"
+#define SMS_FW_FM_RADIO_RIO "/*(DEBLOBBED)*/"
+#define SMS_FW_DVBT_HCW_55XXX "/*(DEBLOBBED)*/"
+#define SMS_FW_ISDBT_HCW_55XXX "/*(DEBLOBBED)*/"
+#define SMS_FW_ISDBT_NOVA_12MHZ_B0 "/*(DEBLOBBED)*/"
+#define SMS_FW_ISDBT_NOVA_12MHZ "/*(DEBLOBBED)*/"
+#define SMS_FW_ISDBT_PELE "/*(DEBLOBBED)*/"
+#define SMS_FW_ISDBT_RIO "/*(DEBLOBBED)*/"
+#define SMS_FW_DVBT_NOVA_A "/*(DEBLOBBED)*/"
+#define SMS_FW_DVBT_NOVA_B "/*(DEBLOBBED)*/"
+#define SMS_FW_DVBT_STELLAR "/*(DEBLOBBED)*/"
+#define SMS_FW_TDMB_DENVER "/*(DEBLOBBED)*/"
+#define SMS_FW_TDMB_NOVA_12MHZ_B0 "/*(DEBLOBBED)*/"
+#define SMS_FW_TDMB_NOVA_12MHZ "/*(DEBLOBBED)*/"
+
+#define SMS_PROTOCOL_MAX_RAOUNDTRIP_MS (10000)
+#define SMS_ALLOC_ALIGNMENT 128
+#define SMS_DMA_ALIGNMENT 16
+#define SMS_ALIGN_ADDRESS(addr) \
+ ((((uintptr_t)(addr)) + (SMS_DMA_ALIGNMENT-1)) & ~(SMS_DMA_ALIGNMENT-1))
+
+#define SMS_DEVICE_FAMILY1 0
+#define SMS_DEVICE_FAMILY2 1
+#define SMS_ROM_NO_RESPONSE 2
+#define SMS_DEVICE_NOT_READY 0x8000000
+
+enum sms_device_type_st {
+ SMS_UNKNOWN_TYPE = -1,
+ SMS_STELLAR = 0,
+ SMS_NOVA_A0,
+ SMS_NOVA_B0,
+ SMS_VEGA,
+ SMS_VENICE,
+ SMS_MING,
+ SMS_PELE,
+ SMS_RIO,
+ SMS_DENVER_1530,
+ SMS_DENVER_2160,
+ SMS_NUM_OF_DEVICE_TYPES
+};
+
+enum sms_power_mode_st {
+ SMS_POWER_MODE_ACTIVE,
+ SMS_POWER_MODE_SUSPENDED
+};
+
+struct smscore_device_t;
+struct smscore_client_t;
+struct smscore_buffer_t;
+
+typedef int (*hotplug_t)(struct smscore_device_t *coredev,
+ struct device *device, int arrival);
+
+typedef int (*setmode_t)(void *context, int mode);
+typedef void (*detectmode_t)(void *context, int *mode);
+typedef int (*sendrequest_t)(void *context, void *buffer, size_t size);
+typedef int (*loadfirmware_t)(void *context, void *buffer, size_t size);
+typedef int (*preload_t)(void *context);
+typedef int (*postload_t)(void *context);
+
+typedef int (*onresponse_t)(void *context, struct smscore_buffer_t *cb);
+typedef void (*onremove_t)(void *context);
+
+struct smscore_buffer_t {
+ /* public members, once passed to clients can be changed freely */
+ struct list_head entry;
+ int size;
+ int offset;
+
+ /* private members, read-only for clients */
+ void *p;
+ dma_addr_t phys;
+ unsigned long offset_in_common;
+};
+
+struct smsdevice_params_t {
+ struct device *device;
+
+ int buffer_size;
+ int num_buffers;
+
+ char devpath[32];
+ unsigned long flags;
+
+ setmode_t setmode_handler;
+ detectmode_t detectmode_handler;
+ sendrequest_t sendrequest_handler;
+ preload_t preload_handler;
+ postload_t postload_handler;
+
+ void *context;
+ enum sms_device_type_st device_type;
+};
+
+struct smsclient_params_t {
+ int initial_id;
+ int data_type;
+ onresponse_t onresponse_handler;
+ onremove_t onremove_handler;
+ void *context;
+};
+
+struct smscore_device_t {
+ struct list_head entry;
+
+ struct list_head clients;
+ struct list_head subclients;
+ spinlock_t clientslock;
+
+ struct list_head buffers;
+ spinlock_t bufferslock;
+ int num_buffers;
+
+ void *common_buffer;
+ int common_buffer_size;
+ dma_addr_t common_buffer_phys;
+
+ void *context;
+ struct device *device;
+
+ char devpath[32];
+ unsigned long device_flags;
+
+ setmode_t setmode_handler;
+ detectmode_t detectmode_handler;
+ sendrequest_t sendrequest_handler;
+ preload_t preload_handler;
+ postload_t postload_handler;
+
+ int mode, modes_supported;
+
+ /* host <--> device messages */
+ struct completion version_ex_done, data_download_done, trigger_done;
+ struct completion data_validity_done, device_ready_done;
+ struct completion init_device_done, reload_start_done, resume_done;
+ struct completion gpio_configuration_done, gpio_set_level_done;
+ struct completion gpio_get_level_done, ir_init_done;
+
+ /* Buffer management */
+ wait_queue_head_t buffer_mng_waitq;
+
+ /* GPIO */
+ int gpio_get_res;
+
+ /* Target hardware board */
+ int board_id;
+
+ /* Firmware */
+ u8 *fw_buf;
+ u32 fw_buf_size;
+ u16 fw_version;
+
+ /* Infrared (IR) */
+ struct ir_t ir;
+
+ /*
+ * Identify if device is USB or not.
+ * Used by smsdvb-sysfs to know the root node for debugfs
+ */
+ bool is_usb_device;
+
+ int led_state;
+
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ struct media_device *media_dev;
+#endif
+};
+
+/* GPIO definitions for antenna frequency domain control (SMS8021) */
+#define SMS_ANTENNA_GPIO_0 1
+#define SMS_ANTENNA_GPIO_1 0
+
+enum sms_bandwidth_mode {
+ BW_8_MHZ = 0,
+ BW_7_MHZ = 1,
+ BW_6_MHZ = 2,
+ BW_5_MHZ = 3,
+ BW_ISDBT_1SEG = 4,
+ BW_ISDBT_3SEG = 5,
+ BW_2_MHZ = 6,
+ BW_FM_RADIO = 7,
+ BW_ISDBT_13SEG = 8,
+ BW_1_5_MHZ = 15,
+ BW_UNKNOWN = 0xffff
+};
+
+
+#define MSG_HDR_FLAG_SPLIT_MSG 4
+
+#define MAX_GPIO_PIN_NUMBER 31
+
+#define HIF_TASK 11
+#define HIF_TASK_SLAVE 22
+#define HIF_TASK_SLAVE2 33
+#define HIF_TASK_SLAVE3 44
+#define SMS_HOST_LIB 150
+#define DVBT_BDA_CONTROL_MSG_ID 201
+
+#define SMS_MAX_PAYLOAD_SIZE 240
+#define SMS_TUNE_TIMEOUT 500
+
+enum msg_types {
+ MSG_TYPE_BASE_VAL = 500,
+ MSG_SMS_GET_VERSION_REQ = 503,
+ MSG_SMS_GET_VERSION_RES = 504,
+ MSG_SMS_MULTI_BRIDGE_CFG = 505,
+ MSG_SMS_GPIO_CONFIG_REQ = 507,
+ MSG_SMS_GPIO_CONFIG_RES = 508,
+ MSG_SMS_GPIO_SET_LEVEL_REQ = 509,
+ MSG_SMS_GPIO_SET_LEVEL_RES = 510,
+ MSG_SMS_GPIO_GET_LEVEL_REQ = 511,
+ MSG_SMS_GPIO_GET_LEVEL_RES = 512,
+ MSG_SMS_EEPROM_BURN_IND = 513,
+ MSG_SMS_LOG_ENABLE_CHANGE_REQ = 514,
+ MSG_SMS_LOG_ENABLE_CHANGE_RES = 515,
+ MSG_SMS_SET_MAX_TX_MSG_LEN_REQ = 516,
+ MSG_SMS_SET_MAX_TX_MSG_LEN_RES = 517,
+ MSG_SMS_SPI_HALFDUPLEX_TOKEN_HOST_TO_DEVICE = 518,
+ MSG_SMS_SPI_HALFDUPLEX_TOKEN_DEVICE_TO_HOST = 519,
+ MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_REQ = 520,
+ MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_RES = 521,
+ MSG_SMS_BACKGROUND_SCAN_SIGNAL_DETECTED_IND = 522,
+ MSG_SMS_BACKGROUND_SCAN_NO_SIGNAL_IND = 523,
+ MSG_SMS_CONFIGURE_RF_SWITCH_REQ = 524,
+ MSG_SMS_CONFIGURE_RF_SWITCH_RES = 525,
+ MSG_SMS_MRC_PATH_DISCONNECT_REQ = 526,
+ MSG_SMS_MRC_PATH_DISCONNECT_RES = 527,
+ MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_REQ = 528,
+ MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_RES = 529,
+ MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_REQ = 530,
+ MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_RES = 531,
+ MSG_WR_REG_RFT_REQ = 533,
+ MSG_WR_REG_RFT_RES = 534,
+ MSG_RD_REG_RFT_REQ = 535,
+ MSG_RD_REG_RFT_RES = 536,
+ MSG_RD_REG_ALL_RFT_REQ = 537,
+ MSG_RD_REG_ALL_RFT_RES = 538,
+ MSG_HELP_INT = 539,
+ MSG_RUN_SCRIPT_INT = 540,
+ MSG_SMS_EWS_INBAND_REQ = 541,
+ MSG_SMS_EWS_INBAND_RES = 542,
+ MSG_SMS_RFS_SELECT_REQ = 543,
+ MSG_SMS_RFS_SELECT_RES = 544,
+ MSG_SMS_MB_GET_VER_REQ = 545,
+ MSG_SMS_MB_GET_VER_RES = 546,
+ MSG_SMS_MB_WRITE_CFGFILE_REQ = 547,
+ MSG_SMS_MB_WRITE_CFGFILE_RES = 548,
+ MSG_SMS_MB_READ_CFGFILE_REQ = 549,
+ MSG_SMS_MB_READ_CFGFILE_RES = 550,
+ MSG_SMS_RD_MEM_REQ = 552,
+ MSG_SMS_RD_MEM_RES = 553,
+ MSG_SMS_WR_MEM_REQ = 554,
+ MSG_SMS_WR_MEM_RES = 555,
+ MSG_SMS_UPDATE_MEM_REQ = 556,
+ MSG_SMS_UPDATE_MEM_RES = 557,
+ MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_REQ = 558,
+ MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_RES = 559,
+ MSG_SMS_RF_TUNE_REQ = 561,
+ MSG_SMS_RF_TUNE_RES = 562,
+ MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_REQ = 563,
+ MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_RES = 564,
+ MSG_SMS_ISDBT_SB_RECEPTION_REQ = 565,
+ MSG_SMS_ISDBT_SB_RECEPTION_RES = 566,
+ MSG_SMS_GENERIC_EPROM_WRITE_REQ = 567,
+ MSG_SMS_GENERIC_EPROM_WRITE_RES = 568,
+ MSG_SMS_GENERIC_EPROM_READ_REQ = 569,
+ MSG_SMS_GENERIC_EPROM_READ_RES = 570,
+ MSG_SMS_EEPROM_WRITE_REQ = 571,
+ MSG_SMS_EEPROM_WRITE_RES = 572,
+ MSG_SMS_CUSTOM_READ_REQ = 574,
+ MSG_SMS_CUSTOM_READ_RES = 575,
+ MSG_SMS_CUSTOM_WRITE_REQ = 576,
+ MSG_SMS_CUSTOM_WRITE_RES = 577,
+ MSG_SMS_INIT_DEVICE_REQ = 578,
+ MSG_SMS_INIT_DEVICE_RES = 579,
+ MSG_SMS_ATSC_SET_ALL_IP_REQ = 580,
+ MSG_SMS_ATSC_SET_ALL_IP_RES = 581,
+ MSG_SMS_ATSC_START_ENSEMBLE_REQ = 582,
+ MSG_SMS_ATSC_START_ENSEMBLE_RES = 583,
+ MSG_SMS_SET_OUTPUT_MODE_REQ = 584,
+ MSG_SMS_SET_OUTPUT_MODE_RES = 585,
+ MSG_SMS_ATSC_IP_FILTER_GET_LIST_REQ = 586,
+ MSG_SMS_ATSC_IP_FILTER_GET_LIST_RES = 587,
+ MSG_SMS_SUB_CHANNEL_START_REQ = 589,
+ MSG_SMS_SUB_CHANNEL_START_RES = 590,
+ MSG_SMS_SUB_CHANNEL_STOP_REQ = 591,
+ MSG_SMS_SUB_CHANNEL_STOP_RES = 592,
+ MSG_SMS_ATSC_IP_FILTER_ADD_REQ = 593,
+ MSG_SMS_ATSC_IP_FILTER_ADD_RES = 594,
+ MSG_SMS_ATSC_IP_FILTER_REMOVE_REQ = 595,
+ MSG_SMS_ATSC_IP_FILTER_REMOVE_RES = 596,
+ MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_REQ = 597,
+ MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_RES = 598,
+ MSG_SMS_WAIT_CMD = 599,
+ MSG_SMS_ADD_PID_FILTER_REQ = 601,
+ MSG_SMS_ADD_PID_FILTER_RES = 602,
+ MSG_SMS_REMOVE_PID_FILTER_REQ = 603,
+ MSG_SMS_REMOVE_PID_FILTER_RES = 604,
+ MSG_SMS_FAST_INFORMATION_CHANNEL_REQ = 605,
+ MSG_SMS_FAST_INFORMATION_CHANNEL_RES = 606,
+ MSG_SMS_DAB_CHANNEL = 607,
+ MSG_SMS_GET_PID_FILTER_LIST_REQ = 608,
+ MSG_SMS_GET_PID_FILTER_LIST_RES = 609,
+ MSG_SMS_POWER_DOWN_REQ = 610,
+ MSG_SMS_POWER_DOWN_RES = 611,
+ MSG_SMS_ATSC_SLT_EXIST_IND = 612,
+ MSG_SMS_ATSC_NO_SLT_IND = 613,
+ MSG_SMS_GET_STATISTICS_REQ = 615,
+ MSG_SMS_GET_STATISTICS_RES = 616,
+ MSG_SMS_SEND_DUMP = 617,
+ MSG_SMS_SCAN_START_REQ = 618,
+ MSG_SMS_SCAN_START_RES = 619,
+ MSG_SMS_SCAN_STOP_REQ = 620,
+ MSG_SMS_SCAN_STOP_RES = 621,
+ MSG_SMS_SCAN_PROGRESS_IND = 622,
+ MSG_SMS_SCAN_COMPLETE_IND = 623,
+ MSG_SMS_LOG_ITEM = 624,
+ MSG_SMS_DAB_SUBCHANNEL_RECONFIG_REQ = 628,
+ MSG_SMS_DAB_SUBCHANNEL_RECONFIG_RES = 629,
+ MSG_SMS_HO_PER_SLICES_IND = 630,
+ MSG_SMS_HO_INBAND_POWER_IND = 631,
+ MSG_SMS_MANUAL_DEMOD_REQ = 632,
+ MSG_SMS_HO_TUNE_ON_REQ = 636,
+ MSG_SMS_HO_TUNE_ON_RES = 637,
+ MSG_SMS_HO_TUNE_OFF_REQ = 638,
+ MSG_SMS_HO_TUNE_OFF_RES = 639,
+ MSG_SMS_HO_PEEK_FREQ_REQ = 640,
+ MSG_SMS_HO_PEEK_FREQ_RES = 641,
+ MSG_SMS_HO_PEEK_FREQ_IND = 642,
+ MSG_SMS_MB_ATTEN_SET_REQ = 643,
+ MSG_SMS_MB_ATTEN_SET_RES = 644,
+ MSG_SMS_ENABLE_STAT_IN_I2C_REQ = 649,
+ MSG_SMS_ENABLE_STAT_IN_I2C_RES = 650,
+ MSG_SMS_SET_ANTENNA_CONFIG_REQ = 651,
+ MSG_SMS_SET_ANTENNA_CONFIG_RES = 652,
+ MSG_SMS_GET_STATISTICS_EX_REQ = 653,
+ MSG_SMS_GET_STATISTICS_EX_RES = 654,
+ MSG_SMS_SLEEP_RESUME_COMP_IND = 655,
+ MSG_SMS_SWITCH_HOST_INTERFACE_REQ = 656,
+ MSG_SMS_SWITCH_HOST_INTERFACE_RES = 657,
+ MSG_SMS_DATA_DOWNLOAD_REQ = 660,
+ MSG_SMS_DATA_DOWNLOAD_RES = 661,
+ MSG_SMS_DATA_VALIDITY_REQ = 662,
+ MSG_SMS_DATA_VALIDITY_RES = 663,
+ MSG_SMS_SWDOWNLOAD_TRIGGER_REQ = 664,
+ MSG_SMS_SWDOWNLOAD_TRIGGER_RES = 665,
+ MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ = 666,
+ MSG_SMS_SWDOWNLOAD_BACKDOOR_RES = 667,
+ MSG_SMS_GET_VERSION_EX_REQ = 668,
+ MSG_SMS_GET_VERSION_EX_RES = 669,
+ MSG_SMS_CLOCK_OUTPUT_CONFIG_REQ = 670,
+ MSG_SMS_CLOCK_OUTPUT_CONFIG_RES = 671,
+ MSG_SMS_I2C_SET_FREQ_REQ = 685,
+ MSG_SMS_I2C_SET_FREQ_RES = 686,
+ MSG_SMS_GENERIC_I2C_REQ = 687,
+ MSG_SMS_GENERIC_I2C_RES = 688,
+ MSG_SMS_DVBT_BDA_DATA = 693,
+ MSG_SW_RELOAD_REQ = 697,
+ MSG_SMS_DATA_MSG = 699,
+ MSG_TABLE_UPLOAD_REQ = 700,
+ MSG_TABLE_UPLOAD_RES = 701,
+ MSG_SW_RELOAD_START_REQ = 702,
+ MSG_SW_RELOAD_START_RES = 703,
+ MSG_SW_RELOAD_EXEC_REQ = 704,
+ MSG_SW_RELOAD_EXEC_RES = 705,
+ MSG_SMS_SPI_INT_LINE_SET_REQ = 710,
+ MSG_SMS_SPI_INT_LINE_SET_RES = 711,
+ MSG_SMS_GPIO_CONFIG_EX_REQ = 712,
+ MSG_SMS_GPIO_CONFIG_EX_RES = 713,
+ MSG_SMS_WATCHDOG_ACT_REQ = 716,
+ MSG_SMS_WATCHDOG_ACT_RES = 717,
+ MSG_SMS_LOOPBACK_REQ = 718,
+ MSG_SMS_LOOPBACK_RES = 719,
+ MSG_SMS_RAW_CAPTURE_START_REQ = 720,
+ MSG_SMS_RAW_CAPTURE_START_RES = 721,
+ MSG_SMS_RAW_CAPTURE_ABORT_REQ = 722,
+ MSG_SMS_RAW_CAPTURE_ABORT_RES = 723,
+ MSG_SMS_RAW_CAPTURE_COMPLETE_IND = 728,
+ MSG_SMS_DATA_PUMP_IND = 729,
+ MSG_SMS_DATA_PUMP_REQ = 730,
+ MSG_SMS_DATA_PUMP_RES = 731,
+ MSG_SMS_FLASH_DL_REQ = 732,
+ MSG_SMS_EXEC_TEST_1_REQ = 734,
+ MSG_SMS_EXEC_TEST_1_RES = 735,
+ MSG_SMS_ENBALE_TS_INTERFACE_REQ = 736,
+ MSG_SMS_ENBALE_TS_INTERFACE_RES = 737,
+ MSG_SMS_SPI_SET_BUS_WIDTH_REQ = 738,
+ MSG_SMS_SPI_SET_BUS_WIDTH_RES = 739,
+ MSG_SMS_SEND_EMM_REQ = 740,
+ MSG_SMS_SEND_EMM_RES = 741,
+ MSG_SMS_DISABLE_TS_INTERFACE_REQ = 742,
+ MSG_SMS_DISABLE_TS_INTERFACE_RES = 743,
+ MSG_SMS_IS_BUF_FREE_REQ = 744,
+ MSG_SMS_IS_BUF_FREE_RES = 745,
+ MSG_SMS_EXT_ANTENNA_REQ = 746,
+ MSG_SMS_EXT_ANTENNA_RES = 747,
+ MSG_SMS_CMMB_GET_NET_OF_FREQ_REQ_OBSOLETE = 748,
+ MSG_SMS_CMMB_GET_NET_OF_FREQ_RES_OBSOLETE = 749,
+ MSG_SMS_BATTERY_LEVEL_REQ = 750,
+ MSG_SMS_BATTERY_LEVEL_RES = 751,
+ MSG_SMS_CMMB_INJECT_TABLE_REQ_OBSOLETE = 752,
+ MSG_SMS_CMMB_INJECT_TABLE_RES_OBSOLETE = 753,
+ MSG_SMS_FM_RADIO_BLOCK_IND = 754,
+ MSG_SMS_HOST_NOTIFICATION_IND = 755,
+ MSG_SMS_CMMB_GET_CONTROL_TABLE_REQ_OBSOLETE = 756,
+ MSG_SMS_CMMB_GET_CONTROL_TABLE_RES_OBSOLETE = 757,
+ MSG_SMS_CMMB_GET_NETWORKS_REQ = 760,
+ MSG_SMS_CMMB_GET_NETWORKS_RES = 761,
+ MSG_SMS_CMMB_START_SERVICE_REQ = 762,
+ MSG_SMS_CMMB_START_SERVICE_RES = 763,
+ MSG_SMS_CMMB_STOP_SERVICE_REQ = 764,
+ MSG_SMS_CMMB_STOP_SERVICE_RES = 765,
+ MSG_SMS_CMMB_ADD_CHANNEL_FILTER_REQ = 768,
+ MSG_SMS_CMMB_ADD_CHANNEL_FILTER_RES = 769,
+ MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_REQ = 770,
+ MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_RES = 771,
+ MSG_SMS_CMMB_START_CONTROL_INFO_REQ = 772,
+ MSG_SMS_CMMB_START_CONTROL_INFO_RES = 773,
+ MSG_SMS_CMMB_STOP_CONTROL_INFO_REQ = 774,
+ MSG_SMS_CMMB_STOP_CONTROL_INFO_RES = 775,
+ MSG_SMS_ISDBT_TUNE_REQ = 776,
+ MSG_SMS_ISDBT_TUNE_RES = 777,
+ MSG_SMS_TRANSMISSION_IND = 782,
+ MSG_SMS_PID_STATISTICS_IND = 783,
+ MSG_SMS_POWER_DOWN_IND = 784,
+ MSG_SMS_POWER_DOWN_CONF = 785,
+ MSG_SMS_POWER_UP_IND = 786,
+ MSG_SMS_POWER_UP_CONF = 787,
+ MSG_SMS_POWER_MODE_SET_REQ = 790,
+ MSG_SMS_POWER_MODE_SET_RES = 791,
+ MSG_SMS_DEBUG_HOST_EVENT_REQ = 792,
+ MSG_SMS_DEBUG_HOST_EVENT_RES = 793,
+ MSG_SMS_NEW_CRYSTAL_REQ = 794,
+ MSG_SMS_NEW_CRYSTAL_RES = 795,
+ MSG_SMS_CONFIG_SPI_REQ = 796,
+ MSG_SMS_CONFIG_SPI_RES = 797,
+ MSG_SMS_I2C_SHORT_STAT_IND = 798,
+ MSG_SMS_START_IR_REQ = 800,
+ MSG_SMS_START_IR_RES = 801,
+ MSG_SMS_IR_SAMPLES_IND = 802,
+ MSG_SMS_CMMB_CA_SERVICE_IND = 803,
+ MSG_SMS_SLAVE_DEVICE_DETECTED = 804,
+ MSG_SMS_INTERFACE_LOCK_IND = 805,
+ MSG_SMS_INTERFACE_UNLOCK_IND = 806,
+ MSG_SMS_SEND_ROSUM_BUFF_REQ = 810,
+ MSG_SMS_SEND_ROSUM_BUFF_RES = 811,
+ MSG_SMS_ROSUM_BUFF = 812,
+ MSG_SMS_SET_AES128_KEY_REQ = 815,
+ MSG_SMS_SET_AES128_KEY_RES = 816,
+ MSG_SMS_MBBMS_WRITE_REQ = 817,
+ MSG_SMS_MBBMS_WRITE_RES = 818,
+ MSG_SMS_MBBMS_READ_IND = 819,
+ MSG_SMS_IQ_STREAM_START_REQ = 820,
+ MSG_SMS_IQ_STREAM_START_RES = 821,
+ MSG_SMS_IQ_STREAM_STOP_REQ = 822,
+ MSG_SMS_IQ_STREAM_STOP_RES = 823,
+ MSG_SMS_IQ_STREAM_DATA_BLOCK = 824,
+ MSG_SMS_GET_EEPROM_VERSION_REQ = 825,
+ MSG_SMS_GET_EEPROM_VERSION_RES = 826,
+ MSG_SMS_SIGNAL_DETECTED_IND = 827,
+ MSG_SMS_NO_SIGNAL_IND = 828,
+ MSG_SMS_MRC_SHUTDOWN_SLAVE_REQ = 830,
+ MSG_SMS_MRC_SHUTDOWN_SLAVE_RES = 831,
+ MSG_SMS_MRC_BRINGUP_SLAVE_REQ = 832,
+ MSG_SMS_MRC_BRINGUP_SLAVE_RES = 833,
+ MSG_SMS_EXTERNAL_LNA_CTRL_REQ = 834,
+ MSG_SMS_EXTERNAL_LNA_CTRL_RES = 835,
+ MSG_SMS_SET_PERIODIC_STATISTICS_REQ = 836,
+ MSG_SMS_SET_PERIODIC_STATISTICS_RES = 837,
+ MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_REQ = 838,
+ MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_RES = 839,
+ LOCAL_TUNE = 850,
+ LOCAL_IFFT_H_ICI = 851,
+ MSG_RESYNC_REQ = 852,
+ MSG_SMS_CMMB_GET_MRC_STATISTICS_REQ = 853,
+ MSG_SMS_CMMB_GET_MRC_STATISTICS_RES = 854,
+ MSG_SMS_LOG_EX_ITEM = 855,
+ MSG_SMS_DEVICE_DATA_LOSS_IND = 856,
+ MSG_SMS_MRC_WATCHDOG_TRIGGERED_IND = 857,
+ MSG_SMS_USER_MSG_REQ = 858,
+ MSG_SMS_USER_MSG_RES = 859,
+ MSG_SMS_SMART_CARD_INIT_REQ = 860,
+ MSG_SMS_SMART_CARD_INIT_RES = 861,
+ MSG_SMS_SMART_CARD_WRITE_REQ = 862,
+ MSG_SMS_SMART_CARD_WRITE_RES = 863,
+ MSG_SMS_SMART_CARD_READ_IND = 864,
+ MSG_SMS_TSE_ENABLE_REQ = 866,
+ MSG_SMS_TSE_ENABLE_RES = 867,
+ MSG_SMS_CMMB_GET_SHORT_STATISTICS_REQ = 868,
+ MSG_SMS_CMMB_GET_SHORT_STATISTICS_RES = 869,
+ MSG_SMS_LED_CONFIG_REQ = 870,
+ MSG_SMS_LED_CONFIG_RES = 871,
+ MSG_PWM_ANTENNA_REQ = 872,
+ MSG_PWM_ANTENNA_RES = 873,
+ MSG_SMS_CMMB_SMD_SN_REQ = 874,
+ MSG_SMS_CMMB_SMD_SN_RES = 875,
+ MSG_SMS_CMMB_SET_CA_CW_REQ = 876,
+ MSG_SMS_CMMB_SET_CA_CW_RES = 877,
+ MSG_SMS_CMMB_SET_CA_SALT_REQ = 878,
+ MSG_SMS_CMMB_SET_CA_SALT_RES = 879,
+ MSG_SMS_NSCD_INIT_REQ = 880,
+ MSG_SMS_NSCD_INIT_RES = 881,
+ MSG_SMS_NSCD_PROCESS_SECTION_REQ = 882,
+ MSG_SMS_NSCD_PROCESS_SECTION_RES = 883,
+ MSG_SMS_DBD_CREATE_OBJECT_REQ = 884,
+ MSG_SMS_DBD_CREATE_OBJECT_RES = 885,
+ MSG_SMS_DBD_CONFIGURE_REQ = 886,
+ MSG_SMS_DBD_CONFIGURE_RES = 887,
+ MSG_SMS_DBD_SET_KEYS_REQ = 888,
+ MSG_SMS_DBD_SET_KEYS_RES = 889,
+ MSG_SMS_DBD_PROCESS_HEADER_REQ = 890,
+ MSG_SMS_DBD_PROCESS_HEADER_RES = 891,
+ MSG_SMS_DBD_PROCESS_DATA_REQ = 892,
+ MSG_SMS_DBD_PROCESS_DATA_RES = 893,
+ MSG_SMS_DBD_PROCESS_GET_DATA_REQ = 894,
+ MSG_SMS_DBD_PROCESS_GET_DATA_RES = 895,
+ MSG_SMS_NSCD_OPEN_SESSION_REQ = 896,
+ MSG_SMS_NSCD_OPEN_SESSION_RES = 897,
+ MSG_SMS_SEND_HOST_DATA_TO_DEMUX_REQ = 898,
+ MSG_SMS_SEND_HOST_DATA_TO_DEMUX_RES = 899,
+ MSG_LAST_MSG_TYPE = 900,
+};
+
+#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \
+ (ptr)->msg_type = type; \
+ (ptr)->msg_src_id = src; \
+ (ptr)->msg_dst_id = dst; \
+ (ptr)->msg_length = len; \
+ (ptr)->msg_flags = 0; \
+} while (0)
+
+#define SMS_INIT_MSG(ptr, type, len) \
+ SMS_INIT_MSG_EX(ptr, type, 0, HIF_TASK, len)
+
+enum SMS_DVB3_EVENTS {
+ DVB3_EVENT_INIT = 0,
+ DVB3_EVENT_SLEEP,
+ DVB3_EVENT_HOTPLUG,
+ DVB3_EVENT_FE_LOCK,
+ DVB3_EVENT_FE_UNLOCK,
+ DVB3_EVENT_UNC_OK,
+ DVB3_EVENT_UNC_ERR
+};
+
+enum SMS_DEVICE_MODE {
+ DEVICE_MODE_NONE = -1,
+ DEVICE_MODE_DVBT = 0,
+ DEVICE_MODE_DVBH,
+ DEVICE_MODE_DAB_TDMB,
+ DEVICE_MODE_DAB_TDMB_DABIP,
+ DEVICE_MODE_DVBT_BDA,
+ DEVICE_MODE_ISDBT,
+ DEVICE_MODE_ISDBT_BDA,
+ DEVICE_MODE_CMMB,
+ DEVICE_MODE_RAW_TUNER,
+ DEVICE_MODE_FM_RADIO,
+ DEVICE_MODE_FM_RADIO_BDA,
+ DEVICE_MODE_ATSC,
+ DEVICE_MODE_MAX,
+};
+
+struct sms_msg_hdr {
+ u16 msg_type;
+ u8 msg_src_id;
+ u8 msg_dst_id;
+ u16 msg_length; /* length of entire message, including header */
+ u16 msg_flags;
+};
+
+struct sms_msg_data {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[1];
+};
+
+struct sms_msg_data2 {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[2];
+};
+
+struct sms_msg_data4 {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[4];
+};
+
+struct sms_data_download {
+ struct sms_msg_hdr x_msg_header;
+ u32 mem_addr;
+ u8 payload[SMS_MAX_PAYLOAD_SIZE];
+};
+
+struct sms_version_res {
+ struct sms_msg_hdr x_msg_header;
+
+ u16 chip_model; /* e.g. 0x1102 for SMS-1102 "Nova" */
+ u8 step; /* 0 - step A */
+ u8 metal_fix; /* 0 - Metal 0 */
+
+ /* firmware_id 0xFF if ROM, otherwise the
+ * value indicated by SMSHOSTLIB_DEVICE_MODES_E */
+ u8 firmware_id;
+ /* supported_protocols Bitwise OR combination of
+ * supported protocols */
+ u8 supported_protocols;
+
+ u8 version_major;
+ u8 version_minor;
+ u8 version_patch;
+ u8 version_field_patch;
+
+ u8 rom_ver_major;
+ u8 rom_ver_minor;
+ u8 rom_ver_patch;
+ u8 rom_ver_field_patch;
+
+ u8 TextLabel[34];
+};
+
+struct sms_firmware {
+ u32 check_sum;
+ u32 length;
+ u32 start_address;
+ u8 payload[1];
+};
+
+/* statistics information returned as response for
+ * SmsHostApiGetstatistics_Req */
+struct sms_stats {
+ u32 reserved; /* reserved */
+
+ /* Common parameters */
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ /* Reception quality */
+ s32 SNR; /* dB */
+ u32 ber; /* Post Viterbi ber [1E-5] */
+ u32 FIB_CRC; /* CRC errors percentage, valid only for DAB */
+ u32 ts_per; /* Transport stream PER,
+ 0xFFFFFFFF indicate N/A, valid only for DVB-T/H */
+ u32 MFER; /* DVB-H frame error rate in percentage,
+ 0xFFFFFFFF indicate N/A, valid only for DVB-H */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in bin/1024 */
+
+ /* Transmission parameters */
+ u32 frequency; /* frequency in Hz */
+ u32 bandwidth; /* bandwidth in MHz, valid only for DVB-T/H */
+ u32 transmission_mode; /* Transmission Mode, for DAB modes 1-4,
+ for DVB-T/H FFT mode carriers in Kilos */
+ u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET,
+ valid only for DVB-T/H */
+ u32 guard_interval; /* Guard Interval from
+ SMSHOSTLIB_GUARD_INTERVALS_ET, valid only for DVB-T/H */
+ u32 code_rate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET,
+ valid only for DVB-T/H */
+ u32 lp_code_rate; /* Low Priority Code Rate from
+ SMSHOSTLIB_CODE_RATE_ET, valid only for DVB-T/H */
+ u32 hierarchy; /* hierarchy from SMSHOSTLIB_HIERARCHY_ET,
+ valid only for DVB-T/H */
+ u32 constellation; /* constellation from
+ SMSHOSTLIB_CONSTELLATION_ET, valid only for DVB-T/H */
+
+ /* Burst parameters, valid only for DVB-H */
+ u32 burst_size; /* Current burst size in bytes,
+ valid only for DVB-H */
+ u32 burst_duration; /* Current burst duration in mSec,
+ valid only for DVB-H */
+ u32 burst_cycle_time; /* Current burst cycle time in mSec,
+ valid only for DVB-H */
+ u32 calc_burst_cycle_time;/* Current burst cycle time in mSec,
+ as calculated by demodulator, valid only for DVB-H */
+ u32 num_of_rows; /* Number of rows in MPE table,
+ valid only for DVB-H */
+ u32 num_of_padd_cols; /* Number of padding columns in MPE table,
+ valid only for DVB-H */
+ u32 num_of_punct_cols; /* Number of puncturing columns in MPE table,
+ valid only for DVB-H */
+ u32 error_ts_packets; /* Number of erroneous
+ transport-stream packets */
+ u32 total_ts_packets; /* Total number of transport-stream packets */
+ u32 num_of_valid_mpe_tlbs; /* Number of MPE tables which do not include
+ errors after MPE RS decoding */
+ u32 num_of_invalid_mpe_tlbs;/* Number of MPE tables which include errors
+ after MPE RS decoding */
+ u32 num_of_corrected_mpe_tlbs;/* Number of MPE tables which were
+ corrected by MPE RS decoding */
+ /* Common params */
+ u32 ber_error_count; /* Number of errornous SYNC bits. */
+ u32 ber_bit_count; /* Total number of SYNC bits. */
+
+ /* Interface information */
+ u32 sms_to_host_tx_errors; /* Total number of transmission errors. */
+
+ /* DAB/T-DMB */
+ u32 pre_ber; /* DAB/T-DMB only: Pre Viterbi ber [1E-5] */
+
+ /* DVB-H TPS parameters */
+ u32 cell_id; /* TPS Cell ID in bits 15..0, bits 31..16 zero;
+ if set to 0xFFFFFFFF cell_id not yet recovered */
+ u32 dvbh_srv_ind_hp; /* DVB-H service indication info, bit 1 -
+ Time Slicing indicator, bit 0 - MPE-FEC indicator */
+ u32 dvbh_srv_ind_lp; /* DVB-H service indication info, bit 1 -
+ Time Slicing indicator, bit 0 - MPE-FEC indicator */
+
+ u32 num_mpe_received; /* DVB-H, Num MPE section received */
+
+ u32 reservedFields[10]; /* reserved */
+};
+
+struct sms_msg_statistics_info {
+ u32 request_result;
+
+ struct sms_stats stat;
+
+ /* Split the calc of the SNR in DAB */
+ u32 signal; /* dB */
+ u32 noise; /* dB */
+
+};
+
+struct sms_isdbt_layer_stats {
+ /* Per-layer information */
+ u32 code_rate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET,
+ * 255 means layer does not exist */
+ u32 constellation; /* constellation from SMSHOSTLIB_CONSTELLATION_ET,
+ * 255 means layer does not exist */
+ u32 ber; /* Post Viterbi ber [1E-5], 0xFFFFFFFF indicate N/A */
+ u32 ber_error_count; /* Post Viterbi Error Bits Count */
+ u32 ber_bit_count; /* Post Viterbi Total Bits Count */
+ u32 pre_ber; /* Pre Viterbi ber [1E-5], 0xFFFFFFFF indicate N/A */
+ u32 ts_per; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */
+ u32 error_ts_packets; /* Number of erroneous transport-stream packets */
+ u32 total_ts_packets; /* Total number of transport-stream packets */
+ u32 ti_ldepth_i; /* Time interleaver depth I parameter,
+ * 255 means layer does not exist */
+ u32 number_of_segments; /* Number of segments in layer A,
+ * 255 means layer does not exist */
+ u32 tmcc_errors; /* TMCC errors */
+};
+
+struct sms_isdbt_stats {
+ u32 statistics_type; /* Enumerator identifying the type of the
+ * structure. Values are the same as
+ * SMSHOSTLIB_DEVICE_MODES_E
+ *
+ * This field MUST always be first in any
+ * statistics structure */
+
+ u32 full_size; /* Total size of the structure returned by the modem.
+ * If the size requested by the host is smaller than
+ * full_size, the struct will be truncated */
+
+ /* Common parameters */
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ /* Reception quality */
+ s32 SNR; /* dB */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in Hz */
+
+ /* Transmission parameters */
+ u32 frequency; /* frequency in Hz */
+ u32 bandwidth; /* bandwidth in MHz */
+ u32 transmission_mode; /* ISDB-T transmission mode */
+ u32 modem_state; /* 0 - Acquisition, 1 - Locked */
+ u32 guard_interval; /* Guard Interval, 1 divided by value */
+ u32 system_type; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */
+ u32 partial_reception; /* TRUE - partial reception, FALSE otherwise */
+ u32 num_of_layers; /* Number of ISDB-T layers in the network */
+
+ /* Per-layer information */
+ /* Layers A, B and C */
+ struct sms_isdbt_layer_stats layer_info[3];
+ /* Per-layer statistics, see sms_isdbt_layer_stats */
+
+ /* Interface information */
+ u32 sms_to_host_tx_errors; /* Total number of transmission errors. */
+};
+
+struct sms_isdbt_stats_ex {
+ u32 statistics_type; /* Enumerator identifying the type of the
+ * structure. Values are the same as
+ * SMSHOSTLIB_DEVICE_MODES_E
+ *
+ * This field MUST always be first in any
+ * statistics structure */
+
+ u32 full_size; /* Total size of the structure returned by the modem.
+ * If the size requested by the host is smaller than
+ * full_size, the struct will be truncated */
+
+ /* Common parameters */
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ /* Reception quality */
+ s32 SNR; /* dB */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in Hz */
+
+ /* Transmission parameters */
+ u32 frequency; /* frequency in Hz */
+ u32 bandwidth; /* bandwidth in MHz */
+ u32 transmission_mode; /* ISDB-T transmission mode */
+ u32 modem_state; /* 0 - Acquisition, 1 - Locked */
+ u32 guard_interval; /* Guard Interval, 1 divided by value */
+ u32 system_type; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */
+ u32 partial_reception; /* TRUE - partial reception, FALSE otherwise */
+ u32 num_of_layers; /* Number of ISDB-T layers in the network */
+
+ u32 segment_number; /* Segment number for ISDB-Tsb */
+ u32 tune_bw; /* Tuned bandwidth - BW_ISDBT_1SEG / BW_ISDBT_3SEG */
+
+ /* Per-layer information */
+ /* Layers A, B and C */
+ struct sms_isdbt_layer_stats layer_info[3];
+ /* Per-layer statistics, see sms_isdbt_layer_stats */
+
+ /* Interface information */
+ u32 reserved1; /* Was sms_to_host_tx_errors - obsolete . */
+ /* Proprietary information */
+ u32 ext_antenna; /* Obsolete field. */
+ u32 reception_quality;
+ u32 ews_alert_active; /* signals if EWS alert is currently on */
+ u32 lna_on_off; /* Internal LNA state: 0: OFF, 1: ON */
+
+ u32 rf_agc_level; /* RF AGC Level [linear units], full gain = 65535 (20dB) */
+ u32 bb_agc_level; /* Baseband AGC level [linear units], full gain = 65535 (71.5dB) */
+ u32 fw_errors_counter; /* Application errors - should be always zero */
+ u8 FwErrorsHistoryArr[8]; /* Last FW errors IDs - first is most recent, last is oldest */
+
+ s32 MRC_SNR; /* dB */
+ u32 snr_full_res; /* dB x 65536 */
+ u32 reserved4[4];
+};
+
+
+struct sms_pid_stats_data {
+ struct PID_BURST_S {
+ u32 size;
+ u32 padding_cols;
+ u32 punct_cols;
+ u32 duration;
+ u32 cycle;
+ u32 calc_cycle;
+ } burst;
+
+ u32 tot_tbl_cnt;
+ u32 invalid_tbl_cnt;
+ u32 tot_cor_tbl;
+};
+
+struct sms_pid_data {
+ u32 pid;
+ u32 num_rows;
+ struct sms_pid_stats_data pid_statistics;
+};
+
+#define CORRECT_STAT_RSSI(_stat) ((_stat).RSSI *= -1)
+#define CORRECT_STAT_BANDWIDTH(_stat) (_stat.bandwidth = 8 - _stat.bandwidth)
+#define CORRECT_STAT_TRANSMISSON_MODE(_stat) \
+ if (_stat.transmission_mode == 0) \
+ _stat.transmission_mode = 2; \
+ else if (_stat.transmission_mode == 1) \
+ _stat.transmission_mode = 8; \
+ else \
+ _stat.transmission_mode = 4;
+
+struct sms_tx_stats {
+ u32 frequency; /* frequency in Hz */
+ u32 bandwidth; /* bandwidth in MHz */
+ u32 transmission_mode; /* FFT mode carriers in Kilos */
+ u32 guard_interval; /* Guard Interval from
+ SMSHOSTLIB_GUARD_INTERVALS_ET */
+ u32 code_rate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET */
+ u32 lp_code_rate; /* Low Priority Code Rate from
+ SMSHOSTLIB_CODE_RATE_ET */
+ u32 hierarchy; /* hierarchy from SMSHOSTLIB_HIERARCHY_ET */
+ u32 constellation; /* constellation from
+ SMSHOSTLIB_CONSTELLATION_ET */
+
+ /* DVB-H TPS parameters */
+ u32 cell_id; /* TPS Cell ID in bits 15..0, bits 31..16 zero;
+ if set to 0xFFFFFFFF cell_id not yet recovered */
+ u32 dvbh_srv_ind_hp; /* DVB-H service indication info, bit 1 -
+ Time Slicing indicator, bit 0 - MPE-FEC indicator */
+ u32 dvbh_srv_ind_lp; /* DVB-H service indication info, bit 1 -
+ Time Slicing indicator, bit 0 - MPE-FEC indicator */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+};
+
+struct sms_rx_stats {
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */
+ s32 SNR; /* dB */
+ u32 ber; /* Post Viterbi ber [1E-5] */
+ u32 ber_error_count; /* Number of erroneous SYNC bits. */
+ u32 ber_bit_count; /* Total number of SYNC bits. */
+ u32 ts_per; /* Transport stream PER,
+ 0xFFFFFFFF indicate N/A */
+ u32 MFER; /* DVB-H frame error rate in percentage,
+ 0xFFFFFFFF indicate N/A, valid only for DVB-H */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in bin/1024 */
+ u32 error_ts_packets; /* Number of erroneous
+ transport-stream packets */
+ u32 total_ts_packets; /* Total number of transport-stream packets */
+
+ s32 MRC_SNR; /* dB */
+ s32 MRC_RSSI; /* dBm */
+ s32 mrc_in_band_pwr; /* In band power in dBM */
+};
+
+struct sms_rx_stats_ex {
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */
+ s32 SNR; /* dB */
+ u32 ber; /* Post Viterbi ber [1E-5] */
+ u32 ber_error_count; /* Number of erroneous SYNC bits. */
+ u32 ber_bit_count; /* Total number of SYNC bits. */
+ u32 ts_per; /* Transport stream PER,
+ 0xFFFFFFFF indicate N/A */
+ u32 MFER; /* DVB-H frame error rate in percentage,
+ 0xFFFFFFFF indicate N/A, valid only for DVB-H */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in bin/1024 */
+ u32 error_ts_packets; /* Number of erroneous
+ transport-stream packets */
+ u32 total_ts_packets; /* Total number of transport-stream packets */
+
+ s32 ref_dev_ppm;
+ s32 freq_dev_hz;
+
+ s32 MRC_SNR; /* dB */
+ s32 MRC_RSSI; /* dBm */
+ s32 mrc_in_band_pwr; /* In band power in dBM */
+};
+
+
+/* statistics information returned as response for
+ * SmsHostApiGetstatisticsEx_Req for DVB applications, SMS1100 and up */
+struct sms_stats_dvb {
+ /* Reception */
+ struct sms_rx_stats reception_data;
+
+ /* Transmission parameters */
+ struct sms_tx_stats transmission_data;
+
+ /* Burst parameters, valid only for DVB-H */
+#define SRVM_MAX_PID_FILTERS 8
+ struct sms_pid_data pid_data[SRVM_MAX_PID_FILTERS];
+};
+
+/* statistics information returned as response for
+ * SmsHostApiGetstatisticsEx_Req for DVB applications, SMS1100 and up */
+struct sms_stats_dvb_ex {
+ /* Reception */
+ struct sms_rx_stats_ex reception_data;
+
+ /* Transmission parameters */
+ struct sms_tx_stats transmission_data;
+
+ /* Burst parameters, valid only for DVB-H */
+#define SRVM_MAX_PID_FILTERS 8
+ struct sms_pid_data pid_data[SRVM_MAX_PID_FILTERS];
+};
+
+struct sms_srvm_signal_status {
+ u32 result;
+ u32 snr;
+ u32 ts_packets;
+ u32 ets_packets;
+ u32 constellation;
+ u32 hp_code;
+ u32 tps_srv_ind_lp;
+ u32 tps_srv_ind_hp;
+ u32 cell_id;
+ u32 reason;
+
+ s32 in_band_power;
+ u32 request_id;
+};
+
+struct sms_i2c_req {
+ u32 device_address; /* I2c device address */
+ u32 write_count; /* number of bytes to write */
+ u32 read_count; /* number of bytes to read */
+ u8 Data[1];
+};
+
+struct sms_i2c_res {
+ u32 status; /* non-zero value in case of failure */
+ u32 read_count; /* number of bytes read */
+ u8 Data[1];
+};
+
+
+struct smscore_config_gpio {
+#define SMS_GPIO_DIRECTION_INPUT 0
+#define SMS_GPIO_DIRECTION_OUTPUT 1
+ u8 direction;
+
+#define SMS_GPIO_PULLUPDOWN_NONE 0
+#define SMS_GPIO_PULLUPDOWN_PULLDOWN 1
+#define SMS_GPIO_PULLUPDOWN_PULLUP 2
+#define SMS_GPIO_PULLUPDOWN_KEEPER 3
+ u8 pullupdown;
+
+#define SMS_GPIO_INPUTCHARACTERISTICS_NORMAL 0
+#define SMS_GPIO_INPUTCHARACTERISTICS_SCHMITT 1
+ u8 inputcharacteristics;
+
+ /* 10xx */
+#define SMS_GPIO_OUTPUT_SLEW_RATE_FAST 0
+#define SMS_GPIO_OUTPUT_SLEW_WRATE_SLOW 1
+
+ /* 11xx */
+#define SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS 0
+#define SMS_GPIO_OUTPUT_SLEW_RATE_0_9_V_NS 1
+#define SMS_GPIO_OUTPUT_SLEW_RATE_1_7_V_NS 2
+#define SMS_GPIO_OUTPUT_SLEW_RATE_3_3_V_NS 3
+
+ u8 outputslewrate;
+
+ /* 10xx */
+#define SMS_GPIO_OUTPUTDRIVING_S_4mA 0
+#define SMS_GPIO_OUTPUTDRIVING_S_8mA 1
+#define SMS_GPIO_OUTPUTDRIVING_S_12mA 2
+#define SMS_GPIO_OUTPUTDRIVING_S_16mA 3
+
+ /* 11xx*/
+#define SMS_GPIO_OUTPUTDRIVING_1_5mA 0
+#define SMS_GPIO_OUTPUTDRIVING_2_8mA 1
+#define SMS_GPIO_OUTPUTDRIVING_4mA 2
+#define SMS_GPIO_OUTPUTDRIVING_7mA 3
+#define SMS_GPIO_OUTPUTDRIVING_10mA 4
+#define SMS_GPIO_OUTPUTDRIVING_11mA 5
+#define SMS_GPIO_OUTPUTDRIVING_14mA 6
+#define SMS_GPIO_OUTPUTDRIVING_16mA 7
+
+ u8 outputdriving;
+};
+
+char *smscore_translate_msg(enum msg_types msgtype);
+
+extern int smscore_registry_getmode(char *devpath);
+
+extern int smscore_register_hotplug(hotplug_t hotplug);
+extern void smscore_unregister_hotplug(hotplug_t hotplug);
+
+extern int smscore_register_device(struct smsdevice_params_t *params,
+ struct smscore_device_t **coredev,
+ void *mdev);
+extern void smscore_unregister_device(struct smscore_device_t *coredev);
+
+extern int smscore_start_device(struct smscore_device_t *coredev);
+extern int smscore_load_firmware(struct smscore_device_t *coredev,
+ char *filename,
+ loadfirmware_t loadfirmware_handler);
+
+extern int smscore_set_device_mode(struct smscore_device_t *coredev, int mode);
+extern int smscore_get_device_mode(struct smscore_device_t *coredev);
+
+extern int smscore_register_client(struct smscore_device_t *coredev,
+ struct smsclient_params_t *params,
+ struct smscore_client_t **client);
+extern void smscore_unregister_client(struct smscore_client_t *client);
+
+extern int smsclient_sendrequest(struct smscore_client_t *client,
+ void *buffer, size_t size);
+extern void smscore_onresponse(struct smscore_device_t *coredev,
+ struct smscore_buffer_t *cb);
+
+extern int smscore_get_common_buffer_size(struct smscore_device_t *coredev);
+extern int smscore_map_common_buffer(struct smscore_device_t *coredev,
+ struct vm_area_struct *vma);
+extern int smscore_send_fw_file(struct smscore_device_t *coredev,
+ u8 *ufwbuf, int size);
+
+extern
+struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev);
+extern void smscore_putbuffer(struct smscore_device_t *coredev,
+ struct smscore_buffer_t *cb);
+
+/* old GPIO management */
+int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
+ struct smscore_config_gpio *pinconfig);
+int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level);
+
+/* new GPIO management */
+extern int smscore_gpio_configure(struct smscore_device_t *coredev, u8 pin_num,
+ struct smscore_config_gpio *p_gpio_config);
+extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 pin_num,
+ u8 new_level);
+extern int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num,
+ u8 *level);
+
+void smscore_set_board_id(struct smscore_device_t *core, int id);
+int smscore_get_board_id(struct smscore_device_t *core);
+
+int smscore_led_state(struct smscore_device_t *core, int led);
+
+
+/* ------------------------------------------------------------------------ */
+
+#endif /* __SMS_CORE_API_H__ */
diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c
new file mode 100644
index 000000000..1a8677ade
--- /dev/null
+++ b/drivers/media/common/siano/smsdvb-debugfs.c
@@ -0,0 +1,549 @@
+/***********************************************************************
+ *
+ * Copyright(c) 2013 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.
+
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ ***********************************************************************/
+
+#include "smscoreapi.h"
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+
+#include "smsdvb.h"
+
+static struct dentry *smsdvb_debugfs_usb_root;
+
+struct smsdvb_debugfs {
+ struct kref refcount;
+ spinlock_t lock;
+
+ char stats_data[PAGE_SIZE];
+ unsigned stats_count;
+ bool stats_was_read;
+
+ wait_queue_head_t stats_queue;
+};
+
+static void smsdvb_print_dvb_stats(struct smsdvb_debugfs *debug_data,
+ struct sms_stats *p)
+{
+ int n = 0;
+ char *buf;
+
+ spin_lock(&debug_data->lock);
+ if (debug_data->stats_count) {
+ spin_unlock(&debug_data->lock);
+ return;
+ }
+
+ buf = debug_data->stats_data;
+
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "is_rf_locked = %d\n", p->is_rf_locked);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "is_demod_locked = %d\n", p->is_demod_locked);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "is_external_lna_on = %d\n", p->is_external_lna_on);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "SNR = %d\n", p->SNR);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "ber = %d\n", p->ber);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "FIB_CRC = %d\n", p->FIB_CRC);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "ts_per = %d\n", p->ts_per);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "MFER = %d\n", p->MFER);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "RSSI = %d\n", p->RSSI);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "in_band_pwr = %d\n", p->in_band_pwr);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "carrier_offset = %d\n", p->carrier_offset);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "modem_state = %d\n", p->modem_state);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "frequency = %d\n", p->frequency);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "bandwidth = %d\n", p->bandwidth);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "transmission_mode = %d\n", p->transmission_mode);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "modem_state = %d\n", p->modem_state);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "guard_interval = %d\n", p->guard_interval);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "code_rate = %d\n", p->code_rate);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "lp_code_rate = %d\n", p->lp_code_rate);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "hierarchy = %d\n", p->hierarchy);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "constellation = %d\n", p->constellation);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "burst_size = %d\n", p->burst_size);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "burst_duration = %d\n", p->burst_duration);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "burst_cycle_time = %d\n", p->burst_cycle_time);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "calc_burst_cycle_time = %d\n",
+ p->calc_burst_cycle_time);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "num_of_rows = %d\n", p->num_of_rows);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "num_of_padd_cols = %d\n", p->num_of_padd_cols);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "num_of_punct_cols = %d\n", p->num_of_punct_cols);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "error_ts_packets = %d\n", p->error_ts_packets);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "total_ts_packets = %d\n", p->total_ts_packets);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "num_of_valid_mpe_tlbs = %d\n", p->num_of_valid_mpe_tlbs);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "num_of_invalid_mpe_tlbs = %d\n", p->num_of_invalid_mpe_tlbs);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "num_of_corrected_mpe_tlbs = %d\n", p->num_of_corrected_mpe_tlbs);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "ber_error_count = %d\n", p->ber_error_count);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "ber_bit_count = %d\n", p->ber_bit_count);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "pre_ber = %d\n", p->pre_ber);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "cell_id = %d\n", p->cell_id);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "dvbh_srv_ind_hp = %d\n", p->dvbh_srv_ind_hp);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "dvbh_srv_ind_lp = %d\n", p->dvbh_srv_ind_lp);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "num_mpe_received = %d\n", p->num_mpe_received);
+
+ debug_data->stats_count = n;
+ spin_unlock(&debug_data->lock);
+ wake_up(&debug_data->stats_queue);
+}
+
+static void smsdvb_print_isdb_stats(struct smsdvb_debugfs *debug_data,
+ struct sms_isdbt_stats *p)
+{
+ int i, n = 0;
+ char *buf;
+
+ spin_lock(&debug_data->lock);
+ if (debug_data->stats_count) {
+ spin_unlock(&debug_data->lock);
+ return;
+ }
+
+ buf = debug_data->stats_data;
+
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "statistics_type = %d\t", p->statistics_type);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "full_size = %d\n", p->full_size);
+
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "is_rf_locked = %d\t\t", p->is_rf_locked);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "is_demod_locked = %d\t", p->is_demod_locked);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "is_external_lna_on = %d\n", p->is_external_lna_on);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "SNR = %d dB\t\t", p->SNR);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "RSSI = %d dBm\t\t", p->RSSI);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "in_band_pwr = %d dBm\n", p->in_band_pwr);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "carrier_offset = %d\t", p->carrier_offset);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "bandwidth = %d\t\t", p->bandwidth);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "frequency = %d Hz\n", p->frequency);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "transmission_mode = %d\t", p->transmission_mode);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "modem_state = %d\t\t", p->modem_state);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "guard_interval = %d\n", p->guard_interval);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "system_type = %d\t\t", p->system_type);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "partial_reception = %d\t", p->partial_reception);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "num_of_layers = %d\n", p->num_of_layers);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors);
+
+ for (i = 0; i < 3; i++) {
+ if (p->layer_info[i].number_of_segments < 1 ||
+ p->layer_info[i].number_of_segments > 13)
+ continue;
+
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t",
+ p->layer_info[i].code_rate);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n",
+ p->layer_info[i].constellation);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t",
+ p->layer_info[i].ber);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tber_error_count = %-5d\t",
+ p->layer_info[i].ber_error_count);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n",
+ p->layer_info[i].ber_bit_count);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t",
+ p->layer_info[i].pre_ber);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n",
+ p->layer_info[i].ts_per);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\terror_ts_packets = %-5d\t",
+ p->layer_info[i].error_ts_packets);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "total_ts_packets = %-5d\t",
+ p->layer_info[i].total_ts_packets);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n",
+ p->layer_info[i].ti_ldepth_i);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "\tnumber_of_segments = %d\t",
+ p->layer_info[i].number_of_segments);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n",
+ p->layer_info[i].tmcc_errors);
+ }
+
+ debug_data->stats_count = n;
+ spin_unlock(&debug_data->lock);
+ wake_up(&debug_data->stats_queue);
+}
+
+static void smsdvb_print_isdb_stats_ex(struct smsdvb_debugfs *debug_data,
+ struct sms_isdbt_stats_ex *p)
+{
+ int i, n = 0;
+ char *buf;
+
+ spin_lock(&debug_data->lock);
+ if (debug_data->stats_count) {
+ spin_unlock(&debug_data->lock);
+ return;
+ }
+
+ buf = debug_data->stats_data;
+
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "statistics_type = %d\t", p->statistics_type);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "full_size = %d\n", p->full_size);
+
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "is_rf_locked = %d\t\t", p->is_rf_locked);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "is_demod_locked = %d\t", p->is_demod_locked);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "is_external_lna_on = %d\n", p->is_external_lna_on);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "SNR = %d dB\t\t", p->SNR);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "RSSI = %d dBm\t\t", p->RSSI);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "in_band_pwr = %d dBm\n", p->in_band_pwr);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "carrier_offset = %d\t", p->carrier_offset);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "bandwidth = %d\t\t", p->bandwidth);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "frequency = %d Hz\n", p->frequency);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "transmission_mode = %d\t", p->transmission_mode);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "modem_state = %d\t\t", p->modem_state);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "guard_interval = %d\n", p->guard_interval);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "system_type = %d\t\t", p->system_type);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "partial_reception = %d\t", p->partial_reception);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "num_of_layers = %d\n", p->num_of_layers);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "segment_number = %d\t",
+ p->segment_number);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "tune_bw = %d\n",
+ p->tune_bw);
+
+ for (i = 0; i < 3; i++) {
+ if (p->layer_info[i].number_of_segments < 1 ||
+ p->layer_info[i].number_of_segments > 13)
+ continue;
+
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t",
+ p->layer_info[i].code_rate);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n",
+ p->layer_info[i].constellation);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t",
+ p->layer_info[i].ber);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tber_error_count = %-5d\t",
+ p->layer_info[i].ber_error_count);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n",
+ p->layer_info[i].ber_bit_count);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t",
+ p->layer_info[i].pre_ber);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n",
+ p->layer_info[i].ts_per);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "\terror_ts_packets = %-5d\t",
+ p->layer_info[i].error_ts_packets);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "total_ts_packets = %-5d\t",
+ p->layer_info[i].total_ts_packets);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n",
+ p->layer_info[i].ti_ldepth_i);
+ n += snprintf(&buf[n], PAGE_SIZE - n,
+ "\tnumber_of_segments = %d\t",
+ p->layer_info[i].number_of_segments);
+ n += snprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n",
+ p->layer_info[i].tmcc_errors);
+ }
+
+
+ debug_data->stats_count = n;
+ spin_unlock(&debug_data->lock);
+
+ wake_up(&debug_data->stats_queue);
+}
+
+static int smsdvb_stats_open(struct inode *inode, struct file *file)
+{
+ struct smsdvb_client_t *client = inode->i_private;
+ struct smsdvb_debugfs *debug_data = client->debug_data;
+
+ kref_get(&debug_data->refcount);
+
+ spin_lock(&debug_data->lock);
+ debug_data->stats_count = 0;
+ debug_data->stats_was_read = false;
+ spin_unlock(&debug_data->lock);
+
+ file->private_data = debug_data;
+
+ return 0;
+}
+
+static void smsdvb_debugfs_data_release(struct kref *ref)
+{
+ struct smsdvb_debugfs *debug_data;
+
+ debug_data = container_of(ref, struct smsdvb_debugfs, refcount);
+ kfree(debug_data);
+}
+
+static int smsdvb_stats_wait_read(struct smsdvb_debugfs *debug_data)
+{
+ int rc = 1;
+
+ spin_lock(&debug_data->lock);
+
+ if (debug_data->stats_was_read)
+ goto exit;
+
+ rc = debug_data->stats_count;
+
+exit:
+ spin_unlock(&debug_data->lock);
+ return rc;
+}
+
+static unsigned int smsdvb_stats_poll(struct file *file, poll_table *wait)
+{
+ struct smsdvb_debugfs *debug_data = file->private_data;
+ int rc;
+
+ kref_get(&debug_data->refcount);
+
+ poll_wait(file, &debug_data->stats_queue, wait);
+
+ rc = smsdvb_stats_wait_read(debug_data);
+ if (rc > 0)
+ rc = POLLIN | POLLRDNORM;
+
+ kref_put(&debug_data->refcount, smsdvb_debugfs_data_release);
+
+ return rc;
+}
+
+static ssize_t smsdvb_stats_read(struct file *file, char __user *user_buf,
+ size_t nbytes, loff_t *ppos)
+{
+ int rc = 0, len;
+ struct smsdvb_debugfs *debug_data = file->private_data;
+
+ kref_get(&debug_data->refcount);
+
+ if (file->f_flags & O_NONBLOCK) {
+ rc = smsdvb_stats_wait_read(debug_data);
+ if (!rc) {
+ rc = -EWOULDBLOCK;
+ goto ret;
+ }
+ } else {
+ rc = wait_event_interruptible(debug_data->stats_queue,
+ smsdvb_stats_wait_read(debug_data));
+ if (rc < 0)
+ goto ret;
+ }
+
+ if (debug_data->stats_was_read) {
+ rc = 0; /* EOF */
+ goto ret;
+ }
+
+ len = debug_data->stats_count - *ppos;
+ if (len >= 0)
+ rc = simple_read_from_buffer(user_buf, nbytes, ppos,
+ debug_data->stats_data, len);
+ else
+ rc = 0;
+
+ if (*ppos >= debug_data->stats_count) {
+ spin_lock(&debug_data->lock);
+ debug_data->stats_was_read = true;
+ spin_unlock(&debug_data->lock);
+ }
+ret:
+ kref_put(&debug_data->refcount, smsdvb_debugfs_data_release);
+ return rc;
+}
+
+static int smsdvb_stats_release(struct inode *inode, struct file *file)
+{
+ struct smsdvb_debugfs *debug_data = file->private_data;
+
+ spin_lock(&debug_data->lock);
+ debug_data->stats_was_read = true; /* return EOF to read() */
+ spin_unlock(&debug_data->lock);
+ wake_up_interruptible_sync(&debug_data->stats_queue);
+
+ kref_put(&debug_data->refcount, smsdvb_debugfs_data_release);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static const struct file_operations debugfs_stats_ops = {
+ .open = smsdvb_stats_open,
+ .poll = smsdvb_stats_poll,
+ .read = smsdvb_stats_read,
+ .release = smsdvb_stats_release,
+ .llseek = generic_file_llseek,
+};
+
+/*
+ * Functions used by smsdvb, in order to create the interfaces
+ */
+
+int smsdvb_debugfs_create(struct smsdvb_client_t *client)
+{
+ struct smscore_device_t *coredev = client->coredev;
+ struct dentry *d;
+ struct smsdvb_debugfs *debug_data;
+
+ if (!smsdvb_debugfs_usb_root || !coredev->is_usb_device)
+ return -ENODEV;
+
+ client->debugfs = debugfs_create_dir(coredev->devpath,
+ smsdvb_debugfs_usb_root);
+ if (IS_ERR_OR_NULL(client->debugfs)) {
+ pr_info("Unable to create debugfs %s directory.\n",
+ coredev->devpath);
+ return -ENODEV;
+ }
+
+ d = debugfs_create_file("stats", S_IRUGO | S_IWUSR, client->debugfs,
+ client, &debugfs_stats_ops);
+ if (!d) {
+ debugfs_remove(client->debugfs);
+ return -ENOMEM;
+ }
+
+ debug_data = kzalloc(sizeof(*client->debug_data), GFP_KERNEL);
+ if (!debug_data)
+ return -ENOMEM;
+
+ client->debug_data = debug_data;
+ client->prt_dvb_stats = smsdvb_print_dvb_stats;
+ client->prt_isdb_stats = smsdvb_print_isdb_stats;
+ client->prt_isdb_stats_ex = smsdvb_print_isdb_stats_ex;
+
+ init_waitqueue_head(&debug_data->stats_queue);
+ spin_lock_init(&debug_data->lock);
+ kref_init(&debug_data->refcount);
+
+ return 0;
+}
+
+void smsdvb_debugfs_release(struct smsdvb_client_t *client)
+{
+ if (!client->debugfs)
+ return;
+
+ client->prt_dvb_stats = NULL;
+ client->prt_isdb_stats = NULL;
+ client->prt_isdb_stats_ex = NULL;
+
+ debugfs_remove_recursive(client->debugfs);
+ kref_put(&client->debug_data->refcount, smsdvb_debugfs_data_release);
+
+ client->debug_data = NULL;
+ client->debugfs = NULL;
+}
+
+int smsdvb_debugfs_register(void)
+{
+ struct dentry *d;
+
+ /*
+ * FIXME: This was written to debug Siano USB devices. So, it creates
+ * the debugfs node under <debugfs>/usb.
+ * A similar logic would be needed for Siano sdio devices, but, in that
+ * case, usb_debug_root is not a good choice.
+ *
+ * Perhaps the right fix here would be to create another sysfs root
+ * node for sdio-based boards, but this may need some logic at sdio
+ * subsystem.
+ */
+ d = debugfs_create_dir("smsdvb", usb_debug_root);
+ if (IS_ERR_OR_NULL(d)) {
+ pr_err("Couldn't create sysfs node for smsdvb\n");
+ return PTR_ERR(d);
+ } else {
+ smsdvb_debugfs_usb_root = d;
+ }
+ return 0;
+}
+
+void smsdvb_debugfs_unregister(void)
+{
+ debugfs_remove_recursive(smsdvb_debugfs_usb_root);
+ smsdvb_debugfs_usb_root = NULL;
+}
diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c
new file mode 100644
index 000000000..367b8e77f
--- /dev/null
+++ b/drivers/media/common/siano/smsdvb-main.c
@@ -0,0 +1,1244 @@
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2008, Uri Shkolnik
+
+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, see <http://www.gnu.org/licenses/>.
+
+****************************************************************/
+
+#include "smscoreapi.h"
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/div64.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+
+#include "sms-cards.h"
+
+#include "smsdvb.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static struct list_head g_smsdvb_clients;
+static struct mutex g_smsdvb_clientslock;
+
+static u32 sms_to_guard_interval_table[] = {
+ [0] = GUARD_INTERVAL_1_32,
+ [1] = GUARD_INTERVAL_1_16,
+ [2] = GUARD_INTERVAL_1_8,
+ [3] = GUARD_INTERVAL_1_4,
+};
+
+static u32 sms_to_code_rate_table[] = {
+ [0] = FEC_1_2,
+ [1] = FEC_2_3,
+ [2] = FEC_3_4,
+ [3] = FEC_5_6,
+ [4] = FEC_7_8,
+};
+
+
+static u32 sms_to_hierarchy_table[] = {
+ [0] = HIERARCHY_NONE,
+ [1] = HIERARCHY_1,
+ [2] = HIERARCHY_2,
+ [3] = HIERARCHY_4,
+};
+
+static u32 sms_to_modulation_table[] = {
+ [0] = QPSK,
+ [1] = QAM_16,
+ [2] = QAM_64,
+ [3] = DQPSK,
+};
+
+
+/* Events that may come from DVB v3 adapter */
+static void sms_board_dvb3_event(struct smsdvb_client_t *client,
+ enum SMS_DVB3_EVENTS event) {
+
+ struct smscore_device_t *coredev = client->coredev;
+ switch (event) {
+ case DVB3_EVENT_INIT:
+ pr_debug("DVB3_EVENT_INIT\n");
+ sms_board_event(coredev, BOARD_EVENT_BIND);
+ break;
+ case DVB3_EVENT_SLEEP:
+ pr_debug("DVB3_EVENT_SLEEP\n");
+ sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND);
+ break;
+ case DVB3_EVENT_HOTPLUG:
+ pr_debug("DVB3_EVENT_HOTPLUG\n");
+ sms_board_event(coredev, BOARD_EVENT_POWER_INIT);
+ break;
+ case DVB3_EVENT_FE_LOCK:
+ if (client->event_fe_state != DVB3_EVENT_FE_LOCK) {
+ client->event_fe_state = DVB3_EVENT_FE_LOCK;
+ pr_debug("DVB3_EVENT_FE_LOCK\n");
+ sms_board_event(coredev, BOARD_EVENT_FE_LOCK);
+ }
+ break;
+ case DVB3_EVENT_FE_UNLOCK:
+ if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) {
+ client->event_fe_state = DVB3_EVENT_FE_UNLOCK;
+ pr_debug("DVB3_EVENT_FE_UNLOCK\n");
+ sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK);
+ }
+ break;
+ case DVB3_EVENT_UNC_OK:
+ if (client->event_unc_state != DVB3_EVENT_UNC_OK) {
+ client->event_unc_state = DVB3_EVENT_UNC_OK;
+ pr_debug("DVB3_EVENT_UNC_OK\n");
+ sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK);
+ }
+ break;
+ case DVB3_EVENT_UNC_ERR:
+ if (client->event_unc_state != DVB3_EVENT_UNC_ERR) {
+ client->event_unc_state = DVB3_EVENT_UNC_ERR;
+ pr_debug("DVB3_EVENT_UNC_ERR\n");
+ sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS);
+ }
+ break;
+
+ default:
+ pr_err("Unknown dvb3 api event\n");
+ break;
+ }
+}
+
+static void smsdvb_stats_not_ready(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+ struct smscore_device_t *coredev = client->coredev;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int i, n_layers;
+
+ switch (smscore_get_device_mode(coredev)) {
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ n_layers = 4;
+ break;
+ default:
+ n_layers = 1;
+ }
+
+ /* Global stats */
+ c->strength.len = 1;
+ c->cnr.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+ /* Per-layer stats */
+ c->post_bit_error.len = n_layers;
+ c->post_bit_count.len = n_layers;
+ c->block_error.len = n_layers;
+ c->block_count.len = n_layers;
+
+ /*
+ * Put all of them at FE_SCALE_NOT_AVAILABLE. They're dynamically
+ * changed when the stats become available.
+ */
+ for (i = 0; i < n_layers; i++) {
+ c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+}
+
+static inline int sms_to_mode(u32 mode)
+{
+ switch (mode) {
+ case 2:
+ return TRANSMISSION_MODE_2K;
+ case 4:
+ return TRANSMISSION_MODE_4K;
+ case 8:
+ return TRANSMISSION_MODE_8K;
+ }
+ return TRANSMISSION_MODE_AUTO;
+}
+
+static inline int sms_to_status(u32 is_demod_locked, u32 is_rf_locked)
+{
+ if (is_demod_locked)
+ return FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_LOCK;
+
+ if (is_rf_locked)
+ return FE_HAS_SIGNAL | FE_HAS_CARRIER;
+
+ return 0;
+}
+
+static inline u32 sms_to_bw(u32 value)
+{
+ return value * 1000000;
+}
+
+#define convert_from_table(value, table, defval) ({ \
+ u32 __ret; \
+ if (value < ARRAY_SIZE(table)) \
+ __ret = table[value]; \
+ else \
+ __ret = defval; \
+ __ret; \
+})
+
+#define sms_to_guard_interval(value) \
+ convert_from_table(value, sms_to_guard_interval_table, \
+ GUARD_INTERVAL_AUTO);
+
+#define sms_to_code_rate(value) \
+ convert_from_table(value, sms_to_code_rate_table, \
+ FEC_NONE);
+
+#define sms_to_hierarchy(value) \
+ convert_from_table(value, sms_to_hierarchy_table, \
+ FEC_NONE);
+
+#define sms_to_modulation(value) \
+ convert_from_table(value, sms_to_modulation_table, \
+ FEC_NONE);
+
+static void smsdvb_update_tx_params(struct smsdvb_client_t *client,
+ struct sms_tx_stats *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ c->frequency = p->frequency;
+ client->fe_status = sms_to_status(p->is_demod_locked, 0);
+ c->bandwidth_hz = sms_to_bw(p->bandwidth);
+ c->transmission_mode = sms_to_mode(p->transmission_mode);
+ c->guard_interval = sms_to_guard_interval(p->guard_interval);
+ c->code_rate_HP = sms_to_code_rate(p->code_rate);
+ c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);
+ c->hierarchy = sms_to_hierarchy(p->hierarchy);
+ c->modulation = sms_to_modulation(p->constellation);
+}
+
+static void smsdvb_update_per_slices(struct smsdvb_client_t *client,
+ struct RECEPTION_STATISTICS_PER_SLICES_S *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u64 tmp;
+
+ client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
+ c->modulation = sms_to_modulation(p->constellation);
+
+ /* signal Strength, in DBm */
+ c->strength.stat[0].uvalue = p->in_band_power * 1000;
+
+ /* Carrier to noise ratio, in DB */
+ c->cnr.stat[0].svalue = p->snr * 1000;
+
+ /* PER/BER requires demod lock */
+ if (!p->is_demod_locked)
+ return;
+
+ /* TS PER */
+ client->last_per = c->block_error.stat[0].uvalue;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += p->ets_packets;
+ c->block_count.stat[0].uvalue += p->ets_packets + p->ts_packets;
+
+ /* ber */
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += p->ber_error_count;
+ c->post_bit_count.stat[0].uvalue += p->ber_bit_count;
+
+ /* Legacy PER/BER */
+ tmp = p->ets_packets * 65535;
+ if (p->ts_packets + p->ets_packets)
+ do_div(tmp, p->ts_packets + p->ets_packets);
+ client->legacy_per = tmp;
+}
+
+static void smsdvb_update_dvb_stats(struct smsdvb_client_t *client,
+ struct sms_stats *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (client->prt_dvb_stats)
+ client->prt_dvb_stats(client->debug_data, p);
+
+ client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
+
+ /* Update DVB modulation parameters */
+ c->frequency = p->frequency;
+ client->fe_status = sms_to_status(p->is_demod_locked, 0);
+ c->bandwidth_hz = sms_to_bw(p->bandwidth);
+ c->transmission_mode = sms_to_mode(p->transmission_mode);
+ c->guard_interval = sms_to_guard_interval(p->guard_interval);
+ c->code_rate_HP = sms_to_code_rate(p->code_rate);
+ c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);
+ c->hierarchy = sms_to_hierarchy(p->hierarchy);
+ c->modulation = sms_to_modulation(p->constellation);
+
+ /* update reception data */
+ c->lna = p->is_external_lna_on ? 1 : 0;
+
+ /* Carrier to noise ratio, in DB */
+ c->cnr.stat[0].svalue = p->SNR * 1000;
+
+ /* signal Strength, in DBm */
+ c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
+
+ /* PER/BER requires demod lock */
+ if (!p->is_demod_locked)
+ return;
+
+ /* TS PER */
+ client->last_per = c->block_error.stat[0].uvalue;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += p->error_ts_packets;
+ c->block_count.stat[0].uvalue += p->total_ts_packets;
+
+ /* ber */
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += p->ber_error_count;
+ c->post_bit_count.stat[0].uvalue += p->ber_bit_count;
+
+ /* Legacy PER/BER */
+ client->legacy_ber = p->ber;
+};
+
+static void smsdvb_update_isdbt_stats(struct smsdvb_client_t *client,
+ struct sms_isdbt_stats *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct sms_isdbt_layer_stats *lr;
+ int i, n_layers;
+
+ if (client->prt_isdb_stats)
+ client->prt_isdb_stats(client->debug_data, p);
+
+ client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
+
+ /*
+ * Firmware 2.1 seems to report only lock status and
+ * signal strength. The signal strength indicator is at the
+ * wrong field.
+ */
+ if (p->statistics_type == 0) {
+ c->strength.stat[0].uvalue = ((s32)p->transmission_mode) * 1000;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return;
+ }
+
+ /* Update ISDB-T transmission parameters */
+ c->frequency = p->frequency;
+ c->bandwidth_hz = sms_to_bw(p->bandwidth);
+ c->transmission_mode = sms_to_mode(p->transmission_mode);
+ c->guard_interval = sms_to_guard_interval(p->guard_interval);
+ c->isdbt_partial_reception = p->partial_reception ? 1 : 0;
+ n_layers = p->num_of_layers;
+ if (n_layers < 1)
+ n_layers = 1;
+ if (n_layers > 3)
+ n_layers = 3;
+ c->isdbt_layer_enabled = 0;
+
+ /* update reception data */
+ c->lna = p->is_external_lna_on ? 1 : 0;
+
+ /* Carrier to noise ratio, in DB */
+ c->cnr.stat[0].svalue = p->SNR * 1000;
+
+ /* signal Strength, in DBm */
+ c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
+
+ /* PER/BER and per-layer stats require demod lock */
+ if (!p->is_demod_locked)
+ return;
+
+ client->last_per = c->block_error.stat[0].uvalue;
+
+ /* Clears global counters, as the code below will sum it again */
+ c->block_error.stat[0].uvalue = 0;
+ c->block_count.stat[0].uvalue = 0;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = 0;
+ c->post_bit_count.stat[0].uvalue = 0;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+
+ for (i = 0; i < n_layers; i++) {
+ lr = &p->layer_info[i];
+
+ /* Update per-layer transmission parameters */
+ if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {
+ c->isdbt_layer_enabled |= 1 << i;
+ c->layer[i].segment_count = lr->number_of_segments;
+ } else {
+ continue;
+ }
+ c->layer[i].modulation = sms_to_modulation(lr->constellation);
+
+ /* TS PER */
+ c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;
+ c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;
+
+ /* Update global PER counter */
+ c->block_error.stat[0].uvalue += lr->error_ts_packets;
+ c->block_count.stat[0].uvalue += lr->total_ts_packets;
+
+ /* BER */
+ c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;
+ c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;
+
+ /* Update global BER counter */
+ c->post_bit_error.stat[0].uvalue += lr->ber_error_count;
+ c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;
+ }
+}
+
+static void smsdvb_update_isdbt_stats_ex(struct smsdvb_client_t *client,
+ struct sms_isdbt_stats_ex *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct sms_isdbt_layer_stats *lr;
+ int i, n_layers;
+
+ if (client->prt_isdb_stats_ex)
+ client->prt_isdb_stats_ex(client->debug_data, p);
+
+ /* Update ISDB-T transmission parameters */
+ c->frequency = p->frequency;
+ client->fe_status = sms_to_status(p->is_demod_locked, 0);
+ c->bandwidth_hz = sms_to_bw(p->bandwidth);
+ c->transmission_mode = sms_to_mode(p->transmission_mode);
+ c->guard_interval = sms_to_guard_interval(p->guard_interval);
+ c->isdbt_partial_reception = p->partial_reception ? 1 : 0;
+ n_layers = p->num_of_layers;
+ if (n_layers < 1)
+ n_layers = 1;
+ if (n_layers > 3)
+ n_layers = 3;
+ c->isdbt_layer_enabled = 0;
+
+ /* update reception data */
+ c->lna = p->is_external_lna_on ? 1 : 0;
+
+ /* Carrier to noise ratio, in DB */
+ c->cnr.stat[0].svalue = p->SNR * 1000;
+
+ /* signal Strength, in DBm */
+ c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
+
+ /* PER/BER and per-layer stats require demod lock */
+ if (!p->is_demod_locked)
+ return;
+
+ client->last_per = c->block_error.stat[0].uvalue;
+
+ /* Clears global counters, as the code below will sum it again */
+ c->block_error.stat[0].uvalue = 0;
+ c->block_count.stat[0].uvalue = 0;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = 0;
+ c->post_bit_count.stat[0].uvalue = 0;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+
+ c->post_bit_error.len = n_layers + 1;
+ c->post_bit_count.len = n_layers + 1;
+ c->block_error.len = n_layers + 1;
+ c->block_count.len = n_layers + 1;
+ for (i = 0; i < n_layers; i++) {
+ lr = &p->layer_info[i];
+
+ /* Update per-layer transmission parameters */
+ if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {
+ c->isdbt_layer_enabled |= 1 << i;
+ c->layer[i].segment_count = lr->number_of_segments;
+ } else {
+ continue;
+ }
+ c->layer[i].modulation = sms_to_modulation(lr->constellation);
+
+ /* TS PER */
+ c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;
+ c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;
+
+ /* Update global PER counter */
+ c->block_error.stat[0].uvalue += lr->error_ts_packets;
+ c->block_count.stat[0].uvalue += lr->total_ts_packets;
+
+ /* ber */
+ c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;
+ c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;
+
+ /* Update global ber counter */
+ c->post_bit_error.stat[0].uvalue += lr->ber_error_count;
+ c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;
+ }
+}
+
+static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
+{
+ struct smsdvb_client_t *client = (struct smsdvb_client_t *) context;
+ struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) (((u8 *) cb->p)
+ + cb->offset);
+ void *p = phdr + 1;
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ bool is_status_update = false;
+
+ switch (phdr->msg_type) {
+ case MSG_SMS_DVBT_BDA_DATA:
+ /*
+ * Only feed data to dvb demux if are there any feed listening
+ * to it and if the device has tuned
+ */
+ if (client->feed_users && client->has_tuned)
+ dvb_dmx_swfilter(&client->demux, p,
+ cb->size - sizeof(struct sms_msg_hdr));
+ break;
+
+ case MSG_SMS_RF_TUNE_RES:
+ case MSG_SMS_ISDBT_TUNE_RES:
+ complete(&client->tune_done);
+ break;
+
+ case MSG_SMS_SIGNAL_DETECTED_IND:
+ client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+
+ is_status_update = true;
+ break;
+
+ case MSG_SMS_NO_SIGNAL_IND:
+ client->fe_status = 0;
+
+ is_status_update = true;
+ break;
+
+ case MSG_SMS_TRANSMISSION_IND:
+ smsdvb_update_tx_params(client, p);
+
+ is_status_update = true;
+ break;
+
+ case MSG_SMS_HO_PER_SLICES_IND:
+ smsdvb_update_per_slices(client, p);
+
+ is_status_update = true;
+ break;
+
+ case MSG_SMS_GET_STATISTICS_RES:
+ switch (smscore_get_device_mode(client->coredev)) {
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ smsdvb_update_isdbt_stats(client, p);
+ break;
+ default:
+ /* Skip sms_msg_statistics_info:request_result field */
+ smsdvb_update_dvb_stats(client, p + sizeof(u32));
+ }
+
+ is_status_update = true;
+ break;
+
+ /* Only for ISDB-T */
+ case MSG_SMS_GET_STATISTICS_EX_RES:
+ /* Skip sms_msg_statistics_info:request_result field? */
+ smsdvb_update_isdbt_stats_ex(client, p + sizeof(u32));
+ is_status_update = true;
+ break;
+ default:
+ pr_debug("message not handled\n");
+ }
+ smscore_putbuffer(client->coredev, cb);
+
+ if (is_status_update) {
+ if (client->fe_status & FE_HAS_LOCK) {
+ sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK);
+ if (client->last_per == c->block_error.stat[0].uvalue)
+ sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK);
+ else
+ sms_board_dvb3_event(client, DVB3_EVENT_UNC_ERR);
+ client->has_tuned = true;
+ } else {
+ smsdvb_stats_not_ready(fe);
+ client->has_tuned = false;
+ sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK);
+ }
+ complete(&client->stats_done);
+ }
+
+ return 0;
+}
+
+static void smsdvb_media_device_unregister(struct smsdvb_client_t *client)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ struct smscore_device_t *coredev = client->coredev;
+
+ if (!coredev->media_dev)
+ return;
+ media_device_unregister(coredev->media_dev);
+ kfree(coredev->media_dev);
+ coredev->media_dev = NULL;
+#endif
+}
+
+static void smsdvb_unregister_client(struct smsdvb_client_t *client)
+{
+ /* must be called under clientslock */
+
+ list_del(&client->entry);
+
+ smsdvb_debugfs_release(client);
+ smscore_unregister_client(client->smsclient);
+ dvb_unregister_frontend(&client->frontend);
+ dvb_dmxdev_release(&client->dmxdev);
+ dvb_dmx_release(&client->demux);
+ smsdvb_media_device_unregister(client);
+ dvb_unregister_adapter(&client->adapter);
+ kfree(client);
+}
+
+static void smsdvb_onremove(void *context)
+{
+ kmutex_lock(&g_smsdvb_clientslock);
+
+ smsdvb_unregister_client((struct smsdvb_client_t *) context);
+
+ kmutex_unlock(&g_smsdvb_clientslock);
+}
+
+static int smsdvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct smsdvb_client_t *client =
+ container_of(feed->demux, struct smsdvb_client_t, demux);
+ struct sms_msg_data pid_msg;
+
+ pr_debug("add pid %d(%x)\n",
+ feed->pid, feed->pid);
+
+ client->feed_users++;
+
+ pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ pid_msg.x_msg_header.msg_dst_id = HIF_TASK;
+ pid_msg.x_msg_header.msg_flags = 0;
+ pid_msg.x_msg_header.msg_type = MSG_SMS_ADD_PID_FILTER_REQ;
+ pid_msg.x_msg_header.msg_length = sizeof(pid_msg);
+ pid_msg.msg_data[0] = feed->pid;
+
+ return smsclient_sendrequest(client->smsclient,
+ &pid_msg, sizeof(pid_msg));
+}
+
+static int smsdvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct smsdvb_client_t *client =
+ container_of(feed->demux, struct smsdvb_client_t, demux);
+ struct sms_msg_data pid_msg;
+
+ pr_debug("remove pid %d(%x)\n",
+ feed->pid, feed->pid);
+
+ client->feed_users--;
+
+ pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ pid_msg.x_msg_header.msg_dst_id = HIF_TASK;
+ pid_msg.x_msg_header.msg_flags = 0;
+ pid_msg.x_msg_header.msg_type = MSG_SMS_REMOVE_PID_FILTER_REQ;
+ pid_msg.x_msg_header.msg_length = sizeof(pid_msg);
+ pid_msg.msg_data[0] = feed->pid;
+
+ return smsclient_sendrequest(client->smsclient,
+ &pid_msg, sizeof(pid_msg));
+}
+
+static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client,
+ void *buffer, size_t size,
+ struct completion *completion)
+{
+ int rc;
+
+ rc = smsclient_sendrequest(client->smsclient, buffer, size);
+ if (rc < 0)
+ return rc;
+
+ return wait_for_completion_timeout(completion,
+ msecs_to_jiffies(2000)) ?
+ 0 : -ETIME;
+}
+
+static int smsdvb_send_statistics_request(struct smsdvb_client_t *client)
+{
+ int rc;
+ struct sms_msg_hdr msg;
+
+ /* Don't request stats too fast */
+ if (client->get_stats_jiffies &&
+ (!time_after(jiffies, client->get_stats_jiffies)))
+ return 0;
+ client->get_stats_jiffies = jiffies + msecs_to_jiffies(100);
+
+ msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.msg_dst_id = HIF_TASK;
+ msg.msg_flags = 0;
+ msg.msg_length = sizeof(msg);
+
+ switch (smscore_get_device_mode(client->coredev)) {
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ /*
+ * Check for firmware version, to avoid breaking for old cards
+ */
+ if (client->coredev->fw_version >= 0x800)
+ msg.msg_type = MSG_SMS_GET_STATISTICS_EX_REQ;
+ else
+ msg.msg_type = MSG_SMS_GET_STATISTICS_REQ;
+ break;
+ default:
+ msg.msg_type = MSG_SMS_GET_STATISTICS_REQ;
+ }
+
+ rc = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->stats_done);
+
+ return rc;
+}
+
+static inline int led_feedback(struct smsdvb_client_t *client)
+{
+ if (!(client->fe_status & FE_HAS_LOCK))
+ return sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+
+ return sms_board_led_feedback(client->coredev,
+ (client->legacy_ber == 0) ?
+ SMS_LED_HI : SMS_LED_LO);
+}
+
+static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+ int rc;
+ struct smsdvb_client_t *client;
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ *stat = client->fe_status;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ int rc;
+ struct smsdvb_client_t *client;
+
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ *ber = client->legacy_ber;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc;
+ s32 power = (s32) c->strength.stat[0].uvalue;
+ struct smsdvb_client_t *client;
+
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ if (power < -95)
+ *strength = 0;
+ else if (power > -29)
+ *strength = 65535;
+ else
+ *strength = (power + 95) * 65535 / 66;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc;
+ struct smsdvb_client_t *client;
+
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ /* Preferred scale for SNR with legacy API: 0.1 dB */
+ *snr = ((u32)c->cnr.stat[0].svalue) / 100;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ int rc;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct smsdvb_client_t *client;
+
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ *ucblocks = c->block_error.stat[0].uvalue;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ pr_debug("\n");
+
+ tune->min_delay_ms = 400;
+ tune->step_size = 250000;
+ tune->max_drift = 0;
+ return 0;
+}
+
+static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ struct {
+ struct sms_msg_hdr msg;
+ u32 Data[3];
+ } msg;
+
+ int ret;
+
+ client->fe_status = 0;
+ client->event_fe_state = -1;
+ client->event_unc_state = -1;
+ fe->dtv_property_cache.delivery_system = SYS_DVBT;
+
+ msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.msg.msg_dst_id = HIF_TASK;
+ msg.msg.msg_flags = 0;
+ msg.msg.msg_type = MSG_SMS_RF_TUNE_REQ;
+ msg.msg.msg_length = sizeof(msg);
+ msg.Data[0] = c->frequency;
+ msg.Data[2] = 12000000;
+
+ pr_debug("%s: freq %d band %d\n", __func__, c->frequency,
+ c->bandwidth_hz);
+
+ switch (c->bandwidth_hz / 1000000) {
+ case 8:
+ msg.Data[1] = BW_8_MHZ;
+ break;
+ case 7:
+ msg.Data[1] = BW_7_MHZ;
+ break;
+ case 6:
+ msg.Data[1] = BW_6_MHZ;
+ break;
+ case 0:
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+ /* Disable LNA, if any. An error is returned if no LNA is present */
+ ret = sms_board_lna_control(client->coredev, 0);
+ if (ret == 0) {
+ fe_status_t status;
+
+ /* tune with LNA off at first */
+ ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->tune_done);
+
+ smsdvb_read_status(fe, &status);
+
+ if (status & FE_HAS_LOCK)
+ return ret;
+
+ /* previous tune didn't lock - enable LNA and tune again */
+ sms_board_lna_control(client->coredev, 1);
+ }
+
+ return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->tune_done);
+}
+
+static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+ int board_id = smscore_get_board_id(client->coredev);
+ struct sms_board *board = sms_get_board(board_id);
+ enum sms_device_type_st type = board->type;
+ int ret;
+
+ struct {
+ struct sms_msg_hdr msg;
+ u32 Data[4];
+ } msg;
+
+ fe->dtv_property_cache.delivery_system = SYS_ISDBT;
+
+ msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.msg.msg_dst_id = HIF_TASK;
+ msg.msg.msg_flags = 0;
+ msg.msg.msg_type = MSG_SMS_ISDBT_TUNE_REQ;
+ msg.msg.msg_length = sizeof(msg);
+
+ if (c->isdbt_sb_segment_idx == -1)
+ c->isdbt_sb_segment_idx = 0;
+
+ if (!c->isdbt_layer_enabled)
+ c->isdbt_layer_enabled = 7;
+
+ msg.Data[0] = c->frequency;
+ msg.Data[1] = BW_ISDBT_1SEG;
+ msg.Data[2] = 12000000;
+ msg.Data[3] = c->isdbt_sb_segment_idx;
+
+ if (c->isdbt_partial_reception) {
+ if ((type == SMS_PELE || type == SMS_RIO) &&
+ c->isdbt_sb_segment_count > 3)
+ msg.Data[1] = BW_ISDBT_13SEG;
+ else if (c->isdbt_sb_segment_count > 1)
+ msg.Data[1] = BW_ISDBT_3SEG;
+ } else if (type == SMS_PELE || type == SMS_RIO)
+ msg.Data[1] = BW_ISDBT_13SEG;
+
+ c->bandwidth_hz = 6000000;
+
+ pr_debug("freq %d segwidth %d segindex %d\n",
+ c->frequency, c->isdbt_sb_segment_count,
+ c->isdbt_sb_segment_idx);
+
+ /* Disable LNA, if any. An error is returned if no LNA is present */
+ ret = sms_board_lna_control(client->coredev, 0);
+ if (ret == 0) {
+ fe_status_t status;
+
+ /* tune with LNA off at first */
+ ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->tune_done);
+
+ smsdvb_read_status(fe, &status);
+
+ if (status & FE_HAS_LOCK)
+ return ret;
+
+ /* previous tune didn't lock - enable LNA and tune again */
+ sms_board_lna_control(client->coredev, 1);
+ }
+ return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->tune_done);
+}
+
+static int smsdvb_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+ struct smscore_device_t *coredev = client->coredev;
+
+ smsdvb_stats_not_ready(fe);
+ c->strength.stat[0].uvalue = 0;
+ c->cnr.stat[0].uvalue = 0;
+
+ client->has_tuned = false;
+
+ switch (smscore_get_device_mode(coredev)) {
+ case DEVICE_MODE_DVBT:
+ case DEVICE_MODE_DVBT_BDA:
+ return smsdvb_dvbt_set_frontend(fe);
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ return smsdvb_isdbt_set_frontend(fe);
+ default:
+ return -EINVAL;
+ }
+}
+
+/* Nothing to do here, as stats are automatically updated */
+static int smsdvb_get_frontend(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static int smsdvb_init(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ sms_board_power(client->coredev, 1);
+
+ sms_board_dvb3_event(client, DVB3_EVENT_INIT);
+ return 0;
+}
+
+static int smsdvb_sleep(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+ sms_board_power(client->coredev, 0);
+
+ sms_board_dvb3_event(client, DVB3_EVENT_SLEEP);
+
+ return 0;
+}
+
+static void smsdvb_release(struct dvb_frontend *fe)
+{
+ /* do nothing */
+}
+
+static struct dvb_frontend_ops smsdvb_fe_ops = {
+ .info = {
+ .name = "Siano Mobile Digital MDTV Receiver",
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 250000,
+ .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 = smsdvb_release,
+
+ .set_frontend = smsdvb_set_frontend,
+ .get_frontend = smsdvb_get_frontend,
+ .get_tune_settings = smsdvb_get_tune_settings,
+
+ .read_status = smsdvb_read_status,
+ .read_ber = smsdvb_read_ber,
+ .read_signal_strength = smsdvb_read_signal_strength,
+ .read_snr = smsdvb_read_snr,
+ .read_ucblocks = smsdvb_read_ucblocks,
+
+ .init = smsdvb_init,
+ .sleep = smsdvb_sleep,
+};
+
+static int smsdvb_hotplug(struct smscore_device_t *coredev,
+ struct device *device, int arrival)
+{
+ struct smsclient_params_t params;
+ struct smsdvb_client_t *client;
+ int rc;
+
+ /* device removal handled by onremove callback */
+ if (!arrival)
+ return 0;
+ client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+ /* register dvb adapter */
+ rc = dvb_register_adapter(&client->adapter,
+ sms_get_board(
+ smscore_get_board_id(coredev))->name,
+ THIS_MODULE, device, adapter_nr);
+ if (rc < 0) {
+ pr_err("dvb_register_adapter() failed %d\n", rc);
+ goto adapter_error;
+ }
+ dvb_register_media_controller(&client->adapter, coredev->media_dev);
+
+ /* init dvb demux */
+ client->demux.dmx.capabilities = DMX_TS_FILTERING;
+ client->demux.filternum = 32; /* todo: nova ??? */
+ client->demux.feednum = 32;
+ client->demux.start_feed = smsdvb_start_feed;
+ client->demux.stop_feed = smsdvb_stop_feed;
+
+ rc = dvb_dmx_init(&client->demux);
+ if (rc < 0) {
+ pr_err("dvb_dmx_init failed %d\n", rc);
+ goto dvbdmx_error;
+ }
+
+ /* init dmxdev */
+ client->dmxdev.filternum = 32;
+ client->dmxdev.demux = &client->demux.dmx;
+ client->dmxdev.capabilities = 0;
+
+ rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter);
+ if (rc < 0) {
+ pr_err("dvb_dmxdev_init failed %d\n", rc);
+ goto dmxdev_error;
+ }
+
+ /* init and register frontend */
+ memcpy(&client->frontend.ops, &smsdvb_fe_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ switch (smscore_get_device_mode(coredev)) {
+ case DEVICE_MODE_DVBT:
+ case DEVICE_MODE_DVBT_BDA:
+ client->frontend.ops.delsys[0] = SYS_DVBT;
+ break;
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ client->frontend.ops.delsys[0] = SYS_ISDBT;
+ break;
+ }
+
+ rc = dvb_register_frontend(&client->adapter, &client->frontend);
+ if (rc < 0) {
+ pr_err("frontend registration failed %d\n", rc);
+ goto frontend_error;
+ }
+
+ params.initial_id = 1;
+ params.data_type = MSG_SMS_DVBT_BDA_DATA;
+ params.onresponse_handler = smsdvb_onresponse;
+ params.onremove_handler = smsdvb_onremove;
+ params.context = client;
+
+ rc = smscore_register_client(coredev, &params, &client->smsclient);
+ if (rc < 0) {
+ pr_err("smscore_register_client() failed %d\n", rc);
+ goto client_error;
+ }
+
+ client->coredev = coredev;
+
+ init_completion(&client->tune_done);
+ init_completion(&client->stats_done);
+
+ kmutex_lock(&g_smsdvb_clientslock);
+
+ list_add(&client->entry, &g_smsdvb_clients);
+
+ kmutex_unlock(&g_smsdvb_clientslock);
+
+ client->event_fe_state = -1;
+ client->event_unc_state = -1;
+ sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG);
+
+ sms_board_setup(coredev);
+
+ if (smsdvb_debugfs_create(client) < 0)
+ pr_info("failed to create debugfs node\n");
+
+ dvb_create_media_graph(&client->adapter);
+
+ pr_info("DVB interface registered.\n");
+ return 0;
+
+client_error:
+ dvb_unregister_frontend(&client->frontend);
+
+frontend_error:
+ dvb_dmxdev_release(&client->dmxdev);
+
+dmxdev_error:
+ dvb_dmx_release(&client->demux);
+
+dvbdmx_error:
+ smsdvb_media_device_unregister(client);
+ dvb_unregister_adapter(&client->adapter);
+
+adapter_error:
+ kfree(client);
+ return rc;
+}
+
+static int __init smsdvb_module_init(void)
+{
+ int rc;
+
+ INIT_LIST_HEAD(&g_smsdvb_clients);
+ kmutex_init(&g_smsdvb_clientslock);
+
+ smsdvb_debugfs_register();
+
+ rc = smscore_register_hotplug(smsdvb_hotplug);
+
+ pr_debug("\n");
+
+ return rc;
+}
+
+static void __exit smsdvb_module_exit(void)
+{
+ smscore_unregister_hotplug(smsdvb_hotplug);
+
+ kmutex_lock(&g_smsdvb_clientslock);
+
+ while (!list_empty(&g_smsdvb_clients))
+ smsdvb_unregister_client((struct smsdvb_client_t *)g_smsdvb_clients.next);
+
+ smsdvb_debugfs_unregister();
+
+ kmutex_unlock(&g_smsdvb_clientslock);
+}
+
+module_init(smsdvb_module_init);
+module_exit(smsdvb_module_exit);
+
+MODULE_DESCRIPTION("SMS DVB subsystem adaptation module");
+MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/siano/smsdvb.h b/drivers/media/common/siano/smsdvb.h
new file mode 100644
index 000000000..ae36d0ae0
--- /dev/null
+++ b/drivers/media/common/siano/smsdvb.h
@@ -0,0 +1,130 @@
+/***********************************************************************
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ ***********************************************************************/
+
+struct smsdvb_debugfs;
+struct smsdvb_client_t;
+
+typedef void (*sms_prt_dvb_stats_t)(struct smsdvb_debugfs *debug_data,
+ struct sms_stats *p);
+
+typedef void (*sms_prt_isdb_stats_t)(struct smsdvb_debugfs *debug_data,
+ struct sms_isdbt_stats *p);
+
+typedef void (*sms_prt_isdb_stats_ex_t)
+ (struct smsdvb_debugfs *debug_data,
+ struct sms_isdbt_stats_ex *p);
+
+
+struct smsdvb_client_t {
+ struct list_head entry;
+
+ struct smscore_device_t *coredev;
+ struct smscore_client_t *smsclient;
+
+ struct dvb_adapter adapter;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dvb_frontend frontend;
+
+ fe_status_t fe_status;
+
+ struct completion tune_done;
+ struct completion stats_done;
+
+ int last_per;
+
+ int legacy_ber, legacy_per;
+
+ int event_fe_state;
+ int event_unc_state;
+
+ unsigned long get_stats_jiffies;
+
+ int feed_users;
+ bool has_tuned;
+
+ /* stats debugfs data */
+ struct dentry *debugfs;
+
+ struct smsdvb_debugfs *debug_data;
+
+ sms_prt_dvb_stats_t prt_dvb_stats;
+ sms_prt_isdb_stats_t prt_isdb_stats;
+ sms_prt_isdb_stats_ex_t prt_isdb_stats_ex;
+};
+
+/*
+ * This struct is a mix of struct sms_rx_stats_ex and
+ * struct sms_srvm_signal_status.
+ * It was obtained by comparing the way it was filled by the original code
+ */
+struct RECEPTION_STATISTICS_PER_SLICES_S {
+ u32 result;
+ u32 snr;
+ s32 in_band_power;
+ u32 ts_packets;
+ u32 ets_packets;
+ u32 constellation;
+ u32 hp_code;
+ u32 tps_srv_ind_lp;
+ u32 tps_srv_ind_hp;
+ u32 cell_id;
+ u32 reason;
+ u32 request_id;
+ u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */
+
+ u32 ber; /* Post Viterbi BER [1E-5] */
+ s32 RSSI; /* dBm */
+ s32 carrier_offset; /* Carrier Offset in bin/1024 */
+
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+
+ u32 ber_bit_count; /* Total number of SYNC bits. */
+ u32 ber_error_count; /* Number of erroneous SYNC bits. */
+
+ s32 MRC_SNR; /* dB */
+ s32 mrc_in_band_pwr; /* In band power in dBM */
+ s32 MRC_RSSI; /* dBm */
+};
+
+/* From smsdvb-debugfs.c */
+#ifdef CONFIG_SMS_SIANO_DEBUGFS
+
+int smsdvb_debugfs_create(struct smsdvb_client_t *client);
+void smsdvb_debugfs_release(struct smsdvb_client_t *client);
+int smsdvb_debugfs_register(void);
+void smsdvb_debugfs_unregister(void);
+
+#else
+
+static inline int smsdvb_debugfs_create(struct smsdvb_client_t *client)
+{
+ return 0;
+}
+
+static inline void smsdvb_debugfs_release(struct smsdvb_client_t *client) {}
+
+static inline int smsdvb_debugfs_register(void)
+{
+ return 0;
+};
+
+static inline void smsdvb_debugfs_unregister(void) {};
+
+#endif
+
diff --git a/drivers/media/common/siano/smsendian.c b/drivers/media/common/siano/smsendian.c
new file mode 100644
index 000000000..bfe831c10
--- /dev/null
+++ b/drivers/media/common/siano/smsendian.c
@@ -0,0 +1,103 @@
+/****************************************************************
+
+ Siano Mobile Silicon, Inc.
+ MDTV receiver kernel modules.
+ Copyright (C) 2006-2009, Uri Shkolnik
+
+ 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, see <http://www.gnu.org/licenses/>.
+
+ ****************************************************************/
+
+#include <linux/export.h>
+#include <asm/byteorder.h>
+
+#include "smsendian.h"
+#include "smscoreapi.h"
+
+void smsendian_handle_tx_message(void *buffer)
+{
+#ifdef __BIG_ENDIAN
+ struct sms_msg_data *msg = (struct sms_msg_data *)buffer;
+ int i;
+ int msg_words;
+
+ switch (msg->x_msg_header.msg_type) {
+ case MSG_SMS_DATA_DOWNLOAD_REQ:
+ {
+ msg->msg_data[0] = le32_to_cpu(msg->msg_data[0]);
+ break;
+ }
+
+ default:
+ msg_words = (msg->x_msg_header.msg_length -
+ sizeof(struct sms_msg_hdr))/4;
+
+ for (i = 0; i < msg_words; i++)
+ msg->msg_data[i] = le32_to_cpu(msg->msg_data[i]);
+
+ break;
+ }
+#endif /* __BIG_ENDIAN */
+}
+EXPORT_SYMBOL_GPL(smsendian_handle_tx_message);
+
+void smsendian_handle_rx_message(void *buffer)
+{
+#ifdef __BIG_ENDIAN
+ struct sms_msg_data *msg = (struct sms_msg_data *)buffer;
+ int i;
+ int msg_words;
+
+ switch (msg->x_msg_header.msg_type) {
+ case MSG_SMS_GET_VERSION_EX_RES:
+ {
+ struct sms_version_res *ver =
+ (struct sms_version_res *) msg;
+ ver->chip_model = le16_to_cpu(ver->chip_model);
+ break;
+ }
+
+ case MSG_SMS_DVBT_BDA_DATA:
+ case MSG_SMS_DAB_CHANNEL:
+ case MSG_SMS_DATA_MSG:
+ {
+ break;
+ }
+
+ default:
+ {
+ msg_words = (msg->x_msg_header.msg_length -
+ sizeof(struct sms_msg_hdr))/4;
+
+ for (i = 0; i < msg_words; i++)
+ msg->msg_data[i] = le32_to_cpu(msg->msg_data[i]);
+
+ break;
+ }
+ }
+#endif /* __BIG_ENDIAN */
+}
+EXPORT_SYMBOL_GPL(smsendian_handle_rx_message);
+
+void smsendian_handle_message_header(void *msg)
+{
+#ifdef __BIG_ENDIAN
+ struct sms_msg_hdr *phdr = (struct sms_msg_hdr *)msg;
+
+ phdr->msg_type = le16_to_cpu(phdr->msg_type);
+ phdr->msg_length = le16_to_cpu(phdr->msg_length);
+ phdr->msg_flags = le16_to_cpu(phdr->msg_flags);
+#endif /* __BIG_ENDIAN */
+}
+EXPORT_SYMBOL_GPL(smsendian_handle_message_header);
diff --git a/drivers/media/common/siano/smsendian.h b/drivers/media/common/siano/smsendian.h
new file mode 100644
index 000000000..1624d6fd3
--- /dev/null
+++ b/drivers/media/common/siano/smsendian.h
@@ -0,0 +1,32 @@
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2009, Uri Shkolnik
+
+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, see <http://www.gnu.org/licenses/>.
+
+****************************************************************/
+
+#ifndef __SMS_ENDIAN_H__
+#define __SMS_ENDIAN_H__
+
+#include <asm/byteorder.h>
+
+extern void smsendian_handle_tx_message(void *buffer);
+extern void smsendian_handle_rx_message(void *buffer);
+extern void smsendian_handle_message_header(void *msg);
+
+#endif /* __SMS_ENDIAN_H__ */
+
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
new file mode 100644
index 000000000..1d60d200d
--- /dev/null
+++ b/drivers/media/common/siano/smsir.c
@@ -0,0 +1,113 @@
+/****************************************************************
+
+ Siano Mobile Silicon, Inc.
+ MDTV receiver kernel modules.
+ Copyright (C) 2006-2009, Uri Shkolnik
+
+ Copyright (c) 2010 - Mauro Carvalho Chehab
+ - Ported the driver to use rc-core
+ - IR raw event decoding is now done at rc-core
+ - Code almost re-written
+
+ 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, see <http://www.gnu.org/licenses/>.
+
+ ****************************************************************/
+
+
+#include "smscoreapi.h"
+
+#include <linux/types.h>
+#include <linux/input.h>
+
+#include "smsir.h"
+#include "sms-cards.h"
+
+#define MODULE_NAME "smsmdtv"
+
+void sms_ir_event(struct smscore_device_t *coredev, const char *buf, int len)
+{
+ int i;
+ const s32 *samples = (const void *)buf;
+
+ for (i = 0; i < len >> 2; i++) {
+ DEFINE_IR_RAW_EVENT(ev);
+
+ ev.duration = abs(samples[i]) * 1000; /* Convert to ns */
+ ev.pulse = (samples[i] > 0) ? false : true;
+
+ ir_raw_event_store(coredev->ir.dev, &ev);
+ }
+ ir_raw_event_handle(coredev->ir.dev);
+}
+
+int sms_ir_init(struct smscore_device_t *coredev)
+{
+ int err;
+ int board_id = smscore_get_board_id(coredev);
+ struct rc_dev *dev;
+
+ pr_debug("Allocating rc device\n");
+ dev = rc_allocate_device();
+ if (!dev)
+ return -ENOMEM;
+
+ coredev->ir.controller = 0; /* Todo: vega/nova SPI number */
+ coredev->ir.timeout = IR_DEFAULT_TIMEOUT;
+ pr_debug("IR port %d, timeout %d ms\n",
+ coredev->ir.controller, coredev->ir.timeout);
+
+ snprintf(coredev->ir.name, sizeof(coredev->ir.name),
+ "SMS IR (%s)", sms_get_board(board_id)->name);
+
+ strlcpy(coredev->ir.phys, coredev->devpath, sizeof(coredev->ir.phys));
+ strlcat(coredev->ir.phys, "/ir0", sizeof(coredev->ir.phys));
+
+ dev->input_name = coredev->ir.name;
+ dev->input_phys = coredev->ir.phys;
+ dev->dev.parent = coredev->device;
+
+#if 0
+ /* TODO: properly initialize the parameters bellow */
+ dev->input_id.bustype = BUS_USB;
+ dev->input_id.version = 1;
+ dev->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ dev->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+#endif
+
+ dev->priv = coredev;
+ dev->driver_type = RC_DRIVER_IR_RAW;
+ dev->allowed_protocols = RC_BIT_ALL;
+ dev->map_name = sms_get_board(board_id)->rc_codes;
+ dev->driver_name = MODULE_NAME;
+
+ pr_debug("Input device (IR) %s is set for key events\n",
+ dev->input_name);
+
+ err = rc_register_device(dev);
+ if (err < 0) {
+ pr_err("Failed to register device\n");
+ rc_free_device(dev);
+ return err;
+ }
+
+ coredev->ir.dev = dev;
+ return 0;
+}
+
+void sms_ir_exit(struct smscore_device_t *coredev)
+{
+ rc_unregister_device(coredev->ir.dev);
+
+ pr_debug("\n");
+}
diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h
new file mode 100644
index 000000000..fc8b7925c
--- /dev/null
+++ b/drivers/media/common/siano/smsir.h
@@ -0,0 +1,63 @@
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2009, Uri Shkolnik
+
+ Copyright (c) 2010 - Mauro Carvalho Chehab
+ - Ported the driver to use rc-core
+ - IR raw event decoding is now done at rc-core
+ - Code almost re-written
+
+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, see <http://www.gnu.org/licenses/>.
+
+****************************************************************/
+
+#ifndef __SMS_IR_H__
+#define __SMS_IR_H__
+
+#include <linux/input.h>
+#include <media/rc-core.h>
+
+#define IR_DEFAULT_TIMEOUT 100
+
+struct smscore_device_t;
+
+struct ir_t {
+ struct rc_dev *dev;
+ char name[40];
+ char phys[32];
+
+ char *rc_codes;
+
+ u32 timeout;
+ u32 controller;
+};
+
+#ifdef CONFIG_SMS_SIANO_RC
+int sms_ir_init(struct smscore_device_t *coredev);
+void sms_ir_exit(struct smscore_device_t *coredev);
+void sms_ir_event(struct smscore_device_t *coredev,
+ const char *buf, int len);
+#else
+inline static int sms_ir_init(struct smscore_device_t *coredev) {
+ return 0;
+}
+inline static void sms_ir_exit(struct smscore_device_t *coredev) {};
+inline static void sms_ir_event(struct smscore_device_t *coredev,
+ const char *buf, int len) {};
+#endif
+
+#endif /* __SMS_IR_H__ */
+
diff --git a/drivers/media/common/tveeprom.c b/drivers/media/common/tveeprom.c
new file mode 100644
index 000000000..47da0378c
--- /dev/null
+++ b/drivers/media/common/tveeprom.c
@@ -0,0 +1,798 @@
+/*
+ * tveeprom - eeprom decoder for tvcard configuration eeproms
+ *
+ * Data and decoding routines shamelessly borrowed from bttv-cards.c
+ * eeprom access routine shamelessly borrowed from bttv-if.c
+ * which are:
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2001 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+ * Adjustments to fit a more general model and all bugs:
+
+ Copyright (C) 2003 John Klar <linpvr at projectplasma.com>
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-common.h>
+
+MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver");
+MODULE_AUTHOR("John Klar");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define STRM(array, i) \
+ (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown")
+
+#define tveeprom_info(fmt, arg...) \
+ v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg)
+#define tveeprom_warn(fmt, arg...) \
+ v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg)
+#define tveeprom_dbg(fmt, arg...) do { \
+ if (debug) \
+ v4l_printk(KERN_DEBUG, "tveeprom", \
+ c->adapter, c->addr, fmt , ## arg); \
+ } while (0)
+
+/*
+ * The Hauppauge eeprom uses an 8bit field to determine which
+ * tuner formats the tuner supports.
+ */
+static const struct {
+ int id;
+ const char * const name;
+} hauppauge_tuner_fmt[] = {
+ { V4L2_STD_UNKNOWN, " UNKNOWN" },
+ { V4L2_STD_UNKNOWN, " FM" },
+ { V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" },
+ { V4L2_STD_MN, " NTSC(M)" },
+ { V4L2_STD_PAL_I, " PAL(I)" },
+ { V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, " SECAM(L/L')" },
+ { V4L2_STD_DK, " PAL(D/D1/K)" },
+ { V4L2_STD_ATSC, " ATSC/DVB Digital" },
+};
+
+/* This is the full list of possible tuners. Many thanks to Hauppauge for
+ supplying this information. Note that many tuners where only used for
+ testing and never made it to the outside world. So you will only see
+ a subset in actual produced cards. */
+static const struct {
+ int id;
+ const char * const name;
+} hauppauge_tuner[] = {
+ /* 0-9 */
+ { TUNER_ABSENT, "None" },
+ { TUNER_ABSENT, "External" },
+ { TUNER_ABSENT, "Unspecified" },
+ { TUNER_PHILIPS_PAL, "Philips FI1216" },
+ { TUNER_PHILIPS_SECAM, "Philips FI1216MF" },
+ { TUNER_PHILIPS_NTSC, "Philips FI1236" },
+ { TUNER_PHILIPS_PAL_I, "Philips FI1246" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FI1256" },
+ { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" },
+ { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" },
+ /* 10-19 */
+ { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" },
+ { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" },
+ { TUNER_TEMIC_NTSC, "Temic 4032FY5" },
+ { TUNER_TEMIC_PAL, "Temic 4002FH5" },
+ { TUNER_TEMIC_PAL_I, "Temic 4062FY5" },
+ { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" },
+ { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" },
+ { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" },
+ { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" },
+ /* 20-29 */
+ { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" },
+ { TUNER_PHILIPS_PAL, "Philips FM1216" },
+ { TUNER_PHILIPS_SECAM, "Philips FM1216MF" },
+ { TUNER_PHILIPS_NTSC, "Philips FM1236" },
+ { TUNER_PHILIPS_PAL_I, "Philips FM1246" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FM1256" },
+ { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" },
+ { TUNER_ABSENT, "Samsung TCPN9082D" },
+ { TUNER_ABSENT, "Samsung TCPM9092P" },
+ { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" },
+ /* 30-39 */
+ { TUNER_ABSENT, "Samsung TCPN9085D" },
+ { TUNER_ABSENT, "Samsung TCPB9085P" },
+ { TUNER_ABSENT, "Samsung TCPL9091P" },
+ { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" },
+ { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" },
+ { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" },
+ { TUNER_PHILIPS_NTSC, "Philips TD1536" },
+ { TUNER_PHILIPS_NTSC, "Philips TD1536D" },
+ { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */
+ { TUNER_ABSENT, "Philips FI1256MP" },
+ /* 40-49 */
+ { TUNER_ABSENT, "Samsung TCPQ9091P" },
+ { TUNER_TEMIC_4006FN5_MULTI_PAL,"Temic 4006FN5" },
+ { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" },
+ { TUNER_TEMIC_4046FM5, "Temic 4046FM5" },
+ { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" },
+ { TUNER_ABSENT, "Philips TD1536D FH 44"},
+ { TUNER_LG_NTSC_FM, "LG TP18NSR01F"},
+ { TUNER_LG_PAL_FM, "LG TP18PSB01D"},
+ { TUNER_LG_PAL, "LG TP18PSB11D"},
+ { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"},
+ /* 50-59 */
+ { TUNER_LG_PAL_I, "LG TAPC-I701D"},
+ { TUNER_ABSENT, "Temic 4042FI5"},
+ { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"},
+ { TUNER_ABSENT, "LG TPI8NSR11F"},
+ { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"},
+ { TUNER_ABSENT, "Philips FI1236 MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"},
+ { TUNER_ABSENT, "Philips FM1216MP MK3"},
+ /* 60-69 */
+ { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"},
+ { TUNER_ABSENT, "LG M001D MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"},
+ { TUNER_ABSENT, "LG M701D MK3"},
+ { TUNER_ABSENT, "Temic 4146FM5"},
+ { TUNER_ABSENT, "Temic 4136FY5"},
+ { TUNER_ABSENT, "Temic 4106FH5"},
+ { TUNER_ABSENT, "Philips FQ1216LMP MK3"},
+ { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"},
+ { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"},
+ /* 70-79 */
+ { TUNER_ABSENT, "LG TALN H200T"},
+ { TUNER_ABSENT, "LG TALN H250T"},
+ { TUNER_ABSENT, "LG TALN M200T"},
+ { TUNER_ABSENT, "LG TALN Z200T"},
+ { TUNER_ABSENT, "LG TALN S200T"},
+ { TUNER_ABSENT, "Thompson DTT7595"},
+ { TUNER_ABSENT, "Thompson DTT7592"},
+ { TUNER_ABSENT, "Silicon TDA8275C1 8290"},
+ { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"},
+ { TUNER_ABSENT, "Thompson DTT757"},
+ /* 80-89 */
+ { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"},
+ { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"},
+ { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"},
+ { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"},
+ { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"},
+ { TUNER_TCL_2002N, "TCL 2002N 6A"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"},
+ { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"},
+ { TUNER_ABSENT, "Samsung TCPE 4121P30A"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"},
+ /* 90-99 */
+ { TUNER_ABSENT, "LG TALN H202T"},
+ { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"},
+ { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"},
+ { TUNER_ABSENT, "Philips FQ1286A MK4"},
+ { TUNER_ABSENT, "Philips FQ1216ME MK5"},
+ { TUNER_ABSENT, "Philips FQ1236 MK5"},
+ { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"},
+ { TUNER_TCL_2002MB, "TCL 2002MB_3H"},
+ { TUNER_ABSENT, "TCL 2002MI_3H"},
+ { TUNER_TCL_2002N, "TCL 2002N 5H"},
+ /* 100-109 */
+ { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"},
+ { TUNER_TEA5767, "Philips TEA5768HL FM Radio"},
+ { TUNER_ABSENT, "Panasonic ENV57H12D5"},
+ { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"},
+ { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"},
+ { TUNER_ABSENT, "TCL MQNM05-4"},
+ { TUNER_ABSENT, "LG TAPC-W701D"},
+ { TUNER_ABSENT, "TCL 9886P-WM"},
+ { TUNER_ABSENT, "TCL 1676NM-WM"},
+ /* 110-119 */
+ { TUNER_ABSENT, "Thompson DTT75105"},
+ { TUNER_ABSENT, "Conexant_CX24109"},
+ { TUNER_TCL_2002N, "TCL M2523_5N_E"},
+ { TUNER_TCL_2002MB, "TCL M2523_3DB_E"},
+ { TUNER_ABSENT, "Philips 8275A"},
+ { TUNER_ABSENT, "Microtune MT2060"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"},
+ { TUNER_ABSENT, "TCL M2523_3DI_E"},
+ { TUNER_ABSENT, "Samsung THPD5222FG30A"},
+ /* 120-129 */
+ { TUNER_XC2028, "Xceive XC3028"},
+ { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"},
+ { TUNER_ABSENT, "Philips FQD1216LME"},
+ { TUNER_ABSENT, "Conexant CX24118A"},
+ { TUNER_ABSENT, "TCL DMF11WIP"},
+ { TUNER_ABSENT, "TCL MFNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MPE05_2H_E"},
+ { TUNER_ABSENT, "TCL MQNM05_4_U"},
+ { TUNER_ABSENT, "TCL M2523_5NH_E"},
+ /* 130-139 */
+ { TUNER_ABSENT, "TCL M2523_3DBH_E"},
+ { TUNER_ABSENT, "TCL M2523_3DIH_E"},
+ { TUNER_ABSENT, "TCL MFPE05_2_U"},
+ { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"},
+ { TUNER_ABSENT, "Philips FRH2036B"},
+ { TUNER_ABSENT, "Panasonic ENGF75_01GF"},
+ { TUNER_ABSENT, "MaxLinear MXL5005"},
+ { TUNER_ABSENT, "MaxLinear MXL5003"},
+ { TUNER_ABSENT, "Xceive XC2028"},
+ { TUNER_ABSENT, "Microtune MT2131"},
+ /* 140-149 */
+ { TUNER_ABSENT, "Philips 8275A_8295"},
+ { TUNER_ABSENT, "TCL MF02GIP_5N_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DB_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DI_E"},
+ { TUNER_ABSENT, "Microtune MT2266"},
+ { TUNER_ABSENT, "TCL MF10WPP_4N_E"},
+ { TUNER_ABSENT, "LG TAPQ_H702F"},
+ { TUNER_ABSENT, "TCL M09WPP_4N_E"},
+ { TUNER_ABSENT, "MaxLinear MXL5005_v2"},
+ { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"},
+ /* 150-159 */
+ { TUNER_XC5000, "Xceive XC5000"},
+ { TUNER_ABSENT, "Xceive XC3028L"},
+ { TUNER_ABSENT, "NXP 18271C2_716x"},
+ { TUNER_ABSENT, "Xceive XC4000"},
+ { TUNER_ABSENT, "Dibcom 7070"},
+ { TUNER_PHILIPS_TDA8290, "NXP 18271C2"},
+ { TUNER_ABSENT, "Siano SMS1010"},
+ { TUNER_ABSENT, "Siano SMS1150"},
+ { TUNER_ABSENT, "MaxLinear 5007"},
+ { TUNER_ABSENT, "TCL M09WPP_2P_E"},
+ /* 160-169 */
+ { TUNER_ABSENT, "Siano SMS1180"},
+ { TUNER_ABSENT, "Maxim_MAX2165"},
+ { TUNER_ABSENT, "Siano SMS1140"},
+ { TUNER_ABSENT, "Siano SMS1150 B1"},
+ { TUNER_ABSENT, "MaxLinear 111"},
+ { TUNER_ABSENT, "Dibcom 7770"},
+ { TUNER_ABSENT, "Siano SMS1180VNS"},
+ { TUNER_ABSENT, "Siano SMS1184"},
+ { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"},
+ { TUNER_ABSENT, "TCL_M11WPP_2PN_E"},
+ /* 170-179 */
+ { TUNER_ABSENT, "MaxLinear 301"},
+ { TUNER_ABSENT, "Mirics MSi001"},
+ { TUNER_ABSENT, "MaxLinear MxL241SF"},
+ { TUNER_XC5000C, "Xceive XC5000C"},
+ { TUNER_ABSENT, "Montage M68TS2020"},
+ { TUNER_ABSENT, "Siano SMS1530"},
+ { TUNER_ABSENT, "Dibcom 7090"},
+ { TUNER_ABSENT, "Xceive XC5200C"},
+ { TUNER_ABSENT, "NXP 18273"},
+ { TUNER_ABSENT, "Montage M88TS2022"},
+ /* 180-188 */
+ { TUNER_ABSENT, "NXP 18272M"},
+ { TUNER_ABSENT, "NXP 18272S"},
+
+ { TUNER_ABSENT, "Mirics MSi003"},
+ { TUNER_ABSENT, "MaxLinear MxL256"},
+ { TUNER_ABSENT, "SiLabs Si2158"},
+ { TUNER_ABSENT, "SiLabs Si2178"},
+ { TUNER_ABSENT, "SiLabs Si2157"},
+ { TUNER_ABSENT, "SiLabs Si2177"},
+ { TUNER_ABSENT, "ITE IT9137FN"},
+};
+
+/* Use TVEEPROM_AUDPROC_INTERNAL for those audio 'chips' that are
+ * internal to a video chip, i.e. not a separate audio chip. */
+static const struct {
+ u32 id;
+ const char * const name;
+} audio_ic[] = {
+ /* 0-4 */
+ { TVEEPROM_AUDPROC_NONE, "None" },
+ { TVEEPROM_AUDPROC_OTHER, "TEA6300" },
+ { TVEEPROM_AUDPROC_OTHER, "TEA6320" },
+ { TVEEPROM_AUDPROC_OTHER, "TDA9850" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3400C" },
+ /* 5-9 */
+ { TVEEPROM_AUDPROC_MSP, "MSP3410D" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3415" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3430" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3438" },
+ { TVEEPROM_AUDPROC_OTHER, "CS5331" },
+ /* 10-14 */
+ { TVEEPROM_AUDPROC_MSP, "MSP3435" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3440" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3445" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3411" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3416" },
+ /* 15-19 */
+ { TVEEPROM_AUDPROC_MSP, "MSP3425" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3451" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3418" },
+ { TVEEPROM_AUDPROC_OTHER, "Type 0x12" },
+ { TVEEPROM_AUDPROC_OTHER, "OKI7716" },
+ /* 20-24 */
+ { TVEEPROM_AUDPROC_MSP, "MSP4410" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4420" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4440" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4450" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4408" },
+ /* 25-29 */
+ { TVEEPROM_AUDPROC_MSP, "MSP4418" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4428" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4448" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4458" },
+ { TVEEPROM_AUDPROC_MSP, "Type 0x1d" },
+ /* 30-34 */
+ { TVEEPROM_AUDPROC_INTERNAL, "CX880" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX881" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX883" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX882" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25840" },
+ /* 35-39 */
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25841" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25842" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25843" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23418" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23885" },
+ /* 40-44 */
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23888" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7131" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23887" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7164" },
+ { TVEEPROM_AUDPROC_INTERNAL, "AU8522" },
+ /* 45-49 */
+ { TVEEPROM_AUDPROC_INTERNAL, "AVF4910B" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7231" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23102" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7163" },
+ { TVEEPROM_AUDPROC_OTHER, "AK4113" },
+ /* 50-52 */
+ { TVEEPROM_AUDPROC_OTHER, "CS5340" },
+ { TVEEPROM_AUDPROC_OTHER, "CS8416" },
+ { TVEEPROM_AUDPROC_OTHER, "CX20810" },
+};
+
+/* This list is supplied by Hauppauge. Thanks! */
+static const char *decoderIC[] = {
+ /* 0-4 */
+ "None", "BT815", "BT817", "BT819", "BT815A",
+ /* 5-9 */
+ "BT817A", "BT819A", "BT827", "BT829", "BT848",
+ /* 10-14 */
+ "BT848A", "BT849A", "BT829A", "BT827A", "BT878",
+ /* 15-19 */
+ "BT879", "BT880", "VPX3226E", "SAA7114", "SAA7115",
+ /* 20-24 */
+ "CX880", "CX881", "CX883", "SAA7111", "SAA7113",
+ /* 25-29 */
+ "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842",
+ /* 30-34 */
+ "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888",
+ /* 35-39 */
+ "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A",
+ /* 40-44 */
+ "SAA7164", "CX23885B", "AU8522", "ADV7401", "AVF4910B",
+ /* 45-49 */
+ "SAA7231", "CX23102", "SAA7163", "ADV7441A", "ADV7181C",
+ /* 50-53 */
+ "CX25836", "TDA9955", "TDA19977", "ADV7842"
+};
+
+static int hasRadioTuner(int tunerType)
+{
+ switch (tunerType) {
+ case 18: /* PNPEnv_TUNER_FR1236_MK2 */
+ case 23: /* PNPEnv_TUNER_FM1236 */
+ case 38: /* PNPEnv_TUNER_FMR1236 */
+ case 16: /* PNPEnv_TUNER_FR1216_MK2 */
+ case 19: /* PNPEnv_TUNER_FR1246_MK2 */
+ case 21: /* PNPEnv_TUNER_FM1216 */
+ case 24: /* PNPEnv_TUNER_FM1246 */
+ case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */
+ case 22: /* PNPEnv_TUNER_FM1216MF */
+ case 20: /* PNPEnv_TUNER_FR1256_MK2 */
+ case 25: /* PNPEnv_TUNER_FM1256 */
+ case 33: /* PNPEnv_TUNER_4039FR5 */
+ case 42: /* PNPEnv_TUNER_4009FR5 */
+ case 52: /* PNPEnv_TUNER_4049FM5 */
+ case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */
+ case 44: /* PNPEnv_TUNER_4009FN5 */
+ case 31: /* PNPEnv_TUNER_TCPB9085P */
+ case 30: /* PNPEnv_TUNER_TCPN9085D */
+ case 46: /* PNPEnv_TUNER_TP18NSR01F */
+ case 47: /* PNPEnv_TUNER_TP18PSB01D */
+ case 49: /* PNPEnv_TUNER_TAPC_I001D */
+ case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */
+ case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */
+ case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */
+ case 58: /* PNPEnv_TUNER_FM1236_MK3 */
+ case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */
+ case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */
+ case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */
+ case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */
+ case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */
+ case 105:
+ return 1;
+ }
+ return 0;
+}
+
+void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
+ unsigned char *eeprom_data)
+{
+ /* ----------------------------------------------
+ ** The hauppauge eeprom format is tagged
+ **
+ ** if packet[0] == 0x84, then packet[0..1] == length
+ ** else length = packet[0] & 3f;
+ ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum
+ **
+ ** In our (ivtv) case we're interested in the following:
+ ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner)
+ ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into
+ ** hauppauge_tuner_fmt)
+ ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM)
+ ** audio proc: tag [02].01 or [05].00 (mask with 0x7f)
+ ** decoder proc: tag [09].01)
+
+ ** Fun info:
+ ** model: tag [00].07-08 or [06].00-01
+ ** revision: tag [00].09-0b or [06].04-06
+ ** serial#: tag [01].05-07 or [04].04-06
+
+ ** # of inputs/outputs ???
+ */
+
+ int i, j, len, done, beenhere, tag, start;
+
+ int tuner1 = 0, t_format1 = 0, audioic = -1;
+ const char *t_name1 = NULL;
+ const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" };
+
+ int tuner2 = 0, t_format2 = 0;
+ const char *t_name2 = NULL;
+ const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" };
+
+ memset(tvee, 0, sizeof(*tvee));
+ tvee->tuner_type = TUNER_ABSENT;
+ tvee->tuner2_type = TUNER_ABSENT;
+
+ done = len = beenhere = 0;
+
+ /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */
+ if (eeprom_data[0] == 0x1a &&
+ eeprom_data[1] == 0xeb &&
+ eeprom_data[2] == 0x67 &&
+ eeprom_data[3] == 0x95)
+ start = 0xa0; /* Generic em28xx offset */
+ else if ((eeprom_data[0] & 0xe1) == 0x01 &&
+ eeprom_data[1] == 0x00 &&
+ eeprom_data[2] == 0x00 &&
+ eeprom_data[8] == 0x84)
+ start = 8; /* Generic cx2388x offset */
+ else if (eeprom_data[1] == 0x70 &&
+ eeprom_data[2] == 0x00 &&
+ eeprom_data[4] == 0x74 &&
+ eeprom_data[8] == 0x84)
+ start = 8; /* Generic cx23418 offset (models 74xxx) */
+ else
+ start = 0;
+
+ for (i = start; !done && i < 256; i += len) {
+ if (eeprom_data[i] == 0x84) {
+ len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8);
+ i += 3;
+ } else if ((eeprom_data[i] & 0xf0) == 0x70) {
+ if (eeprom_data[i] & 0x08) {
+ /* verify checksum! */
+ done = 1;
+ break;
+ }
+ len = eeprom_data[i] & 0x07;
+ ++i;
+ } else {
+ tveeprom_warn("Encountered bad packet header [%02x]. "
+ "Corrupt or not a Hauppauge eeprom.\n",
+ eeprom_data[i]);
+ return;
+ }
+
+ if (debug) {
+ tveeprom_info("Tag [%02x] + %d bytes:",
+ eeprom_data[i], len - 1);
+ for (j = 1; j < len; j++)
+ printk(KERN_CONT " %02x", eeprom_data[i + j]);
+ printk(KERN_CONT "\n");
+ }
+
+ /* process by tag */
+ tag = eeprom_data[i];
+ switch (tag) {
+ case 0x00:
+ /* tag: 'Comprehensive' */
+ tuner1 = eeprom_data[i+6];
+ t_format1 = eeprom_data[i+5];
+ tvee->has_radio = eeprom_data[i+len-1];
+ /* old style tag, don't know how to detect
+ IR presence, mark as unknown. */
+ tvee->has_ir = 0;
+ tvee->model =
+ eeprom_data[i+8] +
+ (eeprom_data[i+9] << 8);
+ tvee->revision = eeprom_data[i+10] +
+ (eeprom_data[i+11] << 8) +
+ (eeprom_data[i+12] << 16);
+ break;
+
+ case 0x01:
+ /* tag: 'SerialID' */
+ tvee->serial_number =
+ eeprom_data[i+6] +
+ (eeprom_data[i+7] << 8) +
+ (eeprom_data[i+8] << 16);
+ break;
+
+ case 0x02:
+ /* tag 'AudioInfo'
+ Note mask with 0x7F, high bit used on some older models
+ to indicate 4052 mux was removed in favor of using MSP
+ inputs directly. */
+ audioic = eeprom_data[i+2] & 0x7f;
+ if (audioic < ARRAY_SIZE(audio_ic))
+ tvee->audio_processor = audio_ic[audioic].id;
+ else
+ tvee->audio_processor = TVEEPROM_AUDPROC_OTHER;
+ break;
+
+ /* case 0x03: tag 'EEInfo' */
+
+ case 0x04:
+ /* tag 'SerialID2' */
+ tvee->serial_number =
+ eeprom_data[i+5] +
+ (eeprom_data[i+6] << 8) +
+ (eeprom_data[i+7] << 16)+
+ (eeprom_data[i+8] << 24);
+
+ if (eeprom_data[i + 8] == 0xf0) {
+ tvee->MAC_address[0] = 0x00;
+ tvee->MAC_address[1] = 0x0D;
+ tvee->MAC_address[2] = 0xFE;
+ tvee->MAC_address[3] = eeprom_data[i + 7];
+ tvee->MAC_address[4] = eeprom_data[i + 6];
+ tvee->MAC_address[5] = eeprom_data[i + 5];
+ tvee->has_MAC_address = 1;
+ }
+ break;
+
+ case 0x05:
+ /* tag 'Audio2'
+ Note mask with 0x7F, high bit used on some older models
+ to indicate 4052 mux was removed in favor of using MSP
+ inputs directly. */
+ audioic = eeprom_data[i+1] & 0x7f;
+ if (audioic < ARRAY_SIZE(audio_ic))
+ tvee->audio_processor = audio_ic[audioic].id;
+ else
+ tvee->audio_processor = TVEEPROM_AUDPROC_OTHER;
+
+ break;
+
+ case 0x06:
+ /* tag 'ModelRev' */
+ tvee->model =
+ eeprom_data[i + 1] +
+ (eeprom_data[i + 2] << 8) +
+ (eeprom_data[i + 3] << 16) +
+ (eeprom_data[i + 4] << 24);
+ tvee->revision =
+ eeprom_data[i + 5] +
+ (eeprom_data[i + 6] << 8) +
+ (eeprom_data[i + 7] << 16);
+ break;
+
+ case 0x07:
+ /* tag 'Details': according to Hauppauge not interesting
+ on any PCI-era or later boards. */
+ break;
+
+ /* there is no tag 0x08 defined */
+
+ case 0x09:
+ /* tag 'Video' */
+ tvee->decoder_processor = eeprom_data[i + 1];
+ break;
+
+ case 0x0a:
+ /* tag 'Tuner' */
+ if (beenhere == 0) {
+ tuner1 = eeprom_data[i + 2];
+ t_format1 = eeprom_data[i + 1];
+ beenhere = 1;
+ } else {
+ /* a second (radio) tuner may be present */
+ tuner2 = eeprom_data[i + 2];
+ t_format2 = eeprom_data[i + 1];
+ /* not a TV tuner? */
+ if (t_format2 == 0)
+ tvee->has_radio = 1; /* must be radio */
+ }
+ break;
+
+ case 0x0b:
+ /* tag 'Inputs': according to Hauppauge this is specific
+ to each driver family, so no good assumptions can be
+ made. */
+ break;
+
+ /* case 0x0c: tag 'Balun' */
+ /* case 0x0d: tag 'Teletext' */
+
+ case 0x0e:
+ /* tag: 'Radio' */
+ tvee->has_radio = eeprom_data[i+1];
+ break;
+
+ case 0x0f:
+ /* tag 'IRInfo' */
+ tvee->has_ir = 1 | (eeprom_data[i+1] << 1);
+ break;
+
+ /* case 0x10: tag 'VBIInfo' */
+ /* case 0x11: tag 'QCInfo' */
+ /* case 0x12: tag 'InfoBits' */
+
+ default:
+ tveeprom_dbg("Not sure what to do with tag [%02x]\n",
+ tag);
+ /* dump the rest of the packet? */
+ }
+ }
+
+ if (!done) {
+ tveeprom_warn("Ran out of data!\n");
+ return;
+ }
+
+ if (tvee->revision != 0) {
+ tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f);
+ tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f);
+ tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f);
+ tvee->rev_str[3] = 32 + (tvee->revision & 0x3f);
+ tvee->rev_str[4] = 0;
+ }
+
+ if (hasRadioTuner(tuner1) && !tvee->has_radio) {
+ tveeprom_info("The eeprom says no radio is present, but the tuner type\n");
+ tveeprom_info("indicates otherwise. I will assume that radio is present.\n");
+ tvee->has_radio = 1;
+ }
+
+ if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) {
+ tvee->tuner_type = hauppauge_tuner[tuner1].id;
+ t_name1 = hauppauge_tuner[tuner1].name;
+ } else {
+ t_name1 = "unknown";
+ }
+
+ if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) {
+ tvee->tuner2_type = hauppauge_tuner[tuner2].id;
+ t_name2 = hauppauge_tuner[tuner2].name;
+ } else {
+ t_name2 = "unknown";
+ }
+
+ tvee->tuner_hauppauge_model = tuner1;
+ tvee->tuner2_hauppauge_model = tuner2;
+ tvee->tuner_formats = 0;
+ tvee->tuner2_formats = 0;
+ for (i = j = 0; i < 8; i++) {
+ if (t_format1 & (1 << i)) {
+ tvee->tuner_formats |= hauppauge_tuner_fmt[i].id;
+ t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name;
+ }
+ }
+ for (i = j = 0; i < 8; i++) {
+ if (t_format2 & (1 << i)) {
+ tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id;
+ t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name;
+ }
+ }
+
+ tveeprom_info("Hauppauge model %d, rev %s, serial# %u\n",
+ tvee->model, tvee->rev_str, tvee->serial_number);
+ if (tvee->has_MAC_address == 1)
+ tveeprom_info("MAC address is %pM\n", tvee->MAC_address);
+ tveeprom_info("tuner model is %s (idx %d, type %d)\n",
+ t_name1, tuner1, tvee->tuner_type);
+ tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n",
+ t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2],
+ t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5],
+ t_fmt_name1[6], t_fmt_name1[7], t_format1);
+ if (tuner2)
+ tveeprom_info("second tuner model is %s (idx %d, type %d)\n",
+ t_name2, tuner2, tvee->tuner2_type);
+ if (t_format2)
+ tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n",
+ t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2],
+ t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5],
+ t_fmt_name2[6], t_fmt_name2[7], t_format2);
+ if (audioic < 0) {
+ tveeprom_info("audio processor is unknown (no idx)\n");
+ tvee->audio_processor = TVEEPROM_AUDPROC_OTHER;
+ } else {
+ if (audioic < ARRAY_SIZE(audio_ic))
+ tveeprom_info("audio processor is %s (idx %d)\n",
+ audio_ic[audioic].name, audioic);
+ else
+ tveeprom_info("audio processor is unknown (idx %d)\n",
+ audioic);
+ }
+ if (tvee->decoder_processor)
+ tveeprom_info("decoder processor is %s (idx %d)\n",
+ STRM(decoderIC, tvee->decoder_processor),
+ tvee->decoder_processor);
+ if (tvee->has_ir)
+ tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n",
+ tvee->has_radio ? "" : "no ",
+ (tvee->has_ir & 2) ? "" : "no ",
+ (tvee->has_ir & 4) ? "" : "no ");
+ else
+ tveeprom_info("has %sradio\n",
+ tvee->has_radio ? "" : "no ");
+}
+EXPORT_SYMBOL(tveeprom_hauppauge_analog);
+
+/* ----------------------------------------------------------------------- */
+/* generic helper functions */
+
+int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len)
+{
+ unsigned char buf;
+ int err;
+
+ buf = 0;
+ err = i2c_master_send(c, &buf, 1);
+ if (err != 1) {
+ tveeprom_info("Huh, no eeprom present (err=%d)?\n", err);
+ return -1;
+ }
+ err = i2c_master_recv(c, eedata, len);
+ if (err != len) {
+ tveeprom_warn("i2c eeprom read error (err=%d)\n", err);
+ return -1;
+ }
+ if (debug) {
+ int i;
+
+ tveeprom_info("full 256-byte eeprom dump:\n");
+ for (i = 0; i < len; i++) {
+ if (0 == (i % 16))
+ tveeprom_info("%02x:", i);
+ printk(KERN_CONT " %02x", eedata[i]);
+ if (15 == (i % 16))
+ printk(KERN_CONT "\n");
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tveeprom_read);